Files
crm.clientright.ru/modules/Settings/MailConverter/handlers/MailScannerRule.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

456 lines
17 KiB
PHP

<?php
/*********************************************************************************
** The contents of this file are subject to the vtiger CRM Public License Version 1.0
* ("License"); You may not use this file except in compliance with the License
* The Original Code is: vtiger CRM Open Source
* The Initial Developer of the Original Code is vtiger.
* Portions created by vtiger are Copyright (C) vtiger.
* All Rights Reserved.
*
********************************************************************************/
require_once('modules/Settings/MailConverter/handlers/MailScannerAction.php');
/**
* Scanner Rule
*/
class Vtiger_MailScannerRule {
// id of this instance
var $ruleid = false;
// scanner to which this rule is linked
var $scannerid = false;
// from address criteria
var $fromaddress= false;
// to address criteria
var $toaddress = false;
// cc address criteria
var $cc = false;
// bcc address criteria
var $bcc = false;
// subject criteria operator
var $subjectop = false;
// subject criteria
var $subject = false;
// body criteria operator
var $bodyop = false;
// body criteria
var $body = false;
// order of this rule
var $sequence = false;
// is this action valid
var $isvalid = false;
// match criteria ALL or ANY
var $matchusing = false;
// assigned to user id
var $assigned_to = false;
// associated actions for this rule
var $actions = false;
// TODO we are restricting one action for one rule right now
var $useaction= false;
/** DEBUG functionality **/
var $debug = false;
function log($message) {
global $log;
if($log && $this->debug) { $log->debug($message); }
else if($this->debug) echo "$message\n";
}
/**
* Constructor
*/
function __construct($forruleid) {
$this->initialize($forruleid);
}
/**
* String representation of this instance
*/
function __toString() {
$tostring = '';
$tostring .= "FROM $this->fromaddress, TO $this->toaddress, CC $this->cc, BCC $this->bcc";
$tostring .= ",SUBJECT $this->subjectop $this->subject, BODY $this->bodyop $this->body, MATCH USING, $this->matchusing";
return $tostring;
}
/**
* Initialize this instance
*/
function initialize($forruleid) {
global $adb;
$result = $adb->pquery("SELECT * FROM vtiger_mailscanner_rules WHERE ruleid=? ORDER BY sequence", Array($forruleid));
if ($adb->num_rows($result)) {
$this->ruleid = $adb->query_result($result, 0, 'ruleid');
$this->scannerid = $adb->query_result($result, 0, 'scannerid');
$this->fromaddress = $adb->query_result($result, 0, 'fromaddress');
$this->toaddress = $adb->query_result($result, 0, 'toaddress');
$this->cc = $adb->query_result($result, 0, 'cc');
$this->bcc = $adb->query_result($result, 0, 'bcc');
$this->subjectop = $adb->query_result($result, 0, 'subjectop');
$this->subject = $adb->query_result($result, 0, 'subject');
$this->bodyop = $adb->query_result($result, 0, 'bodyop');
$this->body = $adb->query_result($result, 0, 'body');
$this->sequence = $adb->query_result($result, 0, 'sequence');
$this->matchusing = $adb->query_result($result, 0, 'matchusing');
$this->assigned_to = $adb->query_result($result, 0, 'assigned_to');
$this->isvalid = true;
$this->initializeActions();
// At present we support only one action for a rule
if(!empty($this->actions)) $this->useaction = $this->actions[0];
}
}
/**
* Initialize the actions
*/
function initializeActions() {
global $adb;
if($this->ruleid) {
$this->actions = Array();
$actionres = $adb->pquery("SELECT actionid FROM vtiger_mailscanner_ruleactions WHERE ruleid=?",Array($this->ruleid));
$actioncount = $adb->num_rows($actionres);
if($actioncount) {
for($index = 0; $index < $actioncount; ++$index) {
$actionid = $adb->query_result($actionres, $index, 'actionid');
$ruleaction = new Vtiger_MailScannerAction($actionid);
$ruleaction->debug = $this->debug;
$this->actions[] = $ruleaction;
}
}
}
}
/**
* Is body rule defined?
*/
function hasBodyRule() {
return (!empty($this->bodyop));
}
/**
* Check if the rule criteria is matching
*/
function isMatching($matchfound1, $matchfound2 = null) {
if ($matchfound2 === null)
return $matchfound1;
if ($this->matchusing == 'AND')
return ($matchfound1 && $matchfound2);
if ($this->matchusing == 'OR')
return ($matchfound1 || $matchfound2);
return false;
}
/**
* Apply all the criteria.
* @param $mailrecord
* @param $includingBody
* @returns false if not match is found or else all matching result found
*/
function applyAll($mailrecord, $includingBody=true) {
$matchresults = Array();
$matchfound = null;
if ($this->hasACondition()) {
$subrules = Array('FROM', 'TO', 'CC', 'BCC', 'SUBJECT', 'BODY');
foreach ($subrules as $subrule) {
// Body rule could be defered later to improve performance
// in that case skip it.
if($subrule == 'BODY' && !$includingBody) continue;
$checkmatch = $this->apply($subrule, $mailrecord);
$matchfound = $this->isMatching($checkmatch, $matchfound);
// Collect matching result array
if ($matchfound && is_array($checkmatch))
$matchresults[] = $checkmatch;
}
} else {
$matchfound = false;
if($this->matchusing == 'OR') {
$matchfound = true;
$matchresults[] = $this->__CreateMatchResult('BLANK','','','');
}
}
return ($matchfound)? $matchresults : false;
}
/**
* Check if at least one condition is set for this rule.
*/
function hasACondition() {
$hasFromAddress = $this->fromaddress ? true : false;
$hasToAddress = $this->toaddress ? true : false;
$hasCcAddress = $this->cc ? true : false;
$hasBccAddress = $this->bcc ? true : false;
$hasSubjectOp = $this->subjectop ? true : false;
$hasBodyOp = $this->bodyop ? true : false;
return ($hasFromAddress || $hasToAddress || $hasCcAddress || $hasBccAddress || $hasSubjectOp || $hasBodyOp);
}
/**
* Apply required condition on the mail record.
*/
function apply($subrule, $mailrecord) {
$matchfound = false;
if($this->isvalid) {
switch(strtoupper($subrule)) {
case 'FROM':
if($this->fromaddress) {
if(strpos($this->fromaddress, '*') == 0)
$this->fromaddress = trim($this->fromaddress, '*');
$matchfound = $this->find($subrule, 'Contains', $mailrecord->_from[0], $this->fromaddress);
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
case 'TO':
if($this->toaddress) {
foreach($mailrecord->_to as $toemail) {
$matchfound = $this->find($subrule, 'Contains', $toemail, $this->toaddress);
if($matchfound) break;
}
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
case 'CC':
if ($this->cc) {
foreach ($mailrecord->_cc as $toemail) {
$matchfound = $this->find($subrule, 'Contains', $toemail, $this->cc);
if ($matchfound)
break;
}
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
case 'BCC':
if ($this->bcc) {
foreach ($mailrecord->_bcc as $toemail) {
$matchfound = $this->find($subrule, 'Contains', $toemail, $this->bcc);
if ($matchfound)
break;
}
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
case 'SUBJECT':
if ($this->subjectop) {
$matchfound = $this->find($subrule, $this->subjectop, $mailrecord->_subject, $this->subject);
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
case 'BODY':
if ($this->bodyop) {
$matchfound = $this->find($subrule, $this->bodyop, trim(strip_tags($mailrecord->_body)), trim($this->body));
} else {
$matchfound = $this->__CreateDefaultMatchResult($subrule);
}
break;
}
}
return $matchfound;
}
/**
* Find if the rule matches based on condition and parameters
*/
function find($subrule, $condition, $input, $searchfor) {
if (!$input)
return false;
$input = trim(preg_replace("/\r/", '', decode_html($input)));
$searchfor = decode_html($searchfor);
$matchfound = false;
$matches = false;
switch ($condition) {
case 'Contains':
$matchfound = stripos($input, $searchfor);
$matchfound = ($matchfound !== FALSE);
$matches = $searchfor;
break;
case 'Not Contains':
$matchfound = stripos($input, $searchfor);
$matchfound = ($matchfound === FALSE);
$matches = $searchfor;
break;
case 'Equals':
$matchfound = strcasecmp($input, $searchfor);
$matchfound = ($matchfound === 0);
$matches = $searchfor;
break;
case 'Not Equals':
$matchfound = strcasecmp($input, $searchfor);
$matchfound = ($matchfound !== 0);
$matches = $searchfor;
break;
case 'Begins With':
$matchfound = stripos($input, $searchfor);
$matchfound = ($matchfound === 0);
$matches = $searchfor;
break;
case 'Ends With':
$matchfound = strripos($input, $searchfor);
$matchfound = ($matchfound === strlen($input) - strlen($searchfor));
$matches = $searchfor;
break;
case 'Regex':
$regmatches = Array();
$matchfound = false;
$searchfor = str_replace('/', '\/', $searchfor);
$input = str_replace("_", " ", $input);
if (preg_match("/$searchfor/i", $input, $regmatches)) {
// Pick the last matching group
$matches = $regmatches[count($regmatches) - 1];
$matchfound = true;
}
break;
case 'Has Ticket Number':
$regmatches = Array();
$matchfound = false;
$searchfor = "Ticket Id[^:]?: ([0-9]+)";
$searchfor = str_replace('/', '\/', $searchfor);
if (preg_match("/$searchfor/i", $input, $regmatches)) {
// Pick the last matching group
$matches = $regmatches[count($regmatches) - 1];
$matchfound = true;
}
break;
}
if($matchfound) $matchfound = $this->__CreateMatchResult($subrule, $condition, $searchfor, $matches);
return $matchfound;
}
/**
* Create matching result for the subrule.
*/
function __CreateMatchResult($subrule, $condition, $searchfor, $matches) {
return Array( 'subrule' => $subrule, 'condition' => $condition, 'searchfor' => $searchfor, 'matches' => $matches);
}
/**
* Create default success matching result
*/
function __CreateDefaultMatchResult($subrule) {
if($this->matchusing == 'OR') return false;
if($this->matchusing == 'AND') return $this->__CreateMatchResult($subrule, 'Contains', '', '');
}
/**
* Detect if the rule match result has Regex condition
* @param $matchresult result of apply obtained earlier
* @returns matchinfo if Regex match is found, false otherwise
*/
function hasRegexMatch($matchresult) {
foreach($matchresult as $matchinfo) {
$match_condition = $matchinfo['condition'];
$match_string = $matchinfo['matches'];
if(($match_condition == 'Regex' || $match_condition == 'Has Ticket Number') && $match_string)
return $matchinfo;
}
return false;
}
/**
* Swap (reset) sequence of two rules.
*/
static function resetSequence($ruleid1, $ruleid2) {
global $adb;
$ruleresult = $adb->pquery("SELECT ruleid, sequence FROM vtiger_mailscanner_rules WHERE ruleid = ? or ruleid = ?",
Array($ruleid1, $ruleid2));
$rule_partinfo = Array();
if($adb->num_rows($ruleresult) != 2) {
return false;
} else {
$rule_partinfo[$adb->query_result($ruleresult, 0, 'ruleid')] = $adb->query_result($ruleresult, 0, 'sequence');
$rule_partinfo[$adb->query_result($ruleresult, 1, 'ruleid')] = $adb->query_result($ruleresult, 1, 'sequence');
$adb->pquery("UPDATE vtiger_mailscanner_rules SET sequence = ? WHERE ruleid = ?", Array($rule_partinfo[$ruleid2], $ruleid1));
$adb->pquery("UPDATE vtiger_mailscanner_rules SET sequence = ? WHERE ruleid = ?", Array($rule_partinfo[$ruleid1], $ruleid2));
}
}
/**
* Update rule information in database.
*/
function update() {
global $adb;
if ($this->ruleid) {
$adb->pquery("UPDATE vtiger_mailscanner_rules SET scannerid=?,fromaddress=?,toaddress=?,subjectop=?,subject=?,bodyop=?,body=?,matchusing=?,assigned_to=?,cc=?,bcc=?
WHERE ruleid=?", Array($this->scannerid, $this->fromaddress, $this->toaddress, $this->subjectop, $this->subject,
$this->bodyop, $this->body, $this->matchusing, $this->assigned_to, $this->cc, $this->bcc, $this->ruleid));
} else {
$this->sequence = $this->__nextsequence();
$adb->pquery("INSERT INTO vtiger_mailscanner_rules(scannerid,fromaddress,toaddress,subjectop,subject,bodyop,body,matchusing,sequence,assigned_to,cc,bcc)
VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", Array($this->scannerid, $this->fromaddress, $this->toaddress, $this->subjectop, $this->subject,
$this->bodyop, $this->body, $this->matchusing, $this->sequence, $this->assigned_to, $this->cc, $this->bcc));
$this->ruleid = $adb->database->Insert_ID();
}
}
/**
* Get next sequence to use
*/
function __nextsequence() {
global $adb;
$seqres = $adb->pquery("SELECT max(sequence) AS max_sequence FROM vtiger_mailscanner_rules", Array());
$maxsequence = 0;
if($adb->num_rows($seqres)) {
$maxsequence = $adb->query_result($seqres, 0, 'max_sequence');
}
++$maxsequence;
return $maxsequence;
}
/**
* Delete the rule and associated information.
*/
function delete() {
global $adb;
// Delete dependencies
if(!empty($this->actions)) {
foreach($this->actions as $action) {
$action->delete();
}
}
if($this->ruleid) {
$adb->pquery("DELETE FROM vtiger_mailscanner_ruleactions WHERE ruleid = ?", Array($this->ruleid));
$adb->pquery("DELETE FROM vtiger_mailscanner_rules WHERE ruleid=?", Array($this->ruleid));
}
}
/**
* Update action linked to the rule.
*/
function updateAction($actionid, $actiontext) {
$action = $this->useaction;
if($actionid != '' && $actiontext == '') {
if($action) $action->delete();
} else {
if($actionid == '') {
$action = new Vtiger_MailScannerAction($actionid);
}
$action->scannerid = $this->scannerid;
$action->update($this->ruleid, $actiontext);
}
}
/**
* Take action on mail record
*/
function takeAction($mailscanner, $mailrecord, $matchresult) {
if(empty($this->actions)) return false;
$action = $this->useaction; // Action is limited to One right now
return $action->apply($mailscanner, $mailrecord, $this, $matchresult);
}
}
?>