+//
+// Copyright 2012 "Leberwurscht" <leberwurscht@hoegners.de>
+//
+// This file is dual-licensed under the MIT license (see MIT.txt) and the AGPL license (see jappix/COPYING).
+//
+
function jappixmini_addon_xor(str1, str2) {
if (str1.length != str2.length) throw "not same length";
- encoded = "";
+ var encoded = "";
- for (i=0; i<str1.length;i++) {
+ for (var i=0; i<str1.length;i++) {
var a = str1.charCodeAt(i);
var b = str2.charCodeAt(i);
var c = a ^ b;
function jappixmini_addon_set_client_secret(password) {
if (!password) return;
- salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N";
- salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3";
+ var salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N";
+ var salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3";
- client_secret1 = str_sha1(salt1+password);
- client_secret2 = str_sha1(salt2+password);
- client_secret = client_secret1 + client_secret2;
+ var client_secret1 = str_sha1(salt1+password);
+ var client_secret2 = str_sha1(salt2+password);
+ var client_secret = client_secret1 + client_secret2;
- setDB('jappix-mini', 'client_secret', client_secret);
+ setPersistent('jappix-mini', 'client-secret', client_secret);
console.log("client secret set");
}
-function jappixmini_addon_get_client_secret() {
- client_secret = getDB('jappix-mini', 'client_secret');
+function jappixmini_addon_get_client_secret(callback) {
+ var client_secret = getPersistent('jappix-mini', 'client-secret');
if (client_secret===null) {
- div = $('<div style="position:fixed;padding:1em;background-color:#F00;color:#fff;top:50px;left:50px;">Retype your Friendica password for chatting:</div>');
- div.append($("<br>"));
- input = $('<input type="password">')
- div.append(input);
- button = $('<input type="button" value="OK">');
+ var div = document.getElementById("#jappixmini-password-query-div");
+
+ if (!div) {
+ div = $('<div id="jappixmini-password-query-div" style="position:fixed;padding:1em;background-color:#F00;color:#fff;top:50px;left:650px;">Retype your Friendica password for chatting:<br></div>');
+
+ var input = $('<input type="password" id="jappixmini-password-query-input">')
+ div.append(input);
+
+ var button = $('<input type="button" value="OK" id="jappixmini-password-query-button">');
+ div.append(button);
+
+ $("body").append(div);
+ }
+
button.click(function(){
- password = input.val();
+ var password = $("#jappixmini-password-query-input").val();
jappixmini_addon_set_client_secret(password);
div.remove();
+
+ var client_secret = getPersistent('jappix-mini', 'client-secret');
+ callback(client_secret);
});
- div.append(button);
- $("body").append(div);
}
-
- return client_secret;
+ else {
+ callback(client_secret);
+ }
}
-function jappixmini_addon_encrypt_password(password) {
- client_secret = jappixmini_addon_get_client_secret();
-
- // add \0 to password until it has the same length as secret
- if (password.length>client_secret.length-1) throw "password too long";
- while (password.length<client_secret.length) {
- password += "\0";
- }
+function jappixmini_addon_encrypt_password(password, callback) {
+ jappixmini_addon_get_client_secret(function(client_secret){
+ // add \0 to password until it has the same length as secret
+ if (password.length>client_secret.length-1) throw "password too long";
+ while (password.length<client_secret.length) {
+ password += "\0";
+ }
- // xor password with secret
- encrypted_password = jappixmini_addon_xor(client_secret, password);
+ // xor password with secret
+ var encrypted_password = jappixmini_addon_xor(client_secret, password);
- encrypted_password = encodeURI(encrypted_password)
- return encrypted_password;
+ encrypted_password = encodeURI(encrypted_password)
+ callback(encrypted_password);
+ });
}
-function jappixmini_addon_decrypt_password(encrypted_password) {
+function jappixmini_addon_decrypt_password(encrypted_password, callback) {
encrypted_password = decodeURI(encrypted_password);
- client_secret = jappixmini_addon_get_client_secret();
-
- // xor password with secret
- password = jappixmini_addon_xor(client_secret, encrypted_password);
+ jappixmini_addon_get_client_secret(function(client_secret){
+ // xor password with secret
+ var password = jappixmini_addon_xor(client_secret, encrypted_password);
- // remove \0
- first_null = password.indexOf("\0")
- password = password.substr(0, first_null);
+ // remove \0
+ var first_null = password.indexOf("\0")
+ if (first_null==-1) throw "Decrypted password does not contain \\0";
+ password = password.substr(0, first_null);
- return password;
+ callback(password);
+ });
}
-function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
+function jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe) {
// listen for subscriptions
con.registerHandler('presence',function(presence){
var type = presence.getType();
var from = fullXID(getStanzaFrom(presence));
var xid = bareXID(from);
+ var pstatus = presence.getStatus();
+
+ var approve;
+
+ if (autoapprove && contacts[xid]!==undefined) {
+ // approve known address
+ approve = true;
+ console.log("Approve known Friendica contact "+xid+".");
+ }
+ else if (autoapprove && pstatus && pstatus.indexOf("Friendica")!=-1) {
+ // Unknown address claims to be a Friendica contact.
+ // This is probably because the other side knows our
+ // address, but we do not know the other side yet.
+ // But it's only a matter of time, so wait - do not
+ // approve yet and do not annoy the user by asking.
+ approve = false;
+ console.log("Do not approve unknown Friendica contact "+xid+" - wait instead.");
+ }
+ else {
+ // In all other cases, ask the user.
+ var message = "Accept "+xid+" for chat?";
+ if (pstatus) message += "\n\nStatus:\n"+pstatus;
+ approve = confirm(message);
- approve = true;
- if ((!autoapprove) || contacts[xid]===undefined)
- approve = confirm("Accept "+xid+" for chat?");
+ // do not ask any more
+ if (!approve) sendSubscribe(xid, "unsubscribed");
+ }
if (approve) {
- acceptSubscribe(xid, contacts[xid]);
- console.log("Accepted "+xid+" for chat.");
+ var name = contacts[xid];
+ if (!name) name = xid;
+
+ acceptSubscribe(xid, name);
+ console.log("Accepted "+xid+" ("+name+") for chat.");
}
});
// autosubscribe
- if (autosubscribe) {
- for (i=0; i<contacts.length; i++) {
- xid = contacts[i];
- sendSubscribe(xid, "subscribe");
- console.log("Subscribed to "+xid);
+ if (!autosubscribe) return;
+
+ var stored_hash = getPersistent("jappix-mini", "contacts-hash");
+ var contacts_changed = (stored_hash != contacts_hash); // stored_hash gets updated later if everything was successful
+ if (!contacts_changed) return;
+
+ console.log("Start autosubscribe.");
+
+ var get_roster = new JSJaCIQ();
+ get_roster.setType('get');
+ get_roster.setQuery(NS_ROSTER);
+
+ con.send(get_roster, function(iq){
+ var handleXML = iq.getQuery();
+
+ // filter out contacts that are already in the roster
+ $(handleXML).find('item').each(function() {
+ var node = $(this);
+ var xid = node.attr("jid");
+ var name = node.attr("name");
+ var subscription = node.attr("subscription");
+
+ // ignore accounts that are not in the list
+ if (contacts[xid]===undefined) return;
+
+ // add to Friendica group or change name if necessary
+ var groups = [];
+ var group_missing = false;
+ node.find('group').each(function() {
+ var group_text = $(this).text();
+ if (group_text) groups.push(group_text);
+ });
+ if ($.inArray("Friendica", groups)==-1) {
+ group_missing = true;
+ groups.push("Friendica");
+ }
+
+ if (group_missing || name!=contacts[xid]) {
+ sendRoster(xid, null, contacts[xid], groups);
+ console.log("Added "+xid+" to Friendica group and set name to "+contacts[xid]+".");
+ }
+
+ // authorize if necessary
+ if (subscription=="to") {
+ sendSubscribe(xid, 'subscribed');
+ console.log("Authorized "+xid+" automatically.");
+ }
+
+ // remove from list
+ delete contacts[xid];
+ });
+
+ // go through remaining contacts
+ for (var xid in contacts) {if(!contacts.hasOwnProperty(xid)) continue;
+ // subscribe
+ var presence = new JSJaCPresence();
+ presence.setTo(xid);
+ presence.setType("subscribe");
+
+ // must contain the word "~Friendica" so the other side knows
+ // how to handle this
+ presence.setStatus("I'm "+MINI_NICKNAME+" from ~Friendica.\n[machine-generated message]");
+
+ con.send(presence);
+ console.log("Subscribed to "+xid+" automatically.");
+
+ // add to roster
+ var iq = new JSJaCIQ();
+ iq.setType('set');
+ var iqQuery = iq.setQuery(NS_ROSTER);
+ var item = iqQuery.appendChild(iq.buildNode('item', {'xmlns': NS_ROSTER, 'jid': xid}));
+ item.setAttribute('name', contacts[xid]);
+ item.appendChild(iq.buildNode('group', {'xmlns': NS_ROSTER}, "Friendica"));
+ con.send(iq);
+ console.log("Added "+xid+" ("+contacts[xid]+") to roster.");
}
- }
+
+ setPersistent("jappix-mini", "contacts-hash", contacts_hash);
+ console.log("Autosubscribe done.");
+ });
+
}
-function jappixmini_addon_start(server, username, bosh, encrypted_password, nickname) {
- // check if settings have changed, reinitialize jappix mini if this is the case
- settings_identifier = str_sha1(server);
- settings_identifier += str_sha1(username);
- settings_identifier += str_sha1(bosh);
- settings_identifier += str_sha1(encrypted_password);
- settings_identifier += str_sha1(nickname);
+function jappixmini_addon_subscribe() {
+ if (!window.con) {
+ alert("Not connected.");
+ return;
+ }
- saved_identifier = getDB("jappix-mini", "settings_identifier");
- if (saved_identifier != settings_identifier) removeDB('jappix-mini', 'dom');
- setDB("jappix-mini", "settings_identifier", settings_identifier);
+ var xid = prompt("Jabber address");
+ sendSubscribe(xid, "subscribe");
+}
- // set bosh host
- HOST_BOSH = HOST_BOSH+"?host_bosh="+encodeURI(bosh);
+function jappixmini_addon_start(server, username, proxy, bosh, encrypted, password, nickname, contacts, contacts_hash, autoapprove, autosubscribe, groupchats) {
+ var handler = function(password){
+ // check if settings have changed, reinitialize jappix mini if this is the case
+ var settings_identifier = str_sha1(server);
+ settings_identifier += str_sha1(username);
+ settings_identifier += str_sha1(proxy);
+ settings_identifier += str_sha1(bosh);
+ settings_identifier += str_sha1(password);
+ settings_identifier += str_sha1(nickname);
- // decrypt password
- password = jappixmini_addon_decrypt_password(encrypted_password);
+ var saved_identifier = getDB("jappix-mini", "settings-identifier");
+ if (saved_identifier != settings_identifier) {
+ disconnectMini();
+ removeDB('jappix-mini', 'dom');
+ removePersistent("jappix-mini", "contacts-hash");
+ }
+ setDB("jappix-mini", "settings-identifier", settings_identifier);
+
+ // set HOST_BOSH
+ if (proxy)
+ HOST_BOSH = proxy+"?host_bosh="+encodeURI(bosh);
+ else
+ HOST_BOSH = bosh;
+
+ // start jappix mini
+ MINI_GROUPCHATS = groupchats;
+ MINI_NICKNAME = nickname;
+ LOCK_HOST = "off";
+ launchMini(true, false, server, username, password);
+
+ // increase priority over other Jabber clients - does not seem to work?
+ var priority = 101;
+ presenceMini(null,null,priority);
+
+ jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe)
+ }
- // start jappix mini
- MINI_NICKNAME = nickname;
- launchMini(true, false, server, username, password);
+ // decrypt password if necessary
+ if (encrypted)
+ jappixmini_addon_decrypt_password(password, handler);
+ else
+ handler(password);
}