]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappixmini.php
add missing </div> to jappixmini settings
[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         // return the requested Jabber address
153         try {
154                 $username = get_pconfig($uid, 'jappixmini', 'username');
155                 $server = get_pconfig($uid, 'jappixmini', 'server');
156                 $address = "$username@$server";
157
158                 $encrypted_address = "";
159                 $encrypt_func($address, $encrypted_address, $key);
160
161                 $encrypted_address_hex = bin2hex($encrypted_address);
162
163                 $answer = Array(
164                         "status"=>"ok",
165                         "encrypted_address"=>$encrypted_address_hex
166                 );
167
168                 $answer_json = json_encode($answer);
169                 echo $answer_json;
170                 killme();
171         } catch (Exception $e) {
172                 killme();
173         }
174 }
175
176 function jappixmini_settings(&$a, &$s) {
177     // addon settings for a user
178
179     if (!file_exists("addon/jappixmini/jappix")) return;
180
181     $activate = get_pconfig(local_user(),'jappixmini','activate');
182     $activate = intval($activate) ? ' checked="checked"' : '';
183
184     $username = get_pconfig(local_user(),'jappixmini','username');
185     $username = htmlentities($username);
186     $server = get_pconfig(local_user(),'jappixmini','server');
187     $server = htmlentities($server);
188     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
189     $bosh = htmlentities($bosh);
190     $password = get_pconfig(local_user(),'jappixmini','password');
191     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
192     $autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
193     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
194     $autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
195     $encrypt = intval(get_pconfig(local_user(),'jappixmini','encrypt'));
196     $encrypt_checked = $encrypt ? ' checked="checked"' : '';
197     $encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
198
199     if (!$activate) {
200         // load scripts if not yet activated so that password can be saved
201         $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";
202         $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";
203
204         $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";
205         $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";
206         $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";
207
208         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
209     }
210
211     $s .= '<div class="settings-block">';
212
213     $s .= '<h3>Jappix Mini addon settings</h3>';
214     $s .= '<div>';
215     $s .= '<label for="jappixmini-activate">Activate addon</label>';
216     $s .= ' <input id="jappixmini-activate" type="checkbox" name="jappixmini-activate" value="1"'.$activate.' />';
217     $s .= '<br />';
218     $s .= '<label for="jappixmini-username">Jabber username</label>';
219     $s .= ' <input id="jappixmini-username" type="text" name="jappixmini-username" value="'.$username.'" />';
220     $s .= '<br />';
221     $s .= '<label for="jappixmini-server">Jabber server</label>';
222     $s .= ' <input id="jappixmini-server" type="text" name="jappixmini-server" value="'.$server.'" />';
223     $s .= '<br />';
224
225     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
226     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
227     if ($matches[1]=="on") {
228         $s .= '<label for="jappixmini-bosh">Jabber BOSH host</label>';
229         $s .= ' <input id="jappixmini-bosh" type="text" name="jappixmini-bosh" value="'.$bosh.'" />';
230         $s .= '<br />';
231     }
232
233     $s .= '<label for="jappixmini-password">Jabber password</label>';
234     $s .= ' <input type="hidden" id="jappixmini-password" name="jappixmini-encrypted-password" value="'.$password.'" />';
235     $s .= ' <input id="jappixmini-clear-password" type="password" value="" onchange="jappixmini_set_password();" />';
236     $s .= '<br />';
237     $onchange = "document.getElementById('jappixmini-friendica-password').disabled = !this.checked;jappixmini_set_password();";
238     $s .= '<label for="jappixmini-encrypt">Encrypt Jabber password with Friendica password (recommended)</label>';
239     $s .= ' <input id="jappixmini-encrypt" type="checkbox" name="jappixmini-encrypt" onchange="'.$onchange.'" value="1"'.$encrypt_checked.' />';
240     $s .= '<br />';
241     $s .= '<label for="jappixmini-friendica-password">Friendica password</label>';
242     $s .= ' <input id="jappixmini-friendica-password" name="jappixmini-friendica-password" type="password" onchange="jappixmini_set_password();" value=""'.$encrypt_disabled.' />';
243     $s .= '<br />';
244     $s .= '<label for="jappixmini-autoapprove">Approve subscription requests from Friendica contacts automatically</label>';
245     $s .= ' <input id="jappixmini-autoapprove" type="checkbox" name="jappixmini-autoapprove" value="1"'.$autoapprove.' />';
246     $s .= '<br />';
247     $s .= '<label for="jappixmini-autosubscribe">Subscribe to Friendica contacts automatically</label>';
248     $s .= ' <input id="jappixmini-autosubscribe" type="checkbox" name="jappixmini-autosubscribe" value="1"'.$autosubscribe.' />';
249     $s .= '<br />';
250     $s .= '<label for="jappixmini-purge">Purge internal list of jabber addresses of contacts</label>';
251     $s .= ' <input id="jappixmini-purge" type="checkbox" name="jappixmini-purge" value="1" />';
252     $s .= '<br />';
253     $s .= '<input type="submit" name="jappixmini-submit" value="' . t('Submit') . '" />';
254     $s .= ' <input type="button" value="Add contact" onclick="jappixmini_addon_subscribe();" />';
255     $s .= '</div>';
256
257     $s .= '</div>';
258
259     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
260         function jappixmini_set_password() {
261             encrypt = document.getElementById('jappixmini-encrypt').checked;
262             password = document.getElementById('jappixmini-password');
263             clear_password = document.getElementById('jappixmini-clear-password');
264             if (encrypt) {
265                 friendica_password = document.getElementById('jappixmini-friendica-password');
266                 jappixmini_addon_set_client_secret(friendica_password.value);
267                 password.value = jappixmini_addon_encrypt_password(clear_password.value);
268             }
269             else {
270                 password.value = clear_password.value;
271             }
272         }
273
274         jQuery(document).ready(function() {
275             encrypt = document.getElementById('jappixmini-encrypt').checked;
276             password = document.getElementById('jappixmini-password');
277             clear_password = document.getElementById('jappixmini-clear-password');
278             if (encrypt) {
279                 clear_password.value = jappixmini_addon_decrypt_password(password.value);
280             }
281             else {
282                 clear_password.value = password.value;
283             }
284         });
285     </script>";
286 }
287
288 function jappixmini_settings_post(&$a,&$b) {
289         // save addon settings for a user
290
291         if (!file_exists("addon/jappixmini/jappix")) return;
292
293         if(! local_user()) return;
294         $uid = local_user();
295
296         if($_POST['jappixmini-submit']) {
297                 $encrypt = intval($b['jappixmini-encrypt']);
298                 if ($encrypt) {
299                         // check that Jabber password was encrypted with correct Friendica password
300                         $friendica_password = trim($b['jappixmini-friendica-password']);
301                         $encrypted = hash('whirlpool',$friendica_password);
302                         $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
303                                 dbesc($encrypted)
304                         );
305                         if (!count($r)) {
306                                 info("Wrong friendica password!");
307                                 return;
308                         }
309                 }
310
311                 $purge = intval($b['jappixmini-purge']);
312
313                 $username = trim($b['jappixmini-username']);
314                 $old_username = get_pconfig($uid,'jappixmini','username');
315                 if ($username!=$old_username) $purge = 1;
316
317                 $server = trim($b['jappixmini-server']);
318                 $old_server = get_pconfig($uid,'jappixmini','server');
319                 if ($server!=$old_server) $purge = 1;
320
321                 set_pconfig($uid,'jappixmini','username',$username);
322                 set_pconfig($uid,'jappixmini','server',$server);
323                 set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
324                 set_pconfig($uid,'jappixmini','password',trim($b['jappixmini-encrypted-password']));
325                 set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
326                 set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
327                 set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
328                 set_pconfig($uid,'jappixmini','encrypt',$encrypt);
329                 info( 'Jappix Mini settings saved.' );
330
331                 if ($purge) {
332                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
333                         info( 'List of addresses purged.' );
334                 }
335         }
336 }
337
338 function jappixmini_script(&$a,&$s) {
339     // adds the script to the page header which starts Jappix Mini
340
341     if (!file_exists("addon/jappixmini/jappix")) return;
342     if(! local_user()) return;
343
344     $activate = get_pconfig(local_user(),'jappixmini','activate');
345     if (!$activate) return;
346
347     $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";
348     $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";
349
350     $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";
351     $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";
352     $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";
353
354     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
355
356     $username = get_pconfig(local_user(),'jappixmini','username');
357     $username = str_replace("'", "\\'", $username);
358     $server = get_pconfig(local_user(),'jappixmini','server');
359     $server = str_replace("'", "\\'", $server);
360     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
361     $bosh = str_replace("'", "\\'", $bosh);
362     $encrypt = get_pconfig(local_user(),'jappixmini','encrypt');
363     $encrypt = intval($encrypt);
364     $password = get_pconfig(local_user(),'jappixmini','password');
365     $password = str_replace("'", "\\'", $password);
366
367     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
368     $autoapprove = intval($autoapprove);
369     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
370     $autosubscribe = intval($autosubscribe);
371
372     // deactivate bosh host if proxy is off
373     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
374     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
375     if ($matches[1]!="on") {
376         $bosh = '';
377     }
378
379     // get a list of jabber accounts of the contacts
380     $contacts = Array();
381     $uid = local_user();
382     $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
383     foreach ($rows as $row) {
384         $key = $row['k'];
385         $pos = strpos($key, ":");
386         $dfrn_id = substr($key, $pos+1);
387         $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND `dfrn-id`='%s' OR `issued-id`='%s'",
388                 dbesc($dfrn_id),
389                 dbesc($dfrn_id)
390         );
391         $name = $r[0]["name"];
392
393         $value = $row['v'];
394         $pos = strpos($value, ":");
395         $address = substr($value, $pos+1);
396         if (!$address) continue;
397         if (!$name) $name = $address;
398
399         $contacts[$address] = $name;
400     }
401     $contacts_json = json_encode($contacts);
402
403     // get nickname
404     $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
405     $nickname = json_encode($r[0]["username"]);
406
407     // add javascript to start Jappix Mini
408     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
409         jQuery(document).ready(function() {
410            jappixmini_addon_start('$server', '$username', '$bosh', $encrypt, '$password', $nickname);
411            jappixmini_manage_roster($contacts_json, $autoapprove, $autosubscribe);
412         });
413     </script>";
414
415     return;
416 }
417
418 function jappixmini_login(&$a, &$o) {
419     // create client secret on login to be able to encrypt jabber passwords
420
421     if (!file_exists("addon/jappixmini/jappix")) return;
422
423     // for setDB, needed by jappixmini_addon_set_client_secret
424     $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";
425
426     // for str_sha1, needed by jappixmini_addon_set_client_secret
427     $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";
428
429     // for jappixmini_addon_set_client_secret
430     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
431
432     // save hash of password
433     $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
434 }
435
436 function jappixmini_cron(&$a, $d) {
437         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
438
439         if (!file_exists("addon/jappixmini/jappix")) return;
440
441         // go through list of users with jabber enabled
442         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
443
444         foreach ($users as $row) {
445                 $uid = $row["uid"];
446
447                 // for each user, go through list of contacts
448                 $contacts = q("SELECT * FROM `contact` WHERE `uid`=%d AND ((LENGTH(`dfrn-id`) AND LENGTH(`pubkey`)) OR (LENGTH(`issued-id`) AND LENGTH(`prvkey`)))", intval($uid));
449                 foreach ($contacts as $contact_row) {
450                         $request = $contact_row["request"];
451                         if (!$request) continue;
452
453                         $dfrn_id = $contact_row["dfrn-id"];
454                         if ($dfrn_id) {
455                                 $key = $contact_row["pubkey"];
456                                 $encrypt_func = openssl_public_encrypt;
457                                 $decrypt_func = openssl_public_decrypt;
458                                 $role = "prv";
459                         } else {
460                                 $dfrn_id = $contact_row["issued-id"];
461                                 $key = $contact_row["prvkey"];
462                                 $encrypt_func = openssl_private_encrypt;
463                                 $decrypt_func = openssl_private_decrypt;
464                                 $role = "pub";
465                         }
466
467                         // check if jabber address already present
468                         $present = get_pconfig($uid, "jappixmini", "id:".$dfrn_id);
469                         $now = intval(time());
470                         if ($present) {
471                                 // $present has format "timestamp:jabber_address"
472                                 $p = strpos($present, ":");
473                                 $timestamp = intval(substr($present, 0, $p));
474
475                                 // do not re-retrieve jabber address if last retrieval
476                                 // is not older than a week
477                                 if ($now-$timestamp<3600*24*7) continue;
478                         }
479
480                         // construct base retrieval address
481                         $pos = strpos($request, "/dfrn_request/");
482                         if ($pos===false) continue;
483
484                         $base = substr($request, 0, $pos)."/jappixmini?role=$role";
485
486                         // construct own address
487                         $username = get_pconfig($uid, 'jappixmini', 'username');
488                         if (!$username) continue;
489                         $server = get_pconfig($uid, 'jappixmini', 'server');
490                         if (!$server) continue;
491
492                         $address = $username."@".$server;
493
494                         // sign address
495                         $signed_address = "";
496                         $encrypt_func($address, $signed_address, $key);
497
498                         // construct request url
499                         $signed_address_hex = bin2hex($signed_address);
500                         $url = $base."&signed_address=$signed_address_hex&dfrn_id=".urlencode($dfrn_id);
501
502                         try {
503                                 // send request
504                                 $answer_json = fetch_url($url);
505
506                                 // parse answer
507                                 $answer = json_decode($answer_json);
508                                 if ($answer->status != "ok") throw new Exception();
509
510                                 $encrypted_address_hex = $answer->encrypted_address;
511                                 if (!$encrypted_address_hex) throw new Exception();
512
513                                 $encrypted_address = hex2bin($encrypted_address_hex);
514                                 if (!$encrypted_address) throw new Exception();
515
516                                 // decrypt address
517                                 $decrypted_address = "";
518                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
519                                 if (!$decrypted_address) throw new Exception();
520                         } catch (Exception $e) {
521                                 $decrypted_address = "";
522                         }
523
524                         // save address
525                         set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
526                 }
527         }
528 }
529
530 function jappixmini_download_source(&$a,&$b) {
531         // Jappix Mini source download link on About page
532
533         if (!file_exists("addon/jappixmini/jappix")) return;
534
535         $b .= '<h1>Jappix Mini</h1>';
536         $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>';
537         $b .= '<p>You can download the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix.zip">source code</a>.</p>';
538 }