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.

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