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