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