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.

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