PageRenderTime 63ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/Settings/MailScanner/core/MailScanner.php

https://github.com/m-fuji-0616/vtigercrm-5.1.x-ja
PHP | 412 lines | 275 code | 45 blank | 92 comment | 73 complexity | 7227d8074c27b0a929991aee22811ff4 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /*********************************************************************************
  3. ** The contents of this file are subject to the vtiger CRM Public License Version 1.0
  4. * ("License"); You may not use this file except in compliance with the License
  5. * The Original Code is: vtiger CRM Open Source
  6. * The Initial Developer of the Original Code is vtiger.
  7. * Portions created by vtiger are Copyright (C) vtiger.
  8. * All Rights Reserved.
  9. *
  10. ********************************************************************************/
  11. require_once('modules/Settings/MailScanner/core/MailBox.php');
  12. require_once('modules/Settings/MailScanner/core/MailAttachmentMIME.php');
  13. /**
  14. * Mail Scanner provides the ability to scan through the given mailbox
  15. * applying the rules configured.
  16. */
  17. class Vtiger_MailScanner {
  18. // MailScanner information instance
  19. var $_scannerinfo = false;
  20. // Reference mailbox to use
  21. var $_mailbox = false;
  22. // Ignore scanning the folders always
  23. var $_generalIgnoreFolders = Array( "INBOX.Trash", "INBOX.Drafts", "[Gmail]/Spam", "[Gmail]/Trash", "[Gmail]/Drafts" );
  24. /** DEBUG functionality. */
  25. var $debug = false;
  26. function log($message) {
  27. global $log;
  28. if($log && $this->debug) { $log->debug($message); }
  29. else if($this->debug) echo "$message\n";
  30. }
  31. /**
  32. * Constructor.
  33. */
  34. function __construct($scannerinfo) {
  35. $this->_scannerinfo = $scannerinfo;
  36. }
  37. /**
  38. * Get mailbox instance configured for the scan
  39. */
  40. function getMailBox() {
  41. if(!$this->_mailbox) {
  42. $this->_mailbox = new Vtiger_MailBox($this->_scannerinfo);
  43. $this->_mailbox->debug = $this->debug;
  44. }
  45. return $this->_mailbox;
  46. }
  47. /**
  48. * Start Scanning.
  49. */
  50. function performScanNow() {
  51. // Check if rules exists to proceed
  52. $rules = $this->_scannerinfo->rules;
  53. if(empty($rules)) {
  54. $this->log("No rules setup for scanner [". $this->_scannerinfo->scannername . "] SKIPING\n");
  55. return;
  56. }
  57. // Build ignore folder list
  58. $ignoreFolders = Array() + $this->_generalIgnoreFolders;
  59. $folderinfoList = $this->_scannerinfo->getFolderInfo();
  60. foreach($folderinfoList as $foldername=>$folderinfo) {
  61. if(!$folderinfo[enabled]) $ignoreFolders[] = $foldername;
  62. }
  63. // Get mailbox instance to work with
  64. $mailbox = $this->getMailBox();
  65. $mailbox->connect();
  66. /** Loop through all the folders. */
  67. $folders = $mailbox->getFolders();
  68. if($folders) $this->log("Folders found: " . implode(',', $folders) . "\n");
  69. foreach($folders as $lookAtFolder) {
  70. // Skip folder scanning?
  71. if(in_array($lookAtFolder, $ignoreFolders)) {
  72. $this->log("\nIgnoring Folder: $lookAtFolder\n");
  73. continue;
  74. }
  75. // If a new folder has been added we should avoid scanning it
  76. if(!isset($folderinfoList[$lookAtFolder])) {
  77. $this->log("\nSkipping New Folder: $lookAtFolder\n");
  78. continue;
  79. }
  80. // Search for mail in the folder
  81. $mailsearch = $mailbox->search($lookAtFolder);
  82. $this->log($mailsearch? "Total Mails Found in [$lookAtFolder]: " . count($mailsearch) : "No Mails Found in [$lookAtFolder]");
  83. // No emails? Continue with next folder
  84. if(empty($mailsearch)) continue;
  85. // Loop through each of the email searched
  86. foreach($mailsearch as $messageid) {
  87. // Fetch only header part first, based on account lookup fetch the body.
  88. $mailrecord = $mailbox->getMessage($messageid, false);
  89. $mailrecord->debug = $mailbox->debug;
  90. $mailrecord->log();
  91. // If the email is already scanned & rescanning is not set, skip it
  92. if($this->isMessageScanned($mailrecord, $lookAtFolder)) {
  93. $this->log("\nMessage already scanned [$mailrecord->_subject], IGNORING...\n");
  94. unset($mailrecord);
  95. continue;
  96. }
  97. // Apply rules configured for the mailbox
  98. $crmid = false;
  99. foreach($rules as $mailscannerrule) {
  100. $crmid = $this->applyRule($mailscannerrule, $mailrecord, $mailbox, $messageid);
  101. if($crmid) {
  102. break; // Rule was successfully applied and action taken
  103. }
  104. }
  105. // Mark the email message as scanned
  106. $this->markMessageScanned($mailrecord, $crmid);
  107. $mailbox->markMessage($messageid);
  108. /** Free the resources consumed. */
  109. unset($mailrecord);
  110. }
  111. /* Update lastscan for this folder and reset rescan flag */
  112. // TODO: Update lastscan only if all the mail searched was parsed successfully?
  113. $rescanFolderFlag = false;
  114. $this->updateLastScan($lookAtFolder, $rescanFolderFlag);
  115. }
  116. // Close the mailbox at end
  117. $mailbox->close();
  118. }
  119. /**
  120. * Apply all the rules configured for a mailbox on the mailrecord.
  121. */
  122. function applyRule($mailscannerrule, $mailrecord, $mailbox, $messageid) {
  123. // If no actions are set, don't proceed
  124. if(empty($mailscannerrule->actions)) return false;
  125. // Check if rule is defined for the body
  126. $bodyrule = $mailscannerrule->hasBodyRule();
  127. if($bodyrule) {
  128. // We need the body part for rule evaluation
  129. $mailrecord->fetchBody($mailbox->_imap, $messageid);
  130. }
  131. // Apply rule to check if record matches the criteria
  132. $matchresult = $mailscannerrule->applyAll($mailrecord, $bodyrule);
  133. // If record matches the conditions fetch body to take action.
  134. $crmid = false;
  135. if($matchresult) {
  136. $mailrecord->fetchBody($mailbox->_imap, $messageid);
  137. $crmid = $mailscannerrule->takeAction($this, $mailrecord, $matchresult);
  138. }
  139. // Return the CRMID
  140. return $crmid;
  141. }
  142. /**
  143. * Mark the email as scanned.
  144. */
  145. function markMessageScanned($mailrecord, $crmid=false) {
  146. global $adb;
  147. if($crmid === false) $crmid = null;
  148. // TODO Make sure we have unique entry
  149. $adb->pquery("INSERT INTO vtiger_mailscanner_ids(scannerid, messageid, crmid) VALUES(?,?,?)",
  150. Array($this->_scannerinfo->scannerid, $mailrecord->_uniqueid, $crmid));
  151. }
  152. /**
  153. * Check if email was scanned.
  154. */
  155. function isMessageScanned($mailrecord, $lookAtFolder) {
  156. global $adb;
  157. $messages = $adb->pquery("SELECT * FROM vtiger_mailscanner_ids WHERE scannerid=? AND messageid=?",
  158. Array($this->_scannerinfo->scannerid, $mailrecord->_uniqueid));
  159. $folderRescan = $this->_scannerinfo->needRescan($lookAtFolder);
  160. $isScanned = false;
  161. if($adb->num_rows($messages)) {
  162. $isScanned = true;
  163. // If folder is scheduled for rescan and earlier message was not acted upon?
  164. $relatedCRMId = $adb->query_result($messages, 0, 'crmid');
  165. if($folderRescan && empty($relatedCRMId)) {
  166. $adb->pquery("DELETE FROM vtiger_mailscanner_ids WHERE scannerid=? AND messageid=?",
  167. Array($this->_scannerinfo->scannerid, $mailrecord->_uniqueid));
  168. $isScanned = false;
  169. }
  170. }
  171. return $isScanned;
  172. }
  173. /**
  174. * Update last scan on the folder.
  175. */
  176. function updateLastscan($folder) {
  177. $this->_scannerinfo->updateLastscan($folder);
  178. }
  179. /**
  180. * Convert string to integer value.
  181. * @param $strvalue
  182. * @returns false if given contain non-digits, else integer value
  183. */
  184. function __toInteger($strvalue) {
  185. $ival = intval($strvalue);
  186. $intvalstr = "$ival";
  187. if(strlen($strvalue) == strlen($intvalstr)) {
  188. return $ival;
  189. }
  190. return false;
  191. }
  192. /** Lookup functionality. */
  193. var $_cachedContactIds = Array();
  194. var $_cachedAccountIds = Array();
  195. var $_cachedTicketIds = Array();
  196. var $_cachedAccounts = Array();
  197. var $_cachedContacts = Array();
  198. var $_cachedTickets = Array();
  199. /**
  200. * Lookup Contact record based on the email given.
  201. */
  202. function LookupContact($email) {
  203. global $adb;
  204. if($this->_cachedContactIds[$email]) {
  205. $this->log("Reusing Cached Contact Id for email: $email");
  206. return $this->_cachedContactIds[$email];
  207. }
  208. $contactid = false;
  209. $contactres = $adb->pquery("SELECT contactid FROM vtiger_contactdetails WHERE email=?", Array($email));
  210. if($adb->num_rows($contactres)) {
  211. $contactid = $adb->query_result($contactres, 0, 'contactid');
  212. $crmres = $adb->pquery("SELECT deleted FROM vtiger_crmentity WHERE crmid=?", Array($contactid));
  213. if($adb->num_rows($crmres) && $adb->query_result($crmres, 0, 'deleted')) $contactid = false;
  214. }
  215. if($contactid) {
  216. $this->log("Caching Contact Id found for email: $email");
  217. $this->_cachedContactIds[$email] = $contactid;
  218. } else {
  219. $this->log("No matching Contact found for email: $email");
  220. }
  221. return $contactid;
  222. }
  223. /**
  224. * Lookup Account record based on the email given.
  225. */
  226. function LookupAccount($email) {
  227. global $adb;
  228. if($this->_cachedAccountIds[$email]) {
  229. $this->log("Reusing Cached Account Id for email: $email");
  230. return $this->_cachedAccountIds[$email];
  231. }
  232. $accountid = false;
  233. $accountres = $adb->pquery("SELECT accountid FROM vtiger_account WHERE email1=? OR email2=?", Array($email, $email));
  234. if($adb->num_rows($accountres)) {
  235. $accountid = $adb->query_result($accountres, 0, 'accountid');
  236. $crmres = $adb->pquery("SELECT deleted FROM vtiger_crmentity WHERE crmid=?", Array($accountid));
  237. if($adb->num_rows($crmres) && $adb->query_result($crmres, 0, 'deleted')) $accountid = false;
  238. }
  239. if($accountid) {
  240. $this->log("Caching Account Id found for email: $email");
  241. $this->_cachedAccountIds[$email] = $accountid;
  242. } else {
  243. $this->log("No matching Account found for email: $email");
  244. }
  245. return $accountid;
  246. }
  247. /**
  248. * Lookup Ticket record based on the subject or id given.
  249. */
  250. function LookupTicket($subjectOrId) {
  251. global $adb;
  252. $checkTicketId = $this->__toInteger($subjectOrId);
  253. if(!$checkTicketId) {
  254. $ticketres = $adb->pquery("SELECT ticketid FROM vtiger_troubletickets WHERE title = ?", Array($subjectOrId));
  255. if($adb->num_rows($ticketres)) $checkTicketId = $adb->query_result($ticketres, 0, 'ticketid');
  256. }
  257. if(!$checkTicketId) return false;
  258. if($this->_cachedTicketIds[$checkTicketId]) {
  259. $this->log("Reusing Cached Ticket Id for: $subjectOrId");
  260. return $this->_cachedTicketIds[$checkTicketId];
  261. }
  262. $ticketid = false;
  263. if($checkTicketId) {
  264. $crmres = $adb->pquery("SELECT setype, deleted FROM vtiger_crmentity WHERE crmid=?", Array($checkTicketId));
  265. if($adb->num_rows($crmres)) {
  266. if($adb->query_result($crmres, 0, 'setype') == 'HelpDesk' &&
  267. $adb->query_result($crmres, 0, 'deleted') == '0') $ticketid = $checkTicketId;
  268. }
  269. }
  270. if($ticketid) {
  271. $this->log("Caching Ticket Id found for: $subjectOrId");
  272. $this->_cachedTicketIds[$checkTicketId] = $ticketid;
  273. } else {
  274. $this->log("No matching Ticket found for: $subjectOrId");
  275. }
  276. return $ticketid;
  277. }
  278. /**
  279. * Get Account record information based on email.
  280. */
  281. function GetAccountRecord($email) {
  282. require_once('modules/Accounts/Accounts.php');
  283. $accountid = $this->LookupAccount($email);
  284. $account_focus = false;
  285. if($accountid) {
  286. if($this->_cachedAccounts[$accountid]) {
  287. $account_focus = $this->_cachedAccounts[$accountid];
  288. $this->log("Reusing Cached Account [" . $account_focus->column_fields[accountname] . "]");
  289. } else {
  290. $account_focus = new Accounts();
  291. $account_focus->retrieve_entity_info($accountid, 'Accounts');
  292. $account_focus->id = $accountid;
  293. $this->log("Caching Account [" . $account_focus->column_fields[accountname] . "]");
  294. $this->_cachedAccounts[$accountid] = $account_focus;
  295. }
  296. }
  297. return $account_focus;
  298. }
  299. /**
  300. * Get Contact record information based on email.
  301. */
  302. function GetContactRecord($email) {
  303. require_once('modules/Contacts/Contacts.php');
  304. $contactid = $this->LookupContact($email);
  305. $contact_focus = false;
  306. if($contactid) {
  307. if($this->_cachedContacts[$contactid]) {
  308. $contact_focus = $this->_cachedContacts[$contactid];
  309. $this->log("Reusing Cached Contact [" . $contact_focus->column_fields[lastname] .
  310. '-' . $contact_focus->column_fields[firstname] . "]");
  311. } else {
  312. $contact_focus = new Contacts();
  313. $contact_focus->retrieve_entity_info($contactid, 'Contacts');
  314. $contact_focus->id = $contactid;
  315. $this->log("Caching Contact [" . $contact_focus->column_fields[lastname] .
  316. '-' . $contact_focus->column_fields[firstname] . "]");
  317. $this->_cachedContacts[$contactid] = $contact_focus;
  318. }
  319. }
  320. return $contact_focus;
  321. }
  322. /**
  323. * Lookup Contact or Account based on from email and with respect to given CRMID
  324. */
  325. function LookupContactOrAccount($fromemail, $checkWithId=false) {
  326. $recordid = $this->LookupContact($fromemail);
  327. if($checkWithId && $recordid != $checkWithId) {
  328. $recordid = $this->LookupAccount($fromemail);
  329. if($checkWithId && $recordid != $checkWithId) $recordid = false;
  330. }
  331. return $recordid;
  332. }
  333. /**
  334. * Get Ticket record information based on subject or id.
  335. */
  336. function GetTicketRecord($subjectOrId, $fromemail=false) {
  337. require_once('modules/HelpDesk/HelpDesk.php');
  338. $ticketid = $this->LookupTicket($subjectOrId);
  339. $ticket_focus = false;
  340. if($ticketid) {
  341. if($this->_cachedTickets[$ticketid]) {
  342. $ticket_focus = $this->_cachedTickets[$ticketid];
  343. // Check the parentid association if specified.
  344. if($fromemail && !$this->LookupContactOrAccount($fromemail, $ticket_focus->column_fields[parent_id])) {
  345. $ticket_focus = false;
  346. }
  347. if($ticket_focus) {
  348. $this->log("Reusing Cached Ticket [" . $ticket_focus->column_fields[ticket_title] ."]");
  349. }
  350. } else {
  351. $ticket_focus = new HelpDesk();
  352. $ticket_focus->retrieve_entity_info($ticketid, 'HelpDesk');
  353. $ticket_focus->id = $ticketid;
  354. // Check the parentid association if specified.
  355. if($fromemail && !$this->LookupContactOrAccount($fromemail, $ticket_focus->column_fields[parent_id])) {
  356. $ticket_focus = false;
  357. }
  358. if($ticket_focus) {
  359. $this->log("Caching Ticket [" . $ticket_focus->column_fields[ticket_title] . "]");
  360. $this->_cachedTickets[$ticketid] = $ticket_focus;
  361. }
  362. }
  363. }
  364. return $ticket_focus;
  365. }
  366. }
  367. ?>