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