]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/lib.js
Merge pull request #1000 from annando/bots-again
[friendica-addons.git] / jappixmini / lib.js
1 //
2 // Copyright 2012 "Leberwurscht" <leberwurscht@hoegners.de>
3 //
4 // This file is dual-licensed under the MIT license (see MIT.txt) and the AGPL license (see jappix/COPYING).
5 //
6
7 function jappixmini_addon_xor(str1, str2) {
8     if (str1.length != str2.length) throw "not same length";
9
10     var encoded = "";
11
12     for (var i=0; i<str1.length;i++) {
13         var a = str1.charCodeAt(i);
14         var b = str2.charCodeAt(i);
15         var c = a ^ b;
16
17         encoded += String.fromCharCode(c);
18     }
19
20     return encoded;
21 }
22
23 function jappixmini_addon_set_client_secret(password) {
24         if (!password) return;
25
26         var salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N";
27         var salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3";
28
29         var client_secret1 = str_sha1(salt1+password);
30         var client_secret2 = str_sha1(salt2+password);
31         var client_secret = client_secret1 + client_secret2;
32
33         setPersistent('jappix-mini', 'client-secret', client_secret);
34         console.log("client secret set");
35 }
36
37 function jappixmini_addon_get_client_secret(callback) {
38         var client_secret = getPersistent('jappix-mini', 'client-secret');
39         if (client_secret===null) {
40                 var div = document.getElementById("#jappixmini-password-query-div");
41
42                 if (!div) {
43                         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>');
44
45                         var input = $('<input type="password" id="jappixmini-password-query-input">')
46                         div.append(input);
47
48                         var button = $('<input type="button" value="OK" id="jappixmini-password-query-button">');
49                         div.append(button);
50
51                         $("body").append(div);
52                 }
53
54                 button.click(function(){
55                         var password = $("#jappixmini-password-query-input").val();
56                         jappixmini_addon_set_client_secret(password);
57                         div.remove();
58
59                         var client_secret = getPersistent('jappix-mini', 'client-secret');
60                         callback(client_secret);
61                 });
62         }
63         else {
64                 callback(client_secret);
65         }
66 }
67
68 function jappixmini_addon_encrypt_password(password, callback) {
69         jappixmini_addon_get_client_secret(function(client_secret){
70                 // add \0 to password until it has the same length as secret
71                 if (password.length>client_secret.length-1) throw "password too long";
72                 while (password.length<client_secret.length) {
73                         password += "\0";
74                 }
75
76                 // xor password with secret
77                 var encrypted_password = jappixmini_addon_xor(client_secret, password);
78
79                 encrypted_password = encodeURI(encrypted_password)
80                 callback(encrypted_password);
81         });
82 }
83
84 function jappixmini_addon_decrypt_password(encrypted_password, callback) {
85         encrypted_password = decodeURI(encrypted_password);
86
87         jappixmini_addon_get_client_secret(function(client_secret){
88                 // xor password with secret
89                 var password = jappixmini_addon_xor(client_secret, encrypted_password);
90
91                 // remove \0
92                 var first_null = password.indexOf("\0")
93                 if (first_null==-1) throw "Decrypted password does not contain \\0";
94                 password = password.substr(0, first_null);
95
96                 callback(password);
97         });
98 }
99
100 function jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe) {
101         // listen for subscriptions
102         con.registerHandler('presence',function(presence){
103                 var type = presence.getType();
104                 if (type != "subscribe") return;
105
106                 var from = fullXID(getStanzaFrom(presence));
107                 var xid = bareXID(from);
108                 var pstatus = presence.getStatus();
109
110                 var approve;
111
112                 if (autoapprove && contacts[xid]!==undefined) {
113                         // approve known address
114                         approve = true;
115                         console.log("Approve known Friendica contact "+xid+".");
116                 }
117                 else if (autoapprove && pstatus && pstatus.indexOf("Friendica")!=-1) {
118                         // Unknown address claims to be a Friendica contact.
119                         // This is probably because the other side knows our
120                         // address, but we do not know the other side yet.
121                         // But it's only a matter of time, so wait - do not
122                         // approve yet and do not annoy the user by asking.
123                         approve = false;
124                         console.log("Do not approve unknown Friendica contact "+xid+" - wait instead.");
125                 }
126                 else {
127                         // In all other cases, ask the user.
128                         var message = "Accept "+xid+" for chat?";
129                         if (pstatus) message += "\n\nStatus:\n"+pstatus;
130                         approve = confirm(message);
131
132                         // do not ask any more
133                         if (!approve) sendSubscribe(xid, "unsubscribed");
134                 }
135
136                 if (approve) {
137                         var name = contacts[xid];
138                         if (!name) name = xid;
139
140                         acceptSubscribe(xid, name);
141                         console.log("Accepted "+xid+" ("+name+") for chat.");
142                 }
143         });
144
145         // autosubscribe
146         if (!autosubscribe) return;
147
148         var stored_hash = getPersistent("jappix-mini", "contacts-hash");
149         var contacts_changed = (stored_hash != contacts_hash); // stored_hash gets updated later if everything was successful
150         if (!contacts_changed) return;
151
152         console.log("Start autosubscribe.");
153
154         var get_roster = new JSJaCIQ();
155         get_roster.setType('get');
156         get_roster.setQuery(NS_ROSTER);
157
158         con.send(get_roster, function(iq){
159                 var handleXML = iq.getQuery();
160
161                 // filter out contacts that are already in the roster
162                 $(handleXML).find('item').each(function() {
163                         var node = $(this);
164                         var xid = node.attr("jid");
165                         var name = node.attr("name");
166                         var subscription = node.attr("subscription");
167
168                         // ignore accounts that are not in the list
169                         if (contacts[xid]===undefined) return;
170
171                         // add to Friendica group or change name if necessary
172                         var groups = [];
173                         var group_missing = false;
174                         node.find('group').each(function() {
175                                 var group_text = $(this).text();
176                                 if (group_text) groups.push(group_text);
177                         });
178                         if ($.inArray("Friendica", groups)==-1) {
179                                 group_missing = true;
180                                 groups.push("Friendica");
181                         }
182
183                         if (group_missing || name!=contacts[xid]) {
184                                 sendRoster(xid, null, contacts[xid], groups);
185                                 console.log("Added "+xid+" to Friendica group and set name to "+contacts[xid]+".");
186                         }
187
188                         // authorize if necessary
189                         if (subscription=="to") {
190                                 sendSubscribe(xid, 'subscribed');
191                                 console.log("Authorized "+xid+" automatically.");
192                         }
193
194                         // remove from list
195                         delete contacts[xid];
196                 });
197
198                 // go through remaining contacts
199                 for (var xid in contacts) {if(!contacts.hasOwnProperty(xid)) continue;
200                         // subscribe
201                         var presence = new JSJaCPresence();
202                         presence.setTo(xid);
203                         presence.setType("subscribe");
204
205                         // must contain the word "~Friendica" so the other side knows
206                         // how to handle this
207                         presence.setStatus("I'm "+MINI_NICKNAME+" from ~Friendica.\n[machine-generated message]");
208
209                         con.send(presence);
210                         console.log("Subscribed to "+xid+" automatically.");
211
212                         // add to roster
213                         var iq = new JSJaCIQ();
214                         iq.setType('set');
215                         var iqQuery = iq.setQuery(NS_ROSTER);
216                         var item = iqQuery.appendChild(iq.buildNode('item', {'xmlns': NS_ROSTER, 'jid': xid}));
217                         item.setAttribute('name', contacts[xid]);
218                         item.appendChild(iq.buildNode('group', {'xmlns': NS_ROSTER}, "Friendica"));
219                         con.send(iq);
220                         console.log("Added "+xid+" ("+contacts[xid]+") to roster.");
221                 }
222
223                 setPersistent("jappix-mini", "contacts-hash", contacts_hash);
224                 console.log("Autosubscribe done.");
225         });
226
227 }
228
229 function jappixmini_addon_subscribe() {
230         if (!window.con) {
231                 alert("Not connected.");
232                 return;
233         }
234
235         var xid = prompt("Jabber address");
236         sendSubscribe(xid, "subscribe");
237 }
238
239 function jappixmini_addon_start(server, username, proxy, bosh, encrypted, password, nickname, contacts, contacts_hash, autoapprove, autosubscribe, groupchats) {
240     var handler = function(password){
241         // check if settings have changed, reinitialize jappix mini if this is the case
242         var settings_identifier = str_sha1(server);
243         settings_identifier += str_sha1(username);
244         settings_identifier += str_sha1(proxy);
245         settings_identifier += str_sha1(bosh);
246         settings_identifier += str_sha1(password);
247         settings_identifier += str_sha1(nickname);
248
249         var saved_identifier = getDB("jappix-mini", "settings-identifier");
250         if (saved_identifier != settings_identifier) {
251             disconnectMini();
252             removeDB('jappix-mini', 'dom');
253             removePersistent("jappix-mini", "contacts-hash");
254         }
255         setDB("jappix-mini", "settings-identifier", settings_identifier);
256
257         // set HOST_BOSH
258         if (proxy)
259             HOST_BOSH = proxy+"?host_bosh="+encodeURI(bosh);
260         else
261             HOST_BOSH = bosh;
262
263         // start jappix mini
264         MINI_GROUPCHATS = groupchats;
265         MINI_NICKNAME = nickname;
266         LOCK_HOST = "off";
267         launchMini(true, false, server, username, password);
268
269         // increase priority over other Jabber clients - does not seem to work?
270         var priority = 101;
271         presenceMini(null,null,priority);
272
273         jappixmini_manage_roster(contacts, contacts_hash, autoapprove, autosubscribe)
274     }
275
276     // decrypt password if necessary
277     if (encrypted)
278         jappixmini_addon_decrypt_password(password, handler);
279     else
280         handler(password);
281 }