mail_ui::importMessageFromVFS2DraftAndEdit()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware - Mail - interface class
4
 *
5
 * @link http://www.egroupware.org
6
 * @package mail
7
 * @author EGroupware GmbH [[email protected]]
8
 * @copyright (c) 2013-2016 by EGroupware GmbH <info-AT-egroupware.org>
9
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
10
 * @version $Id$
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Link;
15
use EGroupware\Api\Framework;
16
use EGroupware\Api\Egw;
17
use EGroupware\Api\Vfs;
18
use EGroupware\Api\Etemplate;
19
use EGroupware\Api\Etemplate\KeyManager;
0 ignored issues
show
Bug introduced by
The type EGroupware\Api\Etemplate\KeyManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use EGroupware\Api\Mail;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Mail. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
22
/**
23
 * Mail User Interface
24
 *
25
 * As we do NOT want to connect to previous imap server, when a profile change is triggered
26
 * by user get_rows and ajax_changeProfile are not static methods and instanciates there own
27
 * mail_ui object.
28
 *
29
 * If they detect a profile change is to be triggered they call:
30
 *		$mail_ui = new mail_ui(false);	// not call constructor / connect to imap server
31
 *		$mail_ui->changeProfile($_profileID);
32
 * If no profile change is needed they just call:
33
 *		$mail_ui = new mail_ui();
34
 * Afterwards they use $mail_ui instead of $this.
35
 */
36
class mail_ui
37
{
38
	/**
39
	 * Methods callable via menuaction
40
	 *
41
	 * @var array
42
	 */
43
	var $public_functions = array
44
	(
45
		'index' => True,
46
		'displayHeader'	=> True,
47
		'displayMessage'	=> True,
48
		'displayImage'		=> True,
49
		'getAttachment'		=> True,
50
		'download_zip'		=> True,
51
		'saveMessage'	=> True,
52
		'vfsSaveMessages' => True,
53
		'loadEmailBody'	=> True,
54
		'importMessage'	=> True,
55
		'importMessageFromVFS2DraftAndDisplay'=>True,
56
		'subscription'	=> True,
57
		'folderManagement' => true,
58
		'smimeExportCert' => true
59
	);
60
61
	/**
62
	 * current icServerID
63
	 *
64
	 * @var int
65
	 */
66
	static $icServerID;
67
68
	/**
69
	 * delimiter - used to separate profileID from foldertreestructure, and separate keyinformation in rowids
70
	 *
71
	 * @var string
72
	 */
73
	static $delimiter = '::';
74
75
	/**
76
	 * nextMatch name for index
77
	 *
78
	 * @var string
79
	 */
80
	static $nm_index = 'nm';
81
82
	/**
83
	 * instance of Mail
84
	 *
85
	 * @var Mail
86
	 */
87
	var $mail_bo;
88
89
	/**
90
	 * definition of available / supported search types
91
	 *
92
	 * @var array
93
	 */
94
	var $searchTypes = array(
95
		'quick'		=> 'quicksearch',	// lang('quicksearch')
96
		'quickwithcc'=> 'quicksearch (with cc)',	// lang('quicksearch (with cc)')
97
		'subject'	=> 'subject',		// lang('subject')
98
		'body'		=> 'message body',	// lang('message body')
99
		'from'		=> 'from',			// lang('from')
100
		'to'		=> 'to',			// lang('to')
101
		'cc'		=> 'cc',			// lang('cc')
102
		'text'		=> 'whole message',	// lang('whole message')
103
		'larger'		=> 'greater than',	// lang('greater than')
104
		'smaller'		=> 'less than',	// lang('less than')
105
		'bydate' 	=> 'Selected date range (with quicksearch)',// lang('Selected date range (with quicksearch)')
106
	);
107
108
	/**
109
	 * definition of available / supported status types
110
	 *
111
	 * @var array
112
	 */
113
	var $statusTypes = array(
114
		'any'		=> 'any status',// lang('any status')
115
		'flagged'	=> 'flagged',	// lang('flagged')
116
		'unseen'	=> 'unread',	// lang('unread')
117
		'answered'	=> 'replied',	// lang('replied')
118
		'seen'		=> 'read',		// lang('read')
119
		'deleted'	=> 'deleted',	// lang('deleted')
120
	);
121
122
	/**
123
	 * Constructor
124
	 *
125
	 * @param boolean $run_constructor =true false: no not run constructor and therefore do NOT connect to imap server
126
	 */
127
	function __construct($run_constructor=true)
128
	{
129
		$this->mail_tree = new mail_tree($this);
0 ignored issues
show
Bug Best Practice introduced by
The property mail_tree does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
130
		if (!$run_constructor) return;
131
132
		if (Mail::$debugTimes) $starttime = microtime (true);
133
		// no autohide of the sidebox, as we use it for folderlist now.
134
		unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
135
136
		if (isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']) && !empty($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']))
137
		{
138
			self::$icServerID = (int)$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
139
		}
140
		if ($_GET["resetConnection"])
141
		{
142
			unset($_GET["resetConnection"]);
143
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Connection Reset triggered: for Profile with ID:'.self::$icServerID);
144
			Mail::unsetCachedObjects(self::$icServerID);
145
		}
146
147
		try {
148
			$this->mail_bo = Mail::getInstance(true,self::$icServerID, true, false, true);
149
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
150
			//error_log(__METHOD__.__LINE__.array2string($this->mail_bo->icServer));
151
152
			// RegEx to minimize extra openConnection
153
			$needle = '/^(?!mail)/';
154
			if (!preg_match($needle,$_GET['menuaction']) && !Api\Json\Request::isJSONRequest())
155
			{
156
				//error_log(__METHOD__.__LINE__.' Fetched IC Server openConnection:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
157
				//openConnection gathers SpecialUseFolderInformation and Delimiter Info
158
				$this->mail_bo->openConnection(self::$icServerID);
159
			}
160
		}
161
		catch (Exception $e)
162
		{
163
			// we need this to handle failed JSONRequests
164
			if (Api\Json\Request::isJSONRequest() && $_GET['menuaction'] != 'mail.mail_ui.index')
165
			{
166
				$response = Api\Json\Response::get();
167
				$response->call('egw.message',$e->getMessage(),'error');
168
			}
169
			// redirect to mail wizard to handle it (redirect works for ajax too), unless index is called. we want the sidebox
170
			if ($_GET['menuaction'] != 'mail.mail_ui.index') self::callWizard($e->getMessage(),true,'error',false);
171
		}
172
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
173
	}
174
175
	/**
176
	 * callWizard
177
	 *
178
	 * @param string $message
179
	 * @param boolean $exit If true, will call exit() after opening the wizardpopup
180
	 * @param string $msg_type = 'success' message type
181
	 */
182
	static function callWizard($message, $exit=true, $msg_type='success',$reset_sidebox_on_index=true)
183
	{
184
		//error_log(__METHOD__."('$message', $exit) ".function_backtrace());
185
		$linkData=(self::$icServerID ? array(
186
				'menuaction' => 'mail.mail_wizard.edit',
187
				'acc_id' => self::$icServerID,
188
			) : array(
189
				'menuaction' => 'mail.mail_wizard.add',
190
			)) + array(
191
				'msg' => $message,
192
				'msg_type' => $msg_type
193
			);
194
195
		if (Api\Json\Response::isJSONResponse())
196
		{
197
			$response = Api\Json\Response::get();
198
			$windowName = "editMailAccount".self::$icServerID;
199
			$response->call("egw.open_link", Egw::link('/index.php', $linkData), $windowName, "600x480",null,true);
200
			Framework::message($message, 'error');
201
			if ($_GET['menuaction'] == 'mail.mail_ui.index' && $reset_sidebox_on_index)
202
			{
203
				$response->call('framework.setSidebox','mail',array(),'md5');
204
			}
205
			if ($exit)
206
			{
207
				exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
208
			}
209
		}
210
		else	// regular GET request eg. in idots template
211
		{
212
			$windowName = "editMailAccount".self::$icServerID;
213
			Framework::popup(Framework::link('/index.php',$linkData),$windowName);
214
			$GLOBALS['egw']->framework->render($message,'',true);
215
			if ($exit)
216
			{
217
				exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
218
			}
219
		}
220
	}
221
222
	/**
223
	 * changeProfile
224
	 *
225
	 * @param int $_icServerID
226
	 * @param boolean $unsetCache
227
	 *
228
	 * @throws Api\Exception
229
	 */
230
	function changeProfile($_icServerID,$unsetCache=false)
231
	{
232
		if (Mail::$debugTimes) $starttime = microtime (true);
233
		if (self::$icServerID != $_icServerID)
234
		{
235
			self::$icServerID = $_icServerID;
236
		}
237
		if (Mail::$debug) error_log(__METHOD__.__LINE__.'->'.self::$icServerID.'<->'.$_icServerID);
238
239
		if ($unsetCache) Mail::unsetCachedObjects(self::$icServerID);
240
		$this->mail_bo = Mail::getInstance(false,self::$icServerID,true, false, true);
241
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
242
		// no icServer Object: something failed big time
243
		if (!isset($this->mail_bo) || !isset($this->mail_bo->icServer) || $this->mail_bo->icServer->ImapServerId<>$_icServerID)
244
		{
245
			self::$icServerID = $_icServerID;
246
			throw new Api\Exception('Profile change failed!');
247
		}
248
249
		// save session varchar
250
		$oldicServerID =& Api\Cache::getSession('mail','activeProfileID');
251
		if ($oldicServerID <> self::$icServerID) $this->mail_bo->openConnection(self::$icServerID);
252
		if (true) $oldicServerID = self::$icServerID;
253
		if (!Mail::storeActiveProfileIDToPref($this->mail_bo->icServer, self::$icServerID, true ))
0 ignored issues
show
Bug Best Practice introduced by
The expression EGroupware\Api\Mail::sto...self::icServerID, true) of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false 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...
254
		{
255
			throw new Api\Exception(__METHOD__." failed to change Profile to $_icServerID");
256
		}
257
258
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
259
	}
260
261
	/**
262
	 * Ajax function to request next branch of a tree branch
263
	 */
264
	static function ajax_tree_autoloading ($_id = null)
265
	{
266
		$mail_ui = new mail_ui();
267
		$id = $_id ? $_id : $_GET['id'];
268
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,false));
269
	}
270
271
	/**
272
	 * Subscription popup window
273
	 *
274
	 * @param array $content
275
	 * @param type $msg
276
	 */
277
	function subscription(array $content=null ,$msg=null)
278
	{
279
		$stmpl = new Etemplate('mail.subscribe');
280
281
		if(is_array($content))
282
		{
283
			$profileId = $content['profileId'];
284
		}
285
		elseif (!($profileId = (int)$_GET['acc_id']))
286
		{
287
			Framework::window_close('Missing acc_id!');
288
		}
289
		// Initial tree's options, the rest would be loaded dynamicaly by autoloading,
290
		// triggered from client-side. Also, we keep this here as
291
		$sel_options['foldertree'] =  $this->mail_tree->getTree(null,$profileId,1,true,false,true);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sel_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sel_options = array(); before regardless.
Loading history...
292
293
		//Get all subscribed folders
294
		// as getting all subscribed folders is very fast operation
295
		// we can use it to get a comparison base for folders which
296
		// got subscribed or unsubscribed by the user
297
		try {
298
			$subscribed = $this->mail_bo->icServer->listSubscribedMailboxes('',0,true);
299
		} catch (Exception $ex) {
300
			Framework::message($ex->getMessage());
301
		}
302
303
		if (!is_array($content))
304
		{
305
			$content['foldertree'] = array();
306
307
			foreach ($subscribed as $folder)
308
			{
309
				$folderName = $profileId . self::$delimiter . $folder['MAILBOX'];
310
				array_push($content['foldertree'], $folderName);
311
			}
312
		}
313
		else
314
		{
315
			$button = @key($content['button']);
316
			switch ($button)
317
			{
318
				case 'save':
319
				case 'apply':
320
				{
321
					// do not let user (un)subscribe namespace roots eg. "other", "user" or "INBOX", same for tree-root/account itself
322
					$namespace_roots = array($profileId);
323
					foreach($this->mail_bo->_getNameSpaces() as $namespace)
324
					{
325
						$namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']);
326
					}
327
					$to_unsubscribe = $to_subscribe = array();
328
					foreach ($content['foldertree'] as $path => $value)
329
					{
330
						list(,$node) = explode($profileId.self::$delimiter, $path);
331
						if ($node)
332
						{
333
							if (is_array($subscribed) && $subscribed[$node] && !$value['value']) $to_unsubscribe []= $node;
334
							if (is_array($subscribed) && !$subscribed[$node] && $value['value']) $to_subscribe [] = $node;
335
							if ($value['value']) $cont[] = $path;
336
						}
337
338
					}
339
					$content['foldertree'] = $cont;
340
					// set foldertree options to basic node in order to avoid initial autoloading
341
					// from client side, as no options would trigger that.
342
					$sel_options['foldertree'] = array('id' => '0', 'item'=> array());
343
					foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox)
344
					{
345
						if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true))
346
						{
347
							continue;
348
						}
349
						$subscribe = in_array($mailbox, $to_subscribe);
350
						try {
351
							$this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe);
352
						}
353
						catch (Exception $ex)
354
						{
355
							$msg_type = 'error';
356
							if ($subscribe)
357
							{
358
								$msg .= lang('Failed to subscribe folder %1!', $mailbox).' '.$ex->getMessage();
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $mailbox. ( Ignorable by Annotation )

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

358
								$msg .= /** @scrutinizer ignore-call */ lang('Failed to subscribe folder %1!', $mailbox).' '.$ex->getMessage();

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
359
							}
360
							else
361
							{
362
								$msg .= lang('Failed to unsubscribe folder %1!', $mailbox).' '.$ex->getMessage();
363
							}
364
						}
365
					}
366
					if (!isset($msg))
367
					{
368
						$msg_type = 'success';
369
						if ($to_subscribe || $to_unsubscribe)
370
						{
371
							$msg = lang('Subscription successfully saved.');
372
						}
373
						else
374
						{
375
							$msg = lang('Nothing to change.');
376
						}
377
					}
378
					// update foldertree in main window
379
					$parentFolder='INBOX';
380
					$refreshData = array(
381
						$profileId => lang($parentFolder),
382
					);
383
					$response = Api\Json\Response::get();
384
					foreach($refreshData as $folder => &$name)
385
					{
386
						$name = $this->mail_tree->getTree($folder, $profileId,1,true,true,true);
387
					}
388
					// give success/error message to opener and popup itself
389
					//$response->call('opener.app.mail.subscription_refresh',$refreshData);
390
					$response->call('opener.app.mail.mail_reloadNode',$refreshData);
391
392
					Framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type);
393
					if ($button == 'apply')
394
					{
395
						Framework::message($msg, $msg_type);
396
						break;
397
					}
398
				}
399
				case 'cancel':
400
				{
401
					Framework::window_close();
402
				}
403
			}
404
		}
405
406
		$preserv['profileId'] = $profileId;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$preserv was never initialized. Although not strictly required by PHP, it is generally a good practice to add $preserv = array(); before regardless.
Loading history...
407
408
		$readonlys = array();
409
410
		$stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2);
411
	}
412
413
	/**
414
	 * Main mail page
415
	 *
416
	 * @param array $content
417
	 * @param string $msg
418
	 */
419
	function index(array $content=null,$msg=null)
