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