]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
Showmore: A little html bug was fixed (missing </div>)
[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_consume_all($uid) {
1182
1183         require_once('include/items.php');
1184
1185         $access_token = get_pconfig($uid,'facebook','access_token');
1186         if(! $access_token)
1187                 return;
1188         
1189         if(! get_pconfig($uid,'facebook','no_wall')) {
1190                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
1191                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
1192                 if($s) {
1193                         $j = json_decode($s);
1194                         if (isset($j->data)) {
1195                                 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
1196                                 fb_consume_stream($uid,$j,($private_wall) ? false : true);
1197                         } else {
1198                                 logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1199                         }
1200                 }
1201         }
1202         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
1203         if($s) {
1204                 $j = json_decode($s);
1205                 if (isset($j->data)) {
1206                         logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
1207                         fb_consume_stream($uid,$j,false);
1208                 } else {
1209                         logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1210                 }
1211         }
1212
1213 }
1214
1215 function fb_get_photo($uid,$link) {
1216         $access_token = get_pconfig($uid,'facebook','access_token');
1217         if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
1218                 return "";
1219                 //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1220         $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
1221         if($ret)
1222                 $photo_id = $match[1];
1223         $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
1224         $j = json_decode($x);
1225         if($j->picture)
1226                 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
1227         //else
1228         //      return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1229 }
1230
1231 function fb_consume_stream($uid,$j,$wall = false) {
1232
1233         $a = get_app();
1234
1235
1236         $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
1237                 intval($uid)
1238         );
1239         if(! count($user))
1240                 return;
1241
1242         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1243
1244         $no_linking = get_pconfig($uid,'facebook','no_linking');
1245         if($no_linking)
1246                 return;
1247
1248         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1249                 intval($uid)
1250         );
1251
1252         $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
1253         $blocked_apps_arr = explode(',',$blocked_apps);
1254
1255         $self_id = get_pconfig($uid,'facebook','self_id');
1256         if(! count($j->data) || (! strlen($self_id)))
1257                 return;
1258
1259         foreach($j->data as $entry) {
1260                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
1261                 $datarray = array();
1262
1263                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
1264                                 dbesc('fb::' . $entry->id),
1265                                 dbesc('fb::' . $entry->id),
1266                                 intval($uid)
1267                 );
1268                 if(count($r)) {
1269                         $post_exists = true;
1270                         $orig_post = $r[0];
1271                         $top_item = $r[0]['id'];
1272                 }
1273                 else {
1274                         $post_exists = false;
1275                         $orig_post = null;
1276                 }
1277
1278                 if(! $orig_post) {
1279                         $datarray['gravity'] = 0;
1280                         $datarray['uid'] = $uid;
1281                         $datarray['wall'] = (($wall) ? 1 : 0);
1282                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
1283                         $from = $entry->from;
1284                         if($from->id == $self_id)
1285                                 $datarray['contact-id'] = $self[0]['id'];
1286                         else {
1287                                 // Looking if user is known - if not he is added
1288                                 $access_token = get_pconfig($uid, 'facebook', 'access_token');
1289                                 fb_get_friends_sync_new($uid, $access_token, $from);
1290
1291                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1292                                         dbesc($from->id),
1293                                         intval($uid)
1294                                 );
1295                                 if(count($r))
1296                                         $datarray['contact-id'] = $r[0]['id'];
1297                         }
1298
1299                         // don't store post if we don't have a contact
1300
1301                         if(! x($datarray,'contact-id')) {
1302                                 //if (get_config('facebook', 'pages')) {
1303                                 //      // If no user is found then post it under the own id.
1304                                 //      // Definitely a quickhack
1305                                 //      $datarray['contact-id'] = $self[0]['id'];
1306                                 //} else {
1307                                         logger('no contact: post ignored');
1308                                         continue;
1309                                 //}
1310                         }
1311
1312                         $datarray['verb'] = ACTIVITY_POST;
1313                         if($wall) {
1314                                 $datarray['owner-name'] = $self[0]['name'];
1315                                 $datarray['owner-link'] = $self[0]['url'];
1316                                 $datarray['owner-avatar'] = $self[0]['thumb'];
1317                         }
1318                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
1319                                 $datarray['app'] = strip_tags($entry->application->name);
1320                         else
1321                                 $datarray['app'] = 'facebook';
1322
1323                         $found_blocked = false;
1324
1325                         if(count($blocked_apps_arr)) {
1326                                 foreach($blocked_apps_arr as $bad_appl) {
1327                                         if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
1328                                                 $found_blocked = true;
1329                                         }
1330                                 }
1331                         }
1332                                 
1333                         if($found_blocked) {
1334                                 logger('facebook: blocking application: ' . $datarray['app']);
1335                                 continue;
1336                         }
1337
1338                         $datarray['author-name'] = $from->name;
1339                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
1340                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
1341                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
1342
1343                         $datarray['body'] = escape_tags($entry->message);
1344
1345                         if($entry->name and $entry->link)
1346                                 $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
1347                         elseif ($entry->name)
1348                                 $datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
1349
1350                         if($entry->caption) {
1351                                 if(!$entry->name and $entry->link)
1352                                         $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
1353                                 else
1354                                         $datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
1355                         }
1356
1357                         if(!$entry->caption and !$entry->name) {
1358                                 if ($entry->link)
1359                                         $datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
1360                                 else
1361                                         $datarray['body'] .= "\n";
1362                         }
1363
1364                         $quote = "";
1365                         if($entry->description)
1366                                 $quote = $entry->description;
1367
1368                         if ($entry->properties)
1369                                 foreach ($entry->properties as $property)
1370                                         $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
1371
1372                         if ($quote)
1373                                 $datarray['body'] .= "\n[quote]".$quote."[/quote]";
1374
1375                         // Only import the picture when the message is no video
1376                         // oembed display a picture of the video as well 
1377                         if ($entry->type != "video") {
1378                                 if($entry->picture && $entry->link) {
1379                                         $datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';   
1380                                 }
1381                                 else {
1382                                         if($entry->picture)
1383                                                 $datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
1384                                         // if just a link, it may be a wall photo - check
1385                                         if($entry->link)
1386                                                 $datarray['body'] .= fb_get_photo($uid,$entry->link);
1387                                 }
1388                         }
1389
1390                         if(trim($datarray['body']) == '') {
1391                                 logger('facebook: empty body');
1392                                 continue;
1393                         }
1394
1395                         $datarray['body'] .= "\n";
1396
1397                         if ($entry->icon)
1398                                 $datarray['body'] .= "[img]".$entry->icon."[/img] &nbsp; ";
1399
1400                         if ($entry->actions)
1401                                 foreach ($entry->actions as $action)
1402                                         if (($action->name != "Comment") and ($action->name != "Like"))
1403                                                 $datarray['body'] .= "[url=".$action->link."]".$action->name."[/url] &nbsp; ";
1404
1405                         $datarray['body'] = trim($datarray['body']);
1406
1407                         //if(($datarray['body'] != '') and ($uid == 1))
1408                         //      $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
1409
1410
1411                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1412                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1413
1414                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1415                         // as the identities are from a foreign system. Mark it as private to the owner.
1416
1417                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1418                                 $datarray['private'] = 1;
1419                                 $datarray['allow_cid'] = '<' . $self[0]['id'] . '>';
1420                         }
1421
1422                         $top_item = item_store($datarray);
1423                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1424                                 intval($top_item),
1425                                 intval($uid)
1426                         );
1427                         if(count($r)) {
1428                                 $orig_post = $r[0];
1429                                 logger('fb: new top level item posted');
1430                         }
1431                 }
1432
1433                 if(isset($entry->likes) && isset($entry->likes->data))
1434                         $likers = $entry->likes->data;
1435                 else
1436                         $likers = null;
1437
1438                 if(isset($entry->comments) && isset($entry->comments->data))
1439                         $comments = $entry->comments->data;
1440                 else
1441                         $comments = null;
1442
1443                 if(is_array($likers)) {
1444                         foreach($likers as $likes) {
1445
1446                                 if(! $orig_post)
1447                                         continue;
1448
1449                                 // If we posted the like locally, it will be found with our url, not the FB url.
1450
1451                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
1452
1453                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
1454                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1455                                         dbesc($orig_post['uri']),
1456                                         intval($uid),
1457                                         dbesc(ACTIVITY_LIKE),
1458                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1459                                         dbesc($second_url)
1460                                 );
1461
1462                                 if(count($r))
1463                                         continue;
1464                                         
1465                                 $likedata = array();
1466                                 $likedata['parent'] = $top_item;
1467                                 $likedata['verb'] = ACTIVITY_LIKE;
1468                                 $likedata['gravity'] = 3;
1469                                 $likedata['uid'] = $uid;
1470                                 $likedata['wall'] = (($wall) ? 1 : 0);
1471                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1472                                 $likedata['parent-uri'] = $orig_post['uri'];
1473                                 if($likes->id == $self_id)
1474                                         $likedata['contact-id'] = $self[0]['id'];
1475                                 else {
1476                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1477                                                 dbesc($likes->id),
1478                                                 intval($uid)
1479                                         );
1480                                         if(count($r))
1481                                                 $likedata['contact-id'] = $r[0]['id'];
1482                                 }
1483                                 if(! x($likedata,'contact-id'))
1484                                         $likedata['contact-id'] = $orig_post['contact-id'];
1485
1486                                 $likedata['app'] = 'facebook';
1487                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1488                                 $likedata['author-name'] = $likes->name;
1489                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1490                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1491                                 
1492                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1493                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1494                                 $post_type = t('status');
1495                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1496                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1497
1498                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1499                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1500                                         '<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>';  
1501
1502                                 $item = item_store($likedata);                  
1503                         }
1504                 }
1505                 if(is_array($comments)) {
1506                         foreach($comments as $cmnt) {
1507
1508                                 if(! $orig_post)
1509                                         continue;
1510
1511                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1512                                         intval($uid),
1513                                         dbesc('fb::' . $cmnt->id),
1514                                         dbesc('fb::' . $cmnt->id)
1515                                 );
1516                                 if(count($r))
1517                                         continue;
1518
1519                                 $cmntdata = array();
1520                                 $cmntdata['parent'] = $top_item;
1521                                 $cmntdata['verb'] = ACTIVITY_POST;
1522                                 $cmntdata['gravity'] = 6;
1523                                 $cmntdata['uid'] = $uid;
1524                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1525                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1526                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1527                                 if($cmnt->from->id == $self_id) {
1528                                         $cmntdata['contact-id'] = $self[0]['id'];
1529                                 }
1530                                 else {
1531                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1532                                                 dbesc($cmnt->from->id),
1533                                                 intval($uid)
1534                                         );
1535                                         if(count($r)) {
1536                                                 $cmntdata['contact-id'] = $r[0]['id'];
1537                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1538                                                         continue;
1539                                         }
1540                                 }
1541                                 if(! x($cmntdata,'contact-id'))
1542                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1543
1544                                 $cmntdata['app'] = 'facebook';
1545                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1546                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1547                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1548                                 $cmntdata['author-name'] = $cmnt->from->name;
1549                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1550                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1551                                 $cmntdata['body'] = $cmnt->message;
1552                                 $item = item_store($cmntdata);                  
1553                                 
1554                                 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
1555                                         dbesc($orig_post['uri']),
1556                                         intval($uid)
1557                                 );
1558
1559                                 if(count($myconv)) {
1560                                         $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1561
1562                                         foreach($myconv as $conv) {
1563
1564                                                 // now if we find a match, it means we're in this conversation
1565         
1566                                                 if(! link_compare($conv['author-link'],$importer_url))
1567                                                         continue;
1568
1569                                                 require_once('include/enotify.php');
1570                                                                 
1571                                                 $conv_parent = $conv['parent'];
1572
1573                                                 notification(array(
1574                                                         'type'         => NOTIFY_COMMENT,
1575                                                         'notify_flags' => $user[0]['notify-flags'],
1576                                                         'language'     => $user[0]['language'],
1577                                                         'to_name'      => $user[0]['username'],
1578                                                         'to_email'     => $user[0]['email'],
1579                                                         'uid'          => $user[0]['uid'],
1580                                                         'item'         => $cmntdata,
1581                                                         'link'             => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $item,
1582                                                         'source_name'  => $cmntdata['author-name'],
1583                                                         'source_link'  => $cmntdata['author-link'],
1584                                                         'source_photo' => $cmntdata['author-avatar'],
1585                                                         'verb'         => ACTIVITY_POST,
1586                                                         'otype'        => 'item',
1587                                                         'parent'       => $conv_parent,
1588                                                 ));
1589
1590                                                 // only send one notification
1591                                                 break;
1592                                         }
1593                                 }
1594                         }
1595                 }
1596         }
1597 }
1598
1599
1600 function fb_get_app_access_token() {
1601         
1602         $acc_token = get_config('facebook','app_access_token');
1603         
1604         if ($acc_token !== false) return $acc_token;
1605         
1606         $appid = get_config('facebook','appid');
1607         $appsecret = get_config('facebook', 'appsecret');
1608         
1609         if ($appid === false || $appsecret === false) {
1610                 logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
1611                 return false;
1612         }
1613         logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA);
1614         $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials');
1615         
1616         if(strpos($x,'access_token=') !== false) {
1617                 logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
1618         
1619                 $token = str_replace('access_token=', '', $x);
1620                 if(strpos($token,'&') !== false)
1621                         $token = substr($token,0,strpos($token,'&'));
1622                 
1623                 if ($token == "") {
1624                         logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
1625                         return false;
1626                 }
1627                 set_config('facebook','app_access_token',$token);
1628                 return $token;
1629         } else {
1630                 logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
1631                 return false;
1632         }
1633 }
1634
1635 function facebook_subscription_del_users() {
1636         $a = get_app();
1637         $access_token = fb_get_app_access_token();
1638         
1639         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1640         facebook_delete_url($url);
1641         
1642         if (!facebook_check_realtime_active()) del_config('facebook', 'realtime_active');
1643 }
1644
1645 function facebook_subscription_add_users($second_try = false) {
1646         $a = get_app();
1647         $access_token = fb_get_app_access_token();
1648         
1649         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1650         
1651         list($usec, $sec) = explode(" ", microtime());
1652         $verify_token = sha1($usec . $sec . rand(0, 999999999));
1653         set_config('facebook', 'cb_verify_token', $verify_token);
1654         
1655         $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
1656         
1657         $j = post_url($url,array(
1658                 "object" => "user",
1659                 "fields" => "feed,friends",
1660                 "callback_url" => $cb,
1661                 "verify_token" => $verify_token,
1662         ));
1663         del_config('facebook', 'cb_verify_token');
1664         
1665         if ($j) {
1666                 $x = json_decode($j);
1667                 logger("Facebook reponse: " . $j, LOGGER_DATA);
1668                 if (isset($x->error)) {
1669                         logger('facebook_subscription_add_users: got an error: ' . $j);
1670                         if ($x->error->type == "OAuthException" && $x->error->code == 190) {
1671                                 del_config('facebook', 'app_access_token');
1672                                 if ($second_try === false) facebook_subscription_add_users(true);
1673                         }
1674                 } else {
1675                         logger('facebook_subscription_add_users: sucessful');
1676                         if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
1677                 }
1678         };
1679 }
1680
1681 function facebook_subscriptions_get() {
1682         
1683         $access_token = fb_get_app_access_token();
1684         if (!$access_token) return null;
1685         
1686         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1687         $j = fetch_url($url);
1688         $ret = null;
1689         if ($j) {
1690                 $x = json_decode($j);
1691                 if (isset($x->data)) $ret = $x->data;
1692         }
1693         return $ret;
1694 }
1695
1696
1697 function facebook_check_realtime_active() {
1698         $ret = facebook_subscriptions_get();
1699         if (is_null($ret)) return false;
1700         if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
1701         return false;
1702 }
1703
1704
1705
1706
1707 // DELETE-request to $url
1708
1709 if(! function_exists('facebook_delete_url')) {
1710 function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
1711         $a = get_app();
1712         $ch = curl_init($url);
1713         if(($redirects > 8) || (! $ch)) 
1714                 return false;
1715
1716         curl_setopt($ch, CURLOPT_HEADER, true);
1717         curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
1718         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
1719         curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
1720
1721         if(intval($timeout)) {
1722                 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1723         }
1724         else {
1725                 $curl_time = intval(get_config('system','curl_timeout'));
1726                 curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
1727         }
1728
1729         if(defined('LIGHTTPD')) {
1730                 if(!is_array($headers)) {
1731                         $headers = array('Expect:');
1732                 } else {
1733                         if(!in_array('Expect:', $headers)) {
1734                                 array_push($headers, 'Expect:');
1735                         }
1736                 }
1737         }
1738         if($headers)
1739                 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1740
1741         $check_cert = get_config('system','verifyssl');
1742         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
1743         $prx = get_config('system','proxy');
1744         if(strlen($prx)) {
1745                 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
1746                 curl_setopt($ch, CURLOPT_PROXY, $prx);
1747                 $prxusr = get_config('system','proxyuser');
1748                 if(strlen($prxusr))
1749                         curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
1750         }
1751
1752         $a->set_curl_code(0);
1753
1754         // don't let curl abort the entire application
1755         // if it throws any errors.
1756
1757         $s = @curl_exec($ch);
1758
1759         $base = $s;
1760         $curl_info = curl_getinfo($ch);
1761         $http_code = $curl_info['http_code'];
1762
1763         $header = '';
1764
1765         // Pull out multiple headers, e.g. proxy and continuation headers
1766         // allow for HTTP/2.x without fixing code
1767
1768         while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
1769                 $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
1770                 $header .= $chunk;
1771                 $base = substr($base,strlen($chunk));
1772         }
1773
1774         if($http_code == 301 || $http_code == 302 || $http_code == 303) {
1775         $matches = array();
1776         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
1777         $url = trim(array_pop($matches));
1778         $url_parsed = @parse_url($url);
1779         if (isset($url_parsed)) {
1780             $redirects++;
1781             return delete_url($url,$headers,$redirects,$timeout);
1782         }
1783     }
1784         $a->set_curl_code($http_code);
1785         $body = substr($s,strlen($header));
1786
1787         $a->set_curl_headers($header);
1788
1789         curl_close($ch);
1790         return($body);
1791 }}