]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappixmini.php
remove superfluous password popup in jappixmini
[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
267                 if (friendica_password) {
268                     jappixmini_addon_set_client_secret(friendica_password.value);
269                     password.value = jappixmini_addon_encrypt_password(clear_password.value);
270                 }
271             }
272             else {
273                 password.value = clear_password.value;
274             }
275         }
276
277         jQuery(document).ready(function() {
278             encrypt = document.getElementById('jappixmini-encrypt').checked;
279             password = document.getElementById('jappixmini-password');
280             clear_password = document.getElementById('jappixmini-clear-password');
281             if (encrypt) {
282                 clear_password.value = jappixmini_addon_decrypt_password(password.value);
283             }
284             else {
285                 clear_password.value = password.value;
286             }
287         });
288     </script>";
289 }
290
291 function jappixmini_settings_post(&$a,&$b) {
292         // save addon settings for a user
293
294         if (!file_exists("addon/jappixmini/jappix")) return;
295
296         if(! local_user()) return;
297         $uid = local_user();
298
299         if($_POST['jappixmini-submit']) {
300                 $encrypt = intval($b['jappixmini-encrypt']);
301                 if ($encrypt) {
302                         // check that Jabber password was encrypted with correct Friendica password
303                         $friendica_password = trim($b['jappixmini-friendica-password']);
304                         $encrypted = hash('whirlpool',$friendica_password);
305                         $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
306                                 dbesc($encrypted)
307                         );
308                         if (!count($r)) {
309                                 info("Wrong friendica password!");
310                                 return;
311                         }
312                 }
313
314                 $purge = intval($b['jappixmini-purge']);
315
316                 $username = trim($b['jappixmini-username']);
317                 $old_username = get_pconfig($uid,'jappixmini','username');
318                 if ($username!=$old_username) $purge = 1;
319
320                 $server = trim($b['jappixmini-server']);
321                 $old_server = get_pconfig($uid,'jappixmini','server');
322                 if ($server!=$old_server) $purge = 1;
323
324                 set_pconfig($uid,'jappixmini','username',$username);
325                 set_pconfig($uid,'jappixmini','server',$server);
326                 set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
327                 set_pconfig($uid,'jappixmini','password',trim($b['jappixmini-encrypted-password']));
328                 set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
329                 set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
330                 set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
331                 set_pconfig($uid,'jappixmini','encrypt',$encrypt);
332                 info( 'Jappix Mini settings saved.' );
333
334                 if ($purge) {
335                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
336                         info( 'List of addresses purged.' );
337                 }
338         }
339 }
340
341 function jappixmini_script(&$a,&$s) {
342     // adds the script to the page header which starts Jappix Mini
343
344     if (!file_exists("addon/jappixmini/jappix")) return;
345     if(! local_user()) return;
346
347     $activate = get_pconfig(local_user(),'jappixmini','activate');
348     if (!$activate) return;
349
350     $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";
351     $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";
352
353     $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";
354     $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";
355     $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";
356
357     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
358
359     $username = get_pconfig(local_user(),'jappixmini','username');
360     $username = str_replace("'", "\\'", $username);
361     $server = get_pconfig(local_user(),'jappixmini','server');
362     $server = str_replace("'", "\\'", $server);
363     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
364     $bosh = str_replace("'", "\\'", $bosh);
365     $encrypt = get_pconfig(local_user(),'jappixmini','encrypt');
366     $encrypt = intval($encrypt);
367     $password = get_pconfig(local_user(),'jappixmini','password');
368     $password = str_replace("'", "\\'", $password);
369
370     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
371     $autoapprove = intval($autoapprove);
372     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
373     $autosubscribe = intval($autosubscribe);
374
375     // deactivate bosh host if proxy is off
376     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
377     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
378     if ($matches[1]!="on") {
379         $bosh = '';
380     }
381
382     // get a list of jabber accounts of the contacts
383     $contacts = Array();
384     $uid = local_user();
385     $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
386     foreach ($rows as $row) {
387         $key = $row['k'];
388         $pos = strpos($key, ":");
389         $dfrn_id = substr($key, $pos+1);
390         $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND `dfrn-id`='%s' OR `issued-id`='%s'",
391                 dbesc($dfrn_id),
392                 dbesc($dfrn_id)
393         );
394         $name = $r[0]["name"];
395
396         $value = $row['v'];
397         $pos = strpos($value, ":");
398         $address = substr($value, $pos+1);
399         if (!$address) continue;
400         if (!$name) $name = $address;
401
402         $contacts[$address] = $name;
403     }
404     $contacts_json = json_encode($contacts);
405
406     // get nickname
407     $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
408     $nickname = json_encode($r[0]["username"]);
409
410     // add javascript to start Jappix Mini
411     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
412         jQuery(document).ready(function() {
413            jappixmini_addon_start('$server', '$username', '$bosh', $encrypt, '$password', $nickname);
414            jappixmini_manage_roster($contacts_json, $autoapprove, $autosubscribe);
415         });
416     </script>";
417
418     return;
419 }
420
421 function jappixmini_login(&$a, &$o) {
422     // create client secret on login to be able to encrypt jabber passwords
423
424     if (!file_exists("addon/jappixmini/jappix")) return;
425
426     // for setDB, 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=datastore.js"></script>'."\r\n";
428
429     // for str_sha1, needed by jappixmini_addon_set_client_secret
430     $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";
431
432     // for jappixmini_addon_set_client_secret
433     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
434
435     // save hash of password
436     $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
437 }
438
439 function jappixmini_cron(&$a, $d) {
440         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
441
442         if (!file_exists("addon/jappixmini/jappix")) return;
443
444         // go through list of users with jabber enabled
445         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
446
447         foreach ($users as $row) {
448                 $uid = $row["uid"];
449
450                 // for each user, go through list of contacts
451                 $contacts = q("SELECT * FROM `contact` WHERE `uid`=%d AND ((LENGTH(`dfrn-id`) AND LENGTH(`pubkey`)) OR (LENGTH(`issued-id`) AND LENGTH(`prvkey`)))", intval($uid));
452                 foreach ($contacts as $contact_row) {
453                         $request = $contact_row["request"];
454                         if (!$request) continue;
455
456                         $dfrn_id = $contact_row["dfrn-id"];
457                         if ($dfrn_id) {
458                                 $key = $contact_row["pubkey"];
459                                 $encrypt_func = openssl_public_encrypt;
460                                 $decrypt_func = openssl_public_decrypt;
461                                 $role = "prv";
462                         } else {
463                                 $dfrn_id = $contact_row["issued-id"];
464                                 $key = $contact_row["prvkey"];
465                                 $encrypt_func = openssl_private_encrypt;
466                                 $decrypt_func = openssl_private_decrypt;
467                                 $role = "pub";
468                         }
469
470                         // check if jabber address already present
471                         $present = get_pconfig($uid, "jappixmini", "id:".$dfrn_id);
472                         $now = intval(time());
473                         if ($present) {
474                                 // $present has format "timestamp:jabber_address"
475                                 $p = strpos($present, ":");
476                                 $timestamp = intval(substr($present, 0, $p));
477
478                                 // do not re-retrieve jabber address if last retrieval
479                                 // is not older than a week
480                                 if ($now-$timestamp<3600*24*7) continue;
481                         }
482
483                         // construct base retrieval address
484                         $pos = strpos($request, "/dfrn_request/");
485                         if ($pos===false) continue;
486
487                         $base = substr($request, 0, $pos)."/jappixmini?role=$role";
488
489                         // construct own address
490                         $username = get_pconfig($uid, 'jappixmini', 'username');
491                         if (!$username) continue;
492                         $server = get_pconfig($uid, 'jappixmini', 'server');
493                         if (!$server) continue;
494
495                         $address = $username."@".$server;
496
497                         // sign address
498                         $signed_address = "";
499                         $encrypt_func($address, $signed_address, $key);
500
501                         // construct request url
502                         $signed_address_hex = bin2hex($signed_address);
503                         $url = $base."&signed_address=$signed_address_hex&dfrn_id=".urlencode($dfrn_id);
504
505                         try {
506                                 // send request
507                                 $answer_json = fetch_url($url);
508
509                                 // parse answer
510                                 $answer = json_decode($answer_json);
511                                 if ($answer->status != "ok") throw new Exception();
512
513                                 $encrypted_address_hex = $answer->encrypted_address;
514                                 if (!$encrypted_address_hex) throw new Exception();
515
516                                 $encrypted_address = hex2bin($encrypted_address_hex);
517                                 if (!$encrypted_address) throw new Exception();
518
519                                 // decrypt address
520                                 $decrypted_address = "";
521                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
522                                 if (!$decrypted_address) throw new Exception();
523                         } catch (Exception $e) {
524                                 $decrypted_address = "";
525                         }
526
527                         // save address
528                         set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
529                 }
530         }
531 }
532
533 function jappixmini_download_source(&$a,&$b) {
534         // Jappix Mini source download link on About page
535
536         if (!file_exists("addon/jappixmini/jappix")) return;
537
538         $b .= '<h1>Jappix Mini</h1>';
539         $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>';
540         $b .= '<p>You can download the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix.zip">source code</a>.</p>';
541 }