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