420
	{
421
		//error_log(__METHOD__.__LINE__.array2string($content));
422
		try	{
423
				if (!isset($this->mail_bo)) throw new Api\Exception\WrongUserinput(lang('Initialization of mail failed. Please use the Wizard to cope with the problem.'));
424
				//error_log(__METHOD__.__LINE__.function_backtrace());
425
				if (Mail::$debugTimes) $starttime = microtime (true);
426
				$this->mail_bo->restoreSessionData();
427
				$sessionFolder = $this->mail_bo->sessionData['mailbox'];
428
				if ($this->mail_bo->folderExists($sessionFolder))
429
				{
430
					$this->mail_bo->reopen($sessionFolder); // needed to fetch full set of capabilities
431
				}
432
				else
433
				{
434
					$sessionFolder = $this->mail_bo->sessionData['mailbox'] = 'INBOX';
0 ignored issues
show
Bug Best Practice introduced by
The property sessionData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Unused Code introduced by
The assignment to $sessionFolder is dead and can be removed.
Loading history...
435
				}
436
				//error_log(__METHOD__.__LINE__.' SessionFolder:'.$sessionFolder.' isToSchema:'.$toSchema);
437
				if (!is_array($content))
438
				{
439
					$content = array(
440
						self::$nm_index => Api\Cache::getSession('mail', 'index'),
441
					);
442
					if (!is_array($content[self::$nm_index]))
443
					{
444
						// These only set on first load
445
						$content[self::$nm_index] = array(
446
							'filter'         => 'any',	// filter is used to choose the mailbox
447
							'lettersearch'   => false,	// I  show a lettersearch
448
							'searchletter'   =>	false,	// I0 active letter of the lettersearch or false for [all]
449
							'start'          =>	0,		// IO position in list
450
							'order'          =>	'date',	// IO name of the column to sort after (optional for the sortheaders)
451
							'sort'           =>	'DESC',	// IO direction of the sort: 'ASC' or 'DESC'
452
						);
453
					}
454
					if (Api\Header\UserAgent::mobile()) $content[self::$nm_index]['header_row'] = 'mail.index.header_right';
455
				}
456
457
				// These must always be set, even if $content is an array
458
				$content[self::$nm_index]['cat_is_select'] = true;    // Category select is just a normal selectbox
459
				$content[self::$nm_index]['no_filter2'] = false;       // Disable second filter
460
				$content[self::$nm_index]['actions'] = self::get_actions();
0 ignored issues
show
Bug Best Practice introduced by
The method mail_ui::get_actions() 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

460
				/** @scrutinizer ignore-call */ 
461
    $content[self::$nm_index]['actions'] = self::get_actions();
Loading history...
461
				$content[self::$nm_index]['row_id'] = 'row_id';	     // is a concatenation of trim($GLOBALS['egw_info']['user']['account_id']):profileID:base64_encode(FOLDERNAME):uid
462
				$content[self::$nm_index]['placeholder_actions'] = array('composeasnew');
463
				$content[self::$nm_index]['get_rows'] = 'mail_ui::get_rows';
464
				$content[self::$nm_index]['num_rows'] = 0;      // Do not send any rows with initial request
465
				$content[self::$nm_index]['default_cols'] = 'status,attachments,subject,address,date,size';	// I  columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
466
				$content[self::$nm_index]['csv_fields'] = false;
467
				if ($msg)
468
				{
469
					$content['msg'] = $msg;
470
				}
471
				else
472
				{
473
					unset($msg);
474
					unset($content['msg']);
475
				}
476
				// call getQuotaRoot asynchronously in getRows by initiating a client Server roundtrip
477
				$quota = false;//$this->mail_bo->getQuotaRoot();
478
				if($quota !== false && $quota['limit'] != 'NOT SET') {
0 ignored issues
show
introduced by
The condition $quota !== false is always false.
Loading history...
479
					$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
480
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = $quotainfo['text'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sel_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sel_options = array(); before regardless.
Loading history...
481
					$content[self::$nm_index]['quotainpercent'] = $sel_options[self::$nm_index]['quotainpercent'] =  (string)$quotainfo['percent'];
482
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = $quotainfo['class'];
483
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "";
484
				} else {
485
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = lang("Quota not provided by server");
486
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = "mail_DisplayNone";
487
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "mail_DisplayNone";
488
				}
489
490
				//$zstarttime = microtime (true);
491
				$sel_options[self::$nm_index]['foldertree'] = $this->mail_tree->getInitialIndexTree(null, $this->mail_bo->profileID, null, !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
492
				//$zendtime = microtime(true) - $zstarttime;
493
				//error_log(__METHOD__.__LINE__. " time used: ".$zendtime);
494
				$content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.(!empty($this->mail_bo->sessionData['mailbox'])?$this->mail_bo->sessionData['mailbox']:'INBOX');
495
				// since we are connected,(and selected the folder) we check for capabilities SUPPORTS_KEYWORDS to eventually add the keyword filters
496
				if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
497
				{
498
					$this->statusTypes = array_merge($this->statusTypes,array(
499
						'keyword1'	=> 'important',//lang('important'),
500
						'keyword2'	=> 'job',	//lang('job'),
501
						'keyword3'	=> 'personal',//lang('personal'),
502
						'keyword4'	=> 'to do',	//lang('to do'),
503
						'keyword5'	=> 'later',	//lang('later'),
504
					));
505
				}
506
				else
507
				{
508
					$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
509
					foreach($keywords as &$k)
510
					{
511
						if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
512
					}
513
				}
514
515
				if (!isset($content[self::$nm_index]['foldertree'])) $content[self::$nm_index]['foldertree'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
516
				if (!isset($content[self::$nm_index]['selectedFolder'])) $content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
517
518
				$content[self::$nm_index]['foldertree'] = $content[self::$nm_index]['selectedFolder'];
519
520
				if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
521
				{
522
					Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
523
					if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
524
				}
525
				if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
526
				{
527
					unset($this->searchTypes['quick']);
528
					unset($this->searchTypes['quickwithcc']);
529
				}
530
				$sel_options['cat_id'] = $this->searchTypes;
531
				//error_log(__METHOD__.__LINE__.array2string($sel_options['cat_id']));
532
				//error_log(__METHOD__.__LINE__.array2string($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']));
533
				$content[self::$nm_index]['cat_id'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType'];
534
				$sel_options['filter'] = $this->statusTypes;
535
				$sel_options['filter2'] = array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list'));
536
				$content[self::$nm_index]['filter2'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails'];
537
538
				$etpl = new Etemplate('mail.index');
539
				//apply infolog_filter_change javascript method (hide/show of date filter form) over onchange filter
540
				$content[self::$nm_index]['cat_id_onchange'] = "app.mail.mail_searchtype_change()";
541
				// set the actions on tree
542
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions());
543
544
				// sending preview toolbar actions
545
				if ($content['mailSplitter']) $etpl->setElementAttribute('mailPreview[toolbar]', 'actions', $this->get_toolbar_actions());
546
547
				// We need to send toolbar actions to client-side because view template needs them
548
				if (Api\Header\UserAgent::mobile()) $sel_options['toolbar'] = $this->get_toolbar_actions();
549
550
				//we use the category "filter" option as specifier where we want to search (quick, subject, from, to, etc. ....)
551
				if (empty($content[self::$nm_index]['cat_id']) || empty($content[self::$nm_index]['search']))
552
				{
553
					$content[self::$nm_index]['cat_id']=($content[self::$nm_index]['cat_id']?(!Mail::$supportsORinQuery[$this->mail_bo->profileID]&&($content[self::$nm_index]['cat_id']=='quick'||$content[self::$nm_index]['cat_id']=='quickwithcc')?'subject':$content[self::$nm_index]['cat_id']):(Mail::$supportsORinQuery[$this->mail_bo->profileID]?'quick':'subject'));
554
				}
555
				$readonlys = $preserv = array();
556
				if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
557
		}
558
		catch (Exception $e)
559
		{
560
			// do not exit here. mail-tree should be build. if we exit here, we never get there.
561
			error_log(__METHOD__.__LINE__.$e->getMessage().($e->details?', '.$e->details:'').' Menuaction:'.$_GET['menuaction'].'.'.function_backtrace());
562
			if (isset($this->mail_bo))
563
			{
564
				if (empty($etpl))
565
				{
566
					$sel_options[self::$nm_index]['foldertree'] = $this->mail_tree->getInitialIndexTree(null, $this->mail_bo->profileID, null, !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
567
					$etpl = new Etemplate('mail.index');
568
				}
569
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions(false));
570
			}
571
			$readonlys = $preserv = array();
572
			if (empty($content)) $content=array();
573
574
			self::callWizard($e->getMessage().($e->details?', '.$e->details:''),(isset($this->mail_bo)?false:true), 'error',false);
575
			//return false;
576
		}
577
		switch ($this->mail_bo->mailPreferences['previewPane'])
578
		{
579
			case "1"://preference used to be '1', now 'hide'
580
			case "hide":
581
				$etpl->setElementAttribute('splitter', 'template', 'mail.index.nosplitter');
582
				break;
583
			case "vertical":
584
				$etpl->setElementAttribute('mailSplitter', 'orientation', 'v');
585
				break;
586
			case "expand":
587
			case "fixed":
588
				$etpl->setElementAttribute('mailSplitter', 'orientation', 'h');
589
				break;
590
			default:
591
				$etpl->setElementAttribute('mailSplitter', 'orientation', 'v');
592
		}
593
		return $etpl->exec('mail.mail_ui.index',$content,$sel_options,$readonlys,$preserv);
594
	}
595
596
	/**
597
	 * Get tree actions / context menu for tree
598
	 *
599
	 * Changes here, may require to log out, as $content[self::$nm_index] get stored in session!
600
	 * @param {boolean} $imap_actions set to false if you want to avoid to talk to the imap-server
0 ignored issues
show
Documentation Bug introduced by
The doc comment {boolean} at position 0 could not be parsed: Unknown type name '{' at position 0 in {boolean}.
Loading history...
601
	 * @return array
602
	 */
603
	function get_tree_actions($imap_actions=true)
604
	{
605
		// Start at 2 so auto-added copy+paste actions show up as second group
606
		// Needed because there's no 'select all' action to push things down
607
		$group=1;
608
		// Set tree actions
609
		$tree_actions = array(
610
			'drop_move_mail' => array(
611
				'type' => 'drop',
612
				'acceptedTypes' => 'mail',
613
				'icon' => 'move',
614
				'caption' => 'Move to',
615
				'onExecute' => 'javaScript:app.mail.mail_move'
616
			),
617
			'drop_copy_mail' => array(
618
				'type' => 'drop',
619
				'acceptedTypes' => 'mail',
620
				'icon' => 'copy',
621
				'caption' => 'Copy to',
622
				'onExecute' => 'javaScript:app.mail.mail_copy'
623
			),
624
			'drop_cancel' => array(
625
				'icon' => 'cancel',
626
				'caption' => 'Cancel',
627
				'acceptedTypes' => 'mail',
628
				'type' => 'drop',
629
			),
630
			'drop_move_folder' => array(
631
				'caption' => 'Move folder',
632
				'hideOnDisabled' => true,
633
				'type' => 'drop',
634
				'acceptedTypes' => 'mailFolder',
635
				'onExecute' => 'javaScript:app.mail.mail_MoveFolder'
636
			),
637
			// Tree does support this one
638
			'add' => array(
639
				'caption' => 'Add Folder',
640
				'onExecute' => 'javaScript:app.mail.mail_AddFolder',
641
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
642
				'group'		=> $group,
643
			),
644
			'edit' => array(
645
				'caption' => 'Rename Folder',
646
				'onExecute' => 'javaScript:app.mail.mail_RenameFolder',
647
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
648
				'group'		=> $group,
649
			),
650
			'move' => array(
651
				'caption' => 'Move Folder',
652
				'type' => 'drag',
653
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
654
				'dragType' => array('mailFolder'),
655
				'group'		=> $group,
656
			),
657
			'delete' => array(
658
				'caption' => 'Delete Folder',
659
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
660
				'onExecute' => 'javaScript:app.mail.mail_DeleteFolder',
661
				'group'		=> $group,
662
			),
663
			'readall' => array(
664
				'group' => $group,
665
				'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
666
				'icon' => 'kmmsgread',
667
				'onExecute' => 'javaScript:app.mail.mail_flag',
668
				'hint' => 'mark all messages in folder as read',
669
				'toolbarDefault' => false
670
			),
671
			'subscribe' => array(
672
				'caption' => 'Subscribe folder ...',
673
				//'icon' => 'configure',
674
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
675
				'onExecute' => 'javaScript:app.mail.edit_subscribe',
676
				'group'		=> $group
677
			),
678
			'unsubscribe' => array(
679
				'caption' => 'Unsubscribe folder',
680
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
681
				'onExecute' => 'javaScript:app.mail.unsubscribe_folder',
682
				'group'		=> $group,
683
			),
684
			'foldermanagement' => array(
685
				'caption' => 'Folder Management ...',
686
				'icon' => 'folder_management',
687
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
688
				'onExecute' => 'javaScript:app.mail.folderManagement',
689
				'group'		=> $group,
690
				'hideOnMobile' => true
691
			),
692
			'sieve' => array(
693
				'caption' => 'Mail filter',
694
				'onExecute' => 'javaScript:app.mail.edit_sieve',
695
696
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
697
				'icon' => 'mail/filter',	// funnel
698
				'hideOnMobile' => true
699
			),
700
			'vacation' => array(
701
				'caption' => 'Vacation notice',
702
				'icon' => 'mail/navbar',	// mail as in admin
703
				'onExecute' => 'javaScript:app.mail.edit_vacation',
704
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
705
			),
706
			'edit_account' => array(
707
				'caption' => 'Edit account ...',
708
				'icon' => 'configure',
709
				'onExecute' => 'javaScript:app.mail.edit_account',
710
			),
711
			'edit_acl'	=> array(
712
				'caption' => 'Edit folder ACL ...',
713
				'icon'	=> 'lock',
714
				'enabled'	=> 'javaScript:app.mail.acl_enabled',
715
				'onExecute' => 'javaScript:app.mail.edit_acl',
716
			),
717
		);
718
		// the preference prefaskformove controls actually if there is a popup on target or not
719
		// if there are multiple options there is a popup on target, 0 for prefaskformove means
720
		// that only move is available; 1 stands for move and cancel; 2 (should be the default if
721
		// not set); so we are assuming this, when not set
722
		if (isset($this->mail_bo->mailPreferences['prefaskformove']))
723
		{
724
			switch ($this->mail_bo->mailPreferences['prefaskformove'])
725
			{
726
				case 0:
727
					unset($tree_actions['drop_copy_mail']);
728
					unset($tree_actions['drop_cancel']);
729
					break;
730
				case 1:
731
					unset($tree_actions['drop_copy_mail']);
732
					break;
733
				default:
734
					// everything is fine
735
			}
736
		}
737
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
738
		if ($this->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
739
		{
740
			unset($tree_actions['subscribe']);
741
			unset($tree_actions['unsubscribe']);
742
		}
743
		++$group;	// put delete in own group
744
		switch($GLOBALS['egw_info']['user']['preferences']['mail']['deleteOptions'])
745
		{
746
			case 'move_to_trash':
747
				$tree_actions['empty_trash'] = array(
748
					'caption' => 'empty trash',
749
					'icon' => 'dhtmlxtree/MailFolderTrash',
750
					'onExecute' => 'javaScript:app.mail.mail_emptyTrash',
751
					'group'	=> $group,
752
				);
753
				break;
754
			case 'mark_as_deleted':
755
				$tree_actions['compress_folder'] = array(
756
					'caption' => 'compress folder',
757
					'icon' => 'dhtmlxtree/MailFolderTrash',
758
					'onExecute' => 'javaScript:app.mail.mail_compressFolder',
759
					'group'	=> $group,
760
				);
761
				break;
762
		}
763
		$junkFolder = ($imap_actions?$this->mail_bo->getJunkFolder():null);
764
765
		//error_log(__METHOD__.__LINE__.$junkFolder);
766
		if ($junkFolder && !empty($junkFolder))
767
		{
768
			$tree_actions['empty_spam'] = array(
769
				'caption' => 'empty junk',
770
				'icon' => 'dhtmlxtree/MailFolderJunk',
771
				'enabled'	=> 'javaScript:app.mail.spamfolder_enabled',
772
				'onExecute' => 'javaScript:app.mail.mail_emptySpam',
773
				'group'	=> $group,
774
			);
775
		}
776
		$tree_actions['sieve']['group']	= $tree_actions['vacation']['group'] = ++$group;	// new group for filter
777
		$tree_actions['edit_account']['group'] = $tree_actions['edit_acl']['group']	= ++$group;
778
779
780
		// enforce global (group-specific) ACL
781
		if (!mail_hooks::access('aclmanagement'))
782
		{
783
			unset($tree_actions['edit_acl']);
784
		}
785
		if (!mail_hooks::access('editfilterrules'))
786
		{
787
			unset($tree_actions['sieve']);
788
		}
789
		if (!mail_hooks::access('absentnotice'))
790
		{
791
			unset($tree_actions['vacation']);
792
		}
793
		if (!mail_hooks::access('managefolders'))
794
		{
795
			unset($tree_actions['add']);
796
			unset($tree_actions['move']);
797
			unset($tree_actions['delete']);
798
			unset($tree_actions['foldermanagement']);
799
			// manage folders should not affect the ability to subscribe or unsubscribe
800
			// to existing folders, it should only affect add/rename/move/delete
801
		}
802
		return $tree_actions;
803
	}
804
805
	/**
806
	 * Ajax callback to subscribe / unsubscribe a Mailbox of an account
807
	 *
808
	 * @param {int} $_acc_id profile Id of selected mailbox
0 ignored issues
show
Documentation Bug introduced by
The doc comment {int} at position 0 could not be parsed: Unknown type name '{' at position 0 in {int}.
Loading history...
809
	 * @param {string} $_folderName name of mailbox needs to be subcribe or unsubscribed
810
	 * @param {boolean} $_status set true for subscribe and false to unsubscribe
811
	 */
812
	public function ajax_foldersubscription($_acc_id,$_folderName, $_status)
813
	{
814
		//Change the Mail object to related profileId
815
		$this->changeProfile($_acc_id);
816
		try{
817
			$this->mail_bo->icServer->subscribeMailbox($_folderName, $_status);
818
			$this->mail_bo->resetFolderObjectCache($_acc_id);
819
			$this->ajax_reloadNode($_acc_id,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
820
		} catch (Horde_Imap_Client_Exception $ex) {
821
			error_log(__METHOD__.__LINE__."()". lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $_folderName. ( Ignorable by Annotation )

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

821
			error_log(__METHOD__.__LINE__."()". /** @scrutinizer ignore-call */ lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
822
			Framework::message(lang('Folder %1 %2 failed!',$_folderName,$_status));
823
		}
824
	}
825
826
	/**
827
	 * Ajax callback to fetch folders for given profile
828
	 *
829
	 * We currently load all folders of a given profile, tree can also load parts of a tree.
830
	 *
831
	 * @param string $_nodeID if of node whose children are requested
832
	 * @param boolean $_subscribedOnly flag to tell whether to fetch all or only subscribed (default)
833
	 */
834
	public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null)
835
	{
836
		$nodeID = $_GET['id'];
837
		if (!is_null($_nodeID)) $nodeID = $_nodeID;
838
		$subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
839
		$fetchCounters = !is_null($_nodeID);
0 ignored issues
show
Unused Code introduced by
The assignment to $fetchCounters is dead and can be removed.
Loading history...
840
		list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2);
841
842
		if (!empty($_folderName)) $fetchCounters = true;
843
844
		// Check if it is called for refresh root
845
		// then we need to reinitialized the index tree
846
		if(!$nodeID && !$_profileID)
847
		{
848
			$data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
0 ignored issues
show
Unused Code introduced by
The call to mail_tree::getInitialIndexTree() has too many arguments starting with ! $this->mail_bo->mailPr...llFoldersInFolderPane']. ( Ignorable by Annotation )

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

848
			/** @scrutinizer ignore-call */ 
849
   $data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
849
		}
850
		else
851
		{
852
			$data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
853
		}
854
		if (!is_null($_nodeID)) return $data;
855
		Etemplate\Widget\Tree::send_quote_json($data);
856
	}
857
858
	/**
859
	 * findNode - helper function to return only a branch of the tree
860
	 *
861
	 * @param array $_out out array (to be searched)
862
	 * @param string $_nodeID node to search for
863
	 * @param boolean $childElements return node itself, or only its child items
864
	 * @return array structured subtree
865
	 */
866
	static function findNode($_out, $_nodeID, $childElements = false)
867
	{
868
		foreach($_out['item'] as $node)
869
		{
870
			if (strcmp($node['id'],$_nodeID)===0)
871
			{
872
				//error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']);
873
				return ($childElements?$node['item']:$node);
874
			}
875
			elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id']))
876
			{
877
				//error_log(__METHOD__.__LINE__.' descend into '.$node['id']);
878
				return self::findNode($node,$_nodeID,$childElements);
879
			}
880
		}
881
	}
882
883
	/**
884
	 * Method to execute spam actions
885
	 *
886
	 * @param type $_action action id
887
	 * @param type $_items
888
	 */
889
	public function ajax_spamAction($_action, $_items)
890
	{
891
		$msg = array();
892
		$refresh = false;
893
		$response = Api\Json\Response::get();
894
		// Check active profile and change it if it's neccessary
895
		if (is_array($_items[0]))
896
		{
897
			$id_parts = self::splitRowID($_items[0]['row_id']);
898
			if ($id_parts['profileID'] && $id_parts['profileID'] != $this->mail_bo->profileID)
899
			{
900
				$this->changeProfile($id_parts['profileID']);
901
			}
902
		}
903
904
		$delimiter = $this->mail_bo->getHierarchyDelimiter();
0 ignored issues
show
Unused Code introduced by
The assignment to $delimiter is dead and can be removed.
Loading history...
905
		// Ham folder
906
		$ham = $this->mail_bo->profileID.self::$delimiter.$this->mail_bo->icServer->acc_folder_ham;
0 ignored issues
show
Bug Best Practice introduced by
The property acc_folder_ham does not exist on EGroupware\Api\Mail\Imap. Since you implemented __get, consider adding a @property annotation.
Loading history...
907
		// Junk folder
908
		$junk = $this->mail_bo->profileID.self::$delimiter.$this->mail_bo->getJunkFolder();
0 ignored issues
show
Bug introduced by
Are you sure $this->mail_bo->getJunkFolder() of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

908
		$junk = $this->mail_bo->profileID.self::$delimiter./** @scrutinizer ignore-type */ $this->mail_bo->getJunkFolder();
Loading history...
909
		// Inbox folder
910
		$inbox = $this->mail_bo->profileID.self::$delimiter.'INBOX';
911
912
		$messages = array();
913
914
		foreach ($_items as &$params)
915
		{
916
			$id_parts = self::splitRowID($params['row_id']);
917
			// Current Mailbox
918
			$mailbox = $id_parts['folder'];
919
			$messages[] = $params['row_id'];
920
			if ($GLOBALS['egw_info']['apps']['stylite'] && $this->mail_bo->icServer->acc_spam_api)
0 ignored issues
show
Bug Best Practice introduced by
The property acc_spam_api does not exist on EGroupware\Api\Mail\Imap. Since you implemented __get, consider adding a @property annotation.
Loading history...
921
			{
922
				$params['mailbody'] = $this->get_load_email_data($params['uid'], null, $mailbox);
923
			}
924
		}
925
		switch ($_action)
926
		{
927
			case 'spam':
928
				$msg[] = $this->ajax_copyMessages($junk, array(
929
					'all' => false,
930
					'msg' => $messages
931
					), 'move', null, true);
932
				$refresh = true;
933
				break;
934
			case 'ham':
935
				if ($this->mail_bo->icServer->acc_folder_ham && empty($this->mail_bo->icServer->acc_spam_api))
936
				{
937
					$msg[] = $this->ajax_copyMessages($ham, array(
938
						'all' => false,
939
						'msg' => $messages
940
						), 'copy', null, true);
941
				}
942
				// Move mails to Inbox if they are in Junk folder
943
				if ($junk == $this->mail_bo->profileID.self::$delimiter.$mailbox)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mailbox seems to be defined by a foreach iteration on line 914. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
944
				{
945
					$msg[] = $this->ajax_copyMessages($inbox, array(
946
						'all' => false,
947
						'msg' => $messages
948
					), 'move', null, true);
949
					$refresh = true;
950
				}
951
				break;
952
		}
953
		if ($GLOBALS['egw_info']['apps']['stylite'] && $this->mail_bo->icServer->acc_spam_api)
954
		{
955
			stylite_mail_spamtitan::setActionItems($_action, $_items, array(
0 ignored issues
show
Bug introduced by
The type stylite_mail_spamtitan was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
956
				'userpwd'	=> $this->mail_bo->icServer->acc_imap_password,
957
				'user'		=> $this->mail_bo->icServer->acc_imap_username,
958
				'api_url'	=> $this->mail_bo->icServer->acc_spam_api
959
			));
960
961
			// sync aliases to SpamTitan when the first spam action in a session is used
962
			if (Api\Mail\Account::read($this->mail_bo->profileID)->acc_smtp_type !== 'EGroupware\\Api\\Mail\\Smtp' &&
963
				!Api\Cache::getSession('SpamTitian', 'AliasesSynced-'.$this->mail_bo->icServer->acc_id.'-'.$this->mail_bo->icServer->acc_imap_username))
964
			{
965
				$data = Api\Mail\Account::read($this->mail_bo->profileID)->smtpServer()->getUserData($GLOBALS['egw_info']['user']['account_id']);
966
				if (($m = stylite_mail_spamtitan::setActionItems('sync_aliases',
967
					array(array_merge((array)$data['mailLocalAddress'], (array)$data['mailAlternateAddress'])), array(
968
					'userpwd'	=> $this->mail_bo->icServer->acc_imap_password,
969
					'user'		=> $this->mail_bo->icServer->acc_imap_username,
970
					'api_url'	=> $this->mail_bo->icServer->acc_spam_api
971
				))))
972
				{
973
					$msg[] = $m;
974
				}
975
				Api\Cache::setSession('SpamTitian', 'AliasesSynced-'.$this->mail_bo->icServer->acc_id.'-'.$this->mail_bo->icServer->acc_imap_username, true);
976
			}
977
		}
978
979
		if ($refresh)
0 ignored issues
show
introduced by
The condition $refresh is always false.
Loading history...
980
		{
981
			$response->data([implode('\n',$msg),$messages]);
982
		}
983
		else
984
		{
985
			$response->apply('egw.message',[implode('\n',$msg)]);
986
		}
987
	}
988
989
	/**
990
	 * Build spam actions
991
	 *
992
	 * @return array actions
993
	 */
994
	public function getSpamActions ()
995
	{
996
		$actions = array (
997
			'spamfilter' => array (
998
				'caption'	=> 'Spam',
999
				'icon'		=> 'dhtmlxtree/MailFolderJunk',
1000
				'allowOnMultiple' => true,
1001
				'children'	=> array (
1002
					'spam' => array (
1003
						'caption'	=> 'Report as Spam',
1004
						'icon'		=> 'dhtmlxtree/MailFolderJunk',
1005
						'onExecute' => 'javaScript:app.mail.spam_actions',
1006
						'hint'		=> 'Report this email content as Spam - spam solutions like spamTitan will learn',
1007
						'allowOnMultiple' => true
1008
					),
1009
					'ham' => array (
1010
						'caption'	=> 'Report as Ham',
1011
						'icon'		=> 'dhtmlxtree/MailFolderHam',
1012
						'onExecute' => 'javaScript:app.mail.spam_actions',
1013
						'hint'		=> 'Report this email content as Ham (not spam) - spam solutions like spamTitan will learn',
1014
						'allowOnMultiple' => true
1015
					)
1016
				)
1017
			)
1018
		);
1019
		$account = Mail\Account::read($this->mail_bo->profileID);
1020
		// spamTitan actions
1021
		if ($account->acc_spam_api)
1022
		{
1023
			$actions['spamfilter']['children'] = array_merge($actions['spamfilter']['children'], stylite_mail_spamtitan::getActions());
1024
		}
1025
		return $actions;
1026
	}
1027
1028
	/**
1029
	 * Get actions / context menu for index
1030
	 *
1031
	 * Changes here, require to log out, as $content[self::$nm_index] get stored in session!
1032
	 * @return array see nextmatch_widget::egw_actions()
1033
	 */
1034
	private function get_actions()
1035
	{
1036
		static $accArray=array(); // buffer identity names on single request
1037
		// duplicated from mail_hooks
1038
		static $deleteOptions = array(
1039
			'move_to_trash'		=> 'move to trash',
1040
			'mark_as_deleted'	=> 'mark as deleted',
1041
			'remove_immediately' =>	'remove immediately',
1042
		);
1043
		// todo: real hierarchical folder list
1044
		$lastFolderUsedForMove = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $lastFolderUsedForMove is dead and can be removed.
Loading history...
1045
		$moveactions = array();
1046
		$archiveFolder = $this->mail_bo->getArchiveFolder();
1047
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
1048
		//error_log(__METHOD__.__LINE__." StoredFolders->".array2string($lastFoldersUsedForMoveCont));
1049
		//error_log(__METHOD__.__LINE__.' ProfileId:'.$this->mail_bo->profileID." StoredFolders->(".count($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]).") ".array2string($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]));
1050
		if (is_null($accArray))
1051
		{
1052
			foreach(Mail\Account::search($only_current_user=true, false) as $acc_id => $accountObj)
1053
			{
1054
				//error_log(__METHOD__.__LINE__.array2string($accountObj));
1055
				if (!$accountObj->is_imap())
1056
				{
1057
					// not to be used for IMAP Foldertree, as there is no Imap host
1058
					continue;
1059
				}
1060
				$identity_name = Mail\Account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']);
1061
				$accArray[$acc_id] = str_replace(array('<','>'),array('[',']'),$identity_name);// as angle brackets are quoted, display in Javascript messages when used is ugly, so use square brackets instead
1062
			}
1063
		}
1064
		if (!is_array($lastFoldersUsedForMoveCont)) $lastFoldersUsedForMoveCont=array();
1065
		foreach (array_keys($lastFoldersUsedForMoveCont) as $pid)
1066
		{
1067
			if ($this->mail_bo->profileID==$pid && isset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]))
1068
			{
1069
				$_folder = $this->mail_bo->icServer->getCurrentMailbox();
1070
				//error_log(__METHOD__.__LINE__.' '.$_folder."<->".$lastFoldersUsedForMoveCont[$this->mail_bo->profileID].function_backtrace());
1071
				$counter =1;
1072
				foreach ($lastFoldersUsedForMoveCont[$this->mail_bo->profileID] as $i => $lastFolderUsedForMoveCont)
1073
				{
1074
					$moveaction = 'move_';
1075
					if ($_folder!=$i)
1076
					{
1077
						$moveaction .= $lastFolderUsedForMoveCont;
1078
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
1079
						//error_log(__METHOD__.__LINE__.'#'.$currentArchiveActionKey);
1080
						if ($this->mail_bo->folderExists($i)) // only 10 entries per mailaccount.Control this on setting the buffered folders
1081
						{
1082
							$fS['profileID'] = $this->mail_bo->profileID;
1083
							$fS['profileName'] = $accArray[$this->mail_bo->profileID];
1084
							$fS['shortDisplayName'] = $i;
1085
							$moveactions[$moveaction] = $fS;
1086
							$counter ++;
1087
						}
1088
						else
1089
						{
1090
							unset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID][$i]);
1091
						}
1092
						//error_log(array2string($moveactions[$moveaction]));
1093
					}
1094
				}
1095
			}
1096
			elseif ($this->mail_bo->profileID!=$pid && isset($lastFoldersUsedForMoveCont[$pid]) && !empty($lastFoldersUsedForMoveCont[$pid]))
1097
			{
1098
				$counter =1;
1099
				foreach ($lastFoldersUsedForMoveCont[$pid] as $i => $lastFolderUsedForMoveCont)
1100
				{
1101
					//error_log(__METHOD__.__LINE__."$i => $lastFolderUsedForMoveCont");
1102
					if (!empty($lastFolderUsedForMoveCont)) // only 10 entries per mailaccount.Control this on setting the buffered folders
1103
					{
1104
						$moveaction = 'move_'.$lastFolderUsedForMoveCont;
1105
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
1106
						$fS = array();
1107
						$fS['profileID'] = $pid;
1108
						$fS['profileName'] = $accArray[$pid];
1109
						$fS['shortDisplayName'] = $i;
1110
						$moveactions[$moveaction] = $fS;
1111
						$counter ++;
1112
					}
1113
				}
1114
			}
1115
		}
1116
		Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
1117
		$group = 0;
1118
		$actions =  array(
1119
			'open' => array(
1120
				'caption' => lang('Open'),
1121
				'icon' => 'view',
1122
				'group' => ++$group,
1123
				'onExecute' => Api\Header\UserAgent::mobile()?'javaScript:app.mail.mobileView':'javaScript:app.mail.mail_open',
1124
				'allowOnMultiple' => false,
1125
				'default' => true,
1126
				'mobileViewTemplate' => 'view?'.filemtime(Api\Etemplate\Widget\Template::rel2path('/mail/templates/mobile/view.xet'))
1127
			),
1128
			'reply' => array(
1129
				'caption' => 'Reply',
1130
				'icon' => 'mail_reply',
1131
				'group' => ++$group,
1132
				'onExecute' => 'javaScript:app.mail.mail_compose',
1133
				'allowOnMultiple' => false,
1134
				'toolbarDefault' => true
1135
			),
1136
			'reply_all' => array(
1137
				'caption' => 'Reply All',
1138
				'icon' => 'mail_replyall',
1139
				'group' => $group,
1140
				'onExecute' => 'javaScript:app.mail.mail_compose',
1141
				'allowOnMultiple' => false,
1142
				'shortcut' => array('ctrl' => true, 'shift' => true, 'keyCode' => 65, 'caption' => 'Ctrl + Shift + A'),
1143
			),
1144
			'forward' => array(
1145
				'caption' => 'Forward',
1146
				'icon' => 'mail_forward',
1147
				'group' => $group,
1148
				'children' => array(
1149
					'forwardinline' => array(
1150
						'caption' => 'Inline',
1151
						'icon' => 'mail_forward',
1152
						'group' => $group,
1153
						'hint' => 'forward inline',
1154
						'onExecute' => 'javaScript:app.mail.mail_compose',
1155
						'allowOnMultiple' => false,
1156
						'shortcut' => array('ctrl' => true, 'keyCode' => 70, 'caption' => 'Ctrl + F'),
1157
						'toolbarDefault' => true
1158
					),
1159
					'forwardasattach' => array(
1160
						'caption' => 'Attachment',
1161
						'hint' => 'forward as attachment',
1162
						'icon' => 'mail_forward_attach',
1163
						'group' => $group,
1164
						'onExecute' => 'javaScript:app.mail.mail_compose',
1165
					),
1166
				),
1167
				'hideOnMobile' => true
1168
			),
1169
			'composeasnew' => array(
1170
				'caption' => 'Compose',
1171
				'icon' => 'new',
1172
				'hint' => 'Compose as new',
1173
				'group' => $group,
1174
				'onExecute' => 'javaScript:app.mail.mail_compose',
1175
				'allowOnMultiple' => false,
1176
			),
1177
			'modifysubject' => array(
1178
				'caption' => 'Modify Subject',
1179
				'icon' => 'edit',
1180
				'hint' => 'Modify subject of this message',
1181
				'group' => $group,
1182
				'onExecute' => 'javaScript:app.mail.modifyMessageSubjectDialog',
1183
				'allowOnMultiple' => false,
1184
				'shortcut' =>  array('ctrl' => true, 'keyCode' => 77, 'caption' => 'Ctrl + M'),
1185
			)
1186
		);
1187
		$macounter=0;
1188
		if (!empty($moveactions))
1189
		{
1190
			//error_log(__METHOD__.__LINE__.array2string($moveactions));
1191
			$children=array();
1192
			$pID=0;
1193
			foreach ($moveactions as $moveaction => $lastFolderUsedForMove)
1194
			{
1195
				$group = ($pID != $lastFolderUsedForMove['profileID'] && $macounter>0? $group+1 : $group);
1196
				//error_log(__METHOD__.__LINE__."#$pID != ".$lastFolderUsedForMove['profileID']."#".$macounter.'#'.$groupCounter.'#');
1197
				$children = array_merge($children,
1198
					array(
1199
						$moveaction => array(
1200
							'caption' => (!empty($lastFolderUsedForMove['profileName'])?$lastFolderUsedForMove['profileName']:'('.$lastFolderUsedForMove['profileID'].')').': '.(isset($lastFolderUsedForMove['shortDisplayName'])?$lastFolderUsedForMove['shortDisplayName']:''),
1201
							'icon' => 'move',
1202
							'group' => $group,
1203
							'onExecute' => 'javaScript:app.mail.mail_move2folder',
1204
							'allowOnMultiple' => true,
1205
						)
1206
					)
1207
				);
1208
				$pID = $lastFolderUsedForMove['profileID'];
1209
				$macounter++;
1210
			}
1211
			$actions['moveto'] =	array(
1212
				'caption' => lang('Move selected to'),
1213
				'icon' => 'move',
1214
				'group' => $group,
1215
				'children' => $children,
1216
			);
1217
1218
		} else {
1219
			$group++;
1220
		}
1221
		$spam_actions = $this->getSpamActions();
1222
		$group++;
1223
		foreach ($spam_actions as &$action)
1224
		{
1225
			$action['group'] = $group;
1226
		}
1227
		//error_log(__METHOD__.__LINE__.$archiveFolder);
1228
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter.$archiveFolder] = array( //toarchive
0 ignored issues
show
Bug introduced by
Are you sure $archiveFolder of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1228
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter./** @scrutinizer ignore-type */ $archiveFolder] = array( //toarchive
Loading history...
1229
			'caption' => 'Move to archive',
1230
			'hint' => 'move selected mails to archive',
1231
			'icon' => 'archive',
1232
			'group' => $group++,
1233
			'enabled' => 'javaScript:app.mail.archivefolder_enabled',
1234
			//'hideOnDisabled' => true, // does not work as expected on message-list
1235
			'onExecute' => 'javaScript:app.mail.mail_move2folder',
1236
			'shortcut' => KeyManager::shortcut(KeyManager::V, true, true),
1237
			'allowOnMultiple' => true,
1238
			'toolbarDefault' => false
1239
		);
1240
1241
		$actions += array(
1242
			'infolog' => array(
1243
				'caption' => 'InfoLog',
1244
				'hint' => 'Save as InfoLog',
1245
				'icon' => 'infolog/navbar',
1246
				'group' => ++$group,
1247
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1248
				'popup' => Link::get_registry('infolog', 'add_popup'),
1249
				'allowOnMultiple' => false,
1250
				'toolbarDefault' => true
1251
			),
1252
			'tracker' => array(
1253
				'caption' => 'Tracker',
1254
				'hint' => 'Save as ticket',
1255
				'group' => $group,
1256
				'icon' => 'tracker/navbar',
1257
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1258
				'popup' => Link::get_registry('tracker', 'add_popup'),
1259
				'mail_import' => Api\Hooks::single(array('location' => 'mail_import'),'tracker'),
1260
				'allowOnMultiple' => false,
1261
			),
1262
			'calendar' => array(
1263
				'caption' => 'Calendar',
1264
				'hint' => 'Save as Calendar',
1265
				'icon' => 'calendar/navbar',
1266
				'group' => $group,
1267
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1268
				'popup' => Link::get_registry('calendar', 'add_popup'),
1269
				'allowOnMultiple' => false,
1270
				'toolbarDefault' => true
1271
			),
1272
			'print' => array(
1273
				'caption' => 'Print',
1274
				'group' => ++$group,
1275
				'onExecute' => 'javaScript:app.mail.mail_print',
1276
				'allowOnMultiple' => false,
1277
				'hideOnMobile' => true
1278
			),
1279
			'save' => array(
1280
				'caption' => 'Save',
1281
				'group' => $group,
1282
				'icon' => 'fileexport',
1283
				'children' => array(
1284
					'save2disk' => array(
1285
						'caption' => 'Save to disk',
1286
						'hint' => 'Save message to disk',
1287
						'group' => $group,
1288
						'icon' => 'fileexport',
1289
						'onExecute' => 'javaScript:app.mail.mail_save',
1290
						'allowOnMultiple' => true,
1291
						'hideOnMobile' => true
1292
					),
1293
					'save2filemanager' => array(
1294
						'caption' => 'Filemanager',
1295
						'hint' => 'Save to filemanager',
1296
						'group' => $group,
1297
						'icon' => 'filemanager/navbar',
1298
						'onExecute' => 'javaScript:app.mail.mail_save2fm',
1299
						'allowOnMultiple' => true,
1300
					),
1301
				),
1302
				'hideOnMobile' => true
1303
			),
1304
			'view' => array(
1305
				'caption' => 'View',
1306
				'group' => $group,
1307
				'icon' => 'kmmsgread',
1308
				'children' => array(
1309
					'header' => array(
1310
						'caption' => 'Header',
1311
						'hint' => 'View header lines',
1312
						'group' => $group,
1313
						'icon' => 'kmmsgread',
1314
						'onExecute' => 'javaScript:app.mail.mail_header',
1315
						'allowOnMultiple' => false,
1316
					),
1317
					'mailsource' => array(
1318
						'caption' => 'Source',
1319
						'hint' => 'View full Mail Source',
1320
						'group' => $group,
1321
						'icon' => 'source',
1322
						'onExecute' => 'javaScript:app.mail.mail_mailsource',
1323
						'allowOnMultiple' => false,
1324
					),
1325
					'openastext' => array(
1326
						'caption' => lang('Text mode'),
1327
						'hint' => 'Open in Text mode',
1328
						'group' => ++$group,
1329
						'icon' => 'textmode',
1330
						'onExecute' => 'javaScript:app.mail.mail_openAsText',
1331
						'allowOnMultiple' => false,
1332
					),
1333
					'openashtml' => array(
1334
						'caption' => lang('HTML mode'),
1335
						'hint' => 'Open in HTML mode',
1336
						'group' => $group,
1337
						'icon' => 'htmlmode',
1338
						'onExecute' => 'javaScript:app.mail.mail_openAsHtml',
1339
						'allowOnMultiple' => false,
1340
					),
1341
				),
1342
				'hideOnMobile' => true
1343
			),
1344
			'mark' => array(
1345
				'caption' => 'Set / Remove Flags',
1346
				'icon' => 'kmmsgread',
1347
				'group' => ++$group,
1348
				'children' => array(
1349
					// icons used from http://creativecommons.org/licenses/by-sa/3.0/
1350
					// Artist: Led24
1351
					// Iconset Homepage: http://led24.de/iconset
1352
					// License: CC Attribution 3.0
1353
					'setLabel' => array(
1354
						'caption' => 'Set / Remove Labels',
1355
						'icon' => 'tag_message',
1356
						'group' => ++$group,
1357
						// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
1358
						'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
1359
						'hideOnDisabled' => true,
1360
						'children' => array(
1361
							'unlabel' => array(
1362
								'group' => ++$group,
1363
								'caption' => "<font color='#ff0000'>".lang('remove all')."</font>",
1364
								'icon' => 'mail_label',
1365
								'onExecute' => 'javaScript:app.mail.mail_flag',
1366
								'shortcut' => KeyManager::shortcut(KeyManager::_0, true, true),
1367
							),
1368
							'label1' => array(
1369
								'group' => ++$group,
1370
								'caption' => "<font color='#ff0000'>".lang('important')."</font>",
1371
								'icon' => 'mail_label1',
1372
								'onExecute' => 'javaScript:app.mail.mail_flag',
1373
								'shortcut' => KeyManager::shortcut(KeyManager::_1, true, true),
1374
							),
1375
							'label2' => array(
1376
								'group' => $group,
1377
								'caption' => "<font color='#ff8000'>".lang('job')."</font>",
1378
								'icon' => 'mail_label2',
1379
								'onExecute' => 'javaScript:app.mail.mail_flag',
1380
								'shortcut' => KeyManager::shortcut(KeyManager::_2, true, true),
1381
							),
1382
							'label3' => array(
1383
								'group' => $group,
1384
								'caption' => "<font color='#008000'>".lang('personal')."</font>",
1385
								'icon' => 'mail_label3',
1386
								'onExecute' => 'javaScript:app.mail.mail_flag',
1387
								'shortcut' => KeyManager::shortcut(KeyManager::_3, true, true),
1388
							),
1389
							'label4' => array(
1390
								'group' => $group,
1391
								'caption' => "<font color='#0000ff'>".lang('to do')."</font>",
1392
								'icon' => 'mail_label4',
1393
								'onExecute' => 'javaScript:app.mail.mail_flag',
1394
								'shortcut' => KeyManager::shortcut(KeyManager::_4, true, true),
1395
							),
1396
							'label5' => array(
1397
								'group' => $group,
1398
								'caption' => "<font color='#8000ff'>".lang('later')."</font>",
1399
								'icon' => 'mail_label5',
1400
								'onExecute' => 'javaScript:app.mail.mail_flag',
1401
								'shortcut' => KeyManager::shortcut(KeyManager::_5, true, true),
1402
							),
1403
						),
1404
					),
1405
					// modified icons from http://creativecommons.org/licenses/by-sa/3.0/
1406
					'flagged' => array(
1407
						'group' => ++$group,
1408
						'caption' => 'Flag / Unflag',
1409
						'icon' => 'unread_flagged_small',
1410
						'onExecute' => 'javaScript:app.mail.mail_flag',
1411
						'hint' => 'Flag or Unflag a mail',
1412
						'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
1413
						'toolbarDefault' => true
1414
					),
1415
					'read' => array(
1416
						'group' => $group,
1417
						'caption' => 'Read / Unread',
1418
						'icon' => 'kmmsgread',
1419
						'onExecute' => 'javaScript:app.mail.mail_flag',
1420
						'shortcut' => KeyManager::shortcut(KeyManager::U, true, true),
1421
1422
					),
1423
					'readall' => array(
1424
						'group' => ++$group,
1425
						'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
1426
						'icon' => 'kmmsgread',
1427
						'onExecute' => 'javaScript:app.mail.mail_flag',
1428
						'hint' => 'mark all messages in folder as read',
1429
						'toolbarDefault' => false
1430
					),
1431
					'undelete' => array(
1432
						'group' => $group,
1433
						'caption' => 'Undelete',
1434
						'icon' => 'revert',
1435
						'onExecute' => 'javaScript:app.mail.mail_flag',
1436
					),
1437
				),
1438
			),
1439
			'delete' => array(
1440
				'caption' => 'Delete',
1441
				'hint' => $deleteOptions[$this->mail_bo->mailPreferences['deleteOptions']],
1442
				'group' => ++$group,
1443
				'onExecute' => 'javaScript:app.mail.mail_delete',
1444
				'toolbarDefault' => true
1445
			),
1446
			'drag_mail' => array(
1447
				'dragType' => array('mail'),
1448
				'type' => 'drag',
1449
				//'onExecute' => 'javaScript:app.mail.mail_dragStart',
1450
			)
1451
		);
1452
		//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
1453
		// save as tracker, save as infolog, as this are actions that are either available for all, or not, we do that for all and not via css-class disabling
1454
		if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
1455
		{
1456
			unset($actions['infolog']);
1457
		}
1458
		if (!isset($GLOBALS['egw_info']['user']['apps']['tracker']))
1459
		{
1460
			unset($actions['tracker']);
1461
		}
1462
		if (!isset($GLOBALS['egw_info']['user']['apps']['calendar']))
1463
		{
1464
			unset($actions['calendar']);
1465
		}
1466
		// remove vfs actions if the user has no run access to filemanager
1467
		if (!$GLOBALS['egw_info']['user']['apps']['filemanager'])
1468
		{
1469
			unset($actions['save']['children']['save2filemanager']);
1470
		}
1471
		return array_merge($actions, $spam_actions);
1472
	}
1473
1474
	/**
1475
	 * Callback to fetch the rows for the nextmatch widget
1476
	 *
1477
	 * Function is static to not automatic call constructor in case profile is changed.
1478
	 *
1479
	 * @param array $query
1480
	 * @param array &$rows
1481
	 * @param array &$readonlys
1482
	 */
1483
	public static function get_rows(&$query,&$rows,&$readonlys)
1484
	{
1485
		unset($readonlys);	// not used, but required by function signature
1486
1487
		// handle possible profile change in get_rows
1488
		if (!empty($query['selectedFolder']))
1489
		{
1490
			list($_profileID,$folderName) = explode(self::$delimiter, $query['selectedFolder'], 2);
1491
			if (is_numeric(($_profileID)) && $_profileID != $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])
1492
			{
1493
				try {
1494
					$mail_ui = new mail_ui(false);	// do NOT run constructor, as we change profile anyway
1495
					$mail_ui->changeProfile($_profileID);
1496
					$query['actions'] = $mail_ui->get_actions();
1497
				}
1498
				catch(Exception $e)
1499
				{
1500
					unset($e);
1501
					$rows=array();
1502
					return 0;
1503
				}
1504
				if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
1505
			}
1506
		}
1507
		if (!isset($mail_ui))
1508
		{
1509
			try
1510
			{
1511
				$mail_ui = new mail_ui(true);	// run constructor for current profile
1512
			}
1513
			catch(Exception $e)
1514
			{
1515
				unset($e);
1516
				$rows=array();
1517
				return 0;
1518
			}
1519
			if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
1520
		}
1521
		//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
1522
		//Mail::$debugTimes=true;
1523
		if (Mail::$debugTimes) $starttime = microtime(true);
1524
		//$query['search'] is the phrase in the searchbox
1525
1526
		$mail_ui->mail_bo->restoreSessionData();
1527
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$query['selectedFolder'];
1528
1529
		$sRToFetch = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $sRToFetch is dead and can be removed.
Loading history...
1530
		list($_profileID,$_folderName) = explode(self::$delimiter,$query['selectedFolder'],2);
1531
		if (strpos($_folderName,self::$delimiter)!==false)
1532
		{
1533
			list($app,$_profileID,$_folderName) = explode(self::$delimiter,$_folderName,3);
1534
			unset($app);
1535
		}
1536
		//save selected Folder to sessionData (mailbox)->currentFolder
1537
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$_folderName;
1538
		$toSchema = false;//decides to select list schema with column to selected (if false fromaddress is default)
1539
		if ($mail_ui->mail_bo->folderExists($_folderName))
1540
		{
1541
			$toSchema = $mail_ui->mail_bo->isDraftFolder($_folderName,false)||$mail_ui->mail_bo->isSentFolder($_folderName,false)||$mail_ui->mail_bo->isTemplateFolder($_folderName,false);
1542
		}
1543
		else
1544
		{
1545
			// take the extra time on failure
1546
			if (!$mail_ui->mail_bo->folderExists($_folderName,true))
1547
			{
1548
				//error_log(__METHOD__.__LINE__.' Test on Folder:'.$_folderName.' failed; Using INBOX instead');
1549
				$query['selectedFolder']=$mail_ui->mail_bo->sessionData['mailbox']=$_folderName='INBOX';
1550
			}
1551
		}
1552
		$rowsFetched['messages'] = null;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$rowsFetched was never initialized. Although not strictly required by PHP, it is generally a good practice to add $rowsFetched = array(); before regardless.
Loading history...
1553
		$offset = $query['start']+1; // we always start with 1
1554
		$maxMessages = $query['num_rows'];
1555
		//error_log(__METHOD__.__LINE__.array2string($query));
1556
		$sort = ($query['order']=='address'?($toSchema?'toaddress':'fromaddress'):$query['order']);
1557
		if (!empty($query['search'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
1558
		{
1559
			if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1560
			{
1561
				Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
1562
				if (!isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1563
				{
1564
					Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]=true;
1565
				}
1566
			}
1567
			//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
1568
			$cutoffdate = $cutoffdate2 = null;
1569
			if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
1570
			if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
1571
			//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
1572
			$filter = array(
1573
				'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
1574
				'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
1575
				'string' => $query['search'],
1576
				'status' => 'any',
1577
				//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
1578
			);
1579
			if ($query['enddate']||$query['startdate']) {
1580
				$filter['range'] = "BETWEEN";
1581
				if ($cutoffdate) {
1582
					$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
1583
					if (empty($cutoffdate2)) $filter['range'] = "SINCE";
1584
				}
1585
				if ($cutoffdate2) {
1586
					$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
1587
					if (empty($cutoffdate)) $filter['range'] = "BEFORE";
1588
				}
1589
			}
1590
		}
1591
		else
1592
		{
1593
			$filter = array();
1594
		}
1595
		if ($query['filter'])
1596
		{
1597
			$filter['status'] = $query['filter'];
1598
		}
1599
		$reverse = ($query['sort']=='ASC'?false:true);
1600
		$prefchanged = false;
1601
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']) || ($query['cat_id'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']))
1602
		{
1603
			//error_log(__METHOD__.__LINE__.' Changing userPref ActivesearchType:'.$query['cat_id']);
1604
			$GLOBALS['egw']->preferences->add('mail','ActiveSearchType',$query['cat_id'],'user');
1605
			$prefchanged = true;
1606
		}
1607
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']) || ($query['filter2'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']))
1608
		{
1609
			$GLOBALS['egw']->preferences->add('mail','ShowDetails',$query['filter2'],'user');
1610
			$prefchanged = true;
1611
		}
1612
		if ($prefchanged)
1613
		{
1614
			// save prefs
1615
			$GLOBALS['egw']->preferences->save_repository(true);
1616
		}
1617
		//error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($mail_ui->sessionData['messageFilter']));
1618
/*
1619
$cutoffdate = Api\DateTime::to('now','ts')-(3600*24*6);//SINCE, enddate
1620
$cutoffdate2 = Api\DateTime::to('now','ts')-(3600*24*3);//BEFORE, startdate
1621
$filter['range'] = "BETWEEN";// we support SINCE, BEFORE, BETWEEN and ON
1622
$filter['since'] = date("d-M-Y", $cutoffdate);
1623
$filter['before']= date("d-M-Y", $cutoffdate2);
1624
*/
1625
		$sR = array();
1626
		try
1627
		{
1628
			if ($maxMessages > 75)
1629
			{
1630
				$rByUid = true;
1631
				$_sR = $mail_ui->mail_bo->getSortedList(
1632
					$_folderName,
1633
					$sort,
1634
					$reverse,
1635
					$filter,
1636
					$rByUid
1637
				);
1638
				$rowsFetched['messages'] = $_sR['count'];
1639
				$ids = $_sR['match']->ids;
1640
				// if $sR is false, something failed fundamentally
1641
				if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
1642
				$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
1643
				$sRToFetch = $sR;//array_slice($sR,0,50); // we fetch only the headers of a subset of the fetched uids
1644
				//error_log(__METHOD__.__LINE__.' Rows fetched (UID only):'.count($sR).' Data:'.array2string($sR));
1645
				$maxMessages = 75;
1646
				$sortResultwH['header'] = array();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sortResultwH was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sortResultwH = array(); before regardless.
Loading history...
1647
				if (count($sRToFetch)>0)
1648
				{
1649
					//error_log(__METHOD__.__LINE__.' Headers to fetch with UIDs:'.count($sRToFetch).' Data:'.array2string($sRToFetch));
1650
					$sortResult = array();
1651
					// fetch headers
1652
					$sortResultwH = $mail_ui->mail_bo->getHeaders(
1653
						$_folderName,
1654
						$offset,
1655
						$maxMessages,
1656
						$sort,
1657
						$reverse,
1658
						$filter,
1659
						$sRToFetch,
1660
						true, //cacheResult
1661
						($query['filter2']?true:false) // fetchPreview
1662
					);
1663
				}
1664
			}
1665
			else
1666
			{
1667
				$sortResult = array();
1668
				// fetch headers
1669
				$sortResultwH = $mail_ui->mail_bo->getHeaders(
1670
					$_folderName,
1671
					$offset,
1672
					$maxMessages,
1673
					$sort,
1674
					$reverse,
1675
					$filter,
1676
					null, // this uids only
1677
					true, // cacheResult
1678
					($query['filter2']?true:false) // fetchPreview
1679
				);
1680
				$rowsFetched['messages'] = $sortResultwH['info']['total'];
1681
			}
1682
		}
1683
		catch (Exception $e)
1684
		{
1685
			$sortResultwH=array();
1686
			$sR=array();
1687
			self::callWizard($e->getMessage(), false, 'error');
1688
		}
1689
		$response = Api\Json\Response::get();
1690
		// unlock immediately after fetching the rows
1691
		if (stripos($_GET['menuaction'],'ajax_get_rows')!==false)
1692
		{
1693
			//error_log(__METHOD__.__LINE__.' unlock tree ->'.$_GET['menuaction']);
1694
			$response->call('app.mail.unlock_tree');
1695
		}
1696
1697
		if (is_array($sR) && count($sR)>0)
1698
		{
1699
			foreach ((array)$sR as $key => $v)
1700
			{
1701
				if (array_key_exists($key,(array)$sortResultwH['header'])==true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1702
				{
1703
					$sortResult['header'][] = $sortResultwH['header'][$key];
1704
				}
1705
				else
1706
				{
1707
					if (!empty($v)) $sortResult['header'][] = array('uid'=>$v);
1708
				}
1709
			}
1710
		}
1711
		else
1712
		{
1713
			$sortResult = $sortResultwH;
1714
		}
1715
		$rowsFetched['rowsFetched'] = $sortResult['header'] ? count($sortResult['header']) : 0;
1716
		if (empty($rowsFetched['messages'])) $rowsFetched['messages'] = $rowsFetched['rowsFetched'];
1717
1718
		//error_log(__METHOD__.__LINE__.' Rows fetched:'.$rowsFetched.' Data:'.array2string($sortResult));
1719
		$cols = array('row_id','uid','status','attachments','subject','address','toaddress','fromaddress','ccaddress','additionaltoaddress','date','size','modified','bodypreview', 'security');
1720
		if ($GLOBALS['egw_info']['user']['preferences']['common']['select_mode']=='EGW_SELECTMODE_TOGGLE') unset($cols[0]);
1721
		$rows = $mail_ui->header2gridelements($sortResult['header'],$cols, $_folderName, $folderType=$toSchema);
1722
1723
		// Save the session (since we are committing session) at the end
1724
		// to make sure all necessary data are stored in session.
1725
		// e.g.: Link:: get_data which is used to read attachments data.
1726
		$mail_ui->mail_bo->saveSessionData();
1727
1728
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName.' Start:'.$query['start'].' NumRows:'.$query['num_rows'],__METHOD__.__LINE__);
1729
		return $rowsFetched['messages'];
1730
	}
1731
1732
	/**
1733
	 * function createRowID - create a unique rowID for the grid
1734
	 *
1735
	 * @param string $_folderName used to ensure the uniqueness of the uid over all folders
1736
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1737
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1738
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1739
	 */
1740
	function createRowID($_folderName, $message_uid, $_prependApp=false)
1741
	{
1742
		return self::generateRowID($this->mail_bo->profileID, $_folderName, $message_uid, $_prependApp);
1743
	}
1744
1745
	/**
1746
	 * static function generateRowID - create a unique rowID for the grid
1747
	 *
1748
	 * @param integer $_profileID profile ID for the rowid to be used
1749
	 * @param string $_folderName to ensure the uniqueness of the uid over all folders
1750
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1751
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1752
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1753
	 */
1754
	static function generateRowID($_profileID, $_folderName, $message_uid, $_prependApp=false)
1755
	{
1756
		return ($_prependApp?'mail'.self::$delimiter:'').trim($GLOBALS['egw_info']['user']['account_id']).self::$delimiter.$_profileID.self::$delimiter.base64_encode($_folderName).self::$delimiter.$message_uid;
1757
	}
1758
1759
	/**
1760
	 * function splitRowID - split the rowID into its parts
1761
	 *
1762
	 * @param string $_rowID string - a colon separated string in the form accountID:profileID:folder:message_uid
1763
	 * @return array populated named result array (accountID,profileID,folder,msgUID)
1764
	 */
1765
	static function splitRowID($_rowID)
1766
	{
1767
		$res = explode(self::$delimiter,$_rowID);
1768
		// as a rowID is perceeded by app::, should be mail!
1769
		//error_log(__METHOD__.__LINE__.array2string($res).' [0] isInt:'.is_int($res[0]).' [0] isNumeric:'.is_numeric($res[0]).' [0] isString:'.is_string($res[0]).' Count:'.count($res));
1770
		if (count($res)==4 && is_numeric($res[0]) )
1771
		{
1772
			// we have an own created rowID; prepend app=mail
1773
			array_unshift($res,'mail');
1774
		}
1775
		return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
1776
	}
1777
1778
	/**
1779
	 * Get actions for preview toolbar
1780
	 *
1781
	 * @return array
1782
	 */
1783
	function get_toolbar_actions()
1784
	{
1785
		$actions = $this->get_actions();
1786
		$arrActions = array('composeasnew', 'reply', 'reply_all', 'forward', 'flagged', 'delete', 'print',
1787
			'infolog', 'tracker', 'calendar', 'save', 'view', 'read', 'label1',	'label2', 'label3',	'label4', 'label5','spam', 'ham');
1788
		foreach( $arrActions as &$act)
1789
		{
1790
			//error_log(__METHOD__.__LINE__.' '.$act.'->'.array2string($actions[$act]));
1791
			switch ($act)
1792
			{
1793
				case 'forward':
1794
					$actionsenabled[$act]=$actions[$act];
1795
					break;
1796
				case 'save':
1797
					$actionsenabled[$act]=$actions[$act];
1798
1799
					break;
1800
				case 'view':
1801
					$actionsenabled[$act]=$actions[$act];
1802
					break;
1803
				case 'flagged':
1804
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1805
					break;
1806
				case 'read':
1807
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1808
					break;
1809
				case 'label1':
1810
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('important');
1811
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1812
					break;
1813
				case 'label2':
1814
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('job');
1815
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1816
					break;
1817
				case 'label3':
1818
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('personal');
1819
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1820
					break;
1821
				case 'label4':
1822
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('to do');
1823
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1824
					break;
1825
				case 'label5':
1826
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('later');
1827
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1828
					break;
1829
				case 'ham':
1830
				case 'spam':
1831
					$actionsenabled[$act]= $actions['spamfilter']['children'][$act];
1832
					break;
1833
				default:
1834
					if (isset($actions[$act])) $actionsenabled[$act]=$actions[$act];
1835
			}
1836
		}
1837
		unset($actionsenabled['drag_mail']);
1838
		//error_log(array2string($actionsenabled['view']));
1839
		unset($actionsenabled['view']['children']['openastext']);//not supported in preview
1840
		unset($actionsenabled['view']['children']['openashtml']);//not supported in preview
1841
1842
		return $actionsenabled;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $actionsenabled seems to be defined by a foreach iteration on line 1788. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1843
	}
1844
1845
	/**
1846
	 * function header2gridelements - to populate the grid elements with the collected Data
1847
	 *
1848
	 * @param array $_headers headerdata to process
1849
	 * @param array $cols cols to populate
1850
	 * @param array $_folderName to ensure the uniqueness of the uid over all folders
1851
	 * @param array $_folderType used to determine if we need to populate from/to
1852
	 * @return array populated result array
1853
	 */
1854
	public function header2gridelements($_headers, $cols, $_folderName, $_folderType=0)
1855
	{
1856
		if (Mail::$debugTimes) $starttime = microtime(true);
1857
		$rv = array();
1858
		$i=0;
1859
		foreach((array)$_headers as $header)
1860
		{
1861
			$i++;
1862
			$data = array();
1863
			//error_log(__METHOD__.array2string($header));
1864
			$message_uid = $header['uid'];
1865
			$data['uid'] = $message_uid;
1866
			$data['row_id']=$this->createRowID($_folderName,$message_uid);
1867
1868
			if ($header['smimeType'])
1869
			{
1870
				$data['smime'] = Mail\Smime::isSmimeSignatureOnly($header['smimeType'])?
1871
				Mail\Smime::TYPE_SIGN : Mail\Smime::TYPE_ENCRYPT;
1872
			}
1873
1874
			$flags = "";
1875
			if(!empty($header['recent'])) $flags .= "R";
1876
			if(!empty($header['flagged'])) $flags .= "F";
1877
			if(!empty($header['answered'])) $flags .= "A";
1878
			if(!empty($header['forwarded'])) $flags .= "W";
1879
			if(!empty($header['deleted'])) $flags .= "D";
1880
			if(!empty($header['seen'])) $flags .= "S";
1881
			if(!empty($header['label1'])) $flags .= "1";
1882
			if(!empty($header['label2'])) $flags .= "2";
1883
			if(!empty($header['label3'])) $flags .= "3";
1884
			if(!empty($header['label4'])) $flags .= "4";
1885
			if(!empty($header['label5'])) $flags .= "5";
1886
1887
			$data["status"] = "<span class=\"status_img\"></span>";
1888
			//error_log(__METHOD__.array2string($header).' Flags:'.$flags);
1889
1890
			// the css for this row
1891
			$is_recent=false;
1892
			$css_styles = array("mail");
1893
			if ($header['deleted']) {
1894
				$css_styles[] = 'deleted';
1895
			}
1896
			if ($header['recent'] && !($header['deleted'] || $header['seen'] || $header['answered'] || $header['forwarded'])) {
1897
				$css_styles[] = 'recent';
1898
				$is_recent=true;
0 ignored issues
show
Unused Code introduced by
The assignment to $is_recent is dead and can be removed.
Loading history...
1899
			}
1900
			if ($header['priority'] < 3) {
1901
				$css_styles[] = 'prio_high';
1902
			}
1903
			if ($header['flagged']) {
1904
				$css_styles[] = 'flagged';
1905
			}
1906
			if (!$header['seen']) {
1907
				$css_styles[] = 'unseen'; // different status image for recent // solved via css !important
1908
			}
1909
			if ($header['answered']) {
1910
				$css_styles[] = 'replied';
1911
			}
1912
			if ($header['forwarded']) {
1913
				$css_styles[] = 'forwarded';
1914
			}
1915
			if ($header['label1']) {
1916
				$css_styles[] = 'labelone';
1917
			}
1918
			if ($header['label2']) {
1919
				$css_styles[] = 'labeltwo';
1920
			}
1921
			if ($header['label3']) {
1922
				$css_styles[] = 'labelthree';
1923
			}
1924
			if ($header['label4']) {
1925
				$css_styles[] = 'labelfour';
1926
			}
1927
			if ($header['label5']) {
1928
				$css_styles[] = 'labelfive';
1929
			}
1930
1931
			//error_log(__METHOD__.array2string($css_styles));
1932
1933
			if (in_array("subject", $cols))
1934
			{
1935
				// filter out undisplayable characters
1936
				$search = array('[\016]','[\017]',
1937
					'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
1938
					'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
1939
				$replace = '';
1940
1941
				$header['subject'] = preg_replace($search,$replace,$header['subject']);
1942
				// curly brackets get messed up by the template!
1943
1944
				if (!empty($header['subject'])) {
1945
					// make the subject shorter if it is to long
1946
					$subject = $header['subject'];
1947
				} else {
1948
					$subject = '('. lang('no subject') .')';
1949
				}
1950
1951
				$data["subject"] = $subject; // the mailsubject
1952
			}
1953
1954
			$imageHTMLBlock = '';
1955
			//error_log(__METHOD__.__LINE__.array2string($header));
1956
			if (in_array("attachments", $cols))
1957
			{
1958
				if (!empty($header['attachments']) && (in_array($header['mimetype'], array(
1959
						'multipart/mixed', 'multipart/signed', 'multipart/related', 'multipart/report',
1960
						'text/calendar', 'text/html', 'multipart/alternative',
1961
					)) ||
1962
					substr($header['mimetype'],0,11) == 'application' ||
1963
					substr($header['mimetype'],0,5) == 'audio' ||
1964
					substr($header['mimetype'],0,5) == 'video'))
1965
				{
1966
					$image = Api\Html::image('mail','attach');
0 ignored issues
show
Unused Code introduced by
The assignment to $image is dead and can be removed.
Loading history...
1967
					$datarowid = $this->createRowID($_folderName,$message_uid,true);
1968
					$attachments = $header['attachments'];
1969
					if (count($attachments) == 1)
1970
					{
1971
						$image = Api\Html::image('mail','attach',$attachments[0]['name'].(!empty($attachments[0]['mimeType'])?' ('.$attachments[0]['mimeType'].')':''));
1972
					}
1973
					else
1974
					{
1975
						$image = Api\Html::image('mail','attach',lang('%1 attachments',count($attachments)));
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with count($attachments). ( Ignorable by Annotation )

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

1975
						$image = Api\Html::image('mail','attach',/** @scrutinizer ignore-call */ lang('%1 attachments',count($attachments)));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1976
					}
1977
					$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1978
1979
					$attachmentFlag = $image;
1980
				}
1981
				else
1982
				{
1983
					$attachmentFlag = '&nbsp;';
1984
					$imageHTMLBlock = '';
1985
				}
1986
				// show priority flag
1987
				if ($header['priority'] < 3)
1988
				{
1989
					 $image = Api\Html::image('mail','prio_high');
1990
				}
1991
				elseif ($header['priority'] > 3)
1992
				{
1993
					$image = Api\Html::image('mail','prio_low');
1994
				}
1995
				else
1996
				{
1997
					$image = '';
1998
				}
1999
				// show a flag for flagged messages
2000
				$imageflagged ='';
2001
				if ($header['flagged'])
2002
				{
2003
					$imageflagged = Api\Html::image('mail','unread_flagged_small');
2004
				}
2005
				$data["attachments"] = $image.$attachmentFlag.$imageflagged; // icon for attachments available
2006
			}
2007
2008
			// sent or draft or template folder -> to address
2009
			if (in_array("toaddress", $cols))
2010
			{
2011
				// sent or drafts or template folder means foldertype > 0, use to address instead of from
2012
				$data["toaddress"] = $header['to_address'];//Mail::htmlentities($header['to_address'],$this->charset);
2013
			}
2014
2015
			if (in_array("additionaltoaddress", $cols))
2016
			{
2017
				$data['additionaltoaddress'] = $header['additional_to_addresses'];
2018
			}
2019
			//fromaddress
2020
			if (in_array("fromaddress", $cols))
2021
			{
2022
				$data["fromaddress"] = $header['sender_address'];
2023
			}
2024
			$data['additionalfromaddress'] = $header['additional_from_addresses'];
2025
			if (in_array("ccaddress", $cols))
2026
			{
2027
				$data['ccaddress'] = $header['cc_addresses'];
2028
			}
2029
			if (in_array("date", $cols))
2030
			{
2031
				$data["date"] = $header['date'];
2032
			}
2033
			if (in_array("modified", $cols))
2034
			{
2035
				$data["modified"] = $header['internaldate'];
2036
			}
2037
2038
			if (in_array("size", $cols))
2039
				$data["size"] = $header['size']; /// size
2040
2041
			$data["class"] = implode(' ', $css_styles);
2042
			//translate style-classes back to flags
2043
			$data['flags'] = Array();
2044
			if ($header['seen']) $data["flags"]['read'] = 'read';
2045
			foreach ($css_styles as &$flag) {
2046
				if ($flag!='mail')
2047
				{
2048
					if ($flag=='labelone') {$data["flags"]['label1'] = 'label1';}
2049
					elseif ($flag=='labeltwo') {$data["flags"]['label2'] = 'label2';}
2050
					elseif ($flag=='labelthree') {$data["flags"]['label3'] = 'label3';}
2051
					elseif ($flag=='labelfour') {$data["flags"]['label4'] = 'label4';}
2052
					elseif ($flag=='labelfive') {$data["flags"]['label5'] = 'label5';}
2053
					elseif ($flag=='unseen') {unset($data["flags"]['read']);}
2054
					else $data["flags"][$flag] = $flag;
2055
				}
2056
			}
2057
			if ($header['disposition-notification-to']) $data['dispositionnotificationto'] = $header['disposition-notification-to'];
2058
			if (($header['mdnsent']||$header['mdnnotsent']|$header['seen'])&&isset($data['dispositionnotificationto'])) unset($data['dispositionnotificationto']);
2059
			$data['attachmentsBlock'] = $imageHTMLBlock;
2060
			$data['address'] = ($_folderType?$data["toaddress"]:$data["fromaddress"]);
2061
			if (in_array("bodypreview", $cols)&&$header['bodypreview'])
2062
			{
2063
				$data["bodypreview"] = $header['bodypreview'];
2064
			}
2065
			$rv[] = $data;
2066
			//error_log(__METHOD__.__LINE__.array2string($data));
2067
		}
2068
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName,__METHOD__.__LINE__);
0 ignored issues
show
Bug introduced by
Are you sure $_folderName of type array can be used in concatenation? ( Ignorable by Annotation )

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

2068
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'./** @scrutinizer ignore-type */ $_folderName,__METHOD__.__LINE__);
Loading history...
2069
2070
		// ToDo: call this ONLY if labels change
2071
		Etemplate\Widget::setElementAttribute('toolbar', 'actions', $this->get_toolbar_actions());
2072
2073
		return $rv;
2074
	}
2075
2076
	/**
2077
	 * display messages header lines
2078
	 *
2079
	 * all params are passed as GET Parameters
2080
	 */
2081
	function displayHeader()
2082
	{
2083
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2084
		if(isset($_GET['part'])) $partID = $_GET['part'];
2085
2086
		$hA = self::splitRowID($rowID);
2087
		$uid = $hA['msgUID'];
2088
		$mailbox = $hA['folder'];
2089
		$icServerID = $hA['profileID'];
2090
		$rememberServerID = $this->mail_bo->profileID;
2091
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2092
		{
2093
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2094
			$this->changeProfile($icServerID);
2095
		}
2096
2097
		$this->mail_bo->reopen($mailbox);
2098
		$headers_in	= $this->mail_bo->getMessageRawHeader($uid, $partID);
2099
2100
		// add line breaks to $rawheaders
2101
		$newRawHeaders = explode("\n",$headers_in);
2102
		reset($newRawHeaders);
2103
2104
		// reset $rawheaders
2105
		$rawheaders 	= "";
2106
		// create it new, with good line breaks
2107
		reset($newRawHeaders);
2108
		foreach($newRawHeaders as $value)
2109
		{
2110
			$rawheaders .= wordwrap($value, 90, "\n     ");
2111
		}
2112
2113
		$this->mail_bo->closeConnection();
2114
		if ($rememberServerID != $this->mail_bo->profileID)
2115
		{
2116
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2117
			$this->changeProfile($rememberServerID);
2118
		}
2119
2120
		header('Content-type: text/html; charset=iso-8859-1');
2121
		print '<pre>'. htmlspecialchars($rawheaders, ENT_NOQUOTES, 'iso-8859-1') .'</pre>';
2122
2123
	}
2124
2125
	/**
2126
	 * display messages
2127
	 * @param array $_requesteddata etemplate content
2128
	 * all params are passed as GET Parameters, but can be passed via ExecMethod2 as array too
2129
	 */
2130
	function displayMessage($_requesteddata = null)
2131
	{
2132
		if (is_null($_requesteddata)) $_requesteddata = $_GET;
2133
2134
		$preventRedirect=false;
2135
		if(isset($_requesteddata['id'])) $rowID	= $_requesteddata['id'];
2136
		if(isset($_requesteddata['part'])) $partID = $_requesteddata['part']!='null'?$_requesteddata['part']:null;
2137
		if(isset($_requesteddata['mode'])) $preventRedirect   = (($_requesteddata['mode']=='display' || $_requesteddata['mode'] == 'print')?true:false);
2138
2139
		$hA = self::splitRowID($rowID);
2140
		$uid = $hA['msgUID'];
2141
		$mailbox = $hA['folder'];
2142
		$icServerID = $hA['profileID'];
2143
		$rememberServerID = $this->mail_bo->profileID;
2144
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2145
		{
2146
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2147
			$this->changeProfile($icServerID);
2148
		}
2149
		$htmlOptions = $this->mail_bo->htmlOptions;
2150
		if (!empty($_requesteddata['tryastext'])) $htmlOptions  = "only_if_no_text";
2151
		if (!empty($_requesteddata['tryashtml'])) $htmlOptions  = "always_display";
2152
2153
		//error_log(__METHOD__.__LINE__.array2string($hA));
2154
		if (($this->mail_bo->isDraftFolder($mailbox)) && $_requesteddata['mode'] == 'print')
2155
		{
2156
			$response = Api\Json\Response::get();
2157
			$response->call('app.mail.print_for_compose', $rowID);
2158
		}
2159
		if (!$preventRedirect && ($this->mail_bo->isDraftFolder($mailbox) || $this->mail_bo->isTemplateFolder($mailbox)))
2160
		{
2161
			Egw::redirect_link('/index.php',array('menuaction'=>'mail.mail_compose.compose','id'=>$rowID,'from'=>'composefromdraft'));
2162
		}
2163
		$this->mail_bo->reopen($mailbox);
2164
		// retrieve the flags of the message, before touching it.
2165
		try
2166
		{
2167
			$headers	= $this->mail_bo->getMessageHeader($uid, $partID,true,true,$mailbox);
2168
		}
2169
		catch (Api\Exception $e)
2170
		{
2171
			$error_msg[] = lang("ERROR: Message could not be displayed.");
0 ignored issues
show
Comprehensibility Best Practice introduced by
$error_msg was never initialized. Although not strictly required by PHP, it is generally a good practice to add $error_msg = array(); before regardless.
Loading history...
2172
			$error_msg[] = lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $mailbox. ( Ignorable by Annotation )

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

2172
			$error_msg[] = /** @scrutinizer ignore-call */ lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2173
			Framework::message($e->getMessage(), 'error');
2174
		}
2175
		if (!empty($uid)) $this->mail_bo->getFlags($uid);
2176
		$envelope	= $this->mail_bo->getMessageEnvelope($uid, $partID,true,$mailbox);
2177
		//error_log(__METHOD__.__LINE__.array2string($envelope));
2178
		$this->mail_bo->getMessageRawHeader($uid, $partID,$mailbox);
2179
		$fetchEmbeddedImages = false;
2180
		// if we are in HTML so its likely that we should show the embedded images; as a result
2181
		// we do NOT want to see those, that are embedded in the list of attachments
2182
		if ($htmlOptions !='always_display') $fetchEmbeddedImages = true;
2183
		try{
2184
			$attachments = $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox);
2185
		}
2186
		catch(Mail\Smime\PassphraseMissing $e)
2187
		{
2188
			//continue
2189
		}
2190
2191
		//error_log(__METHOD__.__LINE__.array2string($attachments));
2192
		$attachmentHTMLBlock = self::createAttachmentBlock($attachments, $rowID, $uid, $mailbox);
2193
2194
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2195
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2196
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2197
2198
		//error_log(__METHOD__.__LINE__.$mailBody);
2199
		$this->mail_bo->closeConnection();
2200
		//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
2201
		$etpl = new Etemplate('mail.display');
2202
		$subject = $this->mail_bo->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false);
2203
2204
		// Set up data for taglist widget(s)
2205
		if ($envelope['FROM']==$envelope['SENDER']) unset($envelope['SENDER']);
2206
		$sel_options = array();
2207
		foreach(array('SENDER','FROM','TO','CC','BCC') as $field)
2208
		{
2209
			if (!isset($envelope[$field])) continue;
2210
			foreach($envelope[$field] as $field_data)
2211
			{
2212
				//error_log(__METHOD__.__LINE__.array2string($field_data));
2213
				$content[$field][] = $field_data;
2214
				$sel_options[$field][] = array(
2215
					// taglist requires these - not optional
2216
					'id' => $field_data,
2217
					'label' => str_replace('"',"'",$field_data),
2218
				);
2219
			}
2220
		}
2221
		$actionsenabled = $this->getDisplayToolbarActions();
2222
		$content['displayToolbaractions'] = json_encode($actionsenabled);
2223
		if (empty($subject)) $subject = lang('no subject');
2224
		$content['msg'] = (is_array($error_msg)?implode("<br>",$error_msg):$error_msg);
2225
		// Send mail ID so we can use it for actions
2226
		$content['mail_id'] = $rowID;
2227
		if (!is_array($headers) || !isset($headers['DATE']))
0 ignored issues
show
introduced by
The condition is_array($headers) is always true.
Loading history...
2228
		{
2229
			$headers['DATE'] = (is_array($envelope)&&$envelope['DATE']?$envelope['DATE']:'');
2230
		}
2231
		$content['mail_displaydate'] = Mail::_strtotime($headers['DATE'],'ts',true);
2232
		$content['mail_displaysubject'] = $subject;
2233
		$linkData = array('menuaction'=>"mail.mail_ui.loadEmailBody","_messageID"=>$rowID);
2234
		if (!empty($partID)) $linkData['_partID']=$partID;
2235
		if ($htmlOptions != $this->mail_bo->htmlOptions) $linkData['_htmloptions']=$htmlOptions;
2236
		$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
2237
		$content['mail_displayattachments'] = $attachmentHTMLBlock;
2238
		$content['mail_id']=$rowID;
2239
		$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
2240
		$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
2241
2242
		// DRAG attachments actions
2243
		$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
2244
			'file_drag' => array(
2245
				'dragType' => 'file',
2246
				'type' => 'drag',
2247
				'onExecute' => 'javaScript:app.mail.drag_attachment'
2248
			)
2249
		));
2250
		$readonlys = $preserv = $content;
2251
		$readonlys['mail_displaydate'] = true;
2252
		if ($rememberServerID != $this->mail_bo->profileID)
2253
		{
2254
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2255
			$this->changeProfile($rememberServerID);
2256
		}
2257
2258
		$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
2259
	}
2260
2261
	/**
2262
	 * This is a helper function to trigger Push method
2263
	 * faster than normal 60 sec cycle.
2264
	 * @todo: Once we have socket push implemented we should
2265
	 * remove this function plus its client side companion.
2266
	 */
2267
	function ajax_smimeAttachmentsChecker ()
2268
	{
2269
		$response = Api\Json\Response::get();
2270
		$response->data(true);
2271
	}
2272
2273
	/**
2274
	 * Adds certificate to relevant contact
2275
	 * @param array $_metadata data of sender's certificate
2276
	 */
2277
	function ajax_smimeAddCertToContact ($_metadata)
2278
	{
2279
		$response = Api\Json\Response::get();
2280
		$ab = new addressbook_bo();
2281
		$response->data($ab->set_smime_keys(array($_metadata['email'] => $_metadata['cert'])));
2282
	}
2283
2284
	/**
2285
	 * Generates certificate base on given data and send
2286
	 * private key, pubkey and certificate back to client callback.
2287
	 *
2288
	 * @param array $_data
2289
	 */
2290
	function ajax_smimeGenCertificate ($_data)
2291
	{
2292
		$smime = new Mail\Smime();
2293
		$response = Api\Json\Response::get();
2294
		// fields need to be excluded from data
2295
		$discards = array ('passphrase', 'passphraseConf', 'ca', 'validity');
2296
		$ca = $_data['ca'];
2297
		$passphrase = $_data['passphrase'];
2298
		foreach (array_keys($_data) as $key)
2299
		{
2300
			if (empty($_data[$key]) || in_array($key, $discards)) unset($_data[$key]);
2301
		}
2302
		$response->data($smime->generate_certificate($_data, $ca, null, $passphrase, $_data['validity']));
0 ignored issues
show
Unused Code introduced by
The call to EGroupware\Api\Mail\Smime::generate_certificate() has too many arguments starting with $passphrase. ( Ignorable by Annotation )

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

2302
		$response->data($smime->/** @scrutinizer ignore-call */ generate_certificate($_data, $ca, null, $passphrase, $_data['validity']));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2303
	}
2304
2305
	/**
2306
	 * Export stored smime certificate in database
2307
	 * @return boolean return false if not successful
2308
	 */
2309
	function smimeExportCert()
2310
	{
2311
		if (empty($_GET['acc_id'])) return false;
2312
		$acc_smime = Mail\Smime::get_acc_smime($_GET['acc_id']);
2313
		$length = 0;
2314
		$mime = 'application/x-pkcs12';
2315
		Api\Header\Content::safe($acc_smime['acc_smime_password'], "certificate.p12", $mime, $length, true, true);
2316
		echo $acc_smime['acc_smime_password'];
2317
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2318
	}
2319
2320
	/**
2321
	 * Build actions for display toolbar
2322
	 */
2323
	function getDisplayToolbarActions ()
2324
	{
2325
		$actions = $this->get_toolbar_actions();
2326
		$actions['mark']['children']['flagged']=array(
2327
			'group' => $actions['mark']['children']['flagged']['group'],
2328
			'caption' => 'Flagged',
2329
			'icon' => 'unread_flagged_small',
2330
			'onExecute' => 'javaScript:app.mail.mail_flag',
2331
		);
2332
		$actions['mark']['children']['unflagged']=array(
2333
			'group' => $actions['mark']['children']['flagged']['group'],
2334
			'caption' => 'Unflagged',
2335
			'icon' => 'read_flagged_small',
2336
			'onExecute' => 'javaScript:app.mail.mail_flag',
2337
		);
2338
		$actions['tracker']['toolbarDefault'] = true;
2339
		$actions['forward']['toolbarDefault'] = true;
2340
2341
		$compose = $actions['composeasnew'];
2342
		unset($actions['composeasnew']);
2343
2344
		$actions2 = array_reverse($actions,true);
2345
		$actions2['composeasnew']= $compose;
2346
		return array_reverse($actions2,true);
2347
	}
2348
2349
	/**
2350
	 * helper function to create the attachment block/table
2351
	 *
2352
	 * @param array $attachments array with the attachments information
2353
	 * @param string $rowID rowid of the message
2354
	 * @param int $uid uid of the message
2355
	 * @param string $mailbox mailbox identifier
2356
	 * @param boolean $_returnFullHTML flag wether to return HTML or data array
2357
	 * @return array|string data array or html or empty string
2358
	 */
2359
	static function createAttachmentBlock($attachments, $rowID, $uid, $mailbox,$_returnFullHTML=false)
0 ignored issues
show
Unused Code introduced by
The parameter $mailbox is not used and could be removed. ( Ignorable by Annotation )

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

2359
	static function createAttachmentBlock($attachments, $rowID, $uid, /** @scrutinizer ignore-unused */ $mailbox,$_returnFullHTML=false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $uid is not used and could be removed. ( Ignorable by Annotation )

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

2359
	static function createAttachmentBlock($attachments, $rowID, /** @scrutinizer ignore-unused */ $uid, $mailbox,$_returnFullHTML=false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2360
	{
2361
		$attachmentHTMLBlock='';
2362
		$attachmentHTML = array();
2363
2364
		// skip message/delivery-status and set a title for original eml file
2365
		if (($attachments[0]['mimeType'] === 'message/delivery-status'))
2366
		{
2367
			unset($attachments[0]);
2368
			if (is_array($attachments))
0 ignored issues
show
introduced by
The condition is_array($attachments) is always true.
Loading history...
2369
			{
2370
				$attachments = array_values($attachments);
2371
				$attachments[0]['name'] = lang('Original Email Content');
2372
			}
2373
		}
2374
2375
		if (is_array($attachments) && count($attachments) > 0) {
2376
			foreach ($attachments as $key => $value)
2377
			{
2378
				if (Mail\Smime::isSmime($value['mimeType'])) continue;
2379
				$attachmentHTML[$key]['filename']= ($value['name'] ? ( $value['filename'] ? $value['filename'] : $value['name'] ) : lang('(no subject)'));
2380
				$attachmentHTML[$key]['filename'] = Api\Translation::convert_jsonsafe($attachmentHTML[$key]['filename'],'utf-8');
2381
				//error_log(array2string($value));
2382
				//error_log(strtoupper($value['mimeType']) .'<->'. Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']));
2383
				if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']);
2384
				$attachmentHTML[$key]['type']=$value['mimeType'];
2385
				$attachmentHTML[$key]['mimetype'] = Api\MimeMagic::mime2label($value['mimeType']);
2386
				$hA = self::splitRowID($rowID);
2387
				$uid = $hA['msgUID'];
2388
				$mailbox = $hA['folder'];
2389
				$acc_id = $hA['profileID'];
2390
2391
				$attachmentHTML[$key]['mime_data'] = Link::set_data($value['mimeType'], 'EGroupware\\Api\\Mail::getAttachmentAccount', array(
2392
					$acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true
2393
				));
2394
				$attachmentHTML[$key]['size']=Vfs::hsize($value['size']);
2395
				$attachmentHTML[$key]['attachment_number']=$key;
2396
				$attachmentHTML[$key]['partID']=$value['partID'];
2397
				$attachmentHTML[$key]['mail_id'] = $rowID;
2398
				$attachmentHTML[$key]['winmailFlag']=$value['is_winmail'];
2399
				$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "mail_DisplayNone";
2400
				$attachmentHTML[$key]['smime_type'] = $value['smime_type'];
2401
				// reset mode array as it should be considered differently for
2402
				// each attachment
2403
				$mode = array();
2404
				switch(strtoupper($value['mimeType']))
2405
				{
2406
					case 'MESSAGE/RFC822':
2407
						$linkData = array
2408
						(
2409
							'menuaction'	=> 'mail.mail_ui.displayMessage',
2410
							'mode'		=> 'display', //message/rfc822 attachments should be opened in display mode
2411
							'id'		=> $rowID,
2412
							'part'		=> $value['partID'],
2413
							'is_winmail'    => $value['is_winmail']
2414
						);
2415
						$windowName = 'displayMessage_'. $rowID.'_'.$value['partID'];
2416
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',700,egw_getWindowOuterHeight());";
2417
						break;
2418
					case 'IMAGE/JPEG':
2419
					case 'IMAGE/PNG':
2420
					case 'IMAGE/GIF':
2421
					case 'IMAGE/BMP':
2422
						// set mode for media mimetypes because we need
2423
						// to structure a download url to be used maybe in expose.
2424
						$mode = array(
2425
							'mode' => 'save'
2426
						);
2427
					case 'APPLICATION/PDF':
2428
					case 'TEXT/PLAIN':
2429
					case 'TEXT/HTML':
2430
					case 'TEXT/DIRECTORY':
2431
						$sfxMimeType = $value['mimeType'];
2432
						$buff = explode('.',$value['name']);
2433
						$suffix = '';
2434
						if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2435
						if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2436
						if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD')
2437
						{
2438
							$attachments[$key]['mimeType'] = $sfxMimeType;
2439
							$value['mimeType'] = strtoupper($sfxMimeType);
2440
						}
2441
					case 'TEXT/X-VCARD':
2442
					case 'TEXT/VCARD':
2443
					case 'TEXT/CALENDAR':
2444
					case 'TEXT/X-VCALENDAR':
2445
						$linkData = array_merge(array
2446
						(
2447
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2448
							'id'		=> $rowID,
2449
							'part'		=> $value['partID'],
2450
							'is_winmail'=> $value['is_winmail'],
2451
							'mailbox'   => base64_encode($mailbox),
2452
							'smime_type' => $value['smime_type']
2453
						) , $mode);
2454
						$windowName = 'displayAttachment_'. $uid;
2455
						$reg = '800x600';
2456
						// handle calendar/vcard
2457
						if (strtoupper($value['mimeType'])=='TEXT/CALENDAR')
2458
						{
2459
							$windowName = 'displayEvent_'. $rowID;
2460
							$reg2 = Link::get_registry('calendar','view_popup');
2461
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2462
						}
2463
						if (strtoupper($value['mimeType'])=='TEXT/X-VCARD' || strtoupper($value['mimeType'])=='TEXT/VCARD')
2464
						{
2465
							$windowName = 'displayContact_'. $rowID;
2466
							$reg2 = Link::get_registry('addressbook','add_popup');
2467
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2468
						}
2469
						// apply to action
2470
						list($width,$height) = explode('x',(!empty($reg2) ? $reg2 : $reg));
2471
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',$width,$height);";
2472
						break;
2473
					default:
2474
						$linkData = array
2475
						(
2476
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2477
							'id'		=> $rowID,
2478
							'part'		=> $value['partID'],
2479
							'is_winmail'    => $value['is_winmail'],
2480
							'mailbox'   => base64_encode($mailbox),
2481
							'smime_type' => $value['smime_type']
2482
						);
2483
						$linkView = "window.location.href = '".Egw::link('/index.php',$linkData)."';";
2484
						break;
2485
				}
2486
				// we either use mime_data for server-side supported mime-types or mime_url for client-side or download
2487
				if (empty($attachmentHTML[$key]['mime_data']))
2488
				{
2489
					$attachmentHTML[$key]['mime_url'] = Egw::link('/index.php',$linkData);
2490
					unset($attachmentHTML[$key]['mime_data']);
2491
				}
2492
				$attachmentHTML[$key]['windowName'] = $windowName;
2493
2494
				//error_log(__METHOD__.__LINE__.$linkView);
2495
				$attachmentHTML[$key]['link_view'] = '<a href="#" ." title="'.$attachmentHTML[$key]['filename'].'" onclick="'.$linkView.' return false;"><b>'.
2496
					($value['name'] ? $value['name'] : lang('(no subject)')).
2497
					'</b></a>';
2498
2499
				$linkData = array
2500
				(
2501
					'menuaction'	=> 'mail.mail_ui.getAttachment',
2502
					'mode'		=> 'save',
2503
					'id'		=> $rowID,
2504
					'part'		=> $value['partID'],
2505
					'is_winmail'    => $value['is_winmail'],
2506
					'mailbox'   => base64_encode($mailbox),
2507
					'smime_type' => $value['smime_type']
2508
				);
2509
				$attachmentHTML[$key]['link_save'] ="<a href='".Egw::link('/index.php',$linkData)."' title='".$attachmentHTML[$key]['filename']."'>".Api\Html::image('mail','fileexport')."</a>";
2510
				// add save-all and download zip icon for first attachment only
2511
				// if more than one attachments.
2512
				if ($key == 0 && count($attachments) > 1)
2513
				{
2514
					$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "";
2515
				}
2516
			}
2517
			$attachmentHTMLBlock="<table width='100%'>";
2518
			foreach ((array)$attachmentHTML as $row)
2519
			{
2520
				$attachmentHTMLBlock .= "<tr><td><div class='useEllipsis'>".$row['link_view'].'</div></td>';
2521
				$attachmentHTMLBlock .= "<td>".$row['mimetype'].'</td>';
2522
				$attachmentHTMLBlock .= "<td>".$row['size'].'</td>';
2523
				$attachmentHTMLBlock .= "<td>".$row['link_save'].'</td></tr>';
2524
			}
2525
			$attachmentHTMLBlock .= "</table>";
2526
		}
2527
		if (!$_returnFullHTML)
2528
		{
2529
			foreach ((array)$attachmentHTML as $ikey => $value)
2530
			{
2531
				unset($attachmentHTML[$ikey]['link_view']);
2532
				unset($attachmentHTML[$ikey]['link_save']);
2533
			}
2534
		}
2535
		return ($_returnFullHTML?$attachmentHTMLBlock:$attachmentHTML);
2536
	}
2537
2538
	/**
2539
	 * fetch vacation info from active Server using icServer object
2540
	 *
2541
	 * @param array $cachedVacations an array of cached vacations for an user
2542
	 * @return array|boolean array with vacation on success or false on failure
2543
	 */
2544
	function gatherVacation($cachedVacations = array())
2545
	{
2546
		$isVacationEnabled = $this->mail_bo->icServer->acc_sieve_enabled && ($this->mail_bo->icServer->acc_sieve_host||$this->mail_bo->icServer->acc_imap_host);
2547
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
2548
2549
		if ($isVacationEnabled)
2550
		{
2551
			$sieveServer = $this->mail_bo->icServer;
2552
			try
2553
			{
2554
				$sieveServer->retrieveRules();
0 ignored issues
show
Bug introduced by
The method retrieveRules() does not exist on EGroupware\Api\Mail\Imap. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

2554
				$sieveServer->/** @scrutinizer ignore-call */ 
2555
                  retrieveRules();
Loading history...
2555
				$vacation = $sieveServer->getVacation();
0 ignored issues
show
Bug introduced by
The method getVacation() does not exist on EGroupware\Api\Mail\Imap. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

2555
				/** @scrutinizer ignore-call */ 
2556
    $vacation = $sieveServer->getVacation();
Loading history...
2556
2557
				$cachedVacations = array($sieveServer->acc_id => $vacation) + (array)$cachedVacations;
2558
				// Set vacation to the instance cache for particular account with expiration of one day
2559
				Api\Cache::setCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations, 60*60*24);
2560
			}
2561
			catch (PEAR_Exception $ex)
2562
			{
2563
				$this->callWizard($ex->getMessage(), true, 'error');
2564
			}
2565
		}
2566
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Vacation retrieved:'.array2string($vacation));
2567
		return $vacation;
2568
	}
2569
2570
	/**
2571
	 * gather Info on how to display the quota info
2572
	 *
2573
	 * @param int $_usage amount of usage in Kb
2574
	 * @param int $_limit amount of limit in Kb
2575
	 * @return array  returns an array of info used for quota
2576
	 *		array(
2577
	 *			class		=> string,
2578
	 *			text		=> string,
2579
	 *			percent		=> string,
2580
	 *			freespace	=> integer
2581
	 *		)
2582
	 */
2583
	function quotaDisplay($_usage, $_limit)
2584
	{
2585
		$percent = $_limit == 0 ? 100 : round(($_usage*100)/$_limit);
2586
		$limit = Mail::show_readable_size($_limit*1024);
2587
		$usage = Mail::show_readable_size($_usage*1024);
2588
2589
		if ($_limit > 0)
2590
		{
2591
			$text = $usage .'/'.$limit;
2592
			switch ($percent)
2593
			{
2594
				case ($percent > 90):
2595
					$class ='mail-index_QuotaRed';
2596
					break;
2597
				case ($percent > 80):
2598
					$class ='mail-index_QuotaYellow';
2599
					break;
2600
				default:
2601
					$class ='mail-index_QuotaGreen';
2602
			}
2603
		}
2604
		else
2605
		{
2606
			$text = $usage;
2607
			$class ='mail-index_QuotaGreen';
2608
		}
2609
		return array (
2610
			'class'		=> $class,
2611
			'text'		=> lang('Quota: %1',$text),
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $text. ( Ignorable by Annotation )

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

2611
			'text'		=> /** @scrutinizer ignore-call */ lang('Quota: %1',$text),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2612
			'percent'	=> $percent,
2613
			'freespace'	=> $_limit*1024 - $_usage*1024
2614
		);
2615
	}
2616
2617
	/**
2618
	 * display image
2619
	 *
2620
	 * all params are passed as GET Parameters
2621
	 */
2622
	function displayImage()
2623
	{
2624
		$uid	= $_GET['uid'];
2625
		$cid	= base64_decode($_GET['cid']);
2626
		$partID = urldecode($_GET['partID']);
2627
		if (!empty($_GET['mailbox'])) $mailbox  = base64_decode($_GET['mailbox']);
2628
2629
		//error_log(__METHOD__.__LINE__.":$uid, $cid, $partID");
2630
		$this->mail_bo->reopen($mailbox);
2631
2632
		$attachment = $this->mail_bo->getAttachmentByCID($uid, $cid, $partID, true);	// true get contents as stream
2633
2634
		$this->mail_bo->closeConnection();
2635
2636
		$GLOBALS['egw']->session->commit_session();
2637
2638
		if ($attachment)
0 ignored issues
show
introduced by
$attachment is of type Horde_Mime_Part, thus it always evaluated to true.
Loading history...
2639
		{
2640
			header("Content-Type: ". $attachment->getType());
2641
			header('Content-Disposition: inline; filename="'. $attachment->getDispositionParameter('filename') .'"');
2642
			//header("Expires: 0");
2643
			// the next headers are for IE and SSL
2644
			//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2645
			//header("Pragma: public");
2646
			Api\Session::cache_control(true);
2647
			echo $attachment->getContents();
0 ignored issues
show
Bug introduced by
Are you sure $attachment->getContents() of type resource|string can be used in echo? ( Ignorable by Annotation )

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

2647
			echo /** @scrutinizer ignore-type */ $attachment->getContents();
Loading history...
2648
		}
2649
		else
2650
		{
2651
			// send a 404 Not found
2652
			header("HTTP/1.1 404 Not found");
2653
		}
2654
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2655
	}
2656
2657
	function getAttachment()
2658
	{
2659
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2660
2661
		$hA = self::splitRowID($rowID);
2662
		$uid = $hA['msgUID'];
2663
		$mailbox = $hA['folder'];
2664
		$icServerID = $hA['profileID'];
2665
		$rememberServerID = $this->mail_bo->profileID;
2666
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2667
		{
2668
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2669
			$this->changeProfile($icServerID);
2670
		}
2671
		$part		= $_GET['part'];
2672
		$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
2673
2674
		$this->mail_bo->reopen($mailbox);
2675
		$attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false);
2676
		$this->mail_bo->closeConnection();
2677
		if ($rememberServerID != $this->mail_bo->profileID)
2678
		{
2679
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2680
			$this->changeProfile($rememberServerID);
2681
		}
2682
2683
		$GLOBALS['egw']->session->commit_session();
2684
		//error_log(__METHOD__.print_r($_GET,true));
2685
		if ($_GET['mode'] != "save")
2686
		{
2687
			if (strtoupper($attachment['type']) == 'TEXT/DIRECTORY' || empty($attachment['type']))
2688
			{
2689
				$sfxMimeType = $attachment['type'];
2690
				$buff = explode('.',$attachment['filename']);
2691
				$suffix = '';
2692
				if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
0 ignored issues
show
introduced by
The condition is_array($buff) is always true.
Loading history...
2693
				if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2694
				$attachment['type'] = $sfxMimeType;
2695
				if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD') $attachment['type'] = strtoupper($sfxMimeType);
2696
			}
2697
			//error_log(__METHOD__.print_r($attachment,true));
2698
			if (strtoupper($attachment['type']) == 'TEXT/CALENDAR' || strtoupper($attachment['type']) == 'TEXT/X-VCALENDAR')
2699
			{
2700
				//error_log(__METHOD__."about to call calendar_ical");
2701
				$calendar_ical = new calendar_ical();
2702
				$eventid = $calendar_ical->search($attachment['attachment'],-1);
2703
				//error_log(__METHOD__.array2string($eventid));
2704
				if (!$eventid) $eventid = -1;
2705
				$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
2706
				//error_log(__METHOD__.$event);
2707
				if ((int)$event > 0)
2708
				{
2709
					$vars = array(
2710
						'menuaction'      => 'calendar.calendar_uiforms.edit',
2711
						'cal_id'      => $event,
2712
					);
2713
					Egw::redirect_link('../index.php',$vars);
2714
				}
2715
				//Import failed, download content anyway
2716
			}
2717
			if (strtoupper($attachment['type']) == 'TEXT/X-VCARD' || strtoupper($attachment['type']) == 'TEXT/VCARD')
2718
			{
2719
				$addressbook_vcal = new addressbook_vcal();
2720
				// double \r\r\n seems to end a vcard prematurely, so we set them to \r\n
2721
				//error_log(__METHOD__.__LINE__.$attachment['attachment']);
2722
				$attachment['attachment'] = str_replace("\r\r\n", "\r\n", $attachment['attachment']);
2723
				$vcard = $addressbook_vcal->vcardtoegw($attachment['attachment'], $attachment['charset']);
2724
				if ($vcard['uid'])
2725
				{
2726
					$vcard['uid'] = trim($vcard['uid']);
2727
					//error_log(__METHOD__.__LINE__.print_r($vcard,true));
2728
					$contact = $addressbook_vcal->find_contact($vcard,false);
2729
				}
2730
				if (!$contact) $contact = null;
2731
				// if there are not enough fields in the vcard (or the parser was unable to correctly parse the vcard (as of VERSION:3.0 created by MSO))
2732
				if ($contact || count($vcard)>2)
2733
				{
2734
					$contact = $addressbook_vcal->addVCard($attachment['attachment'],(is_array($contact)?array_shift($contact):$contact),true,$attachment['charset']);
2735
				}
2736
				if ((int)$contact > 0)
2737
				{
2738
					$vars = array(
2739
						'menuaction'	=> 'addressbook.addressbook_ui.edit',
2740
						'contact_id'	=> $contact,
2741
					);
2742
					Egw::redirect_link('../index.php',$vars);
2743
				}
2744
				//Import failed, download content anyway
2745
			}
2746
		}
2747
		//error_log(__METHOD__.__LINE__.'->'.array2string($attachment));
2748
		$filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part));
2749
		Api\Header\Content::safe($attachment['attachment'], $filename, $attachment['type'], $size=0, True, $_GET['mode'] == "save");
0 ignored issues
show
Bug introduced by
$size = 0 cannot be passed to EGroupware\Api\Header\Content::safe() as the parameter $length expects a reference. ( Ignorable by Annotation )

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

2749
		Api\Header\Content::safe($attachment['attachment'], $filename, $attachment['type'], /** @scrutinizer ignore-type */ $size=0, True, $_GET['mode'] == "save");
Loading history...
2750
		echo $attachment['attachment'];
2751
2752
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2753
	}
2754
2755
2756
	/**
2757
	 * save messages on disk or filemanager, or display it in popup
2758
	 *
2759
	 * all params are passed as GET Parameters
2760
	 */
2761
	function saveMessage()
2762
	{
2763
		$display = false;
2764
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2765
		if(isset($_GET['part'])) $partID = $_GET['part'];
2766
		if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display	= $_GET['location'];
2767
2768
		$hA = self::splitRowID($rowID);
2769
		$uid = $hA['msgUID'];
2770
		$mailbox = $hA['folder'];
2771
		$icServerID = $hA['profileID'];
2772
		$rememberServerID = $this->mail_bo->profileID;
2773
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2774
		{
2775
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2776
			$this->changeProfile($icServerID);
2777
		}
2778
2779
		$this->mail_bo->reopen($mailbox);
2780
2781
		$message = $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox);
2782
2783
		$this->mail_bo->closeConnection();
2784
		if ($rememberServerID != $this->mail_bo->profileID)
2785
		{
2786
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2787
			$this->changeProfile($rememberServerID);
2788
		}
2789
2790
		$GLOBALS['egw']->session->commit_session();
2791
		if (!$display)
2792
		{
2793
			$headers = Horde_Mime_Headers::parseHeaders($message);
2794
			$subject = str_replace('$$','__',Mail::decode_header($headers['SUBJECT']));
2795
			$subject = mail_bo::clean_subject_for_filename($subject);
2796
			Api\Header\Content::safe($message, $subject.".eml", $mime='message/rfc822', $size=0, true, true);
0 ignored issues
show
Bug introduced by
$size = 0 cannot be passed to EGroupware\Api\Header\Content::safe() as the parameter $length expects a reference. ( Ignorable by Annotation )

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

2796
			Api\Header\Content::safe($message, $subject.".eml", $mime='message/rfc822', /** @scrutinizer ignore-type */ $size=0, true, true);
Loading history...
Bug introduced by
$mime = 'message/rfc822' cannot be passed to EGroupware\Api\Header\Content::safe() as the parameter $mime expects a reference. ( Ignorable by Annotation )

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

2796
			Api\Header\Content::safe($message, $subject.".eml", /** @scrutinizer ignore-type */ $mime='message/rfc822', $size=0, true, true);
Loading history...
2797
			echo $message;
2798
		}
2799
		else
2800
		{
2801
			$subject = mail_bo::clean_subject_for_filename($subject);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subject seems to be never defined.
Loading history...
2802
			Api\Header\Content::safe($message, $subject.".eml", $mime='text/html', $size=0, true, false);
0 ignored issues
show
Bug introduced by
$mime = 'text/html' cannot be passed to EGroupware\Api\Header\Content::safe() as the parameter $mime expects a reference. ( Ignorable by Annotation )

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

2802
			Api\Header\Content::safe($message, $subject.".eml", /** @scrutinizer ignore-type */ $mime='text/html', $size=0, true, false);
Loading history...
2803
			print '<pre>'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'</pre>';
2804
		}
2805
	}
2806
2807
	/**
2808
	 * Ajax function to save message(s)/attachment(s) in the vfs
2809
	 *
2810
	 * @param array $params array of mail ids and action name
2811
	 *			params = array (
2812
	 *				ids => array of string
2813
	 *				action => string
2814
	 *			)
2815
	 * @param string $path path to save the emails
2816
	 * @param string $submit_button_id dialog button id of triggered submit
2817
	 * @param string $savemode save mode: 'overwrite' or 'rename'
2818
	 */
2819
	function ajax_vfsSave ($params, $path, $submit_button_id='', $savemode='rename')
2820
	{
2821
		unset($submit_button_id); // not used here
2822
2823
		$response = Api\Json\Response::get();
2824
2825
		switch ($params['action'])
2826
		{
2827
			case 'message':
2828
				$result = $this->vfsSaveMessages($params['ids'], $path, $savemode);
2829
				break;
2830
			case 'attachment':
2831
				$result = $this->vfsSaveAttachments($params['ids'], $path, $savemode);
2832
				break;
2833
		}
2834
		$response->call('app.mail.vfsSaveCallback', $result);
2835
	}
2836
2837
	/**
2838
	 * Save Message(s) in the vfs
2839
	 *
2840
	 * @param string|array $ids use splitRowID, to separate values
2841
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2842
	 * @param string $savemode save mode: 'overwrite' or 'rename'
2843
	 *
2844
	 * @return array returns an array including message and success result
2845
	 *		array (
2846
	 *			'msg' => STRING,
2847
	 *			'success' => BOOLEAN
2848
	 *		)
2849
	 */
2850
	function vfsSaveMessages($ids,$path, $savemode='rename')
2851
	{
2852
		// add mail translation
2853
		Api\Translation::add_app('mail');
2854
		$res = array ();
2855
2856
		// extract dir from the path
2857
		$dir = Vfs::is_dir($path) ? $path : Vfs::dirname($path);
2858
2859
		// exit if user has no right to the dir
2860
		if (!Vfs::is_writable($dir))
2861
		{
2862
			return array (
2863
				'msg' => lang('%1 is NOT writable by you!',$path),
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $path. ( Ignorable by Annotation )

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

2863
				'msg' => /** @scrutinizer ignore-call */ lang('%1 is NOT writable by you!',$path),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2864
				'success' => false
2865
			);
2866
		}
2867
2868
		$preservedServerID = $this->mail_bo->profileID;
2869
		foreach((array)$ids as $id)
2870
		{
2871
			$hA = self::splitRowID($id);
2872
			$uid = $hA['msgUID'];
2873
			$mailbox = $hA['folder'];
2874
			$icServerID = $hA['profileID'];
2875
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2876
			{
2877
				$this->changeProfile($icServerID);
2878
			}
2879
			$message = $this->mail_bo->getMessageRawBody($uid, $partID='', $mailbox);
2880
2881
			// is multiple messages
2882
			if (Vfs::is_dir($path))
2883
			{
2884
				$headers = $this->mail_bo->getMessageHeader($uid,$partID,true,false,$mailbox);
2885
				$file = $dir . '/'.mail_bo::clean_subject_for_filename($headers['SUBJECT']).'.eml';
0 ignored issues
show
Bug introduced by
Are you sure $dir of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

2885
				$file = /** @scrutinizer ignore-type */ $dir . '/'.mail_bo::clean_subject_for_filename($headers['SUBJECT']).'.eml';
Loading history...
2886
			}
2887
			else
2888
			{
2889
				$file = $dir . '/' . mail_bo::clean_subject_for_filename(str_replace($dir.'/', '', $path));
2890
			}
2891
2892
			if ($savemode != 'overwrite')
2893
			{
2894
				// Check if file already exists, then try to assign a none existance filename
2895
				$counter = 1;
2896
				$tmp_file = $file;
2897
				while (Vfs::file_exists($tmp_file))
2898
				{
2899
					$tmp_file = $file;
2900
					$pathinfo = pathinfo(Vfs::basename($tmp_file));
2901
					$tmp_file = $dir . '/' . $pathinfo['filename'] . '(' . $counter . ')' . '.' . $pathinfo['extension'];
2902
					$counter++;
2903
				}
2904
				$file = $tmp_file;
2905
			}
2906
2907
			if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
2908
			{
2909
				$res['msg'] = lang('Error saving %1!',$file);
2910
				$res['success'] = false;
2911
			}
2912
			else
2913
			{
2914
				$res['success'] = true;
2915
			}
2916
			if ($fp) fclose($fp);
2917
			if ($res['success'])
2918
			{
2919
				unset($headers['SUBJECT']);//already in filename
2920
				$infoSection = Mail::createHeaderInfoSection($headers, 'SUPPRESS', false);
2921
				$props = array(array('name' => 'comment','val' => $infoSection));
2922
				Vfs::proppatch($file,$props);
2923
			}
2924
		}
2925
		if ($preservedServerID != $this->mail_bo->profileID)
2926
		{
2927
			//change Profile back to where we came from
2928
			$this->changeProfile($preservedServerID);
2929
		}
2930
		return $res;
2931
	}
2932
2933
	/**
2934
	 * Save attachment(s) in the vfs
2935
	 *
2936
	 * @param string|array $ids '::' delimited mailbox::uid::part-id::is_winmail::name (::name for multiple id's)
2937
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2938
	 * @param string $savemode save mode: 'overwrite' or 'rename'
2939
	 *
2940
	 * @return array returns an array including message and success result
2941
	 *		array (
2942
	 *			'msg' => STRING,
2943
	 *			'success' => BOOLEAN
2944
	 *		)
2945
	 */
2946
	function vfsSaveAttachments($ids,$path, $savemode='rename')
2947
	{
2948
		$res = array (
2949
			'msg' => lang('Attachment has been saved successfully.'),
2950
			'success' => true
2951
		);
2952
2953
		if (Vfs::is_dir($path))
2954
		{
2955
			$dir = $path;
2956
		}
2957
		else
2958
		{
2959
			$dir = Vfs::dirname($path);
2960
			// Need to deal with any ? here, or basename will truncate
2961
			$filename = mail_bo::clean_subject_for_filename(str_replace('?','_',Vfs::basename($path)));
2962
		}
2963
2964
		if (!Vfs::is_writable($dir))
2965
		{
2966
			return array (
2967
				'msg' => lang('%1 is NOT writable by you!',$path),
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $path. ( Ignorable by Annotation )

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

2967
				'msg' => /** @scrutinizer ignore-call */ lang('%1 is NOT writable by you!',$path),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2968
				'success' => false
2969
			);
2970
		}
2971
2972
		$preservedServerID = $this->mail_bo->profileID;
2973
2974
		/**
2975
		 * Extract all parameteres from the given id
2976
		 * @param int $id message id ('::' delimited mailbox::uid::part-id::is_winmail::name)
2977
		 *
2978
		 * @return array an array of parameters
2979
		 */
2980
		$getParams = function ($id) {
2981
			list($app,$user,$serverID,$mailbox,$uid,$part,$is_winmail,$name) = explode('::',$id,8);
2982
			$lId = implode('::',array($app,$user,$serverID,$mailbox,$uid));
2983
			$hA = mail_ui::splitRowID($lId);
2984
			return array(
2985
				'is_winmail' => $is_winmail == "null" || !$is_winmail?false:$is_winmail,
2986
				'user' => $user,
2987
				'name' => $name,
2988
				'part' => $part,
2989
				'uid' => $hA['msgUID'],
2990
				'mailbox' => $hA['folder'],
2991
				'icServer' => $hA['profileID']
2992
			);
2993
		};
2994
2995
		//Examine the first attachment to see if attachment
2996
		//is winmail.dat embedded attachments.
2997
		$p = $getParams((is_array($ids)?$ids[0]:$ids));
2998
		if ($p['is_winmail'])
2999
		{
3000
			if ($p['icServer'] && $p['icServer'] != $this->mail_bo->profileID)
3001
			{
3002
				$this->changeProfile($p['icServer']);
3003
			}
3004
			$this->mail_bo->reopen($p['mailbox']);
3005
			// retrieve all embedded attachments at once
3006
			// avoids to fetch heavy winmail.dat content
3007
			// for each file.
3008
			$attachments = $this->mail_bo->getTnefAttachments($p['uid'],$p['part'], false, $p['mailbox']);
3009
		}
3010
3011
		foreach((array)$ids as $id)
3012
		{
3013
			$params = $getParams($id);
3014
3015
			if ($params['icServer'] && $params['icServer'] != $this->mail_bo->profileID)
3016
			{
3017
				$this->changeProfile($params['icServer']);
3018
			}
3019
			$this->mail_bo->reopen($params['mailbox']);
3020
3021
			// is multiple attachments
3022
			if (Vfs::is_dir($path) || $params['is_winmail'])
3023
			{
3024
				if ($params['is_winmail'])
3025
				{
3026
					// Try to find the right content for file id
3027
					foreach ($attachments as $key => $val)
3028
					{
3029
						if ($key == $params['is_winmail']) $attachment = $val;
3030
					}
3031
				}
3032
				else
3033
				{
3034
					$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
3035
				}
3036
			}
3037
			else
3038
			{
3039
				$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
3040
			}
3041
3042
			$file = $dir. '/' . ($filename ? $filename : mail_bo::clean_subject_for_filename($attachment['filename']));
0 ignored issues
show
Bug introduced by
Are you sure $dir of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

3042
			$file = /** @scrutinizer ignore-type */ $dir. '/' . ($filename ? $filename : mail_bo::clean_subject_for_filename($attachment['filename']));
Loading history...
3043
3044
			if ($savemode != 'overwrite')
3045
			{
3046
				$counter = 1;
3047
				$tmp_file = $file;
3048
				while (Vfs::file_exists($tmp_file))
3049
				{
3050
					$tmp_file = $file;
3051
					$pathinfo = pathinfo(Vfs::basename($tmp_file));
3052
					$tmp_file = $dir . '/' . $pathinfo['filename'] . '(' . $counter . ')' . '.' . $pathinfo['extension'];
3053
					$counter++;
3054
				}
3055
				$file = $tmp_file;
3056
			}
3057
3058
			if (!($fp = Vfs::fopen($file,'wb')) ||
3059
				!fwrite($fp,$attachment['attachment']))
3060
			{
3061
				$res['msg'] = lang('Error saving %1!',$file);
3062
				$res['success'] = false;
3063
			}
3064
			if ($fp)
3065
			{
3066
				fclose($fp);
3067
			}
3068
		}
3069
3070
		$this->mail_bo->closeConnection();
3071
3072
		if ($preservedServerID != $this->mail_bo->profileID)
3073
		{
3074
			//change Profile back to where we came from
3075
			$this->changeProfile($preservedServerID);
3076
		}
3077
		return $res;
3078
	}
3079
3080
	/**
3081
	 * Zip all attachments and send to user
3082
	 * @param string $message_id = null
3083
	 */
3084
	function download_zip($message_id=null)
3085
	{
3086
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3087
		// First, get all attachment IDs
3088
		if(isset($_GET['id'])) $message_id	= $_GET['id'];
3089
		//error_log(__METHOD__.__LINE__.$message_id);
3090
		$rememberServerID = $this->mail_bo->profileID;
3091
		if(!is_numeric($message_id))
3092
		{
3093
			$hA = self::splitRowID($message_id);
3094
			$message_id = $hA['msgUID'];
3095
			$mailbox = $hA['folder'];
3096
			$icServerID = $hA['profileID'];
3097
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3098
			{
3099
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3100
				$this->changeProfile($icServerID);
3101
			}
3102
		}
3103
		else
3104
		{
3105
			$mailbox = $this->mail_bo->sessionData['mailbox'];
3106
		}
3107
		// always fetch all, even inline (images)
3108
		$fetchEmbeddedImages = true;
3109
		$attachments = $this->mail_bo->getMessageAttachments($message_id,null, null, $fetchEmbeddedImages, true,true,$mailbox);
3110
		// put them in VFS so they can be zipped
3111
		$header = $this->mail_bo->getMessageHeader($message_id,'',true,false,$mailbox);
3112
		//get_home_dir may fetch the users startfolder if set; if not writeable, action will fail. TODO: use temp_dir
3113
		$homedir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
3114
		$temp_path = $homedir/*Vfs::get_home_dir()*/ . "/.mail_$message_id";
3115
		if(Vfs::is_dir($temp_path)) Vfs::remove ($temp_path);
3116
3117
		// Add subject to path, so it gets used as the file name, replacing ':'
3118
		// as it seems to cause an error
3119
		$path = $temp_path . '/' . ($header['SUBJECT'] ? Vfs::encodePathComponent(mail_bo::clean_subject_for_filename(str_replace(':','-', $header['SUBJECT']))) : lang('mail')) .'/';
0 ignored issues
show
Bug introduced by
Are you sure $header['SUBJECT'] ? EGr...ECT']))) : lang('mail') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

3119
		$path = $temp_path . '/' . (/** @scrutinizer ignore-type */ $header['SUBJECT'] ? Vfs::encodePathComponent(mail_bo::clean_subject_for_filename(str_replace(':','-', $header['SUBJECT']))) : lang('mail')) .'/';
Loading history...
3120
		if(!Vfs::mkdir($path, 0700, true))
3121
		{
3122
			echo "Unable to open temp directory $path";
3123
			return;
3124
		}
3125
3126
		$file_list = array();
3127
		$dupe_count = array();
3128
		$this->mail_bo->reopen($mailbox);
3129
		if ($attachments[0]['is_winmail'] && $attachments[0]['is_winmail']!='null')
3130
		{
3131
			$tnefAttachments = $this->mail_bo->getTnefAttachments($message_id, $attachments[0]['partID'],true, $mailbox);
3132
		}
3133
		foreach($attachments as $file)
3134
		{
3135
			if ($file['is_winmail'])
3136
			{
3137
				// Try to find the right content for file id
3138
				foreach ($tnefAttachments as $key => $val)
3139
				{
3140
					error_log(__METHOD__.' winmail = '.$key);
3141
					if ($key == $file['is_winmail']) $attachment = $val;
3142
				}
3143
			}
3144
			else
3145
			{
3146
				$attachment = $this->mail_bo->getAttachment($message_id,$file['partID'],$file['is_winmail'],false,true);
3147
			}
3148
			$success=true;
3149
			if (empty($file['filename'])) $file['filename'] = $file['name'];
3150
			if(in_array($path.$file['filename'], $file_list))
3151
			{
3152
				$dupe_count[$path.$file['filename']]++;
3153
				$file['filename'] = pathinfo($file['filename'], PATHINFO_FILENAME) .
3154
					' ('.($dupe_count[$path.$file['filename']] + 1).')' . '.' .
3155
					pathinfo($file['filename'], PATHINFO_EXTENSION);
3156
			}
3157
			// Strip special characters to make sure the files are visible for all OS (windows has issues)
3158
			$target_name = mail_bo::clean_subject_for_filename(iconv($file['charset'] ? $file['charset'] : $GLOBALS['egw_info']['server']['system_charset'], 'ASCII//IGNORE', $file['filename']));
3159
3160
			if (!($fp = Vfs::fopen($path.$target_name,'wb')) ||
3161
				!(!fseek($attachment['attachment'], 0, SEEK_SET) && stream_copy_to_stream($attachment['attachment'], $fp)))
3162
			{
3163
				$success=false;
3164
				Framework::message("Unable to zip {$target_name}",'error');
3165
			}
3166
			if ($success) $file_list[] = $path.$target_name;
3167
			if ($fp) fclose($fp);
3168
		}
3169
		$this->mail_bo->closeConnection();
3170
		if ($rememberServerID != $this->mail_bo->profileID)
3171
		{
3172
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
3173
			$this->changeProfile($rememberServerID);
3174
		}
3175
3176
		// Zip it up
3177
		Vfs::download_zip($file_list);
3178
3179
		// Clean up
3180
		Vfs::remove($temp_path);
3181
3182
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
3183
	}
3184
3185
	function get_load_email_data($uid, $partID, $mailbox,$htmlOptions=null, $smimePassphrase = '')
3186
	{
3187
		// seems to be needed, as if we open a mail from notification popup that is
3188
		// located in a different folder, we experience: could not parse message
3189
		$this->mail_bo->reopen($mailbox);
3190
		$this->mailbox = $mailbox;
0 ignored issues
show
Bug Best Practice introduced by
The property mailbox does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
3191
		$this->uid = $uid;
0 ignored issues
show
Bug Best Practice introduced by
The property uid does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
3192
		$this->partID = $partID;
0 ignored issues
show
Bug Best Practice introduced by
The property partID does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
3193
		$bufferHtmlOptions = $this->mail_bo->htmlOptions;
3194
		if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions;
3195
		// fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice
3196
		try
3197
		{
3198
			if ($smimePassphrase)
3199
			{
3200
				if ($this->mail_bo->mailPreferences['smime_pass_exp'] != $_POST['smime_pass_exp'])
3201
				{
3202
					$GLOBALS['egw']->preferences->add('mail', 'smime_pass_exp', $_POST['smime_pass_exp']);
3203
					$GLOBALS['egw']->preferences->save_repository();
3204
				}
3205
				Api\Cache::setSession('mail', 'smime_passphrase', $smimePassphrase, $_POST['smime_pass_exp'] * 60);
3206
			}
3207
			$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
3208
			if (($smime = $structure->getMetadata('X-EGroupware-Smime')))
3209
			{
3210
				$acc_smime = Mail\Smime::get_acc_smime($this->mail_bo->profileID);
3211
				$attachments = $this->mail_bo->getMessageAttachments($uid, $partID, $structure,true,false,true, $mailbox);
3212
				$push = new Api\Json\Push($GLOBALS['egw_info']['user']['account_id']);
3213
				if (!empty($acc_smime) && !empty($smime['addtocontact'])) $push->call('app.mail.smime_certAddToContact', $smime);
3214
				if (is_array($attachments))
0 ignored issues
show
introduced by
The condition is_array($attachments) is always true.
Loading history...
3215
				{
3216
					$push->call('app.mail.set_smimeAttachments', $this->createAttachmentBlock($attachments, $_GET['_messageID'], $uid, $mailbox));
3217
				}
3218
				$push->call('app.mail.set_smimeFlags', $smime);
3219
			}
3220
		}
3221
		catch(Mail\Smime\PassphraseMissing $e)
3222
		{
3223
			$acc_smime = Mail\Smime::get_acc_smime($this->mail_bo->profileID);
3224
			if (empty($acc_smime))
3225
			{
3226
				self::callWizard($e->getMessage().' '.lang('Please configure your S/MIME certificate in Encryption tab located at Edit Account dialog.'));
3227
			}
3228
			Framework::message($e->getMessage());
3229
			$configs = Api\Config::read('mail');
3230
			// do NOT include any default CSS
3231
			$smimeHtml = $this->get_email_header().
3232
			'<div class="smime-message">'.lang("This message is smime encrypted and password protected.").'</div>'.
3233
			'<form id="smimePasswordRequest" method="post">'.
3234
					'<div class="bg-style"></div>'.
3235
					'<div>'.
3236
						'<input type="password" placeholder="'.lang("Please enter password").'" name="smime_passphrase"/>'.
3237
						'<input type="submit" value="'.lang("submit").'"/>'.
3238
						'<div style="margin-top:10px;position:relative;text-align:center;margin-left:-15px;">'.
3239
							lang("Remember the password for ").
3240
								'<input name="smime_pass_exp" type="number" max="480" min="1" placeholder="'.
3241
								(is_array($configs) && $configs['smime_pass_exp'] ? $configs['smime_pass_exp'] : "10").
3242
								'" value="'.$this->mail_bo->mailPreferences['smime_pass_exp'].'"/> '.lang("minutes.").
3243
						'</div>'.
3244
					'</div>'.
3245
			'</form>';
3246
			return $smimeHtml;
3247
		}
3248
		$calendar_part = null;
3249
		$bodyParts	= $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
3250
3251
		// for meeting requests (multipart alternative with text/calendar part) let calendar render it
3252
		if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
3253
		{
3254
			$charset = $calendar_part->getContentTypeParameter('charset');
3255
			$this->mail_bo->fetchPartContents($uid, $calendar_part);
3256
			Api\Cache::setSession('calendar', 'ical', array(
3257
				'charset' => $charset ? $charset : 'utf-8',
3258
				'attachment' => $calendar_part->getContents(),
3259
				'method' => $calendar_part->getContentTypeParameter('method'),
3260
			));
3261
			$this->mail_bo->htmlOptions = $bufferHtmlOptions;
3262
			Api\Translation::add_app('calendar');
3263
			return ExecMethod('calendar.calendar_uiforms.meeting',
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod() has been deprecated: use autoloadable class-names, instanciate and call method or use static methods ( Ignorable by Annotation )

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

3263
			return /** @scrutinizer ignore-deprecated */ ExecMethod('calendar.calendar_uiforms.meeting',

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3264
				array('event'=>null,'msg'=>'','useSession'=>true)
3265
			);
3266
		}
3267
		if (!$smime)
3268
		{
3269
			Api\Session::cache_control(true);
3270
3271
			// more strict CSP for displaying mail
3272
			foreach(['frame-src', 'connect-src', 'manifest-src'] as $src)
3273
			{
3274
				Api\Header\ContentSecurityPolicy::add($src, 'none');
3275
			}
3276
			Api\Header\ContentSecurityPolicy::add('script-src', 'self', true);	// true = remove default 'unsafe-eval'
3277
			Api\Header\ContentSecurityPolicy::add('img-src', 'http:');
3278
			Api\Header\ContentSecurityPolicy::add('media-src', ['https:','http:']);
3279
		}
3280
		// Compose the content of the frame
3281
		$frameHtml =
3282
			$this->get_email_header($this->mail_bo->getStyles($bodyParts)).
3283
			$this->showBody($this->getdisplayableBody($bodyParts,true,false), false);
3284
		//IE10 eats away linebreaks preceeded by a whitespace in PRE sections
3285
		$frameHtml = str_replace(" \r\n","\r\n",$frameHtml);
3286
		$this->mail_bo->htmlOptions = $bufferHtmlOptions;
3287
3288
		return $frameHtml;
3289
	}
3290
3291
	static function get_email_header($additionalStyle='')
3292
	{
3293
		// egw_info[flags][css] already include <style> tags
3294
		$GLOBALS['egw_info']['flags']['css'] = preg_replace('|</?style[^>]*>|i', '', $additionalStyle);
3295
		$GLOBALS['egw_info']['flags']['nofooter']=true;
3296
		$GLOBALS['egw_info']['flags']['nonavbar']=true;
3297
		// do NOT include any default CSS
3298
		Framework::includeCSS('mail', 'preview', true, true);
3299
3300
		// load preview.js to activate mailto links
3301
		Framework::includeJS('/mail/js/preview.js');
3302
3303
		// send CSP and content-type header
3304
		return $GLOBALS['egw']->framework->header();
3305
	}
3306
3307
	function showBody(&$body, $print=true,$fullPageTags=true)
3308
	{
3309
		$BeginBody = '<div class="mailDisplayBody">
3310
<table width="100%" style="table-layout:fixed"><tr><td class="td_display">';
3311
3312
		$EndBody = '</td></tr></table></div>';
3313
		if ($fullPageTags) $EndBody .= "</body></html>";
3314
		if ($print)	{
3315
			print $BeginBody. $body .$EndBody;
3316
		} else {
3317
			return $BeginBody. $body .$EndBody;
3318
		}
3319
	}
3320
3321
	function &getdisplayableBody($_bodyParts,$modifyURI=true,$useTidy = true)
3322
	{
3323
		$bodyParts	= $_bodyParts;
3324
3325
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
3326
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
3327
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
3328
3329
		$body = '';
3330
3331
		//error_log(__METHOD__.array2string($bodyParts)); //exit;
3332
		if (empty($bodyParts)) return "";
3333
		foreach((array)$bodyParts as $singleBodyPart) {
3334
			if (!isset($singleBodyPart['body'])) {
3335
				$singleBodyPart['body'] = $this->getdisplayableBody($singleBodyPart,$modifyURI,$useTidy);
3336
				$body .= $singleBodyPart['body'];
3337
				continue;
3338
			}
3339
			$bodyPartIsSet = strlen(trim($singleBodyPart['body']));
3340
			if (!$bodyPartIsSet)
3341
			{
3342
				$body .= '';
3343
				continue;
3344
			}
3345
			if(!empty($body)) {
3346
				$body .= '<hr style="border:dotted 1px silver;">';
3347
			}
3348
			//error_log($singleBodyPart['body']);
3349
			//error_log(__METHOD__.__LINE__.' CharSet:'.$singleBodyPart['charSet'].' mimeType:'.$singleBodyPart['mimeType']);
3350
			// some characterreplacements, as they fail to translate
3351
			$sar = array(
3352
				'@(\x84|\x93|\x94)@',
3353
				'@(\x96|\x97|\x1a)@',
3354
				'@(\x82|\x91|\x92)@',
3355
				'@(\x85)@',
3356
				'@(\x86)@',
3357
				'@(\x99)@',
3358
				'@(\xae)@',
3359
			);
3360
			$rar = array(
3361
				'"',
3362
				'-',
3363
				'\'',
3364
				'...',
3365
				'&',
3366
				'(TM)',
3367
				'(R)',
3368
			);
3369
3370
			if(($singleBodyPart['mimeType'] == 'text/html' || $singleBodyPart['mimeType'] == 'text/plain') &&
3371
				strtoupper($singleBodyPart['charSet']) != 'UTF-8')
3372
			{
3373
				// check if client set a wrong charset and content is utf-8 --> use utf-8
3374
				if (preg_match('//u', $singleBodyPart['body']))
3375
				{
3376
					$singleBodyPart['charSet'] = 'UTF-8';
3377
				}
3378
				else
3379
				{
3380
					$singleBodyPart['body'] = preg_replace($sar,$rar,$singleBodyPart['body']);
3381
				}
3382
			}
3383
			//error_log(__METHOD__.__LINE__.'reports:'.$singleBodyPart['charSet']);
3384
			if ($singleBodyPart['charSet']=='us-ascii')
3385
			{
3386
				$orgCharSet=$singleBodyPart['charSet'];
3387
				$singleBodyPart['charSet'] = Api\Translation::detect_encoding($singleBodyPart['body']);
3388
				error_log(__METHOD__.__LINE__.'reports:'.$orgCharSet.' but seems to be:'.$singleBodyPart['charSet']);
3389
			}
3390
			$singleBodyPart['body'] = Api\Translation::convert_jsonsafe($singleBodyPart['body'],$singleBodyPart['charSet']);
3391
			//error_log(__METHOD__.__LINE__.array2string($singleBodyPart));
3392
			if($singleBodyPart['mimeType'] == 'text/plain')
3393
			{
3394
				$newBody	= @htmlentities($singleBodyPart['body'],ENT_QUOTES, strtoupper(Mail::$displayCharset));
3395
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3396
				// if empty and charset is utf8 try sanitizing the string in question
3397
				if (empty($newBody) && strtolower($singleBodyPart['charSet'])=='utf-8') $newBody = @htmlentities(iconv('utf-8', 'utf-8', $singleBodyPart['body']),ENT_QUOTES, strtoupper(Mail::$displayCharset));
3398
				// if the conversion to htmlentities fails somehow, try without specifying the charset, which defaults to iso-
3399
				if (empty($newBody)) $newBody    = htmlentities($singleBodyPart['body'],ENT_QUOTES);
3400
3401
				// search http[s] links and make them as links available again
3402
				// to understand what's going on here, have a look at
3403
				// http://www.php.net/manual/en/function.preg-replace.php
3404
3405
				// create links for websites
3406
				if ($modifyURI) $newBody = Api\Html::activate_links($newBody);
3407
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3408
				// redirect links for websites if you use no cookies
3409
				#if (!($GLOBALS['egw_info']['server']['usecookies']))
3410
				#	$newBody = preg_replace("/href=(\"|\')((http(s?):\/\/)|(www\.))([\w,\-,\/,\?,\=,\.,&amp;,!\n,\%,@,\(,\),\*,#,:,~,\+]+)(\"|\')/ie",
3411
				#		"'href=\"$webserverURL/redirect.php?go='.@htmlentities(urlencode('http$4://$5$6'),ENT_QUOTES,\"Mail::$displayCharset\").'\"'", $newBody);
3412
3413
				// create links for email addresses
3414
				//TODO:if ($modifyURI) $this->parseEmail($newBody);
3415
				// create links for inline images
3416
				if ($modifyURI)
3417
				{
3418
					$newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain');
3419
				}
3420
3421
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3422
				// to display a mailpart of mimetype plain/text, may be better taged as preformatted
3423
				#$newBody	= nl2br($newBody);
3424
				// since we do not display the message as HTML anymore we may want to insert good linebreaking (for visibility).
3425
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3426
				// dont break lines that start with > (&gt; as the text was processed with htmlentities before)
3427
				$newBody	= "<pre>".Mail::wordwrap($newBody,90,"\n",'&gt;')."</pre>";
3428
			}
3429
			else
3430
			{
3431
				$alreadyHtmlLawed=false;
3432
				$newBody	= $singleBodyPart['body'];
3433
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3434
				#error_log(print_r($newBody,true));
3435
				if ($useTidy && extension_loaded('tidy'))
3436
				{
3437
					$tidy = new tidy();
3438
					$cleaned = $tidy->repairString($newBody, Mail::$tidy_config,'utf8');
3439
					// Found errors. Strip it all so there's some output
3440
					if($tidy->getStatus() == 2)
3441
					{
3442
						error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$tidy->errorBuffer);
3443
					}
3444
					else
3445
					{
3446
						$newBody = $cleaned;
3447
					}
3448
					if (!$preserveHTML)	// ToDo KL: $preserveHTML is NOT initialised, so always if is dead code
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $preserveHTML seems to be never defined.
Loading history...
3449
					{
3450
						// filter only the 'body', as we only want that part, if we throw away the Api\Html
3451
						preg_match('`(<htm.+?<body[^>]*>)(.+?)(</body>.*?</html>)`ims', $newBody, $matches=array());
0 ignored issues
show
Bug introduced by
$matches = array() cannot be passed to preg_match() as the parameter $matches expects a reference. ( Ignorable by Annotation )

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

3451
						preg_match('`(<htm.+?<body[^>]*>)(.+?)(</body>.*?</html>)`ims', $newBody, /** @scrutinizer ignore-type */ $matches=array());
Loading history...
3452
						if ($matches[2])
3453
						{
3454
							$hasOther = true;
3455
							$newBody = $matches[2];
3456
						}
3457
					}
3458
				}
3459
				else
3460
				{
3461
					// htmLawed filter only the 'body'
3462
					preg_match('`(<htm.+?<body[^>]*>)(.+?)(</body>.*?</html>)`ims', $newBody, $matches=array());
3463
					if ($matches[2])
3464
					{
3465
						$hasOther = true;
3466
						$newBody = $matches[2];
3467
					}
3468
					$htmLawed = new Api\Html\HtmLawed();
3469
					// the next line should not be needed, but produces better results on HTML 2 Text conversion,
3470
					// as we switched off HTMLaweds tidy functionality
3471
					$newBody = str_replace(array('&amp;amp;','<DIV><BR></DIV>',"<DIV>&nbsp;</DIV>",'<div>&nbsp;</div>'),array('&amp;','<BR>','<BR>','<BR>'),$newBody);
3472
					$newBody = $htmLawed->run($newBody,Mail::$htmLawed_config);
3473
					if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3];
3474
					$alreadyHtmlLawed=true;
0 ignored issues
show
Unused Code introduced by
The assignment to $alreadyHtmlLawed is dead and can be removed.
Loading history...
3475
				}
3476
				// do the cleanup, set for the use of purifier
3477
				//$newBodyBuff = $newBody;
3478
				/* if (!$alreadyHtmlLawed)*/ Mail::getCleanHTML($newBody);
3479
/*
3480
				// in a way, this tests if we are having real utf-8 (the displayCharset) by now; we should if charsets reported (or detected) are correct
3481
				if (strtoupper(Mail::$displayCharset) == 'UTF-8')
3482
				{
3483
					$test = @json_encode($newBody);
3484
					//error_log(__METHOD__.__LINE__.' ->'.strlen($singleBodyPart['body']).' Error:'.json_last_error().'<- BodyPart:#'.$test.'#');
3485
					if (($test=="null" || $test === false || is_null($test)) && strlen($newBody)>0)
3486
					{
3487
						$newBody = $newBodyBuff;
3488
						$tv = Mail::$htmLawed_config['tidy'];
3489
						Mail::$htmLawed_config['tidy'] = 0;
3490
						Mail::getCleanHTML($newBody);
3491
						Mail::$htmLawed_config['tidy'] = $tv;
3492
					}
3493
				}
3494
*/
3495
				// removes stuff between http and ?http
3496
				$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';    // only http:// gets removed, other protocolls are shown
3497
				$newBody = preg_replace('~'.$Protocol.'[^>]*\?'.$Protocol.'~sim','$1',$newBody); // removes stuff between http:// and ?http://
3498
				// TRANSFORM MAILTO LINKS TO EMAILADDRESS ONLY, WILL BE SUBSTITUTED BY parseEmail TO CLICKABLE LINK
3499
				$newBody = preg_replace('/(?<!"|href=|href\s=\s|href=\s|href\s=)'.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
3500
					"\\1@\\2.\\3",
3501
					$newBody);
3502
3503
				// redirect links for websites if you use no cookies
3504
				#if (!($GLOBALS['egw_info']['server']['usecookies'])) { //do it all the time, since it does mask the mailadresses in urls
3505
					//TODO:if ($modifyURI) $this->parseHREF($newBody);
3506
				#}
3507
				// create links for inline images
3508
				if ($modifyURI)
3509
				{
3510
					$newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID);
3511
				}
3512
				// email addresses / mailto links get now activated on client-side
3513
			}
