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