Update PEP native bookmarks to :1 version, fixes
This commit is contained in:
parent
f2ef2bcfe7
commit
fa6d49e3bf
|
@ -32,12 +32,13 @@ public class EntityInfo : StreamInteractionModule, Object {
|
||||||
this.entity_capabilities_storage = new EntityCapabilitiesStorage(db);
|
this.entity_capabilities_storage = new EntityCapabilitiesStorage(db);
|
||||||
|
|
||||||
stream_interactor.account_added.connect(on_account_added);
|
stream_interactor.account_added.connect(on_account_added);
|
||||||
stream_interactor.stream_negotiated.connect((account, stream) => {
|
stream_interactor.connection_manager.stream_opened.connect((account, stream) => {
|
||||||
var cache = new CapsCacheImpl(account, this);
|
stream.received_features_node.connect(() => {
|
||||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).cache = cache;
|
|
||||||
|
|
||||||
string? hash = EntityCapabilities.get_server_caps_hash(stream);
|
string? hash = EntityCapabilities.get_server_caps_hash(stream);
|
||||||
|
if (hash != null) {
|
||||||
entity_caps_hashes[account.bare_jid.domain_jid] = hash;
|
entity_caps_hashes[account.bare_jid.domain_jid] = hash;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules);
|
stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules);
|
||||||
|
|
||||||
|
@ -169,14 +170,14 @@ public class EntityInfo : StreamInteractionModule, Object {
|
||||||
return identities;
|
return identities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
identities = new HashSet<Identity>(Identity.hash_func, Identity.equals_func);
|
||||||
var qry = db.entity_identity.select().with(db.entity_identity.entity, "=", entity);
|
var qry = db.entity_identity.select().with(db.entity_identity.entity, "=", entity);
|
||||||
foreach (Row row in qry) {
|
foreach (Row row in qry) {
|
||||||
if (identities == null) identities = new HashSet<Identity>(Identity.hash_func, Identity.equals_func);
|
|
||||||
var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]);
|
var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]);
|
||||||
identities.add(identity);
|
identities.add(identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity_identity.size == 0) {
|
if (identities.size == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
entity_identity[entity] = identities;
|
entity_identity[entity] = identities;
|
||||||
|
@ -202,6 +203,9 @@ public class EntityInfo : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_account_added(Account account) {
|
private void on_account_added(Account account) {
|
||||||
|
var cache = new CapsCacheImpl(account, this);
|
||||||
|
stream_interactor.module_manager.get_module(account, ServiceDiscovery.Module.IDENTITY).cache = cache;
|
||||||
|
|
||||||
stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available.connect((stream, presence) => on_received_available_presence(account, presence));
|
stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available.connect((stream, presence) => on_received_available_presence(account, presence));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -386,21 +386,6 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
private_room_occupant_updated(account, room, occupant);
|
private_room_occupant_updated(account, room, occupant);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks.Module.IDENTITY);
|
|
||||||
|
|
||||||
bookmarks_provider[account].received_conferences.connect( (stream, conferences) => {
|
|
||||||
sync_autojoin_active(account, conferences);
|
|
||||||
bookmarks_updated(account, conferences);
|
|
||||||
});
|
|
||||||
bookmarks_provider[account].conference_added.connect( (stream, conference) => {
|
|
||||||
// TODO join (for Bookmarks2)
|
|
||||||
conference_added(account, conference);
|
|
||||||
});
|
|
||||||
bookmarks_provider[account].conference_removed.connect( (stream, jid) => {
|
|
||||||
// TODO part (for Bookmarks2)
|
|
||||||
conference_removed(account, jid);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void search_default_muc_server(Account account) {
|
private async void search_default_muc_server(Account account) {
|
||||||
|
@ -434,7 +419,7 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void on_stream_negotiated(Account account, XmppStream stream) {
|
private async void on_stream_negotiated(Account account, XmppStream stream) {
|
||||||
if (bookmarks_provider[account] == null) return;
|
yield initialize_bookmarks_provider(account);
|
||||||
|
|
||||||
Set<Conference>? conferences = yield bookmarks_provider[account].get_conferences(stream);
|
Set<Conference>? conferences = yield bookmarks_provider[account].get_conferences(stream);
|
||||||
|
|
||||||
|
@ -449,6 +434,31 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void initialize_bookmarks_provider(Account account) {
|
||||||
|
if (bookmarks_provider.has_key(account)) return;
|
||||||
|
|
||||||
|
// Use PEP native bookmarks (urn:xmpp:bookmarks:1) if conversion is available, legacy bookmarks (storage:bookmarks) otherwise.
|
||||||
|
bool has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(account, account.bare_jid, Xep.Bookmarks2.NS_URI_COMPAT);
|
||||||
|
if (has_feature) {
|
||||||
|
debug("[%s] Using PEP native bookmarks (urn:xmpp:bookmarks:1)", account.bare_jid.to_string());
|
||||||
|
bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks2.Module.IDENTITY);
|
||||||
|
} else {
|
||||||
|
debug("[%s] Using legacy bookmarks (storage:bookmarks)", account.bare_jid.to_string());
|
||||||
|
bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks.Module.IDENTITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmarks_provider[account].received_conferences.connect( (stream, conferences) => {
|
||||||
|
sync_autojoin_active(account, conferences);
|
||||||
|
bookmarks_updated(account, conferences);
|
||||||
|
});
|
||||||
|
bookmarks_provider[account].conference_added.connect( (stream, conference) => {
|
||||||
|
on_conference_added(account, conference);
|
||||||
|
});
|
||||||
|
bookmarks_provider[account].conference_removed.connect( (stream, jid) => {
|
||||||
|
on_conference_removed(account, jid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) {
|
private void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) {
|
||||||
if (!invites.has_key(account)) {
|
if (!invites.has_key(account)) {
|
||||||
invites[account] = new LinkedList<Jid>(Jid.equals_func);
|
invites[account] = new LinkedList<Jid>(Jid.equals_func);
|
||||||
|
@ -544,6 +554,29 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void on_conference_added(Account account, Xmpp.Conference conference) {
|
||||||
|
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conference.jid, account, Conversation.Type.GROUPCHAT);
|
||||||
|
if (conversation != null) {
|
||||||
|
if (!conversation.active && conference.autojoin) {
|
||||||
|
join.begin(account, conference.jid, conference.nick, conference.password);
|
||||||
|
} else if (conversation.active && !conference.autojoin) {
|
||||||
|
part(account, conference.jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conference.autojoin) {
|
||||||
|
join.begin(account, conference.jid, conference.nick, conference.password);
|
||||||
|
}
|
||||||
|
conference_added(account, conference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_conference_removed(Account account, Jid jid) {
|
||||||
|
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT);
|
||||||
|
if (conversation != null && conversation.active) {
|
||||||
|
part(account, jid);
|
||||||
|
}
|
||||||
|
conference_removed(account, jid);
|
||||||
|
}
|
||||||
|
|
||||||
private void self_ping(Account account) {
|
private void self_ping(Account account) {
|
||||||
XmppStream? stream = stream_interactor.get_stream(account);
|
XmppStream? stream = stream_interactor.get_stream(account);
|
||||||
if (stream == null) return;
|
if (stream == null) return;
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
public const string NS_URI = "http://jabber.org/protocol/pubsub";
|
public const string NS_URI = "http://jabber.org/protocol/pubsub";
|
||||||
private const string NS_URI_EVENT = NS_URI + "#event";
|
private const string NS_URI_EVENT = NS_URI + "#event";
|
||||||
private const string NS_URI_OWNER = NS_URI + "#owner";
|
private const string NS_URI_OWNER = NS_URI + "#owner";
|
||||||
|
public const string NS_URI_ERROR = NS_URI + "#errors";
|
||||||
|
|
||||||
public const string ACCESS_MODEL_AUTHORIZE = "authorize";
|
public const string ACCESS_MODEL_AUTHORIZE = "authorize";
|
||||||
public const string ACCESS_MODEL_OPEN = "open";
|
public const string ACCESS_MODEL_OPEN = "open";
|
||||||
|
@ -66,7 +67,11 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async bool publish(XmppStream stream, Jid? jid, string node_id, string? item_id, StanzaNode content, string? access_model=null) {
|
public async bool publish(
|
||||||
|
XmppStream stream, Jid? jid, string node_id, string? item_id, StanzaNode content,
|
||||||
|
PublishOptions? publish_options = null,
|
||||||
|
bool try_reconfiguring = true
|
||||||
|
) {
|
||||||
StanzaNode pubsub_node = new StanzaNode.build("pubsub", NS_URI).add_self_xmlns();
|
StanzaNode pubsub_node = new StanzaNode.build("pubsub", NS_URI).add_self_xmlns();
|
||||||
StanzaNode publish_node = new StanzaNode.build("publish", NS_URI).put_attribute("node", node_id);
|
StanzaNode publish_node = new StanzaNode.build("publish", NS_URI).put_attribute("node", node_id);
|
||||||
pubsub_node.put_node(publish_node);
|
pubsub_node.put_node(publish_node);
|
||||||
|
@ -75,7 +80,8 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
items_node.put_node(content);
|
items_node.put_node(content);
|
||||||
publish_node.put_node(items_node);
|
publish_node.put_node(items_node);
|
||||||
|
|
||||||
if (access_model != null) {
|
// Send along our requirements for the node
|
||||||
|
if (publish_options != null) {
|
||||||
StanzaNode publish_options_node = new StanzaNode.build("publish-options", NS_URI);
|
StanzaNode publish_options_node = new StanzaNode.build("publish-options", NS_URI);
|
||||||
pubsub_node.put_node(publish_options_node);
|
pubsub_node.put_node(publish_options_node);
|
||||||
|
|
||||||
|
@ -83,23 +89,30 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
||||||
form_type_field.set_value_string(NS_URI + "#publish-options");
|
form_type_field.set_value_string(NS_URI + "#publish-options");
|
||||||
data_form.add_field(form_type_field);
|
data_form.add_field(form_type_field);
|
||||||
if (access_model != null) {
|
|
||||||
DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="pubsub#access_model" };
|
foreach (string field_name in publish_options.settings.keys) {
|
||||||
field.set_value_string(access_model);
|
DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var=field_name };
|
||||||
|
field.set_value_string(publish_options.settings[field_name]);
|
||||||
data_form.add_field(field);
|
data_form.add_field(field);
|
||||||
}
|
}
|
||||||
publish_options_node.put_node(data_form.get_submit_node());
|
publish_options_node.put_node(data_form.get_submit_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Iq.Stanza iq = new Iq.Stanza.set(pubsub_node);
|
Iq.Stanza iq = new Iq.Stanza.set(pubsub_node);
|
||||||
bool ok = true;
|
|
||||||
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, result_iq) => {
|
|
||||||
ok = !result_iq.is_error();
|
|
||||||
Idle.add(publish.callback);
|
|
||||||
});
|
|
||||||
yield;
|
|
||||||
|
|
||||||
return ok;
|
// If the node was configured differently before, reconfigure it to meet our requirements and try again
|
||||||
|
Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq);
|
||||||
|
if (iq_result.is_error()) {
|
||||||
|
if (publish_options == null || !try_reconfiguring) return false;
|
||||||
|
bool precondition_not_met = iq_result.get_error().error_node.get_subnode("precondition-not-met", NS_URI_ERROR) != null;
|
||||||
|
if (precondition_not_met) {
|
||||||
|
bool success = yield change_node_config(stream, jid, node_id, publish_options);
|
||||||
|
if (!success) return false;
|
||||||
|
return yield publish(stream, jid, node_id, item_id, content, publish_options, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async bool retract_item(XmppStream stream, Jid? jid, string node_id, string item_id) {
|
public async bool retract_item(XmppStream stream, Jid? jid, string node_id, string item_id) {
|
||||||
|
@ -139,7 +152,7 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
return DataForms.DataForm.create_from_node(data_form_node);
|
return DataForms.DataForm.create_from_node(data_form_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void submit_node_config(XmppStream stream, DataForms.DataForm data_form, string node_id) {
|
public async bool submit_node_config(XmppStream stream, DataForms.DataForm data_form, string node_id) {
|
||||||
StanzaNode submit_node = data_form.get_submit_node();
|
StanzaNode submit_node = data_form.get_submit_node();
|
||||||
|
|
||||||
StanzaNode pubsub_node = new StanzaNode.build("pubsub", Pubsub.NS_URI_OWNER).add_self_xmlns();
|
StanzaNode pubsub_node = new StanzaNode.build("pubsub", Pubsub.NS_URI_OWNER).add_self_xmlns();
|
||||||
|
@ -149,7 +162,9 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
|
|
||||||
|
|
||||||
Iq.Stanza iq = new Iq.Stanza.set(pubsub_node);
|
Iq.Stanza iq = new Iq.Stanza.set(pubsub_node);
|
||||||
yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq);
|
Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq);
|
||||||
|
|
||||||
|
return !iq_result.is_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
public override void attach(XmppStream stream) {
|
||||||
|
@ -192,7 +207,42 @@ namespace Xmpp.Xep.Pubsub {
|
||||||
retract_listeners[node].on_result(stream, message.from, id);
|
retract_listeners[node].on_result(stream, message.from, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async bool change_node_config(XmppStream stream, Jid jid, string node, PublishOptions publish_options) {
|
||||||
|
DataForms.DataForm? data_form = yield stream.get_module(Pubsub.Module.IDENTITY).request_node_config(stream, null, node);
|
||||||
|
if (data_form == null) return false;
|
||||||
|
|
||||||
|
foreach (DataForms.DataForm.Field field in data_form.fields) {
|
||||||
|
if (publish_options.settings.has_key(field.var) && publish_options.settings[field.var] != field.get_value_string()) {
|
||||||
|
field.set_value_string(publish_options.settings[field.var]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yield stream.get_module(Pubsub.Module.IDENTITY).submit_node_config(stream, data_form, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PublishOptions {
|
||||||
|
public HashMap<string, string> settings = new HashMap<string, string>();
|
||||||
|
|
||||||
|
public PublishOptions set_persist_items(bool persist) {
|
||||||
|
settings["pubsub#persist_items"] = persist.to_string();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublishOptions set_max_items(string max) {
|
||||||
|
settings["pubsub#max_items"] = max;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublishOptions set_send_last_published_item(string send) {
|
||||||
|
settings["pubsub#send_last_published_item"] = send.to_string();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublishOptions set_access_model(string model) {
|
||||||
|
settings["pubsub#access_model"] = model;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ using Gee;
|
||||||
|
|
||||||
namespace Xmpp.Xep.Bookmarks2 {
|
namespace Xmpp.Xep.Bookmarks2 {
|
||||||
|
|
||||||
private const string NS_URI = "urn:xmpp:bookmarks:0";
|
public const string NS_URI = "urn:xmpp:bookmarks:1";
|
||||||
|
public const string NS_URI_COMPAT = NS_URI + "#compat";
|
||||||
|
|
||||||
public class Module : BookmarksProvider, XmppStreamModule {
|
public class Module : BookmarksProvider, XmppStreamModule {
|
||||||
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0402_bookmarks2");
|
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0402_bookmarks2");
|
||||||
|
@ -35,14 +36,23 @@ public class Module : BookmarksProvider, XmppStreamModule {
|
||||||
|
|
||||||
public async void add_conference(XmppStream stream, Conference conference) {
|
public async void add_conference(XmppStream stream, Conference conference) {
|
||||||
StanzaNode conference_node = new StanzaNode.build("conference", NS_URI).add_self_xmlns()
|
StanzaNode conference_node = new StanzaNode.build("conference", NS_URI).add_self_xmlns()
|
||||||
.put_attribute("autojoin", conference.autojoin ? "true" : "false");
|
.put_attribute("autojoin", conference.autojoin.to_string());
|
||||||
if (conference.name != null) {
|
if (conference.name != null) {
|
||||||
conference_node.put_attribute("name", conference.name);
|
conference_node.put_attribute("name", conference.name);
|
||||||
}
|
}
|
||||||
if (conference.nick != null) {
|
if (conference.nick != null) {
|
||||||
conference_node.put_node((new StanzaNode.build("nick", NS_URI)).put_node(new StanzaNode.text(conference.nick)));
|
conference_node.put_node((new StanzaNode.build("nick", NS_URI)).put_node(new StanzaNode.text(conference.nick)));
|
||||||
}
|
}
|
||||||
yield stream.get_module(Pubsub.Module.IDENTITY).publish(stream, stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid, NS_URI, conference.jid.to_string(), conference_node, Xmpp.Xep.Pubsub.ACCESS_MODEL_WHITELIST);
|
if (conference.password != null) {
|
||||||
|
conference_node.put_node((new StanzaNode.build("password", NS_URI)).put_node(new StanzaNode.text(conference.password)));
|
||||||
|
}
|
||||||
|
var publish_options = new Pubsub.PublishOptions()
|
||||||
|
.set_persist_items(true)
|
||||||
|
.set_max_items("max")
|
||||||
|
.set_send_last_published_item("never")
|
||||||
|
.set_access_model("whitelist");
|
||||||
|
|
||||||
|
yield stream.get_module(Pubsub.Module.IDENTITY).publish(stream, stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid, NS_URI, conference.jid.to_string(), conference_node, publish_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void replace_conference(XmppStream stream, Jid muc_jid, Conference modified_conference) {
|
public async void replace_conference(XmppStream stream, Jid muc_jid, Conference modified_conference) {
|
||||||
|
@ -92,7 +102,7 @@ public class Module : BookmarksProvider, XmppStreamModule {
|
||||||
if (conference_node.name != "conference" || conference_node.ns_uri != NS_URI) return null;
|
if (conference_node.name != "conference" || conference_node.ns_uri != NS_URI) return null;
|
||||||
|
|
||||||
conference.name = conference_node.get_attribute("name", NS_URI);
|
conference.name = conference_node.get_attribute("name", NS_URI);
|
||||||
conference.autojoin = conference_node.get_attribute("autojoin", NS_URI) == "true";
|
conference.autojoin = conference_node.get_attribute_bool("autojoin", false, NS_URI);
|
||||||
conference.nick = conference_node.get_deep_string_content("nick");
|
conference.nick = conference_node.get_deep_string_content("nick");
|
||||||
return conference;
|
return conference;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue