email-import.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. /**
  3. * Insipid
  4. * Personal web-bookmark-system
  5. *
  6. * Copyright 2016-2019 Johannes Keßler
  7. *
  8. * Development starting from 2011: Johannes Keßler
  9. * https://www.bananas-playground.net/projekt/insipid/
  10. *
  11. * creator:
  12. * Luke Reeves <luke@neuro-tech.net>
  13. *
  14. * This program is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU General Public License as published by
  16. * the Free Software Foundation, either version 3 of the License, or
  17. * (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.
  26. *
  27. */
  28. mb_http_output('UTF-8');
  29. mb_internal_encoding('UTF-8');
  30. ini_set('error_reporting',-1); // E_ALL & E_STRICT
  31. # time settings
  32. date_default_timezone_set('Europe/Berlin');
  33. define('DEBUG',false);
  34. ## set the error reporting
  35. ini_set('log_errors',true);
  36. ini_set('error_log','import.log');
  37. if(DEBUG === true) {
  38. ini_set('display_errors',true);
  39. }
  40. else {
  41. ini_set('display_errors',false);
  42. }
  43. require('../config.php');
  44. require('../lib/summoner.class.php');
  45. require('../lib/tag.class.php');
  46. require('../lib/category.class.php');
  47. require('../lib/link.class.php');
  48. require('../lib/simple-imap.class.php');
  49. require('../lib/email-import-helper.class.php');
  50. # load only if needed
  51. use PHPMailer\PHPMailer\PHPMailer;
  52. use PHPMailer\PHPMailer\SMTP;
  53. if(EMAIL_REPORT_BACK === true) {
  54. require('../lib/phpmailer/PHPMailer.php');
  55. require('../lib/phpmailer/SMTP.php');
  56. $phpmailer = new PHPMailer();
  57. if(DEBUG === true) $phpmailer->SMTPDebug = SMTP::DEBUG_SERVER;
  58. $phpmailer->isSMTP();
  59. $phpmailer->Host = EMAIL_SERVER;
  60. $phpmailer->SMTPAuth = true;
  61. $phpmailer->SMTPSecure = $phpmailer::ENCRYPTION_SMTPS;
  62. $phpmailer->Username = EMAIL_SERVER_USER;
  63. $phpmailer->Password = EMAIL_SERVER_PASS;
  64. $phpmailer->Port = EMAIL_SERVER_PORT_SMTP;
  65. $phpmailer->setFrom(EMAIL_REPLY_BACK_ADDRESS);
  66. $phpmailer->Subject = EMAIL_REPLY_BACK_SUBJECT;
  67. $phpmailer->Timeout = 3;
  68. if(DEBUG === true) $phpmailer->SMTPDebug = SMTP::DEBUG_SERVER;
  69. $phpmailer->SMTPOptions = array(
  70. 'ssl' => [
  71. 'verify_peer' => false,
  72. 'verify_peer_name' => false,
  73. 'allow_self_signed' => true
  74. ],
  75. );
  76. }
  77. $DB = false;
  78. ## DB connection
  79. $DB = new mysqli(DB_HOST, DB_USERNAME,DB_PASSWORD, DB_NAME);
  80. if ($DB->connect_errno) exit('Can not connect to MySQL Server');
  81. $DB->set_charset("utf8mb4");
  82. $DB->query("SET collation_connection = 'utf8mb4_bin'");
  83. $driver = new mysqli_driver();
  84. $driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;;
  85. # the email reader
  86. $EmailReader = new SimpleImap();
  87. $emails = array();
  88. try {
  89. $EmailReader->connect();
  90. if(DEBUG === true) $EmailReader->mailboxStatus();
  91. }
  92. catch (Exception $e) {
  93. error_log('ERROR Email server connection failed: '.var_export($e->getMessage(),true));
  94. exit();
  95. }
  96. try {
  97. // emailid => info of the mail as an array
  98. // this is not the message-id
  99. $emails = $EmailReader->messageWithValidSubject(EMAIL_MARKER);
  100. }
  101. catch (Exception $e) {
  102. error_log('ERROR Can not process email messages: '.var_export($e->getMessage(),true));
  103. exit();
  104. }
  105. # process the emails and then move the emails
  106. $invalidProcessedEmails = array();
  107. $validProcessedEmails = array();
  108. if(!empty($emails)) {
  109. foreach($emails as $emailId=>$emailData) {
  110. $links = EmailImportHelper::extractEmailLinks($emailData['body']);
  111. if(!empty($links)) {
  112. if(DEBUG === true) var_dump($links);
  113. foreach($links as $linkstring) {
  114. # defaults
  115. $newdata['link'] = $linkstring;
  116. $newdata['description'] = '';
  117. $newdata['title'] = '';
  118. $newdata['image'] = '';
  119. $newdata['status'] = '3'; # moderation required
  120. $newdata['search'] = '';
  121. $newdata['tagArr'] = array();
  122. $newdata['catArr'] = array();
  123. $newdata['hash'] = '';
  124. if(strstr($linkstring, "|")) {
  125. $_t = explode("|", $linkstring);
  126. $newdata['link'] = $_t[0];
  127. $newdata['catArr'] = Summoner::prepareTagOrCategoryStr($_t[1]);
  128. if(isset($_t[2])) {
  129. $newdata['tagArr'] = Summoner::prepareTagOrCategoryStr($_t[2]);
  130. }
  131. }
  132. $newdata['link'] = filter_var($newdata['link'], FILTER_SANITIZE_URL);
  133. $newdata['link'] = Summoner::addSchemeToURL($newdata['link']);
  134. if (!filter_var($newdata['link'], FILTER_VALIDATE_URL)) {
  135. error_log("ERROR Invalid URL: ".var_export($newdata['link'],true));
  136. if(DEBUG === true) var_dump($newdata['link']);
  137. continue;
  138. }
  139. $newdata['hash'] = md5($newdata['link']);
  140. $linkInfo = Summoner::gatherInfoFromURL($newdata['link']);
  141. if(!empty($linkInfo) && !empty($linkInfo['title'])) {
  142. $newdata['title'] = $linkInfo['title'];
  143. if(isset($linkInfo['description'])) {
  144. $newdata['description'] = $linkInfo['description'];
  145. }
  146. if(isset($linkInfo['image'])) {
  147. $newdata['image'] = $linkInfo['image'];
  148. }
  149. }
  150. else {
  151. error_log("WARN No valid title for link found: ".$newdata['link']);
  152. if(DEBUG === true) var_dump("WARN No valid title for link found: ".var_export($newdata,true));
  153. array_push($invalidProcessedEmails, $emailData);
  154. continue;
  155. }
  156. $newdata['search'] = $newdata['title'];
  157. $newdata['search'] .= ' '.$newdata['description'];
  158. $newdata['search'] .= ' '.implode(" ",$newdata['tagArr']);
  159. $newdata['search'] .= ' '.implode(" ",$newdata['catArr']);
  160. $newdata['search'] = trim($newdata['search']);
  161. if(DEBUG === true) var_dump($newdata);
  162. $DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
  163. $linkObj = new Link($DB);
  164. $linkID = false;
  165. # check for duplicate
  166. $existing = $linkObj->load($newdata['hash']);
  167. if(!empty($existing) && isset($existing['id'])) {
  168. $linkID = $existing['id'];
  169. error_log('INFO Updating existing link with tag or category '.$newdata['link']);
  170. }
  171. else {
  172. $linkObj = new Link($DB);
  173. try {
  174. $linkID = $linkObj->create(array(
  175. 'hash' => $newdata['hash'],
  176. 'search' => $newdata['search'],
  177. 'link' => $newdata['link'],
  178. 'status' => $newdata['status'],
  179. 'description' => $newdata['description'],
  180. 'title' => $newdata['title'],
  181. 'image' => $newdata['image']
  182. ), true);
  183. } catch (Exception $e) {
  184. $_m = "WARN Can not create new link into DB." . $e->getMessage();
  185. error_log($_m);
  186. $emailData['importmessage'] = $_m;
  187. array_push($invalidProcessedEmails, $emailData);
  188. if (DEBUG === true) var_dump($_m);
  189. if (DEBUG === true) var_dump($newdata);
  190. continue;
  191. }
  192. }
  193. if(!empty($linkID)) {
  194. if(!empty($newdata['catArr'])) {
  195. foreach($newdata['catArr'] as $c) {
  196. $catObj = new Category($DB);
  197. $catObj->initbystring($c);
  198. $catObj->setRelation($linkID);
  199. unset($catObj);
  200. }
  201. }
  202. if(!empty($newdata['tagArr'])) {
  203. foreach($newdata['tagArr'] as $t) {
  204. $tagObj = new Tag($DB);
  205. $tagObj->initbystring($t);
  206. $tagObj->setRelation($linkID);
  207. unset($tagObj);
  208. }
  209. }
  210. $DB->commit();
  211. error_log("INFO Link successfully added/updated: ".$newdata['link']);
  212. array_push($validProcessedEmails,$emailData);
  213. }
  214. else {
  215. $DB->rollback();
  216. error_log("ERROR Link could not be added. SQL problem? ".$newdata['link']);
  217. $emailData['importmessage'] = "Link could not be added";
  218. array_push($invalidProcessedEmails,$emailData);
  219. }
  220. }
  221. }
  222. }
  223. }
  224. # if we have invalid import mails, ignore them, just log em
  225. # if EMAIL_REPORT_BACK is true then report back with errors if EMAIL_REPLY_BACK_VALID
  226. if(!empty($invalidProcessedEmails)) {
  227. error_log("INFO We have invalid import messages.");
  228. foreach ($invalidProcessedEmails as $invalidMail) {
  229. if(EmailImportHelper::canSendReplyTo($invalidMail['header_rfc822']->reply_toaddress)
  230. && !EmailImportHelper::isAutoReplyMessage($invalidMail['header_array'])) {
  231. $_address = PHPMailer::parseAddresses($invalidMail['header_rfc822']->reply_toaddress);
  232. $phpmailer->Body = $invalidMail['importmessage']."\n\n";
  233. $phpmailer->Body .= $invalidMail['body'];
  234. $phpmailer->addAddress($_address[0]['address']);
  235. $phpmailer->send();
  236. error_log("INFO Report back email to: ".$_address[0]['address']);
  237. }
  238. else {
  239. error_log("WARN Invalid message: ".$invalidMail['header_rfc822']->subject);
  240. }
  241. }
  242. }
  243. # move them to the processed / archive folder
  244. if(!empty($validProcessedEmails)) {
  245. error_log("INFO We have valid import messages.");
  246. foreach ($validProcessedEmails as $validMail) {
  247. $EmailReader->moveMessage($validMail['uid']);
  248. error_log("INFO Mail moved to archive ".$validMail['header_rfc822']->subject);
  249. }
  250. }
  251. $DB->close();
  252. $EmailReader->close();