1 function jappixmini_addon_xor(str1, str2) {
2 if (str1.length != str2.length) throw "not same length";
6 for (var i=0; i<str1.length;i++) {
7 var a = str1.charCodeAt(i);
8 var b = str2.charCodeAt(i);
11 encoded += String.fromCharCode(c);
17 function jappixmini_addon_set_client_secret(password) {
18 if (!password) return;
20 var salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N";
21 var salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3";
23 var client_secret1 = str_sha1(salt1+password);
24 var client_secret2 = str_sha1(salt2+password);
25 var client_secret = client_secret1 + client_secret2;
27 setPersistent('jappix-mini', 'client-secret', client_secret);
28 console.log("client secret set");
31 function jappixmini_addon_get_client_secret(callback) {
32 var client_secret = getPersistent('jappix-mini', 'client-secret');
33 if (client_secret===null) {
34 var div = document.getElementById("#jappixmini-password-query-div");
37 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>');
39 var input = $('<input type="password" id="jappixmini-password-query-input">')
42 var button = $('<input type="button" value="OK" id="jappixmini-password-query-button">');
45 $("body").append(div);
48 button.click(function(){
49 var password = $("#jappixmini-password-query-input").val();
50 jappixmini_addon_set_client_secret(password);
53 var client_secret = getPersistent('jappix-mini', 'client-secret');
54 callback(client_secret);
58 callback(client_secret);
62 function jappixmini_addon_encrypt_password(password, callback) {
63 jappixmini_addon_get_client_secret(function(client_secret){
64 // add \0 to password until it has the same length as secret
65 if (password.length>client_secret.length-1) throw "password too long";
66 while (password.length<client_secret.length) {
70 // xor password with secret
71 var encrypted_password = jappixmini_addon_xor(client_secret, password);
73 encrypted_password = encodeURI(encrypted_password)
74 callback(encrypted_password);
78 function jappixmini_addon_decrypt_password(encrypted_password, callback) {
79 encrypted_password = decodeURI(encrypted_password);
81 jappixmini_addon_get_client_secret(function(client_secret){
82 // xor password with secret
83 var password = jappixmini_addon_xor(client_secret, encrypted_password);
86 var first_null = password.indexOf("\0")
87 if (first_null==-1) throw "Decrypted password does not contain \\0";
88 password = password.substr(0, first_null);
94 function jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe) {
95 // listen for subscriptions
96 con.registerHandler('presence',function(presence){
97 var type = presence.getType();
98 if (type != "subscribe") return;
100 var from = fullXID(getStanzaFrom(presence));
101 var xid = bareXID(from);
102 var pstatus = presence.getStatus();
106 if (autoapprove && contacts[xid]!==undefined) {
107 // approve known address
109 console.log("Approve known Friendica contact "+xid+".");
111 else if (autoapprove && pstatus && pstatus.indexOf("Friendica")!=-1) {
112 // Unknown address claims to be a Friendica contact.
113 // This is probably because the other side knows our
114 // address, but we do not know the other side yet.
115 // But it's only a matter of time, so wait - do not
116 // approve yet and do not annoy the user by asking.
118 console.log("Do not approve unknown Friendica contact "+xid+" - wait instead.");
121 // In all other cases, ask the user.
122 var message = "Accept "+xid+" for chat?";
123 if (pstatus) message += "\n\nStatus:\n"+pstatus;
124 approve = confirm(message);
126 // do not ask any more
127 if (!approve) sendSubscribe(xid, "unsubscribed");
131 var name = contacts[xid];
132 if (!name) name = xid;
134 acceptSubscribe(xid, name);
135 console.log("Accepted "+xid+" ("+name+") for chat.");
140 if (!autosubscribe) return;
142 var stored_hash = getPersistent("jappix-mini", "contacts-hash");
143 var contacts_changed = (stored_hash != contacts_hash); // stored_hash gets updated later if everything was successful
144 if (!contacts_changed) return;
146 console.log("Start autosubscribe.");
148 var get_roster = new JSJaCIQ();
149 get_roster.setType('get');
150 get_roster.setQuery(NS_ROSTER);
152 con.send(get_roster, function(iq){
153 var handleXML = iq.getQuery();
155 // filter out contacts that are already in the roster
156 $(handleXML).find('item').each(function() {
158 var xid = node.attr("jid");
159 var name = node.attr("name");
160 var subscription = node.attr("subscription");
162 // ignore accounts that are not in the list
163 if (contacts[xid]===undefined) return;
165 // add to Friendica group or change name if necessary
167 var group_missing = false;
168 node.find('group').each(function() {
169 var group_text = $(this).text();
170 if (group_text) groups.push(group_text);
172 if ($.inArray("Friendica", groups)==-1) {
173 group_missing = true;
174 groups.push("Friendica");
177 if (group_missing || name!=contacts[xid]) {
178 sendRoster(xid, null, contacts[xid], groups);
179 console.log("Added "+xid+" to Friendica group and set name to "+contacts[xid]+".");
182 // authorize if necessary
183 if (subscription=="to") {
184 sendSubscribe(xid, 'subscribed');
185 console.log("Authorized "+xid+" automatically.");
189 delete contacts[xid];
192 // go through remaining contacts
193 for (var xid in contacts) {if(!contacts.hasOwnProperty(xid)) continue;
195 var presence = new JSJaCPresence();
197 presence.setType("subscribe");
199 // must contain the word "~Friendica" so the other side knows
200 // how to handle this
201 presence.setStatus("I'm "+MINI_NICKNAME+" from ~Friendica.\n[machine-generated message]");
204 console.log("Subscribed to "+xid+" automatically.");
207 var iq = new JSJaCIQ();
209 var iqQuery = iq.setQuery(NS_ROSTER);
210 var item = iqQuery.appendChild(iq.buildNode('item', {'xmlns': NS_ROSTER, 'jid': xid}));
211 item.setAttribute('name', contacts[xid]);
212 item.appendChild(iq.buildNode('group', {'xmlns': NS_ROSTER}, "Friendica"));
214 console.log("Added "+xid+" ("+contacts[xid]+") to roster.");
217 setPersistent("jappix-mini", "contacts-hash", contacts_hash);
218 console.log("Autosubscribe done.");
223 function jappixmini_addon_subscribe() {
225 alert("Not connected.");
229 var xid = prompt("Jabber address");
230 sendSubscribe(xid, "subscribe");
233 function jappixmini_addon_start(server, username, proxy, bosh, encrypted, password, nickname, contacts, contacts_hash, autoapprove, autosubscribe) {
234 var handler = function(password){
235 // check if settings have changed, reinitialize jappix mini if this is the case
236 var settings_identifier = str_sha1(server);
237 settings_identifier += str_sha1(username);
238 settings_identifier += str_sha1(proxy);
239 settings_identifier += str_sha1(bosh);
240 settings_identifier += str_sha1(password);
241 settings_identifier += str_sha1(nickname);
243 var saved_identifier = getDB("jappix-mini", "settings-identifier");
244 if (saved_identifier != settings_identifier) {
246 removeDB('jappix-mini', 'dom');
247 removePersistent("jappix-mini", "contacts-hash");
249 setDB("jappix-mini", "settings-identifier", settings_identifier);
253 HOST_BOSH = proxy+"?host_bosh="+encodeURI(bosh);
258 MINI_NICKNAME = nickname;
260 launchMini(true, false, server, username, password);
262 // increase priority over other Jabber clients - does not seem to work?
264 presenceMini(null,null,priority);
266 jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe)
269 // decrypt password if necessary
271 jappixmini_addon_decrypt_password(password, handler);