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