3 * Name: Facebook Connector
5 * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
6 * Tobias Hößl <https://github.com/CatoTH/>
10 * Installing the Friendica/Facebook connector
12 * 1. register an API key for your site from developer.facebook.com
13 * a. We'd be very happy if you include "Friendica" in the application name
14 * to increase name recognition. The Friendica icons are also present
15 * in the images directory and may be uploaded as a Facebook app icon.
16 * Use images/friendica-16.jpg for the Icon and images/friendica-128.jpg for the Logo.
17 * b. The url should be your site URL with a trailing slash.
18 * Friendica is a software application and does not require a Privacy Policy
19 * or Terms of Service, though your installation of it might. Facebook may require
20 * that you provide a Privacy Policy, which we find ironic.
21 * c. Set the following values in your .htconfig.php file
22 * $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
23 * $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
24 * Replace with the settings Facebook gives you.
25 * d. Navigate to Set Web->Site URL & Domain -> Website Settings. Set
26 * Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your
28 * 2. (This step is now obsolete. Enable the plugin via the Admin panel.)
29 * Enable the facebook plugin by including it in .htconfig.php - e.g.
30 * $a->config['system']['addon'] = 'plugin1,plugin2,facebook';
31 * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
32 * and click 'Install Facebook Connector'.
33 * 4. This will ask you to login to Facebook and grant permission to the
34 * plugin to do its stuff. Allow it to do so.
35 * 5. Optional step: If you want to use Facebook Real Time Updates (so new messages
36 * and new contacts are added ~1min after they are postet / added on FB), go to
37 * Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button.
38 * 6. You're done. To turn it off visit the Plugin Settings page again and
39 * 'Remove Facebook posting'.
41 * Vidoes and embeds will not be posted if there is no other content. Links
42 * and images will be converted to a format suitable for the Facebook API and
43 * long posts truncated - with a link to view the full post.
45 * Facebook contacts will not be able to view private photos, as they are not able to
46 * authenticate to your site to establish identity. We will address this
47 * in a future release.
50 define('FACEBOOK_MAXPOSTLEN', 420);
53 function facebook_install() {
54 register_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
55 register_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
56 register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
57 register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
58 register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
59 register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
61 if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_add_users(); // Restore settings, if the plugin was installed before
65 function facebook_uninstall() {
66 unregister_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
67 unregister_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
68 unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
69 unregister_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
70 unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
71 unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
74 unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
75 unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
77 if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_del_users();
78 del_config('facebook', 'app_access_token');
82 /* declare the facebook_module function so that /facebook url requests will land here */
84 function facebook_module() {}
88 // If a->argv[1] is a nickname, this is a callback from Facebook oauth requests.
89 // If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API
91 function facebook_init(&$a) {
93 if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
94 logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
96 if (x($_REQUEST, "hub_verify_token")) {
97 // this is the verification callback while registering for real time updates
99 $verify_token = get_config('facebook', 'cb_verify_token');
100 if ($verify_token != $_REQUEST["hub_verify_token"]) {
101 logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
105 if (x($_REQUEST, "hub_challenge")) {
106 logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
107 echo $_REQUEST["hub_challenge"];
112 require_once('include/items.php');
114 // this is a status update
115 $content = file_get_contents("php://input");
116 if (is_numeric($content)) $content = file_get_contents("php://input");
117 $js = json_decode($content);
118 logger(print_r($js, true), LOGGER_DATA);
120 if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
121 logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
125 $affected_users = array("feed" => array(), "friends" => array());
127 foreach ($js->entry as $entry) {
128 $fbuser = $entry->uid;
129 foreach ($entry->changed_fields as $field) {
130 if (!isset($affected_users[$field])) {
131 logger('facebook_init: Unknown field "' . $field . '"');
134 if (in_array($fbuser, $affected_users[$field])) continue;
136 $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
141 $access_token = get_pconfig($uid,'facebook','access_token');
147 logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
149 if(! get_pconfig($uid,'facebook','no_wall')) {
150 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
151 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
153 $j = json_decode($s);
154 if (isset($j->data)) {
155 logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
156 fb_consume_stream($uid,$j,($private_wall) ? false : true);
158 logger('facebook_init: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
165 logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
167 fb_get_friends($uid, false);
168 set_pconfig($uid,'facebook','friend_check',time());
171 logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
173 $affected_users[$field][] = $fbuser;
183 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
190 $auth_code = (x($_GET, 'code') ? $_GET['code'] : '');
191 $error = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
195 logger('facebook_init: Error: ' . $error);
197 if($auth_code && $uid) {
199 $appid = get_config('facebook','appid');
200 $appsecret = get_config('facebook', 'appsecret');
202 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
203 . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
204 . urlencode($a->get_baseurl() . '/facebook/' . $nick)
205 . '&code=' . $auth_code);
207 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
209 if(strpos($x,'access_token=') !== false) {
210 $token = str_replace('access_token=', '', $x);
211 if(strpos($token,'&') !== false)
212 $token = substr($token,0,strpos($token,'&'));
213 set_pconfig($uid,'facebook','access_token',$token);
214 set_pconfig($uid,'facebook','post','1');
215 if(get_pconfig($uid,'facebook','no_linking') === false)
216 set_pconfig($uid,'facebook','no_linking',1);
218 fb_get_friends($uid, true);
219 fb_consume_all($uid);
228 function fb_get_self($uid) {
229 $access_token = get_pconfig($uid,'facebook','access_token');
232 $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
234 $j = json_decode($s);
235 set_pconfig($uid,'facebook','self_id',(string) $j->id);
239 function fb_get_friends_sync_new($uid, $access_token, $person) {
240 $link = 'http://facebook.com/profile.php?id=' . $person->id;
242 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
247 if (count($r) == 0) {
248 logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
250 fb_get_friends_sync_full($uid, $access_token, $person);
254 function fb_get_friends_sync_full($uid, $access_token, $person) {
255 $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
257 $jp = json_decode($s);
258 logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
260 // always use numeric link for consistency
262 $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
264 // check if we already have a contact
266 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
273 // check that we have all the photos, this has been known to fail on occasion
275 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
276 require_once("Photo.php");
278 $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
280 $r = q("UPDATE `contact` SET `photo` = '%s',
286 WHERE `id` = %d LIMIT 1
291 dbesc(datetime_convert()),
292 dbesc(datetime_convert()),
293 dbesc(datetime_convert()),
301 // create contact record
302 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
303 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
304 `writable`, `blocked`, `readonly`, `pending` )
305 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
307 dbesc(datetime_convert()),
309 dbesc(normalise_link($jp->link)),
313 dbesc('facebook ' . $jp->id),
315 dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
316 dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
317 dbesc(NETWORK_FACEBOOK),
318 intval(CONTACT_IS_FRIEND),
324 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
334 $contact_id = $r[0]['id'];
336 require_once("Photo.php");
338 $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
340 $r = q("UPDATE `contact` SET `photo` = '%s',
346 WHERE `id` = %d LIMIT 1
351 dbesc(datetime_convert()),
352 dbesc(datetime_convert()),
353 dbesc(datetime_convert()),
360 // if $fullsync is true, only new contacts are searched for
362 function fb_get_friends($uid, $fullsync = true) {
364 $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
370 $access_token = get_pconfig($uid,'facebook','access_token');
372 $no_linking = get_pconfig($uid,'facebook','no_linking');
378 $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
380 logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
381 $j = json_decode($s);
382 logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
385 foreach($j->data as $person)
387 fb_get_friends_sync_full($uid, $access_token, $person);
389 fb_get_friends_sync_new($uid, $access_token, $person);
393 // This is the POST method to the facebook settings page
394 // Content is posted to Facebook in the function facebook_post_hook()
396 function facebook_post(&$a) {
401 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
402 set_pconfig($uid,'facebook','post_by_default', $value);
404 $no_linking = get_pconfig($uid,'facebook','no_linking');
406 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
407 set_pconfig($uid,'facebook','no_wall',$no_wall);
409 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
410 set_pconfig($uid,'facebook','private_wall',$private_wall);
413 set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
415 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
416 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
418 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
420 if((! intval($no_linking)) && (! intval($linkvalue))) {
421 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
423 dbesc(NETWORK_FACEBOOK)
426 require_once('include/Contact.php');
428 contact_remove($rr['id']);
431 elseif(intval($no_linking) && intval($linkvalue)) {
432 // FB linkage is now allowed - import stuff.
434 fb_get_friends($uid, true);
435 fb_consume_all($uid);
438 info( t('Settings updated.') . EOL);
444 // Facebook settings form
446 function facebook_content(&$a) {
449 notice( t('Permission denied.') . EOL);
453 if($a->argc > 1 && $a->argv[1] === 'remove') {
454 del_pconfig(local_user(),'facebook','post');
455 info( t('Facebook disabled') . EOL);
458 if($a->argc > 1 && $a->argv[1] === 'friends') {
459 fb_get_friends(local_user(), true);
460 info( t('Updating contacts') . EOL);
464 $fb_installed = get_pconfig(local_user(),'facebook','post');
466 $appid = get_config('facebook','appid');
469 notice( t('Facebook API key is missing.') . EOL);
473 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
474 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
476 $o .= '<h3>' . t('Facebook Connect') . '</h3>';
478 if(! $fb_installed) {
479 $o .= '<div id="facebook-enable-wrapper">';
481 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
482 . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
487 $o .= '<div id="facebook-disable-wrapper">';
489 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
491 $o .= '<div id="facebook-enable-wrapper">';
493 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
494 . $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>';
497 $o .= '<div id="facebook-post-default-form">';
498 $o .= '<form action="facebook" method="post" >';
499 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
500 $checked = (($post_by_default) ? ' checked="checked" ' : '');
501 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
503 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
504 $checked = (($no_linking) ? '' : ' checked="checked" ');
505 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
507 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
508 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
509 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
511 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
512 $checked = (($private_wall) ? ' checked="checked" ' : '');
513 $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 ;
516 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
517 $checked = (($no_wall) ? ' checked="checked" ' : '');
518 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
520 $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>';
523 $blocked_apps = get_pconfig(local_user(),'facebook','blocked_apps');
525 $o .= '<div><label id="blocked-apps-label" for="blocked-apps">' . t('Comma separated applications to ignore') . ' </label></div>';
526 $o .= '<div><textarea id="blocked-apps" name="blocked_apps" >' . htmlspecialchars($blocked_apps) . '</textarea></div>';
528 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
536 function facebook_cron($a,$b) {
538 $last = get_config('facebook','last_poll');
540 $poll_interval = intval(get_config('facebook','poll_interval'));
542 $poll_interval = 3600;
545 $next = $last + $poll_interval;
550 logger('facebook_cron');
553 // Find the FB users on this site and randomize in case one of them
554 // uses an obscene amount of memory. It may kill this queue run
555 // but hopefully we'll get a few others through on each run.
557 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
560 if(get_pconfig($rr['uid'],'facebook','no_linking'))
562 $ab = intval(get_config('system','account_abandon_days'));
564 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
572 // check for new friends once a day
573 $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
574 if($last_friend_check)
575 $next_friend_check = $last_friend_check + 86400;
576 if($next_friend_check <= time()) {
577 fb_get_friends($rr['uid'], true);
578 set_pconfig($rr['uid'],'facebook','friend_check',time());
580 fb_consume_all($rr['uid']);
584 if (get_config('facebook', 'realtime_active') == 1) {
585 if (!facebook_check_realtime_active()) {
587 logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
588 facebook_subscription_add_users();
590 if (facebook_check_realtime_active())
591 logger('facebook_cron: Successful', LOGGER_NORMAL);
593 logger('facebook_cron: Failed', LOGGER_NORMAL);
595 if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) {
596 $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'),
597 "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannob be solved automatically. Maybe an permission issue?\n\nThis e-mail will only be sent once.",
598 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
599 . 'Content-type: text/plain; charset=UTF-8' . "\n"
600 . 'Content-transfer-encoding: 8bit'
603 set_config('facebook', 'realtime_err_mailsent', 1);
606 } else { // !facebook_check_realtime_active()
607 del_config('facebook', 'realtime_err_mailsent');
611 set_config('facebook','last_poll', time());
617 function facebook_plugin_settings(&$a,&$b) {
619 $b .= '<div class="settings-block">';
620 $b .= '<h3>' . t('Facebook') . '</h3>';
621 $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
627 function facebook_plugin_admin(&$a, &$o){
629 $activated = facebook_check_realtime_active();
631 $o = t('Real-Time Updates are activated.') . '<br><br>';
632 $o .= '<input type="submit" name="real_time_deactivate" value="' . t('Deactivate Real-Time Updates') . '">';
634 $o = t('Real-Time Updates not activated.') . '<br><input type="submit" name="real_time_activate" value="' . t('Activate Real-Time Updates') . '">';
638 function facebook_plugin_admin_post(&$a, &$o){
639 if (x($_REQUEST,'real_time_activate')) {
640 facebook_subscription_add_users();
642 if (x($_REQUEST,'real_time_deactivate')) {
643 facebook_subscription_del_users();
647 function facebook_jot_nets(&$a,&$b) {
651 $fb_post = get_pconfig(local_user(),'facebook','post');
652 if(intval($fb_post) == 1) {
653 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
654 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
655 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> '
656 . t('Post to Facebook') . '</div>';
661 function facebook_post_hook(&$a,&$b) {
664 if($b['deleted'] || ($b['created'] !== $b['edited']))
668 * Post to Facebook stream
671 require_once('include/group.php');
673 logger('Facebook post');
678 $toplevel = (($b['id'] == $b['parent']) ? true : false);
681 $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
683 if((! $toplevel) && ($linking)) {
684 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
685 intval($b['parent']),
688 if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
689 $reply = substr($r[0]['uri'],4);
690 elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
691 $reply = substr($r[0]['extid'],4);
695 $u = q("SELECT * FROM user where uid = %d limit 1",
701 // only accept comments from the item owner. Other contacts are unknown to FB.
703 if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
707 logger('facebook reply id=' . $reply);
710 if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
712 if($b['private'] && $reply === false) {
713 $allow_people = expand_acl($b['allow_cid']);
714 $allow_groups = expand_groups(expand_acl($b['allow_gid']));
715 $deny_people = expand_acl($b['deny_cid']);
716 $deny_groups = expand_groups(expand_acl($b['deny_gid']));
718 $recipients = array_unique(array_merge($allow_people,$allow_groups));
719 $deny = array_unique(array_merge($deny_people,$deny_groups));
721 $allow_str = dbesc(implode(', ',$recipients));
723 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
724 $allow_arr = array();
727 $allow_arr[] = $rr['notify'];
730 $deny_str = dbesc(implode(', ',$deny));
732 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
736 $deny_arr[] = $rr['notify'];
739 if(count($deny_arr) && (! count($allow_arr))) {
741 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
742 // This might cause the post to be open to public on Facebook, but only to selected members
743 // on another network. Since this could potentially leak a post to somebody who was denied,
744 // we will skip posting it to Facebook with a slightly vague but relevant message that will
745 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
747 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
752 // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
754 if((! count($allow_arr)) && (! count($deny_arr)))
758 if($b['verb'] == ACTIVITY_LIKE)
762 $appid = get_config('facebook', 'appid' );
763 $secret = get_config('facebook', 'appsecret' );
765 if($appid && $secret) {
767 logger('facebook: have appid+secret');
769 $fb_token = get_pconfig($b['uid'],'facebook','access_token');
772 // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
773 // or it's a private message with facebook participants
774 // or it's a reply or likes action to an existing facebook post
776 if($fb_token && ($toplevel || $b['private'] || $reply)) {
777 logger('facebook: able to post');
778 require_once('library/facebook.php');
779 require_once('include/bbcode.php');
783 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
785 // make links readable before we strip the code
787 // unless it's a dislike - just send the text as a comment
789 if($b['verb'] == ACTIVITY_DISLIKE)
790 $msg = trim(strip_tags(bbcode($msg)));
792 $search_str = $a->get_baseurl() . '/search';
794 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
796 // don't use hashtags for message link
798 if(strpos($matches[2],$search_str) === false) {
800 if(substr($matches[2],0,5) != '[img]')
801 $linkname = $matches[2];
805 // strip tag links to avoid link clutter, this really should be
806 // configurable because we're losing information
808 $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
810 // provide the link separately for normal links
811 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
813 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
814 $image = $matches[1];
816 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
818 if((strpos($link,z_root()) !== false) && (! $image))
819 $image = $a->get_baseurl() . '/images/friendica-64.jpg';
821 $msg = trim(strip_tags(bbcode($msg)));
822 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
824 // add any attachments as text urls
826 $arr = explode(',',$b['attach']);
830 foreach($arr as $r) {
832 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
839 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
841 require_once('library/slinky.php');
843 $display_url = $b['plink'];
845 $slinky = new Slinky( $display_url );
846 // setup a cascade of shortening services
847 // try to get a short link from these services
848 // in the order ur1.ca, trim, id.gd, tinyurl
849 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
850 $shortlink = $slinky->short();
851 // the new message will be shortened such that "... $shortlink"
852 // will fit into the character limit
853 $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
854 $msg .= '... ' . $shortlink;
859 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
862 $postvars = array('access_token' => $fb_token);
866 'access_token' => $fb_token,
870 $postvars['picture'] = $image;
872 $postvars['link'] = $link;
874 $postvars['name'] = $linkname;
877 if(($b['private']) && ($toplevel)) {
878 $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
879 if(count($allow_arr))
880 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
882 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
883 $postvars['privacy'] .= '}';
888 $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
891 $url = 'https://graph.facebook.com/me/feed';
893 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
896 logger('facebook: post to ' . $url);
897 logger('facebook: postvars: ' . print_r($postvars,true));
899 // "test_mode" prevents anything from actually being posted.
900 // Otherwise, let's do it.
902 if(! get_config('facebook','test_mode')) {
903 $x = post_url($url, $postvars);
905 $retj = json_decode($x);
907 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
908 dbesc('fb::' . $retj->id),
914 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
915 require_once('include/queue_fn.php');
916 add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
917 notice( t('Facebook post failed. Queued for retry.') . EOL);
921 logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
929 function facebook_post_local(&$a,&$b) {
931 // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
932 // where we will discover it during background delivery.
934 // This can only be triggered by a local user posting to their own wall.
936 if((local_user()) && (local_user() == $b['uid'])) {
938 $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
939 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
941 // if API is used, default to the chosen settings
942 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
948 if(strlen($b['postopts']))
949 $b['postopts'] .= ',';
950 $b['postopts'] .= 'facebook';
955 function fb_queue_hook(&$a,&$b) {
957 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
958 dbesc(NETWORK_FACEBOOK)
963 require_once('include/queue_fn.php');
966 if($x['network'] !== NETWORK_FACEBOOK)
969 logger('facebook_queue: run');
971 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
972 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
980 $appid = get_config('facebook', 'appid' );
981 $secret = get_config('facebook', 'appsecret' );
983 if($appid && $secret) {
984 $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
985 $fb_token = get_pconfig($user['uid'],'facebook','access_token');
987 if($fb_post && $fb_token) {
988 logger('facebook_queue: able to post');
989 require_once('library/facebook.php');
991 $z = unserialize($x['content']);
993 $j = post_url($z['url'],$z['post']);
995 $retj = json_decode($j);
997 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
998 dbesc('fb::' . $retj->id),
1001 logger('facebook_queue: success: ' . $j);
1002 remove_queue_item($x['id']);
1005 logger('facebook_queue: failed: ' . $j);
1006 update_queue_time($x['id']);
1013 function fb_consume_all($uid) {
1015 require_once('include/items.php');
1017 $access_token = get_pconfig($uid,'facebook','access_token');
1021 if(! get_pconfig($uid,'facebook','no_wall')) {
1022 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
1023 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
1025 $j = json_decode($s);
1026 if (isset($j->data)) {
1027 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
1028 fb_consume_stream($uid,$j,($private_wall) ? false : true);
1030 logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1034 $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
1036 $j = json_decode($s);
1037 if (isset($j->data)) {
1038 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
1039 fb_consume_stream($uid,$j,false);
1041 logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
1047 function fb_get_photo($uid,$link) {
1048 $access_token = get_pconfig($uid,'facebook','access_token');
1049 if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
1050 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1051 $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
1053 $photo_id = $match[1];
1054 $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
1055 $j = json_decode($x);
1057 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
1059 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1062 function fb_consume_stream($uid,$j,$wall = false) {
1067 $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
1073 $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1075 $no_linking = get_pconfig($uid,'facebook','no_linking');
1079 $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1083 $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
1084 $blocked_apps_arr = explode(',',$blocked_apps);
1086 $self_id = get_pconfig($uid,'facebook','self_id');
1087 if(! count($j->data) || (! strlen($self_id)))
1090 foreach($j->data as $entry) {
1091 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
1092 $datarray = array();
1094 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
1095 dbesc('fb::' . $entry->id),
1096 dbesc('fb::' . $entry->id),
1100 $post_exists = true;
1102 $top_item = $r[0]['id'];
1105 $post_exists = false;
1110 $datarray['gravity'] = 0;
1111 $datarray['uid'] = $uid;
1112 $datarray['wall'] = (($wall) ? 1 : 0);
1113 $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
1114 $from = $entry->from;
1115 if($from->id == $self_id)
1116 $datarray['contact-id'] = $self[0]['id'];
1118 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1123 $datarray['contact-id'] = $r[0]['id'];
1126 // don't store post if we don't have a contact
1128 if(! x($datarray,'contact-id')) {
1129 logger('no contact: post ignored');
1133 $datarray['verb'] = ACTIVITY_POST;
1135 $datarray['owner-name'] = $self[0]['name'];
1136 $datarray['owner-link'] = $self[0]['url'];
1137 $datarray['owner-avatar'] = $self[0]['thumb'];
1139 if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
1140 $datarray['app'] = strip_tags($entry->application->name);
1142 $datarray['app'] = 'facebook';
1144 $found_blocked = false;
1146 if(count($blocked_apps_arr)) {
1147 foreach($blocked_apps_arr as $bad_appl) {
1148 if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
1149 $found_blocked = true;
1154 if($found_blocked) {
1155 logger('facebook: blocking application: ' . $datarray['app']);
1159 $datarray['author-name'] = $from->name;
1160 $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
1161 $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
1162 $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
1164 $datarray['body'] = escape_tags($entry->message);
1166 if($entry->picture && $entry->link) {
1167 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
1171 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
1172 // if just a link, it may be a wall photo - check
1174 $datarray['body'] .= fb_get_photo($uid,$entry->link);
1177 $datarray['body'] .= "\n" . $entry->name;
1179 $datarray['body'] .= "\n" . $entry->caption;
1180 if($entry->description)
1181 $datarray['body'] .= "\n" . $entry->description;
1182 $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1183 $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1185 // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1186 // as the identities are from a foreign system. Mark it as private to the owner.
1188 if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1189 $datarray['private'] = 1;
1190 $datarray['allow_cid'] = '<' . $uid . '>';
1193 if(trim($datarray['body']) == '') {
1194 logger('facebook: empty body');
1198 $top_item = item_store($datarray);
1199 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1205 logger('fb: new top level item posted');
1209 if(isset($entry->likes) && isset($entry->likes->data))
1210 $likers = $entry->likes->data;
1214 if(isset($entry->comments) && isset($entry->comments->data))
1215 $comments = $entry->comments->data;
1219 if(is_array($likers)) {
1220 foreach($likers as $likes) {
1225 // If we posted the like locally, it will be found with our url, not the FB url.
1227 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
1229 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
1230 AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1231 dbesc($orig_post['uri']),
1233 dbesc(ACTIVITY_LIKE),
1234 dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1241 $likedata = array();
1242 $likedata['parent'] = $top_item;
1243 $likedata['verb'] = ACTIVITY_LIKE;
1244 $likedata['gravity'] = 3;
1245 $likedata['uid'] = $uid;
1246 $likedata['wall'] = (($wall) ? 1 : 0);
1247 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1248 $likedata['parent-uri'] = $orig_post['uri'];
1249 if($likes->id == $self_id)
1250 $likedata['contact-id'] = $self[0]['id'];
1252 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1257 $likedata['contact-id'] = $r[0]['id'];
1259 if(! x($likedata,'contact-id'))
1260 $likedata['contact-id'] = $orig_post['contact-id'];
1262 $likedata['app'] = 'facebook';
1263 $likedata['verb'] = ACTIVITY_LIKE;
1264 $likedata['author-name'] = $likes->name;
1265 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1266 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1268 $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1269 $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1270 $post_type = t('status');
1271 $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1272 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1274 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1275 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
1276 '<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>';
1278 $item = item_store($likedata);
1281 if(is_array($comments)) {
1282 foreach($comments as $cmnt) {
1287 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1289 dbesc('fb::' . $cmnt->id),
1290 dbesc('fb::' . $cmnt->id)
1295 $cmntdata = array();
1296 $cmntdata['parent'] = $top_item;
1297 $cmntdata['verb'] = ACTIVITY_POST;
1298 $cmntdata['gravity'] = 6;
1299 $cmntdata['uid'] = $uid;
1300 $cmntdata['wall'] = (($wall) ? 1 : 0);
1301 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1302 $cmntdata['parent-uri'] = $orig_post['uri'];
1303 if($cmnt->from->id == $self_id) {
1304 $cmntdata['contact-id'] = $self[0]['id'];
1307 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1308 dbesc($cmnt->from->id),
1312 $cmntdata['contact-id'] = $r[0]['id'];
1313 if($r[0]['blocked'] || $r[0]['readonly'])
1317 if(! x($cmntdata,'contact-id'))
1318 $cmntdata['contact-id'] = $orig_post['contact-id'];
1320 $cmntdata['app'] = 'facebook';
1321 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1322 $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1323 $cmntdata['verb'] = ACTIVITY_POST;
1324 $cmntdata['author-name'] = $cmnt->from->name;
1325 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1326 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1327 $cmntdata['body'] = $cmnt->message;
1328 $item = item_store($cmntdata);
1330 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
1331 dbesc($orig_post['uri']),
1335 if(count($myconv)) {
1336 $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1338 foreach($myconv as $conv) {
1340 // now if we find a match, it means we're in this conversation
1342 if(! link_compare($conv['author-link'],$importer_url))
1345 require_once('include/enotify.php');
1347 $conv_parent = $conv['parent'];
1350 'type' => NOTIFY_COMMENT,
1351 'notify_flags' => $user[0]['notify-flags'],
1352 'language' => $user[0]['language'],
1353 'to_name' => $user[0]['username'],
1354 'to_email' => $user[0]['email'],
1355 'uid' => $user[0]['uid'],
1356 'item' => $cmntdata,
1357 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $item,
1358 'source_name' => $cmntdata['author-name'],
1359 'source_link' => $cmntdata['author-link'],
1360 'source_photo' => $cmntdata['author-avatar'],
1361 'verb' => ACTIVITY_POST,
1363 'parent' => $conv_parent,
1366 // only send one notification
1376 function fb_get_app_access_token() {
1378 $acc_token = get_config('facebook','app_access_token');
1380 if ($acc_token !== false) return $acc_token;
1382 $appid = get_config('facebook','appid');
1383 $appsecret = get_config('facebook', 'appsecret');
1385 if ($appid === false || $appsecret === false) {
1386 logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
1390 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . "&grant_type=client_credentials");
1392 if(strpos($x,'access_token=') !== false) {
1393 logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
1395 $token = str_replace('access_token=', '', $x);
1396 if(strpos($token,'&') !== false)
1397 $token = substr($token,0,strpos($token,'&'));
1400 logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
1403 set_config('facebook','app_access_token',$token);
1406 logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
1411 function facebook_subscription_del_users() {
1413 $access_token = fb_get_app_access_token();
1415 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1416 facebook_delete_url($url);
1418 del_config('facebook', 'realtime_active');
1421 function facebook_subscription_add_users() {
1424 $access_token = fb_get_app_access_token();
1426 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1428 list($usec, $sec) = explode(" ", microtime());
1429 $verify_token = sha1($usec . $sec . rand(0, 999999999));
1430 set_config('facebook', 'cb_verify_token', $verify_token);
1432 $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
1434 $j = post_url($url,array(
1436 "fields" => "feed,friends",
1437 "callback_url" => $cb,
1438 "verify_token" => $verify_token,
1440 del_config('facebook', 'cb_verify_token');
1443 logger("Facebook reponse: " . $j, LOGGER_DATA);
1445 if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
1449 function facebook_subscriptions_get() {
1451 $access_token = fb_get_app_access_token();
1452 if (!$access_token) return null;
1454 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1455 $j = fetch_url($url);
1458 $x = json_decode($j);
1459 if (isset($x->data)) $ret = $x->data;
1465 function facebook_check_realtime_active() {
1466 $ret = facebook_subscriptions_get();
1467 if (is_null($ret)) return false;
1468 if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
1475 // DELETE-request to $url
1477 if(! function_exists('facebook_delete_url')) {
1478 function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
1480 $ch = curl_init($url);
1481 if(($redirects > 8) || (! $ch))
1484 curl_setopt($ch, CURLOPT_HEADER, true);
1485 curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
1486 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
1487 curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
1489 if(intval($timeout)) {
1490 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1493 $curl_time = intval(get_config('system','curl_timeout'));
1494 curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
1497 if(defined('LIGHTTPD')) {
1498 if(!is_array($headers)) {
1499 $headers = array('Expect:');
1501 if(!in_array('Expect:', $headers)) {
1502 array_push($headers, 'Expect:');
1507 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1509 $check_cert = get_config('system','verifyssl');
1510 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
1511 $prx = get_config('system','proxy');
1513 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
1514 curl_setopt($ch, CURLOPT_PROXY, $prx);
1515 $prxusr = get_config('system','proxyuser');
1517 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
1520 $a->set_curl_code(0);
1522 // don't let curl abort the entire application
1523 // if it throws any errors.
1525 $s = @curl_exec($ch);
1528 $curl_info = curl_getinfo($ch);
1529 $http_code = $curl_info['http_code'];
1533 // Pull out multiple headers, e.g. proxy and continuation headers
1534 // allow for HTTP/2.x without fixing code
1536 while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
1537 $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
1539 $base = substr($base,strlen($chunk));
1542 if($http_code == 301 || $http_code == 302 || $http_code == 303) {
1544 preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
1545 $url = trim(array_pop($matches));
1546 $url_parsed = @parse_url($url);
1547 if (isset($url_parsed)) {
1549 return delete_url($url,$headers,$redirects,$timeout);
1552 $a->set_curl_code($http_code);
1553 $body = substr($s,strlen($header));
1555 $a->set_curl_headers($header);