]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
PHPDoc + some Notices
[friendica-addons.git] / facebook / facebook.php
1 <?php
2 /**
3  * Name: Facebook Connector
4  * Version: 1.2
5  * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
6  *         Tobias Hößl <https://github.com/CatoTH/>
7  */
8
9 /**
10  * Installing the Friendica/Facebook connector
11  *
12  * Detailed instructions how to use this plugin can be found at
13  * https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector
14  *
15  * Vidoes and embeds will not be posted if there is no other content. Links 
16  * and images will be converted to a format suitable for the Facebook API and 
17  * long posts truncated - with a link to view the full post. 
18  *
19  * Facebook contacts will not be able to view private photos, as they are not able to
20  * authenticate to your site to establish identity. We will address this 
21  * in a future release.
22  */
23  
24  /** TODO
25  * - Implement a method for the administrator to delete all configuration data the plugin has created,
26  *   e.g. the app_access_token
27  */
28
29 // Size of maximum post length increased
30 // see http://www.facebook.com/schrep/posts/203969696349811
31 // define('FACEBOOK_MAXPOSTLEN', 420);
32 define('FACEBOOK_MAXPOSTLEN', 63206);
33 define('FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL', 259200); // 3 days
34 define('FACEBOOK_DEFAULT_POLL_INTERVAL', 60); // given in minutes
35 define('FACEBOOK_MIN_POLL_INTERVAL', 5);
36
37 require_once('include/security.php');
38
39 function facebook_install() {
40         register_hook('post_local',       'addon/facebook/facebook.php', 'facebook_post_local');
41         register_hook('notifier_normal',  'addon/facebook/facebook.php', 'facebook_post_hook');
42         register_hook('jot_networks',     'addon/facebook/facebook.php', 'facebook_jot_nets');
43         register_hook('connector_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
44         register_hook('cron',             'addon/facebook/facebook.php', 'facebook_cron');
45         register_hook('enotify',          'addon/facebook/facebook.php', 'facebook_enotify');
46         register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
47 }
48
49
50 function facebook_uninstall() {
51         unregister_hook('post_local',       'addon/facebook/facebook.php', 'facebook_post_local');
52         unregister_hook('notifier_normal',  'addon/facebook/facebook.php', 'facebook_post_hook');
53         unregister_hook('jot_networks',     'addon/facebook/facebook.php', 'facebook_jot_nets');
54         unregister_hook('connector_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
55         unregister_hook('cron',             'addon/facebook/facebook.php', 'facebook_cron');
56         unregister_hook('enotify',          'addon/facebook/facebook.php', 'facebook_enotify');
57         unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
58
59         // hook moved
60         unregister_hook('post_local_end',  'addon/facebook/facebook.php', 'facebook_post_hook');
61         unregister_hook('plugin_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
62 }
63
64
65 /* declare the facebook_module function so that /facebook url requests will land here */
66
67 function facebook_module() {}
68
69
70
71 // If a->argv[1] is a nickname, this is a callback from Facebook oauth requests.
72 // If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API
73
74 /**
75  * @param App $a
76  */
77 function facebook_init(&$a) {
78
79         if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
80                 logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
81                 
82                 if (x($_REQUEST, "hub_verify_token")) {
83                         // this is the verification callback while registering for real time updates
84                         
85                         $verify_token = get_config('facebook', 'cb_verify_token');
86                         if ($verify_token != $_REQUEST["hub_verify_token"]) {
87                                 logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
88                                 return;
89                         }
90                         
91                         if (x($_REQUEST, "hub_challenge")) {
92                                 logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
93                                 echo $_REQUEST["hub_challenge"];
94                                 die();
95                         }
96                 }
97                 
98                 require_once('include/items.php');
99                 
100                 // this is a status update
101                 $content = file_get_contents("php://input");
102                 if (is_numeric($content)) $content = file_get_contents("php://input");
103                 $js = json_decode($content);
104                 logger(print_r($js, true), LOGGER_DATA);
105                 
106                 if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
107                         logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
108                         return;
109                 }
110                 
111                 $affected_users = array("feed" => array(), "friends" => array());
112                 
113                 foreach ($js->entry as $entry) {
114                         $fbuser = $entry->uid;
115                         foreach ($entry->changed_fields as $field) {
116                                 if (!isset($affected_users[$field])) {
117                                         logger('facebook_init: Unknown field "' . $field . '"');
118                                         continue;
119                                 }
120                                 if (in_array($fbuser, $affected_users[$field])) continue;
121                                 
122                                 $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
123                                 if(! count($r))
124                                         continue;
125                                 $uid = $r[0]['uid'];
126                                 
127                                 $access_token = get_pconfig($uid,'facebook','access_token');
128                                 if(! $access_token)
129                                         return;
130                                 
131                                 switch ($field) {
132                                         case "feed":
133                                                 logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
134                                                 
135                                                 if(! get_pconfig($uid,'facebook','no_wall')) {
136                                                         $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
137                                                         $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
138                                                         if($s) {
139                                                                 $j = json_decode($s);
140                                                                 if (isset($j->data)) {
141                                                                         logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
142                                                                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
143                                                                 } else {
144                                                                         logger('facebook_init: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
145                                                                 }
146                                                         }
147                                                 }
148                                                 
149                                         break;
150                                         case "friends":
151                                                 logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
152                                                 
153                                                 fb_get_friends($uid, false);
154                                                 set_pconfig($uid,'facebook','friend_check',time());
155                                         break;
156                                         default:
157                                                 logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
158                                 }
159                                 $affected_users[$field][] = $fbuser;
160                         }
161                 }
162         }
163
164         
165         if($a->argc != 2)
166                 return;
167         $nick = $a->argv[1];
168         if(strlen($nick))
169                 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
170                                 dbesc($nick)
171                 );
172         if(!(isset($r) && count($r)))
173                 return;
174
175         $uid           = $r[0]['uid'];
176         $auth_code     = (x($_GET, 'code') ? $_GET['code'] : '');
177         $error         = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
178
179
180         if($error)
181                 logger('facebook_init: Error: ' . $error);
182
183         if($auth_code && $uid) {
184
185                 $appid = get_config('facebook','appid');
186                 $appsecret = get_config('facebook', 'appsecret');
187
188                 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
189                         . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
190                         . urlencode($a->get_baseurl() . '/facebook/' . $nick) 
191                         . '&code=' . $auth_code);
192
193                 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
194
195                 if(strpos($x,'access_token=') !== false) {
196                         $token = str_replace('access_token=', '', $x);
197                         if(strpos($token,'&') !== false)
198                                 $token = substr($token,0,strpos($token,'&'));
199                         set_pconfig($uid,'facebook','access_token',$token);
200                         set_pconfig($uid,'facebook','post','1');
201                         if(get_pconfig($uid,'facebook','no_linking') === false)
202                                 set_pconfig($uid,'facebook','no_linking',1);
203                         fb_get_self($uid);
204                         fb_get_friends($uid, true);
205                         fb_consume_all($uid);
206
207                 }
208
209         }
210
211 }
212
213
214 /**
215  * @param int $uid
216  */
217 function fb_get_self($uid) {
218         $access_token = get_pconfig($uid,'facebook','access_token');
219         if(! $access_token)
220                 return;
221         $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
222         if($s) {
223                 $j = json_decode($s);
224                 set_pconfig($uid,'facebook','self_id',(string) $j->id);
225         }
226 }
227
228 /**
229  * @param int $uid
230  * @param string $access_token
231  * @param array $persons
232  */
233 function fb_get_friends_sync_new($uid, $access_token, $persons) {
234     $persons_todo = array();
235     foreach ($persons as $person) {
236         $link = 'http://facebook.com/profile.php?id=' . $person->id;
237
238         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
239             intval($uid),
240             dbesc($link)
241         );
242
243         if (count($r) == 0) {
244             logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
245             $persons_todo[] = $person;
246         }
247
248         if (count($persons_todo) > 0) fb_get_friends_sync_full($uid, $access_token, $persons_todo);
249     }
250 }
251
252 /**
253  * @param int $uid
254  * @param object $contact
255  */
256 function fb_get_friends_sync_parsecontact($uid, $contact) {
257     $contact->link = 'http://facebook.com/profile.php?id=' . $contact->id;
258
259     // If its a page then set the first name from the username
260     if (!$contact->first_name and $contact->username)
261         $contact->first_name = $contact->username;
262
263     // check if we already have a contact
264
265     $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
266         intval($uid),
267         dbesc($contact->link)
268     );
269
270     if(count($r)) {
271
272         // check that we have all the photos, this has been known to fail on occasion
273
274         if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
275             require_once("Photo.php");
276
277             $photos = import_profile_photo('https://graph.facebook.com/' . $contact->id . '/picture', $uid, $r[0]['id']);
278
279             $r = q("UPDATE `contact` SET `photo` = '%s',
280                                         `thumb` = '%s',
281                                         `micro` = '%s',
282                                         `name-date` = '%s',
283                                         `uri-date` = '%s',
284                                         `avatar-date` = '%s'
285                                         WHERE `id` = %d LIMIT 1
286                                 ",
287                 dbesc($photos[0]),
288                 dbesc($photos[1]),
289                 dbesc($photos[2]),
290                 dbesc(datetime_convert()),
291                 dbesc(datetime_convert()),
292                 dbesc(datetime_convert()),
293                 intval($r[0]['id'])
294             );
295         }
296         return;
297     }
298     else {
299
300         // create contact record
301         $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
302                                 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
303                                 `writable`, `blocked`, `readonly`, `pending` )
304                                 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
305             intval($uid),
306             dbesc(datetime_convert()),
307             dbesc($contact->link),
308             dbesc(normalise_link($contact->link)),
309             dbesc(''),
310             dbesc(''),
311             dbesc($contact->id),
312             dbesc('facebook ' . $contact->id),
313             dbesc($contact->name),
314             dbesc(($contact->nickname) ? $contact->nickname : strtolower($contact->first_name)),
315             dbesc('https://graph.facebook.com/' . $contact->id . '/picture'),
316             dbesc(NETWORK_FACEBOOK),
317             intval(CONTACT_IS_FRIEND),
318             intval(1),
319             intval(1)
320         );
321     }
322
323     $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
324         dbesc($contact->link),
325         intval($uid)
326     );
327
328     if(! count($r)) {
329         return;
330     }
331
332     $contact = $r[0];
333     $contact_id  = $r[0]['id'];
334
335     require_once("Photo.php");
336
337     $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
338
339     $r = q("UPDATE `contact` SET `photo` = '%s',
340                         `thumb` = '%s',
341                         `micro` = '%s',
342                         `name-date` = '%s',
343                         `uri-date` = '%s',
344                         `avatar-date` = '%s'
345                         WHERE `id` = %d LIMIT 1
346                 ",
347         dbesc($photos[0]),
348         dbesc($photos[1]),
349         dbesc($photos[2]),
350         dbesc(datetime_convert()),
351         dbesc(datetime_convert()),
352         dbesc(datetime_convert()),
353         intval($contact_id)
354     );
355 }
356
357 /**
358  * @param int $uid
359  * @param string $access_token
360  * @param array $persons
361  */
362 function fb_get_friends_sync_full($uid, $access_token, $persons) {
363     if (count($persons) == 0) return;
364     $nums = Ceil(count($persons) / 50);
365     for ($i = 0; $i < $nums; $i++) {
366         $batch_request = array();
367         for ($j = $i * 50; $j < ($i+1) * 50 && $j < count($persons); $j++) $batch_request[] = array('method'=>'GET', 'relative_url'=>$persons[$j]->id);
368         $s = post_url('https://graph.facebook.com/', array('access_token' => $access_token, 'batch' => json_encode($batch_request)));
369         if($s) {
370             $results = json_decode($s);
371             logger('fb_get_friends: info: ' . print_r($results,true), LOGGER_DATA);
372             foreach ($results as $contact) {
373                 if ($contact->code != 200) logger('fb_get_friends: not found: ' . print_r($contact,true), LOGGER_DEBUG);
374                 else fb_get_friends_sync_parsecontact($uid, json_decode($contact->body));
375             }
376         }
377     }
378 }
379
380
381
382 // if $fullsync is true, only new contacts are searched for
383
384 /**
385  * @param int $uid
386  * @param bool $fullsync
387  */
388 function fb_get_friends($uid, $fullsync = true) {
389
390         $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
391                 intval($uid)
392         );
393         if(! count($r))
394                 return;
395
396         $access_token = get_pconfig($uid,'facebook','access_token');
397
398         $no_linking = get_pconfig($uid,'facebook','no_linking');
399         if($no_linking)
400                 return;
401
402         if(! $access_token)
403                 return;
404         $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
405         if($s) {
406                 logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
407                 $j = json_decode($s);
408                 logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
409                 if(! $j->data)
410                         return;
411
412             $persons_todo = array();
413         foreach($j->data as $person) $persons_todo[] = $person;
414
415         if ($fullsync)
416             fb_get_friends_sync_full($uid, $access_token, $persons_todo);
417         else
418             fb_get_friends_sync_new($uid, $access_token, $persons_todo);
419         }
420 }
421
422 // This is the POST method to the facebook settings page
423 // Content is posted to Facebook in the function facebook_post_hook() 
424
425 /**
426  * @param App $a
427  */
428 function facebook_post(&$a) {
429
430         $uid = local_user();
431         if($uid){
432
433                 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
434                 set_pconfig($uid,'facebook','post_by_default', $value);
435
436                 $no_linking = get_pconfig($uid,'facebook','no_linking');
437
438                 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
439                 set_pconfig($uid,'facebook','no_wall',$no_wall);
440
441                 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
442                 set_pconfig($uid,'facebook','private_wall',$private_wall);
443         
444
445                 set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
446
447                 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
448                 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
449
450                 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
451
452                 if((! intval($no_linking)) && (! intval($linkvalue))) {
453                         $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
454                                 intval($uid),
455                                 dbesc(NETWORK_FACEBOOK)
456                         );
457                         if(count($r)) {
458                                 require_once('include/Contact.php');
459                                 foreach($r as $rr)
460                                         contact_remove($rr['id']);
461                         }
462                 }
463                 elseif(intval($no_linking) && intval($linkvalue)) {
464                         // FB linkage is now allowed - import stuff.
465                         fb_get_self($uid);
466                         fb_get_friends($uid, true);
467                         fb_consume_all($uid);
468                 }
469
470                 info( t('Settings updated.') . EOL);
471         } 
472
473         return;         
474 }
475
476 // Facebook settings form
477
478 /**
479  * @param App $a
480  * @return string
481  */
482 function facebook_content(&$a) {
483
484         if(! local_user()) {
485                 notice( t('Permission denied.') . EOL);
486                 return '';
487         }
488
489         if($a->argc > 1 && $a->argv[1] === 'remove') {
490                 del_pconfig(local_user(),'facebook','post');
491                 info( t('Facebook disabled') . EOL);
492         }
493
494         if($a->argc > 1 && $a->argv[1] === 'friends') {
495                 fb_get_friends(local_user(), true);
496                 info( t('Updating contacts') . EOL);
497         }
498
499         $o = '';
500         
501         $fb_installed = false;
502         if (get_pconfig(local_user(),'facebook','post')) {
503                 $access_token = get_pconfig(local_user(),'facebook','access_token');
504                 if ($access_token) {
505                         $private_wall = intval(get_pconfig(local_user(),'facebook','private_wall'));
506                         $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
507                         if($s) {
508                                 $j = json_decode($s);
509                                 if (isset($j->data)) $fb_installed = true;
510                         }
511                 }
512         }
513         
514         $appid = get_config('facebook','appid');
515
516         if(! $appid) {
517                 notice( t('Facebook API key is missing.') . EOL);
518                 return '';
519         }
520
521         $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' 
522                 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
523
524         $o .= '<h3>' . t('Facebook Connect') . '</h3>';
525
526         if(! $fb_installed) { 
527                 $o .= '<div id="facebook-enable-wrapper">';
528
529                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
530                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
531                 $o .= '</div>';
532         }
533
534         if($fb_installed) {
535                 $o .= '<div id="facebook-disable-wrapper">';
536
537                 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
538
539                 $o .= '<div id="facebook-enable-wrapper">';
540
541                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
542                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . '</a>';
543                 $o .= '</div>';
544         
545                 $o .= '<div id="facebook-post-default-form">';
546                 $o .= '<form action="facebook" method="post" >';
547                 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
548                 $checked = (($post_by_default) ? ' checked="checked" ' : '');
549                 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
550
551                 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
552                 $checked = (($no_linking) ? '' : ' checked="checked" ');
553                 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
554
555                 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
556                 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
557                 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
558
559                 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
560                 $checked = (($private_wall) ? ' checked="checked" ' : '');
561                 $o .= '<input type="checkbox" name="facebook_private_wall" value="1"' . $checked . '/>' . ' ' . t('On this website your Facebook profile wall conversations will only be visible to you') . EOL ;
562
563
564                 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
565                 $checked = (($no_wall) ? ' checked="checked" ' : '');
566                 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
567
568                 $o .= '<p>' . t('If you choose to link conversations and leave both of these boxes unchecked, your Facebook profile wall will be merged with your profile wall on this website and your privacy settings on this website will be used to determine who may see the conversations.') . '</p>';
569
570
571                 $blocked_apps = get_pconfig(local_user(),'facebook','blocked_apps');
572
573                 $o .= '<div><label id="blocked-apps-label" for="blocked-apps">' . t('Comma separated applications to ignore') . ' </label></div>';
574         $o .= '<div><textarea id="blocked-apps" name="blocked_apps" >' . htmlspecialchars($blocked_apps) . '</textarea></div>';
575
576                 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
577         }
578
579         return $o;
580 }
581
582
583 /**
584  * @param App $a
585  * @param null|object $b
586  * @return mixed
587  */
588 function facebook_cron($a,$b) {
589
590         $last = get_config('facebook','last_poll');
591         
592         $poll_interval = intval(get_config('facebook','poll_interval'));
593         if(! $poll_interval)
594                 $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
595
596         if($last) {
597                 $next = $last + $poll_interval;
598                 if($next > time()) 
599                         return;
600         }
601
602         logger('facebook_cron');
603
604
605         // Find the FB users on this site and randomize in case one of them
606         // uses an obscene amount of memory. It may kill this queue run
607         // but hopefully we'll get a few others through on each run. 
608
609         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
610         if(count($r)) {
611                 foreach($r as $rr) {
612                         if(get_pconfig($rr['uid'],'facebook','no_linking'))
613                                 continue;
614                         $ab = intval(get_config('system','account_abandon_days'));
615                         if($ab > 0) {
616                                 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
617                                         intval($rr['uid']),
618                                         intval($ab)
619                                 );
620                                 if(! count($z))
621                                         continue;
622                         }
623
624                         // check for new friends once a day
625                         $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
626                         if($last_friend_check) 
627                                 $next_friend_check = $last_friend_check + 86400;
628                         else
629                             $next_friend_check = 0;
630                         if($next_friend_check <= time()) {
631                                 fb_get_friends($rr['uid'], true);
632                                 set_pconfig($rr['uid'],'facebook','friend_check',time());
633                         }
634                         fb_consume_all($rr['uid']);
635                 }
636         }
637         
638         if (get_config('facebook', 'realtime_active') == 1) {
639                 if (!facebook_check_realtime_active()) {
640                         
641                         logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
642                         facebook_subscription_add_users();
643                         
644                         if (facebook_check_realtime_active()) 
645                                 logger('facebook_cron: Successful', LOGGER_NORMAL);
646                         else {
647                                 logger('facebook_cron: Failed', LOGGER_NORMAL);
648                                 
649                                 if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) {
650                                         $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'), 
651                                                 "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannot be solved automatically. Maybe a permission issue?\n\nPlease try to re-activate it on " . $a->config["system"]["url"] . "/admin/plugins/facebook\n\nThis e-mail will only be sent once.",
652                                                 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
653                                                 . 'Content-type: text/plain; charset=UTF-8' . "\n"
654                                                 . 'Content-transfer-encoding: 8bit'
655                                         );
656                                         
657                                         set_config('facebook', 'realtime_err_mailsent', 1);
658                                 }
659                         }
660                 } else { // !facebook_check_realtime_active()
661                         del_config('facebook', 'realtime_err_mailsent');
662                 }
663         }
664         
665         set_config('facebook','last_poll', time());
666
667 }
668
669
670 /**
671  * @param App $a
672  * @param null|object $b
673  */
674 function facebook_plugin_settings(&$a,&$b) {
675
676         $b .= '<div class="settings-block">';
677         $b .= '<h3>' . t('Facebook') . '</h3>';
678         $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
679         $b .= '</div>';
680
681 }
682
683
684 /**
685  * @param App $a
686  * @param null|object $o
687  */
688 function facebook_plugin_admin(&$a, &$o){
689
690
691         $o = '<input type="hidden" name="form_security_token" value="' . get_form_security_token("fbsave") . '">';
692         
693         $o .= '<h4>' . t('Facebook API Key') . '</h4>';
694         
695         $appid  = get_config('facebook', 'appid'  );
696         $appsecret = get_config('facebook', 'appsecret' );
697         $poll_interval = get_config('facebook', 'poll_interval' );
698         if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
699         
700         $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1");
701         $ret2 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appsecret' LIMIT 1");
702         if ((count($ret1) > 0 && $ret1[0]['v'] != $appid) || (count($ret2) > 0 && $ret2[0]['v'] != $appsecret)) $o .= t('Error: it appears that you have specified the App-ID and -Secret in your .htconfig.php file. As long as they are specified there, they cannot be set using this form.<br><br>');
703         
704         $working_connection = false;
705         if ($appid && $appsecret) {
706                 $subs = facebook_subscriptions_get();
707                 if ($subs === null) $o .= t('Error: the given API Key seems to be incorrect (the application access token could not be retrieved).') . '<br>';
708                 elseif (is_array($subs)) {
709                         $o .= t('The given API Key seems to work correctly.') . '<br>';
710                         $working_connection = true;
711                 } else $o .= t('The correctness of the API Key could not be detected. Somthing strange\'s going on.') . '<br>';
712         }
713         
714         $o .= '<label for="fb_appid">' . t('App-ID / API-Key') . '</label><input name="appid" type="text" value="' . escape_tags($appid ? $appid : "") . '"><br style="clear: both;">';
715         $o .= '<label for="fb_appsecret">' . t('Application secret') . '</label><input name="appsecret" type="text" value="' . escape_tags($appsecret ? $appsecret : "") . '"><br style="clear: both;">';
716         $o .= '<label for="fb_poll_interval">' . sprintf(t('Polling Interval (min. %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . '</label><input name="poll_interval" type="number" min="' . FACEBOOK_MIN_POLL_INTERVAL . '" value="' . $poll_interval . '"><br style="clear: both;">';
717         $o .= '<input type="submit" name="fb_save_keys" value="' . t('Save') . '">';
718         
719         if ($working_connection) {
720                 $o .= '<h4>' . t('Real-Time Updates') . '</h4>';
721                 
722                 $activated = facebook_check_realtime_active();
723                 if ($activated) {
724                         $o .= t('Real-Time Updates are activated.') . '<br><br>';
725                         $o .= '<input type="submit" name="real_time_deactivate" value="' . t('Deactivate Real-Time Updates') . '">';
726                 } else {
727                         $o .= t('Real-Time Updates not activated.') . '<br><input type="submit" name="real_time_activate" value="' . t('Activate Real-Time Updates') . '">';
728                 }
729         }
730 }
731
732 /**
733  * @param App $a
734  * @param null|object $o
735  */
736 function facebook_plugin_admin_post(&$a, &$o){
737         check_form_security_token_redirectOnErr('/admin/plugins/facebook', 'fbsave');
738         
739         if (x($_REQUEST,'fb_save_keys')) {
740                 set_config('facebook', 'appid', $_REQUEST['appid']);
741                 set_config('facebook', 'appsecret', $_REQUEST['appsecret']);
742                 $poll_interval = IntVal($_REQUEST['poll_interval']);
743                 if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval);
744                 del_config('facebook', 'app_access_token');
745                 info(t('The new values have been saved.'));
746         }
747         if (x($_REQUEST,'real_time_activate')) {
748                 facebook_subscription_add_users();
749         }
750         if (x($_REQUEST,'real_time_deactivate')) {
751                 facebook_subscription_del_users();
752         }
753 }
754
755 /**
756  * @param App $a
757  * @param object $b
758  * @return mixed
759  */
760 function facebook_jot_nets(&$a,&$b) {
761         if(! local_user())
762                 return;
763
764         $fb_post = get_pconfig(local_user(),'facebook','post');
765         if(intval($fb_post) == 1) {
766                 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
767                 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
768                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> ' 
769                         . t('Post to Facebook') . '</div>';     
770         }
771 }
772
773
774 /**
775  * @param App $a
776  * @param object $b
777  * @return mixed
778  */
779 function facebook_post_hook(&$a,&$b) {
780
781
782         if($b['deleted'] || ($b['created'] !== $b['edited']))
783                 return;
784
785         /**
786          * Post to Facebook stream
787          */
788
789         require_once('include/group.php');
790         require_once('include/html2plain.php');
791
792         logger('Facebook post');
793
794         $reply = false;
795         $likes = false;
796
797         $deny_arr = array();
798         $allow_arr = array();
799
800         $toplevel = (($b['id'] == $b['parent']) ? true : false);
801
802
803         $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
804
805         if((! $toplevel) && ($linking)) {
806                 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
807                         intval($b['parent']),
808                         intval($b['uid'])
809                 );
810                 if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
811                         $reply = substr($r[0]['uri'],4);
812                 elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
813                         $reply = substr($r[0]['extid'],4);
814                 else
815                         return;
816
817                 $u = q("SELECT * FROM user where uid = %d limit 1",
818                         intval($b['uid'])
819                 );
820                 if(! count($u))
821                         return;
822
823                 // only accept comments from the item owner. Other contacts are unknown to FB.
824  
825                 if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
826                         return;
827                 
828
829                 logger('facebook reply id=' . $reply);
830         }
831
832         if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
833
834                 if($b['private'] && $reply === false) {
835                         $allow_people = expand_acl($b['allow_cid']);
836                         $allow_groups = expand_groups(expand_acl($b['allow_gid']));
837                         $deny_people  = expand_acl($b['deny_cid']);
838                         $deny_groups  = expand_groups(expand_acl($b['deny_gid']));
839
840                         $recipients = array_unique(array_merge($allow_people,$allow_groups));
841                         $deny = array_unique(array_merge($deny_people,$deny_groups));
842
843                         $allow_str = dbesc(implode(', ',$recipients));
844                         if($allow_str) {
845                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); 
846                                 if(count($r))
847                                         foreach($r as $rr)
848                                                 $allow_arr[] = $rr['notify'];
849                         }
850
851                         $deny_str = dbesc(implode(', ',$deny));
852                         if($deny_str) {
853                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); 
854                                 if(count($r))
855                                         foreach($r as $rr)
856                                                 $deny_arr[] = $rr['notify'];
857                         }
858
859                         if(count($deny_arr) && (! count($allow_arr))) {
860
861                                 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
862                                 // This might cause the post to be open to public on Facebook, but only to selected members
863                                 // on another network. Since this could potentially leak a post to somebody who was denied, 
864                                 // we will skip posting it to Facebook with a slightly vague but relevant message that will 
865                                 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
866
867                                 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
868                                 return;
869                         }
870
871
872                         // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
873
874                         if((! count($allow_arr)) && (! count($deny_arr)))
875                                 return;
876                 }
877
878                 if($b['verb'] == ACTIVITY_LIKE)
879                         $likes = true;                          
880
881
882                 $appid  = get_config('facebook', 'appid'  );
883                 $secret = get_config('facebook', 'appsecret' );
884
885                 if($appid && $secret) {
886
887                         logger('facebook: have appid+secret');
888
889                         $fb_token  = get_pconfig($b['uid'],'facebook','access_token');
890
891
892                         // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, 
893                         // or it's a private message with facebook participants
894                         // or it's a reply or likes action to an existing facebook post                 
895
896                         if($fb_token && ($toplevel || $b['private'] || $reply)) {
897                                 logger('facebook: able to post');
898                                 require_once('library/facebook.php');
899                                 require_once('include/bbcode.php');     
900
901                                 $msg = $b['body'];
902
903                                 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
904
905                                 // make links readable before we strip the code
906
907                                 // unless it's a dislike - just send the text as a comment
908
909                                 if($b['verb'] == ACTIVITY_DISLIKE)
910                                         $msg = trim(strip_tags(bbcode($msg)));
911
912                                 // Old code
913                                 /*$search_str = $a->get_baseurl() . '/search';
914
915                                 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
916
917                                         // don't use hashtags for message link
918
919                                         if(strpos($matches[2],$search_str) === false) {
920                                                 $link = $matches[1];
921                                                 if(substr($matches[2],0,5) != '[img]')
922                                                         $linkname = $matches[2];
923                                         }
924                                 }
925
926                                 // strip tag links to avoid link clutter, this really should be 
927                                 // configurable because we're losing information
928
929                                 $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
930
931                                 // provide the link separately for normal links
932                                 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
933
934                                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
935                                         $image = $matches[1];
936
937                                 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
938
939                                 if((strpos($link,z_root()) !== false) && (! $image))
940                                         $image = $a->get_baseurl() . '/images/friendica-64.jpg';
941
942                                 $msg = trim(strip_tags(bbcode($msg)));*/
943
944                                 // New code
945
946                                 // Looking for the first image
947                                 $image = '';
948                                 if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
949                                         $image = $matches[3];
950
951                                 if ($image == '')
952                                         if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
953                                                 $image = $matches[1];
954
955                                 // Checking for a bookmark element
956                                 $body = $b['body'];
957                                 if (strpos($body, "[bookmark") !== false) {
958                                         // splitting the text in two parts:
959                                         // before and after the bookmark
960                                         $pos = strpos($body, "[bookmark");
961                                         $body1 = substr($body, 0, $pos);
962                                         $body2 = substr($body, $pos);
963
964                                         // Removing the bookmark and all quotes after the bookmark
965                                         // they are mostly only the content after the bookmark.
966                                         $body2 = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'',$body2);
967                                         $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
968                                         $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
969
970                                         $body = $body1.$body2;
971                                 }
972
973                                 // At first convert the text to html
974                                 $html = bbcode($body);
975
976                                 // Then convert it to plain text
977                                 $msg = trim($b['title']." \n\n".html2plain($html, 0, true));
978                                 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
979
980                                 // Removing multiple newlines
981                                 while (strpos($msg, "\n\n\n") !== false)
982                                         $msg = str_replace("\n\n\n", "\n\n", $msg);
983
984                                 // add any attachments as text urls
985                                 $arr = explode(',',$b['attach']);
986
987                                 if(count($arr)) {
988                                         $msg .= "\n";
989                                         foreach($arr as $r) {
990                                                 $matches = false;
991                                                 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
992                                                 if($cnt) {
993                                                         $msg .= "\n".$matches[1];
994                                                 }
995                                         }
996                                 }
997
998                                 $link = '';
999                                 $linkname = '';
1000                                 // look for bookmark-bbcode and handle it with priority
1001                                 if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) {
1002                                         $link = $matches[1];
1003                                         $linkname = $matches[2];
1004                                 }
1005
1006                                 // If there is no bookmark element then take the first link
1007                                 if ($link == '') {
1008                                         $links = collecturls($html);
1009                                         if (sizeof($links) > 0) {
1010                                                 reset($links);
1011                                                 $link = current($links);
1012                                         }
1013                                 }
1014
1015                                 // Remove trailing and leading spaces
1016                                 $msg = trim($msg);
1017
1018                                 // Since facebook increased the maxpostlen massively this never should happen again :)
1019                                 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
1020                                         $shortlink = "";
1021                                         require_once('library/slinky.php');
1022
1023                                         $display_url = $b['plink'];
1024
1025                                         $slinky = new Slinky( $display_url );
1026                                         // setup a cascade of shortening services
1027                                         // try to get a short link from these services
1028                                         // in the order ur1.ca, trim, id.gd, tinyurl
1029                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
1030                                         $shortlink = $slinky->short();
1031                                         // the new message will be shortened such that "... $shortlink"
1032                                         // will fit into the character limit
1033                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
1034                                         $msg .= '... ' . $shortlink;
1035                                 }
1036
1037                                 // Fallback - if message is empty
1038                                 if(!strlen($msg))
1039                                         $msg = $link;
1040
1041                                 if(!strlen($msg))
1042                                         $msg = $image;
1043
1044                                 if(!strlen($msg))
1045                                         $msg = $linkname;
1046
1047                                 // If there is nothing to post then exit
1048                                 if(!strlen($msg))
1049                                         return;
1050
1051                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
1052
1053                                 if($likes) { 
1054                                         $postvars = array('access_token' => $fb_token);
1055                                 }
1056                                 else {
1057                                         $postvars = array(
1058                                                 'access_token' => $fb_token, 
1059                                                 'message' => $msg
1060                                         );
1061                                         if(isset($image)) {
1062                                                 $postvars['picture'] = $image;
1063                                                 //$postvars['type'] = "photo";
1064                                         }
1065                                         if(isset($link)) {
1066                                                 $postvars['link'] = $link;
1067                                                 //$postvars['type'] = "link";
1068                                         }
1069                                         if(isset($linkname))
1070                                                 $postvars['name'] = $linkname;
1071                                 }
1072
1073                                 if(($b['private']) && ($toplevel)) {
1074                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
1075                                         if(count($allow_arr))
1076                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
1077                                         if(count($deny_arr))
1078                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
1079                                         $postvars['privacy'] .= '}';
1080
1081                                 }
1082
1083                                 if($reply) {
1084                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
1085                                 } else if (($link != "")  or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) { 
1086                                         $url = 'https://graph.facebook.com/me/feed';
1087                                         if($b['plink'])
1088                                                 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' .  $b['plink'] . '"}';
1089                                 } else {
1090                                         // if its only a message and a subject and the message is larger than 500 characters then post it as note
1091                                         $postvars = array(
1092                                                 'access_token' => $fb_token, 
1093                                                 'message' => bbcode($b['body']),
1094                                                 'subject' => $b['title'],
1095                                         );
1096                                         $url = 'https://graph.facebook.com/me/notes';
1097                                 }
1098
1099                                 logger('facebook: post to ' . $url);
1100                                 logger('facebook: postvars: ' . print_r($postvars,true));
1101
1102                                 // "test_mode" prevents anything from actually being posted.
1103                                 // Otherwise, let's do it.
1104
1105                                 if(! get_config('facebook','test_mode')) {
1106                                         $x = post_url($url, $postvars);
1107                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
1108
1109                                         $retj = json_decode($x);
1110                                         if($retj->id) {
1111                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
1112                                                         dbesc('fb::' . $retj->id),
1113                                                         intval($b['id'])
1114                                                 );
1115                                         }
1116                                         else {
1117                                                 if(! $likes) {
1118                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
1119                                                         require_once('include/queue_fn.php');
1120                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
1121                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
1122                                                 }
1123                                                 
1124                                                 if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) {
1125                                                         logger('Facebook session has expired due to changed password.', LOGGER_DEBUG);
1126                                                         
1127                                                         $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent');
1128                                                         if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) {
1129                                                                 require_once('include/enotify.php');
1130                                                         
1131                                                                 $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) );
1132                                                                 notification(array(
1133                                                                         'uid' => $b['uid'],
1134                                                                         'type' => NOTIFY_SYSTEM,
1135                                                                         'system_type' => 'facebook_connection_invalid',
1136                                                                         'language'     => $r[0]['language'],
1137                                                                         'to_name'      => $r[0]['username'],
1138                                                                         'to_email'     => $r[0]['email'],
1139                                                                         'source_name'  => t('Administrator'),
1140                                                                         'source_link'  => $a->config["system"]["url"],
1141                                                                         'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg',
1142                                                                 ));
1143                                                                 
1144                                                                 set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time());
1145                                                         } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG);
1146                                                 }
1147                                         }
1148                                 }
1149                         }
1150                 }
1151         }
1152 }
1153
1154 /**
1155  * @param App $app
1156  * @param object $data
1157  */
1158 function facebook_enotify(&$app, &$data) {
1159         if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') {
1160                 $data['itemlink'] = '/facebook';
1161                 $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.');
1162                 $data['subject'] = t('Facebook connection became invalid');
1163                 $data['body'] = sprintf( t("Hi %1\$s,\n\nThe connection between your accounts on %2\$s and Facebook became invalid. This usually happens after you change your Facebook-password. To enable the connection again, you have to %3\$sre-authenticate the Facebook-connector%4\$s."), $data['params']['to_name'], "[url=" . $app->config["system"]["url"] . "]" . $app->config["sitename"] . "[/url]", "[url=" . $app->config["system"]["url"] . "/facebook]", "[/url]");
1164         }
1165 }
1166
1167 /**
1168  * @param App $a
1169  * @param object $b
1170  */
1171 function facebook_post_local(&$a,&$b) {
1172
1173         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
1174         // where we will discover it during background delivery.
1175
1176         // This can only be triggered by a local user posting to their own wall.
1177
1178         if((local_user()) && (local_user() == $b['uid'])) {
1179
1180                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
1181                 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
1182
1183                 // if API is used, default to the chosen settings
1184                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
1185                         $fb_enable = 1;
1186
1187                 if(! $fb_enable)
1188                         return;
1189
1190                 if(strlen($b['postopts']))
1191                         $b['postopts'] .= ',';
1192                 $b['postopts'] .= 'facebook';
1193         }
1194 }
1195
1196
1197 /**
1198  * @param App $a
1199  * @param object $b
1200  */
1201 function fb_queue_hook(&$a,&$b) {
1202
1203         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1204                 dbesc(NETWORK_FACEBOOK)
1205         );
1206         if(! count($qi))
1207                 return;
1208
1209         require_once('include/queue_fn.php');
1210
1211         foreach($qi as $x) {
1212                 if($x['network'] !== NETWORK_FACEBOOK)
1213                         continue;
1214
1215                 logger('facebook_queue: run');
1216
1217                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1218                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1219                         intval($x['cid'])
1220                 );
1221                 if(! count($r))
1222                         continue;
1223
1224                 $user = $r[0];
1225
1226                 $appid  = get_config('facebook', 'appid'  );
1227                 $secret = get_config('facebook', 'appsecret' );
1228
1229                 if($appid && $secret) {
1230                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
1231                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
1232
1233                         if($fb_post && $fb_token) {
1234                                 logger('facebook_queue: able to post');
1235                                 require_once('library/facebook.php');
1236
1237                                 $z = unserialize($x['content']);
1238                                 $item = $z['item'];
1239                                 $j = post_url($z['url'],$z['post']);
1240
1241                                 $retj = json_decode($j);
1242                                 if($retj->id) {
1243                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
1244                                                 dbesc('fb::' . $retj->id),
1245                                                 intval($item)
1246                                         );
1247                                         logger('facebook_queue: success: ' . $j); 
1248                                         remove_queue_item($x['id']);
1249                                 }
1250                                 else {
1251                                         logger('facebook_queue: failed: ' . $j);
1252                                         update_queue_time($x['id']);
1253                                 }
1254                         }
1255                 }
1256         }
1257 }
1258
1259 /**
1260  * @param string $access_token
1261  * @param int $since
1262  * @return object
1263  */
1264 function fb_get_timeline($access_token, &$since) {
1265
1266     $entries = new stdClass();
1267         $entries->data = array();
1268         $newest = 0;
1269
1270         $url = 'https://graph.facebook.com/me/home?access_token='.$access_token;
1271
1272         if ($since != 0)
1273                 $url .= "&since=".$since;
1274
1275         do {
1276                 $s = fetch_url($url);
1277                 $j = json_decode($s);
1278                 $oldestdate = time();
1279                 if (isset($j->data))
1280                         foreach ($j->data as $entry) {
1281                                 $created = strtotime($entry->created_time);
1282
1283                                 if ($newest < $created)
1284                                         $newest = $created;
1285
1286                                 if ($created >= $since)
1287                                         $entries->data[] = $entry;
1288
1289                                 if ($created <= $oldestdate)
1290                                         $oldestdate = $created;
1291                         }
1292                 else
1293                         break;
1294
1295                 $url = $j->paging->next;
1296
1297         } while (($oldestdate > $since) and ($since != 0) and ($url != ''));
1298
1299         if ($newest > $since)
1300                 $since = $newest;
1301
1302         return($entries);
1303 }
1304
1305 /**
1306  * @param int $uid
1307  */
1308 function fb_consume_all($uid) {
1309
1310         require_once('include/items.php');
1311
1312         $access_token = get_pconfig($uid,'facebook','access_token');
1313         if(! $access_token)
1314                 return;
1315         
1316         if(! get_pconfig($uid,'facebook','no_wall')) {
1317                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
1318                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
1319                 if($s) {
1320                         $j = json_decode($s);
1321                         if (isset($j->data)) {
1322                                 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
1323                                 fb_consume_stream($uid,$j,($private_wall) ? false : true);
1324                         } else {
1325                                 logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1326                         }
1327                 }
1328         }
1329         // Get the last date
1330         $lastdate = get_pconfig($uid,'facebook','lastdate');
1331         // fetch all items since the last date
1332         $j = fb_get_timeline($access_token, $lastdate);
1333         if (isset($j->data)) {
1334                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
1335                 fb_consume_stream($uid,$j,false);
1336
1337                 // Write back the last date
1338                 set_pconfig($uid,'facebook','lastdate', $lastdate);
1339         } else
1340                 logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1341 }
1342
1343 /**
1344  * @param int $uid
1345  * @param string $link
1346  * @return string
1347  */
1348 function fb_get_photo($uid,$link) {
1349         $access_token = get_pconfig($uid,'facebook','access_token');
1350         if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
1351                 return "";
1352                 //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1353         $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
1354         if($ret)
1355                 $photo_id = $match[1];
1356         else
1357             return "";
1358         $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
1359         $j = json_decode($x);
1360         if($j->picture)
1361                 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
1362         //else
1363         //      return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1364 }
1365
1366 /**
1367  * @param int $uid
1368  * @param object $j
1369  * @param bool $wall
1370  */
1371 function fb_consume_stream($uid,$j,$wall = false) {
1372
1373         $a = get_app();
1374
1375
1376         $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
1377                 intval($uid)
1378         );
1379         if(! count($user))
1380                 return;
1381
1382         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1383
1384         $no_linking = get_pconfig($uid,'facebook','no_linking');
1385         if($no_linking)
1386                 return;
1387
1388         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1389                 intval($uid)
1390         );
1391
1392         $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
1393         $blocked_apps_arr = explode(',',$blocked_apps);
1394
1395         $self_id = get_pconfig($uid,'facebook','self_id');
1396         if(! count($j->data) || (! strlen($self_id)))
1397                 return;
1398
1399         foreach($j->data as $entry) {
1400                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
1401                 $datarray = array();
1402
1403                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
1404                                 dbesc('fb::' . $entry->id),
1405                                 dbesc('fb::' . $entry->id),
1406                                 intval($uid)
1407                 );
1408                 if(count($r)) {
1409                         $post_exists = true;
1410                         $orig_post = $r[0];
1411                         $top_item = $r[0]['id'];
1412                 }
1413                 else {
1414                         $post_exists = false;
1415                         $orig_post = null;
1416                 }
1417
1418                 if(! $orig_post) {
1419                         $datarray['gravity'] = 0;
1420                         $datarray['uid'] = $uid;
1421                         $datarray['wall'] = (($wall) ? 1 : 0);
1422                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
1423                         $from = $entry->from;
1424                         if($from->id == $self_id)
1425                                 $datarray['contact-id'] = $self[0]['id'];
1426                         else {
1427                                 // Looking if user is known - if not he is added
1428                                 $access_token = get_pconfig($uid, 'facebook', 'access_token');
1429                                 fb_get_friends_sync_new($uid, $access_token, $from);
1430
1431                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1432                                         dbesc($from->id),
1433                                         intval($uid)
1434                                 );
1435                                 if(count($r))
1436                                         $datarray['contact-id'] = $r[0]['id'];
1437                         }
1438
1439                         // don't store post if we don't have a contact
1440                         if(! x($datarray,'contact-id')) {
1441                                 logger('facebook: no contact '.$from->name.' '.$from->id.'. post ignored');
1442                                 continue;
1443                         }
1444
1445                         $datarray['verb'] = ACTIVITY_POST;
1446                         if($wall) {
1447                                 $datarray['owner-name'] = $self[0]['name'];
1448                                 $datarray['owner-link'] = $self[0]['url'];
1449                                 $datarray['owner-avatar'] = $self[0]['thumb'];
1450                         }
1451                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
1452                                 $datarray['app'] = strip_tags($entry->application->name);
1453                         else
1454                                 $datarray['app'] = 'facebook';
1455
1456                         $found_blocked = false;
1457
1458                         if(count($blocked_apps_arr)) {
1459                                 foreach($blocked_apps_arr as $bad_appl) {
1460                                         if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
1461                                                 $found_blocked = true;
1462                                         }
1463                                 }
1464                         }
1465                                 
1466                         if($found_blocked) {
1467                                 logger('facebook: blocking application: ' . $datarray['app']);
1468                                 continue;
1469                         }
1470
1471                         $datarray['author-name'] = $from->name;
1472                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
1473                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
1474                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
1475
1476                         logger('facebook: post '.$entry->id.' from '.$from->name);
1477
1478                         $datarray['body'] = escape_tags($entry->message);
1479
1480                         if($entry->name and $entry->link)
1481                                 $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
1482                         elseif ($entry->name)
1483                                 $datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
1484
1485                         if($entry->caption) {
1486                                 if(!$entry->name and $entry->link)
1487                                         $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
1488                                 else
1489                                         $datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
1490                         }
1491
1492                         if(!$entry->caption and !$entry->name) {
1493                                 if ($entry->link)
1494                                         $datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
1495                                 else
1496                                         $datarray['body'] .= "\n";
1497                         }
1498
1499                         $quote = "";
1500                         if($entry->description)
1501                                 $quote = $entry->description;
1502
1503                         if ($entry->properties)
1504                                 foreach ($entry->properties as $property)
1505                                         $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
1506
1507                         if ($quote)
1508                                 $datarray['body'] .= "\n[quote]".$quote."[/quote]";
1509
1510                         // Only import the picture when the message is no video
1511                         // oembed display a picture of the video as well 
1512                         if ($entry->type != "video") {
1513                                 if($entry->picture && $entry->link) {
1514                                         $datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';   
1515                                 }
1516                                 else {
1517                                         if($entry->picture)
1518                                                 $datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
1519                                         // if just a link, it may be a wall photo - check
1520                                         if($entry->link)
1521                                                 $datarray['body'] .= fb_get_photo($uid,$entry->link);
1522                                 }
1523                         }
1524
1525                         if (($datarray['app'] == "Events") and $entry->actions)
1526                                 foreach ($entry->actions as $action)
1527                                         if ($action->name == "View")
1528                                                 $datarray['body'] .= " [url=".$action->link."]".$entry->story."[/url]";
1529
1530                         // Just as a test - to see if these are the missing entries
1531                         //if(trim($datarray['body']) == '')
1532                         //      $datarray['body'] = $entry->story;
1533
1534                         // Adding the "story" text to see if there are useful data in it (testing)
1535                         //if (($datarray['app'] != "Events") and $entry->story)
1536                         //      $datarray['body'] .= "\n".$entry->story;
1537
1538                         if(trim($datarray['body']) == '') {
1539                                 logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
1540                                 continue;
1541                         }
1542
1543                         $datarray['body'] .= "\n";
1544
1545                         if ($entry->icon)
1546                                 $datarray['body'] .= "[img]".$entry->icon."[/img] &nbsp; ";
1547
1548                         if ($entry->actions)
1549                                 foreach ($entry->actions as $action)
1550                                         if (($action->name != "Comment") and ($action->name != "Like"))
1551                                                 $datarray['body'] .= "[url=".$action->link."]".$action->name."[/url] &nbsp; ";
1552
1553                         $datarray['body'] = trim($datarray['body']);
1554
1555                         //if(($datarray['body'] != '') and ($uid == 1))
1556                         //      $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
1557
1558                         if ($entry->place->name or $entry->place->location->street or 
1559                                 $entry->place->location->city or $entry->place->location->Denmark) {
1560                                 $datarray['coord'] = '';
1561                                 if ($entry->place->name)
1562                                         $datarray['coord'] .= $entry->place->name;
1563                                 if ($entry->place->location->street)
1564                                         $datarray['coord'] .= $entry->place->location->street;
1565                                 if ($entry->place->location->city)
1566                                         $datarray['coord'] .= " ".$entry->place->location->city;
1567                                 if ($entry->place->location->country)
1568                                         $datarray['coord'] .= " ".$entry->place->location->country;
1569                         } else if ($entry->place->location->latitude and $entry->place->location->longitude)
1570                                 $datarray['coord'] = substr($entry->place->location->latitude, 0, 8)
1571                                                         .' '.substr($entry->place->location->longitude, 0, 8);
1572
1573                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1574                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1575
1576                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1577                         // as the identities are from a foreign system. Mark it as private to the owner.
1578
1579                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1580                                 $datarray['private'] = 1;
1581                                 $datarray['allow_cid'] = '<' . $self[0]['id'] . '>';
1582                         }
1583
1584                         $top_item = item_store($datarray);
1585                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1586                                 intval($top_item),
1587                                 intval($uid)
1588                         );
1589                         if(count($r)) {
1590                                 $orig_post = $r[0];
1591                                 logger('fb: new top level item posted');
1592                         }
1593                 }
1594
1595                 if(isset($entry->likes) && isset($entry->likes->data))
1596                         $likers = $entry->likes->data;
1597                 else
1598                         $likers = null;
1599
1600                 if(isset($entry->comments) && isset($entry->comments->data))
1601                         $comments = $entry->comments->data;
1602                 else
1603                         $comments = null;
1604
1605                 if(is_array($likers)) {
1606                         foreach($likers as $likes) {
1607
1608                                 if(! $orig_post)
1609                                         continue;
1610
1611                                 // If we posted the like locally, it will be found with our url, not the FB url.
1612
1613                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
1614
1615                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
1616                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1617                                         dbesc($orig_post['uri']),
1618                                         intval($uid),
1619                                         dbesc(ACTIVITY_LIKE),
1620                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1621                                         dbesc($second_url)
1622                                 );
1623
1624                                 if(count($r))
1625                                         continue;
1626                                         
1627                                 $likedata = array();
1628                                 $likedata['parent'] = $top_item;
1629                                 $likedata['verb'] = ACTIVITY_LIKE;
1630                                 $likedata['gravity'] = 3;
1631                                 $likedata['uid'] = $uid;
1632                                 $likedata['wall'] = (($wall) ? 1 : 0);
1633                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1634                                 $likedata['parent-uri'] = $orig_post['uri'];
1635                                 if($likes->id == $self_id)
1636                                         $likedata['contact-id'] = $self[0]['id'];
1637                                 else {
1638                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1639                                                 dbesc($likes->id),
1640                                                 intval($uid)
1641                                         );
1642                                         if(count($r))
1643                                                 $likedata['contact-id'] = $r[0]['id'];
1644                                 }
1645                                 if(! x($likedata,'contact-id'))
1646                                         $likedata['contact-id'] = $orig_post['contact-id'];
1647
1648                                 $likedata['app'] = 'facebook';
1649                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1650                                 $likedata['author-name'] = $likes->name;
1651                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1652                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1653                                 
1654                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1655                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1656                                 $post_type = t('status');
1657                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1658                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1659
1660                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1661                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1662                                         '<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';  
1663
1664                                 $item = item_store($likedata);                  
1665                         }
1666                 }
1667                 if(is_array($comments)) {
1668                         foreach($comments as $cmnt) {
1669
1670                                 if(! $orig_post)
1671                                         continue;
1672
1673                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1674                                         intval($uid),
1675                                         dbesc('fb::' . $cmnt->id),
1676                                         dbesc('fb::' . $cmnt->id)
1677                                 );
1678                                 if(count($r))
1679                                         continue;
1680
1681                                 $cmntdata = array();
1682                                 $cmntdata['parent'] = $top_item;
1683                                 $cmntdata['verb'] = ACTIVITY_POST;
1684                                 $cmntdata['gravity'] = 6;
1685                                 $cmntdata['uid'] = $uid;
1686                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1687                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1688                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1689                                 if($cmnt->from->id == $self_id) {
1690                                         $cmntdata['contact-id'] = $self[0]['id'];
1691                                 }
1692                                 else {
1693                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1694                                                 dbesc($cmnt->from->id),
1695                                                 intval($uid)
1696                                         );
1697                                         if(count($r)) {
1698                                                 $cmntdata['contact-id'] = $r[0]['id'];
1699                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1700                                                         continue;
1701                                         }
1702                                 }
1703                                 if(! x($cmntdata,'contact-id'))
1704                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1705
1706                                 $cmntdata['app'] = 'facebook';
1707                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1708                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1709                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1710                                 $cmntdata['author-name'] = $cmnt->from->name;
1711                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1712                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1713                                 $cmntdata['body'] = $cmnt->message;
1714                                 $item = item_store($cmntdata);                  
1715                                 
1716                                 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
1717                                         dbesc($orig_post['uri']),
1718                                         intval($uid)
1719                                 );
1720
1721                                 if(count($myconv)) {
1722                                         $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1723
1724                                         foreach($myconv as $conv) {
1725
1726                                                 // now if we find a match, it means we're in this conversation
1727         
1728                                                 if(! link_compare($conv['author-link'],$importer_url))
1729                                                         continue;
1730
1731                                                 require_once('include/enotify.php');
1732                                                                 
1733                                                 $conv_parent = $conv['parent'];
1734
1735                                                 notification(array(
1736                                                         'type'         => NOTIFY_COMMENT,
1737                                                         'notify_flags' => $user[0]['notify-flags'],
1738                                                         'language'     => $user[0]['language'],
1739                                                         'to_name'      => $user[0]['username'],
1740                                                         'to_email'     => $user[0]['email'],
1741                                                         'uid'          => $user[0]['uid'],
1742                                                         'item'         => $cmntdata,
1743                                                         'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $item,
1744                                                         'source_name'  => $cmntdata['author-name'],
1745                                                         'source_link'  => $cmntdata['author-link'],
1746                                                         'source_photo' => $cmntdata['author-avatar'],
1747                                                         'verb'         => ACTIVITY_POST,
1748                                                         'otype'        => 'item',
1749                                                         'parent'       => $conv_parent,
1750                                                 ));
1751
1752                                                 // only send one notification
1753                                                 break;
1754                                         }
1755                                 }
1756                         }
1757                 }
1758         }
1759 }
1760
1761
1762 /**
1763  * @return bool|string
1764  */
1765 function fb_get_app_access_token() {
1766         
1767         $acc_token = get_config('facebook','app_access_token');
1768         
1769         if ($acc_token !== false) return $acc_token;
1770         
1771         $appid = get_config('facebook','appid');
1772         $appsecret = get_config('facebook', 'appsecret');
1773         
1774         if ($appid === false || $appsecret === false) {
1775                 logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
1776                 return false;
1777         }
1778         logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA);
1779         $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials');
1780         
1781         if(strpos($x,'access_token=') !== false) {
1782                 logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
1783         
1784                 $token = str_replace('access_token=', '', $x);
1785                 if(strpos($token,'&') !== false)
1786                         $token = substr($token,0,strpos($token,'&'));
1787                 
1788                 if ($token == "") {
1789                         logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
1790                         return false;
1791                 }
1792                 set_config('facebook','app_access_token',$token);
1793                 return $token;
1794         } else {
1795                 logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
1796                 return false;
1797         }
1798 }
1799
1800 function facebook_subscription_del_users() {
1801         $a = get_app();
1802         $access_token = fb_get_app_access_token();
1803         
1804         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1805         facebook_delete_url($url);
1806         
1807         if (!facebook_check_realtime_active()) del_config('facebook', 'realtime_active');
1808 }
1809
1810 /**
1811  * @param bool $second_try
1812  */
1813 function facebook_subscription_add_users($second_try = false) {
1814         $a = get_app();
1815         $access_token = fb_get_app_access_token();
1816         
1817         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1818         
1819         list($usec, $sec) = explode(" ", microtime());
1820         $verify_token = sha1($usec . $sec . rand(0, 999999999));
1821         set_config('facebook', 'cb_verify_token', $verify_token);
1822         
1823         $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
1824         
1825         $j = post_url($url,array(
1826                 "object" => "user",
1827                 "fields" => "feed,friends",
1828                 "callback_url" => $cb,
1829                 "verify_token" => $verify_token,
1830         ));
1831         del_config('facebook', 'cb_verify_token');
1832         
1833         if ($j) {
1834                 $x = json_decode($j);
1835                 logger("Facebook reponse: " . $j, LOGGER_DATA);
1836                 if (isset($x->error)) {
1837                         logger('facebook_subscription_add_users: got an error: ' . $j);
1838                         if ($x->error->type == "OAuthException" && $x->error->code == 190) {
1839                                 del_config('facebook', 'app_access_token');
1840                                 if ($second_try === false) facebook_subscription_add_users(true);
1841                         }
1842                 } else {
1843                         logger('facebook_subscription_add_users: sucessful');
1844                         if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
1845                 }
1846         };
1847 }
1848
1849 /**
1850  * @return null|array
1851  */
1852 function facebook_subscriptions_get() {
1853         
1854         $access_token = fb_get_app_access_token();
1855         if (!$access_token) return null;
1856         
1857         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1858         $j = fetch_url($url);
1859         $ret = null;
1860         if ($j) {
1861                 $x = json_decode($j);
1862                 if (isset($x->data)) $ret = $x->data;
1863         }
1864         return $ret;
1865 }
1866
1867
1868 /**
1869  * @return bool
1870  */
1871 function facebook_check_realtime_active() {
1872         $ret = facebook_subscriptions_get();
1873         if (is_null($ret)) return false;
1874         if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
1875         return false;
1876 }
1877
1878
1879
1880
1881 // DELETE-request to $url
1882
1883 if(! function_exists('facebook_delete_url')) {
1884     /**
1885      * @param string $url
1886      * @param null|array $headers
1887      * @param int $redirects
1888      * @param int $timeout
1889      * @return bool|string
1890      */
1891     function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
1892         $a = get_app();
1893         $ch = curl_init($url);
1894         if(($redirects > 8) || (! $ch)) 
1895                 return false;
1896
1897         curl_setopt($ch, CURLOPT_HEADER, true);
1898         curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
1899         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
1900         curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
1901
1902         if(intval($timeout)) {
1903                 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1904         }
1905         else {
1906                 $curl_time = intval(get_config('system','curl_timeout'));
1907                 curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
1908         }
1909
1910         if(defined('LIGHTTPD')) {
1911                 if(!is_array($headers)) {
1912                         $headers = array('Expect:');
1913                 } else {
1914                         if(!in_array('Expect:', $headers)) {
1915                                 array_push($headers, 'Expect:');
1916                         }
1917                 }
1918         }
1919         if($headers)
1920                 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1921
1922         $check_cert = get_config('system','verifyssl');
1923         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
1924         $prx = get_config('system','proxy');
1925         if(strlen($prx)) {
1926                 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
1927                 curl_setopt($ch, CURLOPT_PROXY, $prx);
1928                 $prxusr = get_config('system','proxyuser');
1929                 if(strlen($prxusr))
1930                         curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
1931         }
1932
1933         $a->set_curl_code(0);
1934
1935         // don't let curl abort the entire application
1936         // if it throws any errors.
1937
1938         $s = @curl_exec($ch);
1939
1940         $base = $s;
1941         $curl_info = curl_getinfo($ch);
1942         $http_code = $curl_info['http_code'];
1943
1944         $header = '';
1945
1946         // Pull out multiple headers, e.g. proxy and continuation headers
1947         // allow for HTTP/2.x without fixing code
1948
1949         while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
1950                 $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
1951                 $header .= $chunk;
1952                 $base = substr($base,strlen($chunk));
1953         }
1954
1955         if($http_code == 301 || $http_code == 302 || $http_code == 303) {
1956         $matches = array();
1957         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
1958         $url = trim(array_pop($matches));
1959         $url_parsed = @parse_url($url);
1960         if (isset($url_parsed)) {
1961             $redirects++;
1962             return facebook_delete_url($url,$headers,$redirects,$timeout);
1963         }
1964     }
1965         $a->set_curl_code($http_code);
1966         $body = substr($s,strlen($header));
1967
1968         $a->set_curl_headers($header);
1969
1970         curl_close($ch);
1971         return($body);
1972 }}