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