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