]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappixmini.php
Fix formatting
[friendica-addons.git] / jappixmini / jappixmini.php
1 <?php
2
3 /**
4  * Name: jappixmini
5  * Description: Provides a Facebook-like chat using Jappix Mini
6  * Version: 1.0.1
7  * Author: leberwurscht <leberwurscht@hoegners.de>
8  *
9  */
10 //
11 // Copyright 2012 "Leberwurscht" <leberwurscht@hoegners.de>
12 //
13 // This file is dual-licensed under the MIT license (see MIT.txt) and the AGPL license (see jappix/COPYING).
14 //
15
16 /*
17
18   Problem:
19  * jabber password should not be stored on server
20  * jabber password should not be sent between server and browser as soon as the user is logged in
21  * jabber password should not be reconstructible from communication between server and browser as soon as the user is logged in
22
23   Solution:
24   Only store an encrypted version of the jabber password on the server. The encryption key is only available to the browser
25   and not to the server (at least as soon as the user is logged in). It can be stored using the jappix setDB function.
26
27   This encryption key could be the friendica password, but then this password would be stored in the browser in cleartext.
28   It is better to use a hash of the password.
29   The server should not be able to reconstruct the password, so we can't take the same hash the server stores. But we can
30   use hash("some_prefix"+password). This will however not work with OpenID logins, for this type of login the password must
31   be queried manually.
32
33   Problem:
34   How to discover the jabber addresses of the friendica contacts?
35
36   Solution:
37   Each Friendica site with this addon provides a /jappixmini/ module page. We go through our contacts and retrieve
38   this information every week using a cron hook.
39
40   Problem:
41   We do not want to make the jabber address public.
42
43   Solution:
44   When two friendica users connect using DFRN, the relation gets a DFRN ID and a keypair is generated.
45   Using this keypair, we can provide the jabber address only to contacts:
46
47   Alice:
48   signed_address = openssl_*_encrypt(alice_jabber_address)
49   send signed_address to Bob, who does
50   trusted_address = openssl_*_decrypt(signed_address)
51   save trusted_address
52   encrypted_address = openssl_*_encrypt(bob_jabber_address)
53   reply with encrypted_address to Alice, who does
54   decrypted_address = openssl_*_decrypt(encrypted_address)
55   save decrypted_address
56
57   Interface for this:
58   GET /jappixmini/?role=%s&signed_address=%s&dfrn_id=%s
59
60   Response:
61   json({"status":"ok", "encrypted_address":"%s"})
62
63  */
64
65 use Friendica\App;
66 use Friendica\Core\Config;
67 use Friendica\Core\PConfig;
68 use Friendica\Model\User;
69
70 function jappixmini_install()
71 {
72         register_hook('plugin_settings', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings');
73         register_hook('plugin_settings_post', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings_post');
74
75         register_hook('page_end', 'addon/jappixmini/jappixmini.php', 'jappixmini_script');
76         register_hook('authenticate', 'addon/jappixmini/jappixmini.php', 'jappixmini_login');
77
78         register_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
79
80         // Jappix source download as required by AGPL
81         register_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
82
83         // set standard configuration
84         $info_text = Config::get("jappixmini", "infotext");
85         if (!$info_text)
86                 set_confConfig::setig("jappixmini", "infotext", "To get the chat working, you need to know a BOSH host which works with your Jabber account. " .
87                         "An example of a BOSH server that works for all accounts is https://bind.jappix.com/, but keep " .
88                         "in mind that the BOSH server can read along all chat messages. If you know that your Jabber " .
89                         "server also provides an own BOSH server, it is much better to use this one!"
90                 );
91
92         $bosh_proxy = Config::get("jappixmini", "bosh_proxy");
93         if ($bosh_proxy === "") {
94                 Config::set("jappixmini", "bosh_proxy", "1");
95         }
96
97         // set addon version so that safe updates are possible later
98         $addon_version = Config::get("jappixmini", "version");
99         if ($addon_version === "") {
100                 Config::set("jappixmini", "version", "1");
101         }
102 }
103
104 function jappixmini_uninstall()
105 {
106         unregister_hook('plugin_settings', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings');
107         unregister_hook('plugin_settings_post', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings_post');
108
109         unregister_hook('page_end', 'addon/jappixmini/jappixmini.php', 'jappixmini_script');
110         unregister_hook('authenticate', 'addon/jappixmini/jappixmini.php', 'jappixmini_login');
111
112         unregister_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
113
114         unregister_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
115 }
116
117 function jappixmini_plugin_admin(App $a, &$o)
118 {
119         // display instructions and warnings on addon settings page for admin
120         if (!file_exists("addon/jappixmini.tgz")) {
121                 $o .= '<p><strong style="color:#fff;background-color:#f00">The source archive jappixmini.tgz does not exist. This is probably a violation of the Jappix License (AGPL).</strong></p>';
122         }
123
124         // warn if cron job has not yet been executed
125         $cron_run = Config::get("jappixmini", "last_cron_execution");
126         if (!$cron_run) {
127                 $o .= "<p><strong>Warning: The cron job has not yet been executed. If this message is still there after some time (usually 10 minutes), this means that autosubscribe and autoaccept will not work.</strong></p>";
128         }
129
130         // bosh proxy
131         $bosh_proxy = intval(Config::get("jappixmini", "bosh_proxy"));
132         $bosh_proxy = intval($bosh_proxy) ? ' checked="checked"' : '';
133         $o .= '<label for="jappixmini-proxy">Activate BOSH proxy</label>';
134         $o .= ' <input id="jappixmini-proxy" type="checkbox" name="jappixmini-proxy" value="1"' . $bosh_proxy . ' /><br />';
135
136         // bosh address
137         $bosh_address = Config::get("jappixmini", "bosh_address");
138         $o .= '<p><label for="jappixmini-address">Adress of the default BOSH proxy. If enabled it overrides the user settings:</label><br />';
139         $o .= '<input id="jappixmini-address" type="text" name="jappixmini-address" value="' . $bosh_address . '" /></p>';
140
141         // default server address
142         $default_server = Config::get("jappixmini", "default_server");
143         $o .= '<p><label for="jappixmini-server">Adress of the default jabber server:</label><br />';
144         $o .= '<input id="jappixmini-server" type="text" name="jappixmini-server" value="' . $default_server . '" /></p>';
145
146         // default user name to friendica nickname
147         $default_user = intval(Config::get("jappixmini", "default_user"));
148         $default_user = intval($default_user) ? ' checked="checked"' : '';
149         $o .= '<label for="jappixmini-user">Set the default username to the nickname:</label>';
150         $o .= ' <input id="jappixmini-user" type="checkbox" name="jappixmini-defaultuser" value="1"' . $default_user . ' /><br />';
151
152         // info text field
153         $info_text = Config::get("jappixmini", "infotext");
154         $o .= '<p><label for="jappixmini-infotext">Info text to help users with configuration (important if you want to provide your own BOSH host!):</label><br />';
155         $o .= '<textarea id="jappixmini-infotext" name="jappixmini-infotext" rows="5" cols="50">' . htmlentities($info_text) . '</textarea></p>';
156
157         // submit button
158         $o .= '<input type="submit" name="jappixmini-admin-settings" value="OK" />';
159 }
160
161 function jappixmini_plugin_admin_post(App $a)
162 {
163         // set info text
164         $submit = $_REQUEST['jappixmini-admin-settings'];
165         if ($submit) {
166                 $info_text = $_REQUEST['jappixmini-infotext'];
167                 $bosh_proxy = intval($_REQUEST['jappixmini-proxy']);
168                 $default_user = intval($_REQUEST['jappixmini-defaultuser']);
169                 $bosh_address = $_REQUEST['jappixmini-address'];
170                 $default_server = $_REQUEST['jappixmini-server'];
171                 Config::set("jappixmini", "infotext", $info_text);
172                 Config::set("jappixmini", "bosh_proxy", $bosh_proxy);
173                 Config::set("jappixmini", "bosh_address", $bosh_address);
174                 Config::set("jappixmini", "default_server", $default_server);
175                 Config::set("jappixmini", "default_user", $default_user);
176         }
177 }
178
179 function jappixmini_module()
180 {
181
182 }
183
184 function jappixmini_init()
185 {
186         // module page where other Friendica sites can submit Jabber addresses to and also can query Jabber addresses
187         // of local users
188         $dfrn_id = $_REQUEST["dfrn_id"];
189         if (!$dfrn_id) {
190                 killme();
191         }
192
193         $role = $_REQUEST["role"];
194         if ($role == "pub") {
195                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id`='%s' LIMIT 1", dbesc($dfrn_id));
196                 if (!count($r)) {
197                         killme();
198                 }
199
200                 $encrypt_func = openssl_public_encrypt;
201                 $decrypt_func = openssl_public_decrypt;
202                 $key = $r[0]["pubkey"];
203         } else if ($role == "prv") {
204                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id`='%s' LIMIT 1", dbesc($dfrn_id));
205                 if (!count($r)) {
206                         killme();
207                 }
208
209                 $encrypt_func = openssl_private_encrypt;
210                 $decrypt_func = openssl_private_decrypt;
211                 $key = $r[0]["prvkey"];
212         } else {
213                 killme();
214         }
215
216         $uid = $r[0]["uid"];
217
218         // save the Jabber address we received
219         try {
220                 $signed_address_hex = $_REQUEST["signed_address"];
221                 $signed_address = hex2bin($signed_address_hex);
222
223                 $trusted_address = "";
224                 $decrypt_func($signed_address, $trusted_address, $key);
225
226                 $now = intval(time());
227                 PConfig::set($uid, "jappixmini", "id:$dfrn_id", "$now:$trusted_address");
228         } catch (Exception $e) {
229
230         }
231
232         // do not return an address if user deactivated plugin
233         $activated = PConfig::get($uid, 'jappixmini', 'activate');
234         if (!$activated) {
235                 killme();
236         }
237
238         // return the requested Jabber address
239         try {
240                 $username = PConfig::get($uid, 'jappixmini', 'username');
241                 $server = PConfig::get($uid, 'jappixmini', 'server');
242                 $address = "$username@$server";
243
244                 $encrypted_address = "";
245                 $encrypt_func($address, $encrypted_address, $key);
246
247                 $encrypted_address_hex = bin2hex($encrypted_address);
248
249                 $answer = Array(
250                         "status" => "ok",
251                         "encrypted_address" => $encrypted_address_hex
252                 );
253
254                 $answer_json = json_encode($answer);
255                 echo $answer_json;
256                 killme();
257         } catch (Exception $e) {
258                 killme();
259         }
260 }
261
262 function jappixmini_settings(App $a, &$s)
263 {
264         // addon settings for a user
265         $activate = PConfig::get(local_user(), 'jappixmini', 'activate');
266         $activate = intval($activate) ? ' checked="checked"' : '';
267         $dontinsertchat = PConfig::get(local_user(), 'jappixmini', 'dontinsertchat');
268         $insertchat = !(intval($dontinsertchat) ? ' checked="checked"' : '');
269
270         $defaultbosh = Config::get("jappixmini", "bosh_address");
271
272         if ($defaultbosh != "") {
273                 PConfig::set(local_user(), 'jappixmini', 'bosh', $defaultbosh);
274         }
275
276         $username = PConfig::get(local_user(), 'jappixmini', 'username');
277         $username = htmlentities($username);
278         $server = PConfig::get(local_user(), 'jappixmini', 'server');
279         $server = htmlentities($server);
280         $bosh = PConfig::get(local_user(), 'jappixmini', 'bosh');
281         $bosh = htmlentities($bosh);
282         $password = PConfig::get(local_user(), 'jappixmini', 'password');
283         $autosubscribe = PConfig::get(local_user(), 'jappixmini', 'autosubscribe');
284         $autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
285         $autoapprove = PConfig::get(local_user(), 'jappixmini', 'autoapprove');
286         $autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
287         $encrypt = intval(PConfig::get(local_user(), 'jappixmini', 'encrypt'));
288         $encrypt_checked = $encrypt ? ' checked="checked"' : '';
289         $encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
290
291         if ($server == "") {
292                 $server = Config::get("jappixmini", "default_server");
293         }
294
295         if (($username == "") && Config::get("jappixmini", "default_user")) {
296                 $username = $a->user["nickname"];
297         }
298
299         $info_text = Config::get("jappixmini", "infotext");
300         $info_text = htmlentities($info_text);
301         $info_text = str_replace("\n", "<br />", $info_text);
302
303         // count contacts
304         $r = q("SELECT COUNT(1) as `cnt` FROM `pconfig` WHERE `uid`=%d AND `cat`='jappixmini' AND `k` LIKE 'id:%%'", local_user());
305         if (count($r)) {
306                 $contact_cnt = $r[0]["cnt"];
307         } else {
308                 $contact_cnt = 0;
309         }
310
311         // count jabber addresses
312         $r = q("SELECT COUNT(1) as `cnt` FROM `pconfig` WHERE `uid`=%d AND `cat`='jappixmini' AND `k` LIKE 'id:%%' AND `v` LIKE '%%@%%'", local_user());
313         if (count($r)) {
314                 $address_cnt = $r[0]["cnt"];
315         } else {
316                 $address_cnt = 0;
317         }
318
319         if (!$activate) {
320                 // load scripts if not yet activated so that password can be saved
321                 $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;g=mini.xml"></script>' . "\r\n";
322                 $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=presence.js~caps.js~name.js~roster.js"></script>' . "\r\n";
323
324                 $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>' . "\r\n";
325         }
326
327         $s .= '<span id="settings_jappixmini_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_jappixmini_expanded\'); openClose(\'settings_jappixmini_inflated\');">';
328         $s .= '<h3>' . t('Jappix Mini') . '</h3>';
329         $s .= '</span>';
330         $s .= '<div id="settings_jappixmini_expanded" class="settings-block" style="display: none;">';
331         $s .= '<span class="fakelink" onclick="openClose(\'settings_jappixmini_expanded\'); openClose(\'settings_jappixmini_inflated\');">';
332         $s .= '<h3>' . t('Jappix Mini') . '</h3>';
333         $s .= '</span>';
334
335         $s .= '<label for="jappixmini-activate">' . t('Activate addon') . '</label>';
336         $s .= ' <input id="jappixmini-activate" type="checkbox" name="jappixmini-activate" value="1"' . $activate . ' />';
337         $s .= '<br />';
338         $s .= '<label for"jappixmini-dont-insertchat">' . t('Do <em>not</em> insert the Jappixmini Chat-Widget into the webinterface') . '</label>';
339         $s .= '<input id="jappixmini-dont-insertchat" type="checkbox" name="jappixmini-dont-insertchat" value="1"' . $insertchat . ' />';
340         $s .= '<br />';
341         $s .= '<label for="jappixmini-username">' . t('Jabber username') . '</label>';
342         $s .= ' <input id="jappixmini-username" type="text" name="jappixmini-username" value="' . $username . '" />';
343         $s .= '<br />';
344         $s .= '<label for="jappixmini-server">' . t('Jabber server') . '</label>';
345         $s .= ' <input id="jappixmini-server" type="text" name="jappixmini-server" value="' . $server . '" />';
346         $s .= '<br />';
347
348         if ($defaultbosh == "") {
349                 $s .= '<label for="jappixmini-bosh">' . t('Jabber BOSH host') . '</label>';
350                 $s .= ' <input id="jappixmini-bosh" type="text" name="jappixmini-bosh" value="' . $bosh . '" />';
351                 $s .= '<br />';
352         }
353
354         $s .= '<label for="jappixmini-password">' . t('Jabber password') . '</label>';
355         $s .= ' <input type="hidden" id="jappixmini-password" name="jappixmini-encrypted-password" value="' . $password . '" />';
356         $s .= ' <input id="jappixmini-clear-password" type="password" value="" onchange="jappixmini_set_password();" />';
357         $s .= '<br />';
358         $onchange = "document.getElementById('jappixmini-friendica-password').disabled = !this.checked;jappixmini_set_password();";
359         $s .= '<label for="jappixmini-encrypt">' . t('Encrypt Jabber password with Friendica password (recommended)') . '</label>';
360         $s .= ' <input id="jappixmini-encrypt" type="checkbox" name="jappixmini-encrypt" onchange="' . $onchange . '" value="1"' . $encrypt_checked . ' />';
361         $s .= '<br />';
362         $s .= '<label for="jappixmini-friendica-password">' . t('Friendica password') . '</label>';
363         $s .= ' <input id="jappixmini-friendica-password" name="jappixmini-friendica-password" type="password" onchange="jappixmini_set_password();" value=""' . $encrypt_disabled . ' />';
364         $s .= '<br />';
365         $s .= '<label for="jappixmini-autoapprove">' . t('Approve subscription requests from Friendica contacts automatically') . '</label>';
366         $s .= ' <input id="jappixmini-autoapprove" type="checkbox" name="jappixmini-autoapprove" value="1"' . $autoapprove . ' />';
367         $s .= '<br />';
368         $s .= '<label for="jappixmini-autosubscribe">' . t('Subscribe to Friendica contacts automatically') . '</label>';
369         $s .= ' <input id="jappixmini-autosubscribe" type="checkbox" name="jappixmini-autosubscribe" value="1"' . $autosubscribe . ' />';
370         $s .= '<br />';
371         $s .= '<label for="jappixmini-purge">' . t('Purge internal list of jabber addresses of contacts') . '</label>';
372         $s .= ' <input id="jappixmini-purge" type="checkbox" name="jappixmini-purge" value="1" />';
373         $s .= '<br />';
374         if ($info_text) {
375                 $s .= '<br />Configuration help:<p style="margin-left:2em;">' . $info_text . '</p>';
376         }
377         $s .= '<br />Status:<p style="margin-left:2em;">Addon knows ' . $address_cnt . ' Jabber addresses of ' . $contact_cnt . ' Friendica contacts (takes some time, usually 10 minutes, to update).</p>';
378         $s .= '<input type="submit" name="jappixmini-submit" value="' . t('Save Settings') . '" />';
379         $s .= ' <input type="button" value="' . t('Add contact') . '" onclick="jappixmini_addon_subscribe();" />';
380
381         $s .= '</div>';
382
383         $a->page['htmlhead'] .= "<script type=\"text/javascript\">
384         function jappixmini_set_password() {
385             encrypt = document.getElementById('jappixmini-encrypt').checked;
386             password = document.getElementById('jappixmini-password');
387             clear_password = document.getElementById('jappixmini-clear-password');
388             if (encrypt) {
389                 friendica_password = document.getElementById('jappixmini-friendica-password');
390
391                 if (friendica_password) {
392                     jappixmini_addon_set_client_secret(friendica_password.value);
393                     jappixmini_addon_encrypt_password(clear_password.value, function(encrypted_password){
394                         password.value = encrypted_password;
395                     });
396                 }
397             }
398             else {
399                 password.value = clear_password.value;
400             }
401         }
402
403         jQuery(document).ready(function() {
404             encrypt = document.getElementById('jappixmini-encrypt').checked;
405             password = document.getElementById('jappixmini-password');
406             clear_password = document.getElementById('jappixmini-clear-password');
407             if (encrypt) {
408                 jappixmini_addon_decrypt_password(password.value, function(decrypted_password){
409                     clear_password.value = decrypted_password;
410                 });
411             }
412             else {
413                 clear_password.value = password.value;
414             }
415         });
416     </script>";
417 }
418
419 function jappixmini_settings_post(App $a, &$b)
420 {
421         // save addon settings for a user
422         if (!local_user()) {
423                 return;
424         }
425         $uid = local_user();
426
427         if ($_POST['jappixmini-submit']) {
428                 $encrypt = intval($b['jappixmini-encrypt']);
429                 if ($encrypt) {
430                         // check that Jabber password was encrypted with correct Friendica password
431                         $friendica_password = trim($b['jappixmini-friendica-password']);
432                         $encrypted = hash('whirlpool',$friendica_password);
433                         $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
434                                 dbesc($encrypted)
435                         );
436                         if (!count($r)) {
437                                 info("Wrong friendica password!");
438                                 return;
439                         }
440                 }
441
442                 $purge = intval($b['jappixmini-purge']);
443
444                 $username = trim($b['jappixmini-username']);
445                 $old_username = PConfig::get($uid, 'jappixmini', 'username');
446                 if ($username != $old_username) {
447                         $purge = 1;
448                 }
449
450                 $server = trim($b['jappixmini-server']);
451                 $old_server = PConfig::get($uid, 'jappixmini', 'server');
452                 if ($server != $old_server) {
453                         $purge = 1;
454                 }
455
456                 PConfig::set($uid, 'jappixmini', 'username'      , $username);
457                 PConfig::set($uid, 'jappixmini', 'server'        , $server);
458                 PConfig::set($uid, 'jappixmini', 'bosh'          , trim($b['jappixmini-bosh']));
459                 PConfig::set($uid, 'jappixmini', 'password'      , trim($b['jappixmini-encrypted-password']));
460                 PConfig::set($uid, 'jappixmini', 'autosubscribe' , intval($b['jappixmini-autosubscribe']));
461                 PConfig::set($uid, 'jappixmini', 'autoapprove'   , intval($b['jappixmini-autoapprove']));
462                 PConfig::set($uid, 'jappixmini', 'activate'      , intval($b['jappixmini-activate']));
463                 PConfig::set($uid, 'jappixmini', 'dontinsertchat', intval($b['jappixmini-dont-insertchat']));
464                 PConfig::set($uid, 'jappixmini', 'encrypt'       , $encrypt);
465                 info('Jappix Mini settings saved.');
466
467                 if ($purge) {
468                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' AND `k` LIKE 'id:%%'");
469                         info('List of addresses purged.');
470                 }
471         }
472 }
473
474 function jappixmini_script(App $a)
475 {
476         // adds the script to the page header which starts Jappix Mini
477         if (!local_user()) {
478                 return;
479         }
480
481         if ($_GET["mode"] == "minimal") {
482                 return;
483         }
484
485         $activate = PConfig::get(local_user(), 'jappixmini', 'activate');
486         $dontinsertchat = PConfig::get(local_user(), 'jappixmini', 'dontinsertchat');
487         if (!$activate || $dontinsertchat) {
488                 return;
489         }
490
491         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;g=mini.xml"></script>' . "\r\n";
492         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=presence.js~caps.js~name.js~roster.js"></script>' . "\r\n";
493
494         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>' . "\r\n";
495
496         $username = PConfig::get(local_user(), 'jappixmini', 'username');
497         $username = str_replace("'", "\\'", $username);
498         $server = PConfig::get(local_user(), 'jappixmini', 'server');
499         $server = str_replace("'", "\\'", $server);
500         $bosh = PConfig::get(local_user(), 'jappixmini', 'bosh');
501         $bosh = str_replace("'", "\\'", $bosh);
502         $encrypt = PConfig::get(local_user(), 'jappixmini', 'encrypt');
503         $encrypt = intval($encrypt);
504         $password = PConfig::get(local_user(), 'jappixmini', 'password');
505         $password = str_replace("'", "\\'", $password);
506
507         $autoapprove = PConfig::get(local_user(), 'jappixmini', 'autoapprove');
508         $autoapprove = intval($autoapprove);
509         $autosubscribe = PConfig::get(local_user(), 'jappixmini', 'autosubscribe');
510         $autosubscribe = intval($autosubscribe);
511
512         // set proxy if necessary
513         $use_proxy = Config::get('jappixmini', 'bosh_proxy');
514         if ($use_proxy) {
515                 $proxy = $a->get_baseurl() . '/addon/jappixmini/proxy.php';
516         } else {
517                 $proxy = "";
518         }
519
520         // get a list of jabber accounts of the contacts
521         $contacts = Array();
522         $uid = local_user();
523         $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' AND `k` LIKE 'id:%%'");
524         foreach ($rows as $row) {
525                 $key = $row['k'];
526                 $pos = strpos($key, ":");
527                 $dfrn_id = substr($key, $pos + 1);
528                 $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND (`dfrn-id`='%s' OR `issued-id`='%s')", dbesc($dfrn_id), dbesc($dfrn_id));
529                 if (count($r))
530                         $name = $r[0]["name"];
531
532                 $value = $row['v'];
533                 $pos = strpos($value, ":");
534                 $address = substr($value, $pos + 1);
535                 if (!$address) {
536                         continue;
537                 }
538                 if (!$name) {
539                         $name = $address;
540                 }
541
542                 $contacts[$address] = $name;
543         }
544         $contacts_json = json_encode($contacts);
545         $contacts_hash = sha1($contacts_json);
546
547         // get nickname
548         $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
549         $nickname = json_encode($r[0]["username"]);
550         $groupchats = Config::get('jappixmini', 'groupchats');
551         //if $groupchats has no value jappix_addon_start will produce a syntax error
552         if (empty($groupchats)) {
553                 $groupchats = "{}";
554         }
555
556         // add javascript to start Jappix Mini
557         $a->page['htmlhead'] .= "<script type=\"text/javascript\">
558         jQuery(document).ready(function() {
559            jappixmini_addon_start('$server', '$username', '$proxy', '$bosh', $encrypt, '$password', $nickname, $contacts_json, '$contacts_hash', $autoapprove, $autosubscribe, $groupchats);
560         });
561     </script>";
562
563         return;
564 }
565
566 function jappixmini_login(App $a, &$o)
567 {
568         // create client secret on login to be able to encrypt jabber passwords
569         // for setDB and str_sha1, needed by jappixmini_addon_set_client_secret
570         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=datastore.js~jsjac.js"></script>' . "\r\n";
571
572         // for jappixmini_addon_set_client_secret
573         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>' . "\r\n";
574
575         // save hash of password
576         $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
577 }
578
579 function jappixmini_cron(App $a, $d)
580 {
581         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
582         Config::set("jappixmini", "last_cron_execution", $d);
583
584         // go through list of users with jabber enabled
585         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
586         logger("jappixmini: Update list of contacts' jabber accounts for " . count($users) . " users.");
587
588         if (!count($users)) {
589                 return;
590         }
591
592         foreach ($users as $row) {
593                 $uid = $row["uid"];
594
595                 // for each user, go through list of contacts
596                 $contacts = q("SELECT * FROM `contact` WHERE `uid`=%d AND ((LENGTH(`dfrn-id`) AND LENGTH(`pubkey`)) OR (LENGTH(`issued-id`) AND LENGTH(`prvkey`))) AND `network` = '%s'",
597                         intval($uid), dbesc(NETWORK_DFRN));
598                 foreach ($contacts as $contact_row) {
599                         $request = $contact_row["request"];
600                         if (!$request) {
601                                 continue;
602                         }
603
604                         $dfrn_id = $contact_row["dfrn-id"];
605                         if ($dfrn_id) {
606                                 $key = $contact_row["pubkey"];
607                                 $encrypt_func = openssl_public_encrypt;
608                                 $decrypt_func = openssl_public_decrypt;
609                                 $role = "prv";
610                         } else {
611                                 $dfrn_id = $contact_row["issued-id"];
612                                 $key = $contact_row["prvkey"];
613                                 $encrypt_func = openssl_private_encrypt;
614                                 $decrypt_func = openssl_private_decrypt;
615                                 $role = "pub";
616                         }
617
618                         // check if jabber address already present
619                         $present = PConfig::get($uid, "jappixmini", "id:" . $dfrn_id);
620                         $now = intval(time());
621                         if ($present) {
622                                 // $present has format "timestamp:jabber_address"
623                                 $p = strpos($present, ":");
624                                 $timestamp = intval(substr($present, 0, $p));
625
626                                 // do not re-retrieve jabber address if last retrieval
627                                 // is not older than a week
628                                 if ($now - $timestamp < 3600 * 24 * 7) {
629                                         continue;
630                                 }
631                         }
632
633                         // construct base retrieval address
634                         $pos = strpos($request, "/dfrn_request/");
635                         if ($pos === false) {
636                                 continue;
637                         }
638
639                         $base = substr($request, 0, $pos) . "/jappixmini?role=$role";
640
641                         // construct own address
642                         $username = PConfig::get($uid, 'jappixmini', 'username');
643                         if (!$username) {
644                                 continue;
645                         }
646                         $server = PConfig::get($uid, 'jappixmini', 'server');
647                         if (!$server) {
648                                 continue;
649                         }
650
651                         $address = $username . "@" . $server;
652
653                         // sign address
654                         $signed_address = "";
655                         $encrypt_func($address, $signed_address, $key);
656
657                         // construct request url
658                         $signed_address_hex = bin2hex($signed_address);
659                         $url = $base . "&signed_address=$signed_address_hex&dfrn_id=" . urlencode($dfrn_id);
660
661                         try {
662                                 // send request
663                                 $answer_json = fetch_url($url);
664
665                                 // parse answer
666                                 $answer = json_decode($answer_json);
667                                 if ($answer->status != "ok") {
668                                         throw new Exception();
669                                 }
670
671                                 $encrypted_address_hex = $answer->encrypted_address;
672                                 if (!$encrypted_address_hex) {
673                                         throw new Exception();
674                                 }
675
676                                 $encrypted_address = hex2bin($encrypted_address_hex);
677                                 if (!$encrypted_address) {
678                                         throw new Exception();
679                                 }
680
681                                 // decrypt address
682                                 $decrypted_address = "";
683                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
684                                 if (!$decrypted_address) {
685                                         throw new Exception();
686                                 }
687                         } catch (Exception $e) {
688                                 $decrypted_address = "";
689                         }
690
691                         // save address
692                         PConfig::set($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
693                 }
694         }
695 }
696
697 function jappixmini_download_source(App $a, &$b)
698 {
699         // Jappix Mini source download link on About page
700         $b .= '<h1>Jappix Mini</h1>';
701         $b .= '<p>This site uses the jappixmini addon, which includes Jappix Mini by the <a href="' . $a->get_baseurl() . '/addon/jappixmini/jappix/AUTHORS">Jappix authors</a> and is distributed under the terms of the <a href="' . $a->get_baseurl() . '/addon/jappixmini/jappix/COPYING">GNU Affero General Public License</a>.</p>';
702         $b .= '<p>You can download the <a href="' . $a->get_baseurl() . '/addon/jappixmini.tgz">source code of the addon</a>. The rest of Friendica is distributed under compatible licenses and can be retrieved from <a href="https://github.com/friendica/friendica">https://github.com/friendica/friendica</a> and <a href="https://github.com/friendica/friendica-addons">https://github.com/friendica/friendica-addons</a></p>';
703 }