Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1461 lines
44 KiB

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