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