]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
Merge commit 'upstream/master'
[friendica-addons.git] / facebook / facebook.php
1 <?php
2 /**
3  * Name: Facebook Connector
4  * Version: 1.1
5  * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
6  *         Tobias Hößl <https://github.com/CatoTH/>
7  */
8
9 /**
10  * Installing the Friendica/Facebook connector
11  *
12  * 1. register an API key for your site from developer.facebook.com
13  *   a. We'd be very happy if you include "Friendica" in the application name
14  *      to increase name recognition. The Friendica icons are also present
15  *      in the images directory and may be uploaded as a Facebook app icon.
16  *      Use images/friendica-16.jpg for the Icon and images/friendica-128.jpg for the Logo.
17  *   b. The url should be your site URL with a trailing slash.
18  *      Friendica is a software application and does not require a Privacy Policy 
19  *      or Terms of Service, though your installation of it might. Facebook may require
20  *      that you provide a Privacy Policy, which we find ironic.  
21  *   c. Set the following values in your .htconfig.php file
22  *         $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
23  *         $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
24  *      Replace with the settings Facebook gives you.
25  *   d. Navigate to Set Web->Site URL & Domain -> Website Settings.  Set 
26  *      Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your 
27  *      yourdomain.com.
28  * 2. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
29  *    and click 'Install Facebook Connector'.
30  * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
31  *    and click 'Install Facebook Connector'.
32  * 4. This will ask you to login to Facebook and grant permission to the 
33  *    plugin to do its stuff. Allow it to do so. 
34  * 5. Optional step: If you want to use Facebook Real Time Updates (so new messages
35  *    and new contacts are added ~1min after they are postet / added on FB), go to
36  *    Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button.
37  * 6. You're done. To turn it off visit the Plugin Settings page again and
38  *    'Remove Facebook posting'.
39  *
40  * Vidoes and embeds will not be posted if there is no other content. Links 
41  * and images will be converted to a format suitable for the Facebook API and 
42  * long posts truncated - with a link to view the full post. 
43  *
44  * Facebook contacts will not be able to view private photos, as they are not able to
45  * authenticate to your site to establish identity. We will address this 
46  * in a future release.
47  */
48  
49  /** TODO
50  * - Implement a method for the administrator to delete all configuration data the plugin has created,
51  *   e.g. the app_access_token
52  */
53
54 // Size of maximum post length increased
55 // see http://www.facebook.com/schrep/posts/203969696349811
56 // define('FACEBOOK_MAXPOSTLEN', 420);
57 define('FACEBOOK_MAXPOSTLEN', 63206);
58 define('FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL', 259200); // 3 days
59 define('FACEBOOK_DEFAULT_POLL_INTERVAL', 60); // given in minutes
60 define('FACEBOOK_MIN_POLL_INTERVAL', 5);
61
62 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                                                 //$postvars['type'] = "photo";
1015                                         }
1016                                         if(isset($link)) {
1017                                                 $postvars['link'] = $link;
1018                                                 //$postvars['type'] = "link";
1019                                         }
1020                                         if(isset($linkname))
1021                                                 $postvars['name'] = $linkname;
1022                                 }
1023
1024                                 if(($b['private']) && ($toplevel)) {
1025                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
1026                                         if(count($allow_arr))
1027                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
1028                                         if(count($deny_arr))
1029                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
1030                                         $postvars['privacy'] .= '}';
1031
1032                                 }
1033
1034                                 if($reply) {
1035                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
1036                                 } else if (($link != "")  or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) { 
1037                                         $url = 'https://graph.facebook.com/me/feed';
1038                                         if($b['plink'])
1039                                                 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' .  $b['plink'] . '"}';
1040                                 } else {
1041                                         // if its only a message and a subject and the message is larger than 500 characters then post it as note
1042                                         $postvars = array(
1043                                                 'access_token' => $fb_token, 
1044                                                 'message' => bbcode($b['body']),
1045                                                 'subject' => $b['title'],
1046                                         );
1047                                         $url = 'https://graph.facebook.com/me/notes';
1048                                 }
1049
1050                                 logger('facebook: post to ' . $url);
1051                                 logger('facebook: postvars: ' . print_r($postvars,true));
1052
1053                                 // "test_mode" prevents anything from actually being posted.
1054                                 // Otherwise, let's do it.
1055
1056                                 if(! get_config('facebook','test_mode')) {
1057                                         $x = post_url($url, $postvars);
1058                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
1059
1060                                         $retj = json_decode($x);
1061                                         if($retj->id) {
1062                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
1063                                                         dbesc('fb::' . $retj->id),
1064                                                         intval($b['id'])
1065                                                 );
1066                                         }
1067                                         else {
1068                                                 if(! $likes) {
1069                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
1070                                                         require_once('include/queue_fn.php');
1071                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
1072                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
1073                                                 }
1074                                                 
1075                                                 if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) {
1076                                                         logger('Facebook session has expired due to changed password.', LOGGER_DEBUG);
1077                                                         
1078                                                         $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent');
1079                                                         if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) {
1080                                                                 require_once('include/enotify.php');
1081                                                         
1082                                                                 $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) );
1083                                                                 notification(array(
1084                                                                         'uid' => $b['uid'],
1085                                                                         'type' => NOTIFY_SYSTEM,
1086                                                                         'system_type' => 'facebook_connection_invalid',
1087                                                                         'language'     => $r[0]['language'],
1088                                                                         'to_name'      => $r[0]['username'],
1089                                                                         'to_email'     => $r[0]['email'],
1090                                                                         'source_name'  => t('Administrator'),
1091                                                                         'source_link'  => $a->config["system"]["url"],
1092                                                                         'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg',
1093                                                                 ));
1094                                                                 
1095                                                                 set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time());
1096                                                         } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG);
1097                                                 }
1098                                         }
1099                                 }
1100                         }
1101                 }
1102         }
1103 }
1104
1105 function facebook_enotify(&$app, &$data) {
1106         if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') {
1107                 $data['itemlink'] = '/facebook';
1108                 $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.');
1109                 $data['subject'] = t('Facebook connection became invalid');
1110                 $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]");
1111         }
1112 }
1113
1114 function facebook_post_local(&$a,&$b) {
1115
1116         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
1117         // where we will discover it during background delivery.
1118
1119         // This can only be triggered by a local user posting to their own wall.
1120
1121         if((local_user()) && (local_user() == $b['uid'])) {
1122
1123                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
1124                 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
1125
1126                 // if API is used, default to the chosen settings
1127                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
1128                         $fb_enable = 1;
1129
1130                 if(! $fb_enable)
1131                         return;
1132
1133                 if(strlen($b['postopts']))
1134                         $b['postopts'] .= ',';
1135                 $b['postopts'] .= 'facebook';
1136         }
1137 }
1138
1139
1140 function fb_queue_hook(&$a,&$b) {
1141
1142         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1143                 dbesc(NETWORK_FACEBOOK)
1144         );
1145         if(! count($qi))
1146                 return;
1147
1148         require_once('include/queue_fn.php');
1149
1150         foreach($qi as $x) {
1151                 if($x['network'] !== NETWORK_FACEBOOK)
1152                         continue;
1153
1154                 logger('facebook_queue: run');
1155
1156                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1157                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1158                         intval($x['cid'])
1159                 );
1160                 if(! count($r))
1161                         continue;
1162
1163                 $user = $r[0];
1164
1165                 $appid  = get_config('facebook', 'appid'  );
1166                 $secret = get_config('facebook', 'appsecret' );
1167
1168                 if($appid && $secret) {
1169                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
1170                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
1171
1172                         if($fb_post && $fb_token) {
1173                                 logger('facebook_queue: able to post');
1174                                 require_once('library/facebook.php');
1175
1176                                 $z = unserialize($x['content']);
1177                                 $item = $z['item'];
1178                                 $j = post_url($z['url'],$z['post']);
1179
1180                                 $retj = json_decode($j);
1181                                 if($retj->id) {
1182                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
1183                                                 dbesc('fb::' . $retj->id),
1184                                                 intval($item)
1185                                         );
1186                                         logger('facebook_queue: success: ' . $j); 
1187                                         remove_queue_item($x['id']);
1188                                 }
1189                                 else {
1190                                         logger('facebook_queue: failed: ' . $j);
1191                                         update_queue_time($x['id']);
1192                                 }
1193                         }
1194                 }
1195         }
1196 }
1197
1198 function fb_get_timeline($access_token, &$since) {
1199
1200         $entries->data = array();
1201         $newest = 0;
1202
1203         $url = 'https://graph.facebook.com/me/home?access_token='.$access_token;
1204
1205         if ($since != 0)
1206                 $url .= "&since=".$since;
1207
1208         do {
1209                 $s = fetch_url($url);
1210                 $j = json_decode($s);
1211                 $oldestdate = time();
1212                 if (isset($j->data))
1213                         foreach ($j->data as $entry) {
1214                                 $created = strtotime($entry->created_time);
1215
1216                                 if ($newest < $created)
1217                                         $newest = $created;
1218
1219                                 if ($created >= $since)
1220                                         $entries->data[] = $entry;
1221
1222                                 if ($created <= $oldestdate)
1223                                         $oldestdate = $created;
1224                         }
1225                 else
1226                         break;
1227
1228                 $url = $j->paging->next;
1229
1230         } while (($oldestdate > $since) and ($since != 0) and ($url != ''));
1231
1232         if ($newest > $since)
1233                 $since = $newest;
1234
1235         return($entries);
1236 }
1237
1238 function fb_consume_all($uid) {
1239
1240         require_once('include/items.php');
1241
1242         $access_token = get_pconfig($uid,'facebook','access_token');
1243         if(! $access_token)
1244                 return;
1245         
1246         if(! get_pconfig($uid,'facebook','no_wall')) {
1247                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
1248                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
1249                 if($s) {
1250                         $j = json_decode($s);
1251                         if (isset($j->data)) {
1252                                 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
1253                                 fb_consume_stream($uid,$j,($private_wall) ? false : true);
1254                         } else {
1255                                 logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1256                         }
1257                 }
1258         }
1259         // Get the last date
1260         $lastdate = get_pconfig($uid,'facebook','lastdate');
1261         // fetch all items since the last date
1262         $j = fb_get_timeline($access_token, &$lastdate);
1263         if (isset($j->data)) {
1264                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
1265                 fb_consume_stream($uid,$j,false);
1266
1267                 // Write back the last date
1268                 set_pconfig($uid,'facebook','lastdate', $lastdate);
1269         } else
1270                 logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1271 }
1272
1273 function fb_get_photo($uid,$link) {
1274         $access_token = get_pconfig($uid,'facebook','access_token');
1275         if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
1276                 return "";
1277                 //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1278         $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
1279         if($ret)
1280                 $photo_id = $match[1];
1281         $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
1282         $j = json_decode($x);
1283         if($j->picture)
1284                 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
1285         //else
1286         //      return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1287 }
1288
1289 function fb_consume_stream($uid,$j,$wall = false) {
1290
1291         $a = get_app();
1292
1293
1294         $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
1295                 intval($uid)
1296         );
1297         if(! count($user))
1298                 return;
1299
1300         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1301
1302         $no_linking = get_pconfig($uid,'facebook','no_linking');
1303         if($no_linking)
1304                 return;
1305
1306         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1307                 intval($uid)
1308         );
1309
1310         $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
1311         $blocked_apps_arr = explode(',',$blocked_apps);
1312
1313         $self_id = get_pconfig($uid,'facebook','self_id');
1314         if(! count($j->data) || (! strlen($self_id)))
1315                 return;
1316
1317         foreach($j->data as $entry) {
1318                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
1319                 $datarray = array();
1320
1321                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
1322                                 dbesc('fb::' . $entry->id),
1323                                 dbesc('fb::' . $entry->id),
1324                                 intval($uid)
1325                 );
1326                 if(count($r)) {
1327                         $post_exists = true;
1328                         $orig_post = $r[0];
1329                         $top_item = $r[0]['id'];
1330                 }
1331                 else {
1332                         $post_exists = false;
1333                         $orig_post = null;
1334                 }
1335
1336                 if(! $orig_post) {
1337                         $datarray['gravity'] = 0;
1338                         $datarray['uid'] = $uid;
1339                         $datarray['wall'] = (($wall) ? 1 : 0);
1340                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
1341                         $from = $entry->from;
1342                         if($from->id == $self_id)
1343                                 $datarray['contact-id'] = $self[0]['id'];
1344                         else {
1345                                 // Looking if user is known - if not he is added
1346                                 $access_token = get_pconfig($uid, 'facebook', 'access_token');
1347                                 fb_get_friends_sync_new($uid, $access_token, $from);
1348
1349                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1350                                         dbesc($from->id),
1351                                         intval($uid)
1352                                 );
1353                                 if(count($r))
1354                                         $datarray['contact-id'] = $r[0]['id'];
1355                         }
1356
1357                         // don't store post if we don't have a contact
1358                         if(! x($datarray,'contact-id')) {
1359                                 logger('facebook: no contact '.$from->name.' '.$from->id.'. post ignored');
1360                                 continue;
1361                         }
1362
1363                         $datarray['verb'] = ACTIVITY_POST;
1364                         if($wall) {
1365                                 $datarray['owner-name'] = $self[0]['name'];
1366                                 $datarray['owner-link'] = $self[0]['url'];
1367                                 $datarray['owner-avatar'] = $self[0]['thumb'];
1368                         }
1369                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
1370                                 $datarray['app'] = strip_tags($entry->application->name);
1371                         else
1372                                 $datarray['app'] = 'facebook';
1373
1374                         $found_blocked = false;
1375
1376                         if(count($blocked_apps_arr)) {
1377                                 foreach($blocked_apps_arr as $bad_appl) {
1378                                         if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
1379                                                 $found_blocked = true;
1380                                         }
1381                                 }
1382                         }
1383                                 
1384                         if($found_blocked) {
1385                                 logger('facebook: blocking application: ' . $datarray['app']);
1386                                 continue;
1387                         }
1388
1389                         $datarray['author-name'] = $from->name;
1390                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
1391                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
1392                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
1393
1394                         logger('facebook: post '.$entry->id.' from '.$from->name);
1395
1396                         $datarray['body'] = escape_tags($entry->message);
1397
1398                         if($entry->name and $entry->link)
1399                                 $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
1400                         elseif ($entry->name)
1401                                 $datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
1402
1403                         if($entry->caption) {
1404                                 if(!$entry->name and $entry->link)
1405                                         $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
1406                                 else
1407                                         $datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
1408                         }
1409
1410                         if(!$entry->caption and !$entry->name) {
1411                                 if ($entry->link)
1412                                         $datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
1413                                 else
1414                                         $datarray['body'] .= "\n";
1415                         }
1416
1417                         $quote = "";
1418                         if($entry->description)
1419                                 $quote = $entry->description;
1420
1421                         if ($entry->properties)
1422                                 foreach ($entry->properties as $property)
1423                                         $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
1424
1425                         if ($quote)
1426                                 $datarray['body'] .= "\n[quote]".$quote."[/quote]";
1427
1428                         // Only import the picture when the message is no video
1429                         // oembed display a picture of the video as well 
1430                         if ($entry->type != "video") {
1431                                 if($entry->picture && $entry->link) {
1432                                         $datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';   
1433                                 }
1434                                 else {
1435                                         if($entry->picture)
1436                                                 $datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
1437                                         // if just a link, it may be a wall photo - check
1438                                         if($entry->link)
1439                                                 $datarray['body'] .= fb_get_photo($uid,$entry->link);
1440                                 }
1441                         }
1442
1443                         if (($datarray['app'] == "Events") and $entry->actions)
1444                                 foreach ($entry->actions as $action)
1445                                         if ($action->name == "View")
1446                                                 $datarray['body'] .= " [url=".$action->link."]".$entry->story."[/url]";
1447
1448                         // Just as a test - to see if these are the missing entries
1449                         //if(trim($datarray['body']) == '')
1450                         //      $datarray['body'] = $entry->story;
1451
1452                         // Adding the "story" text to see if there are useful data in it (testing)
1453                         if (($datarray['app'] != "Events") and $entry->story)
1454                                 $datarray['body'] .= "\n".$entry->story;
1455
1456                         if(trim($datarray['body']) == '') {
1457                                 logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
1458                                 continue;
1459                         }
1460
1461                         $datarray['body'] .= "\n";
1462
1463                         if ($entry->icon)
1464                                 $datarray['body'] .= "[img]".$entry->icon."[/img] &nbsp; ";
1465
1466                         if ($entry->actions)
1467                                 foreach ($entry->actions as $action)
1468                                         if (($action->name != "Comment") and ($action->name != "Like"))
1469                                                 $datarray['body'] .= "[url=".$action->link."]".$action->name."[/url] &nbsp; ";
1470
1471                         $datarray['body'] = trim($datarray['body']);
1472
1473                         //if(($datarray['body'] != '') and ($uid == 1))
1474                         //      $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
1475
1476                         if ($entry->place->name or $entry->place->location->street or 
1477                                 $entry->place->location->city or $entry->place->location->Denmark) {
1478                                 $datarray['coord'] = '';
1479                                 if ($entry->place->name)
1480                                         $datarray['coord'] .= $entry->place->name;
1481                                 if ($entry->place->location->street)
1482                                         $datarray['coord'] .= $entry->place->location->street;
1483                                 if ($entry->place->location->city)
1484                                         $datarray['coord'] .= " ".$entry->place->location->city;
1485                                 if ($entry->place->location->country)
1486                                         $datarray['coord'] .= " ".$entry->place->location->country;
1487                         } else if ($entry->place->location->latitude and $entry->place->location->longitude)
1488                                 $datarray['coord'] = substr($entry->place->location->latitude, 0, 8)
1489                                                         .' '.substr($entry->place->location->longitude, 0, 8);
1490
1491                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1492                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1493
1494                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1495                         // as the identities are from a foreign system. Mark it as private to the owner.
1496
1497                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1498                                 $datarray['private'] = 1;
1499                                 $datarray['allow_cid'] = '<' . $self[0]['id'] . '>';
1500                         }
1501
1502                         $top_item = item_store($datarray);
1503                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1504                                 intval($top_item),
1505                                 intval($uid)
1506                         );
1507                         if(count($r)) {
1508                                 $orig_post = $r[0];
1509                                 logger('fb: new top level item posted');
1510                         }
1511                 }
1512
1513                 if(isset($entry->likes) && isset($entry->likes->data))
1514                         $likers = $entry->likes->data;
1515                 else
1516                         $likers = null;
1517
1518                 if(isset($entry->comments) && isset($entry->comments->data))
1519                         $comments = $entry->comments->data;
1520                 else
1521                         $comments = null;
1522
1523                 if(is_array($likers)) {
1524                         foreach($likers as $likes) {
1525
1526                                 if(! $orig_post)
1527                                         continue;
1528
1529                                 // If we posted the like locally, it will be found with our url, not the FB url.
1530
1531                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
1532
1533                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
1534                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1535                                         dbesc($orig_post['uri']),
1536                                         intval($uid),
1537                                         dbesc(ACTIVITY_LIKE),
1538                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1539                                         dbesc($second_url)
1540                                 );
1541
1542                                 if(count($r))
1543                                         continue;
1544                                         
1545                                 $likedata = array();
1546                                 $likedata['parent'] = $top_item;
1547                                 $likedata['verb'] = ACTIVITY_LIKE;
1548                                 $likedata['gravity'] = 3;
1549                                 $likedata['uid'] = $uid;
1550                                 $likedata['wall'] = (($wall) ? 1 : 0);
1551                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1552                                 $likedata['parent-uri'] = $orig_post['uri'];
1553                                 if($likes->id == $self_id)
1554                                         $likedata['contact-id'] = $self[0]['id'];
1555                                 else {
1556                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1557                                                 dbesc($likes->id),
1558                                                 intval($uid)
1559                                         );
1560                                         if(count($r))
1561                                                 $likedata['contact-id'] = $r[0]['id'];
1562                                 }
1563                                 if(! x($likedata,'contact-id'))
1564                                         $likedata['contact-id'] = $orig_post['contact-id'];
1565
1566                                 $likedata['app'] = 'facebook';
1567                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1568                                 $likedata['author-name'] = $likes->name;
1569                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1570                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1571                                 
1572                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1573                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1574                                 $post_type = t('status');
1575                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1576                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1577
1578                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1579                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1580                                         '<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>';  
1581
1582                                 $item = item_store($likedata);                  
1583                         }
1584                 }
1585                 if(is_array($comments)) {
1586                         foreach($comments as $cmnt) {
1587
1588                                 if(! $orig_post)
1589                                         continue;
1590
1591                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1592                                         intval($uid),
1593                                         dbesc('fb::' . $cmnt->id),
1594                                         dbesc('fb::' . $cmnt->id)
1595                                 );
1596                                 if(count($r))
1597                                         continue;
1598
1599                                 $cmntdata = array();
1600                                 $cmntdata['parent'] = $top_item;
1601                                 $cmntdata['verb'] = ACTIVITY_POST;
1602                                 $cmntdata['gravity'] = 6;
1603                                 $cmntdata['uid'] = $uid;
1604                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1605                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1606                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1607                                 if($cmnt->from->id == $self_id) {
1608                                         $cmntdata['contact-id'] = $self[0]['id'];
1609                                 }
1610                                 else {
1611                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1612                                                 dbesc($cmnt->from->id),
1613                                                 intval($uid)
1614                                         );
1615                                         if(count($r)) {
1616                                                 $cmntdata['contact-id'] = $r[0]['id'];
1617                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1618                                                         continue;
1619                                         }
1620                                 }
1621                                 if(! x($cmntdata,'contact-id'))
1622                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1623
1624                                 $cmntdata['app'] = 'facebook';
1625                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1626                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1627                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1628                                 $cmntdata['author-name'] = $cmnt->from->name;
1629                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1630                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1631                                 $cmntdata['body'] = $cmnt->message;
1632                                 $item = item_store($cmntdata);                  
1633                                 
1634                                 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
1635                                         dbesc($orig_post['uri']),
1636                                         intval($uid)
1637                                 );
1638
1639                                 if(count($myconv)) {
1640                                         $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1641
1642                                         foreach($myconv as $conv) {
1643
1644                                                 // now if we find a match, it means we're in this conversation
1645         
1646                                                 if(! link_compare($conv['author-link'],$importer_url))
1647                                                         continue;
1648
1649                                                 require_once('include/enotify.php');
1650                                                                 
1651                                                 $conv_parent = $conv['parent'];
1652
1653                                                 notification(array(
1654                                                         'type'         => NOTIFY_COMMENT,
1655                                                         'notify_flags' => $user[0]['notify-flags'],
1656                                                         'language'     => $user[0]['language'],
1657                                                         'to_name'      => $user[0]['username'],
1658                                                         'to_email'     => $user[0]['email'],
1659                                                         'uid'          => $user[0]['uid'],
1660                                                         'item'         => $cmntdata,
1661                                                         'link'             => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $item,
1662                                                         'source_name'  => $cmntdata['author-name'],
1663                                                         'source_link'  => $cmntdata['author-link'],
1664                                                         'source_photo' => $cmntdata['author-avatar'],
1665                                                         'verb'         => ACTIVITY_POST,
1666                                                         'otype'        => 'item',
1667                                                         'parent'       => $conv_parent,
1668                                                 ));
1669
1670                                                 // only send one notification
1671                                                 break;
1672                                         }
1673                                 }
1674                         }
1675                 }
1676         }
1677 }
1678
1679
1680 function fb_get_app_access_token() {
1681         
1682         $acc_token = get_config('facebook','app_access_token');
1683         
1684         if ($acc_token !== false) return $acc_token;
1685         
1686         $appid = get_config('facebook','appid');
1687         $appsecret = get_config('facebook', 'appsecret');
1688         
1689         if ($appid === false || $appsecret === false) {
1690                 logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
1691                 return false;
1692         }
1693         logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA);
1694         $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials');
1695         
1696         if(strpos($x,'access_token=') !== false) {
1697                 logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
1698         
1699                 $token = str_replace('access_token=', '', $x);
1700                 if(strpos($token,'&') !== false)
1701                         $token = substr($token,0,strpos($token,'&'));
1702                 
1703                 if ($token == "") {
1704                         logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
1705                         return false;
1706                 }
1707                 set_config('facebook','app_access_token',$token);
1708                 return $token;
1709         } else {
1710                 logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
1711                 return false;
1712         }
1713 }
1714
1715 function facebook_subscription_del_users() {
1716         $a = get_app();
1717         $access_token = fb_get_app_access_token();
1718         
1719         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1720         facebook_delete_url($url);
1721         
1722         if (!facebook_check_realtime_active()) del_config('facebook', 'realtime_active');
1723 }
1724
1725 function facebook_subscription_add_users($second_try = false) {
1726         $a = get_app();
1727         $access_token = fb_get_app_access_token();
1728         
1729         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1730         
1731         list($usec, $sec) = explode(" ", microtime());
1732         $verify_token = sha1($usec . $sec . rand(0, 999999999));
1733         set_config('facebook', 'cb_verify_token', $verify_token);
1734         
1735         $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
1736         
1737         $j = post_url($url,array(
1738                 "object" => "user",
1739                 "fields" => "feed,friends",
1740                 "callback_url" => $cb,
1741                 "verify_token" => $verify_token,
1742         ));
1743         del_config('facebook', 'cb_verify_token');
1744         
1745         if ($j) {
1746                 $x = json_decode($j);
1747                 logger("Facebook reponse: " . $j, LOGGER_DATA);
1748                 if (isset($x->error)) {
1749                         logger('facebook_subscription_add_users: got an error: ' . $j);
1750                         if ($x->error->type == "OAuthException" && $x->error->code == 190) {
1751                                 del_config('facebook', 'app_access_token');
1752                                 if ($second_try === false) facebook_subscription_add_users(true);
1753                         }
1754                 } else {
1755                         logger('facebook_subscription_add_users: sucessful');
1756                         if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
1757                 }
1758         };
1759 }
1760
1761 function facebook_subscriptions_get() {
1762         
1763         $access_token = fb_get_app_access_token();
1764         if (!$access_token) return null;
1765         
1766         $url = "https://graph.facebook.com/" . get_config('facebook', 'appid'  ) . "/subscriptions?access_token=" . $access_token;
1767         $j = fetch_url($url);
1768         $ret = null;
1769         if ($j) {
1770                 $x = json_decode($j);
1771                 if (isset($x->data)) $ret = $x->data;
1772         }
1773         return $ret;
1774 }
1775
1776
1777 function facebook_check_realtime_active() {
1778         $ret = facebook_subscriptions_get();
1779         if (is_null($ret)) return false;
1780         if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
1781         return false;
1782 }
1783
1784
1785
1786
1787 // DELETE-request to $url
1788
1789 if(! function_exists('facebook_delete_url')) {
1790 function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
1791         $a = get_app();
1792         $ch = curl_init($url);
1793         if(($redirects > 8) || (! $ch)) 
1794                 return false;
1795
1796         curl_setopt($ch, CURLOPT_HEADER, true);
1797         curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
1798         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
1799         curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
1800
1801         if(intval($timeout)) {
1802                 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1803         }
1804         else {
1805                 $curl_time = intval(get_config('system','curl_timeout'));
1806                 curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
1807         }
1808
1809         if(defined('LIGHTTPD')) {
1810                 if(!is_array($headers)) {
1811                         $headers = array('Expect:');
1812                 } else {
1813                         if(!in_array('Expect:', $headers)) {
1814                                 array_push($headers, 'Expect:');
1815                         }
1816                 }
1817         }
1818         if($headers)
1819                 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1820
1821         $check_cert = get_config('system','verifyssl');
1822         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
1823         $prx = get_config('system','proxy');
1824         if(strlen($prx)) {
1825                 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
1826                 curl_setopt($ch, CURLOPT_PROXY, $prx);
1827                 $prxusr = get_config('system','proxyuser');
1828                 if(strlen($prxusr))
1829                         curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
1830         }
1831
1832         $a->set_curl_code(0);
1833
1834         // don't let curl abort the entire application
1835         // if it throws any errors.
1836
1837         $s = @curl_exec($ch);
1838
1839         $base = $s;
1840         $curl_info = curl_getinfo($ch);
1841         $http_code = $curl_info['http_code'];
1842
1843         $header = '';
1844
1845         // Pull out multiple headers, e.g. proxy and continuation headers
1846         // allow for HTTP/2.x without fixing code
1847
1848         while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
1849                 $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
1850                 $header .= $chunk;
1851                 $base = substr($base,strlen($chunk));
1852         }
1853
1854         if($http_code == 301 || $http_code == 302 || $http_code == 303) {
1855         $matches = array();
1856         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
1857         $url = trim(array_pop($matches));
1858         $url_parsed = @parse_url($url);
1859         if (isset($url_parsed)) {
1860             $redirects++;
1861             return delete_url($url,$headers,$redirects,$timeout);
1862         }
1863     }
1864         $a->set_curl_code($http_code);
1865         $body = substr($s,strlen($header));
1866
1867         $a->set_curl_headers($header);
1868
1869         curl_close($ch);
1870         return($body);
1871 }}