3514
3515
			$body .= $newBody;
3516
		}
3517
		// create links for windows shares
3518
		// \\\\\\\\ == '\\' in real life!! :)
3519
		$body = preg_replace("/(\\\\\\\\)([\w,\\\\,-]+)/i",
3520
			"<a href=\"file:$1$2\" target=\"_blank\"><font color=\"blue\">$1$2</font></a>", $body);
3521
3522
		$body = preg_replace($nonDisplayAbleCharacters,'',$body);
3523
3524
		return $body;
3525
	}
3526
3527
	/**
3528
	 * Resolve inline images from CID to proper url
3529
	 *
3530
	 * @param string $_body message content
3531
	 * @param string $_mailbox mail folder
3532
	 * @param string $_uid uid
3533
	 * @param string $_partID part id
3534
	 * @param string $_messageType = 'html', message type is either html or plain
3535
	 * @return string message body including all CID images replaced
3536
	 */
3537
	public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html')
3538
	{
3539
		if ($_messageType === 'plain')
3540
		{
3541
			return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain');
3542
		}
3543
		else
3544
		{
3545
			foreach(array('src','url','background') as $type)
3546
			{
3547
				$_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type);
3548
			}
3549
			return $_body;
3550
		}
3551
	}
3552
3553
	/**
3554
	 * Replace CID with proper type of content understandable by browser
3555
	 *
3556
	 * @param type $_body content of message
3557
	 * @param type $_mailbox mail box
3558
	 * @param type $_uid uid
3559
	 * @param type $_partID part id
3560
	 * @param type $_type = 'src' type of inline image that needs to be resolved and replaced
3561
	 *	- types: {plain|src|url|background}
3562
	 * @param callback $_link_callback Function to generate the link to the image.  If
3563
	 *	not provided, a default (using mail) will be used.
3564
	 * @return string returns body content including all CID replacements
3565
	 */
3566
	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src', callable $_link_callback = null)
3567
	{
3568
		/**
3569
		 * Callback to generate the link
3570
		 */
3571
		if(is_null($_link_callback))
3572
		{
3573
			$_link_callback = function($_cid) use ($_mailbox, $_uid, $_partID)
3574
			{
3575
				$linkData = array (
3576
					'menuaction'    => 'mail.mail_ui.displayImage',
3577
					'uid'		=> $_uid,
3578
					'mailbox'	=> base64_encode($_mailbox),
3579
					'cid'		=> base64_encode($_cid),
3580
					'partID'	=> $_partID,
3581
				);
3582
				return Egw::link('/index.php', $linkData);
3583
			};
3584
		}
3585
3586
		/**
3587
		 * Callback for preg_replace_callback function
3588
		 * returns matched CID replacement string based on given type
3589
		 * @param array $matches
3590
		 * @param string $_mailbox
3591
		 * @param string $_uid
3592
		 * @param string $_partID
3593
		 * @param string $_type
3594
		 * @return string|boolean returns the replace
3595
		*/
3596
		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type, $_link_callback)
0 ignored issues
show
Unused Code introduced by
The import $_mailbox is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
3597
		{
3598
			if (!$_type)	return false;
3599
			$CID = '';
3600
			// Build up matches according to selected type
3601
			switch ($_type)
3602
			{
3603
				case "plain":
3604
					$CID = $matches[1];
3605
					break;
3606
				case "src":
3607
					// as src:cid contains some kind of url, it is likely to be urlencoded
3608
					$CID = urldecode($matches[2]);
3609
					break;
3610
				case "url":
3611
					$CID = $matches[1];
3612
					break;
3613
				case "background":
3614
					$CID = $matches[2];
3615
					break;
3616
			}
3617
3618
			static $cache = array();	// some caching, if mails containing the same image multiple times
3619
3620
			if (is_array($matches) && $CID)
3621
			{
3622
				$imageURL = call_user_func($_link_callback, $CID);
3623
				// to test without data uris, comment the if close incl. it's body
3624
				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
3625
				{
3626
					if (!isset($cache[$imageURL]))
3627
					{
3628
						if ($_type !="background" && !$imageURL)
3629
						{
3630
							$bo = Mail::getInstance(false, mail_ui::$icServerID);
3631
							$attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID);
3632
3633
							// only use data uri for "smaller" images, as otherwise the first display of the mail takes to long
3634
							if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192)	// msie=8 allows max 32k data uris
3635
							{
3636
								$bo->fetchPartContents($_uid, $attachment);
3637
								$cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents());
3638
							}
3639
							else
3640
							{
3641
								$cache[$imageURL] = $imageURL;
3642
							}
3643
						}
3644
						else
3645
						{
3646
							$cache[$imageURL] = $imageURL;
3647
						}
3648
					}
3649
					$imageURL = $cache[$imageURL];
3650
				}
3651
3652
				// Decides the final result of replacement according to the type
3653
				switch ($_type)
3654
				{
3655
					case "plain":
3656
						return '<img src="'.$imageURL.'" />';
3657
					case "src":
3658
						return 'src="'.$imageURL.'"';
3659
					case "url":
3660
						return 'url('.$imageURL.');';
3661
					case "background":
3662
						return 'background="'.$imageURL.'"';
3663
				}
3664
			}
3665
			return false;
3666
		};
3667
3668
		// return new body content base on chosen type
3669
		switch($_type)
3670
		{
3671
			case"plain":
3672
				return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body);
3673
			case "src":
3674
				return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3675
			case "url":
3676
				return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body);
3677
			case "background":
3678
				return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3679
		}
3680
	}
3681
3682
	/**
3683
	 * Create a new message from modified message then sends the original one to
3684
	 * the trash.
3685
	 *
3686
	 * @param string $_rowID row id
3687
	 * @param string $_subject subject to be replaced with old subject
3688
	 *
3689
	 * Sends json response to client with following data:
3690
	 *		array (
3691
	 *			success => boolean
3692
	 *			msg => string
3693
	 *		)
3694
	 */
3695
	function ajax_saveModifiedMessageSubject ($_rowID, $_subject)
3696
	{
3697
		$response = Api\Json\Response::get();
3698
		$idData = self::splitRowID($_rowID);
3699
		$folder = $idData['folder'];
3700
		try {
3701
			$raw = $this->mail_bo->getMessageRawBody($idData['msgUID'],'', $folder);
3702
			$result = array ('success' => true, 'msg' =>'');
3703
			if ($raw && $_subject)
3704
			{
3705
				$mailer = new Api\Mailer();
3706
				$this->mail_bo->parseRawMessageIntoMailObject($mailer, $raw);
3707
				$mailer->removeHeader('subject');
3708
				$mailer->addHeader('subject', $_subject);
3709
				$this->mail_bo->openConnection();
3710
				$delimiter = $this->mail_bo->getHierarchyDelimiter();
3711
				if($folder == 'INBOX'.$delimiter) $folder='INBOX';
3712
				if ($this->mail_bo->folderExists($folder,true))
3713
				{
3714
					$this->mail_bo->appendMessage($folder, $mailer->getRaw(), null,'\\Seen');
3715
					$this->mail_bo->deleteMessages($idData['msgUID'], $folder);
3716
				}
3717
				else
3718
				{
3719
					$result['success'] = false;
3720
					$result['msg'] = lang('Changing subject failed folder %1 does not exist', $folder);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $folder. ( Ignorable by Annotation )

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

3720
					$result['msg'] = /** @scrutinizer ignore-call */ lang('Changing subject failed folder %1 does not exist', $folder);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
3721
				}
3722
			}
3723
		} catch (Exception $e) {
3724
			$result['success'] = false;
3725
			$result['msg'] = lang('Changing subject failed because of %1 ', $e->getMessage());
3726
		}
3727
		$response->data($result);
3728
	}
3729
3730
	/**
3731
	 * importMessage
3732
	 * @param array $content = null an array of content
3733
	 */
3734
	function importMessage($content=null)
3735
	{
3736
		//error_log(__METHOD__.__LINE__.$this->mail_bo->getDraftFolder());
3737
3738
		if (!empty($content))
3739
		{
3740
			//error_log(__METHOD__.__LINE__.array2string($content));
3741
			if ($content['vfsfile'])
3742
			{
3743
				$file = $content['vfsfile'] = array(
3744
					'name' => Vfs::basename($content['vfsfile']),
3745
					'type' => Vfs::mime_content_type($content['vfsfile']),
3746
					'file' => Vfs::PREFIX.$content['vfsfile'],
3747
					'size' => filesize(Vfs::PREFIX.$content['vfsfile']),
3748
				);
3749
			}
3750
			else
3751
			{
3752
				$file = $content['uploadForImport'];
3753
			}
3754
			$destination = $content['FOLDER'][0];
3755
3756
			if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
3757
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3758
			{
3759
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3760
				$this->changeProfile($icServerID);
3761
			}
3762
			//error_log(__METHOD__.__LINE__.self::$delimiter.array2string($destination));
3763
			$importID = Mail::getRandomString();
3764
			$importFailed = false;
3765
			try
3766
			{
3767
				$messageUid = $this->importMessageToFolder($file,$destination,$importID);
3768
			    $linkData = array
3769
			    (
3770
					'id' => $this->createRowID($destination, $messageUid, true),
3771
			    );
3772
			}
3773
			catch (Api\Exception\WrongUserinput $e)
3774
			{
3775
					$importFailed=true;
3776
					$content['msg']		= $e->getMessage();
3777
			}
3778
			if (!$importFailed)
3779
			{
3780
				Api\Json\Response::get()->call('egw.open', $linkData['id'], 'mail', 'view');
3781
				Api\Json\Response::get()->call('window.close');
3782
				return;
3783
			}
3784
		}
3785
		if (!is_array($content)) $content = array();
3786
		if (empty($content['FOLDER'])) $content['FOLDER']=(array)$this->mail_bo->getDraftFolder();
3787
		if (!empty($content['FOLDER']))
3788
		{
3789
			$compose = new mail_compose();
3790
			$sel_options['FOLDER'] = $compose->ajax_searchFolder(0,true);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sel_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sel_options = array(); before regardless.
Loading history...
3791
		}
3792
3793
		$etpl = new Etemplate('mail.importMessage');
3794
		$etpl->setElementAttribute('uploadForImport','onFinish','app.mail.uploadForImport');
3795
		$etpl->exec('mail.mail_ui.importMessage',$content,$sel_options,array(),array(),2);
3796
	}
3797
3798
	/**
3799
	 * importMessageToFolder
3800
	 *
3801
	 * @param array $_formData Array with information of name, type, file and size
3802
	 * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if
3803
	 *					folder is modified
3804
	 * @param string $importID ID for the imported message, used by attachments to identify them unambiguously
3805
	 * @return mixed $messageUID or exception
3806
	 */
3807
	function importMessageToFolder($_formData,&$_folder,$importID='')
3808
	{
3809
		$importfailed = false;
3810
		//error_log(__METHOD__.__LINE__.array2string($_formData));
3811
		if (empty($_formData['file'])) $_formData['file'] = $_formData['tmp_name'];
3812
		// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
3813
		try
3814
		{
3815
			$tmpFileName = Mail::checkFileBasics($_formData,$importID);
3816
		}
3817
		catch (Api\Exception\WrongUserinput $e)
3818
		{
3819
			$importfailed = true;
3820
			$alert_msg .= $e->getMessage();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $alert_msg seems to be never defined.
Loading history...
3821
		}
3822
		// -----------------------------------------------------------------------
3823
		if ($importfailed === false)
3824
		{
3825
			$mailObject = new Api\Mailer();
3826
			try
3827
			{
3828
				$this->mail_bo->parseFileIntoMailObject($mailObject, $tmpFileName);
3829
			}
3830
			catch (Api\Exception\AssertionFailed $e)
3831
			{
3832
				$importfailed = true;
3833
				$alert_msg .= $e->getMessage();
3834
			}
3835
			$this->mail_bo->openConnection();
3836
			if (empty($_folder))
3837
			{
3838
				$importfailed = true;
3839
				$alert_msg .= lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $_formData['name']. ( Ignorable by Annotation )

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

3839
				$alert_msg .= /** @scrutinizer ignore-call */ lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
3840
			}
3841
			$delimiter = $this->mail_bo->getHierarchyDelimiter();
3842
			if($_folder=='INBOX'.$delimiter) $_folder='INBOX';
3843
			if ($importfailed === false)
3844
			{
3845
				if ($this->mail_bo->folderExists($_folder,true)) {
3846
					try
3847
					{
3848
						$messageUid = $this->mail_bo->appendMessage($_folder,
3849
							$mailObject->getRaw(),
3850
							null,'\\Seen');
3851
					}
3852
					catch (Api\Exception\WrongUserinput $e)
3853
					{
3854
						$importfailed = true;
3855
						$alert_msg .= lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$_formData['name'],$_folder,$e->getMessage());
3856
					}
3857
				}
3858
				else
3859
				{
3860
					$importfailed = true;
3861
					$alert_msg .= lang("Import of message %1 failed. Destination Folder %2 does not exist.",$_formData['name'],$_folder);
3862
				}
3863
			}
3864
		}
3865
		// set the url to open when refreshing
3866
		if ($importfailed == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
3867
		{
3868
			throw new Api\Exception\WrongUserinput($alert_msg);
3869
		}
3870
		else
3871
		{
3872
			return $messageUid;
3873
		}
3874
	}
3875
3876
	/**
3877
	 * importMessageFromVFS2DraftAndEdit
3878
	 *
3879
	 * @param array $formData Array with information of name, type, file and size; file is required,
3880
	 *                               name, type and size may be set here to meet the requirements
3881
	 *						Example: $formData['name']	= 'a_email.eml';
3882
	 *								 $formData['type']	= 'message/rfc822';
3883
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3884
	 *								 $formData['size']	= 2136;
3885
	 * @return void
3886
	 */
3887
	function importMessageFromVFS2DraftAndEdit($formData='')
3888
	{
3889
		$this->importMessageFromVFS2DraftAndDisplay($formData,'edit');
3890
	}
3891
3892
	/**
3893
	 * importMessageFromVFS2DraftAndDisplay
3894
	 *
3895
	 * @param array $formData Array with information of name, type, file and size; file is required,
3896
	 *                               name, type and size may be set here to meet the requirements
3897
	 *						Example: $formData['name']	= 'a_email.eml';
3898
	 *								 $formData['type']	= 'message/rfc822';
3899
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3900
	 *								 $formData['size']	= 2136;
3901
	 * @param string $mode mode to open ImportedMessage display and edit are supported
3902
	 * @return void
3903
	 */
3904
	function importMessageFromVFS2DraftAndDisplay($formData='',$mode='display')
3905
	{
3906
		if (empty($formData)) if (isset($_REQUEST['formData'])) $formData = $_REQUEST['formData'];
3907
		//error_log(__METHOD__.__LINE__.':'.array2string($formData).' Mode:'.$mode.'->'.function_backtrace());
3908
		$draftFolder = $this->mail_bo->getDraftFolder(false);
3909
		$importID = Mail::getRandomString();
3910
3911
		// handling for mime-data hash
3912
		if (!empty($formData['data']))
3913
		{
3914
			$formData['file'] = 'egw-data://'.$formData['data'];
3915
		}
3916
		// name should be set to meet the requirements of checkFileBasics
3917
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['name']))
3918
		{
3919
			$buff = explode('/',$formData['file']);
3920
			if (is_array($buff)) $formData['name'] = array_pop($buff); // take the last part as name
0 ignored issues
show
introduced by
The condition is_array($buff) is always true.
Loading history...
3921
		}
3922
		// type should be set to meet the requirements of checkFileBasics
3923
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['type']))
3924
		{
3925
			$buff = explode('.',$formData['file']);
3926
			$suffix = '';
3927
			if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
0 ignored issues
show
introduced by
The condition is_array($buff) is always true.
Loading history...
3928
			if (!empty($suffix)) $formData['type'] = Api\MimeMagic::ext2mime($suffix);
3929
		}
3930
		// size should be set to meet the requirements of checkFileBasics
3931
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && !isset($formData['size']))
3932
		{
3933
			$formData['size'] = strlen($formData['file']); // set some size, to meet requirements of checkFileBasics
3934
		}
3935
		try
3936
		{
3937
			$messageUid = $this->importMessageToFolder($formData,$draftFolder,$importID);
3938
			$linkData = array
3939
			(
3940
		        'menuaction'    => ($mode=='display'?'mail.mail_ui.displayMessage':'mail.mail_compose.composeFromDraft'),
3941
				'id'		=> $this->createRowID($draftFolder,$messageUid,true),
3942
				'deleteDraftOnClose' => 1,
3943
			);
3944
			if ($mode!='display')
3945
			{
3946
				unset($linkData['deleteDraftOnClose']);
3947
				$linkData['method']	='importMessageToMergeAndSend';
3948
			}
3949
			else
3950
			{
3951
				$linkData['mode']=$mode;
3952
			}
3953
			Egw::redirect_link('/index.php',$linkData);
3954
		}
3955
		catch (Api\Exception\WrongUserinput $e)
3956
		{
3957
			Framework::window_close($e->getMessage());
3958
		}
3959
	}
3960
3961
	/**
3962
	 * loadEmailBody
3963
	 *
3964
	 * @param string _messageID UID
0 ignored issues
show
Bug introduced by
The type _messageID was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3965
	 *
3966
	 * @return xajax response
0 ignored issues
show
Bug introduced by
The type xajax was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3967
	 */
3968
	function loadEmailBody($_messageID=null,$_partID=null,$_htmloptions=null)
3969
	{
3970
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3971
		if (!$_messageID && !empty($_GET['_messageID'])) $_messageID = $_GET['_messageID'];
3972
		if (!$_partID && !empty($_GET['_partID'])) $_partID = $_GET['_partID'];
3973
		if (!$_htmloptions && !empty($_GET['_htmloptions'])) $_htmloptions = $_GET['_htmloptions'];
3974
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageID,true).",$_partID,$_htmloptions");
3975
		if (empty($_messageID)) return "";
3976
		$uidA = self::splitRowID($_messageID);
3977
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
3978
		$messageID = $uidA['msgUID'];
3979
		$icServerID = $uidA['profileID'];
3980
		//something went wrong. there is a $_messageID but no $messageID: means $_messageID is crippeled
3981
		if (empty($messageID)) return "";
3982
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
3983
		{
3984
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3985
			$this->changeProfile($icServerID);
3986
		}
3987
3988
		$bodyResponse = $this->get_load_email_data($messageID,$_partID,$folder,$_htmloptions, $_POST['smime_passphrase']);
3989
		//error_log(array2string($bodyResponse));
3990
		echo $bodyResponse;
0 ignored issues
show
Bug introduced by
Are you sure $bodyResponse of type false|mixed|string can be used in echo? ( Ignorable by Annotation )

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

3990
		echo /** @scrutinizer ignore-type */ $bodyResponse;
Loading history...
3991
3992
	}
3993
3994
	/**
3995
	 * ajax_setFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3996
	 * gets the counters and sets the text of a treenode if needed (unread Messages found)
3997
	 * @param array $_folder folders to refresh its unseen message counters
3998
	 * @return nothing
3999
	 */
4000
	function ajax_setFolderStatus($_folder)
4001
	{
4002
		Api\Translation::add_app('mail');
4003
		//error_log(__METHOD__.__LINE__.array2string($_folder));
4004
		if ($_folder)
0 ignored issues
show
Bug Best Practice introduced by
The expression $_folder 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...
4005
		{
4006
			$this->mail_bo->getHierarchyDelimiter(false);
4007
			$oA = array();
4008
			foreach ($_folder as $_folderName)
4009
			{
4010
				list($profileID,$folderName) = explode(self::$delimiter,$_folderName,2);
4011
				if (is_numeric($profileID))
4012
				{
4013
					if ($profileID != $this->mail_bo->profileID) continue; // only current connection
4014
					if ($folderName)
4015
					{
4016
						try
4017
						{
4018
							$fS = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4019
						}
4020
						catch (Exception $e)
4021
						{
4022
							if (Mail::$debug) error_log(__METHOD__,' ()'.$e->getMessage ());
4023
							continue;
4024
						}
4025
						if (in_array($fS['shortDisplayName'],Mail::$autoFolders)) $fS['shortDisplayName']=lang($fS['shortDisplayName']);
4026
						//error_log(__METHOD__.__LINE__.array2string($fS));
4027
						if ($fS['unseen'])
4028
						{
4029
							$oA[$_folderName] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
4030
						}
4031
						if ($fS['unseen']==0 && $fS['shortDisplayName'])
4032
						{
4033
							$oA[$_folderName] = $fS['shortDisplayName'];
4034
						}
4035
					}
4036
				}
4037
			}
4038
			//error_log(__METHOD__.__LINE__.array2string($oA));
4039
			if ($oA)
4040
			{
4041
				$response = Api\Json\Response::get();
4042
				$response->call('app.mail.mail_setFolderStatus',$oA);
4043
			}
4044
		}
4045
	}
4046
4047
	/**
4048
	 * This function creates folder/subfolder based on its selected parent
4049
	 *
4050
	 * @param string $_parent folder name or profile+folder name to add a folder to
4051
	 * @param string $_new new folder name to be created
4052
	 *
4053
	 */
