OSSMailScanner_Record_Model::isConnection()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/**
4
 * OSSMailScanner Record model class.
5
 *
6
 * @copyright YetiForce S.A.
7
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
8
 * @author    Mariusz Krzaczkowski <[email protected]>
9
 * @author    Radosław Skrzypczak <[email protected]>
10
 */
11
class OSSMailScanner_Record_Model extends Vtiger_Record_Model
12
{
13
	/**
14
	 * Main folders array.
15
	 *
16
	 * @var array
17
	 */
18
	public static $mainFolders = ['Received', 'Sent', 'Spam', 'Trash', 'All'];
19
20
	/**
21
	 * Returns array list of actions.
22
	 *
23
	 * @return array
24
	 */
25
	public static function getActionsList()
26
	{
27
		$accountsPriority = ['CreatedEmail', 'CreatedHelpDesk', 'BindAccounts', 'BindContacts', 'BindLeads', 'BindHelpDesk', 'BindSSalesProcesses'];
28
		$moduleModel = Vtiger_Module_Model::getInstance('OSSMailScanner');
29
		$iterator = new DirectoryIterator($moduleModel->actionsDir);
30
		$actions = [];
31
		foreach ($iterator as $i => $fileInfo) {
32
			if (!$fileInfo->isDot() && 'php' === $fileInfo->getExtension()) {
33
				$action = trim($fileInfo->getBasename('.php'));
34
				$key = array_search($action, $accountsPriority);
35
				if (false === $key) {
36
					$key = $i + 100;
37
				}
38
				$actions[$key] = $action;
39
			}
40
		}
41
		ksort($actions);
42
43
		return $actions;
44
	}
45
46
	/**
47
	 * Return user identities.
48
	 *
49
	 * @param int $id
50
	 *
51
	 * @return array
52 1
	 */
53
	public static function getIdentities($id = 0)
54 1
	{
55 1
		if (App\Cache::staticHas('OSSMailScanner_Record_Model::getIdentities', $id)) {
56
			return App\Cache::staticGet('OSSMailScanner_Record_Model::getIdentities', $id);
57 1
		}
58 1
		$query = (new \App\Db\Query())->select(['name', 'email', 'identity_id'])->from('roundcube_identities');
59 1
		if ($id) {
60
			$query = $query->where(['user_id' => $id]);
61 1
		}
62 1
		$rows = $query->all();
63 1
		App\Cache::staticSave('OSSMailScanner_Record_Model::getIdentities', $id, $rows);
64
		return $rows;
65
	}
66
67
	/**
68
	 * Delete identity by id.
69
	 *
70
	 * @param int $id
71
	 */
72
	public function deleteIdentities($id)
73
	{
74
		\App\Db::getInstance()->createCommand()->delete('roundcube_identities', ['identity_id' => $id])->execute();
75
	}
76
77
	/**
78
	 * Return email  actions name list.
79
	 *
80
	 * @param array $data
81
	 *
82
	 * @return array
83
	 */
84
	public static function getEmailActionsListName($data)
85
	{
86
		$return = [];
87
		foreach ($data as $row) {
88
			if ('files' == $row[0]) {
89
				$return[] = [$row[1], $row[1]];
90
			} else {
91
				foreach ($row[2] as $row_dir) {
92
					$return[] = [$row_dir[1], $row[1] . '|' . $row_dir[1]];
93
				}
94
			}
95
		}
96
		return $return;
97
	}
98
99
	/**
100
	 * Update user actions.
101
	 *
102
	 * @param int    $userid
103
	 * @param string $value
104
	 */
105
	public static function setActions($userid, $value)
106
	{
107
		\App\Db::getInstance()->createCommand()
108
			->update('roundcube_users', [
109
				'actions' => $value,
110
			], ['user_id' => $userid])
111
			->execute();
112
	}
113
114
	/**
115
	 * Update folder list for user.
116
	 *
117
	 * @param int   $user
118
	 * @param array $foldersByType
119
	 */
120
	public static function setFolderList($user, $foldersByType)
121
	{
122
		$dbCommand = \App\Db::getInstance()->createCommand();
123
		$oldFoldersByType = (new \App\Db\Query())->select(['type', 'folder'])->from('vtiger_ossmailscanner_folders_uid')->where(['user_id' => $user])->createCommand()->queryAllByGroup(2);
124
		foreach (self::$mainFolders as $type) {
125
			$toRemove = $toAdd = $oldFolders = $folders = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $toAdd is dead and can be removed.
Loading history...
Unused Code introduced by
The assignment to $toRemove is dead and can be removed.
Loading history...
126
			if (isset($oldFoldersByType[$type])) {
127
				$oldFolders = $oldFoldersByType[$type];
128
			}
129
			if (isset($foldersByType[$type])) {
130
				$folders = $foldersByType[$type];
131
			}
132
133
			$toAdd = array_diff_assoc($folders, $oldFolders);
134
			$toRemove = array_diff_assoc($oldFolders, $folders);
135
			foreach ($toRemove as $folder) {
136
				$dbCommand->delete('vtiger_ossmailscanner_folders_uid', ['user_id' => $user, 'type' => $type, 'folder' => html_entity_decode($folder)])->execute();
137
			}
138
			foreach ($toAdd as $folder) {
139
				$dbCommand->insert('vtiger_ossmailscanner_folders_uid', [
140
					'user_id' => $user,
141
					'type' => $type,
142
					'folder' => html_entity_decode($folder),
143
				])->execute();
144
			}
145
		}
146
	}
147
148
	/**
149
	 * Return folders config.
150
	 *
151
	 * @param bool|string $folder
152
	 *
153
	 * @return array|string
154
	 */
155
	public static function getConfigFolderList($folder = false)
156
	{
157
		if ($folder) {
158
			return (new \App\Db\Query())->select(['parameter'])->from('vtiger_ossmailscanner_config')->where(['and', ['conf_type' => 'folders'], ['like', 'value', $folder]])->orderBy('parameter')->scalar();
0 ignored issues
show
Bug Best Practice introduced by
The expression return new App\Db\Query(...('parameter')->scalar() could also return false which is incompatible with the documented return type array|string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
159
		}
160
		return (new \App\Db\Query())->select(['parameter', 'value'])->from('vtiger_ossmailscanner_config')->where(['conf_type' => 'folders'])->orderBy(['parameter' => SORT_DESC])->createCommand()->queryAllByGroup(0);
161
	}
162
163
	/**
164
	 * Return mailscanner config.
165
	 *
166
	 * @param bool|string $confType
167
	 *
168
	 * @return array
169 1
	 */
170
	public static function getConfig($confType)
171 1
	{
172 1
		$query = (new \App\Db\Query())->from('vtiger_ossmailscanner_config');
173 1
		if (false !== $confType) {
174
			$query->where(['conf_type' => $confType]);
175 1
		}
176 1
		$query->orderBy(['parameter' => SORT_DESC]);
177 1
		$dataReader = $query->createCommand()->query();
178 1
		$return = [];
179 1
		while ($row = $dataReader->read()) {
180 1
			if (false !== $confType) {
181
				$return[$row['parameter']] = $row['value'];
182
			} else {
183
				$return[$row['conf_type']][$row['parameter']] = $row['value'];
184
			}
185 1
		}
186
		$dataReader->close();
187 1
188
		return $return;
189
	}
190
191
	/**
192
	 * Update config widget param.
193
	 *
194
	 * @param string $confType
195
	 * @param string $type
196
	 * @param string $value
197
	 *
198
	 * @return string
199
	 */
200
	public function setConfigWidget($confType, $type, $value)
201
	{
202
		if (null === $value || 'null' === $value) {
203
			$value = null;
204
		}
205
		App\Db::getInstance()->createCommand()->update('vtiger_ossmailscanner_config', ['value' => $value], ['conf_type' => $confType, 'parameter' => $type])->execute();
206
207
		return App\Language::translate('LBL_SAVE', 'OSSMailScanner');
208
	}
209
210
	/**
211
	 * Returns folder type.
212
	 *
213
	 * @param string $folder
214
	 *
215
	 * @return int
216
	 */
217
	public static function getTypeFolder($folder)
218
	{
219
		switch ($folder) {
220
			case 'Received':
221
				$return = 0;
222
				break;
223
			case 'Sent':
224
				$return = 1;
225
				break;
226
			case 'Spam':
227
				$return = 2;
228
				break;
229
			case 'Trash':
230
				$return = 3;
231
				break;
232
			case 'All':
233
				$return = 4;
234
				break;
235
			default:
236
				break;
237
		}
238
		return $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return does not seem to be defined for all execution paths leading up to this point.
Loading history...
239
	}
240
241
	/**
242
	 * Return folder UID.
243
	 *
244
	 * @param int    $accountId
245
	 * @param string $folder
246
	 *
247
	 * @return int
248 1
	 */
249
	public static function getUidFolder($accountId, $folder)
250 1
	{
251 1
		$rows = (new \App\Db\Query())->select(['uid', 'folder'])->from('vtiger_ossmailscanner_folders_uid')->where(['user_id' => $accountId, 'folder' => $folder])->createCommand()->query();
252 1
		while ($row = $rows->read()) {
253 1
			if ($folder === $row['folder']) {
254
				return $row['uid'];
255
			}
256
		}
257
		return 0;
258
	}
259
260
	/**
261
	 * Return user folders.
262
	 *
263
	 * @param int $accountId
264
	 *
265
	 * @return array
266 1
	 */
267
	public function getFolders($accountId)
268 1
	{
269
		return (new \App\Db\Query())->from('vtiger_ossmailscanner_folders_uid')->where(['user_id' => $accountId])->createCommand()->queryAll();
270
	}
271
272
	/**
273
	 * @param int                $account
274
	 * @param OSSMail_Mail_Model $mail
275
	 * @param string             $folder
276
	 * @param array              $params
277
	 *
278
	 * @return array
279 1
	 */
280
	public static function executeActions($account, OSSMail_Mail_Model $mail, $folder, $params = false)
281 1
	{
282
		\App\Log::trace('Start execute actions: ' . $account['username'], 'MailScanner');
283 1
284 1
		$actions = [];
285
		if ($params && \array_key_exists('actions', $params)) {
286 1
			$actions = $params['actions'];
287
		} elseif (\is_string($account['actions'])) {
288
			$actions = explode(',', $account['actions']);
289 1
		} else {
290
			$actions = $account['actions'];
291 1
		}
292 1
		$mail->setAccount($account);
0 ignored issues
show
Bug introduced by
$account of type integer is incompatible with the type array expected by parameter $account of OSSMail_Mail_Model::setAccount(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

292
		$mail->setAccount(/** @scrutinizer ignore-type */ $account);
Loading history...
293 1
		$mail->setFolder($folder);
294 1
		foreach ($actions as &$action) {
295 1
			$handlerClass = Vtiger_Loader::getComponentClassName('ScannerAction', $action, 'OSSMailScanner');
296 1
			$handler = new $handlerClass();
297 1
			if ($handler) {
298
				\App\Log::trace('Start action: ' . $action, 'MailScanner');
299 1
				try {
300
					$mail->addActionResult($action, $handler->process($mail));
301
				} catch (Exception $e) {
302
					App\Log::error("Action: $action  Folder: $folder ID:" . $mail->get('id') . PHP_EOL . $e->__toString(), 'MailScanner');
303 1
				}
304
				\App\Log::trace('End action', 'MailScanner');
305
			}
306 1
		}
307 1
		$mail->postProcess();
308
		\App\Log::trace('End execute actions', 'MailScanner');
309 1
310
		return $mail->getActionResult();
311
	}
312
313
	/**
314
	 * Manually scan mail.
315
	 *
316
	 * @param int    $uid
317
	 * @param string $folder
318
	 * @param array  $account
319
	 *
320
	 * @throws \App\Exceptions\AppException
321
	 *
322
	 * @return array
323
	 */
324
	public function manualScanMail(int $uid, string $folder, array $account)
325
	{
326
		$imapFolder = \App\Utils::convertCharacterEncoding($folder, 'UTF-8', 'UTF7-IMAP');
327
		$mbox = \OSSMail_Record_Model::imapConnect($account['username'], \App\Encryption::getInstance()->decrypt($account['password']), $account['mail_host'], $imapFolder);
328
		$mail = OSSMail_Record_Model::getMail($mbox, $uid);
0 ignored issues
show
Bug introduced by
$mbox of type IMAP\Connection|false is incompatible with the type resource expected by parameter $mbox of OSSMail_Record_Model::getMail(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
		$mail = OSSMail_Record_Model::getMail(/** @scrutinizer ignore-type */ $mbox, $uid);
Loading history...
329
		if (!$mail) {
330
			return [];
331
		}
332
		$params = [];
333
		if (empty($account['actions'])) {
334
			$params['actions'] = ['CreatedEmail', 'BindAccounts', 'BindContacts', 'BindLeads'];
335
		} else {
336
			if (\is_string($account['actions'])) {
337
				$actions = explode(',', $account['actions']);
338
			} else {
339
				$actions = $account['actions'];
340
			}
341
			if (!\in_array('CreatedEmail', $actions)) {
342
				array_unshift($actions, 'CreatedEmail', );
343
			}
344
			$params['actions'] = $actions;
345
		}
346
		$return = self::executeActions($account, $mail, $folder, $params);
347
		unset($mail);
348
		return $return;
349
	}
350
351
	/**
352
	 * Scan mailbox for emails.
353
	 *
354
	 * @param resource $mbox
355 1
	 * @param array    $account
356
	 * @param string   $folder      Character encoding UTF-8
357 1
	 * @param int      $scan_id
358 1
	 * @param int      $countEmails
359 1
	 *
360 1
	 * @throws \ReflectionException
361 1
	 * @throws \yii\db\Exception
362 1
	 *
363 1
	 * @return array
364 1
	 */
365 1
	public static function mailScan($mbox, array $account, string $folder, int $scan_id, int $countEmails)
366
	{
367 1
		$break = false;
368
		$lastScanUid = self::getUidFolder($account['user_id'], $folder);
369
		\App\Log::beginProfile(__METHOD__ . '|imap_msgno', 'Mail|IMAP');
370
		$msgno = $lastScanUid ? imap_msgno($mbox, $lastScanUid) : 0;
371
		\App\Log::endProfile(__METHOD__ . '|imap_msgno', 'Mail|IMAP');
372
		\App\Log::beginProfile(__METHOD__ . '|imap_num_msg', 'Mail|IMAP');
373
		$numMsg = imap_num_msg($mbox);
374
		\App\Log::endProfile(__METHOD__ . '|imap_num_msg', 'Mail|IMAP');
375
		$getEmails = false;
376
		if (0 === $msgno && 0 !== $numMsg) {
377
			if (0 === $lastScanUid) {
378
				$msgno = 1;
379 1
				$getEmails = true;
380 1
			} elseif (imap_uid($mbox, $numMsg) > $lastScanUid) {
381 1
				foreach (imap_search($mbox, 'ALL', SE_UID) as $uid) {
382 1
					if ($uid > $lastScanUid) {
383 1
						\App\Log::beginProfile(__METHOD__ . '|imap_msgno', 'Mail|IMAP');
384
						$msgno = imap_msgno($mbox, $uid);
385 1
						\App\Log::endProfile(__METHOD__ . '|imap_msgno', 'Mail|IMAP');
386 1
						$getEmails = true;
387 1
						break;
388 1
					}
389 1
				}
390 1
			}
391
		} elseif ($msgno < $numMsg) {
392
			++$msgno;
393
			$getEmails = true;
394
		}
395
		if ($getEmails) {
396 1
			$dbCommand = \App\Db::getInstance()->createCommand();
397
			for ($i = $msgno; $i <= $numMsg; ++$i) {
398
				\App\Log::beginProfile(__METHOD__ . '|imap_uid', 'Mail|IMAP');
399
				$uid = imap_uid($mbox, $i);
400
				\App\Log::endProfile(__METHOD__ . '|imap_uid', 'Mail|IMAP');
401
				$mail = OSSMail_Record_Model::getMail($mbox, $uid, $i);
402
403
				self::executeActions($account, $mail, $folder);
0 ignored issues
show
Bug introduced by
$account of type array is incompatible with the type integer expected by parameter $account of OSSMailScanner_Record_Model::executeActions(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

403
				self::executeActions(/** @scrutinizer ignore-type */ $account, $mail, $folder);
Loading history...
404
				unset($mail);
405
				$dbCommand->update('vtiger_ossmailscanner_folders_uid', ['uid' => $uid], ['user_id' => $account['user_id'], 'folder' => $folder])->execute();
406
				++$countEmails;
407
				if (!self::updateScanHistory($scan_id, ['status' => '1', 'count' => $countEmails, 'action' => 'Action_CronMailScanner'])
408
					|| $countEmails >= \App\Config::performance('NUMBERS_EMAILS_DOWNLOADED_DURING_ONE_SCANNING')) {
409
					$break = true;
410
					break;
411
				}
412
			}
413
		}
414
		return ['count' => $countEmails, 'break' => $break];
415
	}
416
417
	/**
418
	 * Return email search results.
419
	 *
420
	 * @param string $module
421
	 * @param mixed  $onlyMailUitype
422
	 *
423
	 * @return array
424
	 */
425
	public static function getEmailSearch($module = false, $onlyMailUitype = true)
426
	{
427
		$return = [];
428
		$query = (new App\Db\Query())->from('vtiger_field')
429
			->leftJoin('vtiger_tab', 'vtiger_tab.tabid = vtiger_field.tabid')
430
			->where(['and', ['uitype' => ($onlyMailUitype ? 13 : [13, 319])], ['<>', 'vtiger_field.presence', 1], ['<>', 'vtiger_tab.name', 'Users']]);
431
		if ($module) {
432
			$query->andWhere(['vtiger_tab.name' => $module]);
433
		}
434
		$query->orderBy('name');
435
		$dataReader = $query->createCommand()->query();
436
		while ($row = $dataReader->read()) {
437
			$return[] = [
438
				'key' => "{$row['fieldname']}={$row['name']}={$row['uitype']}",
439 1
				'value' => "{$row['fieldname']}={$row['name']}", // item to delete in next version
440
				'fieldlabel' => $row['fieldlabel'],
441 1
				'tablename' => $row['tablename'],
442 1
				'columnname' => $row['columnname'],
443 1
				'name' => $row['name'],
444 1
				'tabid' => $row['tabid'],
445
				'fieldname' => $row['fieldname'],
446 1
			];
447 1
		}
448 1
		$dataReader->close();
449 1
		return $return;
450 1
	}
451
452
	/**
453 1
	 * Return email search list.
454
	 *
455 1
	 * @return array
456
	 */
457
	public static function getEmailSearchList()
458
	{
459
		$cacheKey = 'Mail';
460
		$cacheValue = 'EmailSearchList';
461
		if (\App\Cache::staticHas($cacheKey, $cacheValue)) {
462
			return \App\Cache::staticGet($cacheKey, $cacheValue);
463
		}
464
		$return = [];
465
		$value = (new \App\Db\Query())->select(['value'])->from('vtiger_ossmailscanner_config')
466
			->where(['conf_type' => 'emailsearch', 'parameter' => 'fields'])
467
			->scalar();
468
		if (!empty($value)) {
469
			$return = explode(',', $value);
470
		}
471
		\App\Cache::staticSave($cacheKey, $cacheValue, $return);
472
		return $return;
473
	}
474
475
	/**
476
	 * Set email search list.
477
	 *
478
	 * @param string $value
479
	 */
480
	public static function setEmailSearchList($value)
481
	{
482
		$dbCommand = App\Db::getInstance()->createCommand();
483
		if (null === $value || 'null' == $value) {
484
			$dbCommand->update('vtiger_ossmailscanner_config', ['value' => ''], ['conf_type' => 'emailsearch', 'parameter' => 'fields'])->execute();
485
		} else {
486
			$isExists = (new App\Db\Query())->from('vtiger_ossmailscanner_config')->where(['conf_type' => 'emailsearch', 'parameter' => 'fields'])->exists();
487
			if (!$isExists) {
488
				$dbCommand->insert('vtiger_ossmailscanner_config', [
489
					'conf_type' => 'emailsearch',
490 1
					'parameter' => 'fields',
491
					'value' => $value,
492 1
				])->execute();
493 1
			} else {
494
				$dbCommand->update('vtiger_ossmailscanner_config', ['value' => $value], ['conf_type' => 'emailsearch', 'parameter' => 'fields'])->execute();
495 1
			}
496
		}
497 1
	}
498
499
	/**
500 1
	 * Merge arrays.
501
	 *
502
	 * @param array $tab1
503
	 * @param array $tab2
504
	 *
505
	 * @return array
506
	 */
507
	public static function mergeArray($tab1, $tab2)
508
	{
509
		$return = [];
510
		if (0 != \count($tab1) && 0 != \count($tab2)) {
511
			$return = array_unique(array_merge($tab1, $tab2));
512
		} elseif (0 != \count($tab1)) {
513
			$return = $tab1;
514
		} elseif (0 != \count($tab2)) {
515
			$return = $tab2;
516
		}
517
		return $return;
518
	}
519
520 1
	/**
521
	 * The function returns information about OSSMailScanner Crons.
522 1
	 *
523 1
	 * @return array
524
	 */
525
	public static function getCron()
526
	{
527 1
		return (new App\Db\Query())->select(['name', 'status', 'frequency'])->from('vtiger_cron_task')->where(['module' => 'OSSMailScanner'])->createCommand()->queryAll();
528 1
	}
529
530
	/**
531
	 * Execute cron task.
532 1
	 *
533 1
	 * @param int $whoTrigger
534 1
	 *
535 1
	 * @return bool|string
536 1
	 */
537 1
	public function executeCron($whoTrigger)
538
	{
539
		\App\Log::trace('Start executeCron');
540 1
		if (!self::isPermissionToScan($whoTrigger)) {
541 1
			\App\Log::warning(\App\Language::translate('ERROR_ACTIVE_CRON', 'OSSMailScanner'));
542 1
			return \App\Language::translate('ERROR_ACTIVE_CRON', 'OSSMailScanner');
543
		}
544 1
		$accounts = OSSMail_Record_Model::getAccountsList();
545 1
		if (!$accounts) {
546 1
			\App\Log::info('There are no accounts to be scanned');
547 1
			return false;
548 1
		}
549 1
		$scannerModel = Vtiger_Record_Model::getCleanInstance('OSSMailScanner');
550
		$countEmails = 0;
551 1
		$scanId = $scannerModel->addScanHistory(['user' => $whoTrigger]);
0 ignored issues
show
Bug introduced by
The method addScanHistory() does not exist on Vtiger_Record_Model. It seems like you code against a sub-type of Vtiger_Record_Model such as OSSMailScanner_Record_Model. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

551
		/** @scrutinizer ignore-call */ 
552
  $scanId = $scannerModel->addScanHistory(['user' => $whoTrigger]);
Loading history...
552
		foreach ($accounts as $account) {
553
			\App\Log::trace('Start checking account: ' . $account['username']);
554
			if (empty($account['actions']) || !$this->isConnection($account) || empty($folders = $scannerModel->getFolders($account['user_id']))) {
0 ignored issues
show
Bug introduced by
The method getFolders() does not exist on Vtiger_Record_Model. It seems like you code against a sub-type of Vtiger_Record_Model such as OSSMailScanner_Record_Model or OSSMail_Record_Model. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

554
			if (empty($account['actions']) || !$this->isConnection($account) || empty($folders = $scannerModel->/** @scrutinizer ignore-call */ getFolders($account['user_id']))) {
Loading history...
555
				continue;
556
			}
557
			foreach ($folders as $folderRow) {
558 1
				$folder = \App\Utils::convertCharacterEncoding($folderRow['folder'], 'UTF-8', 'UTF7-IMAP');
559 1
				\App\Log::trace('Start checking folder: ' . $folder);
560
				$mbox = \OSSMail_Record_Model::imapConnect($account['username'], \App\Encryption::getInstance()->decrypt($account['password']), $account['mail_host'], $folder, false, [], $account);
561 1
				if (\is_resource($mbox)) {
562
					$scanSummary = $scannerModel->mailScan($mbox, $account, $folderRow['folder'], $scanId, $countEmails);
0 ignored issues
show
Bug introduced by
The method mailScan() does not exist on Vtiger_Record_Model. It seems like you code against a sub-type of Vtiger_Record_Model such as OSSMailScanner_Record_Model. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

562
					/** @scrutinizer ignore-call */ 
563
     $scanSummary = $scannerModel->mailScan($mbox, $account, $folderRow['folder'], $scanId, $countEmails);
Loading history...
563
					$countEmails = $scanSummary['count'];
564
					if ($scanSummary['break']) {
565
						\App\Log::info('Reached the maximum number of scanned mails');
566
						break 2;
567
					}
568
				} else {
569
					\App\Log::error("Incorrect mail access data, username: {$account['username']} , folder: $folder , type: {$folderRow['type']} ,  Error: " . imap_last_error());
570
				}
571 1
			}
572
		}
573 1
		self::updateScanHistory($scanId, ['status' => '0', 'count' => $countEmails, 'action' => 'Action_CronMailScanner']);
574
		\App\Log::trace('End executeCron');
575 1
		return 'ok';
576 1
	}
577 1
578 1
	/**
579
	 * Function checks connection to mailbox.
580
	 *
581
	 * @param array $account
582
	 *
583 1
	 * @return bool
584
	 */
585
	public function isConnection(array $account)
586
	{
587
		$result = false;
588
		$mbox = \OSSMail_Record_Model::imapConnect($account['username'], \App\Encryption::getInstance()->decrypt($account['password']), $account['mail_host'], '', false, [], $account);
589
		if (\is_resource($mbox)) {
0 ignored issues
show
introduced by
The condition is_resource($mbox) is always false.
Loading history...
590
			$result = true;
591
		}
592
		return $result;
593
	}
594
595
	/**
596
	 * Return history status label.
597
	 *
598
	 * @param int $id
599
	 *
600
	 * @return string
601
	 */
602
	public function getHistoryStatus($id)
603
	{
604
		switch ($id) {
605
			case 0:
606
				$return = 'OK';
607
				break;
608
			case 1:
609
				$return = 'In progress';
610
				break;
611
			case 2:
612
				$return = 'Manually stopped';
613
				break;
614
			default:
615
				break;
616
		}
617
		return $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return does not seem to be defined for all execution paths leading up to this point.
Loading history...
618
	}
619
620
	/**
621
	 * Return scan history.
622
	 *
623
	 * @param int $startNumber
624
	 *
625
	 * @return array
626
	 */
627
	public function getScanHistory($startNumber = 0)
628
	{
629
		$limit = 30;
630
		$endNumber = $startNumber + $limit;
631
		$dataReader = (new App\Db\Query())->from('vtiger_ossmails_logs')->orderBy(['id' => SORT_DESC])->limit($endNumber)->offset($startNumber)->createCommand()->query();
632
		$output = [];
633
		while ($row = $dataReader->read()) {
634
			$startTime = new DateTimeField($row['start_time']);
635
			$endTime = new DateTimeField($row['end_time']);
636
			$output[] = [
637
				'id' => $row['id'],
638
				'start_time' => $startTime->getDisplayDateTimeValue(),
639
				'end_time' => $endTime->getDisplayDateTimeValue(),
640
				'status' => self::getHistoryStatus($row['status']),
0 ignored issues
show
Bug Best Practice introduced by
The method OSSMailScanner_Record_Model::getHistoryStatus() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

640
				'status' => self::/** @scrutinizer ignore-call */ getHistoryStatus($row['status']),
Loading history...
641
				'user' => $row['user'],
642
				'stop_user' => $row['stop_user'],
643
				'count' => $row['count'],
644
				'action' => $row['count'],
645
				'info' => $row['info'],
646
			];
647
		}
648
		$dataReader->close();
649
650
		return $output;
651
	}
652
653 1
	/**
654
	 * Insert new scan history row.
655 1
	 *
656 1
	 * @param array $array
657
	 *
658 1
	 * @throws \yii\db\Exception
659
	 *
660
	 * @return int
661
	 */
662
	public function addScanHistory($array): int
663
	{
664
		$db = \App\Db::getInstance();
665
		$db->createCommand()->insert('vtiger_ossmails_logs', ['status' => 1, 'user' => $array['user'], 'start_time' => date('Y-m-d H:i:s')])->execute();
666
667 1
		return $db->getLastInsertID('vtiger_ossmails_logs_id_seq');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $db->getLastInser..._ossmails_logs_id_seq') returns the type string which is incompatible with the type-hinted return integer.
Loading history...
668
	}
669 1
670 1
	/**
671 1
	 * Update scan history row.
672 1
	 *
673
	 * @param int   $id
674
	 * @param array $array
675 1
	 */
676
	public static function updateScanHistory($id, $array): bool
677
	{
678
		$dbCommand = \App\Db::getInstance()->createCommand();
679
		$result = $dbCommand->update('vtiger_ossmails_logs', ['end_time' => date('Y-m-d H:i:s'), 'status' => $array['status'], 'count' => $array['count'], 'action' => $array['action']],
680
			['and', ['id' => $id], ['<>', 'status', 2]])->execute();
681
		if (!$result) {
682
			$dbCommand->update('vtiger_ossmails_logs', ['count' => $array['count']], ['id' => $id])->execute();
683
		}
684
		return (bool) $result;
685 1
	}
686
687 1
	/**
688 1
	 * State of scan action.
689
	 *
690
	 * @param mixed $whoTrigger
691 1
	 *
692
	 * @return bool
693
	 */
694
	public static function isPermissionToScan($whoTrigger = ''): bool
695
	{
696
		$result = self::isActiveScan();
697
		if ($result && self::getCronTask()->hadTimeout()) {
698
			$result = static::setActiveScan($whoTrigger);
699
		}
700
		return !$result;
701 1
	}
702
703 1
	/**
704 1
	 * State of scan action.
705
	 *
706
	 * @param int|null $scanId
707 1
	 *
708
	 * @return bool
709
	 */
710
	public static function isActiveScan(?int $scanId = null): bool
711
	{
712
		$where = ['status' => 1];
713
		if ($scanId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scanId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
714
			$where['id'] = $scanId;
715
		}
716
		return (new App\Db\Query())->select(['id'])->from('vtiger_ossmails_logs')->where($where)->exists();
717
	}
718
719
	/**
720
	 * Activate scan.
721
	 *
722
	 * @param string   $whoTrigger
723
	 * @param int|null $scanId
724
	 *
725
	 * @return bool
726
	 */
727
	public static function setActiveScan(string $whoTrigger, ?int $scanId = null): bool
728
	{
729
		$where = ['status' => 1];
730
		if ($scanId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scanId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
731
			$where['id'] = $scanId;
732
		}
733
		return (bool) \App\Db::getInstance()->createCommand()
734
			->update('vtiger_ossmails_logs', ['status' => 2, 'stop_user' => $whoTrigger, 'end_time' => date('Y-m-d H:i:s')], $where)->execute();
735
	}
736
737
	/**
738
	 * Cron data.
739
	 *
740
	 * @return \vtlib\Cron
741 1
	 */
742
	public static function getCronTask()
743 1
	{
744 1
		return \vtlib\Cron::getInstance('LBL_MAIL_SCANNER_ACTION')->refreshData();
745 1
	}
746 1
747 1
	/**
748 1
	 * Restart cron.
749
	 *
750
	 * @param int $scanId
751
	 */
752
	public static function runRestartCron(int $scanId)
753
	{
754
		if (self::isActiveScan($scanId)) {
755
			if (self::getCronTask()->hadTimeout()) {
756
				\App\Cron::updateStatus(\App\Cron::STATUS_ENABLED, 'LBL_MAIL_SCANNER_ACTION');
757
			}
758
			self::setActiveScan(\App\User::getCurrentUserModel()->getDetail('user_name'), $scanId);
759
		}
760
	}
761
762
	/**
763 1
	 * Active users list.
764 1
	 *
765
	 * @var array|bool
766
	 */
767
	protected $user = false;
768
769
	/**
770
	 * Return active users list.
771
	 *
772
	 * @return array
773
	 */
774
	public function getUserList()
775
	{
776
		if ($this->user) {
777
			return $this->user;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->user also could return the type true which is incompatible with the documented return type array.
Loading history...
778
		}
779
780
		$this->user = (new \App\Db\Query())->select(['id', 'user_name', 'first_name', 'last_name'])->from('vtiger_users')->where(['status' => 'Active'])->createCommand()->queryAll();
781
782
		return $this->user;
783
	}
784
785
	/**
786
	 * Groups list.
787
	 *
788
	 * @var array
789
	 */
790
	protected $group = false;
791
792
	/**
793
	 * Return groups list.
794
	 *
795
	 * @return array
796
	 */
797
	public function getGroupList()
798
	{
799
		if ($this->group) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->group of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
800
			return $this->group;
801
		}
802
		return $this->group = (new \App\Db\Query())->select(['groupid', 'groupname'])->from('vtiger_groups')->all();
803
	}
804
805
	/**
806
	 * Assign data to model.
807
	 *
808
	 * @param array $row
809
	 *
810
	 * @return bool
811
	 */
812
	public function bindMail($row)
813
	{
814
		if (empty($row['actions'])) {
815
			return false;
816
		}
817
		$actions = array_diff(explode(',', $row['actions']), ['CreatedEmail', 'CreatedHelpDesk']);
818
		if (empty($actions)) {
819
			return false;
820
		}
821
822
		$mail = new OSSMail_Mail_Model();
823
		$mail->setMailCrmId($row['ossmailviewid']);
824
		$mail->setFolder($row['mbox']);
825
		$mail->set('message_id', $row['uid']);
826
		$mail->set('to_email', $row['to_email']);
827
		$mail->set('from_email', $row['from_email']);
828
		$mail->set('reply_to_email', $row['reply_to_email']);
829
		$mail->set('cc_email', $row['cc_email']);
830
		$mail->set('bcc_email', $row['bcc_email']);
831
		$mail->set('subject', $row['subject']);
832
		$mail->set('date', $row['date']);
833
		$mail->set('body', $row['content']);
834
835
		foreach ($actions as $action) {
836
			$handlerClass = Vtiger_Loader::getComponentClassName('ScannerAction', $action, 'OSSMailScanner');
837
			$handler = new $handlerClass();
838
			if ($handler) {
839
				$handler->process($mail);
840
			}
841
		}
842
		return true;
843
	}
844
845
	/**
846
	 * Delete user email accounts.
847
	 *
848
	 * @param int $id
849
	 */
850
	public static function accountDelete($id)
851
	{
852
		$db = App\Db::getInstance();
853
		$db->createCommand()->delete('roundcube_users', ['user_id' => $id])->execute();
854
		$db->createCommand()->delete('vtiger_ossmailscanner_folders_uid', ['user_id' => $id])->execute();
855
	}
856
}
857