- 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.
456 lines
17 KiB
PHP
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);
|
|
}
|
|
|
|
}
|
|
|
|
?>
|