4054
	function ajax_addFolder($_parent, $_new)
4055
	{
4056
		$error='';
4057
		$created = false;
4058
		$response = Api\Json\Response::get();
4059
		$del = $this->mail_bo->getHierarchyDelimiter(false);
4060
		if (strpos($_new, $del) !== FALSE)
4061
		{
4062
			return $response->call('egw.message', lang('failed to rename %1 ! Reason: %2 is not allowed!',$_parent, $del));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $response->call('egw.mes...wed!', $_parent, $del)) targeting EGroupware\Api\Json\Msg::call() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
The call to lang() has too many arguments starting with $_parent. ( Ignorable by Annotation )

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

4062
			return $response->call('egw.message', /** @scrutinizer ignore-call */ lang('failed to rename %1 ! Reason: %2 is not allowed!',$_parent, $del));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
4063
		}
4064
		if ($_parent)
4065
		{
4066
			$parent = $this->mail_bo->decodeEntityFolderName($_parent);
4067
			//the conversion is handeled by horde, frontend interaction is all utf-8
4068
			$new = $this->mail_bo->decodeEntityFolderName($_new);
4069
4070
			list($profileID,$p_no_delimiter) = explode(self::$delimiter,$parent,2);
4071
4072
			if (is_numeric($profileID))
4073
			{
4074
				if ($profileID != $this->mail_bo->profileID) $this->changeProfile ($profileID);
4075
				$delimiter = $this->mail_bo->getHierarchyDelimiter(false);
4076
				$parts = explode($delimiter,$new);
4077
4078
				if (!!empty($parent)) $folderStatus = $this->mail_bo->getFolderStatus($parent,false);
4079
4080
				//open the INBOX
4081
				$this->mail_bo->reopen('INBOX');
4082
4083
				// if $new has delimiter ($del) in it, we need to create the subtree
4084
				if (!empty($parts))
4085
				{
4086
					$counter = 0;
4087
					foreach($parts as $subTree)
4088
					{
4089
						$err = null;
4090
						if(($new = $this->mail_bo->createFolder($p_no_delimiter, $subTree, $err)))
4091
						{
4092
							$counter++;
4093
							if (!$p_no_delimiter)
4094
							{
4095
								$status = $this->mail_bo->getFolderStatus($new,false, true, true);
4096
								if (!$status['subscribed'])
4097
								{
4098
									try
4099
									{
4100
										$this->mail_bo->icServer->subscribeMailbox ('INBOX'.$delimiter.$new);
4101
									}
4102
									catch(Horde_Imap_Client_Exception $e)
4103
									{
4104
										$error = Lang('Folder %1 has been created successfully,'.
4105
												' although the subscription failed because of %2', $new, $e->getMessage());
4106
									}
4107
								}
4108
							}
4109
						}
4110
						else
4111
						{
4112
							if (!$p_no_delimiter)
4113
							{
4114
								$new = $this->mail_bo->createFolder('INBOX', $subTree, $err);
4115
								if ($new) $counter++;
4116
							}
4117
							else
4118
							{
4119
								$error .= $err;
4120
							}
4121
						}
4122
					}
4123
					if ($counter == count($parts)) $created=true;
4124
				}
4125
				if (!empty($new)) $this->mail_bo->reopen($new);
4126
			}
4127
4128
4129
			if ($created===true && $error =='')
4130
			{
4131
				$this->mail_bo->resetFolderObjectCache($profileID);
4132
				if ( $folderStatus['shortDisplayName'])
4133
				{
4134
					$nodeInfo = array($parent=>$folderStatus['shortDisplayName']);
4135
				}
4136
				else
4137
				{
4138
					$nodeInfo = array($profileID=>lang('INBOX'));
4139
				}
4140
				$response->call('app.mail.mail_reloadNode',$nodeInfo);
4141
			}
4142
			else
4143
			{
4144
				if ($error)
4145
				{
4146
					$response->call('egw.message',$error);
4147
				}
4148
			}
4149
		}
4150
		else {
4151
			error_log(__METHOD__.__LINE__."()"."This function needs a parent folder to work!");
4152
		}
4153
	}
4154
4155
	/**
4156
	 * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4157
	 * @param string $_folderName folder to rename and refresh
4158
	 * @param string $_newName new foldername
4159
	 * @return nothing
4160
	 */
4161
	function ajax_renameFolder($_folderName, $_newName)
4162
	{
4163
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName).' NewName:'.array2string($_newName));
4164
		//error_log(__METHOD__.__LINE__.array2string($oA));
4165
		$response = Api\Json\Response::get();
4166
		$del = $this->mail_bo->getHierarchyDelimiter(false);
4167
		if (strpos($_newName, $del) !== FALSE)
4168
		{
4169
			return $response->call('egw.message', lang('failed to rename %1 ! Reason: %2 is not allowed!',$_folderName, $del));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $response->call('egw.mes...', $_folderName, $del)) targeting EGroupware\Api\Json\Msg::call() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
The call to lang() has too many arguments starting with $_folderName. ( Ignorable by Annotation )

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

4169
			return $response->call('egw.message', /** @scrutinizer ignore-call */ lang('failed to rename %1 ! Reason: %2 is not allowed!',$_folderName, $del));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
4170
		}
4171
4172
		if ($_folderName)
4173
		{
4174
			Api\Translation::add_app('mail');
4175
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4176
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
4177
4178
			$oA = array();
4179
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4180
			$hasChildren = false;
4181
			if (is_numeric($profileID))
4182
			{
4183
				if ($profileID != $this->mail_bo->profileID) $this->changeProfile ($profileID);
4184
				$pA = explode($del,$folderName);
4185
				array_pop($pA);
4186
				$parentFolder = implode($del,$pA);
4187
				if (strtoupper($folderName)!= 'INBOX')
4188
				{
4189
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
4190
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false);
4191
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4192
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4193
					{
4194
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4195
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4196
						$nameSpace = $this->mail_bo->_getNameSpaces();
4197
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4198
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4199
						$fragments = array();
4200
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4201
						foreach ($subFolders as $k => $folder)
4202
						{
4203
							// we do not monitor failure or success on subfolders
4204
							if ($folder == $folderName)
4205
							{
4206
								unset($subFolders[$k]);
4207
							}
4208
							else
4209
							{
4210
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rv is correct as $this->mail_bo->icServer...Mailbox($folder, false) targeting Horde_Imap_Client_Base::subscribeMailbox() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
The assignment to $rv is dead and can be removed.
Loading history...
4211
								$fragments[$profileID.self::$delimiter.$folder] = substr($folder,strlen($folderName));
4212
							}
4213
						}
4214
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($fragments));
4215
					}
4216
4217
					$this->mail_bo->reopen('INBOX');
4218
					$success = false;
4219
					try
4220
					{
4221
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
4222
						{
4223
							$this->mail_bo->resetFolderObjectCache($profileID);
4224
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
4225
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
4226
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
4227
							$success = true;
4228
						}
4229
					}
4230
					catch (Exception $e)
4231
					{
4232
						$newFolderName=$folderName;
4233
						$msg = $e->getMessage();
4234
					}
4235
					$this->mail_bo->reopen($newFolderName);
4236
					$fS = $this->mail_bo->getFolderStatus($newFolderName,false);
4237
					//error_log(__METHOD__.__LINE__.array2string($fS));
4238
					if ($hasChildren)
4239
					{
4240
						$subFolders = $this->mail_bo->getMailBoxesRecursive($newFolderName, $delimiter, $prefix);
4241
						foreach ($subFolders as $k => $folder)
4242
						{
4243
							// we do not monitor failure or success on subfolders
4244
							if ($folder == $folderName)
4245
							{
4246
								unset($subFolders[$k]);
4247
							}
4248
							else
4249
							{
4250
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rv is correct as $this->mail_bo->icServer...eMailbox($folder, true) targeting Horde_Imap_Client_Base::subscribeMailbox() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
4251
							}
4252
						}
4253
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4254
					}
4255
4256
					$oA[$_folderName]['id'] = $profileID.self::$delimiter.$newFolderName;
4257
					$oA[$_folderName]['olddesc'] = $oldFolderInfo['shortDisplayName'];
4258
					if ($fS['unseen'])
4259
					{
4260
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
4261
4262
					}
4263
					else
4264
					{
4265
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'];
4266
					}
4267
					foreach($fragments as $oldFolderName => $fragment)
4268
					{
4269
						//error_log(__METHOD__.__LINE__.':'.$oldFolderName.'->'.$profileID.self::$delimiter.$newFolderName.$fragment);
4270
						$oA[$oldFolderName]['id'] = $profileID.self::$delimiter.$newFolderName.$fragment;
4271
						$oA[$oldFolderName]['olddesc'] = '#skip-user-interaction-message#';
4272
						$fS = $this->mail_bo->getFolderStatus($newFolderName.$fragment,false);
4273
						if ($fS['unseen'])
4274
						{
4275
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
4276
4277
						}
4278
						else
4279
						{
4280
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'];
4281
						}
4282
					}
4283
				}
4284
			}
4285
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4286
			{
4287
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
0 ignored issues
show
Bug Best Practice introduced by
The property sessionData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
4288
				$this->mail_bo->saveSessionData();
4289
			}
4290
			//error_log(__METHOD__.__LINE__.array2string($oA));
4291
			$response = Api\Json\Response::get();
4292
			if ($oA && $success)
4293
			{
4294
				$response->call('app.mail.mail_setLeaf',$oA);
4295
			}
4296
			else
4297
			{
4298
				$response->call('egw.refresh',lang('failed to rename %1 ! Reason: %2',$oldFolderName,$msg),'mail');
4299
			}
4300
		}
4301
	}
4302
4303
	/**
4304
	 * reload node
4305
	 *
4306
	 * @param string _folderName  folder to reload
0 ignored issues
show
Bug introduced by
The type _folderName was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4307
	 * @param boolean $_subscribedOnly = true
4308
	 * @return void
4309
	 */
4310
	function ajax_reloadNode($_folderName,$_subscribedOnly=true)
4311
	{
4312
		Api\Translation::add_app('mail');
4313
		$oldPrefForSubscribedOnly = !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'];
4314
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4315
		list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4316
		if ($profileID != $this->mail_bo->profileID) $this->changeProfile($profileID);
4317
4318
		// if pref and required mode dont match -> reset the folderObject cache to ensure
4319
		// that we get what we request
4320
		if ($_subscribedOnly != $oldPrefForSubscribedOnly) $this->mail_bo->resetFolderObjectCache($profileID);
4321
4322
		if (!empty($folderName))
4323
		{
4324
			$parentFolder=(!empty($folderName)?$folderName:'INBOX');
4325
			$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4326
			if ($folderInfo['unseen'])
4327
			{
4328
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'].' ('.$folderInfo['unseen'].')';
4329
			}
4330
			if ($folderInfo['unseen']==0 && $folderInfo['shortDisplayName'])
4331
			{
4332
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'];
4333
			}
4334
4335
			$refreshData = array(
4336
				$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4337
		}
4338
		else
4339
		{
4340
			$refreshData = array(
4341
				$profileID=>lang('INBOX')//string with no meaning lateron
4342
			);
4343
		}
4344
		// Send full info back in the response
4345
		$response = Api\Json\Response::get();
4346
		foreach($refreshData as $folder => &$name)
4347
		{
4348
			$name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true);
4349
		}
4350
		$response->call('app.mail.mail_reloadNode',$refreshData);
4351
4352
	}
4353
4354
	/**
4355
	 * ResolveWinmail fetches the encoded attachments
4356
	 * from winmail.dat and will response expected structure back
4357
	 * to client in order to display them.
4358
	 *
4359
	 * Note: this ajax function should only be called via
4360
	 * nm mail selection as it does not support profile change
4361
	 * and uses the current available ic_server connection.
4362
	 *
4363
	 * @param type $_rowid row id from nm
4364
	 *
4365
	 */
4366
	function ajax_resolveWinmail ($_rowid)
4367
	{
4368
		$response = Api\Json\Response::get();
4369
4370
		$idParts = self::splitRowID($_rowid);
4371
		$uid = $idParts['msgUID'];
4372
		$mbox = $idParts['folder'];
4373
4374
		$attachments = $this->mail_bo->getMessageAttachments($uid, null, null, false,true,true,$mbox);
4375
		if (is_array($attachments))
0 ignored issues
show
introduced by
The condition is_array($attachments) is always true.
Loading history...
4376
		{
4377
			$attachments = $this->createAttachmentBlock($attachments, $_rowid, $uid, $mbox, false);
4378
			$response->data($attachments);
4379
		}
4380
		else
4381
		{
4382
			$response->call('egw.message', lang('Can not resolve the winmail.dat attachment!'));
4383
		}
4384
	}
4385
4386
	/**
4387
	 * move folder
4388
	 *
4389
	 * @param string _folderName  folder to vove
4390
	 * @param string _target target folder
0 ignored issues
show
Bug introduced by
The type _target was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4391
	 *
4392
	 * @return void
4393
	 */
4394
	function ajax_MoveFolder($_folderName, $_target)
4395
	{
4396
		if (Mail::$debug) error_log(__METHOD__.__LINE__."Move Folder: $_folderName to Target: $_target");
4397
		if ($_folderName)
4398
		{
4399
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4400
			$_newLocation2 = $this->mail_bo->decodeEntityFolderName($_target);
4401
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4402
			list($newProfileID,$_newLocation) = explode(self::$delimiter,$_newLocation2,2);
4403
			if ($profileID != $this->mail_bo->profileID || $profileID != $newProfileID) $this->changeProfile($profileID);
4404
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4405
			$hasChildren = false;
4406
			if (is_numeric($profileID))
4407
			{
4408
				$pA = explode($del,$folderName);
4409
				$namePart = array_pop($pA);
4410
				$_newName = $namePart;
4411
				$oldParentFolder = implode($del,$pA);
4412
				$parentFolder = $_newLocation;
4413
4414
				if (strtoupper($folderName)!= 'INBOX' &&
4415
					(($oldParentFolder === $parentFolder) || //$oldParentFolder == $parentFolder means move on same level
4416
					(($oldParentFolder != $parentFolder &&
4417
					strlen($parentFolder)>0 && strlen($folderName)>0 &&
4418
					strpos($parentFolder,$folderName)===false)))) // indicates that we move the older up the tree within its own branch
4419
				{
4420
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
4421
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4422
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4423
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4424
					{
4425
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4426
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4427
						$nameSpace = $this->mail_bo->_getNameSpaces();
4428
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4429
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4430
4431
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4432
						foreach ($subFolders as $k => $folder)
4433
						{
4434
							// we do not monitor failure or success on subfolders
4435
							if ($folder == $folderName)
4436
							{
4437
								unset($subFolders[$k]);
4438
							}
4439
							else
4440
							{
4441
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rv is correct as $this->mail_bo->icServer...Mailbox($folder, false) targeting Horde_Imap_Client_Base::subscribeMailbox() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
The assignment to $rv is dead and can be removed.
Loading history...
4442
							}
4443
						}
4444
					}
4445
4446
					$this->mail_bo->reopen('INBOX');
4447
					$success = false;
4448
					try
4449
					{
4450
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
4451
						{
4452
							$this->mail_bo->resetFolderObjectCache($profileID);
4453
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
4454
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
4455
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
4456
							$this->mail_bo->resetFolderObjectCache($profileID);
4457
							$success = true;
4458
						}
4459
					}
4460
					catch (Exception $e)
4461
					{
4462
						$newFolderName=$folderName;
4463
						$msg = $e->getMessage();
4464
					}
4465
					$this->mail_bo->reopen($parentFolder);
4466
					$this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4467
					//error_log(__METHOD__.__LINE__.array2string($fS));
4468
					if ($hasChildren)
4469
					{
4470
						$subFolders = $this->mail_bo->getMailBoxesRecursive($parentFolder, $delimiter, $prefix);
4471
						foreach ($subFolders as $k => $folder)
4472
						{
4473
							// we do not monitor failure or success on subfolders
4474
							if ($folder == $folderName)
4475
							{
4476
								unset($subFolders[$k]);
4477
							}
4478
							else
4479
							{
4480
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rv is correct as $this->mail_bo->icServer...eMailbox($folder, true) targeting Horde_Imap_Client_Base::subscribeMailbox() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
4481
							}
4482
						}
4483
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4484
					}
4485
				}
4486
			}
4487
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4488
			{
4489
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
0 ignored issues
show
Bug Best Practice introduced by
The property sessionData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
4490
				$this->mail_bo->saveSessionData();
4491
			}
4492
			//error_log(__METHOD__.__LINE__.array2string($oA));
4493
			$response = Api\Json\Response::get();
4494
			if ($success)
4495
			{
4496
				Api\Translation::add_app('mail');
4497
4498
				$oldFolderInfo = $this->mail_bo->getFolderStatus($oldParentFolder,false,false,false);
4499
				$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4500
				$refreshData = array(
4501
					$profileID.self::$delimiter.$oldParentFolder=>$oldFolderInfo['shortDisplayName'],
4502
					$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4503
				// if we move the folder within the same parent-branch of the tree, there is no need no refresh the upper part
4504
				if (strlen($parentFolder)>strlen($oldParentFolder) && strpos($parentFolder,$oldParentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$parentFolder]);
4505
				if (count($refreshData)>1 && strlen($oldParentFolder)>strlen($parentFolder) && strpos($oldParentFolder,$parentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$oldParentFolder]);
4506
4507
				// Send full info back in the response
4508
				foreach($refreshData as $folder => &$name)
4509
				{
4510
					$name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true);
4511
				}
4512
				$response->call('app.mail.mail_reloadNode',$refreshData);
4513
4514
			}
4515
			else
4516
			{
4517
				$response->call('egw.refresh',lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $folderName. ( Ignorable by Annotation )

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

4517
				$response->call('egw.refresh',/** @scrutinizer ignore-call */ lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
4518
			}
4519
		}
4520
	}
4521
4522
	/**
4523
	 * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4524
	 * @param string $_folderName folder to delete
4525
	 * @param boolean $_return = false wheter return the success value (true) or send response to client (false)
4526
	 * @return nothing
4527
	 */
4528
	function ajax_deleteFolder($_folderName, $_return = false)
4529
	{
4530
		//error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName));
4531
		$success = false;
4532
		if ($_folderName)
4533
		{
4534
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4535
			$oA = array();
4536
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4537
			if (is_numeric($profileID) && $profileID != $this->mail_bo->profileID) $this->changeProfile ($profileID);
4538
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4539
			$hasChildren = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $hasChildren is dead and can be removed.
Loading history...
4540
			if (is_numeric($profileID))
4541
			{
4542
				$pA = explode($del,$folderName);
4543
				array_pop($pA);
4544
				if (strtoupper($folderName)!= 'INBOX')
4545
				{
4546
					//error_log(__METHOD__.__LINE__."$folderName,  implode($del,$pA), $_newName");
4547
					$oA = array();
4548
					$subFolders = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $subFolders is dead and can be removed.
Loading history...
4549
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4550
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4551
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4552
					{
4553
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4554
						$ftD = array();
4555
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4556
						$nameSpace = $this->mail_bo->_getNameSpaces();
4557
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4558
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4559
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4560
						//error_log(__METHOD__.__LINE__.'->'."$folderName, $delimiter, $prefix");
4561
						foreach ($subFolders as $k => $f)
4562
						{
4563
							$ftD[substr_count($f,$delimiter)][]=$f;
4564
						}
4565
						krsort($ftD,SORT_NUMERIC);//sort per level
4566
						//we iterate per level of depth of the subtree, deepest nesting is to be deleted first, and then up the tree
4567
						foreach($ftD as $k => $lc)//collection per level
4568
						{
4569
							foreach($lc as $f)//folders contained in that level
4570
							{
4571
								try
4572
								{
4573
									//error_log(__METHOD__.__LINE__.array2string($f).'<->'.$folderName);
4574
									$this->mail_bo->deleteFolder($f);
4575
									$success = true;
4576
									if ($f==$folderName) $oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4577
								}
4578
								catch (Exception $e)
4579
								{
4580
									$msg .= ($msg?' ':'').lang("Failed to delete %1. Server responded:",$f).$e->getMessage();
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $f. ( Ignorable by Annotation )

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

4580
									$msg .= ($msg?' ':'')./** @scrutinizer ignore-call */ lang("Failed to delete %1. Server responded:",$f).$e->getMessage();

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Comprehensibility Best Practice introduced by
The variable $msg seems to be never defined.
Loading history...
4581
									$success = false;
4582
								}
4583
							}
4584
						}
4585
					}
4586
					else
4587
					{
4588
						try
4589
						{
4590
							$this->mail_bo->deleteFolder($folderName);
4591
							$success = true;
4592
							$oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4593
						}
4594
						catch (Exception $e)
4595
						{
4596
							$msg = $e->getMessage();
4597
							$success = false;
4598
						}
4599
					}
4600
				}
4601
				else
4602
				{
4603
					$msg = lang("refused to delete folder INBOX");
4604
				}
4605
			}
4606
			if ($_return) return $success;
4607
			$response = Api\Json\Response::get();
4608
			if ($success)
4609
			{
4610
				//error_log(__METHOD__.__LINE__.array2string($oA));
4611
				$response->call('app.mail.mail_removeLeaf',$oA);
4612
			}
4613
			else
4614
			{
4615
				$response->call('egw.refresh',lang('failed to delete %1 ! Reason: %2',$oldFolderInfo['shortDisplayName'],$msg),'mail');
4616
			}
4617
		}
4618
	}
4619
4620
	/**
4621
	 * empty changeProfile - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4622
	 *
4623
	 * Made static to NOT call __construct, as it would connect to old server, before going to new one
4624
	 *
4625
	 * @param int $icServerID New profile / server ID
4626
	 * @param bool $getFolders The client needs the folders for the profile
4627
	 * @return nothing
4628
	 */
4629
	public static function ajax_changeProfile($icServerID, $getFolders = true, $exec_id=null)
0 ignored issues
show
Unused Code introduced by
The parameter $getFolders is not used and could be removed. ( Ignorable by Annotation )

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

4629
	public static function ajax_changeProfile($icServerID, /** @scrutinizer ignore-unused */ $getFolders = true, $exec_id=null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
4630
	{
4631
		$response = Api\Json\Response::get();
4632
4633
		$previous_id = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
4634
4635
		if ($icServerID && $icServerID != $previous_id)
4636
		{
4637
			$mail_ui = new mail_ui(false);	// do NOT run constructor, as we call changeProfile anyway
4638
			try
4639
			{
4640
				$mail_ui->changeProfile($icServerID);
4641
				// if we have an eTemplate exec_id, also send changed actions
4642
				if ($exec_id && ($actions = $mail_ui->get_actions()))
4643
				{
4644
					$response->generic('assign', array(
4645
						'etemplate_exec_id' => $exec_id,
4646
						'id' => 'nm',
4647
						'key' => 'actions',
4648
						'value' => $actions,
4649
					));
4650
				}
4651
			}
4652
			catch (Exception $e) {
4653
				self::callWizard($e->getMessage(),true, 'error');
4654
			}
4655
		}
4656
		else
4657
		{
4658
			$mail_ui = new mail_ui(true);	// run constructor
0 ignored issues
show
Unused Code introduced by
The assignment to $mail_ui is dead and can be removed.
Loading history...
4659
		}
4660
	}
4661
4662
	/**
4663
	 * ajax_refreshVacationNotice - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4664
	 *	Note: only the activeProfile VacationNotice is refreshed
4665
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4666
	 *						if other than active profile; nothing is done!
4667
	 * @return nothing
4668
	 */
4669
	public static function ajax_refreshVacationNotice($icServerID=null)
4670
	{
4671
		//Get vacation from cache if it's available
4672
		$cachedVacations = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid']);
4673
		$vacation = $cachedVacations[$icServerID];
4674
4675
		if (!$vacation)
4676
		{
4677
			try
4678
			{
4679
				// Create mail app object
4680
				$mail = new mail_ui();
4681
4682
				if (empty($icServerID)) $icServerID = $mail->Mail->profileID;
0 ignored issues
show
Bug Best Practice introduced by
The property Mail does not exist on mail_ui. Did you maybe forget to declare it?
Loading history...
4683
				if ($icServerID != $mail->Mail->profileID) return;
4684
4685
				$vacation = $mail->gatherVacation($cachedVacations);
4686
			} catch (Exception $e) {
4687
				$vacation=false;
4688
				error_log(__METHOD__.__LINE__." ".$e->getMessage());
4689
				unset($e);
4690
			}
4691
		}
4692
4693
		if($vacation) {
4694
			if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date'))
4695
			{
4696
				$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
4697
				$refreshData['vacationnotice'] = lang('Vacation notice is active');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$refreshData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $refreshData = array(); before regardless.
Loading history...
4698
				$refreshData['vacationrange'] = ($vacation['status']=='by_date'? Api\DateTime::server2user($vacation['start_date'],$dtfrmt,true).($vacation['end_date']>$vacation['start_date']?'->'.Api\DateTime::server2user($vacation['end_date']+ 24*3600-1,$dtfrmt,true):''):'');
0 ignored issues
show
Unused Code introduced by
The call to EGroupware\Api\DateTime::server2user() has too many arguments starting with true. ( Ignorable by Annotation )

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

4698
				$refreshData['vacationrange'] = ($vacation['status']=='by_date'? Api\DateTime::/** @scrutinizer ignore-call */ server2user($vacation['start_date'],$dtfrmt,true).($vacation['end_date']>$vacation['start_date']?'->'.Api\DateTime::server2user($vacation['end_date']+ 24*3600-1,$dtfrmt,true):''):'');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
4699
				if ($vacation['status'] == 'by_date' && $vacation['end_date']+ 24*3600 < time())$refreshData = '';
4700
			}
4701
		}
4702
		if ($vacation==false)
4703
		{
4704
			$refreshData['vacationnotice'] =  '';
4705
			$refreshData['vacationrange'] =  '';
4706
		}
4707
		$response = Api\Json\Response::get();
4708
		$response->call('app.mail.mail_refreshVacationNotice',$refreshData);
4709
	}
4710
4711
	/**
4712
	 * ajax_refreshFilters - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4713
	 *	Note: only the activeProfile Filters are refreshed
4714
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4715
	 *						if other than active profile; nothing is done!
4716
	 * @return nothing
4717
	 */
4718
	function ajax_refreshFilters($icServerID=null)
4719
	{
4720
		//error_log(__METHOD__.__LINE__.array2string($icServerId));
4721
		if (empty($icServerID)) $icServerID = $this->mail_bo->profileID;
0 ignored issues
show
Unused Code introduced by
The assignment to $icServerID is dead and can be removed.
Loading history...
4722
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4723
		{
4724
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4725
			if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4726
		}
4727
		if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
4728
		{
4729
			unset($this->searchTypes['quick']);
4730
			unset($this->searchTypes['quickwithcc']);
4731
		}
4732
		if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
4733
		{
4734
			$this->statusTypes = array_merge($this->statusTypes,array(
4735
				'keyword1'	=> 'important',//lang('important'),
4736
				'keyword2'	=> 'job',	//lang('job'),
4737
				'keyword3'	=> 'personal',//lang('personal'),
4738
				'keyword4'	=> 'to do',	//lang('to do'),
4739
				'keyword5'	=> 'later',	//lang('later'),
4740
			));
4741
		}
4742
		else
4743
		{
4744
			$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
4745
			foreach($keywords as &$k)
4746
			{
4747
				if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
4748
			}
4749
		}
4750
4751
		$response = Api\Json\Response::get();
4752
		$response->call('app.mail.mail_refreshCatIdOptions',$this->searchTypes);
4753
		$response->call('app.mail.mail_refreshFilterOptions',$this->statusTypes);
4754
		$response->call('app.mail.mail_refreshFilter2Options',array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list')));
4755
4756
	}
4757
4758
	/**
4759
	 * This function asks quota from IMAP server and makes the
4760
	 * result as JSON response to send it to mail_sendQuotaDisplay
4761
	 * function in client side.
4762
	 *
4763
	 * @param string $icServerID = null
4764
	 *
4765
	 */
4766
	function ajax_refreshQuotaDisplay($icServerID=null)
4767
	{
4768
		Api\Translation::add_app('mail');
4769
		if (is_null($icServerID)) $icServerID = $this->mail_bo->profileID;
4770
		$rememberServerID = $this->mail_bo->profileID;
4771
		try
4772
		{
4773
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4774
			{
4775
				$this->changeProfile($icServerID);
4776
			}
4777
			$quota = $this->mail_bo->getQuotaRoot();
4778
		} catch (Exception $e) {
4779
			$quota['limit'] = 'NOT SET';
4780
			error_log(__METHOD__.__LINE__." ".$e->getMessage());
4781
			unset($e);
4782
		}
4783
4784
		if($quota !== false && $quota['limit'] != 'NOT SET') {
4785
			$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
4786
			$quotaMin = ceil($quotainfo['freespace']/pow(1024, 2));
4787
			$quota_limit_warning = isset(mail::$mailConfig['quota_limit_warning']) ? mail::$mailConfig['quota_limit_warning'] : 30;
4788
			$content = array (
4789
				'quota'				=> $quotainfo['text'],
4790
				'quotainpercent'	=> (string)$quotainfo['percent'],
4791
				'quotaclass'		=> $quotainfo['class'],
4792
				'quotanotsupported'	=> "",
4793
				'profileid'			=> $icServerID,
4794
				'quotawarning'		=> $quotaMin <  $quota_limit_warning ? true : false,
4795
				'quotafreespace'	=> Mail::show_readable_size($quotainfo['freespace'])
4796
			);
4797
		}
4798
		else
4799
		{
4800
			$content = array (
4801
				'quota'				=> lang("Quota not provided by server"),
4802
				'quotaclass'		=> "mail_DisplayNone",
4803
				'quotanotsupported'	=> "mail_DisplayNone"
4804
			);
4805
		}
4806
		if ($rememberServerID != $this->mail_bo->profileID)
4807
		{
4808
			try
4809
			{
4810
				$this->changeProfile($rememberServerID);
4811
			} catch (Exception $e) {
4812
				unset($e);
4813
			}
4814
		}
4815
		$response = Api\Json\Response::get();
4816
		$response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content));
4817
	}
4818
4819
	/**
4820
	 * Empty spam/junk folder
4821
	 *
4822
	 * @param string $icServerID id of the server to empty its junkFolder
4823
	 * @param string $selectedFolder seleted(active) folder by nm filter
4824
	 * @return nothing
4825
	 */
4826
	function ajax_emptySpam($icServerID, $selectedFolder)
4827
	{
4828
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4829
		Api\Translation::add_app('mail');
4830
		$response = Api\Json\Response::get();
4831
		$rememberServerID = $this->mail_bo->profileID;
4832
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4833
		{
4834
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4835
			$this->changeProfile($icServerID);
4836
		}
4837
		$junkFolder = $this->mail_bo->getJunkFolder();
4838
		if(!empty($junkFolder)) {
4839
			if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4840
			{
4841
				// Lock the tree if the active folder is junk folder
4842
				$response->call('app.mail.lock_tree');
4843
			}
4844
			$this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately');
4845
4846
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4847
			$fShortName =  array_pop(explode($heirarchyDelimeter, $junkFolder));
0 ignored issues
show
Bug introduced by
explode($heirarchyDelimeter, $junkFolder) cannot be passed to array_pop() as the parameter $array expects a reference. ( Ignorable by Annotation )

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

4847
			$fShortName =  array_pop(/** @scrutinizer ignore-type */ explode($heirarchyDelimeter, $junkFolder));
Loading history...
4848
			$fStatus = array(
4849
				$icServerID.self::$delimiter.$junkFolder => lang($fShortName)
4850
			);
4851
			//Call to reset folder status counter, after junkFolder triggered not from Junk folder
4852
			//-as we don't have junk folder specific information available on client-side we need to deal with it on server
4853
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4854
		}
4855
		if ($rememberServerID != $this->mail_bo->profileID)
4856
		{
4857
			$oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false);
4858
			$response->call('egw.message',lang('empty junk'));
4859
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName']));
4860
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4861
			$this->changeProfile($rememberServerID);
4862
		}
4863
		else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4864
		{
4865
			$response->call('egw.refresh',lang('empty junk'),'mail');
4866
		}
4867
	}
4868
4869
	/**
4870
	 * Empty trash folder
4871
	 *
4872
	 * @param string $icServerID id of the server to empty its trashFolder
4873
	 * @param string $selectedFolder seleted(active) folder by nm filter
4874
	 * @return nothing
4875
	 */
4876
	function ajax_emptyTrash($icServerID, $selectedFolder)
4877
	{
4878
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4879
		Api\Translation::add_app('mail');
4880
		$response = Api\Json\Response::get();
4881
		$rememberServerID = $this->mail_bo->profileID;
4882
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4883
		{
4884
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4885
			$this->changeProfile($icServerID);
4886
		}
4887
		$trashFolder = $this->mail_bo->getTrashFolder();
4888
		if(!empty($trashFolder)) {
4889
			if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4890
			{
4891
				// Lock the tree if the active folder is Trash folder
4892
				$response->call('app.mail.lock_tree');
4893
			}
4894
			$this->mail_bo->compressFolder($trashFolder);
4895
4896
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4897
			$fShortName =  array_pop(explode($heirarchyDelimeter, $trashFolder));
0 ignored issues
show
Bug introduced by
explode($heirarchyDelimeter, $trashFolder) cannot be passed to array_pop() as the parameter $array expects a reference. ( Ignorable by Annotation )

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

4897
			$fShortName =  array_pop(/** @scrutinizer ignore-type */ explode($heirarchyDelimeter, $trashFolder));
Loading history...
4898
			$fStatus = array(
4899
				$icServerID.self::$delimiter.$trashFolder => lang($fShortName)
4900
			);
4901
			//Call to reset folder status counter, after emptyTrash triggered not from Trash folder
4902
			//-as we don't have trash folder specific information available on client-side we need to deal with it on server
4903
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4904
		}
4905
		if ($rememberServerID != $this->mail_bo->profileID)
4906
		{
4907
			$oldFolderInfo = $this->mail_bo->getFolderStatus($trashFolder,false,false,false);
4908
			$response->call('egw.message',lang('empty trash'));
4909
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$trashFolder=>$oldFolderInfo['shortDisplayName']));
4910
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4911
			$this->changeProfile($rememberServerID);
4912
		}
