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