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