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();
81 /* declare the facebook_module function so that /facebook url requests will land here */
83 function facebook_module() {}
87 // If a->argv[1] is a nickname, this is a callback from Facebook oauth requests.
88 // If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API
90 function facebook_init(&$a) {
92 if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
93 logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
95 if (x($_REQUEST, "hub_verify_token")) {
96 // this is the verification callback while registering for real time updates
98 $verify_token = get_config('facebook', 'cb_verify_token');
99 if ($verify_token != $_REQUEST["hub_verify_token"]) {
100 logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
104 if (x($_REQUEST, "hub_challenge")) {
105 logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
106 echo $_REQUEST["hub_challenge"];
111 require_once('include/items.php');
113 // this is a status update
114 $content = file_get_contents("php://input");
115 if (is_numeric($content)) $content = file_get_contents("php://input");
116 $js = json_decode($content);
117 logger(print_r($js, true), LOGGER_DATA);
119 if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
120 logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
124 $affected_users = array("feed" => array(), "friends" => array());
126 foreach ($js->entry as $entry) {
127 $fbuser = $entry->uid;
128 foreach ($entry->changed_fields as $field) {
129 if (!isset($affected_users[$field])) {
130 logger('facebook_init: Unknown field "' . $field . '"');
133 if (in_array($fbuser, $affected_users[$field])) continue;
135 $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
140 $access_token = get_pconfig($uid,'facebook','access_token');
146 logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
148 if(! get_pconfig($uid,'facebook','no_wall')) {
149 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
150 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
152 $j = json_decode($s);
153 logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
154 fb_consume_stream($uid,$j,($private_wall) ? false : true);
160 logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
162 fb_get_friends($uid, false);
163 set_pconfig($uid,'facebook','friend_check',time());
166 logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
168 $affected_users[$field][] = $fbuser;
178 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
185 $auth_code = (x($_GET, 'code') ? $_GET['code'] : '');
186 $error = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
190 logger('facebook_init: Error: ' . $error);
192 if($auth_code && $uid) {
194 $appid = get_config('facebook','appid');
195 $appsecret = get_config('facebook', 'appsecret');
197 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
198 . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
199 . urlencode($a->get_baseurl() . '/facebook/' . $nick)
200 . '&code=' . $auth_code);
202 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
204 if(strpos($x,'access_token=') !== false) {
205 $token = str_replace('access_token=', '', $x);
206 if(strpos($token,'&') !== false)
207 $token = substr($token,0,strpos($token,'&'));
208 set_pconfig($uid,'facebook','access_token',$token);
209 set_pconfig($uid,'facebook','post','1');
210 if(get_pconfig($uid,'facebook','no_linking') === false)
211 set_pconfig($uid,'facebook','no_linking',1);
213 fb_get_friends($uid, true);
214 fb_consume_all($uid);
223 function fb_get_self($uid) {
224 $access_token = get_pconfig($uid,'facebook','access_token');
227 $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
229 $j = json_decode($s);
230 set_pconfig($uid,'facebook','self_id',(string) $j->id);
234 function fb_get_friends_sync_new($uid, $access_token, $person) {
235 $link = 'http://facebook.com/profile.php?id=' . $person->id;
237 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
242 if (count($r) == 0) {
243 logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
245 fb_get_friends_sync_full($uid, $access_token, $person);
249 function fb_get_friends_sync_full($uid, $access_token, $person) {
250 $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
252 $jp = json_decode($s);
253 logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
255 // always use numeric link for consistency
257 $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
259 // check if we already have a contact
261 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
268 // check that we have all the photos, this has been known to fail on occasion
270 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
271 require_once("Photo.php");
273 $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
275 $r = q("UPDATE `contact` SET `photo` = '%s',
281 WHERE `id` = %d LIMIT 1
286 dbesc(datetime_convert()),
287 dbesc(datetime_convert()),
288 dbesc(datetime_convert()),
296 // create contact record
297 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
298 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
299 `writable`, `blocked`, `readonly`, `pending` )
300 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
302 dbesc(datetime_convert()),
304 dbesc(normalise_link($jp->link)),
308 dbesc('facebook ' . $jp->id),
310 dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
311 dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
312 dbesc(NETWORK_FACEBOOK),
313 intval(CONTACT_IS_FRIEND),
319 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
329 $contact_id = $r[0]['id'];
331 require_once("Photo.php");
333 $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
335 $r = q("UPDATE `contact` SET `photo` = '%s',
341 WHERE `id` = %d LIMIT 1
346 dbesc(datetime_convert()),
347 dbesc(datetime_convert()),
348 dbesc(datetime_convert()),
355 // if $fullsync is true, only new contacts are searched for
357 function fb_get_friends($uid, $fullsync = true) {
359 $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
365 $access_token = get_pconfig($uid,'facebook','access_token');
367 $no_linking = get_pconfig($uid,'facebook','no_linking');
373 $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
375 logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
376 $j = json_decode($s);
377 logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
380 foreach($j->data as $person)
382 fb_get_friends_sync_full($uid, $access_token, $person);
384 fb_get_friends_sync_new($uid, $access_token, $person);
388 // This is the POST method to the facebook settings page
389 // Content is posted to Facebook in the function facebook_post_hook()
391 function facebook_post(&$a) {
396 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
397 set_pconfig($uid,'facebook','post_by_default', $value);
399 $no_linking = get_pconfig($uid,'facebook','no_linking');
401 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
402 set_pconfig($uid,'facebook','no_wall',$no_wall);
404 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
405 set_pconfig($uid,'facebook','private_wall',$private_wall);
408 set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
410 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
411 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
413 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
415 if((! intval($no_linking)) && (! intval($linkvalue))) {
416 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
418 dbesc(NETWORK_FACEBOOK)
421 require_once('include/Contact.php');
423 contact_remove($rr['id']);
426 elseif(intval($no_linking) && intval($linkvalue)) {
427 // FB linkage is now allowed - import stuff.
429 fb_get_friends($uid, true);
430 fb_consume_all($uid);
433 info( t('Settings updated.') . EOL);
439 // Facebook settings form
441 function facebook_content(&$a) {
444 notice( t('Permission denied.') . EOL);
448 if($a->argc > 1 && $a->argv[1] === 'remove') {
449 del_pconfig(local_user(),'facebook','post');
450 info( t('Facebook disabled') . EOL);
453 if($a->argc > 1 && $a->argv[1] === 'friends') {
454 fb_get_friends(local_user(), true);
455 info( t('Updating contacts') . EOL);
459 $fb_installed = get_pconfig(local_user(),'facebook','post');
461 $appid = get_config('facebook','appid');
464 notice( t('Facebook API key is missing.') . EOL);
468 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
469 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
471 $o .= '<h3>' . t('Facebook Connect') . '</h3>';
473 if(! $fb_installed) {
474 $o .= '<div id="facebook-enable-wrapper">';
476 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
477 . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
482 $o .= '<div id="facebook-disable-wrapper">';
484 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
486 $o .= '<div id="facebook-enable-wrapper">';
488 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
489 . $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>';
492 $o .= '<div id="facebook-post-default-form">';
493 $o .= '<form action="facebook" method="post" >';
494 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
495 $checked = (($post_by_default) ? ' checked="checked" ' : '');
496 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
498 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
499 $checked = (($no_linking) ? '' : ' checked="checked" ');
500 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
502 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
503 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
504 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
506 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
507 $checked = (($private_wall) ? ' checked="checked" ' : '');
508 $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 ;
511 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
512 $checked = (($no_wall) ? ' checked="checked" ' : '');
513 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
515 $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>';
518 $blocked_apps = get_pconfig(local_user(),'facebook','blocked_apps');
520 $o .= '<div><label id="blocked-apps-label" for="blocked-apps">' . t('Comma separated applications to ignore') . ' </label></div>';
521 $o .= '<div><textarea id="blocked-apps" name="blocked_apps" >' . htmlspecialchars($blocked_apps) . '</textarea></div>';
523 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
531 function facebook_cron($a,$b) {
533 $last = get_config('facebook','last_poll');
535 $poll_interval = intval(get_config('facebook','poll_interval'));
537 $poll_interval = 3600;
540 $next = $last + $poll_interval;
545 logger('facebook_cron');
548 // Find the FB users on this site and randomize in case one of them
549 // uses an obscene amount of memory. It may kill this queue run
550 // but hopefully we'll get a few others through on each run.
552 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
555 if(get_pconfig($rr['uid'],'facebook','no_linking'))
557 $ab = intval(get_config('system','account_abandon_days'));
559 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
567 // check for new friends once a day
568 $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
569 if($last_friend_check)
570 $next_friend_check = $last_friend_check + 86400;
571 if($next_friend_check <= time()) {
572 fb_get_friends($rr['uid'], true);
573 set_pconfig($rr['uid'],'facebook','friend_check',time());
575 fb_consume_all($rr['uid']);
579 if (get_config('facebook', 'realtime_active') == 1) {
580 if (!facebook_check_realtime_active()) {
582 logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
583 facebook_subscription_add_users();
585 if (facebook_check_realtime_active())
586 logger('facebook_cron: Successful', LOGGER_NORMAL);
588 logger('facebook_cron: Failed', LOGGER_NORMAL);
590 if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) {
591 $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'),
592 "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.",
593 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
594 . 'Content-type: text/plain; charset=UTF-8' . "\n"
595 . 'Content-transfer-encoding: 8bit'
598 set_config('facebook', 'realtime_err_mailsent', 1);
601 } else { // !facebook_check_realtime_active()
602 del_config('facebook', 'realtime_err_mailsent');
606 set_config('facebook','last_poll', time());
612 function facebook_plugin_settings(&$a,&$b) {
614 $b .= '<div class="settings-block">';
615 $b .= '<h3>' . t('Facebook') . '</h3>';
616 $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
622 function facebook_plugin_admin(&$a, &$o){
624 $activated = facebook_check_realtime_active();
626 $o = t('Real-Time Updates are activated.') . '<br><br>';
627 $o .= '<input type="submit" name="real_time_deactivate" value="' . t('Deactivate Real-Time Updates') . '">';
629 $o = t('Real-Time Updates not activated.') . '<br><input type="submit" name="real_time_activate" value="' . t('Activate Real-Time Updates') . '">';
633 function facebook_plugin_admin_post(&$a, &$o){
634 if (x($_REQUEST,'real_time_activate')) {
635 facebook_subscription_add_users();
637 if (x($_REQUEST,'real_time_deactivate')) {
638 facebook_subscription_del_users();
642 function facebook_jot_nets(&$a,&$b) {
646 $fb_post = get_pconfig(local_user(),'facebook','post');
647 if(intval($fb_post) == 1) {
648 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
649 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
650 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> '
651 . t('Post to Facebook') . '</div>';
656 function facebook_post_hook(&$a,&$b) {
659 if($b['deleted'] || ($b['created'] !== $b['edited']))
663 * Post to Facebook stream
666 require_once('include/group.php');
667 require_once('include/html2plain.php');
669 logger('Facebook post');
674 $toplevel = (($b['id'] == $b['parent']) ? true : false);
677 $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
679 if((! $toplevel) && ($linking)) {
680 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
681 intval($b['parent']),
684 if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
685 $reply = substr($r[0]['uri'],4);
686 elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
687 $reply = substr($r[0]['extid'],4);
691 $u = q("SELECT * FROM user where uid = %d limit 1",
697 // only accept comments from the item owner. Other contacts are unknown to FB.
699 if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
703 logger('facebook reply id=' . $reply);
706 if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
708 if($b['private'] && $reply === false) {
709 $allow_people = expand_acl($b['allow_cid']);
710 $allow_groups = expand_groups(expand_acl($b['allow_gid']));
711 $deny_people = expand_acl($b['deny_cid']);
712 $deny_groups = expand_groups(expand_acl($b['deny_gid']));
714 $recipients = array_unique(array_merge($allow_people,$allow_groups));
715 $deny = array_unique(array_merge($deny_people,$deny_groups));
717 $allow_str = dbesc(implode(', ',$recipients));
719 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
720 $allow_arr = array();
723 $allow_arr[] = $rr['notify'];
726 $deny_str = dbesc(implode(', ',$deny));
728 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
732 $deny_arr[] = $rr['notify'];
735 if(count($deny_arr) && (! count($allow_arr))) {
737 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
738 // This might cause the post to be open to public on Facebook, but only to selected members
739 // on another network. Since this could potentially leak a post to somebody who was denied,
740 // we will skip posting it to Facebook with a slightly vague but relevant message that will
741 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
743 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
748 // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
750 if((! count($allow_arr)) && (! count($deny_arr)))
754 if($b['verb'] == ACTIVITY_LIKE)
758 $appid = get_config('facebook', 'appid' );
759 $secret = get_config('facebook', 'appsecret' );
761 if($appid && $secret) {
763 logger('facebook: have appid+secret');
765 $fb_token = get_pconfig($b['uid'],'facebook','access_token');
768 // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
769 // or it's a private message with facebook participants
770 // or it's a reply or likes action to an existing facebook post
772 if($fb_token && ($toplevel || $b['private'] || $reply)) {
773 logger('facebook: able to post');
774 require_once('library/facebook.php');
775 require_once('include/bbcode.php');
779 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
781 // make links readable before we strip the code
783 // unless it's a dislike - just send the text as a comment
785 if($b['verb'] == ACTIVITY_DISLIKE)
786 $msg = trim(strip_tags(bbcode($msg)));
788 /*$search_str = $a->get_baseurl() . '/search';
790 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
792 // don't use hashtags for message link
794 if(strpos($matches[2],$search_str) === false) {
796 if(substr($matches[2],0,5) != '[img]')
797 $linkname = $matches[2];
801 // strip tag links to avoid link clutter, this really should be
802 // configurable because we're losing information
804 $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
806 // provide the link separately for normal links
807 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
809 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
810 $image = $matches[1];
812 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
814 if((strpos($link,z_root()) !== false) && (! $image))
815 $image = $a->get_baseurl() . '/images/friendica-64.jpg';
817 $msg = trim(strip_tags(bbcode($msg)));*/
821 // Looking for images
822 if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
823 $image = $matches[3];
825 if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
826 $image = $matches[1];
828 $html = bbcode($b['body']);
829 $msg = trim($b['title']." \n".html2plain($html, 0, true));
830 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
834 // add any attachments as text urls
836 $arr = explode(',',$b['attach']);
840 foreach($arr as $r) {
842 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
844 $msg .= "\n".$matches[1];
849 // To-Do: look for bookmark-bbcode and handle it with priority
851 $links = collecturls($html);
852 if (sizeof($links) > 0) {
854 $link = current($links);
855 /*if (strlen($msg."\n".$link) <= FACEBOOK_MAXPOSTLEN)
861 if ((strlen($msg) > FACEBOOK_MAXPOSTLEN) or $toolong) {
863 require_once('library/slinky.php');
865 $display_url = $b['plink'];
867 $slinky = new Slinky( $display_url );
868 // setup a cascade of shortening services
869 // try to get a short link from these services
870 // in the order ur1.ca, trim, id.gd, tinyurl
871 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
872 $shortlink = $slinky->short();
873 // the new message will be shortened such that "... $shortlink"
874 // will fit into the character limit
875 $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
876 $msg .= '... ' . $shortlink;
881 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
884 $postvars = array('access_token' => $fb_token);
888 'access_token' => $fb_token,
892 $postvars['picture'] = $image;
894 $postvars['link'] = $link;
896 $postvars['name'] = $linkname;
899 if(($b['private']) && ($toplevel)) {
900 $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
901 if(count($allow_arr))
902 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
904 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
905 $postvars['privacy'] .= '}';
910 $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
913 $url = 'https://graph.facebook.com/me/feed';
915 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
918 logger('facebook: post to ' . $url);
919 logger('facebook: postvars: ' . print_r($postvars,true));
921 // "test_mode" prevents anything from actually being posted.
922 // Otherwise, let's do it.
924 if(! get_config('facebook','test_mode')) {
925 $x = post_url($url, $postvars);
927 $retj = json_decode($x);
929 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
930 dbesc('fb::' . $retj->id),
936 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
937 require_once('include/queue_fn.php');
938 add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
939 notice( t('Facebook post failed. Queued for retry.') . EOL);
943 logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
951 function facebook_post_local(&$a,&$b) {
953 // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
954 // where we will discover it during background delivery.
956 // This can only be triggered by a local user posting to their own wall.
958 if((local_user()) && (local_user() == $b['uid'])) {
960 $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
961 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
963 // if API is used, default to the chosen settings
964 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
970 if(strlen($b['postopts']))
971 $b['postopts'] .= ',';
972 $b['postopts'] .= 'facebook';
977 function fb_queue_hook(&$a,&$b) {
979 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
980 dbesc(NETWORK_FACEBOOK)
985 require_once('include/queue_fn.php');
988 if($x['network'] !== NETWORK_FACEBOOK)
991 logger('facebook_queue: run');
993 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
994 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1002 $appid = get_config('facebook', 'appid' );
1003 $secret = get_config('facebook', 'appsecret' );
1005 if($appid && $secret) {
1006 $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
1007 $fb_token = get_pconfig($user['uid'],'facebook','access_token');
1009 if($fb_post && $fb_token) {
1010 logger('facebook_queue: able to post');
1011 require_once('library/facebook.php');
1013 $z = unserialize($x['content']);
1015 $j = post_url($z['url'],$z['post']);
1017 $retj = json_decode($j);
1019 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
1020 dbesc('fb::' . $retj->id),
1023 logger('facebook_queue: success: ' . $j);
1024 remove_queue_item($x['id']);
1027 logger('facebook_queue: failed: ' . $j);
1028 update_queue_time($x['id']);
1035 function fb_consume_all($uid) {
1037 require_once('include/items.php');
1039 $access_token = get_pconfig($uid,'facebook','access_token');
1043 if(! get_pconfig($uid,'facebook','no_wall')) {
1044 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
1045 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
1047 $j = json_decode($s);
1048 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
1049 fb_consume_stream($uid,$j,($private_wall) ? false : true);
1052 $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
1054 $j = json_decode($s);
1055 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
1056 fb_consume_stream($uid,$j,false);
1061 function fb_get_photo($uid,$link) {
1062 $access_token = get_pconfig($uid,'facebook','access_token');
1063 if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
1064 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1065 $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
1067 $photo_id = $match[1];
1068 $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
1069 $j = json_decode($x);
1071 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
1073 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
1076 function fb_consume_stream($uid,$j,$wall = false) {
1081 $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
1087 $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
1089 $no_linking = get_pconfig($uid,'facebook','no_linking');
1093 $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1097 $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
1098 $blocked_apps_arr = explode(',',$blocked_apps);
1100 $self_id = get_pconfig($uid,'facebook','self_id');
1101 if(! count($j->data) || (! strlen($self_id)))
1104 foreach($j->data as $entry) {
1105 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
1106 $datarray = array();
1108 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
1109 dbesc('fb::' . $entry->id),
1110 dbesc('fb::' . $entry->id),
1114 $post_exists = true;
1116 $top_item = $r[0]['id'];
1119 $post_exists = false;
1124 $datarray['gravity'] = 0;
1125 $datarray['uid'] = $uid;
1126 $datarray['wall'] = (($wall) ? 1 : 0);
1127 $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
1128 $from = $entry->from;
1129 if($from->id == $self_id)
1130 $datarray['contact-id'] = $self[0]['id'];
1132 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1137 $datarray['contact-id'] = $r[0]['id'];
1140 // don't store post if we don't have a contact
1142 if(! x($datarray,'contact-id')) {
1143 logger('no contact: post ignored');
1147 $datarray['verb'] = ACTIVITY_POST;
1149 $datarray['owner-name'] = $self[0]['name'];
1150 $datarray['owner-link'] = $self[0]['url'];
1151 $datarray['owner-avatar'] = $self[0]['thumb'];
1153 if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
1154 $datarray['app'] = strip_tags($entry->application->name);
1156 $datarray['app'] = 'facebook';
1158 $found_blocked = false;
1160 if(count($blocked_apps_arr)) {
1161 foreach($blocked_apps_arr as $bad_appl) {
1162 if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
1163 $found_blocked = true;
1168 if($found_blocked) {
1169 logger('facebook: blocking application: ' . $datarray['app']);
1173 $datarray['author-name'] = $from->name;
1174 $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
1175 $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
1176 $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
1178 $datarray['body'] = escape_tags($entry->message);
1180 if($entry->picture && $entry->link) {
1181 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
1185 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
1186 // if just a link, it may be a wall photo - check
1188 $datarray['body'] .= fb_get_photo($uid,$entry->link);
1191 $datarray['body'] .= "\n" . $entry->name;
1193 $datarray['body'] .= "\n" . $entry->caption;
1194 if($entry->description)
1195 $datarray['body'] .= "\n" . $entry->description;
1196 $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1197 $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1199 // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1200 // as the identities are from a foreign system. Mark it as private to the owner.
1202 if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1203 $datarray['private'] = 1;
1204 $datarray['allow_cid'] = '<' . $uid . '>';
1207 if(trim($datarray['body']) == '') {
1208 logger('facebook: empty body');
1212 $top_item = item_store($datarray);
1213 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1219 logger('fb: new top level item posted');
1223 if(isset($entry->likes) && isset($entry->likes->data))
1224 $likers = $entry->likes->data;
1228 if(isset($entry->comments) && isset($entry->comments->data))
1229 $comments = $entry->comments->data;
1233 if(is_array($likers)) {
1234 foreach($likers as $likes) {
1239 // If we posted the like locally, it will be found with our url, not the FB url.
1241 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
1243 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
1244 AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1245 dbesc($orig_post['uri']),
1247 dbesc(ACTIVITY_LIKE),
1248 dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1255 $likedata = array();
1256 $likedata['parent'] = $top_item;
1257 $likedata['verb'] = ACTIVITY_LIKE;
1258 $likedata['gravity'] = 3;
1259 $likedata['uid'] = $uid;
1260 $likedata['wall'] = (($wall) ? 1 : 0);
1261 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1262 $likedata['parent-uri'] = $orig_post['uri'];
1263 if($likes->id == $self_id)
1264 $likedata['contact-id'] = $self[0]['id'];
1266 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1271 $likedata['contact-id'] = $r[0]['id'];
1273 if(! x($likedata,'contact-id'))
1274 $likedata['contact-id'] = $orig_post['contact-id'];
1276 $likedata['app'] = 'facebook';
1277 $likedata['verb'] = ACTIVITY_LIKE;
1278 $likedata['author-name'] = $likes->name;
1279 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1280 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1282 $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1283 $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1284 $post_type = t('status');
1285 $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1286 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1288 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1289 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
1290 '<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>';
1292 $item = item_store($likedata);
1295 if(is_array($comments)) {
1296 foreach($comments as $cmnt) {
1301 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1303 dbesc('fb::' . $cmnt->id),
1304 dbesc('fb::' . $cmnt->id)
1309 $cmntdata = array();
1310 $cmntdata['parent'] = $top_item;
1311 $cmntdata['verb'] = ACTIVITY_POST;
1312 $cmntdata['gravity'] = 6;
1313 $cmntdata['uid'] = $uid;
1314 $cmntdata['wall'] = (($wall) ? 1 : 0);
1315 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1316 $cmntdata['parent-uri'] = $orig_post['uri'];
1317 if($cmnt->from->id == $self_id) {
1318 $cmntdata['contact-id'] = $self[0]['id'];
1321 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1322 dbesc($cmnt->from->id),
1326 $cmntdata['contact-id'] = $r[0]['id'];
1327 if($r[0]['blocked'] || $r[0]['readonly'])
1331 if(! x($cmntdata,'contact-id'))
1332 $cmntdata['contact-id'] = $orig_post['contact-id'];
1334 $cmntdata['app'] = 'facebook';
1335 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1336 $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1337 $cmntdata['verb'] = ACTIVITY_POST;
1338 $cmntdata['author-name'] = $cmnt->from->name;
1339 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1340 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1341 $cmntdata['body'] = $cmnt->message;
1342 $item = item_store($cmntdata);
1349 function fb_get_app_access_token() {
1351 $acc_token = get_config('facebook','app_access_token');
1353 if ($acc_token !== false) return $acc_token;
1355 $appid = get_config('facebook','appid');
1356 $appsecret = get_config('facebook', 'appsecret');
1358 if ($appid === false || $appsecret === false) {
1359 logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
1363 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . "&grant_type=client_credentials");
1365 if(strpos($x,'access_token=') !== false) {
1366 logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
1368 $token = str_replace('access_token=', '', $x);
1369 if(strpos($token,'&') !== false)
1370 $token = substr($token,0,strpos($token,'&'));
1373 logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
1376 set_config('facebook','app_access_token',$token);
1379 logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
1384 function facebook_subscription_del_users() {
1386 $access_token = fb_get_app_access_token();
1388 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1389 facebook_delete_url($url);
1391 del_config('facebook', 'realtime_active');
1394 function facebook_subscription_add_users() {
1397 $access_token = fb_get_app_access_token();
1399 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1401 list($usec, $sec) = explode(" ", microtime());
1402 $verify_token = sha1($usec . $sec . rand(0, 999999999));
1403 set_config('facebook', 'cb_verify_token', $verify_token);
1405 $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
1407 $j = post_url($url,array(
1409 "fields" => "feed,friends",
1410 "callback_url" => $cb,
1411 "verify_token" => $verify_token,
1413 del_config('facebook', 'cb_verify_token');
1416 logger("Facebook reponse: " . $j, LOGGER_DATA);
1418 if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
1422 function facebook_subscriptions_get() {
1424 $access_token = fb_get_app_access_token();
1425 if (!$access_token) return null;
1427 $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
1428 $j = fetch_url($url);
1431 $x = json_decode($j);
1432 if (isset($x->data)) $ret = $x->data;
1438 function facebook_check_realtime_active() {
1439 $ret = facebook_subscriptions_get();
1440 if (is_null($ret)) return false;
1441 if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
1448 // DELETE-request to $url
1450 if(! function_exists('facebook_delete_url')) {
1451 function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
1453 $ch = curl_init($url);
1454 if(($redirects > 8) || (! $ch))
1457 curl_setopt($ch, CURLOPT_HEADER, true);
1458 curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
1459 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
1460 curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
1462 if(intval($timeout)) {
1463 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1466 $curl_time = intval(get_config('system','curl_timeout'));
1467 curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
1470 if(defined('LIGHTTPD')) {
1471 if(!is_array($headers)) {
1472 $headers = array('Expect:');
1474 if(!in_array('Expect:', $headers)) {
1475 array_push($headers, 'Expect:');
1480 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1482 $check_cert = get_config('system','verifyssl');
1483 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
1484 $prx = get_config('system','proxy');
1486 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
1487 curl_setopt($ch, CURLOPT_PROXY, $prx);
1488 $prxusr = get_config('system','proxyuser');
1490 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
1493 $a->set_curl_code(0);
1495 // don't let curl abort the entire application
1496 // if it throws any errors.
1498 $s = @curl_exec($ch);
1501 $curl_info = curl_getinfo($ch);
1502 $http_code = $curl_info['http_code'];
1506 // Pull out multiple headers, e.g. proxy and continuation headers
1507 // allow for HTTP/2.x without fixing code
1509 while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
1510 $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
1512 $base = substr($base,strlen($chunk));
1515 if($http_code == 301 || $http_code == 302 || $http_code == 303) {
1517 preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
1518 $url = trim(array_pop($matches));
1519 $url_parsed = @parse_url($url);
1520 if (isset($url_parsed)) {
1522 return delete_url($url,$headers,$redirects,$timeout);
1525 $a->set_curl_code($http_code);
1526 $body = substr($s,strlen($header));
1528 $a->set_curl_headers($header);