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