]> git.mxchange.org Git - friendica.git/blob - include/socgraph.php
Merge pull request #2138 from fabrixxm/dbstructure_multicolumn_primarykey
[friendica.git] / include / socgraph.php
1 <?php
2 /**
3  * @file include/socgraph.php
4  * 
5  * @todo Move GNU Social URL schemata (http://server.tld/user/number) to http://server.tld/username
6  * @todo Fetch profile data from profile page for Redmatrix users
7  * @todo Detect if it is a forum
8  */
9
10 require_once('include/datetime.php');
11 require_once("include/Scrape.php");
12 require_once("include/html2bbcode.php");
13
14
15 /*
16  * poco_load
17  *
18  * Given a contact-id (minimum), load the PortableContacts friend list for that contact,
19  * and add the entries to the gcontact (Global Contact) table, or update existing entries
20  * if anything (name or photo) has changed.
21  * We use normalised urls for comparison which ignore http vs https and www.domain vs domain
22  *
23  * Once the global contact is stored add (if necessary) the contact linkage which associates
24  * the given uid, cid to the global contact entry. There can be many uid/cid combinations
25  * pointing to the same global contact id.
26  *
27  */
28
29
30
31
32 function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
33
34         $a = get_app();
35
36         if($cid) {
37                 if((! $url) || (! $uid)) {
38                         $r = q("select `poco`, `uid` from `contact` where `id` = %d limit 1",
39                                 intval($cid)
40                         );
41                         if(count($r)) {
42                                 $url = $r[0]['poco'];
43                                 $uid = $r[0]['uid'];
44                         }
45                 }
46                 if(! $uid)
47                         return;
48         }
49
50         if(! $url)
51                 return;
52
53         $url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation') ;
54
55         logger('poco_load: ' . $url, LOGGER_DEBUG);
56
57         $s = fetch_url($url);
58
59         logger('poco_load: returns ' . $s, LOGGER_DATA);
60
61         logger('poco_load: return code: ' . $a->get_curl_code(), LOGGER_DEBUG);
62
63         if(($a->get_curl_code() > 299) || (! $s))
64                 return;
65
66         $j = json_decode($s);
67
68         logger('poco_load: json: ' . print_r($j,true),LOGGER_DATA);
69
70         if(! isset($j->entry))
71                 return;
72
73         $total = 0;
74         foreach($j->entry as $entry) {
75
76                 $total ++;
77                 $profile_url = '';
78                 $profile_photo = '';
79                 $connect_url = '';
80                 $name = '';
81                 $network = '';
82                 $updated = '0000-00-00 00:00:00';
83                 $location = '';
84                 $about = '';
85                 $keywords = '';
86                 $gender = '';
87                 $generation = 0;
88
89                 $name = $entry->displayName;
90
91                 if(isset($entry->urls)) {
92                         foreach($entry->urls as $url) {
93                                 if($url->type == 'profile') {
94                                         $profile_url = $url->value;
95                                         continue;
96                                 }
97                                 if($url->type == 'webfinger') {
98                                         $connect_url = str_replace('acct:' , '', $url->value);
99                                         continue;
100                                 }
101                         }
102                 }
103                 if(isset($entry->photos)) {
104                         foreach($entry->photos as $photo) {
105                                 if($photo->type == 'profile') {
106                                         $profile_photo = $photo->value;
107                                         continue;
108                                 }
109                         }
110                 }
111
112                 if(isset($entry->updated))
113                         $updated = date("Y-m-d H:i:s", strtotime($entry->updated));
114
115                 if(isset($entry->network))
116                         $network = $entry->network;
117
118                 if(isset($entry->currentLocation))
119                         $location = $entry->currentLocation;
120
121                 if(isset($entry->aboutMe))
122                         $about = html2bbcode($entry->aboutMe);
123
124                 if(isset($entry->gender))
125                         $gender = $entry->gender;
126
127                 if(isset($entry->generation) AND ($entry->generation > 0))
128                         $generation = ++$entry->generation;
129
130                 if(isset($entry->tags))
131                         foreach($entry->tags as $tag)
132                                 $keywords = implode(", ", $tag);
133
134                 // If you query a Friendica server for its profiles, the network has to be Friendica
135                 /// TODO It could also be a Redmatrix server
136                 //if ($uid == 0)
137                 //      $network = NETWORK_DFRN;
138
139                 poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
140
141                 // Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
142                 if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
143                         q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
144                                 WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
145                                 dbesc($location),
146                                 dbesc($about),
147                                 dbesc($keywords),
148                                 dbesc($gender),
149                                 dbesc(normalise_link($profile_url)),
150                                 dbesc(NETWORK_DFRN));
151         }
152         logger("poco_load: loaded $total entries",LOGGER_DEBUG);
153
154         q("DELETE FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `zcid` = %d AND `updated` < UTC_TIMESTAMP - INTERVAL 2 DAY",
155                 intval($cid),
156                 intval($uid),
157                 intval($zcid)
158         );
159
160 }
161
162 function poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid = 0, $uid = 0, $zcid = 0) {
163
164         $a = get_app();
165
166         // Generation:
167         //  0: No definition
168         //  1: Profiles on this server
169         //  2: Contacts of profiles on this server
170         //  3: Contacts of contacts of profiles on this server
171         //  4: ...
172
173         $gcid = "";
174
175         $alternate = poco_alternate_ostatus_url($profile_url);
176
177         if ($profile_url == "")
178                 return $gcid;
179
180         $urlparts = parse_url($profile_url);
181         if (!isset($urlparts["scheme"]))
182                 return $gcid;
183
184         if (in_array($urlparts["host"], array("www.facebook.com", "facebook.com", "twitter.com",
185                                                 "identi.ca", "alpha.app.net")))
186                 return $gcid;
187
188         $orig_updated = $updated;
189
190         // Don't store the statusnet connector as network
191         // We can't simply set this to NETWORK_OSTATUS since the connector could have fetched posts from friendica as well
192         if ($network == NETWORK_STATUSNET)
193                 $network = "";
194
195         // The global contacts should contain the original picture, not the cached one
196         if (($generation != 1) AND stristr(normalise_link($profile_photo), normalise_link($a->get_baseurl()."/photo/")))
197                 $profile_photo = "";
198
199         $r = q("SELECT `network` FROM `contact` WHERE `nurl` = '%s' AND `network` != '' AND `network` != '%s' LIMIT 1",
200                 dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
201         );
202         if(count($r))
203                 $network = $r[0]["network"];
204
205         if (($network == "") OR ($network == NETWORK_OSTATUS)) {
206                 $r = q("SELECT `network`, `url` FROM `contact` WHERE `alias` IN ('%s', '%s') AND `network` != '' AND `network` != '%s' LIMIT 1",
207                         dbesc($profile_url), dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
208                 );
209                 if(count($r)) {
210                         $network = $r[0]["network"];
211                         //$profile_url = $r[0]["url"];
212                 }
213         }
214
215         $x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
216                 dbesc(normalise_link($profile_url))
217         );
218
219         if (count($x)) {
220                 if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET))
221                         $network = $x[0]["network"];
222
223                 if ($updated == "0000-00-00 00:00:00")
224                         $updated = $x[0]["updated"];
225
226                 $created = $x[0]["created"];
227                 $server_url = $x[0]["server_url"];
228                 $nick = $x[0]["nick"];
229                 $addr = $x[0]["addr"];
230         } else {
231                 $created = "0000-00-00 00:00:00";
232                 $server_url = "";
233
234                 $urlparts = parse_url($profile_url);
235                 $nick = end(explode("/", $urlparts["path"]));
236                 $addr = "";
237         }
238
239         if ((($network == "") OR ($name == "") OR ($addr == "") OR ($profile_photo == "") OR ($server_url == "") OR $alternate)
240                 AND poco_reachable($profile_url, $server_url, $network, false)) {
241                 $data = probe_url($profile_url);
242
243                 $orig_profile = $profile_url;
244
245                 $network = $data["network"];
246                 $name = $data["name"];
247                 $nick = $data["nick"];
248                 $addr = $data["addr"];
249                 $profile_url = $data["url"];
250                 $profile_photo = $data["photo"];
251                 $server_url = $data["baseurl"];
252
253                 if ($alternate AND ($network == NETWORK_OSTATUS)) {
254                         // Delete the old entry - if it exists
255                         $r = q("SELECT `id` FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
256                         if ($r) {
257                                 q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
258                                 q("DELETE FROM `glink` WHERE `gcid` = %d", intval($r[0]["id"]));
259                         }
260
261                         // possibly create a new entry
262                         poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
263                 }
264         }
265
266         if ($alternate AND ($network == NETWORK_OSTATUS))
267                 return $gcid;
268
269         if (count($x) AND ($x[0]["network"] == "") AND ($network != "")) {
270                 q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
271                         dbesc($network),
272                         dbesc(normalise_link($profile_url))
273                 );
274         }
275
276         if (($name == "") OR ($profile_photo == ""))
277                 return $gcid;
278
279         if (!in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
280                 return $gcid;
281
282         logger("profile-check generation: ".$generation." Network: ".$network." URL: ".$profile_url." name: ".$name." avatar: ".$profile_photo, LOGGER_DEBUG);
283
284         poco_check_server($server_url, $network);
285
286         if(count($x)) {
287                 $gcid = $x[0]['id'];
288
289                 if (($location == "") AND ($x[0]['location'] != ""))
290                         $location = $x[0]['location'];
291
292                 if (($about == "") AND ($x[0]['about'] != ""))
293                         $about = $x[0]['about'];
294
295                 if (($gender == "") AND ($x[0]['gender'] != ""))
296                         $gender = $x[0]['gender'];
297
298                 if (($keywords == "") AND ($x[0]['keywords'] != ""))
299                         $keywords = $x[0]['keywords'];
300
301                 if (($addr == "") AND ($x[0]['addr'] != ""))
302                         $addr = $x[0]['addr'];
303
304                 if (($generation == 0) AND ($x[0]['generation'] > 0))
305                         $generation = $x[0]['generation'];
306
307                 if($x[0]['name'] != $name || $x[0]['photo'] != $profile_photo || $x[0]['updated'] < $updated) {
308                         q("UPDATE `gcontact` SET `name` = '%s', `addr` = '%s', `network` = '%s', `photo` = '%s', `connect` = '%s', `url` = '%s', `server_url` = '%s',
309                                 `updated` = '%s', `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s', `generation` = %d
310                                 WHERE (`generation` >= %d OR `generation` = 0) AND `nurl` = '%s'",
311                                 dbesc($name),
312                                 dbesc($addr),
313                                 dbesc($network),
314                                 dbesc($profile_photo),
315                                 dbesc($connect_url),
316                                 dbesc($profile_url),
317                                 dbesc($server_url),
318                                 dbesc($updated),
319                                 dbesc($location),
320                                 dbesc($about),
321                                 dbesc($keywords),
322                                 dbesc($gender),
323                                 intval($generation),
324                                 intval($generation),
325                                 dbesc(normalise_link($profile_url))
326                         );
327                 }
328         } else {
329                 q("INSERT INTO `gcontact` (`name`, `nick`, `addr`, `network`, `url`, `nurl`, `photo`, `connect`, `server_url`, `created`, `updated`, `location`, `about`, `keywords`, `gender`, `generation`)
330                         VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)",
331                         dbesc($name),
332                         dbesc($nick),
333                         dbesc($addr),
334                         dbesc($network),
335                         dbesc($profile_url),
336                         dbesc(normalise_link($profile_url)),
337                         dbesc($profile_photo),
338                         dbesc($connect_url),
339                         dbesc($server_url),
340                         dbesc(datetime_convert()),
341                         dbesc($updated),
342                         dbesc($location),
343                         dbesc($about),
344                         dbesc($keywords),
345                         dbesc($gender),
346                         intval($generation)
347                 );
348                 $x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
349                         dbesc(normalise_link($profile_url))
350                 );
351                 if(count($x))
352                         $gcid = $x[0]['id'];
353         }
354
355         if(! $gcid)
356                 return $gcid;
357
358         $r = q("SELECT * FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d LIMIT 1",
359                 intval($cid),
360                 intval($uid),
361                 intval($gcid),
362                 intval($zcid)
363         );
364         if(! count($r)) {
365                 q("INSERT INTO `glink` (`cid`,`uid`,`gcid`,`zcid`, `updated`) VALUES (%d,%d,%d,%d, '%s') ",
366                         intval($cid),
367                         intval($uid),
368                         intval($gcid),
369                         intval($zcid),
370                         dbesc(datetime_convert())
371                 );
372         } else {
373                 q("UPDATE `glink` SET `updated` = '%s' WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d",
374                         dbesc(datetime_convert()),
375                         intval($cid),
376                         intval($uid),
377                         intval($gcid),
378                         intval($zcid)
379                 );
380         }
381
382         // For unknown reasons there are sometimes duplicates
383         q("DELETE FROM `gcontact` WHERE `nurl` = '%s' AND `id` != %d AND
384                 NOT EXISTS (SELECT `gcid` FROM `glink` WHERE `gcid` = `gcontact`.`id`)",
385                 dbesc(normalise_link($profile_url)),
386                 intval($gcid)
387         );
388
389         return $gcid;
390 }
391
392 function poco_reachable($profile, $server = "", $network = "", $force = false) {
393
394         if ($server == "")
395                 $server = poco_detect_server($profile);
396
397         if ($server == "")
398                 return true;
399
400         return poco_check_server($server, $network, $force);
401 }
402
403 function poco_detect_server($profile) {
404
405         // Try to detect the server path based upon some known standard paths
406         $server_url = "";
407
408         if ($server_url == "") {
409                 $friendica = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $profile);
410                 if ($friendica != $profile) {
411                         $server_url = $friendica;
412                         $network = NETWORK_DFRN;
413                 }
414         }
415
416         if ($server_url == "") {
417                 $diaspora = preg_replace("=(https?://)(.*)/u/(.*)=ism", "$1$2", $profile);
418                 if ($diaspora != $profile) {
419                         $server_url = $diaspora;
420                         $network = NETWORK_DIASPORA;
421                 }
422         }
423
424         if ($server_url == "") {
425                 $red = preg_replace("=(https?://)(.*)/channel/(.*)=ism", "$1$2", $profile);
426                 if ($red != $profile) {
427                         $server_url = $red;
428                         $network = NETWORK_DIASPORA;
429                 }
430         }
431
432         return $server_url;
433 }
434
435 function poco_alternate_ostatus_url($url) {
436         return(preg_match("=https?://.+/user/\d+=ism", $url, $matches));
437 }
438
439 function poco_last_updated($profile, $force = false) {
440
441         $gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
442                         dbesc(normalise_link($profile)));
443
444         if ($gcontacts[0]["created"] == "0000-00-00 00:00:00")
445                 q("UPDATE `gcontact` SET `created` = '%s' WHERE `nurl` = '%s'",
446                         dbesc(datetime_convert()), dbesc(normalise_link($profile)));
447
448         if ($gcontacts[0]["server_url"] != "")
449                 $server_url = $gcontacts[0]["server_url"];
450         else
451                 $server_url = poco_detect_server($profile);
452
453         if ($server_url != "") {
454                 if (!poco_check_server($server_url, $gcontacts[0]["network"], $force)) {
455
456                         if ($force)
457                                 q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
458                                         dbesc(datetime_convert()), dbesc(normalise_link($profile)));
459
460                         return false;
461                 }
462
463                 q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
464                         dbesc($server_url), dbesc(normalise_link($profile)));
465         }
466
467         if (in_array($gcontacts[0]["network"], array("", NETWORK_FEED))) {
468                 $server = q("SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''",
469                         dbesc(normalise_link($server_url)));
470
471                 if ($server)
472                         q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
473                                 dbesc($server[0]["network"]), dbesc(normalise_link($profile)));
474                 else
475                         return;
476         }
477
478         // noscrape is really fast so we don't cache the call.
479         if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) {
480
481                 //  Use noscrape if possible
482                 $server = q("SELECT `noscrape` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
483
484                 if ($server) {
485                         $noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
486
487                          if ($noscraperet["success"] AND ($noscraperet["body"] != "")) {
488
489                                 $noscrape = json_decode($noscraperet["body"], true);
490
491                                 if (($noscrape["fn"] != "") AND ($noscrape["fn"] != $gcontacts[0]["name"]))
492                                         q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
493                                                 dbesc($noscrape["fn"]), dbesc(normalise_link($profile)));
494
495                                 if (($noscrape["photo"] != "") AND ($noscrape["photo"] != $gcontacts[0]["photo"]))
496                                         q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
497                                                 dbesc($noscrape["photo"]), dbesc(normalise_link($profile)));
498
499                                 if (($noscrape["updated"] != "") AND ($noscrape["updated"] != $gcontacts[0]["updated"]))
500                                         q("UPDATE `gcontact` SET `updated` = '%s' WHERE `nurl` = '%s'",
501                                                 dbesc($noscrape["updated"]), dbesc(normalise_link($profile)));
502
503                                 if (($noscrape["gender"] != "") AND ($noscrape["gender"] != $gcontacts[0]["gender"]))
504                                         q("UPDATE `gcontact` SET `gender` = '%s' WHERE `nurl` = '%s'",
505                                                 dbesc($noscrape["gender"]), dbesc(normalise_link($profile)));
506
507                                 if (($noscrape["pdesc"] != "") AND ($noscrape["pdesc"] != $gcontacts[0]["about"]))
508                                         q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
509                                                 dbesc($noscrape["pdesc"]), dbesc(normalise_link($profile)));
510
511                                 if (($noscrape["about"] != "") AND ($noscrape["about"] != $gcontacts[0]["about"]))
512                                         q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
513                                                 dbesc($noscrape["about"]), dbesc(normalise_link($profile)));
514
515                                 if (isset($noscrape["comm"]) AND ($noscrape["comm"] != $gcontacts[0]["community"]))
516                                         q("UPDATE `gcontact` SET `community` = %d WHERE `nurl` = '%s'",
517                                                 intval($noscrape["comm"]), dbesc(normalise_link($profile)));
518
519                                 if (isset($noscrape["tags"]))
520                                         $keywords = implode(" ", $noscrape["tags"]);
521                                 else
522                                         $keywords = "";
523
524                                 if (($keywords != "") AND ($keywords != $gcontacts[0]["keywords"]))
525                                         q("UPDATE `gcontact` SET `keywords` = '%s' WHERE `nurl` = '%s'",
526                                                 dbesc($keywords), dbesc(normalise_link($profile)));
527
528                                 $location = $noscrape["locality"];
529
530                                 if ($noscrape["region"] != "") {
531                                         if ($location != "")
532                                                 $location .= ", ";
533
534                                         $location .= $noscrape["region"];
535                                 }
536
537                                 if ($noscrape["country-name"] != "") {
538                                         if ($location != "")
539                                                 $location .= ", ";
540
541                                         $location .= $noscrape["country-name"];
542                                 }
543
544                                 if (($location != "") AND ($location != $gcontacts[0]["location"]))
545                                         q("UPDATE `gcontact` SET `location` = '%s' WHERE `nurl` = '%s'",
546                                                 dbesc($location), dbesc(normalise_link($profile)));
547
548                                 // If we got data from noscrape then mark the contact as reachable
549                                 if (is_array($noscrape) AND count($noscrape))
550                                         q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'",
551                                                 dbesc(datetime_convert()), dbesc(normalise_link($profile)));
552
553                                 return $noscrape["updated"];
554                         }
555                 }
556         }
557
558         // If we only can poll the feed, then we only do this once a while
559         if (!$force AND !poco_do_update($gcontacts[0]["created"], $gcontacts[0]["updated"], $gcontacts[0]["last_failure"],  $gcontacts[0]["last_contact"]))
560                 return $gcontacts[0]["updated"];
561
562         $data = probe_url($profile);
563
564         // Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711)
565         // Then check the other link and delete this one
566         if (($data["network"] == NETWORK_OSTATUS) AND poco_alternate_ostatus_url($profile) AND
567                 (normalise_link($profile) == normalise_link($data["alias"])) AND
568                 (normalise_link($profile) != normalise_link($data["url"]))) {
569
570                 // Delete the old entry
571                 q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($profile)));
572                 q("DELETE FROM `glink` WHERE `gcid` = %d", intval($gcontacts[0]["id"]));
573
574                 poco_check($data["url"], $data["name"], $data["network"], $data["photo"], $gcontacts[0]["about"], $gcontacts[0]["location"],
575                                 $gcontacts[0]["gender"], $gcontacts[0]["keywords"], $data["addr"], $gcontacts[0]["updated"], $gcontacts[0]["generation"]);
576
577                 poco_last_updated($data["url"], $force);
578
579                 return false;
580         }
581
582         if (($data["poll"] == "") OR (in_array($data["network"], array(NETWORK_FEED, NETWORK_PHANTOM)))) {
583                 q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
584                         dbesc(datetime_convert()), dbesc(normalise_link($profile)));
585                 return false;
586         }
587
588         if (($data["name"] != "") AND ($data["name"] != $gcontacts[0]["name"]))
589                 q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
590                         dbesc($data["name"]), dbesc(normalise_link($profile)));
591
592         if (($data["nick"] != "") AND ($data["nick"] != $gcontacts[0]["nick"]))
593                 q("UPDATE `gcontact` SET `nick` = '%s' WHERE `nurl` = '%s'",
594                         dbesc($data["nick"]), dbesc(normalise_link($profile)));
595
596         if (($data["addr"] != "") AND ($data["addr"] != $gcontacts[0]["connect"]))
597                 q("UPDATE `gcontact` SET `connect` = '%s' WHERE `nurl` = '%s'",
598                         dbesc($data["addr"]), dbesc(normalise_link($profile)));
599
600         if (($data["photo"] != "") AND ($data["photo"] != $gcontacts[0]["photo"]))
601                 q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
602                         dbesc($data["photo"]), dbesc(normalise_link($profile)));
603
604         if (($data["baseurl"] != "") AND ($data["baseurl"] != $gcontacts[0]["server_url"]))
605                 q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
606                         dbesc($data["baseurl"]), dbesc(normalise_link($profile)));
607
608         $feedret = z_fetch_url($data["poll"]);
609
610         if (!$feedret["success"]) {
611                 q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
612                         dbesc(datetime_convert()), dbesc(normalise_link($profile)));
613                 return false;
614         }
615
616         $doc = new DOMDocument();
617         @$doc->loadXML($feedret["body"]);
618
619         $xpath = new DomXPath($doc);
620         $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom");
621
622         $entries = $xpath->query('/atom:feed/atom:entry');
623
624         $last_updated = "";
625
626         foreach ($entries AS $entry) {
627                 $published = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
628                 $updated = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
629
630                 if ($last_updated < $published)
631                         $last_updated = $published;
632
633                 if ($last_updated < $updated)
634                         $last_updated = $updated;
635         }
636
637         // Maybe there aren't any entries. Then check if it is a valid feed
638         if ($last_updated == "")
639                 if ($xpath->query('/atom:feed')->length > 0)
640                         $last_updated = "0000-00-00 00:00:00";
641
642         q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'",
643                 dbesc($last_updated), dbesc(datetime_convert()), dbesc(normalise_link($profile)));
644
645         if (($gcontacts[0]["generation"] == 0))
646                 q("UPDATE `gcontact` SET `generation` = 9 WHERE `nurl` = '%s'",
647                         dbesc(normalise_link($profile)));
648
649         return($last_updated);
650 }
651
652 function poco_do_update($created, $updated, $last_failure,  $last_contact) {
653         $now = strtotime(datetime_convert());
654
655         if ($updated > $last_contact)
656                 $contact_time = strtotime($updated);
657         else
658                 $contact_time = strtotime($last_contact);
659
660         $failure_time = strtotime($last_failure);
661         $created_time = strtotime($created);
662
663         // If there is no "created" time then use the current time
664         if ($created_time <= 0)
665                 $created_time = $now;
666
667         // If the last contact was less than 24 hours then don't update
668         if (($now - $contact_time) < (60 * 60 * 24))
669                 return false;
670
671         // If the last failure was less than 24 hours then don't update
672         if (($now - $failure_time) < (60 * 60 * 24))
673                 return false;
674
675         // If the last contact was less than a week ago and the last failure is older than a week then don't update
676         //if ((($now - $contact_time) < (60 * 60 * 24 * 7)) AND ($contact_time > $failure_time))
677         //      return false;
678
679         // If the last contact time was more than a week ago and the contact was created more than a week ago, then only try once a week
680         if ((($now - $contact_time) > (60 * 60 * 24 * 7)) AND (($now - $created_time) > (60 * 60 * 24 * 7)) AND (($now - $failure_time) < (60 * 60 * 24 * 7)))
681                 return false;
682
683         // If the last contact time was more than a month ago and the contact was created more than a month ago, then only try once a month
684         if ((($now - $contact_time) > (60 * 60 * 24 * 30)) AND (($now - $created_time) > (60 * 60 * 24 * 30)) AND (($now - $failure_time) < (60 * 60 * 24 * 30)))
685                 return false;
686
687         return true;
688 }
689
690 function poco_to_boolean($val) {
691         if (($val == "true") OR ($val == 1))
692                 return(true);
693         if (($val == "false") OR ($val == 0))
694                 return(false);
695
696         return ($val);
697 }
698
699 function poco_check_server($server_url, $network = "", $force = false) {
700
701         if ($server_url == "")
702                 return false;
703
704         $servers = q("SELECT * FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
705         if ($servers) {
706
707                 if ($servers[0]["created"] == "0000-00-00 00:00:00")
708                         q("UPDATE `gserver` SET `created` = '%s' WHERE `nurl` = '%s'",
709                                 dbesc(datetime_convert()), dbesc(normalise_link($server_url)));
710
711                 $poco = $servers[0]["poco"];
712                 $noscrape = $servers[0]["noscrape"];
713
714                 if ($network == "")
715                         $network = $servers[0]["network"];
716
717                 $last_contact = $servers[0]["last_contact"];
718                 $last_failure = $servers[0]["last_failure"];
719                 $version = $servers[0]["version"];
720                 $platform = $servers[0]["platform"];
721                 $site_name = $servers[0]["site_name"];
722                 $info = $servers[0]["info"];
723                 $register_policy = $servers[0]["register_policy"];
724
725                 if (!$force AND !poco_do_update($servers[0]["created"], "", $last_failure, $last_contact)) {
726                         logger("Use cached data for server ".$server_url, LOGGER_DEBUG);
727                         return ($last_contact >= $last_failure);
728                 }
729         } else {
730                 $poco = "";
731                 $noscrape = "";
732                 $version = "";
733                 $platform = "";
734                 $site_name = "";
735                 $info = "";
736                 $register_policy = -1;
737
738                 $last_contact = "0000-00-00 00:00:00";
739                 $last_failure = "0000-00-00 00:00:00";
740         }
741         logger("Server ".$server_url." is outdated or unknown. Start discovery. Force: ".$force." Created: ".$servers[0]["created"]." Failure: ".$last_failure." Contact: ".$last_contact, LOGGER_DEBUG);
742
743         $failure = false;
744         $orig_last_failure = $last_failure;
745
746         // Check if the page is accessible via SSL.
747         $server_url = str_replace("http://", "https://", $server_url);
748         $serverret = z_fetch_url($server_url."/.well-known/host-meta");
749
750         // Maybe the page is unencrypted only?
751         $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
752         if (!$serverret["success"] OR ($serverret["body"] == "") OR (@sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
753                 $server_url = str_replace("https://", "http://", $server_url);
754                 $serverret = z_fetch_url($server_url."/.well-known/host-meta");
755
756                 $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
757         }
758
759         if (!$serverret["success"] OR ($serverret["body"] == "") OR (sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
760                 // Workaround for bad configured servers (known nginx problem)
761                 if ($serverret["debug"]["http_code"] != "403") {
762                         $last_failure = datetime_convert();
763                         $failure = true;
764                 }
765         } elseif ($network == NETWORK_DIASPORA)
766                 $last_contact = datetime_convert();
767
768         if (!$failure) {
769                 // Test for Diaspora
770                 $serverret = z_fetch_url($server_url);
771
772                 $lines = explode("\n",$serverret["header"]);
773                 if(count($lines))
774                         foreach($lines as $line) {
775                                 $line = trim($line);
776                                 if(stristr($line,'X-Diaspora-Version:')) {
777                                         $platform = "Diaspora";
778                                         $version = trim(str_replace("X-Diaspora-Version:", "", $line));
779                                         $version = trim(str_replace("x-diaspora-version:", "", $version));
780                                         $network = NETWORK_DIASPORA;
781                                 }
782                         }
783         }
784
785         if (!$failure) {
786                 // Test for Statusnet
787                 // Will also return data for Friendica and GNU Social - but it will be overwritten later
788                 // The "not implemented" is a special treatment for really, really old Friendica versions
789                 $serverret = z_fetch_url($server_url."/api/statusnet/version.json");
790                 if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
791                         $platform = "StatusNet";
792                         $version = trim($serverret["body"], '"');
793                         $network = NETWORK_OSTATUS;
794                 }
795
796                 // Test for GNU Social
797                 $serverret = z_fetch_url($server_url."/api/gnusocial/version.json");
798                 if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
799                         $platform = "GNU Social";
800                         $version = trim($serverret["body"], '"');
801                         $network = NETWORK_OSTATUS;
802                 }
803
804                 $serverret = z_fetch_url($server_url."/api/statusnet/config.json");
805                 if ($serverret["success"]) {
806                         $data = json_decode($serverret["body"]);
807
808                         if (isset($data->site->server)) {
809                                 $last_contact = datetime_convert();
810
811                                 if (isset($data->site->hubzilla)) {
812                                         $platform = $data->site->hubzilla->PLATFORM_NAME;
813                                         $version = $data->site->hubzilla->RED_VERSION;
814                                         $network = NETWORK_DIASPORA;
815                                 }
816                                 if (isset($data->site->redmatrix)) {
817                                         if (isset($data->site->redmatrix->PLATFORM_NAME))
818                                                 $platform = $data->site->redmatrix->PLATFORM_NAME;
819                                         elseif (isset($data->site->redmatrix->RED_PLATFORM))
820                                                 $platform = $data->site->redmatrix->RED_PLATFORM;
821
822                                         $version = $data->site->redmatrix->RED_VERSION;
823                                         $network = NETWORK_DIASPORA;
824                                 }
825                                 if (isset($data->site->friendica)) {
826                                         $platform = $data->site->friendica->FRIENDICA_PLATFORM;
827                                         $version = $data->site->friendica->FRIENDICA_VERSION;
828                                         $network = NETWORK_DFRN;
829                                 }
830
831                                 $site_name = $data->site->name;
832
833                                 $data->site->closed = poco_to_boolean($data->site->closed);
834                                 $data->site->private = poco_to_boolean($data->site->private);
835                                 $data->site->inviteonly = poco_to_boolean($data->site->inviteonly);
836
837                                 if (!$data->site->closed AND !$data->site->private and $data->site->inviteonly)
838                                         $register_policy = REGISTER_APPROVE;
839                                 elseif (!$data->site->closed AND !$data->site->private)
840                                         $register_policy = REGISTER_OPEN;
841                                 else
842                                         $register_policy = REGISTER_CLOSED;
843                         }
844                 }
845         }
846
847         // Query statistics.json. Optional package for Diaspora, Friendica and Redmatrix
848         if (!$failure) {
849                 $serverret = z_fetch_url($server_url."/statistics.json");
850                 if ($serverret["success"]) {
851                         $data = json_decode($serverret["body"]);
852                         if ($version == "")
853                                 $version = $data->version;
854
855                         $site_name = $data->name;
856
857                         if (isset($data->network) AND ($platform == ""))
858                                 $platform = $data->network;
859
860                         if ($platform == "Diaspora")
861                                 $network = NETWORK_DIASPORA;
862
863                         if ($data->registrations_open)
864                                 $register_policy = REGISTER_OPEN;
865                         else
866                                 $register_policy = REGISTER_CLOSED;
867
868                         if (isset($data->version))
869                                 $last_contact = datetime_convert();
870                 }
871         }
872
873         // Check for noscrape
874         // Friendica servers could be detected as OStatus servers
875         if (!$failure AND in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS))) {
876                 $serverret = z_fetch_url($server_url."/friendica/json");
877
878                 if (!$serverret["success"])
879                         $serverret = z_fetch_url($server_url."/friendika/json");
880
881                 if ($serverret["success"]) {
882                         $data = json_decode($serverret["body"]);
883
884                         if (isset($data->version)) {
885                                 $last_contact = datetime_convert();
886                                 $network = NETWORK_DFRN;
887
888                                 $noscrape = $data->no_scrape_url;
889                                 $version = $data->version;
890                                 $site_name = $data->site_name;
891                                 $info = $data->info;
892                                 $register_policy_str = $data->register_policy;
893                                 $platform = $data->platform;
894
895                                 switch ($register_policy_str) {
896                                         case "REGISTER_CLOSED":
897                                                 $register_policy = REGISTER_CLOSED;
898                                                 break;
899                                         case "REGISTER_APPROVE":
900                                                 $register_policy = REGISTER_APPROVE;
901                                                 break;
902                                         case "REGISTER_OPEN":
903                                                 $register_policy = REGISTER_OPEN;
904                                                 break;
905                                 }
906                         }
907                 }
908         }
909
910         // Look for poco
911         if (!$failure) {
912                 $serverret = z_fetch_url($server_url."/poco");
913                 if ($serverret["success"]) {
914                         $data = json_decode($serverret["body"]);
915                         if (isset($data->totalResults)) {
916                                 $poco = $server_url."/poco";
917                                 $last_contact = datetime_convert();
918                         }
919                 }
920         }
921
922         // Check again if the server exists
923         $servers = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
924
925         if ($servers)
926                  q("UPDATE `gserver` SET `url` = '%s', `version` = '%s', `site_name` = '%s', `info` = '%s', `register_policy` = %d, `poco` = '%s', `noscrape` = '%s',
927                         `network` = '%s', `platform` = '%s', `last_contact` = '%s', `last_failure` = '%s' WHERE `nurl` = '%s'",
928                         dbesc($server_url),
929                         dbesc($version),
930                         dbesc($site_name),
931                         dbesc($info),
932                         intval($register_policy),
933                         dbesc($poco),
934                         dbesc($noscrape),
935                         dbesc($network),
936                         dbesc($platform),
937                         dbesc($last_contact),
938                         dbesc($last_failure),
939                         dbesc(normalise_link($server_url))
940                 );
941         else
942                 q("INSERT INTO `gserver` (`url`, `nurl`, `version`, `site_name`, `info`, `register_policy`, `poco`, `noscrape`, `network`, `platform`, `created`, `last_contact`, `last_failure`)
943                                         VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
944                                 dbesc($server_url),
945                                 dbesc(normalise_link($server_url)),
946                                 dbesc($version),
947                                 dbesc($site_name),
948                                 dbesc($info),
949                                 intval($register_policy),
950                                 dbesc($poco),
951                                 dbesc($noscrape),
952                                 dbesc($network),
953                                 dbesc($platform),
954                                 dbesc(datetime_convert()),
955                                 dbesc($last_contact),
956                                 dbesc($last_failure),
957                                 dbesc(datetime_convert())
958                 );
959
960         logger("End discovery for server ".$server_url, LOGGER_DEBUG);
961
962         return !$failure;
963 }
964
965 function poco_contact_from_body($body, $created, $cid, $uid) {
966         preg_replace_callback("/\[share(.*?)\].*?\[\/share\]/ism",
967                 function ($match) use ($created, $cid, $uid){
968                         return(sub_poco_from_share($match, $created, $cid, $uid));
969                 }, $body);
970 }
971
972 function sub_poco_from_share($share, $created, $cid, $uid) {
973         $profile = "";
974         preg_match("/profile='(.*?)'/ism", $share[1], $matches);
975         if ($matches[1] != "")
976                 $profile = $matches[1];
977
978         preg_match('/profile="(.*?)"/ism', $share[1], $matches);
979         if ($matches[1] != "")
980                 $profile = $matches[1];
981
982         if ($profile == "")
983                 return;
984
985         logger("prepare poco_check for profile ".$profile, LOGGER_DEBUG);
986         poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid);
987 }
988
989 function poco_store($item) {
990
991         // Isn't it public?
992         if ($item['private'])
993                 return;
994
995         // Or is it from a network where we don't store the global contacts?
996         if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_STATUSNET, "")))
997                 return;
998
999         // Is it a global copy?
1000         $store_gcontact = ($item["uid"] == 0);
1001
1002         // Is it a comment on a global copy?
1003         if (!$store_gcontact AND ($item["uri"] != $item["parent-uri"])) {
1004                 $q = q("SELECT `id` FROM `item` WHERE `uri`='%s' AND `uid` = 0", $item["parent-uri"]);
1005                 $store_gcontact = count($q);
1006         }
1007
1008         if (!$store_gcontact)
1009                 return;
1010
1011         // "3" means: We don't know this contact directly (Maybe a reshared item)
1012         $generation = 3;
1013         $network = "";
1014         $profile_url = $item["author-link"];
1015
1016         // Is it a user from our server?
1017         $q = q("SELECT `id` FROM `contact` WHERE `self` AND `nurl` = '%s' LIMIT 1",
1018                 dbesc(normalise_link($item["author-link"])));
1019         if (count($q)) {
1020                 logger("Our user (generation 1): ".$item["author-link"], LOGGER_DEBUG);
1021                 $generation = 1;
1022                 $network = NETWORK_DFRN;
1023         } else { // Is it a contact from a user on our server?
1024                 $q = q("SELECT `network`, `url` FROM `contact` WHERE `uid` != 0 AND `network` != ''
1025                         AND (`nurl` = '%s' OR `alias` IN ('%s', '%s')) AND `network` != '%s' LIMIT 1",
1026                         dbesc(normalise_link($item["author-link"])),
1027                         dbesc(normalise_link($item["author-link"])),
1028                         dbesc($item["author-link"]),
1029                         dbesc(NETWORK_STATUSNET));
1030                 if (count($q)) {
1031                         $generation = 2;
1032                         $network = $q[0]["network"];
1033                         $profile_url = $q[0]["url"];
1034                         logger("Known contact (generation 2): ".$profile_url, LOGGER_DEBUG);
1035                 }
1036         }
1037
1038         if ($generation == 3)
1039                 logger("Unknown contact (generation 3): ".$item["author-link"], LOGGER_DEBUG);
1040
1041         poco_check($profile_url, $item["author-name"], $network, $item["author-avatar"], "", "", "", "", "", $item["received"], $generation, $item["contact-id"], $item["uid"]);
1042
1043         // Maybe its a body with a shared item? Then extract a global contact from it.
1044         poco_contact_from_body($item["body"], $item["received"], $item["contact-id"], $item["uid"]);
1045 }
1046
1047 function count_common_friends($uid,$cid) {
1048
1049         $r = q("SELECT count(*) as `total`
1050                 FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
1051                 WHERE `glink`.`cid` = %d AND `glink`.`uid` = %d AND
1052                 ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`))
1053                 AND `gcontact`.`nurl` IN (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 and id != %d ) ",
1054                 intval($cid),
1055                 intval($uid),
1056                 intval($uid),
1057                 intval($cid)
1058         );
1059
1060 //      logger("count_common_friends: $uid $cid {$r[0]['total']}");
1061         if(count($r))
1062                 return $r[0]['total'];
1063         return 0;
1064
1065 }
1066
1067
1068 function common_friends($uid,$cid,$start = 0,$limit=9999,$shuffle = false) {
1069
1070         if($shuffle)
1071                 $sql_extra = " order by rand() ";
1072         else
1073                 $sql_extra = " order by `gcontact`.`name` asc ";
1074
1075         $r = q("SELECT `gcontact`.*, `contact`.`id` AS `cid`
1076                 FROM `glink`
1077                 INNER JOIN `gcontact` ON `glink`.`gcid` = `gcontact`.`id`
1078                 INNER JOIN `contact` ON `gcontact`.`nurl` = `contact`.`nurl`
1079                 WHERE `glink`.`cid` = %d and `glink`.`uid` = %d
1080                         AND `contact`.`uid` = %d AND `contact`.`self` = 0 AND `contact`.`blocked` = 0
1081                         AND `contact`.`hidden` = 0 AND `contact`.`id` != %d
1082                         AND ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`))
1083                         $sql_extra LIMIT %d, %d",
1084                 intval($cid),
1085                 intval($uid),
1086                 intval($uid),
1087                 intval($cid),
1088                 intval($start),
1089                 intval($limit)
1090         );
1091
1092         return $r;
1093
1094 }
1095
1096
1097 function count_common_friends_zcid($uid,$zcid) {
1098
1099         $r = q("SELECT count(*) as `total`
1100                 FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
1101                 where `glink`.`zcid` = %d
1102                 and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) ",
1103                 intval($zcid),
1104                 intval($uid)
1105         );
1106
1107         if(count($r))
1108                 return $r[0]['total'];
1109         return 0;
1110
1111 }
1112
1113 function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = false) {
1114
1115         if($shuffle)
1116                 $sql_extra = " order by rand() ";
1117         else
1118                 $sql_extra = " order by `gcontact`.`name` asc ";
1119
1120         $r = q("SELECT `gcontact`.*
1121                 FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
1122                 where `glink`.`zcid` = %d
1123                 and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) 
1124                 $sql_extra limit %d, %d",
1125                 intval($zcid),
1126                 intval($uid),
1127                 intval($start),
1128                 intval($limit)
1129         );
1130
1131         return $r;
1132
1133 }
1134
1135
1136 function count_all_friends($uid,$cid) {
1137
1138         $r = q("SELECT count(*) as `total`
1139                 FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
1140                 where `glink`.`cid` = %d and `glink`.`uid` = %d AND
1141                 ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`))",
1142                 intval($cid),
1143                 intval($uid)
1144         );
1145
1146         if(count($r))
1147                 return $r[0]['total'];
1148         return 0;
1149
1150 }
1151
1152
1153 function all_friends($uid,$cid,$start = 0, $limit = 80) {
1154
1155         $r = q("SELECT `gcontact`.*, `contact`.`id` AS `cid`
1156                 FROM `glink`
1157                 INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
1158                 LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl` AND `contact`.`uid` = %d
1159                 WHERE `glink`.`cid` = %d AND `glink`.`uid` = %d AND
1160                 ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`))
1161                 ORDER BY `gcontact`.`name` ASC LIMIT %d, %d ",
1162                 intval($uid),
1163                 intval($cid),
1164                 intval($uid),
1165                 intval($start),
1166                 intval($limit)
1167         );
1168
1169         return $r;
1170 }
1171
1172
1173
1174 function suggestion_query($uid, $start = 0, $limit = 80) {
1175
1176         if(! $uid)
1177                 return array();
1178
1179         $network = array(NETWORK_DFRN);
1180
1181         if (get_config('system','diaspora_enabled'))
1182                 $network[] = NETWORK_DIASPORA;
1183
1184         if (!get_config('system','ostatus_disabled'))
1185                 $network[] = NETWORK_OSTATUS;
1186
1187         $sql_network = implode("', '", $network);
1188         //$sql_network = "'".$sql_network."', ''";
1189         $sql_network = "'".$sql_network."'";
1190
1191         $r = q("SELECT count(glink.gcid) as `total`, gcontact.* from gcontact
1192                 INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id`
1193                 where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d )
1194                 AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d)
1195                 AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d)
1196                 AND `gcontact`.`updated` != '0000-00-00 00:00:00'
1197                 AND `gcontact`.`last_contact` >= `gcontact`.`last_failure`
1198                 AND `gcontact`.`network` IN (%s)
1199                 GROUP BY `glink`.`gcid` ORDER BY `gcontact`.`updated` DESC,`total` DESC LIMIT %d, %d",
1200                 intval($uid),
1201                 intval($uid),
1202                 intval($uid),
1203                 intval($uid),
1204                 $sql_network,
1205                 intval($start),
1206                 intval($limit)
1207         );
1208
1209         if(count($r) && count($r) >= ($limit -1))
1210                 return $r;
1211
1212         $r2 = q("SELECT gcontact.* FROM gcontact
1213                 INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id`
1214                 WHERE `glink`.`uid` = 0 AND `glink`.`cid` = 0 AND `glink`.`zcid` = 0 AND NOT `gcontact`.`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = %d)
1215                 AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d)
1216                 AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d)
1217                 AND `gcontact`.`updated` != '0000-00-00 00:00:00'
1218                 AND `gcontact`.`last_contact` >= `gcontact`.`last_failure`
1219                 AND `gcontact`.`network` IN (%s)
1220                 ORDER BY rand() LIMIT %d, %d",
1221                 intval($uid),
1222                 intval($uid),
1223                 intval($uid),
1224                 $sql_network,
1225                 intval($start),
1226                 intval($limit)
1227         );
1228
1229         $list = array();
1230         foreach ($r2 AS $suggestion)
1231                 $list[$suggestion["nurl"]] = $suggestion;
1232
1233         foreach ($r AS $suggestion)
1234                 $list[$suggestion["nurl"]] = $suggestion;
1235
1236         while (sizeof($list) > ($limit))
1237                 array_pop($list);
1238
1239         return $list;
1240 }
1241
1242 function update_suggestions() {
1243
1244         $a = get_app();
1245
1246         $done = array();
1247
1248         /// TODO Check if it is really neccessary to poll the own server
1249         poco_load(0,0,0,$a->get_baseurl() . '/poco');
1250
1251         $done[] = $a->get_baseurl() . '/poco';
1252
1253         if(strlen(get_config('system','directory'))) {
1254                 $x = fetch_url(get_server()."/pubsites");
1255                 if($x) {
1256                         $j = json_decode($x);
1257                         if($j->entries) {
1258                                 foreach($j->entries as $entry) {
1259
1260                                         poco_check_server($entry->url);
1261
1262                                         $url = $entry->url . '/poco';
1263                                         if(! in_array($url,$done))
1264                                                 poco_load(0,0,0,$entry->url . '/poco');
1265                                 }
1266                         }
1267                 }
1268         }
1269
1270         // Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts
1271         $r = q("SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN ('%s', '%s')",
1272                 dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA)
1273         );
1274
1275         if(count($r)) {
1276                 foreach($r as $rr) {
1277                         $base = substr($rr['poco'],0,strrpos($rr['poco'],'/'));
1278                         if(! in_array($base,$done))
1279                                 poco_load(0,0,0,$base);
1280                 }
1281         }
1282 }
1283
1284 function poco_discover_federation() {
1285         $last = get_config('poco','last_federation_discovery');
1286
1287         if($last) {
1288                 $next = $last + (24 * 60 * 60);
1289                 if($next > time())
1290                         return;
1291         }
1292
1293         $serverdata = fetch_url("http://the-federation.info/pods.json");
1294
1295         if (!$serverdata)
1296                 return;
1297
1298         $servers = json_decode($serverdata);
1299
1300         foreach($servers->pods AS $server)
1301                 poco_check_server("https://".$server->host);
1302
1303         set_config('poco','last_federation_discovery', time());
1304
1305 }
1306
1307 function poco_discover($complete = false) {
1308
1309         // Update the server list
1310         poco_discover_federation();
1311
1312         $no_of_queries = 5;
1313
1314         $requery_days = intval(get_config("system", "poco_requery_days"));
1315
1316         if ($requery_days == 0)
1317                 $requery_days = 7;
1318
1319         $last_update = date("c", time() - (60 * 60 * 24 * $requery_days));
1320
1321         $r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
1322         if ($r)
1323                 foreach ($r AS $server) {
1324
1325                         if (!poco_check_server($server["url"], $server["network"])) {
1326                                 // The server is not reachable? Okay, then we will try it later
1327                                 q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
1328                                 continue;
1329                         }
1330
1331                         // Fetch all users from the other server
1332                         $url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
1333
1334                         logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG);
1335
1336                         $retdata = z_fetch_url($url);
1337                         if ($retdata["success"]) {
1338                                 $data = json_decode($retdata["body"]);
1339
1340                                 poco_discover_server($data, 2);
1341
1342                                 if (get_config('system','poco_discovery') > 1) {
1343
1344                                         $timeframe = get_config('system','poco_discovery_since');
1345                                         if ($timeframe == 0)
1346                                                 $timeframe = 30;
1347
1348                                         $updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400);
1349
1350                                         // Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3)
1351                                         $url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
1352
1353                                         $success = false;
1354
1355                                         $retdata = z_fetch_url($url);
1356                                         if ($retdata["success"]) {
1357                                                 logger("Fetch all global contacts from the server ".$server["nurl"], LOGGER_DEBUG);
1358                                                 $success = poco_discover_server(json_decode($retdata["body"]));
1359                                         }
1360
1361                                         if (!$success AND (get_config('system','poco_discovery') > 2)) {
1362                                                 logger("Fetch contacts from users of the server ".$server["nurl"], LOGGER_DEBUG);
1363                                                 poco_discover_server_users($data, $server);
1364                                         }
1365                                 }
1366
1367                                 q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
1368                                 if (!$complete AND (--$no_of_queries == 0))
1369                                         break;
1370                         } else {
1371                                 // If the server hadn't replied correctly, then force a sanity check
1372                                 poco_check_server($server["url"], $server["network"], true);
1373
1374                                 // If we couldn't reach the server, we will try it some time later
1375                                 q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
1376                         }
1377                 }
1378 }
1379
1380 function poco_discover_server_users($data, $server) {
1381
1382         if (!isset($data->entry))
1383                 return;
1384
1385         foreach ($data->entry AS $entry) {
1386                 $username = "";
1387                 if (isset($entry->urls)) {
1388                         foreach($entry->urls as $url)
1389                                 if($url->type == 'profile') {
1390                                         $profile_url = $url->value;
1391                                         $urlparts = parse_url($profile_url);
1392                                         $username = end(explode("/", $urlparts["path"]));
1393                                 }
1394                 }
1395                 if ($username != "") {
1396                         logger("Fetch contacts for the user ".$username." from the server ".$server["nurl"], LOGGER_DEBUG);
1397
1398                         // Fetch all contacts from a given user from the other server
1399                         $url = $server["poco"]."/".$username."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
1400
1401                         $retdata = z_fetch_url($url);
1402                         if ($retdata["success"])
1403                                 poco_discover_server(json_decode($retdata["body"]), 3);
1404                 }
1405         }
1406 }
1407
1408 function poco_discover_server($data, $default_generation = 0) {
1409
1410         if (!isset($data->entry) OR !count($data->entry))
1411                 return false;
1412
1413         $success = false;
1414
1415         foreach ($data->entry AS $entry) {
1416                 $profile_url = '';
1417                 $profile_photo = '';
1418                 $connect_url = '';
1419                 $name = '';
1420                 $network = '';
1421                 $updated = '0000-00-00 00:00:00';
1422                 $location = '';
1423                 $about = '';
1424                 $keywords = '';
1425                 $gender = '';
1426                 $generation = $default_generation;
1427
1428                 $name = $entry->displayName;
1429
1430                 if(isset($entry->urls)) {
1431                         foreach($entry->urls as $url) {
1432                                 if($url->type == 'profile') {
1433                                         $profile_url = $url->value;
1434                                         continue;
1435                                 }
1436                                 if($url->type == 'webfinger') {
1437                                         $connect_url = str_replace('acct:' , '', $url->value);
1438                                         continue;
1439                                 }
1440                         }
1441                 }
1442
1443                 if(isset($entry->photos)) {
1444                         foreach($entry->photos as $photo) {
1445                                 if($photo->type == 'profile') {
1446                                         $profile_photo = $photo->value;
1447                                         continue;
1448                                 }
1449                         }
1450                 }
1451
1452                 if(isset($entry->updated))
1453                         $updated = date("Y-m-d H:i:s", strtotime($entry->updated));
1454
1455                 if(isset($entry->network))
1456                         $network = $entry->network;
1457
1458                 if(isset($entry->currentLocation))
1459                         $location = $entry->currentLocation;
1460
1461                 if(isset($entry->aboutMe))
1462                         $about = html2bbcode($entry->aboutMe);
1463
1464                 if(isset($entry->gender))
1465                         $gender = $entry->gender;
1466
1467                 if(isset($entry->generation) AND ($entry->generation > 0))
1468                         $generation = ++$entry->generation;
1469
1470                 if(isset($entry->tags))
1471                         foreach($entry->tags as $tag)
1472                                 $keywords = implode(", ", $tag);
1473
1474                 if ($generation > 0) {
1475                         $success = true;
1476
1477                         logger("Store profile ".$profile_url, LOGGER_DEBUG);
1478                         poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, 0, 0, 0);
1479                         logger("Done for profile ".$profile_url, LOGGER_DEBUG);
1480                 }
1481         }
1482         return $success;
1483 }
1484 ?>