]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappixmini.php
jappixmini: don't tell addresses to other servers if plugin deactivated
[friendica-addons.git] / jappixmini / jappixmini.php
1 <?php
2
3
4 /**
5 * Name: jappixmini
6 * Description: Inserts a jabber chat
7 * Version: 1.0
8 * Author: leberwurscht
9 *
10 */
11
12 /*
13
14 Problem:
15 * jabber password should not be stored on server
16 * jabber password should not be sent between server and browser as soon as the user is logged in
17 * jabber password should not be reconstructible from communication between server and browser as soon as the user is logged in
18
19 Solution:
20 Only store an encrypted version of the jabber password on the server. The encryption key is only available to the browser
21 and not to the server (at least as soon as the user is logged in). It can be stored using the jappix setDB function.
22
23 This encryption key could be the friendica password, but then this password would be stored in the browser in cleartext.
24 It is better to use a hash of the password.
25 The server should not be able to reconstruct the password, so we can't take the same hash the server stores. But we can
26  use hash("some_prefix"+password). This will however not work with OpenID logins, for this type of login the password must
27 be queried manually.
28
29 Problem:
30 How to discover the jabber addresses of the friendica contacts?
31
32 Solution:
33 Each Friendica site with this addon provides a /jappixmini/ module page. We go through our contacts and retrieve
34 this information every week using a cron hook.
35
36 Problem:
37 We do not want to make the jabber address public.
38
39 Solution:
40 When two friendica users connect using DFRN, the relation gets a DFRN ID and a keypair is generated.
41 Using this keypair, we can provide the jabber address only to contacts:
42
43 Alice:
44   signed_address = openssl_*_encrypt(alice_jabber_address)
45 send signed_address to Bob, who does
46   trusted_address = openssl_*_decrypt(signed_address)
47   save trusted_address
48   encrypted_address = openssl_*_encrypt(bob_jabber_address)
49 reply with encrypted_address to Alice, who does
50   decrypted_address = openssl_*_decrypt(encrypted_address)
51   save decrypted_address
52
53 Interface for this:
54 GET /jappixmini/?role=%s&signed_address=%s&dfrn_id=%s
55
56 Response:
57 json({"status":"ok", "encrypted_address":"%s"})
58
59 */
60
61 function jappixmini_install() {
62 register_hook('plugin_settings', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings');
63 register_hook('plugin_settings_post', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings_post');
64
65 register_hook('page_end', 'addon/jappixmini/jappixmini.php', 'jappixmini_script');
66 register_hook('authenticate', 'addon/jappixmini/jappixmini.php', 'jappixmini_login');
67
68 register_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
69
70 // Jappix source download as required by AGPL
71 register_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
72 }
73
74
75 function jappixmini_uninstall() {
76 unregister_hook('plugin_settings', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings');
77 unregister_hook('plugin_settings_post', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings_post');
78
79 unregister_hook('page_end', 'addon/jappixmini/jappixmini.php', 'jappixmini_script');
80 unregister_hook('authenticate', 'addon/jappixmini/jappixmini.php', 'jappixmini_login');
81
82 unregister_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
83
84 unregister_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
85 }
86
87 function jappixmini_plugin_admin(&$a, &$o) {
88         // display instructions and warnings on addon settings page for admin
89
90         if (!file_exists("addon/jappixmini/jappix")) {
91                 $o .= '<p><strong>You need to install the Jappix application, adapted for Friendica (see README).</strong></p>';
92         }
93         else if (!file_exists("addon/jappixmini/jappix.zip")) {
94                 $o .= '<p><strong style="color:#fff;background-color:#f00">The source archive jappix.zip does not exist. This is probably a violation of the Jappix License (see README).</strong></p>';
95         }
96         else {
97                 $o .= '<p>Jappix is installed.</p>';
98         }
99 }
100
101 function jappixmini_plugin_admin_post(&$a) {
102 }
103
104 function jappixmini_module() {}
105 function jappixmini_init(&$a) {
106         // module page where other Friendica sites can submit Jabber addresses to and also can query Jabber addresses
107         // of local users
108
109         if (!file_exists("addon/jappixmini/jappix")) killme();
110
111         $dfrn_id = $_REQUEST["dfrn_id"];
112         if (!$dfrn_id) killme();
113
114         $role = $_REQUEST["role"];
115         if ($role=="pub") {
116                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id`='%s' LIMIT 1",
117                         dbesc($dfrn_id)
118                 );
119                 if (!count($r)) killme();
120
121                 $encrypt_func = openssl_public_encrypt;
122                 $decrypt_func = openssl_public_decrypt;
123                 $key = $r[0]["pubkey"];
124         } else if ($role=="prv") {
125                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id`='%s' LIMIT 1",
126                         dbesc($dfrn_id)
127                 );
128                 if (!count($r)) killme();
129
130                 $encrypt_func = openssl_private_encrypt;
131                 $decrypt_func = openssl_private_decrypt;
132                 $key = $r[0]["prvkey"];
133         } else {
134                 killme();
135         }
136
137         $uid = $r[0]["uid"];
138
139         // save the Jabber address we received
140         try {
141                 $signed_address_hex = $_REQUEST["signed_address"];
142                 $signed_address = hex2bin($signed_address_hex);
143
144                 $trusted_address = "";
145                 $decrypt_func($signed_address, $trusted_address, $key);
146
147                 $now = intval(time());
148                 set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$trusted_address");
149         } catch (Exception $e) {
150         }
151
152         // do not return an address if user deactivated plugin
153         $activated = get_pconfig($uid, 'jappixmini', 'activate');
154         if (!$activated) killme();
155
156         // return the requested Jabber address
157         try {
158                 $username = get_pconfig($uid, 'jappixmini', 'username');
159                 $server = get_pconfig($uid, 'jappixmini', 'server');
160                 $address = "$username@$server";
161
162                 $encrypted_address = "";
163                 $encrypt_func($address, $encrypted_address, $key);
164
165                 $encrypted_address_hex = bin2hex($encrypted_address);
166
167                 $answer = Array(
168                         "status"=>"ok",
169                         "encrypted_address"=>$encrypted_address_hex
170                 );
171
172                 $answer_json = json_encode($answer);
173                 echo $answer_json;
174                 killme();
175         } catch (Exception $e) {
176                 killme();
177         }
178 }
179
180 function jappixmini_settings(&$a, &$s) {
181     // addon settings for a user
182
183     if (!file_exists("addon/jappixmini/jappix")) return;
184
185     $activate = get_pconfig(local_user(),'jappixmini','activate');
186     $activate = intval($activate) ? ' checked="checked"' : '';
187
188     $username = get_pconfig(local_user(),'jappixmini','username');
189     $username = htmlentities($username);
190     $server = get_pconfig(local_user(),'jappixmini','server');
191     $server = htmlentities($server);
192     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
193     $bosh = htmlentities($bosh);
194     $password = get_pconfig(local_user(),'jappixmini','password');
195     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
196     $autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
197     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
198     $autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
199     $encrypt = intval(get_pconfig(local_user(),'jappixmini','encrypt'));
200     $encrypt_checked = $encrypt ? ' checked="checked"' : '';
201     $encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
202
203     if (!$activate) {
204         // load scripts if not yet activated so that password can be saved
205         $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";
206         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=presence.js"></script>'."\r\n";
207
208         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=caps.js"></script>'."\r\n";
209         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=name.js"></script>'."\r\n";
210         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=roster.js"></script>'."\r\n";
211
212         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
213     }
214
215     $s .= '<div class="settings-block">';
216
217     $s .= '<h3>Jappix Mini addon settings</h3>';
218     $s .= '<div>';
219     $s .= '<label for="jappixmini-activate">Activate addon</label>';
220     $s .= ' <input id="jappixmini-activate" type="checkbox" name="jappixmini-activate" value="1"'.$activate.' />';
221     $s .= '<br />';
222     $s .= '<label for="jappixmini-username">Jabber username</label>';
223     $s .= ' <input id="jappixmini-username" type="text" name="jappixmini-username" value="'.$username.'" />';
224     $s .= '<br />';
225     $s .= '<label for="jappixmini-server">Jabber server</label>';
226     $s .= ' <input id="jappixmini-server" type="text" name="jappixmini-server" value="'.$server.'" />';
227     $s .= '<br />';
228
229     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
230     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
231     if ($matches[1]=="on") {
232         $s .= '<label for="jappixmini-bosh">Jabber BOSH host</label>';
233         $s .= ' <input id="jappixmini-bosh" type="text" name="jappixmini-bosh" value="'.$bosh.'" />';
234         $s .= '<br />';
235     }
236
237     $s .= '<label for="jappixmini-password">Jabber password</label>';
238     $s .= ' <input type="hidden" id="jappixmini-password" name="jappixmini-encrypted-password" value="'.$password.'" />';
239     $s .= ' <input id="jappixmini-clear-password" type="password" value="" onchange="jappixmini_set_password();" />';
240     $s .= '<br />';
241     $onchange = "document.getElementById('jappixmini-friendica-password').disabled = !this.checked;jappixmini_set_password();";
242     $s .= '<label for="jappixmini-encrypt">Encrypt Jabber password with Friendica password (recommended)</label>';
243     $s .= ' <input id="jappixmini-encrypt" type="checkbox" name="jappixmini-encrypt" onchange="'.$onchange.'" value="1"'.$encrypt_checked.' />';
244     $s .= '<br />';
245     $s .= '<label for="jappixmini-friendica-password">Friendica password</label>';
246     $s .= ' <input id="jappixmini-friendica-password" name="jappixmini-friendica-password" type="password" onchange="jappixmini_set_password();" value=""'.$encrypt_disabled.' />';
247     $s .= '<br />';
248     $s .= '<label for="jappixmini-autoapprove">Approve subscription requests from Friendica contacts automatically</label>';
249     $s .= ' <input id="jappixmini-autoapprove" type="checkbox" name="jappixmini-autoapprove" value="1"'.$autoapprove.' />';
250     $s .= '<br />';
251     $s .= '<label for="jappixmini-autosubscribe">Subscribe to Friendica contacts automatically</label>';
252     $s .= ' <input id="jappixmini-autosubscribe" type="checkbox" name="jappixmini-autosubscribe" value="1"'.$autosubscribe.' />';
253     $s .= '<br />';
254     $s .= '<label for="jappixmini-purge">Purge internal list of jabber addresses of contacts</label>';
255     $s .= ' <input id="jappixmini-purge" type="checkbox" name="jappixmini-purge" value="1" />';
256     $s .= '<br />';
257     $s .= '<input type="submit" name="jappixmini-submit" value="' . t('Submit') . '" />';
258     $s .= ' <input type="button" value="Add contact" onclick="jappixmini_addon_subscribe();" />';
259     $s .= '</div>';
260
261     $s .= '</div>';
262
263     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
264         function jappixmini_set_password() {
265             encrypt = document.getElementById('jappixmini-encrypt').checked;
266             password = document.getElementById('jappixmini-password');
267             clear_password = document.getElementById('jappixmini-clear-password');
268             if (encrypt) {
269                 friendica_password = document.getElementById('jappixmini-friendica-password');
270
271                 if (friendica_password) {
272                     jappixmini_addon_set_client_secret(friendica_password.value);
273                     password.value = jappixmini_addon_encrypt_password(clear_password.value);
274                 }
275             }
276             else {
277                 password.value = clear_password.value;
278             }
279         }
280
281         jQuery(document).ready(function() {
282             encrypt = document.getElementById('jappixmini-encrypt').checked;
283             password = document.getElementById('jappixmini-password');
284             clear_password = document.getElementById('jappixmini-clear-password');
285             if (encrypt) {
286                 clear_password.value = jappixmini_addon_decrypt_password(password.value);
287             }
288             else {
289                 clear_password.value = password.value;
290             }
291         });
292     </script>";
293 }
294
295 function jappixmini_settings_post(&$a,&$b) {
296         // save addon settings for a user
297
298         if (!file_exists("addon/jappixmini/jappix")) return;
299
300         if(! local_user()) return;
301         $uid = local_user();
302
303         if($_POST['jappixmini-submit']) {
304                 $encrypt = intval($b['jappixmini-encrypt']);
305                 if ($encrypt) {
306                         // check that Jabber password was encrypted with correct Friendica password
307                         $friendica_password = trim($b['jappixmini-friendica-password']);
308                         $encrypted = hash('whirlpool',$friendica_password);
309                         $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
310                                 dbesc($encrypted)
311                         );
312                         if (!count($r)) {
313                                 info("Wrong friendica password!");
314                                 return;
315                         }
316                 }
317
318                 $purge = intval($b['jappixmini-purge']);
319
320                 $username = trim($b['jappixmini-username']);
321                 $old_username = get_pconfig($uid,'jappixmini','username');
322                 if ($username!=$old_username) $purge = 1;
323
324                 $server = trim($b['jappixmini-server']);
325                 $old_server = get_pconfig($uid,'jappixmini','server');
326                 if ($server!=$old_server) $purge = 1;
327
328                 set_pconfig($uid,'jappixmini','username',$username);
329                 set_pconfig($uid,'jappixmini','server',$server);
330                 set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
331                 set_pconfig($uid,'jappixmini','password',trim($b['jappixmini-encrypted-password']));
332                 set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
333                 set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
334                 set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
335                 set_pconfig($uid,'jappixmini','encrypt',$encrypt);
336                 info( 'Jappix Mini settings saved.' );
337
338                 if ($purge) {
339                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
340                         info( 'List of addresses purged.' );
341                 }
342         }
343 }
344
345 function jappixmini_script(&$a,&$s) {
346     // adds the script to the page header which starts Jappix Mini
347
348     if (!file_exists("addon/jappixmini/jappix")) return;
349     if(! local_user()) return;
350
351     $activate = get_pconfig(local_user(),'jappixmini','activate');
352     if (!$activate) return;
353
354     $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";
355     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=presence.js"></script>'."\r\n";
356
357     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=caps.js"></script>'."\r\n";
358     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=name.js"></script>'."\r\n";
359     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=roster.js"></script>'."\r\n";
360
361     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
362
363     $username = get_pconfig(local_user(),'jappixmini','username');
364     $username = str_replace("'", "\\'", $username);
365     $server = get_pconfig(local_user(),'jappixmini','server');
366     $server = str_replace("'", "\\'", $server);
367     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
368     $bosh = str_replace("'", "\\'", $bosh);
369     $encrypt = get_pconfig(local_user(),'jappixmini','encrypt');
370     $encrypt = intval($encrypt);
371     $password = get_pconfig(local_user(),'jappixmini','password');
372     $password = str_replace("'", "\\'", $password);
373
374     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
375     $autoapprove = intval($autoapprove);
376     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
377     $autosubscribe = intval($autosubscribe);
378
379     // deactivate bosh host if proxy is off
380     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
381     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
382     if ($matches[1]!="on") {
383         $bosh = '';
384     }
385
386     // get a list of jabber accounts of the contacts
387     $contacts = Array();
388     $uid = local_user();
389     $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
390     foreach ($rows as $row) {
391         $key = $row['k'];
392         $pos = strpos($key, ":");
393         $dfrn_id = substr($key, $pos+1);
394         $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND `dfrn-id`='%s' OR `issued-id`='%s'",
395                 dbesc($dfrn_id),
396                 dbesc($dfrn_id)
397         );
398         $name = $r[0]["name"];
399
400         $value = $row['v'];
401         $pos = strpos($value, ":");
402         $address = substr($value, $pos+1);
403         if (!$address) continue;
404         if (!$name) $name = $address;
405
406         $contacts[$address] = $name;
407     }
408     $contacts_json = json_encode($contacts);
409
410     // get nickname
411     $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
412     $nickname = json_encode($r[0]["username"]);
413
414     // add javascript to start Jappix Mini
415     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
416         jQuery(document).ready(function() {
417            jappixmini_addon_start('$server', '$username', '$bosh', $encrypt, '$password', $nickname);
418            jappixmini_manage_roster($contacts_json, $autoapprove, $autosubscribe);
419         });
420     </script>";
421
422     return;
423 }
424
425 function jappixmini_login(&$a, &$o) {
426     // create client secret on login to be able to encrypt jabber passwords
427
428     if (!file_exists("addon/jappixmini/jappix")) return;
429
430     // for setDB, needed by jappixmini_addon_set_client_secret
431     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=datastore.js"></script>'."\r\n";
432
433     // for str_sha1, needed by jappixmini_addon_set_client_secret
434     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/jappix/php/get.php?t=js&amp;f=jsjac.js"></script>'."\r\n";
435
436     // for jappixmini_addon_set_client_secret
437     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
438
439     // save hash of password
440     $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
441 }
442
443 function jappixmini_cron(&$a, $d) {
444         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
445
446         if (!file_exists("addon/jappixmini/jappix")) return;
447
448         // go through list of users with jabber enabled
449         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
450
451         foreach ($users as $row) {
452                 $uid = $row["uid"];
453
454                 // for each user, go through list of contacts
455                 $contacts = q("SELECT * FROM `contact` WHERE `uid`=%d AND ((LENGTH(`dfrn-id`) AND LENGTH(`pubkey`)) OR (LENGTH(`issued-id`) AND LENGTH(`prvkey`)))", intval($uid));
456                 foreach ($contacts as $contact_row) {
457                         $request = $contact_row["request"];
458                         if (!$request) continue;
459
460                         $dfrn_id = $contact_row["dfrn-id"];
461                         if ($dfrn_id) {
462                                 $key = $contact_row["pubkey"];
463                                 $encrypt_func = openssl_public_encrypt;
464                                 $decrypt_func = openssl_public_decrypt;
465                                 $role = "prv";
466                         } else {
467                                 $dfrn_id = $contact_row["issued-id"];
468                                 $key = $contact_row["prvkey"];
469                                 $encrypt_func = openssl_private_encrypt;
470                                 $decrypt_func = openssl_private_decrypt;
471                                 $role = "pub";
472                         }
473
474                         // check if jabber address already present
475                         $present = get_pconfig($uid, "jappixmini", "id:".$dfrn_id);
476                         $now = intval(time());
477                         if ($present) {
478                                 // $present has format "timestamp:jabber_address"
479                                 $p = strpos($present, ":");
480                                 $timestamp = intval(substr($present, 0, $p));
481
482                                 // do not re-retrieve jabber address if last retrieval
483                                 // is not older than a week
484                                 if ($now-$timestamp<3600*24*7) continue;
485                         }
486
487                         // construct base retrieval address
488                         $pos = strpos($request, "/dfrn_request/");
489                         if ($pos===false) continue;
490
491                         $base = substr($request, 0, $pos)."/jappixmini?role=$role";
492
493                         // construct own address
494                         $username = get_pconfig($uid, 'jappixmini', 'username');
495                         if (!$username) continue;
496                         $server = get_pconfig($uid, 'jappixmini', 'server');
497                         if (!$server) continue;
498
499                         $address = $username."@".$server;
500
501                         // sign address
502                         $signed_address = "";
503                         $encrypt_func($address, $signed_address, $key);
504
505                         // construct request url
506                         $signed_address_hex = bin2hex($signed_address);
507                         $url = $base."&signed_address=$signed_address_hex&dfrn_id=".urlencode($dfrn_id);
508
509                         try {
510                                 // send request
511                                 $answer_json = fetch_url($url);
512
513                                 // parse answer
514                                 $answer = json_decode($answer_json);
515                                 if ($answer->status != "ok") throw new Exception();
516
517                                 $encrypted_address_hex = $answer->encrypted_address;
518                                 if (!$encrypted_address_hex) throw new Exception();
519
520                                 $encrypted_address = hex2bin($encrypted_address_hex);
521                                 if (!$encrypted_address) throw new Exception();
522
523                                 // decrypt address
524                                 $decrypted_address = "";
525                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
526                                 if (!$decrypted_address) throw new Exception();
527                         } catch (Exception $e) {
528                                 $decrypted_address = "";
529                         }
530
531                         // save address
532                         set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
533                 }
534         }
535 }
536
537 function jappixmini_download_source(&$a,&$b) {
538         // Jappix Mini source download link on About page
539
540         if (!file_exists("addon/jappixmini/jappix")) return;
541
542         $b .= '<h1>Jappix Mini</h1>';
543         $b .= '<p>This site uses Jappix Mini by the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix/AUTHORS">Jappix authors</a>, which is distributed under the terms of the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix/COPYING">GNU Affero General Public License</a>.</p>';
544         $b .= '<p>You can download the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix.zip">source code</a>.</p>';
545 }