]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappixmini.php
jappixmini: add text field for configuration help
[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 // set standard info text
74 $info_text = get_config("jappixmini", "infotext");
75 if (!$info_text) set_config("jappixmini", "infotext",
76         "To get the chat working, you need to know a BOSH host which works with your Jabber account. ".
77         "An example of a BOSH server that works for all accounts is https://bind.jappix.com/, but keep ".
78         "in mind that the BOSH server can read along all chat messages. If you know that your Jabber ".
79         "server also provides an own BOSH server, it is much better to use this one!"
80 );
81 }
82
83
84 function jappixmini_uninstall() {
85 unregister_hook('plugin_settings', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings');
86 unregister_hook('plugin_settings_post', 'addon/jappixmini/jappixmini.php', 'jappixmini_settings_post');
87
88 unregister_hook('page_end', 'addon/jappixmini/jappixmini.php', 'jappixmini_script');
89 unregister_hook('authenticate', 'addon/jappixmini/jappixmini.php', 'jappixmini_login');
90
91 unregister_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
92
93 unregister_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
94 }
95
96 function jappixmini_plugin_admin(&$a, &$o) {
97         // display instructions and warnings on addon settings page for admin
98
99         if (!file_exists("addon/jappixmini/jappix")) {
100                 $o .= '<p><strong>You need to install the Jappix application, adapted for Friendica (see README).</strong></p>';
101         }
102         else if (!file_exists("addon/jappixmini/jappix.zip")) {
103                 $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>';
104         }
105         else {
106                 $o .= '<p>Jappix is installed.</p>';
107         }
108
109         // info text field
110         $o .= '<label for="jappixmini-infotext">Info text to help users with configuration (important if you want to provide your own BOSH host!):</label><br />';
111         $info_text = get_config("jappixmini", "infotext");
112         $o .= '<textarea id="jappixmini-infotext" name="jappixmini-infotext" rows="5" cols="50">'.htmlentities($info_text).'</textarea><br />';
113         $o .= '<input type="submit" name="jappixmini-admin-settings" value="OK" />';
114 }
115
116 function jappixmini_plugin_admin_post(&$a) {
117         // set info text
118         $submit = $_REQUEST['jappixmini-admin-settings'];
119         if ($submit) {
120                 $info_text = $_REQUEST['jappixmini-infotext'];
121                 set_config("jappixmini", "infotext", $info_text);
122         }
123 }
124
125 function jappixmini_module() {}
126 function jappixmini_init(&$a) {
127         // module page where other Friendica sites can submit Jabber addresses to and also can query Jabber addresses
128         // of local users
129
130         if (!file_exists("addon/jappixmini/jappix")) killme();
131
132         $dfrn_id = $_REQUEST["dfrn_id"];
133         if (!$dfrn_id) killme();
134
135         $role = $_REQUEST["role"];
136         if ($role=="pub") {
137                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id`='%s' LIMIT 1",
138                         dbesc($dfrn_id)
139                 );
140                 if (!count($r)) killme();
141
142                 $encrypt_func = openssl_public_encrypt;
143                 $decrypt_func = openssl_public_decrypt;
144                 $key = $r[0]["pubkey"];
145         } else if ($role=="prv") {
146                 $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id`='%s' LIMIT 1",
147                         dbesc($dfrn_id)
148                 );
149                 if (!count($r)) killme();
150
151                 $encrypt_func = openssl_private_encrypt;
152                 $decrypt_func = openssl_private_decrypt;
153                 $key = $r[0]["prvkey"];
154         } else {
155                 killme();
156         }
157
158         $uid = $r[0]["uid"];
159
160         // save the Jabber address we received
161         try {
162                 $signed_address_hex = $_REQUEST["signed_address"];
163                 $signed_address = hex2bin($signed_address_hex);
164
165                 $trusted_address = "";
166                 $decrypt_func($signed_address, $trusted_address, $key);
167
168                 $now = intval(time());
169                 set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$trusted_address");
170         } catch (Exception $e) {
171         }
172
173         // do not return an address if user deactivated plugin
174         $activated = get_pconfig($uid, 'jappixmini', 'activate');
175         if (!$activated) killme();
176
177         // return the requested Jabber address
178         try {
179                 $username = get_pconfig($uid, 'jappixmini', 'username');
180                 $server = get_pconfig($uid, 'jappixmini', 'server');
181                 $address = "$username@$server";
182
183                 $encrypted_address = "";
184                 $encrypt_func($address, $encrypted_address, $key);
185
186                 $encrypted_address_hex = bin2hex($encrypted_address);
187
188                 $answer = Array(
189                         "status"=>"ok",
190                         "encrypted_address"=>$encrypted_address_hex
191                 );
192
193                 $answer_json = json_encode($answer);
194                 echo $answer_json;
195                 killme();
196         } catch (Exception $e) {
197                 killme();
198         }
199 }
200
201 function jappixmini_settings(&$a, &$s) {
202     // addon settings for a user
203
204     if (!file_exists("addon/jappixmini/jappix")) return;
205
206     $activate = get_pconfig(local_user(),'jappixmini','activate');
207     $activate = intval($activate) ? ' checked="checked"' : '';
208
209     $username = get_pconfig(local_user(),'jappixmini','username');
210     $username = htmlentities($username);
211     $server = get_pconfig(local_user(),'jappixmini','server');
212     $server = htmlentities($server);
213     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
214     $bosh = htmlentities($bosh);
215     $password = get_pconfig(local_user(),'jappixmini','password');
216     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
217     $autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
218     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
219     $autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
220     $encrypt = intval(get_pconfig(local_user(),'jappixmini','encrypt'));
221     $encrypt_checked = $encrypt ? ' checked="checked"' : '';
222     $encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
223
224     $info_text = get_config("jappixmini", "infotext");
225     $info_text = htmlentities($info_text);
226     $info_text = str_replace("\n", "<br />", $info_text);
227
228     if (!$activate) {
229         // load scripts if not yet activated so that password can be saved
230         $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";
231         $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";
232
233         $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";
234         $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";
235         $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";
236
237         $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
238     }
239
240     $s .= '<div class="settings-block">';
241
242     $s .= '<h3>Jappix Mini addon settings</h3>';
243     $s .= '<div>';
244     $s .= '<label for="jappixmini-activate">Activate addon</label>';
245     $s .= ' <input id="jappixmini-activate" type="checkbox" name="jappixmini-activate" value="1"'.$activate.' />';
246     $s .= '<br />';
247     $s .= '<label for="jappixmini-username">Jabber username</label>';
248     $s .= ' <input id="jappixmini-username" type="text" name="jappixmini-username" value="'.$username.'" />';
249     $s .= '<br />';
250     $s .= '<label for="jappixmini-server">Jabber server</label>';
251     $s .= ' <input id="jappixmini-server" type="text" name="jappixmini-server" value="'.$server.'" />';
252     $s .= '<br />';
253
254     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
255     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
256     if ($matches[1]=="on") {
257         $s .= '<label for="jappixmini-bosh">Jabber BOSH host</label>';
258         $s .= ' <input id="jappixmini-bosh" type="text" name="jappixmini-bosh" value="'.$bosh.'" />';
259         $s .= '<br />';
260     }
261
262     $s .= '<label for="jappixmini-password">Jabber password</label>';
263     $s .= ' <input type="hidden" id="jappixmini-password" name="jappixmini-encrypted-password" value="'.$password.'" />';
264     $s .= ' <input id="jappixmini-clear-password" type="password" value="" onchange="jappixmini_set_password();" />';
265     $s .= '<br />';
266     $onchange = "document.getElementById('jappixmini-friendica-password').disabled = !this.checked;jappixmini_set_password();";
267     $s .= '<label for="jappixmini-encrypt">Encrypt Jabber password with Friendica password (recommended)</label>';
268     $s .= ' <input id="jappixmini-encrypt" type="checkbox" name="jappixmini-encrypt" onchange="'.$onchange.'" value="1"'.$encrypt_checked.' />';
269     $s .= '<br />';
270     $s .= '<label for="jappixmini-friendica-password">Friendica password</label>';
271     $s .= ' <input id="jappixmini-friendica-password" name="jappixmini-friendica-password" type="password" onchange="jappixmini_set_password();" value=""'.$encrypt_disabled.' />';
272     $s .= '<br />';
273     $s .= '<label for="jappixmini-autoapprove">Approve subscription requests from Friendica contacts automatically</label>';
274     $s .= ' <input id="jappixmini-autoapprove" type="checkbox" name="jappixmini-autoapprove" value="1"'.$autoapprove.' />';
275     $s .= '<br />';
276     $s .= '<label for="jappixmini-autosubscribe">Subscribe to Friendica contacts automatically</label>';
277     $s .= ' <input id="jappixmini-autosubscribe" type="checkbox" name="jappixmini-autosubscribe" value="1"'.$autosubscribe.' />';
278     $s .= '<br />';
279     $s .= '<label for="jappixmini-purge">Purge internal list of jabber addresses of contacts</label>';
280     $s .= ' <input id="jappixmini-purge" type="checkbox" name="jappixmini-purge" value="1" />';
281     $s .= '<br />';
282     if ($info_text) $s .= '<br />Configuration help:<p style="margin-left:2em;">'.$info_text.'</p>';
283     $s .= '<input type="submit" name="jappixmini-submit" value="' . t('Submit') . '" />';
284     $s .= ' <input type="button" value="Add contact" onclick="jappixmini_addon_subscribe();" />';
285     $s .= '</div>';
286
287     $s .= '</div>';
288
289     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
290         function jappixmini_set_password() {
291             encrypt = document.getElementById('jappixmini-encrypt').checked;
292             password = document.getElementById('jappixmini-password');
293             clear_password = document.getElementById('jappixmini-clear-password');
294             if (encrypt) {
295                 friendica_password = document.getElementById('jappixmini-friendica-password');
296
297                 if (friendica_password) {
298                     jappixmini_addon_set_client_secret(friendica_password.value);
299                     jappixmini_addon_encrypt_password(clear_password.value, function(encrypted_password){
300                         password.value = encrypted_password;
301                     });
302                 }
303             }
304             else {
305                 password.value = clear_password.value;
306             }
307         }
308
309         jQuery(document).ready(function() {
310             encrypt = document.getElementById('jappixmini-encrypt').checked;
311             password = document.getElementById('jappixmini-password');
312             clear_password = document.getElementById('jappixmini-clear-password');
313             if (encrypt) {
314                 jappixmini_addon_decrypt_password(password.value, function(decrypted_password){
315                     clear_password.value = decrypted_password;
316                 });
317             }
318             else {
319                 clear_password.value = password.value;
320             }
321         });
322     </script>";
323 }
324
325 function jappixmini_settings_post(&$a,&$b) {
326         // save addon settings for a user
327
328         if (!file_exists("addon/jappixmini/jappix")) return;
329
330         if(! local_user()) return;
331         $uid = local_user();
332
333         if($_POST['jappixmini-submit']) {
334                 $encrypt = intval($b['jappixmini-encrypt']);
335                 if ($encrypt) {
336                         // check that Jabber password was encrypted with correct Friendica password
337                         $friendica_password = trim($b['jappixmini-friendica-password']);
338                         $encrypted = hash('whirlpool',$friendica_password);
339                         $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
340                                 dbesc($encrypted)
341                         );
342                         if (!count($r)) {
343                                 info("Wrong friendica password!");
344                                 return;
345                         }
346                 }
347
348                 $purge = intval($b['jappixmini-purge']);
349
350                 $username = trim($b['jappixmini-username']);
351                 $old_username = get_pconfig($uid,'jappixmini','username');
352                 if ($username!=$old_username) $purge = 1;
353
354                 $server = trim($b['jappixmini-server']);
355                 $old_server = get_pconfig($uid,'jappixmini','server');
356                 if ($server!=$old_server) $purge = 1;
357
358                 set_pconfig($uid,'jappixmini','username',$username);
359                 set_pconfig($uid,'jappixmini','server',$server);
360                 set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
361                 set_pconfig($uid,'jappixmini','password',trim($b['jappixmini-encrypted-password']));
362                 set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
363                 set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
364                 set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
365                 set_pconfig($uid,'jappixmini','encrypt',$encrypt);
366                 info( 'Jappix Mini settings saved.' );
367
368                 if ($purge) {
369                         q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
370                         info( 'List of addresses purged.' );
371                 }
372         }
373 }
374
375 function jappixmini_script(&$a,&$s) {
376     // adds the script to the page header which starts Jappix Mini
377
378     if (!file_exists("addon/jappixmini/jappix")) return;
379     if(! local_user()) return;
380
381     $activate = get_pconfig(local_user(),'jappixmini','activate');
382     if (!$activate) return;
383
384     $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";
385     $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";
386
387     $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";
388     $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";
389     $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";
390
391     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
392
393     $username = get_pconfig(local_user(),'jappixmini','username');
394     $username = str_replace("'", "\\'", $username);
395     $server = get_pconfig(local_user(),'jappixmini','server');
396     $server = str_replace("'", "\\'", $server);
397     $bosh = get_pconfig(local_user(),'jappixmini','bosh');
398     $bosh = str_replace("'", "\\'", $bosh);
399     $encrypt = get_pconfig(local_user(),'jappixmini','encrypt');
400     $encrypt = intval($encrypt);
401     $password = get_pconfig(local_user(),'jappixmini','password');
402     $password = str_replace("'", "\\'", $password);
403
404     $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
405     $autoapprove = intval($autoapprove);
406     $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
407     $autosubscribe = intval($autosubscribe);
408
409     // deactivate bosh host if proxy is off
410     $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
411     preg_match("/<bosh_proxy>(.*)<\/bosh_proxy>/", $conf, $matches);
412     if ($matches[1]!="on") {
413         $bosh = '';
414     }
415
416     // get a list of jabber accounts of the contacts
417     $contacts = Array();
418     $uid = local_user();
419     $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
420     foreach ($rows as $row) {
421         $key = $row['k'];
422         $pos = strpos($key, ":");
423         $dfrn_id = substr($key, $pos+1);
424         $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND `dfrn-id`='%s' OR `issued-id`='%s'",
425                 dbesc($dfrn_id),
426                 dbesc($dfrn_id)
427         );
428         $name = $r[0]["name"];
429
430         $value = $row['v'];
431         $pos = strpos($value, ":");
432         $address = substr($value, $pos+1);
433         if (!$address) continue;
434         if (!$name) $name = $address;
435
436         $contacts[$address] = $name;
437     }
438     $contacts_json = json_encode($contacts);
439
440     // get nickname
441     $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
442     $nickname = json_encode($r[0]["username"]);
443
444     // add javascript to start Jappix Mini
445     $a->page['htmlhead'] .= "<script type=\"text/javascript\">
446         jQuery(document).ready(function() {
447            jappixmini_addon_start('$server', '$username', '$bosh', $encrypt, '$password', $nickname, $contacts_json, $autoapprove, $autosubscribe);
448         });
449     </script>";
450
451     return;
452 }
453
454 function jappixmini_login(&$a, &$o) {
455     // create client secret on login to be able to encrypt jabber passwords
456
457     if (!file_exists("addon/jappixmini/jappix")) return;
458
459     // for setDB, needed by jappixmini_addon_set_client_secret
460     $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";
461
462     // for str_sha1, needed by jappixmini_addon_set_client_secret
463     $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";
464
465     // for jappixmini_addon_set_client_secret
466     $a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/jappixmini/lib.js"></script>'."\r\n";
467
468     // save hash of password
469     $o = str_replace("<form ", "<form onsubmit=\"jappixmini_addon_set_client_secret(this.elements['id_password'].value);return true;\" ", $o);
470 }
471
472 function jappixmini_cron(&$a, $d) {
473         // For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
474
475         if (!file_exists("addon/jappixmini/jappix")) return;
476
477         // go through list of users with jabber enabled
478         $users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
479         logger("jappixmini: Update list of contacts' jabber accounts for ".count($users)." users.");
480
481         foreach ($users as $row) {
482                 $uid = $row["uid"];
483
484                 // for each user, go through list of contacts
485                 $contacts = q("SELECT * FROM `contact` WHERE `uid`=%d AND ((LENGTH(`dfrn-id`) AND LENGTH(`pubkey`)) OR (LENGTH(`issued-id`) AND LENGTH(`prvkey`)))", intval($uid));
486                 foreach ($contacts as $contact_row) {
487                         $request = $contact_row["request"];
488                         if (!$request) continue;
489
490                         $dfrn_id = $contact_row["dfrn-id"];
491                         if ($dfrn_id) {
492                                 $key = $contact_row["pubkey"];
493                                 $encrypt_func = openssl_public_encrypt;
494                                 $decrypt_func = openssl_public_decrypt;
495                                 $role = "prv";
496                         } else {
497                                 $dfrn_id = $contact_row["issued-id"];
498                                 $key = $contact_row["prvkey"];
499                                 $encrypt_func = openssl_private_encrypt;
500                                 $decrypt_func = openssl_private_decrypt;
501                                 $role = "pub";
502                         }
503
504                         // check if jabber address already present
505                         $present = get_pconfig($uid, "jappixmini", "id:".$dfrn_id);
506                         $now = intval(time());
507                         if ($present) {
508                                 // $present has format "timestamp:jabber_address"
509                                 $p = strpos($present, ":");
510                                 $timestamp = intval(substr($present, 0, $p));
511
512                                 // do not re-retrieve jabber address if last retrieval
513                                 // is not older than a week
514                                 if ($now-$timestamp<3600*24*7) continue;
515                         }
516
517                         // construct base retrieval address
518                         $pos = strpos($request, "/dfrn_request/");
519                         if ($pos===false) continue;
520
521                         $base = substr($request, 0, $pos)."/jappixmini?role=$role";
522
523                         // construct own address
524                         $username = get_pconfig($uid, 'jappixmini', 'username');
525                         if (!$username) continue;
526                         $server = get_pconfig($uid, 'jappixmini', 'server');
527                         if (!$server) continue;
528
529                         $address = $username."@".$server;
530
531                         // sign address
532                         $signed_address = "";
533                         $encrypt_func($address, $signed_address, $key);
534
535                         // construct request url
536                         $signed_address_hex = bin2hex($signed_address);
537                         $url = $base."&signed_address=$signed_address_hex&dfrn_id=".urlencode($dfrn_id);
538
539                         try {
540                                 // send request
541                                 $answer_json = fetch_url($url);
542
543                                 // parse answer
544                                 $answer = json_decode($answer_json);
545                                 if ($answer->status != "ok") throw new Exception();
546
547                                 $encrypted_address_hex = $answer->encrypted_address;
548                                 if (!$encrypted_address_hex) throw new Exception();
549
550                                 $encrypted_address = hex2bin($encrypted_address_hex);
551                                 if (!$encrypted_address) throw new Exception();
552
553                                 // decrypt address
554                                 $decrypted_address = "";
555                                 $decrypt_func($encrypted_address, $decrypted_address, $key);
556                                 if (!$decrypted_address) throw new Exception();
557                         } catch (Exception $e) {
558                                 $decrypted_address = "";
559                         }
560
561                         // save address
562                         set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
563                 }
564         }
565 }
566
567 function jappixmini_download_source(&$a,&$b) {
568         // Jappix Mini source download link on About page
569
570         if (!file_exists("addon/jappixmini/jappix")) return;
571
572         $b .= '<h1>Jappix Mini</h1>';
573         $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>';
574         $b .= '<p>You can download the <a href="'.$a->get_baseurl().'/addon/jappixmini/jappix.zip">source code</a>.</p>';
575 }