]> git.mxchange.org Git - friendica.git/blob - mod/dfrn_request.php
don't use load_view_file() except in email templates and install of htconfig - to...
[friendica.git] / mod / dfrn_request.php
1 <?php
2
3 /**
4  *
5  * Module: dfrn_request
6  *
7  * Purpose: Handles communication associated with the issuance of
8  * friend requests.
9  *
10  */
11
12 if(! function_exists('dfrn_request_init')) {
13 function dfrn_request_init(&$a) {
14
15         if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
16                 return;
17         }
18
19         if($a->argc > 1)
20                 $which = $a->argv[1];
21
22         profile_load($a,$which);
23         return;
24 }}
25
26
27 /**
28  * Function: dfrn_request_post
29  *
30  * Purpose:
31  * Handles multiple scenarios.
32  *
33  * Scenario 1:
34  * Clicking 'submit' on a friend request page.
35  *
36  * Scenario 2:
37  * Following Scenario 1, we are brought back to our home site
38  * in order to link our friend request with our own server cell.
39  * After logging in, we click 'submit' to approve the linkage.
40  *
41  */
42
43 if(! function_exists('dfrn_request_post')) {
44 function dfrn_request_post(&$a) {
45
46         if(($a->argc != 2) || (! count($a->profile)))
47                 return;
48
49
50         if($_POST['cancel']) {
51                 goaway($a->get_baseurl());
52         } 
53
54
55         /**
56          *
57          * Scenario 2: We've introduced ourself to another cell, then have been returned to our own cell
58          * to confirm the request, and then we've clicked submit (perhaps after logging in). 
59          * That brings us here:
60          *
61          */
62
63         if((x($_POST,'localconfirm')) && ($_POST['localconfirm'] == 1)) {
64
65                 /**
66                  * Ensure this is a valid request
67                  */
68
69                 if(local_user() && ($a->user['nickname'] == $a->argv[1]) && (x($_POST,'dfrn_url'))) {
70
71
72                         $dfrn_url    = notags(trim($_POST['dfrn_url']));
73                         $aes_allow   = (((x($_POST,'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0);
74                         $confirm_key = ((x($_POST,'confirm_key')) ? $_POST['confirm_key'] : "");
75
76                         $contact_record = null;
77         
78                         if(x($dfrn_url)) {
79
80                                 /**
81                                  * Lookup the contact based on their URL (which is the only unique thing we have at the moment)
82                                  */
83         
84                                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1",
85                                         intval(local_user()),
86                                         dbesc($dfrn_url)
87                                 );
88         
89                                 if(count($r)) {
90                                         if(strlen($r[0]['dfrn-id'])) {
91
92                                                 /**
93                                                  * We don't need to be here. It has already happened.
94                                                  */
95
96                                                 notice( t("This introduction has already been accepted.") . EOL );
97                                                 return;
98                                         }
99                                         else
100                                                 $contact_record = $r[0];
101                                 }
102         
103                                 if(is_array($contact_record)) {
104                                         $r = q("UPDATE `contact` SET `ret-aes` = %d WHERE `id` = %d LIMIT 1",
105                                                 intval($aes_allow),
106                                                 intval($contact_record['id'])
107                                         );
108                                 }
109                                 else {
110         
111                                         /**
112                                          * Scrape the other site's profile page to pick up the dfrn links, key, fn, and photo
113                                          */
114
115                                         require_once('Scrape.php');
116         
117                                         $parms = scrape_dfrn($dfrn_url);
118         
119                                         if(! count($parms)) {
120                                                 notice( t('Profile location is not valid or does not contain profile information.') . EOL );
121                                                 return;
122                                         }
123                                         else {
124                                                 if(! x($parms,'fn'))
125                                                         notice( t('Warning: profile location has no identifiable owner name.') . EOL );
126                                                 if(! x($parms,'photo'))
127                                                         notice( t('Warning: profile location has no profile photo.') . EOL );
128                                                 $invalid = validate_dfrn($parms);               
129                                                 if($invalid) {
130                                                         notice( sprintf( tt("%d required parameter was not found at the given location",
131                                                                                                 "%d required parameters were not found at the given location",
132                                                                                                 $invalid), $invalid) . EOL );
133                                                         return;
134                                                 }
135                                         }
136
137                                         $dfrn_request = $parms['dfrn-request'];
138
139                     /********* Escape the entire array ********/
140
141                                         dbesc_array($parms);
142
143                                         /******************************************/
144
145                                         /**
146                                          * Create a contact record on our site for the other person
147                                          */
148
149                                         $r = q("INSERT INTO `contact` ( `uid`, `created`,`url`, `name`, `nick`, `photo`, `site-pubkey`,
150                                                 `request`, `confirm`, `notify`, `poll`, `aes_allow`) 
151                                                 VALUES ( %d, '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)",
152                                                 intval(local_user()),
153                                                 datetime_convert(),
154                                                 dbesc($dfrn_url),
155                                                 $parms['fn'],
156                                                 $parms['nick'],
157                                                 $parms['photo'],
158                                                 $parms['key'],
159                                                 $parms['dfrn-request'],
160                                                 $parms['dfrn-confirm'],
161                                                 $parms['dfrn-notify'],
162                                                 $parms['dfrn-poll'],
163                                                 intval($aes_allow)
164                                         );
165                                 }
166
167                                 if($r) {
168                                         notice( t("Introduction complete.") . EOL);
169                                 }
170
171                                 /**
172                                  * Allow the blocked remote notification to complete
173                                  */
174
175                                 if(is_array($contact_record))
176                                         $dfrn_request = $contact_record['request'];
177
178                                 if(strlen($dfrn_request) && strlen($confirm_key))
179                                         $s = fetch_url($dfrn_request . '?confirm_key=' . $confirm_key);
180                                 
181                                 // (ignore reply, nothing we can do it failed)
182
183                                 goaway($dfrn_url);
184                                 return; // NOTREACHED
185
186                         }
187
188                 }
189
190                 // invalid/bogus request
191
192                 notice( t('Unrecoverable protocol error.') . EOL );
193                 goaway($a->get_baseurl());
194                 return; // NOTREACHED
195         }
196
197         /**
198          * Otherwise:
199          * 
200          * Scenario 1:
201          * We are the requestee. A person from a remote cell has made an introduction 
202          * on our profile web page and clicked submit. We will use their DFRN-URL to 
203          * figure out how to contact their cell.  
204          *
205          * Scrape the originating DFRN-URL for everything we need. Create a contact record
206          * and an introduction to show our user next time he/she logs in.
207          * Finally redirect back to the requestor so that their site can record the request.
208          * If our user (the requestee) later confirms this request, a record of it will need 
209          * to exist on the requestor's cell in order for the confirmation process to complete.. 
210          *
211          * It's possible that neither the requestor or the requestee are logged in at the moment,
212          * and the requestor does not yet have any credentials to the requestee profile.
213          *
214          * Who is the requestee? We've already loaded their profile which means their nickname should be
215          * in $a->argv[1] and we should have their complete info in $a->profile.
216          *
217          */
218
219         if(! (is_array($a->profile) && count($a->profile))) {
220                 notice( t('Profile unavailable.') . EOL);
221                 return;
222         }
223
224         $nickname       = $a->profile['nickname'];
225         $notify_flags   = $a->profile['notify-flags'];
226         $uid            = $a->profile['uid'];
227         $maxreq         = intval($a->profile['maxreq']);
228         $contact_record = null;
229         $failed         = false;
230         $parms          = null;
231
232
233         if( x($_POST,'dfrn_url')) {
234
235                 /**
236                  * Block friend request spam
237                  */
238
239                 if($maxreq) {
240                         $r = q("SELECT * FROM `intro` WHERE `datetime` > '%s' AND `uid` = %d",
241                                 dbesc(datetime_convert('UTC','UTC','now - 24 hours')),
242                                 intval($uid)
243                         );
244                         if(count($r) > $maxreq) {
245                                 notice( sprintf( t('%s has received too many connection requests today.'),  $a->profile['name']) . EOL);
246                                 notice( t('Spam protection measures have been invoked.') . EOL);
247                                 notice( t('Friends are advised to please try again in 24 hours.') . EOL);
248                                 return;
249                         } 
250                 }
251
252                 /**
253                  *
254                  * Cleanup old introductions that remain blocked. 
255                  * Also remove the contact record, but only if there is no existing relationship
256                  *
257                  */
258
259                 $r = q("SELECT `intro`.*, `intro`.`id` AS `iid`, `contact`.`id` AS `cid`, `contact`.`rel` 
260                         FROM `intro` LEFT JOIN `contact` on `intro`.`contact-id` = `contact`.`id`
261                         WHERE `intro`.`blocked` = 1 AND `contact`.`self` = 0 AND `intro`.`datetime` < UTC_TIMESTAMP() - INTERVAL 30 MINUTE ");
262                 if(count($r)) {
263                         foreach($r as $rr) {
264                                 if(! $rr['rel']) {
265                                         q("DELETE FROM `contact` WHERE `id` = %d LIMIT 1",
266                                                 intval($rr['cid'])
267                                         );
268                                 }
269                                 q("DELETE FROM `intro` WHERE `id` = %d LIMIT 1",
270                                         intval($rr['iid'])
271                                 );
272                         }
273                 }
274
275                 $url = trim($_POST['dfrn_url']);
276                 if(! strlen($url)) {
277                         notice( t("Invalid locator") . EOL );
278                         return;
279                 }
280
281                 // Canonicalise email-style profile locator
282
283                 $url = webfinger_dfrn($url);
284
285                 if(substr($url,0,5) === 'stat:') {
286                         $network = 'stat';
287                         $url = substr($url,5);
288                 }
289                 else {
290                         $network = 'dfrn';
291                 }
292
293                 logger('dfrn_request: url: ' . $url);
294
295                 if(! strlen($url)) {
296                         notice( t("Unable to resolve your name at the provided location.") . EOL);                      
297                         return;
298                 }
299
300
301                 if($network === 'dfrn') {
302                         $ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1", 
303                                 intval($uid),
304                                 dbesc($url)
305                         );
306
307                         if(count($ret)) {
308                                 if(strlen($ret[0]['issued-id'])) {
309                                         notice( t('You have already introduced yourself here.') . EOL );
310                                         return;
311                                 }
312                                 elseif($ret[0]['rel'] == REL_BUD) {
313                                         notice( sprintf( t('Apparently you are already friends with %s.'), $a->profile['name']) . EOL);
314                                         return;
315                                 }
316                                 else {
317                                         $contact_record = $ret[0];
318                                         $parms = array('dfrn-request' => $ret[0]['request']);
319                                 }
320                         }
321
322                         $issued_id = random_string();
323
324                         if(is_array($contact_record)) {
325                                 // There is a contact record but no issued-id, so this
326                                 // is a reciprocal introduction from a known contact
327                                 $r = q("UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d LIMIT 1",
328                                         dbesc($issued_id),
329                                         intval($contact_record['id'])
330                                 );
331                         }
332                         else {
333                                 if(! validate_url($url)) {
334                                         notice( t('Invalid profile URL.') . EOL);
335                                         goaway($a->get_baseurl() . '/' . $a->cmd);
336                                         return; // NOTREACHED
337                                 }
338
339                                 if(! allowed_url($url)) {
340                                         notice( t('Disallowed profile URL.') . EOL);
341                                         goaway($a->get_baseurl() . '/' . $a->cmd);
342                                         return; // NOTREACHED
343                                 }
344                         
345
346                                 require_once('Scrape.php');
347
348                                 $parms = scrape_dfrn($url);
349
350                                 if(! count($parms)) {
351                                         notice( t('Profile location is not valid or does not contain profile information.') . EOL );
352                                         goaway($a->get_baseurl() . '/' . $a->cmd);
353                                 }
354                                 else {
355                                         if(! x($parms,'fn'))
356                                                 notice( t('Warning: profile location has no identifiable owner name.') . EOL );
357                                         if(! x($parms,'photo'))
358                                                 notice( t('Warning: profile location has no profile photo.') . EOL );
359                                         $invalid = validate_dfrn($parms);               
360                                         if($invalid) {
361                                                 notice( sprintf( tt("%d required parameter was not found at the given location",
362                                                                                         "%d required parameters were not found at the given location",
363                                                                                         $invalid), $invalid) . EOL );
364         
365                                                 return;
366                                         }
367                                 }
368
369
370                                 $parms['url'] = $url;
371                                 $parms['issued-id'] = $issued_id;
372
373
374                                 dbesc_array($parms);
375                                 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `issued-id`, `photo`, `site-pubkey`,
376                                         `request`, `confirm`, `notify`, `poll` )
377                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
378                                         intval($uid),
379                                         datetime_convert(),
380                                         $parms['url'],
381                                         $parms['fn'],
382                                         $parms['nick'],
383                                         $parms['issued-id'],
384                                         $parms['photo'],
385                                         $parms['key'],
386                                         $parms['dfrn-request'],
387                                         $parms['dfrn-confirm'],
388                                         $parms['dfrn-notify'],
389                                         $parms['dfrn-poll']
390                                 );
391
392                                 // find the contact record we just created
393                                 if($r) {        
394                                         $r = q("SELECT `id` FROM `contact` 
395                                                 WHERE `uid` = %d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1",
396                                                 intval($uid),
397                                                 $parms['url'],
398                                                 $parms['issued-id']
399                                         );
400                                         if(count($r)) 
401                                                 $contact_record = $r[0];
402                                 }
403         
404                         }
405                         if($r === false) {
406                                 notice( t('Failed to update contact record.') . EOL );
407                                 return;
408                         }
409
410                         $hash = random_string() . (string) time();   // Generate a confirm_key
411         
412                         if(is_array($contact_record)) {
413                                 $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
414                                         VALUES ( %d, %d, 1, %d, '%s', '%s', '%s' )",
415                                         intval($uid),
416                                         intval($contact_record['id']),
417                                         ((x($_POST,'knowyou') && ($_POST['knowyou'] == 1)) ? 1 : 0),
418                                         dbesc(notags(trim($_POST['dfrn-request-message']))),
419                                         dbesc($hash),
420                                         dbesc(datetime_convert())
421                                 );
422                         }
423         
424                         // This notice will only be seen by the requestor if the requestor and requestee are on the same server.
425
426                         if(! $failed) 
427                                 notice( t('Your introduction has been sent.') . EOL );
428
429                         // "Homecoming" - send the requestor back to their site to record the introduction.
430
431                         $dfrn_url = bin2hex($a->get_baseurl() . '/profile/' . $nickname);
432                         $aes_allow = ((function_exists('openssl_encrypt')) ? 1 : 0);
433
434                         goaway($parms['dfrn-request'] . "?dfrn_url=$dfrn_url" 
435                                 . '&dfrn_version=' . DFRN_PROTOCOL_VERSION 
436                                 . '&confirm_key='  . $hash 
437                                 . (($aes_allow) ? "&aes_allow=1" : "")
438                         );
439                         // NOTREACHED
440                         // END $network === 'dfrn'
441                 }
442                 elseif($network === 'stat') {
443                         
444                         /**
445                          *
446                          * OStatus network
447                          * Check contact existence
448                          * Try and scrape together enough information to create a contact record, with us as REL_VIP
449                          * Substitute our user's feed URL into $url template
450                          * Send the subscriber home to subscribe
451                          *
452                          */
453
454                         $url = str_replace('{uri}', $a->get_baseurl() . '/dfrn_poll/' . $nickname, $url);
455                         goaway($url);
456                         // NOTREACHED
457                         // END $network === 'stat'
458                 }
459
460         }       return;
461 }}
462
463
464
465
466 if(! function_exists('dfrn_request_content')) {
467 function dfrn_request_content(&$a) {
468
469         
470
471         if(($a->argc != 2) || (! count($a->profile)))
472                 return "";
473
474
475         // "Homecoming". Make sure we're logged in to this site as the correct user. Then offer a confirm button
476         // to send us to the post section to record the introduction.
477
478         if(x($_GET,'dfrn_url')) {
479
480                 if(! local_user()) {
481                         notice( t("Please login to confirm introduction.") . EOL );
482
483                         /* setup the return URL to come back to this page if they use openid */
484
485                         $stripped = str_replace('q=','',$a->query_string);
486                         $_SESSION['return_url'] = trim($stripped,'/');
487
488                         return login();
489                 }
490
491                 // Edge case, but can easily happen in the wild. This person is authenticated, 
492                 // but not as the person who needs to deal with this request.
493
494                 if ($a->user['nickname'] != $a->argv[1]) {
495                         notice( t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile.") . EOL);
496                         return login();
497                 }
498
499                 $dfrn_url = notags(trim(hex2bin($_GET['dfrn_url'])));
500                 $aes_allow = (((x($_GET,'aes_allow')) && ($_GET['aes_allow'] == 1)) ? 1 : 0);
501                 $confirm_key = (x($_GET,'confirm_key') ? $_GET['confirm_key'] : "");
502                 $o .= file_get_contents("view/dfrn_req_confirm.tpl");
503                 $o  = replace_macros($o,array(
504                         '$dfrn_url' => $dfrn_url,
505                         '$aes_allow' => (($aes_allow) ? '<input type="hidden" name="aes_allow" value="1" />' : "" ),
506                         '$confirm_key' => $confirm_key,
507                         '$welcome' => sprintf( t('Welcome home %s.'), $a->user['username']),
508                         '$please' => sprintf( t('Please confirm your introduction/connection request to %s.'), $dfrn_url),
509                         '$submit' => t('Confirm'),
510                         '$uid' => $_SESSION['uid'],
511                         '$nickname' => $a->user['nickname'],
512                         'dfrn_rawurl' => $_GET['dfrn_url']
513                         ));
514                 return $o;
515
516         }
517         elseif((x($_GET,'confirm_key')) && strlen($_GET['confirm_key'])) { 
518
519                 // we are the requestee and it is now safe to send our user their introduction,
520                 // We could just unblock it, but first we have to jump through a few hoops to 
521                 // send an email, or even to find out if we need to send an email. 
522
523                 $intro = q("SELECT * FROM `intro` WHERE `hash` = '%s' LIMIT 1",
524                         dbesc($_GET['confirm_key'])
525                 );
526
527                 if(count($intro)) {
528
529                         $r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
530                                 WHERE `contact`.`id` = %d LIMIT 1",
531                                 intval($intro[0]['contact-id'])
532                         );
533
534                         $auto_confirm = false;
535
536                         if(count($r)) {
537                                 if($r[0]['page-flags'] != PAGE_NORMAL)
538                                         $auto_confirm = true;                           
539                                 if(($r[0]['notify-flags'] & NOTIFY_INTRO) && (! $auto_confirm)) {
540                                         $email_tpl = load_view_file('view/request_notify_eml.tpl');
541                                         $email = replace_macros($email_tpl, array(
542                                                 '$requestor' => ((strlen(stripslashes($r[0]['name']))) ? stripslashes($r[0]['name']) : t('[Name Withheld]')),
543                                                 '$url' => stripslashes($r[0]['url']),
544                                                 '$myname' => $r[0]['username'],
545                                                 '$siteurl' => $a->get_baseurl(),
546                                                 '$sitename' => $a->config['sitename']
547                                         ));
548                                         $res = mail($r[0]['email'], 
549                                             t("Introduction received at ") . $a->config['sitename'],
550                                                 $email,
551                                                 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
552                                                 . 'Content-type: text/plain; charset=UTF-8' . "\n"
553                                                 . 'Content-transfer-encoding: 8bit' );
554
555                                         // This is a redundant notification - no point throwing errors if it fails.
556                                 }
557                                 if($auto_confirm) {
558                                         require_once('mod/dfrn_confirm.php');
559                                         $handsfree = array(
560                                                 'uid' => $r[0]['uid'],
561                                                 'node' => $r[0]['nickname'],
562                                                 'dfrn_id' => $r[0]['issued-id'],
563                                                 'intro_id' => $intro[0]['id'],
564                                                 'duplex' => (($r[0]['page-flags'] == PAGE_FREELOVE) ? 1 : 0)
565                                         );
566                                         dfrn_confirm_post($a,$handsfree);
567                                 }
568
569                         }
570
571                         if(! $auto_confirm) {
572
573                                 // If we are auto_confirming, this record will have already been nuked
574                                 // in dfrn_confirm_post()
575
576                                 $r = q("UPDATE `intro` SET `blocked` = 0 WHERE `hash` = '%s' LIMIT 1",
577                                         dbesc($_GET['confirm_key'])
578                                 );
579                         }
580                 }
581                 killme();
582                 return; // NOTREACHED
583         }
584         else {
585
586                 /**
587                  * Normal web request. Display our user's introduction form.
588                  */
589  
590                 if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
591                         notice( t('Public access denied.') . EOL);
592                         return;
593                 }
594
595
596                 /**
597                  * Try to auto-fill the profile address
598                  */
599
600                 if(local_user()) {
601                         if(strlen($a->path)) {
602                                 $myaddr = $a->get_baseurl() . '/profile/' . $a->user['nickname'];
603                         }
604                         else {
605                                 $myaddr = $a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3 );
606                         }
607                 }
608                 elseif(x($_GET,'addr')) {
609                         $myaddr = hex2bin($_GET['addr']);
610                 }
611                 else {
612                         /* $_GET variables are already urldecoded */ 
613                         $myaddr = ((x($_GET,'address')) ? $_GET['address'] : '');
614                 }
615
616                 /**
617                  *
618                  * The auto_request form only has the profile address
619                  * because nobody is going to read the comments and 
620                  * it doesn't matter if they know you or not.
621                  *
622                  */
623
624                 if($a->profile['page-flags'] == PAGE_NORMAL)
625                         $tpl = file_get_contents('view/dfrn_request.tpl');
626                 else
627                         $tpl = file_get_contents('view/auto_request.tpl');
628
629                 $o .= replace_macros($tpl,array(
630                         '$header' => t('Friend/Connection Request'),
631                         '$desc' => t('Examples: jojo@demo.friendika.com, http://demo.friendika.com/profile/jojo, testuser@identi.ca'),
632                         '$pls_answer' => t('Please answer the following:'),
633                         '$does_know' => t('Does $name know you?'),
634                         '$yes' => t('Yes'),
635                         '$no' => t('No'),
636                         '$add_note' => t('Add a personal note:'),
637                         '$page_desc' => t("Please enter your 'Identity Address' from one of the following supported social networks:"),
638                         '$friendika' => t('Friendika'),
639                         '$statusnet' => t('StatusNet/Federated Social Web'),
640                         '$private_net' => t("Private \x28secure\x29 network"),
641                         '$public_net' => t("Public \x28insecure\x29 network"),
642                         '$your_address' => t('Your Identity Address:'),
643                         '$submit' => t('Submit Request'),
644                         '$cancel' => t('Cancel'),
645                         '$nickname' => $a->argv[1],
646                         '$name' => $a->profile['name'],
647                         '$myaddr' => $myaddr
648                 ));
649                 return $o;
650         }
651
652         return; // Somebody is fishing.
653 }}