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