4913
		else if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4914
		{
4915
			$response->call('egw.refresh',lang('empty trash'),'mail');
4916
		}
4917
	}
4918
4919
	/**
4920
	 * compress folder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4921
	 * fetches the current folder from session and compresses it
4922
	 * @param string $_folderName id of the folder to compress
4923
	 * @return nothing
4924
	 */
4925
	function ajax_compressFolder($_folderName)
4926
	{
4927
		//error_log(__METHOD__.__LINE__.' '.$_folderName);
4928
		Api\Translation::add_app('mail');
4929
4930
		$this->mail_bo->restoreSessionData();
4931
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4932
		list($icServerID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4933
4934
		if (empty($folderName)) $folderName = $this->mail_bo->sessionData['mailbox'];
4935
		if ($this->mail_bo->folderExists($folderName))
4936
		{
4937
			$rememberServerID = $this->mail_bo->profileID;
4938
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4939
			{
4940
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4941
				$this->changeProfile($icServerID);
4942
			}
4943
			if(!empty($_folderName)) {
4944
				$this->mail_bo->compressFolder($folderName);
4945
			}
4946
			if ($rememberServerID != $this->mail_bo->profileID)
4947
			{
4948
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4949
				$this->changeProfile($rememberServerID);
4950
			}
4951
			$response = Api\Json\Response::get();
4952
			$response->call('egw.refresh',lang('compress folder').': '.$folderName,'mail');
4953
		}
4954
	}
4955
4956
	/**
4957
	 * sendMDN, ...
4958
	 *
4959
	 * @param array _messageList list of UID's
0 ignored issues
show
Bug introduced by
The type _messageList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4960
	 *
4961
	 * @return nothing
4962
	 */
4963
	function ajax_sendMDN($_messageList)
4964
	{
4965
		if(Mail::$debug) error_log(__METHOD__."->".array2string($_messageList));
4966
		$uidA = self::splitRowID($_messageList['msg'][0]);
4967
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4968
		$this->mail_bo->sendMDN($uidA['msgUID'],$folder);
4969
	}
4970
4971
	/**
4972
	 * flag messages as read, unread, flagged, ...
4973
	 *
4974
	 * @param string _flag name of the flag
0 ignored issues
show
Bug introduced by
The type _flag was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4975
	 * @param array _messageList list of UID's
4976
	 * @param bool _sendJsonResponse tell fuction to send the JsonResponse
0 ignored issues
show
Bug introduced by
The type _sendJsonResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4977
	 *
4978
	 * @return xajax response
4979
	 */
4980
	function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
4981
	{
4982
		if(Mail::$debug) error_log(__METHOD__."->".$_flag.':'.array2string($_messageList));
4983
		Api\Translation::add_app('mail');
4984
		$alreadyFlagged=false;
4985
		$flag2check='';
4986
		$filter2toggle = $query = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $filter2toggle is dead and can be removed.
Loading history...
4987
		if ($_messageList=='all' || !empty($_messageList['msg']))
4988
		{
4989
			if (isset($_messageList['all']) && $_messageList['all'])
4990
			{
4991
				// we have both messageIds AND allFlag folder information
4992
				$uidA = self::splitRowID($_messageList['msg'][0]);
4993
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4994
				if(!$folder && !$uidA['msg'] && $uidA['accountID'])
4995
				{
4996
					$folder = $uidA['accountID'];
4997
				}
4998
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4999
				{
5000
					$query = $_messageList['activeFilters'];
5001
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
5002
					{
5003
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
5004
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
5005
						{
5006
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
5007
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
5008
						}
5009
						//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
5010
						$cutoffdate = $cutoffdate2 = null;
5011
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
5012
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
5013
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
5014
						$filter = array(
5015
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mail_ui seems to be never defined.
Loading history...
5016
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
5017
							'string' => $query['search'],
5018
							'status' => 'any',//this is a status change. status will be manipulated later on
5019
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
5020
						);
5021
						if ($query['enddate']||$query['startdate']) {
5022
							$filter['range'] = "BETWEEN";
5023
							if ($cutoffdate) {
5024
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
5025
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
5026
							}
5027
							if ($cutoffdate2) {
5028
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
5029
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
5030
							}
5031
						}
5032
						$filter2toggle = $filter;
5033
					}
5034
					else
5035
					{
5036
						$filter = $filter2toggle = array();
5037
					}
5038
					// flags read,flagged,label1,label2,label3,label4,label5 can be toggled: handle this when all mails in a folder
5039
					// should be affected serverside. here.
5040
					$messageList = $messageListForToggle = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $messageListForToggle is dead and can be removed.
Loading history...
5041
					$flag2check = ($_flag=='read'?'seen':$_flag);
5042
					if (in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
5043
						!($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))
5044
					{
5045
						$filter2toggle['status'] = array('un'.$_flag);
5046
						if ($query['filter'] && $query['filter']!='any')
5047
						{
5048
							$filter2toggle['status'][] = $query['filter'];
5049
						}
5050
						$_sRt = $this->mail_bo->getSortedList(
5051
							$folder,
5052
							$sort = 0,
5053
							$reverse = 1,
5054
							$filter2toggle,
5055
							$rByUid = true,
0 ignored issues
show
Bug introduced by
$rByUid = true cannot be passed to EGroupware\Api\Mail::getSortedList() as the parameter $resultByUid expects a reference. ( Ignorable by Annotation )

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

5055
							/** @scrutinizer ignore-type */ $rByUid = true,
Loading history...
5056
							false
5057
						);
5058
						$messageListForToggle = $_sRt['match']->ids;
5059
						$filter['status'] = array($_flag);
5060
						if ($query['filter'] && $query['filter'] !='any')
5061
						{
5062
							$filter['status'][] = $query['filter'];
5063
						}
5064
						$_sR = $this->mail_bo->getSortedList(
5065
							$folder,
5066
							$sort = 0,
5067
							$reverse = 1,
5068
							$filter,
5069
							$rByUid = true,
5070
							false
5071
						);
5072
						$messageList = $_sR['match']->ids;
5073
						if (count($messageListForToggle)>0)
5074
						{
5075
							$flag2set = (strtolower($_flag));
5076
							if(Mail::$debug) error_log(__METHOD__.__LINE__." toggle un$_flag -> $flag2set ".array2string($filter2toggle).array2string($messageListForToggle));
5077
							$this->mail_bo->flagMessages($flag2set, $messageListForToggle,$folder);
5078
						}
5079
						if (count($messageList)>0)
5080
						{
5081
							$flag2set = 'un'.$_flag;
5082
							if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag -> $flag2set ".array2string($filter).array2string($messageList));
5083
							$this->mail_bo->flagMessages($flag2set, $messageList,$folder);
5084
						}
5085
						$alreadyFlagged=true;
5086
					}
5087
					elseif (!empty($filter) &&
5088
						(!in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) ||
5089
						(in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
5090
						($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))))
5091
					{
5092
						if ($query['filter'] && $query['filter'] !='any')
5093
						{
5094
							$filter['status'] = $query['filter'];
5095
							// since we toggle and we toggle by the filtered flag we must must change _flag
5096
							$_flag = ($query['filter']=='unseen' && $_flag=='read' ? 'read' : ($query['filter']=='seen'&& $_flag=='read'?'unread':($_flag==$query['filter']?'un'.$_flag:$_flag)));
5097
						}
5098
						if(Mail::$debug) error_log(__METHOD__.__LINE__." flag all with $_flag on filter used:".array2string($filter));
5099
						$rByUid = true;
5100
						$reverse = 1;
5101
						$_sR = $this->mail_bo->getSortedList(
5102
							$folder,
5103
							$sort=0,
5104
							$reverse,
5105
							$filter,
5106
							$rByUid,
5107
							false
5108
						);
5109
						$messageList = $_sR['match']->ids;
5110
						unset($_messageList['all']);
5111
						$_messageList['msg'] = array();
5112
					}
5113
					else
5114
					{
5115
						if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag all ".array2string($filter));
5116
						$alreadyFlagged=true;
5117
						$uidA = self::splitRowID($_messageList['msg'][0]);
5118
						$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5119
						$this->mail_bo->flagMessages($_flag, 'all', $folder);
5120
					}
5121
				}
5122
			}
5123
			else
5124
			{
5125
				$uidA = self::splitRowID($_messageList['msg'][0]);
5126
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5127
			}
5128
			if (!$alreadyFlagged)
5129
			{
5130
				foreach($_messageList['msg'] as $rowID)
5131
				{
5132
					$hA = self::splitRowID($rowID);
5133
					$messageList[] = $hA['msgUID'];
5134
				}
5135
				if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag in $folder:".array2string(((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList)));
5136
				$this->mail_bo->flagMessages($_flag, ((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList),$folder);
5137
			}
5138
		}
5139
		else
5140
		{
5141
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5142
		}
5143
5144
		if ($_sendJsonResponse)
5145
		{
5146
			$flag=array(
5147
				'label1'	=> 'important',//lang('important'),
5148
				'label2'	=> 'job',	//lang('job'),
5149
				'label3'	=> 'personal',//lang('personal'),
5150
				'label4'	=> 'to do',	//lang('to do'),
5151
				'label5'	=> 'later',	//lang('later'),
5152
			);
5153
			$response = Api\Json\Response::get();
5154
			if (isset($_messageList['msg']) && $_messageList['popup'])
5155
			{
5156
				$response->call('egw.refresh',lang('flagged %1 messages as %2 in %3',$_messageList['msg'],lang(($flag[$_flag]?$flag[$_flag]:$_flag)),lang($folder)),'mail', $_messageList['msg'], 'update');
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $_messageList['msg']. ( Ignorable by Annotation )

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

5156
				$response->call('egw.refresh',/** @scrutinizer ignore-call */ lang('flagged %1 messages as %2 in %3',$_messageList['msg'],lang(($flag[$_flag]?$flag[$_flag]:$_flag)),lang($folder)),'mail', $_messageList['msg'], 'update');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
5157
			}
5158
			else if ((isset($_messageList['all']) && $_messageList['all']) || ($query['filter'] && ($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false)))
5159
			{
5160
				$response->call('egw.refresh',lang('flagged %1 messages as %2 in %3',(isset($_messageList['all']) && $_messageList['all']?lang('all'):count($_messageList['msg'])),lang(($flag[$_flag]?$flag[$_flag]:$_flag)),lang($folder)),'mail');
5161
			}
5162
			else
5163
			{
5164
				$response->call('egw.message',lang('flagged %1 messages as %2 in %3',(isset($_messageList['all']) && $_messageList['all']?lang('all'):count($_messageList['msg'])),lang(($flag[$_flag]?$flag[$_flag]:$_flag)),lang($folder)));
5165
			}
5166
		}
5167
	}
5168
5169
	/**
5170
	 * delete messages
5171
	 *
5172
	 * @param array _messageList list of UID's
5173
	 * @param string _forceDeleteMethod - method of deletion to be enforced
0 ignored issues
show
Bug introduced by
The type _forceDeleteMethod was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
5174
	 * @return xajax response
5175
	 */
5176
	function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
5177
	{
5178
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageList,true).' Method:'.$_forceDeleteMethod);
5179
		$error = null;
5180
		$filtered =  false;
0 ignored issues
show
Unused Code introduced by
The assignment to $filtered is dead and can be removed.
Loading history...
5181
		if ($_messageList=='all' || !empty($_messageList['msg']))
5182
		{
5183
			if (isset($_messageList['all']) && $_messageList['all'])
5184
			{
5185
				// we have both messageIds AND allFlag folder information
5186
				$uidA = self::splitRowID($_messageList['msg'][0]);
5187
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5188
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
5189
				{
5190
					$query = $_messageList['activeFilters'];
5191
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
5192
					{
5193
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
5194
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
5195
						{
5196
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
5197
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
5198
						}
5199
						$filtered =  true;
5200
						$cutoffdate = $cutoffdate2 = null;
5201
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
5202
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
5203
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
5204
						$filter = array(
5205
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mail_ui seems to be never defined.
Loading history...
5206
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
5207
							'string' => $query['search'],
5208
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
5209
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
5210
						);
5211
						if ($query['enddate']||$query['startdate']) {
5212
							$filter['range'] = "BETWEEN";
5213
							if ($cutoffdate) {
5214
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
5215
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
5216
							}
5217
							if ($cutoffdate2) {
5218
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
5219
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
5220
							}
5221
						}
5222
					}
5223
					else
5224
					{
5225
						$filter = array();
5226
					}
5227
					//error_log(__METHOD__.__LINE__."->".print_r($filter,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
5228
					$reverse = 1;
5229
					$rByUid = true;
5230
					$_sR = $this->mail_bo->getSortedList(
5231
						$folder,
5232
						$sort=0,
5233
						$reverse,
5234
						$filter,
5235
						$rByUid,
5236
						false
5237
					);
5238
					$messageList = $_sR['match']->ids;
5239
				}
5240
				else
5241
				{
5242
					$messageList='all';
5243
				}
5244
				try
5245
				{
5246
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
5247
					$this->mail_bo->deleteMessages(($messageList=='all' ? 'all':$messageList),$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
5248
				}
5249
				catch (Api\Exception $e)
5250
				{
5251
					$error = str_replace('"',"'",$e->getMessage());
5252
				}
5253
			}
5254
			else
5255
			{
5256
				$uidA = self::splitRowID($_messageList['msg'][0]);
5257
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5258
				foreach($_messageList['msg'] as $rowID)
5259
				{
5260
					$hA = self::splitRowID($rowID);
5261
					$messageList[] = $hA['msgUID'];
5262
				}
5263
				try
5264
				{
5265
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
5266
					$this->mail_bo->deleteMessages($messageList,$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $messageList seems to be defined by a foreach iteration on line 5258. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
5267
				}
5268
				catch (Api\Exception $e)
5269
				{
5270
					$error = str_replace('"',"'",$e->getMessage());
5271
				}
5272
			}
5273
			$response = Api\Json\Response::get();
5274
			if (empty($error))
5275
			{
5276
				$response->call('app.mail.mail_deleteMessagesShowResult',array('egw_message'=>'', 'msg'=>$_messageList['msg']));
5277
			}
5278
			else
5279
			{
5280
				$error = str_replace('\n',"\n",lang('mailserver reported:\n%1 \ndo you want to proceed by deleting the selected messages immediately (click ok)?\nif not, please try to empty your trashfolder before continuing. (click cancel)',$error));
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $error. ( Ignorable by Annotation )

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

5280
				$error = str_replace('\n',"\n",/** @scrutinizer ignore-call */ lang('mailserver reported:\n%1 \ndo you want to proceed by deleting the selected messages immediately (click ok)?\nif not, please try to empty your trashfolder before continuing. (click cancel)',$error));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
5281
				$response->call('app.mail.mail_retryForcedDelete',array('response'=>$error,'messageList'=>$_messageList));
5282
			}
5283
		}
5284
		else
5285
		{
5286
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5287
		}
5288
	}
5289
5290
	/**
5291
	 * copy messages
5292
	 *
5293
	 * @param array _folderName target folder
5294
	 * @param array _messageList list of UID's
5295
	 * @param string _copyOrMove method to use copy or move allowed
5296
	 * @param string _move2ArchiveMarker marker to indicate if a move 2 archive was triggered
0 ignored issues
show
Bug introduced by
The type _move2ArchiveMarker was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
5297
	 * @param boolean _return if true the function will return the result instead of
0 ignored issues
show
Bug introduced by
The type _return was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
5298
	 * responding to client
5299
	 *
5300
	 * @return xajax response
5301
	 */
5302
	function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_move2ArchiveMarker='_', $_return = false)
5303
	{
5304
		if(Mail::$debug) error_log(__METHOD__."->".$_folderName.':'.print_r($_messageList,true).' Method:'.$_copyOrMove.' ArchiveMarker:'.$_move2ArchiveMarker);
5305
		Api\Translation::add_app('mail');
5306
		$folderName = $this->mail_bo->decodeEntityFolderName($_folderName);
5307
		// only copy or move are supported as method
5308
		if (!($_copyOrMove=='copy' || $_copyOrMove=='move')) $_copyOrMove='copy';
5309
		list($targetProfileID,$targetFolder) = explode(self::$delimiter,$folderName,2);
5310
		// check if move2archive was called with the correct archiveFolder
5311
		$archiveFolder = $this->mail_bo->getArchiveFolder();
5312
		if ($_move2ArchiveMarker=='2' && $targetFolder != $archiveFolder)
5313
		{
5314
			error_log(__METHOD__.__LINE__."#Move to Archive called with:"."$targetProfileID,$targetFolder");
5315
			$targetProfileID = $this->mail_bo->profileID;
5316
			$targetFolder = $archiveFolder;
5317
			error_log(__METHOD__.__LINE__."#Fixed ArchiveFolder:"."$targetProfileID,$targetFolder");
5318
		}
5319
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
5320
		$changeFolderActions = false;
5321
		//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder");
5322
		//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5323
		if (!isset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]))
5324
		{
5325
			//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]));
5326
			if ($lastFoldersUsedForMoveCont[$targetProfileID] && count($lastFoldersUsedForMoveCont[$targetProfileID])>3)
5327
			{
5328
				$keys = array_keys($lastFoldersUsedForMoveCont[$targetProfileID]);
5329
				foreach( $keys as &$f)
5330
				{
5331
					if (count($lastFoldersUsedForMoveCont[$targetProfileID])>9) unset($lastFoldersUsedForMoveCont[$targetProfileID][$f]);
5332
					else break;
5333
				}
5334
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID]));
5335
			}
5336
			//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder = $_folderName");
5337
			$lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]=$folderName;
5338
			$changeFolderActions = true;
5339
		}
5340
		$filtered = false;
5341
		if ($_messageList=='all' || !empty($_messageList['msg']))
5342
		{
5343
			$error=false;
5344
			if (isset($_messageList['all']) && $_messageList['all'])
5345
			{
5346
				// we have both messageIds AND allFlag folder information
5347
				$uidA = self::splitRowID($_messageList['msg'][0]);
5348
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5349
				$sourceProfileID = $uidA['profileID'];
5350
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
5351
				{
5352
					$query = $_messageList['activeFilters'];
5353
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
5354
					{
5355
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
5356
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
5357
						{
5358
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
5359
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
5360
						}
5361
						$filtered = true;
5362
						$cutoffdate = $cutoffdate2 = null;
5363
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
5364
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
5365
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
5366
						$filter = array(
5367
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mail_ui seems to be never defined.
Loading history...
5368
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
5369
							'string' => $query['search'],
5370
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
5371
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
5372
						);
5373
						if ($query['enddate']||$query['startdate']) {
5374
							$filter['range'] = "BETWEEN";
5375
							if ($cutoffdate) {
5376
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
5377
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
5378
							}
5379
							if ($cutoffdate2) {
5380
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
5381
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
5382
							}
5383
						}
5384
					}
5385
					else
5386
					{
5387
						$filter = array();
5388
					}
5389
					$reverse = 1;
5390
					$rByUid = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $rByUid is dead and can be removed.
Loading history...
5391
					$_sR = $this->mail_bo->getSortedList(
5392
						$folder,
5393
						$sort=0,
5394
						$reverse,
5395
						$filter,
5396
						$rByUid=true,
0 ignored issues
show
Bug introduced by
$rByUid = true cannot be passed to EGroupware\Api\Mail::getSortedList() as the parameter $resultByUid expects a reference. ( Ignorable by Annotation )

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

5396
						/** @scrutinizer ignore-type */ $rByUid=true,
Loading history...
5397
						false
5398
					);
5399
					$messageList = $_sR['match']->ids;
5400
					foreach($messageList as $uID)
5401
					{
5402
						//error_log(__METHOD__.__LINE__.$uID);
5403
						if ($_copyOrMove=='move')
5404
						{
5405
							$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false);
5406
						}
5407
					}
5408
				}
5409
				else
5410
				{
5411
					$messageList='all';
5412
				}
5413
				try
5414
				{
5415
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
5416
					$this->mail_bo->moveMessages($targetFolder,$messageList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
5417
				}
5418
				catch (Api\Exception $e)
5419
				{
5420
					$error = str_replace('"',"'",$e->getMessage());
5421
				}
5422
			}
5423
			else
5424
			{
5425
				$messageList = array();
5426
				while(count($_messageList['msg']) > 0)
5427
				{
5428
					$uidA = self::splitRowID($_messageList['msg'][0]);
5429
					$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5430
					$sourceProfileID = $uidA['profileID'];
5431
					$moveList = array();
5432
					foreach($_messageList['msg'] as $rowID)
5433
					{
5434
						$hA = self::splitRowID($rowID);
5435
5436
						// If folder changes, stop and move what we've got
5437
						if($hA['folder'] != $folder) break;
5438
5439
						array_shift($_messageList['msg']);
5440
						$messageList[] = $hA['msgUID'];
5441
						$moveList[] = $hA['msgUID'];
5442
						if ($_copyOrMove=='move')
5443
						{
5444
							$helpvar = explode(self::$delimiter,$rowID);
5445
							array_shift($helpvar);
5446
							$messageListForRefresh[]= implode(self::$delimiter,$helpvar);
5447
						}
5448
					}
5449
					try
5450
					{
5451
						//error_log(__METHOD__.__LINE__."->".print_r($moveList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
5452
						$this->mail_bo->moveMessages($targetFolder,$moveList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
5453
					}
5454
					catch (Api\Exception $e)
5455
					{
5456
						$error = str_replace('"',"'",$e->getMessage());
5457
					}
5458
				}
5459
			}
5460
5461
			$response = Api\Json\Response::get();
5462
			if ($error)
5463
			{
5464
				if ($changeFolderActions == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5465
				{
5466
					unset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]);
5467
					$changeFolderActions = true;
5468
				}
5469
				if ($_return) return $error;
5470
				$response->call('egw.message',$error,"error");
5471
			}
5472
			else
5473
			{
5474
				if ($_copyOrMove=='copy')
5475
				{
5476
					$msg = lang('copied %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),lang($folder),lang($targetFolder));
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $messageList == 'all' ||...) : count($messageList). ( Ignorable by Annotation )

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

5476
					$msg = /** @scrutinizer ignore-call */ lang('copied %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),lang($folder),lang($targetFolder));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
5477
					if ($_return) return $msg;
5478
					$response->call('egw.message',$msg);
5479
				}
5480
				else
5481
				{
5482
					$msg = lang('moved %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),lang($folder),lang($targetFolder));
5483
					if ($_return) return $msg;
5484
					$response->call('egw.refresh',$msg,'mail',$messageListForRefresh,'delete');
5485
				}
5486
			}
5487
			if ($changeFolderActions == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5488
			{
5489
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5490
				Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
5491
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::get_actions());
0 ignored issues
show
Bug Best Practice introduced by
The method mail_ui::get_actions() 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

5491
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::/** @scrutinizer ignore-call */ get_actions());
Loading history...
5492
				$response->call('app.mail.mail_rebuildActionsOnList',$actionsnew);
5493
			}
5494
		}
5495
		else
5496
		{
5497
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5498
		}
5499
	}
5500
5501
	/**
5502
	 * Autoloading function to load branches of tree node
5503
	 * of management folder tree
5504
	 *
5505
	 * @param type $_id
5506
	 */
5507
	function ajax_folderMgmtTree_autoloading ($_id = null)
5508
	{
5509
		$mail_ui = new mail_ui();
5510
		$id = $_id? $_id : $_GET['id'];
5511
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,true,false,false,false));
5512
	}
5513
5514
	/**
5515
	 * Main function to handle folder management dialog
5516
	 *
5517
	 * @param array $content content of dialog
5518
	 */
5519
	function folderManagement (array $content = null)
5520
	{
5521
		$dtmpl = new Etemplate('mail.folder_management');
5522
		$profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
5523
		$sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sel_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sel_options = array(); before regardless.
Loading history...
5524
5525
		if (!is_array($content))
5526
		{
5527
			$content = array ('acc_id' => $profileID);
5528
		}
5529
5530
		$readonlys = array();
5531
		// Preserv
5532
		$preserv = array(
5533
			'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side
5534
		);
5535
		$dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2);
5536
	}
5537
5538
	/**
5539
	 * Function to delete folder for management longTask dialog
5540
	 * it sends successfully deleted folder as response to be
5541
	 * used in long task response handler.
5542
	 *
5543
	 * @param type $_folderName
5544
	 */
5545
	function ajax_folderMgmt_delete ($_folderName)
5546
	{
5547
		if ($_folderName)
0 ignored issues
show
introduced by
$_folderName is of type type, thus it always evaluated to true.
Loading history...
5548
		{
5549
			$success = $this->ajax_deleteFolder($_folderName,true);
5550
			$response = Api\Json\Response::get();
5551
			list(,$folderName) = explode(self::$delimiter, $_folderName);
5552
			if ($success)
0 ignored issues
show
introduced by
$success is of type nothing, thus it always evaluated to true.
Loading history...
5553
			{
5554
				$res = $folderName;
5555
			}
5556
			else
5557
			{
5558
				$res = lang("Failed to delete %1",$folderName);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $folderName. ( Ignorable by Annotation )

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

5558
				$res = /** @scrutinizer ignore-call */ lang("Failed to delete %1",$folderName);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
5559
			}
5560
			$response->data($res);
5561
		}
5562
	}
5563
}
5564