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