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.

1111 lines
32 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
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. * poco_load
  7. *
  8. * Given a contact-id (minimum), load the PortableContacts friend list for that contact,
  9. * and add the entries to the gcontact (Global Contact) table, or update existing entries
  10. * if anything (name or photo) has changed.
  11. * We use normalised urls for comparison which ignore http vs https and www.domain vs domain
  12. *
  13. * Once the global contact is stored add (if necessary) the contact linkage which associates
  14. * the given uid, cid to the global contact entry. There can be many uid/cid combinations
  15. * pointing to the same global contact id.
  16. *
  17. */
  18. function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
  19. $a = get_app();
  20. if($cid) {
  21. if((! $url) || (! $uid)) {
  22. $r = q("select `poco`, `uid` from `contact` where `id` = %d limit 1",
  23. intval($cid)
  24. );
  25. if(count($r)) {
  26. $url = $r[0]['poco'];
  27. $uid = $r[0]['uid'];
  28. }
  29. }
  30. if(! $uid)
  31. return;
  32. }
  33. if(! $url)
  34. return;
  35. $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') ;
  36. logger('poco_load: ' . $url, LOGGER_DEBUG);
  37. $s = fetch_url($url);
  38. logger('poco_load: returns ' . $s, LOGGER_DATA);
  39. logger('poco_load: return code: ' . $a->get_curl_code(), LOGGER_DEBUG);
  40. if(($a->get_curl_code() > 299) || (! $s))
  41. return;
  42. $j = json_decode($s);
  43. logger('poco_load: json: ' . print_r($j,true),LOGGER_DATA);
  44. if(! isset($j->entry))
  45. return;
  46. $total = 0;
  47. foreach($j->entry as $entry) {
  48. $total ++;
  49. $profile_url = '';
  50. $profile_photo = '';
  51. $connect_url = '';
  52. $name = '';
  53. $network = '';
  54. $updated = '0000-00-00 00:00:00';
  55. $location = '';
  56. $about = '';
  57. $keywords = '';
  58. $gender = '';
  59. $generation = 0;
  60. $name = $entry->displayName;
  61. if(isset($entry->urls)) {
  62. foreach($entry->urls as $url) {
  63. if($url->type == 'profile') {
  64. $profile_url = $url->value;
  65. continue;
  66. }
  67. if($url->type == 'webfinger') {
  68. $connect_url = str_replace('acct:' , '', $url->value);
  69. continue;
  70. }
  71. }
  72. }
  73. if(isset($entry->photos)) {
  74. foreach($entry->photos as $photo) {
  75. if($photo->type == 'profile') {
  76. $profile_photo = $photo->value;
  77. continue;
  78. }
  79. }
  80. }
  81. if(isset($entry->updated))
  82. $updated = date("Y-m-d H:i:s", strtotime($entry->updated));
  83. if(isset($entry->network))
  84. $network = $entry->network;
  85. if(isset($entry->currentLocation))
  86. $location = $entry->currentLocation;
  87. if(isset($entry->aboutMe))
  88. $about = html2bbcode($entry->aboutMe);
  89. if(isset($entry->gender))
  90. $gender = $entry->gender;
  91. if(isset($entry->generation) AND ($entry->generation > 0))
  92. $generation = ++$entry->generation;
  93. if(isset($entry->tags))
  94. foreach($entry->tags as $tag)
  95. $keywords = implode(", ", $tag);
  96. // If you query a Friendica server for its profiles, the network has to be Friendica
  97. // To-Do: It could also be a Redmatrix server
  98. //if ($uid == 0)
  99. // $network = NETWORK_DFRN;
  100. poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
  101. // Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
  102. if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
  103. q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
  104. WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
  105. dbesc($location),
  106. dbesc($about),
  107. dbesc($keywords),
  108. dbesc($gender),
  109. dbesc(normalise_link($profile_url)),
  110. dbesc(NETWORK_DFRN));
  111. }
  112. logger("poco_load: loaded $total entries",LOGGER_DEBUG);
  113. q("DELETE FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `zcid` = %d AND `updated` < UTC_TIMESTAMP - INTERVAL 2 DAY",
  114. intval($cid),
  115. intval($uid),
  116. intval($zcid)
  117. );
  118. }
  119. function poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid = 0, $uid = 0, $zcid = 0) {
  120. $a = get_app();
  121. // Generation:
  122. // 0: No definition
  123. // 1: Profiles on this server
  124. // 2: Contacts of profiles on this server
  125. // 3: Contacts of contacts of profiles on this server
  126. // 4: ...
  127. $gcid = "";
  128. if ($profile_url == "")
  129. return $gcid;
  130. $orig_updated = $updated;
  131. // Don't store the statusnet connector as network
  132. // We can't simply set this to NETWORK_OSTATUS since the connector could have fetched posts from friendica as well
  133. if ($network == NETWORK_STATUSNET)
  134. $network = "";
  135. // The global contacts should contain the original picture, not the cached one
  136. if (($generation != 1) AND stristr(normalise_link($profile_photo), normalise_link($a->get_baseurl()."/photo/")))
  137. $profile_photo = "";
  138. $r = q("SELECT `network` FROM `contact` WHERE `nurl` = '%s' AND `network` != '' AND `network` != '%s' LIMIT 1",
  139. dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
  140. );
  141. if(count($r))
  142. $network = $r[0]["network"];
  143. if (($network == "") OR ($network == NETWORK_OSTATUS)) {
  144. $r = q("SELECT `network`, `url` FROM `contact` WHERE `alias` IN ('%s', '%s') AND `network` != '' AND `network` != '%s' LIMIT 1",
  145. dbesc($profile_url), dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
  146. );
  147. if(count($r)) {
  148. $network = $r[0]["network"];
  149. $profile_url = $r[0]["url"];
  150. }
  151. }
  152. $x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
  153. dbesc(normalise_link($profile_url))
  154. );
  155. if (count($x)) {
  156. if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET))
  157. $network = $x[0]["network"];
  158. if ($updated == "0000-00-00 00:00:00")
  159. $updated = $x[0]["updated"];
  160. $last_contact = $x[0]["last_contact"];
  161. $last_failure = $x[0]["last_failure"];
  162. $server_url = $x[0]["server_url"];
  163. } else {
  164. $last_contact = "0000-00-00 00:00:00";
  165. $last_failure = "0000-00-00 00:00:00";
  166. $server_url = "";
  167. }
  168. if (($network == "") OR ($name == "") OR ($profile_photo == "") OR ($server_url == "")) {
  169. $data = probe_url($profile_url);
  170. $network = $data["network"];
  171. $name = $data["name"];
  172. $profile_url = $data["url"];
  173. $profile_photo = $data["photo"];
  174. $server_url = $data["baseurl"];
  175. }
  176. if (count($x) AND ($x[0]["network"] == "") AND ($network != "")) {
  177. q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
  178. dbesc($network),
  179. dbesc(normalise_link($profile_url))
  180. );
  181. }
  182. if (($name == "") OR ($profile_photo == ""))
  183. return $gcid;
  184. if (!in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
  185. return $gcid;
  186. logger("profile-check generation: ".$generation." Network: ".$network." URL: ".$profile_url." name: ".$name." avatar: ".$profile_photo, LOGGER_DEBUG);
  187. // Only fetch last update manually if it wasn't provided and enabled in the system
  188. if (get_config('system','poco_completion') AND ($orig_updated == "0000-00-00 00:00:00") AND poco_do_update($updated, $last_contact, $last_failure)) {
  189. $last_updated = poco_last_updated($profile_url);
  190. if ($last_updated) {
  191. $updated = $last_updated;
  192. $last_contact = datetime_convert();
  193. logger("Last updated for profile ".$profile_url.": ".$updated, LOGGER_DEBUG);
  194. if (count($x))
  195. q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'", dbesc($last_contact), dbesc(normalise_link($profile_url)));
  196. } else {
  197. $last_failure = datetime_convert();
  198. if (count($x))
  199. q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'", dbesc($last_failure), dbesc(normalise_link($profile_url)));
  200. }
  201. }
  202. poco_check_server($server_url, $network);
  203. // Test - remove before flight
  204. //if ($last_contact > $last_failure)
  205. // q("UPDATE `gserver` SET `last_contact` = '%s' WHERE `nurl` = '%s'", dbesc($last_contact), dbesc(normalise_link($server_url)));
  206. //else
  207. // q("UPDATE `gserver` SET `last_failure` = '%s' WHERE `nurl` = '%s'", dbesc($last_failure), dbesc(normalise_link($server_url)));
  208. if(count($x)) {
  209. $gcid = $x[0]['id'];
  210. if (($location == "") AND ($x[0]['location'] != ""))
  211. $location = $x[0]['location'];
  212. if (($about == "") AND ($x[0]['about'] != ""))
  213. $about = $x[0]['about'];
  214. if (($gender == "") AND ($x[0]['gender'] != ""))
  215. $gender = $x[0]['gender'];
  216. if (($keywords == "") AND ($x[0]['keywords'] != ""))
  217. $keywords = $x[0]['keywords'];
  218. if (($generation == 0) AND ($x[0]['generation'] > 0))
  219. $generation = $x[0]['generation'];
  220. if($x[0]['name'] != $name || $x[0]['photo'] != $profile_photo || $x[0]['updated'] < $updated) {
  221. q("UPDATE `gcontact` SET `name` = '%s', `network` = '%s', `photo` = '%s', `connect` = '%s', `url` = '%s', `server_url` = '%s',
  222. `updated` = '%s', `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s', `generation` = %d
  223. WHERE (`generation` >= %d OR `generation` = 0) AND `nurl` = '%s'",
  224. dbesc($name),
  225. dbesc($network),
  226. dbesc($profile_photo),
  227. dbesc($connect_url),
  228. dbesc($profile_url),
  229. dbesc($server_url),
  230. dbesc($updated),
  231. dbesc($location),
  232. dbesc($about),
  233. dbesc($keywords),
  234. dbesc($gender),
  235. intval($generation),
  236. intval($generation),
  237. dbesc(normalise_link($profile_url))
  238. );
  239. }
  240. } else {
  241. q("INSERT INTO `gcontact` (`name`,`network`, `url`,`nurl`,`photo`,`connect`, `server_url`, `updated`, `last_contact`, `last_failure`, `location`, `about`, `keywords`, `gender`, `generation`)
  242. VALUES ('%s', '%s', '%s', '%s', '%s','%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)",
  243. dbesc($name),
  244. dbesc($network),
  245. dbesc($profile_url),
  246. dbesc(normalise_link($profile_url)),
  247. dbesc($profile_photo),
  248. dbesc($connect_url),
  249. dbesc($server_url),
  250. dbesc($updated),
  251. dbesc($last_contact),
  252. dbesc($last_failure),
  253. dbesc($location),
  254. dbesc($about),
  255. dbesc($keywords),
  256. dbesc($gender),
  257. intval($generation)
  258. );
  259. $x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
  260. dbesc(normalise_link($profile_url))
  261. );
  262. if(count($x))
  263. $gcid = $x[0]['id'];
  264. }
  265. if(! $gcid)
  266. return $gcid;
  267. $r = q("SELECT * FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d LIMIT 1",
  268. intval($cid),
  269. intval($uid),
  270. intval($gcid),
  271. intval($zcid)
  272. );
  273. if(! count($r)) {
  274. q("INSERT INTO `glink` (`cid`,`uid`,`gcid`,`zcid`, `updated`) VALUES (%d,%d,%d,%d, '%s') ",
  275. intval($cid),
  276. intval($uid),
  277. intval($gcid),
  278. intval($zcid),
  279. dbesc(datetime_convert())
  280. );
  281. } else {
  282. q("UPDATE `glink` SET `updated` = '%s' WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d",
  283. dbesc(datetime_convert()),
  284. intval($cid),
  285. intval($uid),
  286. intval($gcid),
  287. intval($zcid)
  288. );
  289. }
  290. // For unknown reasons there are sometimes duplicates
  291. q("DELETE FROM `gcontact` WHERE `nurl` = '%s' AND `id` != %d AND
  292. NOT EXISTS (SELECT `gcid` FROM `glink` WHERE `gcid` = `gcontact`.`id`)",
  293. dbesc(normalise_link($profile_url)),
  294. intval($gcid)
  295. );
  296. return $gcid;
  297. }
  298. function poco_last_updated($profile) {
  299. $data = probe_url($profile);
  300. if (($data["poll"] == "") OR ($data["network"] == NETWORK_FEED))
  301. return false;
  302. // To-Do: Use noscrape
  303. $feedret = z_fetch_url($data["poll"]);
  304. if (!$feedret["success"])
  305. return false;
  306. $doc = new DOMDocument();
  307. @$doc->loadXML($feedret["body"]);
  308. $xpath = new DomXPath($doc);
  309. $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom");
  310. $entries = $xpath->query('/atom:feed/atom:entry');
  311. $last_updated = "";
  312. foreach ($entries AS $entry) {
  313. $published = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
  314. $updated = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
  315. if ($last_updated < $published)
  316. $last_updated = $published;
  317. if ($last_updated < $updated)
  318. $last_updated = $updated;
  319. }
  320. // Maybe there aren't any entries. Then check if it is a valid feed
  321. if ($last_updated == "")
  322. if ($xpath->query('/atom:feed')->length > 0)
  323. $last_updated = "0000-00-00 00:00:00";
  324. return($last_updated);
  325. }
  326. function poco_do_update($updated, $last_contact, $last_failure) {
  327. $now = strtotime(datetime_convert());
  328. if ($updated > $last_contact)
  329. $contact_time = strtotime($updated);
  330. else
  331. $contact_time = strtotime($last_contact);
  332. $failure_time = strtotime($last_failure);
  333. // If the last contact was less than 24 hours then don't update
  334. if (($now - $contact_time) < (60 * 60 * 24))
  335. return false;
  336. // If the last failure was less than 24 hours then don't update
  337. if (($now - $failure_time) < (60 * 60 * 24))
  338. return false;
  339. // If the last contact was less than a week ago and the last failure is older than a week then don't update
  340. if ((($now - $contact_time) < (60 * 60 * 24 * 7)) AND ($contact_time > $failure_time))
  341. return false;
  342. // If the last contact time was more than a week ago, then only try once a week
  343. if (($now - $contact_time) > (60 * 60 * 24 * 7) AND ($now - $failure_time) < (60 * 60 * 24 * 7))
  344. return false;
  345. // If the last contact time was more than a month ago, then only try once a month
  346. if (($now - $contact_time) > (60 * 60 * 24 * 30) AND ($now - $failure_time) < (60 * 60 * 24 * 30))
  347. return false;
  348. return true;
  349. }
  350. function poco_to_boolean($val) {
  351. if (($val == "true") OR ($val == 1))
  352. return(true);
  353. if (($val == "false") OR ($val == 0))
  354. return(false);
  355. return ($val);
  356. }
  357. function poco_check_server($server_url, $network = "") {
  358. if ($server_url == "")
  359. return;
  360. $servers = q("SELECT * FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
  361. if ($servers) {
  362. $poco = $servers[0]["poco"];
  363. $noscrape = $servers[0]["noscrape"];
  364. if ($network == "")
  365. $network = $servers[0]["network"];
  366. $last_contact = $servers[0]["last_contact"];
  367. $last_failure = $servers[0]["last_failure"];
  368. $version = $servers[0]["version"];
  369. $platform = $servers[0]["platform"];
  370. $site_name = $servers[0]["site_name"];
  371. $info = $servers[0]["info"];
  372. $register_policy = $servers[0]["register_policy"];
  373. // Only check the server once a week
  374. if (strtotime(datetime_convert()) < (strtotime($last_contact) + (60 * 60 * 24 * 7)))
  375. return;
  376. if (strtotime(datetime_convert()) < (strtotime($last_failure) + (60 * 60 * 24 * 7)))
  377. return;
  378. } else {
  379. $poco = "";
  380. $noscrape = "";
  381. $version = "";
  382. $platform = "";
  383. $site_name = "";
  384. $info = "";
  385. $register_policy = -1;
  386. $last_contact = "0000-00-00 00:00:00";
  387. $last_failure = "0000-00-00 00:00:00";
  388. }
  389. $failure = false;
  390. $orig_last_failure = $last_failure;
  391. // Check if the page is accessible via SSL.
  392. $server_url = str_replace("http://", "https://", $server_url);
  393. $serverret = z_fetch_url($server_url."/.well-known/host-meta");
  394. // Maybe the page is unencrypted only?
  395. $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
  396. if (!$serverret["success"] OR ($serverret["body"] == "") OR (@sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
  397. $server_url = str_replace("https://", "http://", $server_url);
  398. $serverret = z_fetch_url($server_url."/.well-known/host-meta");
  399. $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
  400. }
  401. if (!$serverret["success"] OR ($serverret["body"] == "") OR (sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
  402. $last_failure = datetime_convert();
  403. $failure = true;
  404. } elseif ($network == NETWORK_DIASPORA)
  405. $last_contact = datetime_convert();
  406. if (!$failure) {
  407. // Test for Statusnet
  408. // Will also return data for Friendica and GNU Social - but it will be overwritten later
  409. // The "not implemented" is a special treatment for really, really old Friendica versions
  410. $serverret = z_fetch_url($server_url."/api/statusnet/version.json");
  411. if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
  412. $platform = "StatusNet";
  413. $version = trim($serverret["body"], '"');
  414. $network = NETWORK_OSTATUS;
  415. }
  416. // Test for GNU Social
  417. $serverret = z_fetch_url($server_url."/api/gnusocial/version.json");
  418. if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
  419. $platform = "GNU Social";
  420. $version = trim($serverret["body"], '"');
  421. $network = NETWORK_OSTATUS;
  422. }
  423. $serverret = z_fetch_url($server_url."/api/statusnet/config.json");
  424. if ($serverret["success"]) {
  425. $data = json_decode($serverret["body"]);
  426. if (isset($data->site->server)) {
  427. $last_contact = datetime_convert();
  428. if (isset($data->site->redmatrix)) {
  429. if (isset($data->site->redmatrix->PLATFORM_NAME))
  430. $platform = $data->site->redmatrix->PLATFORM_NAME;
  431. elseif (isset($data->site->redmatrix->RED_PLATFORM))
  432. $platform = $data->site->redmatrix->RED_PLATFORM;
  433. $version = $data->site->redmatrix->RED_VERSION;
  434. $network = NETWORK_DIASPORA;
  435. }
  436. if (isset($data->site->friendica)) {
  437. $platform = $data->site->friendica->FRIENDICA_PLATFORM;
  438. $version = $data->site->friendica->FRIENDICA_VERSION;
  439. $network = NETWORK_DFRN;
  440. }
  441. $site_name = $data->site->name;
  442. $data->site->closed = poco_to_boolean($data->site->closed);
  443. $data->site->private = poco_to_boolean($data->site->private);
  444. $data->site->inviteonly = poco_to_boolean($data->site->inviteonly);
  445. if (!$data->site->closed AND !$data->site->private and $data->site->inviteonly)
  446. $register_policy = REGISTER_APPROVE;
  447. elseif (!$data->site->closed AND !$data->site->private)
  448. $register_policy = REGISTER_OPEN;
  449. else
  450. $register_policy = REGISTER_CLOSED;
  451. }
  452. }
  453. }
  454. // Query statistics.json. Optional package for Diaspora, Friendica and Redmatrix
  455. if (!$failure) {
  456. $serverret = z_fetch_url($server_url."/statistics.json");
  457. if ($serverret["success"]) {
  458. $data = json_decode($serverret["body"]);
  459. if ($version == "")
  460. $version = $data->version;
  461. $site_name = $data->name;
  462. if (isset($data->network) AND ($platform == ""))
  463. $platform = $data->network;
  464. if ($data->registrations_open)
  465. $register_policy = REGISTER_OPEN;
  466. else
  467. $register_policy = REGISTER_CLOSED;
  468. if (isset($data->version))
  469. $last_contact = datetime_convert();
  470. }
  471. }
  472. // Check for noscrape
  473. // Friendica servers could be detected as OStatus servers
  474. if (!$failure AND in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS))) {
  475. $serverret = z_fetch_url($server_url."/friendica/json");
  476. if ($serverret["success"]) {
  477. $data = json_decode($serverret["body"]);
  478. if (isset($data->version)) {
  479. $last_contact = datetime_convert();
  480. $network = NETWORK_DFRN;
  481. $noscrape = $data->no_scrape_url;
  482. $version = $data->version;
  483. $site_name = $data->site_name;
  484. $info = $data->info;
  485. $register_policy_str = $data->register_policy;
  486. $platform = $data->platform;
  487. switch ($register_policy_str) {
  488. case "REGISTER_CLOSED":
  489. $register_policy = REGISTER_CLOSED;
  490. break;
  491. case "REGISTER_APPROVE":
  492. $register_policy = REGISTER_APPROVE;
  493. break;
  494. case "REGISTER_OPEN":
  495. $register_policy = REGISTER_OPEN;
  496. break;
  497. }
  498. }
  499. }
  500. }
  501. // Look for poco
  502. if (!$failure) {
  503. $serverret = z_fetch_url($server_url."/poco");
  504. if ($serverret["success"]) {
  505. $data = json_decode($serverret["body"]);
  506. if (isset($data->totalResults)) {
  507. $poco = $server_url."/poco";
  508. $last_contact = datetime_convert();
  509. }
  510. }
  511. }
  512. if ($servers)
  513. q("UPDATE `gserver` SET `url` = '%s', `version` = '%s', `site_name` = '%s', `info` = '%s', `register_policy` = %d, `poco` = '%s', `noscrape` = '%s',
  514. `network` = '%s', `platform` = '%s', `last_contact` = '%s', `last_failure` = '%s' WHERE `nurl` = '%s'",
  515. dbesc($server_url),
  516. dbesc($version),
  517. dbesc($site_name),
  518. dbesc($info),
  519. intval($register_policy),
  520. dbesc($poco),
  521. dbesc($noscrape),
  522. dbesc($network),
  523. dbesc($platform),
  524. dbesc($last_contact),
  525. dbesc($last_failure),
  526. dbesc(normalise_link($server_url))
  527. );
  528. else
  529. q("INSERT INTO `gserver` (`url`, `nurl`, `version`, `site_name`, `info`, `register_policy`, `poco`, `noscrape`, `network`, `platform`, `last_contact`)
  530. VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s')",
  531. dbesc($server_url),
  532. dbesc(normalise_link($server_url)),
  533. dbesc($version),
  534. dbesc($site_name),
  535. dbesc($info),
  536. intval($register_policy),
  537. dbesc($poco),
  538. dbesc($noscrape),
  539. dbesc($network),
  540. dbesc($platform),
  541. dbesc(datetime_convert())
  542. );
  543. }
  544. function poco_contact_from_body($body, $created, $cid, $uid) {
  545. preg_replace_callback("/\[share(.*?)\].*?\[\/share\]/ism",
  546. function ($match) use ($created, $cid, $uid){
  547. return(sub_poco_from_share($match, $created, $cid, $uid));
  548. }, $body);
  549. }
  550. function sub_poco_from_share($share, $created, $cid, $uid) {
  551. $profile = "";
  552. preg_match("/profile='(.*?)'/ism", $share[1], $matches);
  553. if ($matches[1] != "")
  554. $profile = $matches[1];
  555. preg_match('/profile="(.*?)"/ism', $share[1], $matches);
  556. if ($matches[1] != "")
  557. $profile = $matches[1];
  558. if ($profile == "")
  559. return;
  560. logger("prepare poco_check for profile ".$profile, LOGGER_DEBUG);
  561. poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid);
  562. }
  563. function poco_store($item) {
  564. // Isn't it public?
  565. if ($item['private'])
  566. return;
  567. // Or is it from a network where we don't store the global contacts?
  568. if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_STATUSNET, "")))
  569. return;
  570. // Is it a global copy?
  571. $store_gcontact = ($item["uid"] == 0);
  572. // Is it a comment on a global copy?
  573. if (!$store_gcontact AND ($item["uri"] != $item["parent-uri"])) {
  574. $q = q("SELECT `id` FROM `item` WHERE `uri`='%s' AND `uid` = 0", $item["parent-uri"]);
  575. $store_gcontact = count($q);
  576. }
  577. if (!$store_gcontact)
  578. return;
  579. // "3" means: We don't know this contact directly (Maybe a reshared item)
  580. $generation = 3;
  581. $network = "";
  582. $profile_url = $item["author-link"];
  583. // Is it a user from our server?
  584. $q = q("SELECT `id` FROM `contact` WHERE `self` AND `nurl` = '%s' LIMIT 1",
  585. dbesc(normalise_link($item["author-link"])));
  586. if (count($q)) {
  587. logger("Our user (generation 1): ".$item["author-link"], LOGGER_DEBUG);
  588. $generation = 1;
  589. $network = NETWORK_DFRN;
  590. } else { // Is it a contact from a user on our server?
  591. $q = q("SELECT `network`, `url` FROM `contact` WHERE `uid` != 0 AND `network` != ''
  592. AND (`nurl` = '%s' OR `alias` IN ('%s', '%s')) AND `network` != '%s' LIMIT 1",
  593. dbesc(normalise_link($item["author-link"])),
  594. dbesc(normalise_link($item["author-link"])),
  595. dbesc($item["author-link"]),
  596. dbesc(NETWORK_STATUSNET));
  597. if (count($q)) {
  598. $generation = 2;
  599. $network = $q[0]["network"];
  600. $profile_url = $q[0]["url"];
  601. logger("Known contact (generation 2): ".$profile_url, LOGGER_DEBUG);
  602. }
  603. }
  604. if ($generation == 3)
  605. logger("Unknown contact (generation 3): ".$item["author-link"], LOGGER_DEBUG);
  606. poco_check($profile_url, $item["author-name"], $network, $item["author-avatar"], "", "", "", "", "", $item["received"], $generation, $item["contact-id"], $item["uid"]);
  607. // Maybe its a body with a shared item? Then extract a global contact from it.
  608. poco_contact_from_body($item["body"], $item["received"], $item["contact-id"], $item["uid"]);
  609. }
  610. function count_common_friends($uid,$cid) {
  611. $r = q("SELECT count(*) as `total`
  612. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  613. where `glink`.`cid` = %d and `glink`.`uid` = %d
  614. and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 and id != %d ) ",
  615. intval($cid),
  616. intval($uid),
  617. intval($uid),
  618. intval($cid)
  619. );
  620. // logger("count_common_friends: $uid $cid {$r[0]['total']}");
  621. if(count($r))
  622. return $r[0]['total'];
  623. return 0;
  624. }
  625. function common_friends($uid,$cid,$start = 0,$limit=9999,$shuffle = false) {
  626. if($shuffle)
  627. $sql_extra = " order by rand() ";
  628. else
  629. $sql_extra = " order by `gcontact`.`name` asc ";
  630. $r = q("SELECT `gcontact`.*
  631. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  632. where `glink`.`cid` = %d and `glink`.`uid` = %d
  633. and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 and id != %d )
  634. $sql_extra limit %d, %d",
  635. intval($cid),
  636. intval($uid),
  637. intval($uid),
  638. intval($cid),
  639. intval($start),
  640. intval($limit)
  641. );
  642. return $r;
  643. }
  644. function count_common_friends_zcid($uid,$zcid) {
  645. $r = q("SELECT count(*) as `total`
  646. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  647. where `glink`.`zcid` = %d
  648. and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) ",
  649. intval($zcid),
  650. intval($uid)
  651. );
  652. if(count($r))
  653. return $r[0]['total'];
  654. return 0;
  655. }
  656. function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = false) {
  657. if($shuffle)
  658. $sql_extra = " order by rand() ";
  659. else
  660. $sql_extra = " order by `gcontact`.`name` asc ";
  661. $r = q("SELECT `gcontact`.*
  662. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  663. where `glink`.`zcid` = %d
  664. and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 )
  665. $sql_extra limit %d, %d",
  666. intval($zcid),
  667. intval($uid),
  668. intval($start),
  669. intval($limit)
  670. );
  671. return $r;
  672. }
  673. function count_all_friends($uid,$cid) {
  674. $r = q("SELECT count(*) as `total`
  675. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  676. where `glink`.`cid` = %d and `glink`.`uid` = %d ",
  677. intval($cid),
  678. intval($uid)
  679. );
  680. if(count($r))
  681. return $r[0]['total'];
  682. return 0;
  683. }
  684. function all_friends($uid,$cid,$start = 0, $limit = 80) {
  685. $r = q("SELECT `gcontact`.*
  686. FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id`
  687. where `glink`.`cid` = %d and `glink`.`uid` = %d
  688. order by `gcontact`.`name` asc LIMIT %d, %d ",
  689. intval($cid),
  690. intval($uid),
  691. intval($start),
  692. intval($limit)
  693. );
  694. return $r;
  695. }
  696. function suggestion_query($uid, $start = 0, $limit = 80) {
  697. if(! $uid)
  698. return array();
  699. $network = array(NETWORK_DFRN);
  700. if (get_config('system','diaspora_enabled'))
  701. $network[] = NETWORK_DIASPORA;
  702. if (!get_config('system','ostatus_disabled'))
  703. $network[] = NETWORK_OSTATUS;
  704. $sql_network = implode("', '", $network);
  705. //$sql_network = "'".$sql_network."', ''";
  706. $sql_network = "'".$sql_network."'";
  707. $r = q("SELECT count(glink.gcid) as `total`, gcontact.* from gcontact
  708. INNER JOIN glink on glink.gcid = gcontact.id
  709. where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d )
  710. and not gcontact.name in ( select name from contact where uid = %d )
  711. and not gcontact.id in ( select gcid from gcign where uid = %d )
  712. AND `gcontact`.`updated` != '0000-00-00 00:00:00'
  713. AND `gcontact`.`last_contact` >= `gcontact`.`last_failure`
  714. AND `gcontact`.`network` IN (%s)
  715. group by glink.gcid order by gcontact.updated desc,total desc limit %d, %d ",
  716. intval($uid),
  717. intval($uid),
  718. intval($uid),
  719. intval($uid),
  720. $sql_network,
  721. intval($start),
  722. intval($limit)
  723. );
  724. if(count($r) && count($r) >= ($limit -1))
  725. return $r;
  726. $r2 = q("SELECT gcontact.* from gcontact
  727. INNER JOIN glink on glink.gcid = gcontact.id
  728. where glink.uid = 0 and glink.cid = 0 and glink.zcid = 0 and not gcontact.nurl in ( select nurl from contact where uid = %d )
  729. and not gcontact.name in ( select name from contact where uid = %d )
  730. and not gcontact.id in ( select gcid from gcign where uid = %d )
  731. AND `gcontact`.`updated` != '0000-00-00 00:00:00'
  732. AND `gcontact`.`network` IN (%s)
  733. order by rand() limit %d, %d ",
  734. intval($uid),
  735. intval($uid),
  736. intval($uid),
  737. $sql_network,
  738. intval($start),
  739. intval($limit)
  740. );
  741. $list = array();
  742. foreach ($r2 AS $suggestion)
  743. $list[$suggestion["nurl"]] = $suggestion;
  744. foreach ($r AS $suggestion)
  745. $list[$suggestion["nurl"]] = $suggestion;
  746. return $list;
  747. }
  748. function update_suggestions() {
  749. $a = get_app();
  750. $done = array();
  751. // To-Do: Check if it is really neccessary to poll the own server
  752. poco_load(0,0,0,$a->get_baseurl() . '/poco');
  753. $done[] = $a->get_baseurl() . '/poco';
  754. if(strlen(get_config('system','directory_submit_url'))) {
  755. $x = fetch_url('http://dir.friendica.com/pubsites');
  756. if($x) {
  757. $j = json_decode($x);
  758. if($j->entries) {
  759. foreach($j->entries as $entry) {
  760. poco_check_server($entry->url);
  761. $url = $entry->url . '/poco';
  762. if(! in_array($url,$done))
  763. poco_load(0,0,0,$entry->url . '/poco');
  764. }
  765. }
  766. }
  767. }
  768. // Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts
  769. $r = q("SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN ('%s', '%s')",
  770. dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA)
  771. );
  772. if(count($r)) {
  773. foreach($r as $rr) {
  774. $base = substr($rr['poco'],0,strrpos($rr['poco'],'/'));
  775. if(! in_array($base,$done))
  776. poco_load(0,0,0,$base);
  777. }
  778. }
  779. }
  780. function poco_discover($complete = false) {
  781. $last_update = date("c", time() - (60 * 60 * 24));
  782. $r = q("SELECT `poco`, `nurl` FROM `gserver` WHERE `last_contact` > `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
  783. if ($r)
  784. foreach ($r AS $server) {
  785. // Fetch all users from the other server
  786. $url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
  787. logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG);
  788. $retdata = z_fetch_url($url);
  789. if ($retdata["success"]) {
  790. $data = json_decode($retdata["body"]);
  791. poco_discover_server($data, 2);
  792. if (get_config('system','poco_discovery') > 1) {
  793. // Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3)
  794. $url = $server["poco"]."/@global?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
  795. $retdata = z_fetch_url($url);
  796. if ($retdata["success"]) {
  797. logger("Fetch all global contacts from the server ".$server["nurl"], LOGGER_DEBUG);
  798. poco_discover_server(json_decode($retdata["body"]));
  799. } elseif (get_config('system','poco_discovery') > 2) {
  800. logger("Fetch contacts from users of the server ".$server["nurl"], LOGGER_DEBUG);
  801. poco_discover_server_users($data);
  802. }
  803. }
  804. q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
  805. if (!$complete)
  806. break;
  807. }
  808. }
  809. }
  810. function poco_discover_server_users($data) {
  811. foreach ($data->entry AS $entry) {
  812. $username = "";
  813. if (isset($entry->urls)) {
  814. foreach($entry->urls as $url)
  815. if($url->type == 'profile') {
  816. $profile_url = $url->value;
  817. $urlparts = parse_url($profile_url);
  818. $username = end(explode("/", $urlparts["path"]));
  819. }
  820. }
  821. if ($username != "") {
  822. logger("Fetch contacts for the user ".$username." from the server ".$server["nurl"], LOGGER_DEBUG);
  823. // Fetch all contacts from a given user from the other server
  824. $url = $server["poco"]."/".$username."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
  825. $retdata = z_fetch_url($url);
  826. if ($retdata["success"])
  827. poco_discover_server(json_decode($retdata["body"]), 3);
  828. }
  829. }
  830. }
  831. function poco_discover_server($data, $default_generation = 0) {
  832. if (!isset($data->entry) OR !count($data->entry))
  833. return;
  834. foreach ($data->entry AS $entry) {
  835. $profile_url = '';
  836. $profile_photo = '';
  837. $connect_url = '';
  838. $name = '';
  839. $network = '';
  840. $updated = '0000-00-00 00:00:00';
  841. $location = '';
  842. $about = '';
  843. $keywords = '';
  844. $gender = '';
  845. $generation = $default_generation;
  846. $name = $entry->displayName;
  847. if(isset($entry->urls)) {
  848. foreach($entry->urls as $url) {
  849. if($url->type == 'profile') {
  850. $profile_url = $url->value;
  851. continue;
  852. }
  853. if($url->type == 'webfinger') {
  854. $connect_url = str_replace('acct:' , '', $url->value);
  855. continue;
  856. }
  857. }
  858. }
  859. if(isset($entry->photos)) {
  860. foreach($entry->photos as $photo) {
  861. if($photo->type == 'profile') {
  862. $profile_photo = $photo->value;
  863. continue;
  864. }
  865. }
  866. }
  867. if(isset($entry->updated))
  868. $updated = date("Y-m-d H:i:s", strtotime($entry->updated));
  869. if(isset($entry->network))
  870. $network = $entry->network;
  871. if(isset($entry->currentLocation))
  872. $location = $entry->currentLocation;
  873. if(isset($entry->aboutMe))
  874. $about = html2bbcode($entry->aboutMe);
  875. if(isset($entry->gender))
  876. $gender = $entry->gender;
  877. if(isset($entry->generation) AND ($entry->generation > 0))
  878. $generation = ++$entry->generation;
  879. if(isset($entry->tags))
  880. foreach($entry->tags as $tag)
  881. $keywords = implode(", ", $tag);
  882. if ($generation > 0) {
  883. logger("Store profile ".$profile_url, LOGGER_DEBUG);
  884. poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation);
  885. logger("Done for profile ".$profile_url, LOGGER_DEBUG);
  886. }
  887. }
  888. }
  889. ?>