Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

689 wiersze
23 KiB

  1. <?php
  2. /**
  3. * @file src/Worker/OnePoll.php
  4. */
  5. namespace Friendica\Worker;
  6. use Friendica\Core\Config;
  7. use Friendica\Core\Logger;
  8. use Friendica\Core\Protocol;
  9. use Friendica\Database\DBA;
  10. use Friendica\DI;
  11. use Friendica\Model\Contact;
  12. use Friendica\Model\Item;
  13. use Friendica\Model\User;
  14. use Friendica\Protocol\Activity;
  15. use Friendica\Protocol\ActivityPub;
  16. use Friendica\Protocol\Email;
  17. use Friendica\Protocol\PortableContact;
  18. use Friendica\Util\DateTimeFormat;
  19. use Friendica\Util\Network;
  20. use Friendica\Util\Strings;
  21. use Friendica\Util\XML;
  22. class OnePoll
  23. {
  24. public static function execute($contact_id = 0, $command = '')
  25. {
  26. Logger::log('Start for contact ' . $contact_id);
  27. $force = false;
  28. if ($command == "force") {
  29. $force = true;
  30. }
  31. if (!$contact_id) {
  32. Logger::log('no contact');
  33. return;
  34. }
  35. Contact::updateFromProbe($contact_id, '', $force);
  36. $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]);
  37. if (!DBA::isResult($contact)) {
  38. Logger::log('Contact not found or cannot be used.');
  39. return;
  40. }
  41. // Special treatment for wrongly detected local contacts
  42. if (!$force && ($contact['network'] != Protocol::DFRN) && Contact::isLocalById($contact_id)) {
  43. Contact::updateFromProbe($contact_id, Protocol::DFRN, true);
  44. $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]);
  45. }
  46. if (($contact['network'] == Protocol::DFRN) && !Contact::isLegacyDFRNContact($contact)) {
  47. $protocol = Protocol::ACTIVITYPUB;
  48. } else {
  49. $protocol = $contact['network'];
  50. }
  51. $importer_uid = $contact['uid'];
  52. $updated = DateTimeFormat::utcNow();
  53. if ($importer_uid == 0) {
  54. Logger::log('Ignore public contacts');
  55. // set the last-update so we don't keep polling
  56. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  57. return;
  58. }
  59. // Possibly switch the remote contact to AP
  60. if ($protocol === Protocol::OSTATUS) {
  61. ActivityPub\Receiver::switchContact($contact['id'], $importer_uid, $contact['url']);
  62. $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]);
  63. }
  64. // load current friends if possible.
  65. if (!empty($contact['poco']) && ($contact['success_update'] > $contact['failure_update'])) {
  66. if (!DBA::exists('glink', ["`cid` = ? AND updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", $contact['id']])) {
  67. PortableContact::loadWorker($contact['id'], $importer_uid, 0, $contact['poco']);
  68. }
  69. }
  70. // Don't poll if polling is deactivated (But we poll feeds and mails anyway)
  71. if (!in_array($protocol, [Protocol::FEED, Protocol::MAIL]) && Config::get('system', 'disable_polling')) {
  72. Logger::log('Polling is disabled');
  73. // set the last-update so we don't keep polling
  74. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  75. return;
  76. }
  77. // We don't poll AP contacts by now
  78. if ($protocol === Protocol::ACTIVITYPUB) {
  79. Logger::log("Don't poll AP contact");
  80. // set the last-update so we don't keep polling
  81. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  82. return;
  83. }
  84. $importer = User::getOwnerDataById($importer_uid);
  85. if (empty($importer)) {
  86. Logger::log('No self contact for user '.$importer_uid);
  87. // set the last-update so we don't keep polling
  88. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  89. return;
  90. }
  91. $url = '';
  92. $xml = false;
  93. if ($contact['subhub']) {
  94. $poll_interval = Config::get('system', 'pushpoll_frequency', 3);
  95. $contact['priority'] = intval($poll_interval);
  96. $hub_update = false;
  97. if (DateTimeFormat::utcNow() > DateTimeFormat::utc($contact['last-update'] . " + 1 day")) {
  98. $hub_update = true;
  99. }
  100. } else {
  101. $hub_update = false;
  102. }
  103. Logger::log("poll: ({$protocol}-{$contact['id']}) IMPORTER: {$importer['name']}, CONTACT: {$contact['name']}");
  104. $xml = '';
  105. if ($protocol === Protocol::DFRN) {
  106. $xml = self::pollDFRN($contact, $updated);
  107. } elseif (($protocol === Protocol::OSTATUS)
  108. || ($protocol === Protocol::DIASPORA)
  109. || ($protocol === Protocol::FEED)) {
  110. $xml = self::pollFeed($contact, $protocol, $updated);
  111. } elseif ($protocol === Protocol::MAIL) {
  112. self::pollMail($contact, $importer_uid, $updated);
  113. }
  114. if (!empty($xml)) {
  115. Logger::log('received xml : ' . $xml, Logger::DATA);
  116. if (!strstr($xml, '<')) {
  117. Logger::log('post_handshake: response from ' . $url . ' did not contain XML.');
  118. $fields = ['last-update' => $updated, 'failure_update' => $updated];
  119. self::updateContact($contact, $fields);
  120. Contact::markForArchival($contact);
  121. return;
  122. }
  123. Logger::log("Consume feed of contact ".$contact['id']);
  124. consume_feed($xml, $importer, $contact, $hub);
  125. // do it a second time for DFRN so that any children find their parents.
  126. if ($protocol === Protocol::DFRN) {
  127. consume_feed($xml, $importer, $contact, $hub);
  128. }
  129. $hubmode = 'subscribe';
  130. if ($protocol === Protocol::DFRN || $contact['blocked']) {
  131. $hubmode = 'unsubscribe';
  132. }
  133. if (($protocol === Protocol::OSTATUS || $protocol == Protocol::FEED) && (! $contact['hub-verify'])) {
  134. $hub_update = true;
  135. }
  136. if ($force) {
  137. $hub_update = true;
  138. }
  139. Logger::log("Contact ".$contact['id']." returned hub: ".$hub." Network: ".$protocol." Relation: ".$contact['rel']." Update: ".$hub_update);
  140. if (strlen($hub) && $hub_update && (($contact['rel'] != Contact::FOLLOWER) || $protocol == Protocol::FEED)) {
  141. Logger::log('hub ' . $hubmode . ' : ' . $hub . ' contact name : ' . $contact['name'] . ' local user : ' . $importer['name']);
  142. $hubs = explode(',', $hub);
  143. if (count($hubs)) {
  144. foreach ($hubs as $h) {
  145. $h = trim($h);
  146. if (!strlen($h)) {
  147. continue;
  148. }
  149. subscribe_to_hub($h, $importer, $contact, $hubmode);
  150. }
  151. }
  152. }
  153. self::updateContact($contact, ['last-update' => $updated, 'success_update' => $updated]);
  154. Contact::unmarkForArchival($contact);
  155. } elseif (in_array($contact["network"], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::FEED])) {
  156. self::updateContact($contact, ['last-update' => $updated, 'failure_update' => $updated]);
  157. Contact::markForArchival($contact);
  158. } else {
  159. self::updateContact($contact, ['last-update' => $updated]);
  160. }
  161. Logger::log('End');
  162. return;
  163. }
  164. private static function RemoveReply($subject)
  165. {
  166. while (in_array(strtolower(substr($subject, 0, 3)), ["re:", "aw:"])) {
  167. $subject = trim(substr($subject, 4));
  168. }
  169. return $subject;
  170. }
  171. /**
  172. * @brief Updates a personal contact entry and the public contact entry
  173. *
  174. * @param array $contact The personal contact entry
  175. * @param array $fields The fields that are updated
  176. * @throws \Exception
  177. */
  178. private static function updateContact(array $contact, array $fields)
  179. {
  180. DBA::update('contact', $fields, ['id' => $contact['id']]);
  181. DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $contact['nurl']]);
  182. }
  183. /**
  184. * @brief Poll DFRN contacts
  185. *
  186. * @param array $contact The personal contact entry
  187. * @param string $updated The updated date
  188. * @return string polled XML
  189. * @throws \Exception
  190. */
  191. private static function pollDFRN(array $contact, $updated)
  192. {
  193. $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
  194. if (intval($contact['duplex']) && $contact['dfrn-id']) {
  195. $idtosend = '0:' . $orig_id;
  196. }
  197. if (intval($contact['duplex']) && $contact['issued-id']) {
  198. $idtosend = '1:' . $orig_id;
  199. }
  200. // they have permission to write to us. We already filtered this in the contact query.
  201. $perm = 'rw';
  202. // But this may be our first communication, so set the writable flag if it isn't set already.
  203. if (!intval($contact['writable'])) {
  204. $fields = ['writable' => true];
  205. DBA::update('contact', $fields, ['id' => $contact['id']]);
  206. }
  207. $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME)
  208. ? DateTimeFormat::utc('now - 7 days', DateTimeFormat::ATOM)
  209. : DateTimeFormat::utc($contact['last-update'], DateTimeFormat::ATOM)
  210. );
  211. $url = $contact['poll'] . '?dfrn_id=' . $idtosend
  212. . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
  213. . '&type=data&last_update=' . $last_update
  214. . '&perm=' . $perm;
  215. $curlResult = Network::curl($url);
  216. if (!$curlResult->isSuccess() && ($curlResult->getErrorNumber() == CURLE_OPERATION_TIMEDOUT)) {
  217. // set the last-update so we don't keep polling
  218. self::updateContact($contact, ['last-update' => $updated]);
  219. Contact::markForArchival($contact);
  220. Logger::log('Contact archived');
  221. return false;
  222. }
  223. $handshake_xml = $curlResult->getBody();
  224. $html_code = $curlResult->getReturnCode();
  225. Logger::log('handshake with url ' . $url . ' returns xml: ' . $handshake_xml, Logger::DATA);
  226. if (!strlen($handshake_xml) || ($html_code >= 400) || !$html_code) {
  227. // dead connection - might be a transient event, or this might
  228. // mean the software was uninstalled or the domain expired.
  229. // Will keep trying for one month.
  230. Logger::log("$url appears to be dead - marking for death ");
  231. // set the last-update so we don't keep polling
  232. $fields = ['last-update' => $updated, 'failure_update' => $updated];
  233. self::updateContact($contact, $fields);
  234. Contact::markForArchival($contact);
  235. return false;
  236. }
  237. if (!strstr($handshake_xml, '<')) {
  238. Logger::log('response from ' . $url . ' did not contain XML.');
  239. $fields = ['last-update' => $updated, 'failure_update' => $updated];
  240. self::updateContact($contact, $fields);
  241. Contact::markForArchival($contact);
  242. return false;
  243. }
  244. $res = XML::parseString($handshake_xml);
  245. if (!is_object($res)) {
  246. Logger::info('Unparseable response', ['url' => $url]);
  247. $fields = ['last-update' => $updated, 'failure_update' => $updated];
  248. self::updateContact($contact, $fields);
  249. Contact::markForArchival($contact);
  250. return false;
  251. }
  252. if (intval($res->status) == 1) {
  253. // we may not be friends anymore. Will keep trying for one month.
  254. Logger::log("$url replied status 1 - marking for death ");
  255. // set the last-update so we don't keep polling
  256. $fields = ['last-update' => $updated, 'failure_update' => $updated];
  257. self::updateContact($contact, $fields);
  258. Contact::markForArchival($contact);
  259. } elseif ($contact['term-date'] > DBA::NULL_DATETIME) {
  260. Contact::unmarkForArchival($contact);
  261. }
  262. if ((intval($res->status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) {
  263. // set the last-update so we don't keep polling
  264. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  265. Logger::log('Contact status is ' . $res->status);
  266. return false;
  267. }
  268. if (((float)$res->dfrn_version > 2.21) && ($contact['poco'] == '')) {
  269. $fields = ['poco' => str_replace('/profile/', '/poco/', $contact['url'])];
  270. DBA::update('contact', $fields, ['id' => $contact['id']]);
  271. }
  272. $postvars = [];
  273. $sent_dfrn_id = hex2bin((string) $res->dfrn_id);
  274. $challenge = hex2bin((string) $res->challenge);
  275. $final_dfrn_id = '';
  276. if ($contact['duplex'] && strlen($contact['prvkey'])) {
  277. openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']);
  278. openssl_private_decrypt($challenge, $postvars['challenge'], $contact['prvkey']);
  279. } else {
  280. openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']);
  281. openssl_public_decrypt($challenge, $postvars['challenge'], $contact['pubkey']);
  282. }
  283. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  284. if (strpos($final_dfrn_id, ':') == 1) {
  285. $final_dfrn_id = substr($final_dfrn_id, 2);
  286. }
  287. // There are issues with the legacy DFRN transport layer.
  288. // Since we mostly don't use it anyway, we won't dig into it deeper, but simply ignore it.
  289. if (empty($final_dfrn_id) || empty($orig_id)) {
  290. Logger::log('Contact has got no ID - quitting');
  291. return false;
  292. }
  293. if ($final_dfrn_id != $orig_id) {
  294. // did not decode properly - cannot trust this site
  295. Logger::log('ID did not decode: ' . $contact['id'] . ' orig: ' . $orig_id . ' final: ' . $final_dfrn_id);
  296. // set the last-update so we don't keep polling
  297. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  298. Contact::markForArchival($contact);
  299. return false;
  300. }
  301. $postvars['dfrn_id'] = $idtosend;
  302. $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
  303. $postvars['perm'] = 'rw';
  304. return Network::post($contact['poll'], $postvars)->getBody();
  305. }
  306. /**
  307. * @brief Poll Feed/OStatus contacts
  308. *
  309. * @param array $contact The personal contact entry
  310. * @param string $protocol The used protocol of the contact
  311. * @param string $updated The updated date
  312. * @return string polled XML
  313. * @throws \Exception
  314. */
  315. private static function pollFeed(array $contact, $protocol, $updated)
  316. {
  317. // Upgrading DB fields from an older Friendica version
  318. // Will only do this once per notify-enabled OStatus contact
  319. // or if relationship changes
  320. $stat_writeable = ((($contact['notify']) && ($contact['rel'] == Contact::FOLLOWER || $contact['rel'] == Contact::FRIEND)) ? 1 : 0);
  321. // Contacts from OStatus are always writable
  322. if ($protocol === Protocol::OSTATUS) {
  323. $stat_writeable = 1;
  324. }
  325. if ($stat_writeable != $contact['writable']) {
  326. $fields = ['writable' => $stat_writeable];
  327. DBA::update('contact', $fields, ['id' => $contact['id']]);
  328. }
  329. // Are we allowed to import from this person?
  330. if ($contact['rel'] == Contact::FOLLOWER || $contact['blocked']) {
  331. // set the last-update so we don't keep polling
  332. DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]);
  333. Logger::log('Contact is blocked or only a follower');
  334. return false;
  335. }
  336. $cookiejar = tempnam(get_temppath(), 'cookiejar-onepoll-');
  337. $curlResult = Network::curl($contact['poll'], false, ['cookiejar' => $cookiejar]);
  338. unlink($cookiejar);
  339. if ($curlResult->isTimeout()) {
  340. // set the last-update so we don't keep polling
  341. self::updateContact($contact, ['last-update' => $updated]);
  342. Contact::markForArchival($contact);
  343. Logger::log('Contact archived');
  344. return false;
  345. }
  346. return $curlResult->getBody();
  347. }
  348. /**
  349. * @brief Poll Mail contacts
  350. *
  351. * @param array $contact The personal contact entry
  352. * @param integer $importer_uid The UID of the importer
  353. * @param string $updated The updated date
  354. * @throws \Exception
  355. */
  356. private static function pollMail(array $contact, $importer_uid, $updated)
  357. {
  358. Logger::log("Mail: Fetching for ".$contact['addr'], Logger::DEBUG);
  359. $mail_disabled = ((function_exists('imap_open') && !Config::get('system', 'imap_disabled')) ? 0 : 1);
  360. if ($mail_disabled) {
  361. // set the last-update so we don't keep polling
  362. self::updateContact($contact, ['last-update' => $updated]);
  363. Contact::markForArchival($contact);
  364. Logger::log('Contact archived');
  365. return;
  366. }
  367. Logger::log("Mail: Enabled", Logger::DEBUG);
  368. $mbox = null;
  369. $user = DBA::selectFirst('user', ['prvkey'], ['uid' => $importer_uid]);
  370. $condition = ["`server` != '' AND `uid` = ?", $importer_uid];
  371. $mailconf = DBA::selectFirst('mailacct', [], $condition);
  372. if (DBA::isResult($user) && DBA::isResult($mailconf)) {
  373. $mailbox = Email::constructMailboxName($mailconf);
  374. $password = '';
  375. openssl_private_decrypt(hex2bin($mailconf['pass']), $password, $user['prvkey']);
  376. $mbox = Email::connect($mailbox, $mailconf['user'], $password);
  377. unset($password);
  378. Logger::log("Mail: Connect to " . $mailconf['user']);
  379. if ($mbox) {
  380. $fields = ['last_check' => $updated];
  381. DBA::update('mailacct', $fields, ['id' => $mailconf['id']]);
  382. Logger::log("Mail: Connected to " . $mailconf['user']);
  383. } else {
  384. Logger::log("Mail: Connection error ".$mailconf['user']." ".print_r(imap_errors(), true));
  385. }
  386. }
  387. if (!$mbox) {
  388. return;
  389. }
  390. $msgs = Email::poll($mbox, $contact['addr']);
  391. if (count($msgs)) {
  392. Logger::log("Mail: Parsing ".count($msgs)." mails from ".$contact['addr']." for ".$mailconf['user'], Logger::DEBUG);
  393. $metas = Email::messageMeta($mbox, implode(',', $msgs));
  394. if (count($metas) != count($msgs)) {
  395. Logger::log("for " . $mailconf['user'] . " there are ". count($msgs) . " messages but received " . count($metas) . " metas", Logger::DEBUG);
  396. } else {
  397. $msgs = array_combine($msgs, $metas);
  398. foreach ($msgs as $msg_uid => $meta) {
  399. Logger::log("Mail: Parsing mail ".$msg_uid, Logger::DATA);
  400. $datarray = [];
  401. $datarray['uid'] = $importer_uid;
  402. $datarray['contact-id'] = $contact['id'];
  403. $datarray['verb'] = Activity::POST;
  404. $datarray['object-type'] = Activity\ObjectType::NOTE;
  405. $datarray['network'] = Protocol::MAIL;
  406. // $meta = Email::messageMeta($mbox, $msg_uid);
  407. $datarray['uri'] = Email::msgid2iri(trim($meta->message_id, '<>'));
  408. // Have we seen it before?
  409. $fields = ['deleted', 'id'];
  410. $condition = ['uid' => $importer_uid, 'uri' => $datarray['uri']];
  411. $item = Item::selectFirst($fields, $condition);
  412. if (DBA::isResult($item)) {
  413. Logger::log("Mail: Seen before ".$msg_uid." for ".$mailconf['user']." UID: ".$importer_uid." URI: ".$datarray['uri'],Logger::DEBUG);
  414. // Only delete when mails aren't automatically moved or deleted
  415. if (($mailconf['action'] != 1) && ($mailconf['action'] != 3))
  416. if ($meta->deleted && ! $item['deleted']) {
  417. $fields = ['deleted' => true, 'changed' => $updated];
  418. Item::update($fields, ['id' => $item['id']]);
  419. }
  420. switch ($mailconf['action']) {
  421. case 0:
  422. Logger::log("Mail: Seen before ".$msg_uid." for ".$mailconf['user'].". Doing nothing.", Logger::DEBUG);
  423. break;
  424. case 1:
  425. Logger::log("Mail: Deleting ".$msg_uid." for ".$mailconf['user']);
  426. imap_delete($mbox, $msg_uid, FT_UID);
  427. break;
  428. case 2:
  429. Logger::log("Mail: Mark as seen ".$msg_uid." for ".$mailconf['user']);
  430. imap_setflag_full($mbox, $msg_uid, "\\Seen", ST_UID);
  431. break;
  432. case 3:
  433. Logger::log("Mail: Moving ".$msg_uid." to ".$mailconf['movetofolder']." for ".$mailconf['user']);
  434. imap_setflag_full($mbox, $msg_uid, "\\Seen", ST_UID);
  435. if ($mailconf['movetofolder'] != "") {
  436. imap_mail_move($mbox, $msg_uid, $mailconf['movetofolder'], FT_UID);
  437. }
  438. break;
  439. }
  440. continue;
  441. }
  442. // look for a 'references' or an 'in-reply-to' header and try to match with a parent item we have locally.
  443. $raw_refs = (property_exists($meta, 'references') ? str_replace("\t", '', $meta->references) : '');
  444. if (!trim($raw_refs)) {
  445. $raw_refs = (property_exists($meta, 'in_reply_to') ? str_replace("\t", '', $meta->in_reply_to) : '');
  446. }
  447. $raw_refs = trim($raw_refs); // Don't allow a blank reference in $refs_arr
  448. if ($raw_refs) {
  449. $refs_arr = explode(' ', $raw_refs);
  450. if (count($refs_arr)) {
  451. for ($x = 0; $x < count($refs_arr); $x ++) {
  452. $refs_arr[$x] = Email::msgid2iri(str_replace(['<', '>', ' '],['', '', ''], $refs_arr[$x]));
  453. }
  454. }
  455. $condition = ['uri' => $refs_arr, 'uid' => $importer_uid];
  456. $parent = Item::selectFirst(['parent-uri'], $condition);
  457. if (DBA::isResult($parent)) {
  458. $datarray['parent-uri'] = $parent['parent-uri']; // Set the parent as the top-level item
  459. }
  460. }
  461. // Decoding the header
  462. $subject = imap_mime_header_decode($meta->subject ?? '');
  463. $datarray['title'] = "";
  464. foreach ($subject as $subpart) {
  465. if ($subpart->charset != "default") {
  466. $datarray['title'] .= iconv($subpart->charset, 'UTF-8//IGNORE', $subpart->text);
  467. } else {
  468. $datarray['title'] .= $subpart->text;
  469. }
  470. }
  471. $datarray['title'] = Strings::escapeTags(trim($datarray['title']));
  472. //$datarray['title'] = Strings::escapeTags(trim($meta->subject));
  473. $datarray['created'] = DateTimeFormat::utc($meta->date);
  474. // Is it a reply?
  475. $reply = ((substr(strtolower($datarray['title']), 0, 3) == "re:") ||
  476. (substr(strtolower($datarray['title']), 0, 3) == "re-") ||
  477. ($raw_refs != ""));
  478. // Remove Reply-signs in the subject
  479. $datarray['title'] = self::RemoveReply($datarray['title']);
  480. // If it seems to be a reply but a header couldn't be found take the last message with matching subject
  481. if (empty($datarray['parent-uri']) && $reply) {
  482. $condition = ['title' => $datarray['title'], 'uid' => $importer_uid, 'network' => Protocol::MAIL];
  483. $params = ['order' => ['created' => true]];
  484. $parent = Item::selectFirst(['parent-uri'], $condition, $params);
  485. if (DBA::isResult($parent)) {
  486. $datarray['parent-uri'] = $parent['parent-uri'];
  487. }
  488. }
  489. if (empty($datarray['parent-uri'])) {
  490. $datarray['parent-uri'] = $datarray['uri'];
  491. }
  492. $headers = imap_headerinfo($mbox, $meta->msgno);
  493. $object = [];
  494. if (!empty($headers->from)) {
  495. $object['from'] = $headers->from;
  496. }
  497. if (!empty($headers->to)) {
  498. $object['to'] = $headers->to;
  499. }
  500. if (!empty($headers->reply_to)) {
  501. $object['reply_to'] = $headers->reply_to;
  502. }
  503. if (!empty($headers->sender)) {
  504. $object['sender'] = $headers->sender;
  505. }
  506. if (!empty($object)) {
  507. $datarray['object'] = json_encode($object);
  508. }
  509. $fromname = $frommail = $headers->from[0]->mailbox . '@' . $headers->from[0]->host;
  510. if (!empty($headers->from[0]->personal)) {
  511. $fromname = $headers->from[0]->personal;
  512. }
  513. $datarray['author-name'] = $fromname;
  514. $datarray['author-link'] = "mailto:".$frommail;
  515. $datarray['author-avatar'] = $contact['photo'];
  516. $datarray['owner-name'] = $contact['name'];
  517. $datarray['owner-link'] = "mailto:".$contact['addr'];
  518. $datarray['owner-avatar'] = $contact['photo'];
  519. if ($datarray['parent-uri'] === $datarray['uri']) {
  520. $datarray['private'] = 1;
  521. }
  522. if (!DI::pConfig()->get($importer_uid, 'system', 'allow_public_email_replies')) {
  523. $datarray['private'] = 1;
  524. $datarray['allow_cid'] = '<' . $contact['id'] . '>';
  525. }
  526. $datarray = Email::getMessage($mbox, $msg_uid, $reply, $datarray);
  527. if (empty($datarray['body'])) {
  528. Logger::log("Mail: can't fetch msg ".$msg_uid." for ".$mailconf['user']);
  529. continue;
  530. }
  531. Logger::log("Mail: Importing ".$msg_uid." for ".$mailconf['user']);
  532. Item::insert($datarray);
  533. switch ($mailconf['action']) {
  534. case 0:
  535. Logger::log("Mail: Seen before ".$msg_uid." for ".$mailconf['user'].". Doing nothing.", Logger::DEBUG);
  536. break;
  537. case 1:
  538. Logger::log("Mail: Deleting ".$msg_uid." for ".$mailconf['user']);
  539. imap_delete($mbox, $msg_uid, FT_UID);
  540. break;
  541. case 2:
  542. Logger::log("Mail: Mark as seen ".$msg_uid." for ".$mailconf['user']);
  543. imap_setflag_full($mbox, $msg_uid, "\\Seen", ST_UID);
  544. break;
  545. case 3:
  546. Logger::log("Mail: Moving ".$msg_uid." to ".$mailconf['movetofolder']." for ".$mailconf['user']);
  547. imap_setflag_full($mbox, $msg_uid, "\\Seen", ST_UID);
  548. if ($mailconf['movetofolder'] != "") {
  549. imap_mail_move($mbox, $msg_uid, $mailconf['movetofolder'], FT_UID);
  550. }
  551. break;
  552. }
  553. }
  554. }
  555. } else {
  556. Logger::log("Mail: no mails for ".$mailconf['user']);
  557. }
  558. Logger::log("Mail: closing connection for ".$mailconf['user']);
  559. imap_close($mbox);
  560. }
  561. }