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.
 
 
 
 
 
 

1112 lines
32 KiB

  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. ?>