function jappixmini_addon_xor(str1, str2) { if (str1.length != str2.length) throw "not same length"; encoded = ""; for (i=0; i<str1.length;i++) { var a = str1.charCodeAt(i); var b = str2.charCodeAt(i); var c = a ^ b; encoded += String.fromCharCode(c); } return encoded; } function jappixmini_addon_set_client_secret(password) { if (!password) return; salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N"; salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3"; client_secret1 = str_sha1(salt1+password); client_secret2 = str_sha1(salt2+password); client_secret = client_secret1 + client_secret2; setPersistent('jappix-mini', 'client-secret', client_secret); console.log("client secret set"); } function jappixmini_addon_get_client_secret(callback) { client_secret = getPersistent('jappix-mini', 'client-secret'); if (client_secret===null) { 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:50px;">Retype your Friendica password for chatting:<br></div>'); input = $('<input type="password" id="jappixmini-password-query-input">') div.append(input); button = $('<input type="button" value="OK" id="jappixmini-password-query-button">'); div.append(button); $("body").append(div); } button.click(function(){ password = $("#jappixmini-password-query-input").val(); jappixmini_addon_set_client_secret(password); div.remove(); client_secret = getPersistent('jappix-mini', 'client-secret'); callback(client_secret); }); } else { callback(client_secret); } } 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); encrypted_password = encodeURI(encrypted_password) callback(encrypted_password); }); } function jappixmini_addon_decrypt_password(encrypted_password, callback) { encrypted_password = decodeURI(encrypted_password); jappixmini_addon_get_client_secret(function(client_secret){ // xor password with secret password = jappixmini_addon_xor(client_secret, encrypted_password); // remove \0 first_null = password.indexOf("\0") if (first_null==-1) throw "Decrypted password does not contain \\0"; password = password.substr(0, first_null); callback(password); }); } function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) { // listen for subscriptions con.registerHandler('presence',function(presence){ var type = presence.getType(); if (type != "subscribe") return; var from = fullXID(getStanzaFrom(presence)); var xid = bareXID(from); var pstatus = presence.getStatus(); 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. message = "Accept "+xid+" for chat?"; if (pstatus) message += "\n\nStatus:\n"+pstatus; approve = confirm(message); // do not ask any more if (!approve) sendSubscribe(xid, "unsubscribed"); } if (approve) { acceptSubscribe(xid, contacts[xid]); console.log("Accepted "+xid+" for chat."); } }); // autosubscribe if (!autosubscribe) return; 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() { xid = $(this).attr("jid"); name = $(this).attr("name"); subscription = $(this).attr("subscription"); // ignore accounts not in the list if (contacts[xid]===undefined) return; // add to Friendica group if necessary groups = []; $(this).find('group').each(function() { var group_text = $(this).text(); if(group_text) groups.push(group_text); }); if ($.inArray("Friendica", groups)==-1) { console.log("Add "+xid+" to Friendica group."); groups.push("Friendica"); sendRoster(xid, null, null, groups); console.log("Added "+xid+" to Friendica group."); } // authorize if necessary if (subscription=="to") { sendSubscribe(xid, 'subscribed'); } // 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); // 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 to roster: "+xid); } }); } function jappixmini_addon_subscribe() { if (!window.con) { alert("Not connected."); return; } xid = prompt("Jabber address"); sendSubscribe(xid, "subscribe"); } function jappixmini_addon_start(server, username, proxy, bosh, encrypted, password, nickname, contacts, autoapprove, autosubscribe) { handler = function(password){ // 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(proxy); settings_identifier += str_sha1(bosh); settings_identifier += str_sha1(password); settings_identifier += str_sha1(nickname); saved_identifier = getDB("jappix-mini", "settings_identifier"); if (saved_identifier != settings_identifier) removeDB('jappix-mini', 'dom'); 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_NICKNAME = nickname; LOCK_HOST = "off"; launchMini(true, false, server, username, password); // increase priority over other Jabber clients priority = 101; sendPresence(null,null,priority); jappixmini_manage_roster(contacts, autoapprove, autosubscribe) } // decrypt password if necessary if (encrypted) jappixmini_addon_decrypt_password(password, handler); else handler(password); }