Use short form array syntax everywhere
[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 = [
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                         if (!User::authenticate((int) $uid, $friendica_password)) {
433                                 info("Wrong friendica password!");
434                                 return;
435                         }
436                 }
437
438                 $purge = intval($b['jappixmini-purge']);
439
440                 $username = trim($b['jappixmini-username']);
441                 $old_username = PConfig::get($uid, 'jappixmini', 'username');
442                 if ($username != $old_username) {
443                         $purge = 1;
444                 }
445
446                 $server = trim($b['jappixmini-server']);
447                 $old_server = PConfig::get($uid, 'jappixmini', 'server');
448                 if ($server != $old_server) {
449                         $purge = 1;
450                 }
451
452                 PConfig::set($uid, 'jappixmini', 'username'      , $username);
453                 PConfig::set($uid, 'jappixmini', 'server'        , $server);
454                 PConfig::set($uid, 'jappixmini', 'bosh'          , trim($b['jappixmini-bosh']));
455                 PConfig::set($uid, 'jappixmini', 'password'      , trim($b['jappixmini-encrypted-password']));
456                 PConfig::set($uid, 'jappixmini', 'autosubscribe' , intval($b['jappixmini-autosubscribe']));
457                 PConfig::set($uid, 'jappixmini', 'autoapprove'   , intval($b['jappixmini-autoapprove']));
458                 PConfig::set($uid, 'jappixmini', 'activate'      , intval($b['jappixmini-activate']));
459                 PConfig::set($uid, 'jappixmini', 'dontinsertchat', intval($b['jappixmini-dont-insertchat']));
460                 PConfig::set($uid, 'jappixmini', 'encrypt'       , $encrypt);
461                 info('Jappix Mini settings saved.');
462
463                 if ($purge) {
464                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' AND `k` LIKE 'id:%%'");
465                         info('List of addresses purged.');
466                 }
467         }
468 }
469
470 function jappixmini_script(App $a)
471 {
472         // adds the script to the page header which starts Jappix Mini
473         if (!local_user()) {
474                 return;
475         }
476
477         if ($_GET["mode"] == "minimal") {
478                 return;
479         }
480
481         $activate = PConfig::get(local_user(), 'jappixmini', 'activate');
482         $dontinsertchat = PConfig::get(local_user(), 'jappixmini', 'dontinsertchat');
483         if (!$activate || $dontinsertchat) {
484                 return;
485         }
486
487         $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";
488         $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";
489
490         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>' . "\r\n";
491
492         $username = PConfig::get(local_user(), 'jappixmini', 'username');
493         $username = str_replace("'", "\\'", $username);
494         $server = PConfig::get(local_user(), 'jappixmini', 'server');
495         $server = str_replace("'", "\\'", $server);
496         $bosh = PConfig::get(local_user(), 'jappixmini', 'bosh');
497         $bosh = str_replace("'", "\\'", $bosh);
498         $encrypt = PConfig::get(local_user(), 'jappixmini', 'encrypt');
499         $encrypt = intval($encrypt);
500         $password = PConfig::get(local_user(), 'jappixmini', 'password');
501         $password = str_replace("'", "\\'", $password);
502
503         $autoapprove = PConfig::get(local_user(), 'jappixmini', 'autoapprove');
504         $autoapprove = intval($autoapprove);
505         $autosubscribe = PConfig::get(local_user(), 'jappixmini', 'autosubscribe');
506         $autosubscribe = intval($autosubscribe);
507
508         // set proxy if necessary
509         $use_proxy = Config::get('jappixmini', 'bosh_proxy');
510         if ($use_proxy) {
511                 $proxy = $a->get_baseurl() . '/addon/jappixmini/proxy.php';
512         } else {
513                 $proxy = "";
514         }
515
516         // get a list of jabber accounts of the contacts
517         $contacts = [];
518         $uid = local_user();
519         $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' AND `k` LIKE 'id:%%'");
520         foreach ($rows as $row) {
521                 $key = $row['k'];
522                 $pos = strpos($key, ":");
523                 $dfrn_id = substr($key, $pos + 1);
524                 $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND (`dfrn-id`='%s' OR `issued-id`='%s')", dbesc($dfrn_id), dbesc($dfrn_id));
525                 if (count($r))
526                         $name = $r[0]["name"];
527
528                 $value = $row['v'];
529                 $pos = strpos($value, ":");
530                 $address = substr($value, $pos + 1);
531                 if (!$address) {
532                         continue;
533                 }
534                 if (!$name) {
535                         $name = $address;
536                 }
537
538                 $contacts[$address] = $name;
539         }
540         $contacts_json = json_encode($contacts);
541         $contacts_hash = sha1($contacts_json);
542
543         // get nickname
544         $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
545         $nickname = json_encode($r[0]["username"]);
546         $groupchats = Config::get('jappixmini', 'groupchats');
547         //if $groupchats has no value jappix_addon_start will produce a syntax error
548         if (empty($groupchats)) {
549                 $groupchats = "{}";
550         }
551
552         // add javascript to start Jappix Mini
553         $a->page['htmlhead'] .= "<script type=\"text/javascript\">
554         jQuery(document).ready(function() {
555            jappixmini_addon_start('$server', '$username', '$proxy', '$bosh', $encrypt, '$password', $nickname, $contacts_json, '$contacts_hash', $autoapprove, $autosubscribe, $groupchats);
556         });
557     </script>";
558
559         return;
560 }
561
562 function jappixmini_login(App $a, &$o)
563 {
564         // create client secret on login to be able to encrypt jabber passwords
565         // for setDB and str_sha1, needed by jappixmini_addon_set_client_secret
566         $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";
567
568         // for jappixmini_addon_set_client_secret
569         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>' . "\r\n";
570
571         // save hash of password
572         $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
573 }
574
575 function jappixmini_cron(App $a, $d)
576 {
577         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
578         Config::set("jappixmini", "last_cron_execution", $d);
579
580         // go through list of users with jabber enabled
581         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
582         logger("jappixmini: Update list of contacts' jabber accounts for " . count($users) . " users.");
583
584         if (!count($users)) {
585                 return;
586         }
587
588         foreach ($users as $row) {
589                 $uid = $row["uid"];
590
591                 // for each user, go through list of contacts
592                 $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'",
593                         intval($uid), dbesc(NETWORK_DFRN));
594                 foreach ($contacts as $contact_row) {
595                         $request = $contact_row["request"];
596                         if (!$request) {
597                                 continue;
598                         }
599
600                         $dfrn_id = $contact_row["dfrn-id"];
601                         if ($dfrn_id) {
602                                 $key = $contact_row["pubkey"];
603                                 $encrypt_func = openssl_public_encrypt;
604                                 $decrypt_func = openssl_public_decrypt;
605                                 $role = "prv";
606                         } else {
607                                 $dfrn_id = $contact_row["issued-id"];
608                                 $key = $contact_row["prvkey"];
609                                 $encrypt_func = openssl_private_encrypt;
610                                 $decrypt_func = openssl_private_decrypt;
611                                 $role = "pub";
612                         }
613
614                         // check if jabber address already present
615                         $present = PConfig::get($uid, "jappixmini", "id:" . $dfrn_id);
616                         $now = intval(time());
617                         if ($present) {
618                                 // $present has format "timestamp:jabber_address"
619                                 $p = strpos($present, ":");
620                                 $timestamp = intval(substr($present, 0, $p));
621
622                                 // do not re-retrieve jabber address if last retrieval
623                                 // is not older than a week
624                                 if ($now - $timestamp < 3600 * 24 * 7) {
625                                         continue;
626                                 }
627                         }
628
629                         // construct base retrieval address
630                         $pos = strpos($request, "/dfrn_request/");
631                         if ($pos === false) {
632                                 continue;
633                         }
634
635                         $base = substr($request, 0, $pos) . "/jappixmini?role=$role";
636
637                         // construct own address
638                         $username = PConfig::get($uid, 'jappixmini', 'username');
639                         if (!$username) {
640                                 continue;
641                         }
642                         $server = PConfig::get($uid, 'jappixmini', 'server');
643                         if (!$server) {
644                                 continue;
645                         }
646
647                         $address = $username . "@" . $server;
648
649                         // sign address
650                         $signed_address = "";
651                         $encrypt_func($address, $signed_address, $key);
652
653                         // construct request url
654                         $signed_address_hex = bin2hex($signed_address);
655                         $url = $base . "&signed_address=$signed_address_hex&dfrn_id=" . urlencode($dfrn_id);
656
657                         try {
658                                 // send request
659                                 $answer_json = fetch_url($url);
660
661                                 // parse answer
662                                 $answer = json_decode($answer_json);
663                                 if ($answer->status != "ok") {
664                                         throw new Exception();
665                                 }
666
667                                 $encrypted_address_hex = $answer->encrypted_address;
668                                 if (!$encrypted_address_hex) {
669                                         throw new Exception();
670                                 }
671
672                                 $encrypted_address = hex2bin($encrypted_address_hex);
673                                 if (!$encrypted_address) {
674                                         throw new Exception();
675                                 }
676
677                                 // decrypt address
678                                 $decrypted_address = "";
679                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
680                                 if (!$decrypted_address) {
681                                         throw new Exception();
682                                 }
683                         } catch (Exception $e) {
684                                 $decrypted_address = "";
685                         }
686
687                         // save address
688                         PConfig::set($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
689                 }
690         }
691 }
692
693 function jappixmini_download_source(App $a, &$b)
694 {
695         // Jappix Mini source download link on About page
696         $b .= '<h1>Jappix Mini</h1>';
697         $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>';
698         $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>';
699 }