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