]> git.mxchange.org Git - friendica.git/blob - mod/dfrn_poll.php
Merge branch 'master' of git://github.com/friendica/friendica
[friendica.git] / mod / dfrn_poll.php
1 <?php
2
3
4
5 require_once('include/items.php');
6 require_once('include/auth.php');
7
8
9 function dfrn_poll_init(&$a) {
10
11
12         $dfrn_id         = ((x($_GET,'dfrn_id'))         ? $_GET['dfrn_id']              : '');
13         $type            = ((x($_GET,'type'))            ? $_GET['type']                 : 'data');
14         $last_update     = ((x($_GET,'last_update'))     ? $_GET['last_update']          : '');
15         $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url']      : '');
16         $challenge       = ((x($_GET,'challenge'))       ? $_GET['challenge']            : '');
17         $sec             = ((x($_GET,'sec'))             ? $_GET['sec']                  : '');
18         $dfrn_version    = ((x($_GET,'dfrn_version'))    ? (float) $_GET['dfrn_version'] : 2.0);
19         $perm            = ((x($_GET,'perm'))            ? $_GET['perm']                 : 'r');
20
21         $direction = (-1);
22
23
24         if(strpos($dfrn_id,':') == 1) {
25                 $direction = intval(substr($dfrn_id,0,1));
26                 $dfrn_id   = substr($dfrn_id,2);
27         }
28
29         if(($dfrn_id === '') && (! x($_POST,'dfrn_id'))) {
30                 if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
31                         killme();
32                 }
33
34                 $user = '';
35                 if($a->argc > 1) {
36                         $r = q("SELECT `hidewall`,`nickname` FROM `user` WHERE `user`.`nickname` = '%s' LIMIT 1",
37                                 dbesc($a->argv[1])
38                         );
39                         if((! count($r)) || (count($r) && $r[0]['hidewall']))
40                                 killme();
41                         $user = $r[0]['nickname'];
42                 }
43  
44                 logger('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $user);
45                 header("Content-type: application/atom+xml");
46                 echo get_feed_for($a, '', $user,$last_update);
47                 killme();
48         }
49
50         if(($type === 'profile') && (! strlen($sec))) {
51
52                 $sql_extra = '';
53                 switch($direction) {
54                         case (-1):
55                                 $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
56                                 $my_id = $dfrn_id;
57                                 break;
58                         case 0:
59                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
60                                 $my_id = '1:' . $dfrn_id;
61                                 break;
62                         case 1:
63                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
64                                 $my_id = '0:' . $dfrn_id;
65                                 break;
66                         default:
67                                 goaway(z_root());
68                                 break; // NOTREACHED
69                 }
70
71                 $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname` 
72                         FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
73                         WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0 
74                         AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
75                         dbesc($a->argv[1])
76                 );
77                 
78                 if(count($r)) {
79
80                         $s = fetch_url($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
81
82                         logger("dfrn_poll: old profile returns " . $s, LOGGER_DATA);
83
84                         if(strlen($s)) {
85
86                                 $xml = parse_xml_string($s);
87
88                                 if((int) $xml->status == 1) {
89                                         $_SESSION['authenticated'] = 1;
90                                         $_SESSION['visitor_id'] = $r[0]['id'];
91                                         $_SESSION['visitor_home'] = $r[0]['url'];
92                                         $_SESSION['visitor_handle'] = $r[0]['addr'];
93                                         $_SESSION['visitor_visiting'] = $r[0]['uid'];
94                                         info( sprintf(t('%s welcomes %s'), $r[0]['username'] , $r[0]['name']) . EOL);
95                                         // Visitors get 1 day session.
96                                         $session_id = session_id();
97                                         $expire = time() + 86400;
98                                         q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s' LIMIT 1",
99                                                 dbesc($expire),
100                                                 dbesc($session_id)
101                                         ); 
102                                 }
103                         }
104                         $profile = $r[0]['nickname'];
105                         goaway((strlen($destination_url)) ? $destination_url : $a->get_baseurl() . '/profile/' . $profile);
106                 }
107                 goaway(z_root());
108
109         }
110
111         if($type === 'profile-check' && $dfrn_version < 2.2 ) {
112
113                 if((strlen($challenge)) && (strlen($sec))) {
114
115                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
116                         $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
117                                 dbesc($sec)
118                         );
119                         if(! count($r)) {
120                                 xml_status(3, 'No ticket');
121                                 // NOTREACHED
122                         }
123                         $orig_id = $r[0]['dfrn_id'];
124                         if(strpos($orig_id, ':'))
125                                 $orig_id = substr($orig_id,2);
126
127                         $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
128                                 intval($r[0]['cid'])
129                         );
130                         if(! count($c)) {
131                                 xml_status(3, 'No profile');
132                         }
133                         $contact = $c[0];
134
135                         $sent_dfrn_id = hex2bin($dfrn_id);
136                         $challenge    = hex2bin($challenge);
137
138                         $final_dfrn_id = '';
139
140                         if(($contact['duplex']) && strlen($contact['prvkey'])) {
141                                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
142                                 openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
143                         }
144                         else {
145                                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
146                                 openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
147                         }
148
149                         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
150
151                         if(strpos($final_dfrn_id,':') == 1)
152                                 $final_dfrn_id = substr($final_dfrn_id,2);
153
154                         if($final_dfrn_id != $orig_id) {
155                                 logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
156                                 // did not decode properly - cannot trust this site 
157                                 xml_status(3, 'Bad decryption');
158                         }
159
160                         header("Content-type: text/xml");
161                         echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
162                         killme();
163                         // NOTREACHED
164                 }
165                 else {
166                                 // old protocol
167
168                         switch($direction) {
169                                 case 1:
170                                         $dfrn_id = '0:' . $dfrn_id;
171                                         break;
172                                 case 0:
173                                         $dfrn_id = '1:' . $dfrn_id;
174                                         break;
175                                 default:
176                                         break;
177                         }
178
179
180                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
181                         $r = q("SELECT * FROM `profile_check` WHERE `dfrn_id` = '%s' ORDER BY `expire` DESC",
182                                 dbesc($dfrn_id));
183                         if(count($r)) {
184                                 xml_status(1);
185                                 return; // NOTREACHED
186                         }
187                         xml_status(0);
188                         return; // NOTREACHED
189                 }
190         }
191
192 }
193
194
195
196 function dfrn_poll_post(&$a) {
197
198         $dfrn_id      = ((x($_POST,'dfrn_id'))      ? $_POST['dfrn_id']              : '');
199         $challenge    = ((x($_POST,'challenge'))    ? $_POST['challenge']            : '');
200         $url          = ((x($_POST,'url'))          ? $_POST['url']                  : '');
201         $sec          = ((x($_POST,'sec'))          ? $_POST['sec']                  : '');
202         $ptype        = ((x($_POST,'type'))         ? $_POST['type']                 : '');
203         $dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0);
204         $perm         = ((x($_POST,'perm'))         ? $_POST['perm']                 : 'r');
205           
206         if($ptype === 'profile-check') {
207
208                 if((strlen($challenge)) && (strlen($sec))) {
209
210                         logger('dfrn_poll: POST: profile-check');
211  
212                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
213                         $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
214                                 dbesc($sec)
215                         );
216                         if(! count($r)) {
217                                 xml_status(3, 'No ticket');
218                                 // NOTREACHED
219                         }
220                         $orig_id = $r[0]['dfrn_id'];
221                         if(strpos($orig_id, ':'))
222                                 $orig_id = substr($orig_id,2);
223
224                         $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
225                                 intval($r[0]['cid'])
226                         );
227                         if(! count($c)) {
228                                 xml_status(3, 'No profile');
229                         }
230                         $contact = $c[0];
231
232                         $sent_dfrn_id = hex2bin($dfrn_id);
233                         $challenge    = hex2bin($challenge);
234
235                         $final_dfrn_id = '';
236
237                         if(($contact['duplex']) && strlen($contact['prvkey'])) {
238                                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
239                                 openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
240                         }
241                         else {
242                                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
243                                 openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
244                         }
245
246                         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
247
248                         if(strpos($final_dfrn_id,':') == 1)
249                                 $final_dfrn_id = substr($final_dfrn_id,2);
250
251                         if($final_dfrn_id != $orig_id) {
252                                 logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
253                                 // did not decode properly - cannot trust this site 
254                                 xml_status(3, 'Bad decryption');
255                         }
256
257                         header("Content-type: text/xml");
258                         echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
259                         killme();
260                         // NOTREACHED
261                 }
262
263         }
264
265         $direction    = (-1);
266         if(strpos($dfrn_id,':') == 1) {
267                 $direction = intval(substr($dfrn_id,0,1));
268                 $dfrn_id   = substr($dfrn_id,2);
269         }
270
271
272         $r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
273                 dbesc($dfrn_id),
274                 dbesc($challenge)
275         );
276
277         if(! count($r))
278                 killme();
279
280         $type = $r[0]['type'];
281         $last_update = $r[0]['last_update'];
282
283         $r = q("DELETE FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
284                 dbesc($dfrn_id),
285                 dbesc($challenge)
286         );
287
288
289         $sql_extra = '';
290         switch($direction) {
291                 case (-1):
292                         $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
293                         $my_id = $dfrn_id;
294                         break;
295                 case 0:
296                         $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
297                         $my_id = '1:' . $dfrn_id;
298                         break;
299                 case 1:
300                         $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
301                         $my_id = '0:' . $dfrn_id;
302                         break;
303                 default:
304                         goaway(z_root());
305                         break; // NOTREACHED
306         }
307
308
309         $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1");
310
311
312         if(! count($r))
313                 killme();
314
315         $contact = $r[0];
316         $owner_uid = $r[0]['uid'];
317         $contact_id = $r[0]['id']; 
318
319
320         if($type === 'reputation' && strlen($url)) {
321                 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
322                         dbesc($url),
323                         intval($owner_uid)
324                 );
325                 $reputation = 0;
326                 $text = '';
327
328                 if(count($r)) {
329                         $reputation = $r[0]['rating'];
330                         $text = $r[0]['reason'];
331
332                         if($r[0]['id'] == $contact_id) {        // inquiring about own reputation not allowed
333                                 $reputation = 0;
334                                 $text = '';
335                         }
336                 }
337
338                 echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
339                 <reputation>
340                         <url>$url</url>
341                         <rating>$reputation</rating>
342                         <description>$text</description>
343                 </reputation>
344                 ";
345                 killme();
346                 // NOTREACHED
347         }
348         else {
349
350                 // Update the writable flag if it changed               
351                 logger('dfrn_poll: post request feed: ' . print_r($_POST,true),LOGGER_DATA);
352                 if($dfrn_version >= 2.21) {
353                         if($perm === 'rw')
354                                 $writable = 1;
355                         else
356                                 $writable = 0;
357
358                         if($writable !=  $contact['writable']) {
359                                 q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d LIMIT 1",
360                                         intval($writable),
361                                         intval($contact_id)
362                                 );
363                         }
364                 }
365                                 
366                 header("Content-type: application/atom+xml");
367                 $o = get_feed_for($a,$dfrn_id, $a->argv[1], $last_update, $direction);
368                 echo $o;
369                 killme();
370
371         }
372 }
373
374 function dfrn_poll_content(&$a) {
375
376         $dfrn_id         = ((x($_GET,'dfrn_id'))         ? $_GET['dfrn_id']              : '');
377         $type            = ((x($_GET,'type'))            ? $_GET['type']                 : 'data');
378         $last_update     = ((x($_GET,'last_update'))     ? $_GET['last_update']          : '');
379         $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url']      : '');
380         $sec             = ((x($_GET,'sec'))             ? $_GET['sec']                  : '');
381         $dfrn_version    = ((x($_GET,'dfrn_version'))    ? (float) $_GET['dfrn_version'] : 2.0);
382         $perm            = ((x($_GET,'perm'))            ? $_GET['perm']                 : 'r');
383
384         $direction = (-1);
385         if(strpos($dfrn_id,':') == 1) {
386                 $direction = intval(substr($dfrn_id,0,1));
387                 $dfrn_id = substr($dfrn_id,2);
388         }
389
390
391         if($dfrn_id != '') {
392                 // initial communication from external contact
393                 $hash = random_string();
394
395                 $status = 0;
396
397                 $r = q("DELETE FROM `challenge` WHERE `expire` < " . intval(time()));
398
399                 if($type !== 'profile') {
400                         $r = q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` )
401                                 VALUES( '%s', '%s', '%s', '%s', '%s' ) ",
402                                 dbesc($hash),
403                                 dbesc($dfrn_id),
404                                 intval(time() + 60 ),
405                                 dbesc($type),
406                                 dbesc($last_update)
407                         );
408                 }
409                 $sql_extra = '';
410                 switch($direction) {
411                         case (-1):
412                                 if($type === 'profile')
413                                         $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
414                                 else
415                                         $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
416                                 $my_id = $dfrn_id;
417                                 break;
418                         case 0:
419                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
420                                 $my_id = '1:' . $dfrn_id;
421                                 break;
422                         case 1:
423                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
424                                 $my_id = '0:' . $dfrn_id;
425                                 break;
426                         default:
427                                 goaway(z_root());
428                                 break; // NOTREACHED
429                 }
430
431                 $nickname = $a->argv[1];
432
433                 $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname` 
434                         FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
435                         WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0 
436                         AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
437                         dbesc($nickname)
438                 );
439
440                 if(count($r)) {
441
442                         $challenge = '';
443                         $encrypted_id = '';
444                         $id_str = $my_id . '.' . mt_rand(1000,9999);
445
446                         if(($r[0]['duplex'] && strlen($r[0]['pubkey'])) || (! strlen($r[0]['prvkey']))) {
447                                 openssl_public_encrypt($hash,$challenge,$r[0]['pubkey']);
448                                 openssl_public_encrypt($id_str,$encrypted_id,$r[0]['pubkey']);
449                         }
450                         else {
451                                 openssl_private_encrypt($hash,$challenge,$r[0]['prvkey']);
452                                 openssl_private_encrypt($id_str,$encrypted_id,$r[0]['prvkey']);
453                         }
454
455                         $challenge = bin2hex($challenge);
456                         $encrypted_id = bin2hex($encrypted_id);
457                 }
458                 else {
459                         $status = 1;
460                         $challenge = '';
461                         $encrypted_id = '';
462                 }
463
464                 if(($type === 'profile') && (strlen($sec))) {
465
466                         // URL reply
467
468                         if($dfrn_version < 2.2) {
469                                 $s = fetch_url($r[0]['poll'] 
470                                         . '?dfrn_id=' . $encrypted_id 
471                                         . '&type=profile-check'
472                                         . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
473                                         . '&challenge=' . $challenge
474                                         . '&sec=' . $sec
475                                 );
476                         }
477                         else {
478                                 $s = post_url($r[0]['poll'], array(
479                                         'dfrn_id' => $encrypted_id,
480                                         'type' => 'profile-check',
481                                         'dfrn_version' => DFRN_PROTOCOL_VERSION,
482                                         'challenge' => $challenge,
483                                         'sec' => $sec
484                                 ));
485                         }
486                         
487                         $profile = ((count($r) && $r[0]['nickname']) ? $r[0]['nickname'] : $nickname);
488
489                         switch($destination_url) {
490                                 case 'profile':
491                                         $dest = $a->get_baseurl() . '/profile/' . $profile . '?tab=profile';
492                                         break;
493                                 case 'photos':
494                                         $dest = $a->get_baseurl() . '/photos/' . $profile;
495                                         break;
496                                 case 'status':
497                                 case '':
498                                         $dest = $a->get_baseurl() . '/profile/' . $profile;
499                                         break;          
500                                 default:
501                                         $dest = $destination_url;
502                                         break;
503                         }
504
505                         logger("dfrn_poll: sec profile: " . $s, LOGGER_DATA);
506
507                         if(strlen($s) && strstr($s,'<?xml')) {
508
509                                 $xml = parse_xml_string($s);
510
511                                 logger('dfrn_poll: profile: parsed xml: ' . print_r($xml,true), LOGGER_DATA);
512
513                                 logger('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash);
514                                 logger('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec);
515  
516                                 
517                                 if(((int) $xml->status == 0) && ($xml->challenge == $hash)  && ($xml->sec == $sec)) {
518                                         $_SESSION['authenticated'] = 1;
519                                         $_SESSION['visitor_id'] = $r[0]['id'];
520                                         $_SESSION['visitor_home'] = $r[0]['url'];
521                                         $_SESSION['visitor_visiting'] = $r[0]['uid'];
522                                         info( sprintf(t('%s welcomes %s'), $r[0]['username'] , $r[0]['name']) . EOL);
523                                         // Visitors get 1 day session.
524                                         $session_id = session_id();
525                                         $expire = time() + 86400;
526                                         q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s' LIMIT 1",
527                                                 dbesc($expire),
528                                                 dbesc($session_id)
529                                         ); 
530                                 }
531                         
532                                 goaway($dest);
533                         }
534                         goaway($dest);
535                         // NOTREACHED
536
537                 }
538                 else {
539                         // XML reply
540                         header("Content-type: text/xml");
541                         echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
542                                 . '<dfrn_poll>' . "\r\n"
543                                 . "\t" . '<status>' . $status . '</status>' . "\r\n"
544                                 . "\t" . '<dfrn_version>' . DFRN_PROTOCOL_VERSION . '</dfrn_version>' . "\r\n"
545                                 . "\t" . '<dfrn_id>' . $encrypted_id . '</dfrn_id>' . "\r\n"
546                                 . "\t" . '<challenge>' . $challenge . '</challenge>' . "\r\n"
547                                 . '</dfrn_poll>' . "\r\n" ;
548                         killme();
549                         // NOTREACHED
550                 }
551         }
552 }
553
554