Completed
Push — 16.1 ( 3d52b1...edadaa )
by Hadi
39:13 queued 22:22
created

mail_ui::ajax_spamAction()   C

Complexity

Conditions 10
Paths 48

Size

Total Lines 67
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 43
nc 48
nop 2
dl 0
loc 67
rs 6.1506
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
20
use EGroupware\Api\Mail;
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
		'vfsSaveAttachment' => True,
53
		'vfsSaveMessage' => True,
54
		'loadEmailBody'	=> True,
55
		'importMessage'	=> True,
56
		'importMessageFromVFS2DraftAndDisplay'=>True,
57
		'subscription'	=> True,
58
		'folderManagement' => 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);
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 View Code Duplication
		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 View Code Duplication
			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();
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();
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 View Code Duplication
		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::st...elf::$icServerID, true) of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
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);
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
			list($button) = @each($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();
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;
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';
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();
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') {
479
					$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
480
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = $quotainfo['text'];
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
				// call gatherVacation asynchronously in getRows by initiating a client Server roundtrip
490
				$vacation = false;//$this->gatherVacation();
491
				//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
492
				if($vacation) {
493
					if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date' && $vacation['end_date'] > time()))
494
					{
495
						$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']/*.' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat']!='24'?'h:i a':'H:i')*/;
496
						$content[self::$nm_index]['vacationnotice'] = $sel_options[self::$nm_index]['vacationnotice'] = lang('Vacation notice is active');
497
						$content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['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 DateTime::server2user() has too many arguments starting with 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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
498
					}
499
				}
500
				if ($vacation==false)
501
				{
502
					$content[self::$nm_index]['vacationnotice'] = $sel_options[self::$nm_index]['vacationnotice'] = '';
503
					$content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['vacationrange'] = '';
504
				}
505
				//$zstarttime = microtime (true);
506
				$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']);
507
				//$zendtime = microtime(true) - $zstarttime;
508
				//error_log(__METHOD__.__LINE__. " time used: ".$zendtime);
509
				$content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.(!empty($this->mail_bo->sessionData['mailbox'])?$this->mail_bo->sessionData['mailbox']:'INBOX');
510
				// since we are connected,(and selected the folder) we check for capabilities SUPPORTS_KEYWORDS to eventually add the keyword filters
511 View Code Duplication
				if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
512
				{
513
					$this->statusTypes = array_merge($this->statusTypes,array(
514
						'keyword1'	=> 'important',//lang('important'),
515
						'keyword2'	=> 'job',	//lang('job'),
516
						'keyword3'	=> 'personal',//lang('personal'),
517
						'keyword4'	=> 'to do',	//lang('to do'),
518
						'keyword5'	=> 'later',	//lang('later'),
519
					));
520
				}
521
				else
522
				{
523
					$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
524
					foreach($keywords as &$k)
525
					{
526
						if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
527
					}
528
				}
529
530 View Code Duplication
				if (!isset($content[self::$nm_index]['foldertree'])) $content[self::$nm_index]['foldertree'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
531 View Code Duplication
				if (!isset($content[self::$nm_index]['selectedFolder'])) $content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
532
533
				$content[self::$nm_index]['foldertree'] = $content[self::$nm_index]['selectedFolder'];
534
535
				if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
536
				{
537
					Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
538
					if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
539
				}
540 View Code Duplication
				if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
541
				{
542
					unset($this->searchTypes['quick']);
543
					unset($this->searchTypes['quickwithcc']);
544
				}
545
				$sel_options['cat_id'] = $this->searchTypes;
546
				//error_log(__METHOD__.__LINE__.array2string($sel_options['cat_id']));
547
				//error_log(__METHOD__.__LINE__.array2string($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']));
548
				$content[self::$nm_index]['cat_id'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType'];
549
				$sel_options['filter'] = $this->statusTypes;
550
				$sel_options['filter2'] = array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list'));
551
				$content[self::$nm_index]['filter2'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails'];
552
553
				$etpl = new Etemplate('mail.index');
554
				//apply infolog_filter_change javascript method (hide/show of date filter form) over onchange filter
555
				$content[self::$nm_index]['cat_id_onchange'] = "app.mail.mail_searchtype_change()";
556
				// set the actions on tree
557
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions());
558
559
				// sending preview toolbar actions
560
				if ($content['mailSplitter']) $etpl->setElementAttribute('mailPreview[toolbar]', 'actions', $this->get_toolbar_actions());
561
562
				// We need to send toolbar actions to client-side because view template needs them
563
				if (Api\Header\UserAgent::mobile()) $sel_options['toolbar'] = $this->get_toolbar_actions();
564
565
				//we use the category "filter" option as specifier where we want to search (quick, subject, from, to, etc. ....)
566
				if (empty($content[self::$nm_index]['cat_id']) || empty($content[self::$nm_index]['search']))
567
				{
568
					$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'));
569
				}
570
				$readonlys = $preserv = array();
571
				if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
572
		}
573
		catch (Exception $e)
574
		{
575
			// do not exit here. mail-tree should be build. if we exit here, we never get there.
576
			error_log(__METHOD__.__LINE__.$e->getMessage().($e->details?', '.$e->details:'').' Menuaction:'.$_GET['menuaction'].'.'.function_backtrace());
0 ignored issues
show
Bug introduced by
The property details does not seem to exist in Exception.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
577
			if (isset($this->mail_bo))
578
			{
579
				if (empty($etpl))
580
				{
581
					$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']);
582
					$etpl = new Etemplate('mail.index');
583
				}
584
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions(false));
585
			}
586
			$readonlys = $preserv = array();
587
			if (empty($content)) $content=array();
588
589
			self::callWizard($e->getMessage().($e->details?', '.$e->details:''),(isset($this->mail_bo)?false:true), 'error',false);
590
			//return false;
591
		}
592
		// Check preview pane is enabled, then show splitter - preference used to be '1', now 'hide'
593
		if ($this->mail_bo->mailPreferences['previewPane'] == '1' || $this->mail_bo->mailPreferences['previewPane'] == 'hide')
594
		{
595
			$etpl->setElementAttribute('splitter', 'template', 'mail.index.nosplitter');
596
		}
597
598
		return $etpl->exec('mail.mail_ui.index',$content,$sel_options,$readonlys,$preserv);
599
	}
600
601
	/**
602
	 * Get tree actions / context menu for tree
603
	 *
604
	 * Changes here, may require to log out, as $content[self::$nm_index] get stored in session!
605
	 * @param {boolean} $imap_actions set to false if you want to avoid to talk to the imap-server
606
	 * @return array
607
	 */
608
	function get_tree_actions($imap_actions=true)
609
	{
610
		// Start at 2 so auto-added copy+paste actions show up as second group
611
		// Needed because there's no 'select all' action to push things down
612
		$group=1;
613
		// Set tree actions
614
		$tree_actions = array(
615
			'drop_move_mail' => array(
616
				'type' => 'drop',
617
				'acceptedTypes' => 'mail',
618
				'icon' => 'move',
619
				'caption' => 'Move to',
620
				'onExecute' => 'javaScript:app.mail.mail_move'
621
			),
622
			'drop_copy_mail' => array(
623
				'type' => 'drop',
624
				'acceptedTypes' => 'mail',
625
				'icon' => 'copy',
626
				'caption' => 'Copy to',
627
				'onExecute' => 'javaScript:app.mail.mail_copy'
628
			),
629
			'drop_cancel' => array(
630
				'icon' => 'cancel',
631
				'caption' => 'Cancel',
632
				'acceptedTypes' => 'mail',
633
				'type' => 'drop',
634
			),
635
			'drop_move_folder' => array(
636
				'caption' => 'Move folder',
637
				'hideOnDisabled' => true,
638
				'type' => 'drop',
639
				'acceptedTypes' => 'mailFolder',
640
				'onExecute' => 'javaScript:app.mail.mail_MoveFolder'
641
			),
642
			// Tree does support this one
643
			'add' => array(
644
				'caption' => 'Add Folder',
645
				'onExecute' => 'javaScript:app.mail.mail_AddFolder',
646
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
647
				'group'		=> $group,
648
			),
649
			'edit' => array(
650
				'caption' => 'Rename Folder',
651
				'onExecute' => 'javaScript:app.mail.mail_RenameFolder',
652
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
653
				'group'		=> $group,
654
			),
655
			'move' => array(
656
				'caption' => 'Move Folder',
657
				'type' => 'drag',
658
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
659
				'dragType' => array('mailFolder'),
660
				'group'		=> $group,
661
			),
662
			'delete' => array(
663
				'caption' => 'Delete Folder',
664
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
665
				'onExecute' => 'javaScript:app.mail.mail_DeleteFolder',
666
				'group'		=> $group,
667
			),
668
			'subscribe' => array(
669
				'caption' => 'Subscribe folder ...',
670
				//'icon' => 'configure',
671
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
672
				'onExecute' => 'javaScript:app.mail.edit_subscribe',
673
				'group'		=> $group
674
			),
675
			'unsubscribe' => array(
676
				'caption' => 'Unsubscribe folder',
677
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
678
				'onExecute' => 'javaScript:app.mail.unsubscribe_folder',
679
				'group'		=> $group,
680
			),
681
			'foldermanagement' => array(
682
				'caption' => 'Folder Management ...',
683
				'icon' => 'folder_management',
684
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
685
				'onExecute' => 'javaScript:app.mail.folderManagement',
686
				'group'		=> $group,
687
				'hideOnMobile' => true
688
			),
689
			'sieve' => array(
690
				'caption' => 'Mail filter',
691
				'onExecute' => 'javaScript:app.mail.edit_sieve',
692
693
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
694
				'icon' => 'mail/filter',	// funnel
695
				'hideOnMobile' => true
696
			),
697
			'vacation' => array(
698
				'caption' => 'Vacation notice',
699
				'icon' => 'mail/navbar',	// mail as in admin
700
				'onExecute' => 'javaScript:app.mail.edit_vacation',
701
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
702
			),
703
			'edit_account' => array(
704
				'caption' => 'Edit account ...',
705
				'icon' => 'configure',
706
				'onExecute' => 'javaScript:app.mail.edit_account',
707
			),
708
			'edit_acl'	=> array(
709
				'caption' => 'Edit folder ACL ...',
710
				'icon'	=> 'lock',
711
				'enabled'	=> 'javaScript:app.mail.acl_enabled',
712
				'onExecute' => 'javaScript:app.mail.edit_acl',
713
			),
714
		);
715
		// the preference prefaskformove controls actually if there is a popup on target or not
716
		// if there are multiple options there is a popup on target, 0 for prefaskformove means
717
		// that only move is available; 1 stands for move and cancel; 2 (should be the default if
718
		// not set); so we are assuming this, when not set
719
		if (isset($this->mail_bo->mailPreferences['prefaskformove']))
720
		{
721
			switch ($this->mail_bo->mailPreferences['prefaskformove'])
722
			{
723
				case 0:
724
					unset($tree_actions['drop_copy_mail']);
725
					unset($tree_actions['drop_cancel']);
726
					break;
727
				case 1:
728
					unset($tree_actions['drop_copy_mail']);
729
					break;
730
				default:
731
					// everything is fine
732
			}
733
		}
734
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
735
		if ($this->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
736
		{
737
			unset($tree_actions['subscribe']);
738
			unset($tree_actions['unsubscribe']);
739
		}
740
		++$group;	// put delete in own group
741
		switch($GLOBALS['egw_info']['user']['preferences']['mail']['deleteOptions'])
742
		{
743
			case 'move_to_trash':
744
				$tree_actions['empty_trash'] = array(
745
					'caption' => 'empty trash',
746
					'icon' => 'dhtmlxtree/MailFolderTrash',
747
					'onExecute' => 'javaScript:app.mail.mail_emptyTrash',
748
					'group'	=> $group,
749
				);
750
				break;
751
			case 'mark_as_deleted':
752
				$tree_actions['compress_folder'] = array(
753
					'caption' => 'compress folder',
754
					'icon' => 'dhtmlxtree/MailFolderTrash',
755
					'onExecute' => 'javaScript:app.mail.mail_compressFolder',
756
					'group'	=> $group,
757
				);
758
				break;
759
		}
760
		$junkFolder = ($imap_actions?$this->mail_bo->getJunkFolder():null);
761
762
		//error_log(__METHOD__.__LINE__.$junkFolder);
763
		if ($junkFolder && !empty($junkFolder))
764
		{
765
			$tree_actions['empty_spam'] = array(
766
				'caption' => 'empty junk',
767
				'icon' => 'dhtmlxtree/MailFolderJunk',
768
				'enabled'	=> 'javaScript:app.mail.spamfolder_enabled',
769
				'onExecute' => 'javaScript:app.mail.mail_emptySpam',
770
				'group'	=> $group,
771
			);
772
		}
773
		$tree_actions['sieve']['group']	= $tree_actions['vacation']['group'] = ++$group;	// new group for filter
774
		$tree_actions['edit_account']['group'] = $tree_actions['edit_acl']['group']	= ++$group;
775
776
777
		// enforce global (group-specific) ACL
778
		if (!mail_hooks::access('aclmanagement'))
779
		{
780
			unset($tree_actions['edit_acl']);
781
		}
782
		if (!mail_hooks::access('editfilterrules'))
783
		{
784
			unset($tree_actions['sieve']);
785
		}
786
		if (!mail_hooks::access('absentnotice'))
787
		{
788
			unset($tree_actions['vacation']);
789
		}
790
		if (!mail_hooks::access('managefolders'))
791
		{
792
			unset($tree_actions['add']);
793
			unset($tree_actions['move']);
794
			unset($tree_actions['delete']);
795
			unset($tree_actions['foldermanagement']);
796
			// manage folders should not affect the ability to subscribe or unsubscribe
797
			// to existing folders, it should only affect add/rename/move/delete
798
		}
799
		return $tree_actions;
800
	}
801
802
	/**
803
	 * Ajax callback to subscribe / unsubscribe a Mailbox of an account
804
	 *
805
	 * @param {int} $_acc_id profile Id of selected mailbox
806
	 * @param {string} $_folderName name of mailbox needs to be subcribe or unsubscribed
807
	 * @param {boolean} $_status set true for subscribe and false to unsubscribe
808
	 */
809
	public function ajax_foldersubscription($_acc_id,$_folderName, $_status)
810
	{
811
		//Change the Mail object to related profileId
812
		$this->changeProfile($_acc_id);
813
		try{
814
			$this->mail_bo->icServer->subscribeMailbox($_folderName, $_status);
815
			$this->mail_bo->resetFolderObjectCache($_acc_id);
816
			$this->ajax_reloadNode($_acc_id,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
817
		} catch (Horde_Imap_Client_Exception $ex) {
0 ignored issues
show
Bug introduced by
The class Horde_Imap_Client_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
818
			error_log(__METHOD__.__LINE__."()". lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));
819
			Framework::message(lang('Folder %1 %2 failed!',$_folderName,$_status));
820
		}
821
	}
822
823
	/**
824
	 * Ajax callback to fetch folders for given profile
825
	 *
826
	 * We currently load all folders of a given profile, tree can also load parts of a tree.
827
	 *
828
	 * @param string $_nodeID if of node whose children are requested
829
	 * @param boolean $_subscribedOnly flag to tell whether to fetch all or only subscribed (default)
830
	 */
831
	public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null)
832
	{
833
		$nodeID = $_GET['id'];
834
		if (!is_null($_nodeID)) $nodeID = $_nodeID;
835
		$subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
836
		$fetchCounters = !is_null($_nodeID);
837
		list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2);
838
839
		if (!empty($_folderName)) $fetchCounters = true;
840
841
		// Check if it is called for refresh root
842
		// then we need to reinitialized the index tree
843
		if(!$nodeID && !$_profileID)
844
		{
845
			$data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
846
		}
847
		else
848
		{
849
			$data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
850
		}
851
		if (!is_null($_nodeID)) return $data;
852
		Etemplate\Widget\Tree::send_quote_json($data);
853
	}
854
855
	/**
856
	 * findNode - helper function to return only a branch of the tree
857
	 *
858
	 * @param array $_out out array (to be searched)
859
	 * @param string $_nodeID node to search for
860
	 * @param boolean $childElements return node itself, or only its child items
861
	 * @return array structured subtree
862
	 */
863
	static function findNode($_out, $_nodeID, $childElements = false)
864
	{
865
		foreach($_out['item'] as $node)
866
		{
867
			if (strcmp($node['id'],$_nodeID)===0)
868
			{
869
				//error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']);
870
				return ($childElements?$node['item']:$node);
871
			}
872
			elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id']))
873
			{
874
				//error_log(__METHOD__.__LINE__.' descend into '.$node['id']);
875
				return self::findNode($node,$_nodeID,$childElements);
876
			}
877
		}
878
	}
879
880
	/**
881
	 * Method to execute spam actions
882
	 *
883
	 * @param type $_action action id
884
	 * @param type $_params
885
	 */
886
	public function ajax_spamAction($_action, $_params)
887
	{
888
		$msg = array();
889
		$refresh = false;
890
		$response = Api\Json\Response::get();
891
892
		$id_parts = self::splitRowID($_params['row_id']);
893
		if ($id_parts['profileID'] && $id_parts['profileID'] != $this->mail_bo->profileID)
894
		{
895
			$this->changeProfile($id_parts['profileID']);
896
		}
897
		$delimiter = $this->mail_bo->getHierarchyDelimiter();
898
		// Ham folder
899
		$ham = $this->mail_bo->profileID.self::$delimiter.$this->mail_bo->icServer->acc_folder_ham;
900
		// Junk folder
901
		$junk = $this->mail_bo->profileID.self::$delimiter.$this->mail_bo->getJunkFolder();
902
		// Inbox folder
903
		$inbox = $this->mail_bo->profileID.self::$delimiter.'INBOX';
904
		// Current Mailbox
905
		$mailbox = $id_parts['folder'];
906
907
		if ($GLOBALS['egw_info']['apps']['stylite'])
908
		{
909
			$_params['mailbody'] = $this->get_load_email_data($_params['uid'], null, $mailbox);
910
			$msg[] = stylite_mail_spamtitan::execAction($_action, $_params, array(
911
				'userpwd'	=> $this->mail_bo->icServer->acc_imap_password,
912
				'user'		=> $this->mail_bo->icServer->acc_imap_username,
913
				'api_url'	=> $this->mail_bo->icServer->acc_spam_api
914
			));
915
		}
916
		switch ($_action)
917
		{
918
			case 'spam':
919
				$this->ajax_copyMessages($junk, array(
920
					'all' => false,
921
					'msg' => array($_params['row_id'])
922
					), 'move');
923
				$refresh = true;
924
				break;
925
			case 'ham':
926
				if (isset($this->mail_bo->icServer->acc_folder_ham) && !isset($this->mail_bo->icServer->acc_spam_api))
927
				{
928
					$this->ajax_copyMessages($ham, array(
929
						'all' => false,
930
						'msg' => array($_params['row_id'])
931
						), 'copy');
932
				}
933
				// Move mails to Inbox if they are in Junk folder
934
				if ($junk == $this->mail_bo->profileID.self::$delimiter.$mailbox)
935
				{
936
					$this->ajax_copyMessages($inbox, array(
937
						'all' => false,
938
						'msg' => array($_params['row_id'])
939
					), 'move');
940
					$refresh = true;
941
				}
942
				break;
943
		}
944
		if ($refresh)
945
		{
946
			$response->apply('egw.refresh',[implode('\n',$msg),'mail',$_params['row_id'],'delete']);
947
		}
948
		else
949
		{
950
			$response->apply('egw.message',[implode('\n',$msg)]);
951
		}
952
	}
953
954
	/**
955
	 * Build spam actions
956
	 *
957
	 * @return array actions
958
	 */
959
	public function getSpamActions ()
960
	{
961
		$actions = array (
962
			'spamfilter' => array (
963
				'caption'	=> 'Spam',
964
				'icon'		=> 'dhtmlxtree/MailFolderJunk',
965
				'children'	=> array (
966
					'spam' => array (
967
						'caption'	=> 'Report as Spam',
968
						'icon'		=> 'dhtmlxtree/MailFolderJunk',
969
						'onExecute' => 'javaScript:app.mail.spam_actions',
970
						'hint'		=> 'Report this email content as Spam - spam solutions like spamTitan will learn',
971
						'allowOnMultiple' => false
972
					),
973
					'ham' => array (
974
						'caption'	=> 'Report as Ham',
975
						'icon'		=> 'ham',
976
						'onExecute' => 'javaScript:app.mail.spam_actions',
977
						'hint'		=> 'Report this email content as Ham (not spam) - spam solutions like spamTitan will learn',
978
						'allowOnMultiple' => false
979
					)
980
				)
981
			)
982
		);
983
		$account = Mail\Account::read($this->mail_bo->profileID);
984
		// spamTitan actions
985
		if ($GLOBALS['egw_info']['apps']['stylite'] && $account->acc_spam_api)
986
		{
987
			$actions['spamfilter']['children'] = array_merge($actions['spamfilter']['children'], stylite_mail_spamtitan::getActions());
988
		}
989
		return $actions;
990
	}
991
992
	/**
993
	 * Get actions / context menu for index
994
	 *
995
	 * Changes here, require to log out, as $content[self::$nm_index] get stored in session!
996
	 * @return array see nextmatch_widget::egw_actions()
997
	 */
998
	private function get_actions()
999
	{
1000
		static $accArray=array(); // buffer identity names on single request
1001
		// duplicated from mail_hooks
1002
		static $deleteOptions = array(
1003
			'move_to_trash'		=> 'move to trash',
1004
			'mark_as_deleted'	=> 'mark as deleted',
1005
			'remove_immediately' =>	'remove immediately',
1006
		);
1007
		// todo: real hierarchical folder list
1008
		$lastFolderUsedForMove = null;
1009
		$moveactions = array();
1010
		$archiveFolder = $this->mail_bo->getArchiveFolder();
1011
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
1012
		//error_log(__METHOD__.__LINE__." StoredFolders->".array2string($lastFoldersUsedForMoveCont));
1013
		//error_log(__METHOD__.__LINE__.' ProfileId:'.$this->mail_bo->profileID." StoredFolders->(".count($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]).") ".array2string($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]));
1014
		if (is_null($accArray))
1015
		{
1016
			foreach(Mail\Account::search($only_current_user=true, false) as $acc_id => $accountObj)
1017
			{
1018
				//error_log(__METHOD__.__LINE__.array2string($accountObj));
1019
				if (!$accountObj->is_imap())
0 ignored issues
show
Bug introduced by
The method is_imap cannot be called on $accountObj (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1020
				{
1021
					// not to be used for IMAP Foldertree, as there is no Imap host
1022
					continue;
1023
				}
1024
				$identity_name = Mail\Account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']);
1025
				$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
1026
			}
1027
		}
1028
		if (!is_array($lastFoldersUsedForMoveCont)) $lastFoldersUsedForMoveCont=array();
1029
		foreach (array_keys($lastFoldersUsedForMoveCont) as $pid)
1030
		{
1031
			if ($this->mail_bo->profileID==$pid && isset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]))
1032
			{
1033
				$_folder = $this->mail_bo->icServer->getCurrentMailbox();
1034
				//error_log(__METHOD__.__LINE__.' '.$_folder."<->".$lastFoldersUsedForMoveCont[$this->mail_bo->profileID].function_backtrace());
1035
				$counter =1;
1036
				foreach ($lastFoldersUsedForMoveCont[$this->mail_bo->profileID] as $i => $lastFolderUsedForMoveCont)
1037
				{
1038
					$moveaction = 'move_';
1039
					if ($_folder!=$i)
1040
					{
1041
						$moveaction .= $lastFolderUsedForMoveCont;
1042
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
1043
						//error_log(__METHOD__.__LINE__.'#'.$currentArchiveActionKey);
1044
						if ($this->mail_bo->folderExists($i)) // only 10 entries per mailaccount.Control this on setting the buffered folders
1045
						{
1046
							$fS['profileID'] = $this->mail_bo->profileID;
1047
							$fS['profileName'] = $accArray[$this->mail_bo->profileID];
1048
							$fS['shortDisplayName'] = $i;
1049
							$moveactions[$moveaction] = $fS;
1050
							$counter ++;
1051
						}
1052
						else
1053
						{
1054
							unset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID][$i]);
1055
						}
1056
						//error_log(array2string($moveactions[$moveaction]));
1057
					}
1058
				}
1059
			}
1060
			elseif ($this->mail_bo->profileID!=$pid && isset($lastFoldersUsedForMoveCont[$pid]) && !empty($lastFoldersUsedForMoveCont[$pid]))
1061
			{
1062
				$counter =1;
1063
				foreach ($lastFoldersUsedForMoveCont[$pid] as $i => $lastFolderUsedForMoveCont)
1064
				{
1065
					//error_log(__METHOD__.__LINE__."$i => $lastFolderUsedForMoveCont");
1066
					if (!empty($lastFolderUsedForMoveCont)) // only 10 entries per mailaccount.Control this on setting the buffered folders
1067
					{
1068
						$moveaction = 'move_'.$lastFolderUsedForMoveCont;
1069
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
1070
						$fS = array();
1071
						$fS['profileID'] = $pid;
1072
						$fS['profileName'] = $accArray[$pid];
1073
						$fS['shortDisplayName'] = $i;
1074
						$moveactions[$moveaction] = $fS;
1075
						$counter ++;
1076
					}
1077
				}
1078
			}
1079
		}
1080
		Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
1081
		$group = 0;
1082
		$actions =  array(
1083
			'open' => array(
1084
				'caption' => lang('Open'),
1085
				'icon' => 'view',
1086
				'group' => ++$group,
1087
				'onExecute' => Api\Header\UserAgent::mobile()?'javaScript:app.mail.mobileView':'javaScript:app.mail.mail_open',
1088
				'allowOnMultiple' => false,
1089
				'default' => true,
1090
				'mobileViewTemplate' => 'view?'.filemtime(Api\Etemplate\Widget\Template::rel2path('/mail/templates/mobile/view.xet'))
1091
			),
1092
			'reply' => array(
1093
				'caption' => 'Reply',
1094
				'icon' => 'mail_reply',
1095
				'group' => ++$group,
1096
				'onExecute' => 'javaScript:app.mail.mail_compose',
1097
				'allowOnMultiple' => false,
1098
				'toolbarDefault' => true
1099
			),
1100
			'reply_all' => array(
1101
				'caption' => 'Reply All',
1102
				'icon' => 'mail_replyall',
1103
				'group' => $group,
1104
				'onExecute' => 'javaScript:app.mail.mail_compose',
1105
				'allowOnMultiple' => false,
1106
			),
1107
			'forward' => array(
1108
				'caption' => 'Forward',
1109
				'icon' => 'mail_forward',
1110
				'group' => $group,
1111
				'children' => array(
1112
					'forwardinline' => array(
1113
						'caption' => 'Inline',
1114
						'icon' => 'mail_forward',
1115
						'group' => $group,
1116
						'hint' => 'forward inline',
1117
						'onExecute' => 'javaScript:app.mail.mail_compose',
1118
						'allowOnMultiple' => false,
1119
						'toolbarDefault' => true
1120
					),
1121
					'forwardasattach' => array(
1122
						'caption' => 'Attachment',
1123
						'hint' => 'forward as attachment',
1124
						'icon' => 'mail_forward_attach',
1125
						'group' => $group,
1126
						'onExecute' => 'javaScript:app.mail.mail_compose',
1127
					),
1128
				),
1129
				'hideOnMobile' => true
1130
			),
1131
			'composeasnew' => array(
1132
				'caption' => 'Compose',
1133
				'icon' => 'new',
1134
				'hint' => 'Compose as new',
1135
				'group' => $group,
1136
				'onExecute' => 'javaScript:app.mail.mail_compose',
1137
				'allowOnMultiple' => false,
1138
			)
1139
		);
1140
		$macounter=0;
1141
		if (!empty($moveactions))
1142
		{
1143
			//error_log(__METHOD__.__LINE__.array2string($moveactions));
1144
			$children=array();
1145
			$pID=0;
1146
			foreach ($moveactions as $moveaction => $lastFolderUsedForMove)
1147
			{
1148
				$group = ($pID != $lastFolderUsedForMove['profileID'] && $macounter>0? $group+1 : $group);
1149
				//error_log(__METHOD__.__LINE__."#$pID != ".$lastFolderUsedForMove['profileID']."#".$macounter.'#'.$groupCounter.'#');
1150
				$children = array_merge($children,
1151
					array(
1152
						$moveaction => array(
1153
							'caption' => (!empty($lastFolderUsedForMove['profileName'])?$lastFolderUsedForMove['profileName']:'('.$lastFolderUsedForMove['profileID'].')').': '.(isset($lastFolderUsedForMove['shortDisplayName'])?$lastFolderUsedForMove['shortDisplayName']:''),
1154
							'icon' => 'move',
1155
							'group' => $group,
1156
							'onExecute' => 'javaScript:app.mail.mail_move2folder',
1157
							'allowOnMultiple' => true,
1158
						)
1159
					)
1160
				);
1161
				$pID = $lastFolderUsedForMove['profileID'];
1162
				$macounter++;
1163
			}
1164
			$actions['moveto'] =	array(
1165
				'caption' => lang('Move selected to'),
1166
				'icon' => 'move',
1167
				'group' => $group,
1168
				'children' => $children,
1169
			);
1170
1171
		} else {
1172
			$group++;
1173
		}
1174
		$spam_actions = $this->getSpamActions();
1175
		$group++;
1176
		foreach ($spam_actions as &$action)
1177
		{
1178
			$action['group'] = $group;
1179
		}
1180
		//error_log(__METHOD__.__LINE__.$archiveFolder);
1181
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter.$archiveFolder] = array( //toarchive
1182
			'caption' => 'Move to archive',
1183
			'hint' => 'move selected mails to archive',
1184
			'icon' => 'archive',
1185
			'group' => $group++,
1186
			'enabled' => 'javaScript:app.mail.archivefolder_enabled',
1187
			//'hideOnDisabled' => true, // does not work as expected on message-list
1188
			'onExecute' => 'javaScript:app.mail.mail_move2folder',
1189
			'shortcut' => KeyManager::shortcut(KeyManager::V, true, true),
1190
			'allowOnMultiple' => true,
1191
			'toolbarDefault' => false
1192
		);
1193
1194
		$actions += array(
1195
			'infolog' => array(
1196
				'caption' => 'InfoLog',
1197
				'hint' => 'Save as InfoLog',
1198
				'icon' => 'infolog/navbar',
1199
				'group' => ++$group,
1200
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1201
				'popup' => Link::get_registry('infolog', 'add_popup'),
1202
				'allowOnMultiple' => false,
1203
				'toolbarDefault' => true
1204
			),
1205
			'tracker' => array(
1206
				'caption' => 'Tracker',
1207
				'hint' => 'Save as ticket',
1208
				'group' => $group,
1209
				'icon' => 'tracker/navbar',
1210
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1211
				'popup' => Link::get_registry('tracker', 'add_popup'),
1212
				'mail_import' => Api\Hooks::single(array('location' => 'mail_import'),'tracker'),
1213
				'allowOnMultiple' => false,
1214
			),
1215
			'calendar' => array(
1216
				'caption' => 'Calendar',
1217
				'hint' => 'Save as Calendar',
1218
				'icon' => 'calendar/navbar',
1219
				'group' => $group,
1220
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1221
				'popup' => Link::get_registry('calendar', 'add_popup'),
1222
				'allowOnMultiple' => false,
1223
				'toolbarDefault' => true
1224
			),
1225
			'print' => array(
1226
				'caption' => 'Print',
1227
				'group' => ++$group,
1228
				'onExecute' => 'javaScript:app.mail.mail_print',
1229
				'allowOnMultiple' => false,
1230
				'hideOnMobile' => true
1231
			),
1232
			'save' => array(
1233
				'caption' => 'Save',
1234
				'group' => $group,
1235
				'icon' => 'fileexport',
1236
				'children' => array(
1237
					'save2disk' => array(
1238
						'caption' => 'Save to disk',
1239
						'hint' => 'Save message to disk',
1240
						'group' => $group,
1241
						'icon' => 'fileexport',
1242
						'onExecute' => 'javaScript:app.mail.mail_save',
1243
						'allowOnMultiple' => true,
1244
						'hideOnMobile' => true
1245
					),
1246
					'save2filemanager' => array(
1247
						'caption' => 'Filemanager',
1248
						'hint' => 'Save to filemanager',
1249
						'group' => $group,
1250
						'icon' => 'filemanager/navbar',
1251
						'onExecute' => 'javaScript:app.mail.mail_save2fm',
1252
						'allowOnMultiple' => true,
1253
					),
1254
				),
1255
				'hideOnMobile' => true
1256
			),
1257
			'view' => array(
1258
				'caption' => 'View',
1259
				'group' => $group,
1260
				'icon' => 'kmmsgread',
1261
				'children' => array(
1262
					'header' => array(
1263
						'caption' => 'Header',
1264
						'hint' => 'View header lines',
1265
						'group' => $group,
1266
						'icon' => 'kmmsgread',
1267
						'onExecute' => 'javaScript:app.mail.mail_header',
1268
						'allowOnMultiple' => false,
1269
					),
1270
					'mailsource' => array(
1271
						'caption' => 'Source',
1272
						'hint' => 'View full Mail Source',
1273
						'group' => $group,
1274
						'icon' => 'source',
1275
						'onExecute' => 'javaScript:app.mail.mail_mailsource',
1276
						'allowOnMultiple' => false,
1277
					),
1278
					'openastext' => array(
1279
						'caption' => lang('Text mode'),
1280
						'hint' => 'Open in Text mode',
1281
						'group' => ++$group,
1282
						'icon' => 'textmode',
1283
						'onExecute' => 'javaScript:app.mail.mail_openAsText',
1284
						'allowOnMultiple' => false,
1285
					),
1286
					'openashtml' => array(
1287
						'caption' => lang('HTML mode'),
1288
						'hint' => 'Open in HTML mode',
1289
						'group' => $group,
1290
						'icon' => 'htmlmode',
1291
						'onExecute' => 'javaScript:app.mail.mail_openAsHtml',
1292
						'allowOnMultiple' => false,
1293
					),
1294
				),
1295
				'hideOnMobile' => true
1296
			),
1297
			'mark' => array(
1298
				'caption' => 'Set / Remove Flags',
1299
				'icon' => 'read_small',
1300
				'group' => ++$group,
1301
				'children' => array(
1302
					// icons used from http://creativecommons.org/licenses/by-sa/3.0/
1303
					// Artist: Led24
1304
					// Iconset Homepage: http://led24.de/iconset
1305
					// License: CC Attribution 3.0
1306
					'setLabel' => array(
1307
						'caption' => 'Set / Remove Labels',
1308
						'icon' => 'tag_message',
1309
						'group' => ++$group,
1310
						// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
1311
						'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
1312
						'hideOnDisabled' => true,
1313
						'children' => array(
1314
							'unlabel' => array(
1315
								'group' => ++$group,
1316
								'caption' => "<font color='#ff0000'>".lang('remove all')."</font>",
1317
								'icon' => 'mail_label',
1318
								'onExecute' => 'javaScript:app.mail.mail_flag',
1319
								'shortcut' => KeyManager::shortcut(KeyManager::_0, true, true),
1320
							),
1321
							'label1' => array(
1322
								'group' => ++$group,
1323
								'caption' => "<font color='#ff0000'>".lang('important')."</font>",
1324
								'icon' => 'mail_label1',
1325
								'onExecute' => 'javaScript:app.mail.mail_flag',
1326
								'shortcut' => KeyManager::shortcut(KeyManager::_1, true, true),
1327
							),
1328
							'label2' => array(
1329
								'group' => $group,
1330
								'caption' => "<font color='#ff8000'>".lang('job')."</font>",
1331
								'icon' => 'mail_label2',
1332
								'onExecute' => 'javaScript:app.mail.mail_flag',
1333
								'shortcut' => KeyManager::shortcut(KeyManager::_2, true, true),
1334
							),
1335
							'label3' => array(
1336
								'group' => $group,
1337
								'caption' => "<font color='#008000'>".lang('personal')."</font>",
1338
								'icon' => 'mail_label3',
1339
								'onExecute' => 'javaScript:app.mail.mail_flag',
1340
								'shortcut' => KeyManager::shortcut(KeyManager::_3, true, true),
1341
							),
1342
							'label4' => array(
1343
								'group' => $group,
1344
								'caption' => "<font color='#0000ff'>".lang('to do')."</font>",
1345
								'icon' => 'mail_label4',
1346
								'onExecute' => 'javaScript:app.mail.mail_flag',
1347
								'shortcut' => KeyManager::shortcut(KeyManager::_4, true, true),
1348
							),
1349
							'label5' => array(
1350
								'group' => $group,
1351
								'caption' => "<font color='#8000ff'>".lang('later')."</font>",
1352
								'icon' => 'mail_label5',
1353
								'onExecute' => 'javaScript:app.mail.mail_flag',
1354
								'shortcut' => KeyManager::shortcut(KeyManager::_5, true, true),
1355
							),
1356
						),
1357
					),
1358
					// modified icons from http://creativecommons.org/licenses/by-sa/3.0/
1359
					'flagged' => array(
1360
						'group' => ++$group,
1361
						'caption' => 'Flag / Unflag',
1362
						'icon' => 'unread_flagged_small',
1363
						'onExecute' => 'javaScript:app.mail.mail_flag',
1364
						'hint' => 'Flag or Unflag a mail',
1365
						'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
1366
						'toolbarDefault' => true
1367
					),
1368
					'read' => array(
1369
						'group' => $group,
1370
						'caption' => 'Read / Unread',
1371
						'icon' => 'read_small',
1372
						'onExecute' => 'javaScript:app.mail.mail_flag',
1373
						'shortcut' => KeyManager::shortcut(KeyManager::U, true, true),
1374
1375
					),
1376
					'readall' => array(
1377
						'group' => ++$group,
1378
						'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
1379
						'icon' => 'read_small',
1380
						'onExecute' => 'javaScript:app.mail.mail_flag',
1381
						'hint' => 'mark all messages in folder as read',
1382
						'toolbarDefault' => false
1383
					),
1384
					'undelete' => array(
1385
						'group' => $group,
1386
						'caption' => 'Undelete',
1387
						'icon' => 'revert',
1388
						'onExecute' => 'javaScript:app.mail.mail_flag',
1389
					),
1390
				),
1391
			),
1392
			'delete' => array(
1393
				'caption' => 'Delete',
1394
				'hint' => $deleteOptions[$this->mail_bo->mailPreferences['deleteOptions']],
1395
				'group' => ++$group,
1396
				'onExecute' => 'javaScript:app.mail.mail_delete',
1397
				'toolbarDefault' => true
1398
			),
1399
			'drag_mail' => array(
1400
				'dragType' => array('mail'),
1401
				'type' => 'drag',
1402
				//'onExecute' => 'javaScript:app.mail.mail_dragStart',
1403
			)
1404
		);
1405
		//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
1406
		// 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
1407 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
1408
		{
1409
			unset($actions['infolog']);
1410
		}
1411 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['tracker']))
1412
		{
1413
			unset($actions['tracker']);
1414
		}
1415 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['calendar']))
1416
		{
1417
			unset($actions['calendar']);
1418
		}
1419
		// remove vfs actions if the user has no run access to filemanager
1420
		if (!$GLOBALS['egw_info']['user']['apps']['filemanager'])
1421
		{
1422
			unset($actions['save']['children']['save2filemanager']);
1423
		}
1424
		return array_merge($actions, $spam_actions);
1425
	}
1426
1427
	/**
1428
	 * Callback to fetch the rows for the nextmatch widget
1429
	 *
1430
	 * Function is static to not automatic call constructor in case profile is changed.
1431
	 *
1432
	 * @param array $query
1433
	 * @param array &$rows
1434
	 * @param array &$readonlys
1435
	 */
1436
	public static function get_rows(&$query,&$rows,&$readonlys)
1437
	{
1438
		unset($readonlys);	// not used, but required by function signature
1439
1440
		// handle possible profile change in get_rows
1441
		if (!empty($query['selectedFolder']))
1442
		{
1443
			list($_profileID,$folderName) = explode(self::$delimiter, $query['selectedFolder'], 2);
1444
			if (is_numeric(($_profileID)) && $_profileID != $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])
1445
			{
1446
				try {
1447
					$mail_ui = new mail_ui(false);	// do NOT run constructor, as we change profile anyway
1448
					$mail_ui->changeProfile($_profileID);
1449
					$query['actions'] = $mail_ui->get_actions();
1450
				}
1451
				catch(Exception $e)
1452
				{
1453
					unset($e);
1454
					$rows=array();
1455
					return 0;
1456
				}
1457
				if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
1458
			}
1459
		}
1460
		if (!isset($mail_ui))
1461
		{
1462
			try
1463
			{
1464
				$mail_ui = new mail_ui(true);	// run constructor for current profile
1465
			}
1466
			catch(Exception $e)
1467
			{
1468
				unset($e);
1469
				$rows=array();
1470
				return 0;
1471
			}
1472
			if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
1473
		}
1474
		//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
1475
		//Mail::$debugTimes=true;
1476
		if (Mail::$debugTimes) $starttime = microtime(true);
1477
		//$query['search'] is the phrase in the searchbox
1478
1479
		$mail_ui->mail_bo->restoreSessionData();
1480
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$query['selectedFolder'];
1481
1482
		$sRToFetch = null;
1483
		list($_profileID,$_folderName) = explode(self::$delimiter,$query['selectedFolder'],2);
0 ignored issues
show
Unused Code introduced by
The assignment to $_profileID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1484
		if (strpos($_folderName,self::$delimiter)!==false)
1485
		{
1486
			list($app,$_profileID,$_folderName) = explode(self::$delimiter,$_folderName,3);
0 ignored issues
show
Unused Code introduced by
The assignment to $_profileID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1487
			unset($app);
1488
		}
1489
		//save selected Folder to sessionData (mailbox)->currentFolder
1490
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$_folderName;
1491
		$toSchema = false;//decides to select list schema with column to selected (if false fromaddress is default)
1492
		if ($mail_ui->mail_bo->folderExists($_folderName))
1493
		{
1494
			$toSchema = $mail_ui->mail_bo->isDraftFolder($_folderName,false)||$mail_ui->mail_bo->isSentFolder($_folderName,false)||$mail_ui->mail_bo->isTemplateFolder($_folderName,false);
1495
		}
1496
		else
1497
		{
1498
			// take the extra time on failure
1499
			if (!$mail_ui->mail_bo->folderExists($_folderName,true))
1500
			{
1501
				//error_log(__METHOD__.__LINE__.' Test on Folder:'.$_folderName.' failed; Using INBOX instead');
1502
				$query['selectedFolder']=$mail_ui->mail_bo->sessionData['mailbox']=$_folderName='INBOX';
1503
			}
1504
		}
1505
		$rowsFetched['messages'] = null;
1506
		$offset = $query['start']+1; // we always start with 1
1507
		$maxMessages = $query['num_rows'];
1508
		//error_log(__METHOD__.__LINE__.array2string($query));
1509
		$sort = ($query['order']=='address'?($toSchema?'toaddress':'fromaddress'):$query['order']);
1510
		if (!empty($query['search'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
1511
		{
1512
			if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1513
			{
1514
				Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
1515
				if (!isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1516
				{
1517
					Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]=true;
1518
				}
1519
			}
1520
			//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
1521
			$cutoffdate = $cutoffdate2 = null;
1522
			if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
1523
			if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
1524
			//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
1525
			$filter = array(
1526
				'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
1527
				'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
1528
				'string' => $query['search'],
1529
				'status' => 'any',
1530
				//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
1531
			);
1532
			if ($query['enddate']||$query['startdate']) {
1533
				$filter['range'] = "BETWEEN";
1534
				if ($cutoffdate) {
1535
					$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
1536
					if (empty($cutoffdate2)) $filter['range'] = "SINCE";
1537
				}
1538
				if ($cutoffdate2) {
1539
					$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
1540
					if (empty($cutoffdate)) $filter['range'] = "BEFORE";
1541
				}
1542
			}
1543
		}
1544
		else
1545
		{
1546
			$filter = array();
1547
		}
1548
		if ($query['filter'])
1549
		{
1550
			$filter['status'] = $query['filter'];
1551
		}
1552
		$reverse = ($query['sort']=='ASC'?false:true);
1553
		$prefchanged = false;
1554 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']) || ($query['cat_id'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']))
1555
		{
1556
			//error_log(__METHOD__.__LINE__.' Changing userPref ActivesearchType:'.$query['cat_id']);
1557
			$GLOBALS['egw']->preferences->add('mail','ActiveSearchType',$query['cat_id'],'user');
1558
			$prefchanged = true;
1559
		}
1560 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']) || ($query['filter2'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']))
1561
		{
1562
			$GLOBALS['egw']->preferences->add('mail','ShowDetails',$query['filter2'],'user');
1563
			$prefchanged = true;
1564
		}
1565
		if ($prefchanged)
1566
		{
1567
			// save prefs
1568
			$GLOBALS['egw']->preferences->save_repository(true);
1569
		}
1570
		//error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($mail_ui->sessionData['messageFilter']));
1571
/*
1572
$cutoffdate = Api\DateTime::to('now','ts')-(3600*24*6);//SINCE, enddate
1573
$cutoffdate2 = Api\DateTime::to('now','ts')-(3600*24*3);//BEFORE, startdate
1574
$filter['range'] = "BETWEEN";// we support SINCE, BEFORE, BETWEEN and ON
1575
$filter['since'] = date("d-M-Y", $cutoffdate);
1576
$filter['before']= date("d-M-Y", $cutoffdate2);
1577
*/
1578
		try
1579
		{
1580
			if ($maxMessages > 75)
1581
			{
1582
				$rByUid = true;
1583
				$_sR = $mail_ui->mail_bo->getSortedList(
1584
					$_folderName,
1585
					$sort,
1586
					$reverse,
1587
					$filter,
1588
					$rByUid
1589
				);
1590
				$rowsFetched['messages'] = $_sR['count'];
1591
				$ids = $_sR['match']->ids;
1592
				// if $sR is false, something failed fundamentally
1593
				if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
1594
				$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
1595
				$sRToFetch = $sR;//array_slice($sR,0,50); // we fetch only the headers of a subset of the fetched uids
1596
				//error_log(__METHOD__.__LINE__.' Rows fetched (UID only):'.count($sR).' Data:'.array2string($sR));
1597
				$maxMessages = 75;
1598
				$sortResultwH['header'] = array();
1599
				if (count($sRToFetch)>0)
1600
				{
1601
					//error_log(__METHOD__.__LINE__.' Headers to fetch with UIDs:'.count($sRToFetch).' Data:'.array2string($sRToFetch));
1602
					$sortResult = array();
1603
					// fetch headers
1604
					$sortResultwH = $mail_ui->mail_bo->getHeaders(
1605
						$_folderName,
1606
						$offset,
1607
						$maxMessages,
1608
						$sort,
1609
						$reverse,
1610
						$filter,
1611
						$sRToFetch,
1612
						true, //cacheResult
1613
						($query['filter2']?true:false) // fetchPreview
1614
					);
1615
				}
1616
			}
1617
			else
1618
			{
1619
				$sortResult = array();
1620
				// fetch headers
1621
				$sortResultwH = $mail_ui->mail_bo->getHeaders(
1622
					$_folderName,
1623
					$offset,
1624
					$maxMessages,
1625
					$sort,
1626
					$reverse,
1627
					$filter,
1628
					null, // this uids only
1629
					true, // cacheResult
1630
					($query['filter2']?true:false) // fetchPreview
1631
				);
1632
				$rowsFetched['messages'] = $sortResultwH['info']['total'];
1633
			}
1634
		}
1635
		catch (Exception $e)
1636
		{
1637
			$sortResultwH=array();
1638
			$sR=array();
1639
			self::callWizard($e->getMessage(), false, 'error');
1640
		}
1641
		$response = Api\Json\Response::get();
1642
		// unlock immediately after fetching the rows
1643
		if (stripos($_GET['menuaction'],'ajax_get_rows')!==false)
1644
		{
1645
			//error_log(__METHOD__.__LINE__.' unlock tree ->'.$_GET['menuaction']);
1646
			$response->call('app.mail.unlock_tree');
1647
		}
1648
1649
		if (is_array($sR) && count($sR)>0)
1650
		{
1651
			foreach ((array)$sR as $key => $v)
1652
			{
1653
				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...
1654
				{
1655
					$sortResult['header'][] = $sortResultwH['header'][$key];
1656
				}
1657
				else
1658
				{
1659
					if (!empty($v)) $sortResult['header'][] = array('uid'=>$v);
1660
				}
1661
			}
1662
		}
1663
		else
1664
		{
1665
			$sortResult = $sortResultwH;
1666
		}
1667
		$rowsFetched['rowsFetched'] = count($sortResult['header']);
1668
		if (empty($rowsFetched['messages'])) $rowsFetched['messages'] = $rowsFetched['rowsFetched'];
1669
1670
		//error_log(__METHOD__.__LINE__.' Rows fetched:'.$rowsFetched.' Data:'.array2string($sortResult));
1671
		$cols = array('row_id','uid','status','attachments','subject','address','toaddress','fromaddress','ccaddress','additionaltoaddress','date','size','modified','bodypreview');
1672
		if ($GLOBALS['egw_info']['user']['preferences']['common']['select_mode']=='EGW_SELECTMODE_TOGGLE') unset($cols[0]);
1673
		$rows = $mail_ui->header2gridelements($sortResult['header'],$cols, $_folderName, $folderType=$toSchema);
1674
1675
		// Save the session (since we are committing session) at the end
1676
		// to make sure all necessary data are stored in session.
1677
		// e.g.: Link:: get_data which is used to read attachments data.
1678
		$mail_ui->mail_bo->saveSessionData();
1679
1680
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName.' Start:'.$query['start'].' NumRows:'.$query['num_rows'],__METHOD__.__LINE__);
1681
		return $rowsFetched['messages'];
1682
	}
1683
1684
	/**
1685
	 * function createRowID - create a unique rowID for the grid
1686
	 *
1687
	 * @param string $_folderName used to ensure the uniqueness of the uid over all folders
1688
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1689
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1690
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1691
	 */
1692
	function createRowID($_folderName, $message_uid, $_prependApp=false)
1693
	{
1694
		return self::generateRowID($this->mail_bo->profileID, $_folderName, $message_uid, $_prependApp);
1695
	}
1696
1697
	/**
1698
	 * static function generateRowID - create a unique rowID for the grid
1699
	 *
1700
	 * @param integer $_profileID profile ID for the rowid to be used
1701
	 * @param string $_folderName to ensure the uniqueness of the uid over all folders
1702
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1703
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1704
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1705
	 */
1706
	static function generateRowID($_profileID, $_folderName, $message_uid, $_prependApp=false)
1707
	{
1708
		return ($_prependApp?'mail'.self::$delimiter:'').trim($GLOBALS['egw_info']['user']['account_id']).self::$delimiter.$_profileID.self::$delimiter.base64_encode($_folderName).self::$delimiter.$message_uid;
1709
	}
1710
1711
	/**
1712
	 * function splitRowID - split the rowID into its parts
1713
	 *
1714
	 * @param string $_rowID string - a colon separated string in the form accountID:profileID:folder:message_uid
1715
	 * @return array populated named result array (accountID,profileID,folder,msgUID)
1716
	 */
1717
	static function splitRowID($_rowID)
1718
	{
1719
		$res = explode(self::$delimiter,$_rowID);
1720
		// as a rowID is perceeded by app::, should be mail!
1721
		//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));
1722
		if (count($res)==4 && is_numeric($res[0]) )
1723
		{
1724
			// we have an own created rowID; prepend app=mail
1725
			array_unshift($res,'mail');
1726
		}
1727
		return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
1728
	}
1729
1730
	/**
1731
	 * Get actions for preview toolbar
1732
	 *
1733
	 * @return array
1734
	 */
1735
	function get_toolbar_actions()
1736
	{
1737
		$actions = $this->get_actions();
1738
		$arrActions = array('composeasnew', 'reply', 'reply_all', 'forward', 'flagged', 'delete', 'print',
1739
			'infolog', 'tracker', 'calendar', 'save', 'view', 'read', 'label1',	'label2', 'label3',	'label4', 'label5','spam', 'ham');
1740
		foreach( $arrActions as &$act)
1741
		{
1742
			//error_log(__METHOD__.__LINE__.' '.$act.'->'.array2string($actions[$act]));
1743
			switch ($act)
1744
			{
1745
				case 'forward':
1746
					$actionsenabled[$act]=$actions[$act];
1747
					break;
1748
				case 'save':
1749
					$actionsenabled[$act]=$actions[$act];
1750
1751
					break;
1752
				case 'view':
1753
					$actionsenabled[$act]=$actions[$act];
1754
					break;
1755
				case 'flagged':
1756
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1757
					break;
1758
				case 'read':
1759
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1760
					break;
1761 View Code Duplication
				case 'label1':
1762
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('important');
1763
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1764
					break;
1765 View Code Duplication
				case 'label2':
1766
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('job');
1767
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1768
					break;
1769 View Code Duplication
				case 'label3':
1770
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('personal');
1771
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1772
					break;
1773 View Code Duplication
				case 'label4':
1774
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('to do');
1775
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1776
					break;
1777 View Code Duplication
				case 'label5':
1778
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('later');
1779
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1780
					break;
1781
				case 'ham':
1782
				case 'spam':
1783
					$actionsenabled[$act]= $actions['spamfilter']['children'][$act];
1784
					break;
1785
				default:
1786
					if (isset($actions[$act])) $actionsenabled[$act]=$actions[$act];
1787
			}
1788
		}
1789
		unset($actionsenabled['drag_mail']);
1790
		//error_log(array2string($actionsenabled['view']));
1791
		unset($actionsenabled['view']['children']['openastext']);//not supported in preview
1792
		unset($actionsenabled['view']['children']['openashtml']);//not supported in preview
1793
1794
		return $actionsenabled;
1795
	}
1796
1797
	/**
1798
	 * function header2gridelements - to populate the grid elements with the collected Data
1799
	 *
1800
	 * @param array $_headers headerdata to process
1801
	 * @param array $cols cols to populate
1802
	 * @param array $_folderName to ensure the uniqueness of the uid over all folders
1803
	 * @param array $_folderType used to determine if we need to populate from/to
1804
	 * @return array populated result array
1805
	 */
1806
	public function header2gridelements($_headers, $cols, $_folderName, $_folderType=0)
1807
	{
1808
		if (Mail::$debugTimes) $starttime = microtime(true);
1809
		$rv = array();
1810
		$i=0;
1811
		foreach((array)$_headers as $header)
1812
		{
1813
			$i++;
1814
			$data = array();
1815
			//error_log(__METHOD__.array2string($header));
1816
			$message_uid = $header['uid'];
1817
			$data['uid'] = $message_uid;
1818
			$data['row_id']=$this->createRowID($_folderName,$message_uid);
1819
1820
			$flags = "";
1821
			if(!empty($header['recent'])) $flags .= "R";
1822
			if(!empty($header['flagged'])) $flags .= "F";
1823
			if(!empty($header['answered'])) $flags .= "A";
1824
			if(!empty($header['forwarded'])) $flags .= "W";
1825
			if(!empty($header['deleted'])) $flags .= "D";
1826
			if(!empty($header['seen'])) $flags .= "S";
1827
			if(!empty($header['label1'])) $flags .= "1";
1828
			if(!empty($header['label2'])) $flags .= "2";
1829
			if(!empty($header['label3'])) $flags .= "3";
1830
			if(!empty($header['label4'])) $flags .= "4";
1831
			if(!empty($header['label5'])) $flags .= "5";
1832
1833
			$data["status"] = "<span class=\"status_img\"></span>";
1834
			//error_log(__METHOD__.array2string($header).' Flags:'.$flags);
1835
1836
			// the css for this row
1837
			$is_recent=false;
1838
			$css_styles = array("mail");
1839
			if ($header['deleted']) {
1840
				$css_styles[] = 'deleted';
1841
			}
1842
			if ($header['recent'] && !($header['deleted'] || $header['seen'] || $header['answered'] || $header['forwarded'])) {
1843
				$css_styles[] = 'recent';
1844
				$is_recent=true;
1845
			}
1846
			if ($header['priority'] < 3) {
1847
				$css_styles[] = 'prio_high';
1848
			}
1849
			if ($header['flagged']) {
1850
				$css_styles[] = 'flagged';
1851
			}
1852
			if (!$header['seen']) {
1853
				$css_styles[] = 'unseen'; // different status image for recent // solved via css !important
1854
			}
1855
			if ($header['answered']) {
1856
				$css_styles[] = 'replied';
1857
			}
1858
			if ($header['forwarded']) {
1859
				$css_styles[] = 'forwarded';
1860
			}
1861
			if ($header['label1']) {
1862
				$css_styles[] = 'labelone';
1863
			}
1864
			if ($header['label2']) {
1865
				$css_styles[] = 'labeltwo';
1866
			}
1867
			if ($header['label3']) {
1868
				$css_styles[] = 'labelthree';
1869
			}
1870
			if ($header['label4']) {
1871
				$css_styles[] = 'labelfour';
1872
			}
1873
			if ($header['label5']) {
1874
				$css_styles[] = 'labelfive';
1875
			}
1876
1877
			//error_log(__METHOD__.array2string($css_styles));
1878
1879
			if (in_array("subject", $cols))
1880
			{
1881
				// filter out undisplayable characters
1882
				$search = array('[\016]','[\017]',
1883
					'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
1884
					'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
1885
				$replace = '';
1886
1887
				$header['subject'] = preg_replace($search,$replace,$header['subject']);
1888
				// curly brackets get messed up by the template!
1889
1890
				if (!empty($header['subject'])) {
1891
					// make the subject shorter if it is to long
1892
					$subject = $header['subject'];
1893
				} else {
1894
					$subject = '('. lang('no subject') .')';
1895
				}
1896
1897
				$data["subject"] = $subject; // the mailsubject
1898
			}
1899
1900
			$imageHTMLBlock = '';
1901
			//error_log(__METHOD__.__LINE__.array2string($header));
1902
			if (in_array("attachments", $cols))
1903
			{
1904
				if($header['mimetype'] == 'multipart/mixed' ||
1905
					$header['mimetype'] == 'multipart/signed' ||
1906
					$header['mimetype'] == 'multipart/related' ||
1907
					$header['mimetype'] == 'multipart/report' ||
1908
					$header['mimetype'] == 'text/calendar' ||
1909
					$header['mimetype'] == 'text/html' ||
1910
					substr($header['mimetype'],0,11) == 'application' ||
1911
					substr($header['mimetype'],0,5) == 'audio' ||
1912
					substr($header['mimetype'],0,5) == 'video' ||
1913
					$header['mimetype'] == 'multipart/alternative')
1914
				{
1915
					$image = Api\Html::image('mail','attach');
1916
					$imageHTMLBlock = '';
1917
					$datarowid = $this->createRowID($_folderName,$message_uid,true);
1918
					$attachments = $header['attachments'];
1919
					if (count($attachments)<1)
1920
					{
1921
						$image = '&nbsp;';
1922
					}
1923
					if (count($attachments)==1)
1924
					{
1925
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1926
						$image = Api\Html::image('mail','attach',$attachments[0]['name'].(!empty($attachments[0]['mimeType'])?' ('.$attachments[0]['mimeType'].')':''));
1927
					}
1928
					if (count($attachments)>1)
1929
					{
1930
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1931
						$image = Api\Html::image('mail','attach',lang('%1 attachments',count($attachments)));
1932
					}
1933
1934
					$attachmentFlag = $image;
1935
				} else {
1936
					$attachmentFlag ='&nbsp;';
1937
				}
1938
				// show priority flag
1939
				if ($header['priority'] < 3) {
1940
					 $image = Api\Html::image('mail','prio_high');
1941
				} elseif ($header['priority'] > 3) {
1942
					$image = Api\Html::image('mail','prio_low');
1943
				} else {
1944
					$image = '';
1945
				}
1946
				// show a flag for flagged messages
1947
				$imageflagged ='';
1948
				if ($header['flagged'])
1949
				{
1950
					$imageflagged = Api\Html::image('mail','unread_flagged_small');
1951
				}
1952
				$data["attachments"] = $image.$attachmentFlag.$imageflagged; // icon for attachments available
1953
			}
1954
1955
			// sent or draft or template folder -> to address
1956
			if (in_array("toaddress", $cols))
1957
			{
1958
				// sent or drafts or template folder means foldertype > 0, use to address instead of from
1959
				$data["toaddress"] = $header['to_address'];//Mail::htmlentities($header['to_address'],$this->charset);
1960
			}
1961
1962
			if (in_array("additionaltoaddress", $cols))
1963
			{
1964
				$data['additionaltoaddress'] = $header['additional_to_addresses'];
1965
			}
1966
			//fromaddress
1967
			if (in_array("fromaddress", $cols))
1968
			{
1969
				$data["fromaddress"] = $header['sender_address'];
1970
			}
1971
			if (in_array("ccaddress", $cols))
1972
			{
1973
				$data['ccaddress'] = $header['cc_addresses'];
1974
			}
1975
			if (in_array("date", $cols))
1976
			{
1977
				$data["date"] = $header['date'];
1978
			}
1979
			if (in_array("modified", $cols))
1980
			{
1981
				$data["modified"] = $header['internaldate'];
1982
			}
1983
1984
			if (in_array("size", $cols))
1985
				$data["size"] = $header['size']; /// size
1986
1987
			$data["class"] = implode(' ', $css_styles);
1988
			//translate style-classes back to flags
1989
			$data['flags'] = Array();
1990
			if ($header['seen']) $data["flags"]['read'] = 'read';
1991
			foreach ($css_styles as &$flag) {
1992
				if ($flag!='mail')
1993
				{
1994
					if ($flag=='labelone') {$data["flags"]['label1'] = 'label1';}
1995
					elseif ($flag=='labeltwo') {$data["flags"]['label2'] = 'label2';}
1996
					elseif ($flag=='labelthree') {$data["flags"]['label3'] = 'label3';}
1997
					elseif ($flag=='labelfour') {$data["flags"]['label4'] = 'label4';}
1998
					elseif ($flag=='labelfive') {$data["flags"]['label5'] = 'label5';}
1999
					elseif ($flag=='unseen') {unset($data["flags"]['read']);}
2000
					else $data["flags"][$flag] = $flag;
2001
				}
2002
			}
2003
			if ($header['disposition-notification-to']) $data['dispositionnotificationto'] = $header['disposition-notification-to'];
2004
			if (($header['mdnsent']||$header['mdnnotsent']|$header['seen'])&&isset($data['dispositionnotificationto'])) unset($data['dispositionnotificationto']);
2005
			$data['attachmentsBlock'] = $imageHTMLBlock;
2006
			$data['address'] = ($_folderType?$data["toaddress"]:$data["fromaddress"]);
2007
			if (in_array("bodypreview", $cols)&&$header['bodypreview'])
2008
			{
2009
				$data["bodypreview"] = $header['bodypreview'];
2010
			}
2011
			$rv[] = $data;
2012
			//error_log(__METHOD__.__LINE__.array2string($data));
2013
		}
2014
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName,__METHOD__.__LINE__);
2015
2016
		// ToDo: call this ONLY if labels change
2017
		Etemplate\Widget::setElementAttribute('toolbar', 'actions', $this->get_toolbar_actions());
2018
2019
		return $rv;
2020
	}
2021
2022
	/**
2023
	 * display messages header lines
2024
	 *
2025
	 * all params are passed as GET Parameters
2026
	 */
2027
	function displayHeader()
2028
	{
2029
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2030
		if(isset($_GET['part'])) $partID = $_GET['part'];
2031
2032
		$hA = self::splitRowID($rowID);
2033
		$uid = $hA['msgUID'];
2034
		$mailbox = $hA['folder'];
2035
		$icServerID = $hA['profileID'];
2036
		$rememberServerID = $this->mail_bo->profileID;
2037
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2038
		{
2039
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2040
			$this->changeProfile($icServerID);
2041
		}
2042
2043
		$this->mail_bo->reopen($mailbox);
2044
		$headers_in	= $this->mail_bo->getMessageRawHeader($uid, $partID);
2045
2046
		// add line breaks to $rawheaders
2047
		$newRawHeaders = explode("\n",$headers_in);
2048
		reset($newRawHeaders);
2049
2050
		// reset $rawheaders
2051
		$rawheaders 	= "";
2052
		// create it new, with good line breaks
2053
		reset($newRawHeaders);
2054
		while(list($key,$value) = @each($newRawHeaders)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $key is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
2055
			$rawheaders .= wordwrap($value, 90, "\n     ");
2056
		}
2057
2058
		$this->mail_bo->closeConnection();
2059
		if ($rememberServerID != $this->mail_bo->profileID)
2060
		{
2061
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2062
			$this->changeProfile($rememberServerID);
2063
		}
2064
2065
		header('Content-type: text/html; charset=iso-8859-1');
2066
		print '<pre>'. htmlspecialchars($rawheaders, ENT_NOQUOTES, 'iso-8859-1') .'</pre>';
2067
2068
	}
2069
2070
	/**
2071
	 * display messages
2072
	 * @param array $_requesteddata etemplate content
2073
	 * all params are passed as GET Parameters, but can be passed via ExecMethod2 as array too
2074
	 */
2075
	function displayMessage($_requesteddata = null)
2076
	{
2077
		if (is_null($_requesteddata)) $_requesteddata = $_GET;
2078
2079
		$preventRedirect=false;
2080
		if(isset($_requesteddata['id'])) $rowID	= $_requesteddata['id'];
2081
		if(isset($_requesteddata['part'])) $partID = $_requesteddata['part']!='null'?$_requesteddata['part']:null;
2082
		if(isset($_requesteddata['mode'])) $preventRedirect   = (($_requesteddata['mode']=='display' || $_requesteddata['mode'] == 'print')?true:false);
2083
2084
		$hA = self::splitRowID($rowID);
2085
		$uid = $hA['msgUID'];
2086
		$mailbox = $hA['folder'];
2087
		$icServerID = $hA['profileID'];
2088
		$rememberServerID = $this->mail_bo->profileID;
2089
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2090
		{
2091
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2092
			$this->changeProfile($icServerID);
2093
		}
2094
		$htmlOptions = $this->mail_bo->htmlOptions;
2095
		if (!empty($_requesteddata['tryastext'])) $htmlOptions  = "only_if_no_text";
2096
		if (!empty($_requesteddata['tryashtml'])) $htmlOptions  = "always_display";
2097
2098
		//error_log(__METHOD__.__LINE__.array2string($hA));
2099
		if (($this->mail_bo->isDraftFolder($mailbox)) && $_requesteddata['mode'] == 'print')
2100
		{
2101
			$response = Api\Json\Response::get();
2102
			$response->call('app.mail.print_for_compose', $rowID);
2103
		}
2104
		if (!$preventRedirect && ($this->mail_bo->isDraftFolder($mailbox) || $this->mail_bo->isTemplateFolder($mailbox)))
2105
		{
2106
			Egw::redirect_link('/index.php',array('menuaction'=>'mail.mail_compose.compose','id'=>$rowID,'from'=>'composefromdraft'));
2107
		}
2108
		$this->mail_bo->reopen($mailbox);
2109
		// retrieve the flags of the message, before touching it.
2110
		try
2111
		{
2112
			$headers	= $this->mail_bo->getMessageHeader($uid, $partID,true,true,$mailbox);
2113
		}
2114
		catch (Api\Exception $e)
2115
		{
2116
			$error_msg[] = lang("ERROR: Message could not be displayed.");
2117
			$error_msg[] = lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);
2118
			Framework::message($e->getMessage(), 'error');
2119
		}
2120
		if (!empty($uid)) $this->mail_bo->getFlags($uid);
2121
		$envelope	= $this->mail_bo->getMessageEnvelope($uid, $partID,true,$mailbox);
2122
		//error_log(__METHOD__.__LINE__.array2string($envelope));
2123
		$this->mail_bo->getMessageRawHeader($uid, $partID,$mailbox);
2124
		$fetchEmbeddedImages = false;
2125
		// if we are in HTML so its likely that we should show the embedded images; as a result
2126
		// we do NOT want to see those, that are embedded in the list of attachments
2127
		if ($htmlOptions !='always_display') $fetchEmbeddedImages = true;
2128
		$attachments	= $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox);
2129
		//error_log(__METHOD__.__LINE__.array2string($attachments));
2130
		$attachmentHTMLBlock = self::createAttachmentBlock($attachments, $rowID, $uid, $mailbox);
2131
2132
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2133
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2134
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2135
2136
		//error_log(__METHOD__.__LINE__.$mailBody);
2137
		$this->mail_bo->closeConnection();
2138
		//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
2139
		$etpl = new Etemplate('mail.display');
2140
		$subject = $this->mail_bo->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false);
2141
2142
		// Set up data for taglist widget(s)
2143
		if ($envelope['FROM']==$envelope['SENDER']) unset($envelope['SENDER']);
2144
		$sel_options = array();
2145
		foreach(array('SENDER','FROM','TO','CC','BCC') as $field)
2146
		{
2147
			if (!isset($envelope[$field])) continue;
2148
			foreach($envelope[$field] as $field_data)
2149
			{
2150
				//error_log(__METHOD__.__LINE__.array2string($field_data));
2151
				$content[$field][] = $field_data;
2152
				$sel_options[$field][] = array(
2153
					// taglist requires these - not optional
2154
					'id' => $field_data,
2155
					'label' => str_replace('"',"'",$field_data),
2156
				);
2157
			}
2158
		}
2159
		$actionsenabled = $this->getDisplayToolbarActions();
2160
		$content['displayToolbaractions'] = json_encode($actionsenabled);
2161
		if (empty($subject)) $subject = lang('no subject');
2162
		$content['msg'] = (is_array($error_msg)?implode("<br>",$error_msg):$error_msg);
2163
		// Send mail ID so we can use it for actions
2164
		$content['mail_id'] = $rowID;
2165
		if (!is_array($headers) || !isset($headers['DATE']))
2166
		{
2167
			$headers['DATE'] = (is_array($envelope)&&$envelope['DATE']?$envelope['DATE']:'');
2168
		}
2169
		$content['mail_displaydate'] = Mail::_strtotime($headers['DATE'],'ts',true);
2170
		$content['mail_displaysubject'] = $subject;
2171
		$linkData = array('menuaction'=>"mail.mail_ui.loadEmailBody","_messageID"=>$rowID);
2172
		if (!empty($partID)) $linkData['_partID']=$partID;
2173
		if ($htmlOptions != $this->mail_bo->htmlOptions) $linkData['_htmloptions']=$htmlOptions;
2174
		$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
2175
		$content['mail_displayattachments'] = $attachmentHTMLBlock;
2176
		$content['mail_id']=$rowID;
2177
		$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
2178
		$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
2179
2180
		// DRAG attachments actions
2181
		$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
2182
			'file_drag' => array(
2183
				'dragType' => 'file',
2184
				'type' => 'drag',
2185
				'onExecute' => 'javaScript:app.mail.drag_attachment'
2186
			)
2187
		));
2188
		$readonlys = $preserv = $content;
2189
		if ($rememberServerID != $this->mail_bo->profileID)
2190
		{
2191
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2192
			$this->changeProfile($rememberServerID);
2193
		}
2194
2195
		$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
2196
	}
2197
2198
	/**
2199
	 * Build actions for display toolbar
2200
	 */
2201
	function getDisplayToolbarActions ()
2202
	{
2203
		$actions = $this->get_toolbar_actions();
2204
		$actions['mark']['children']['flagged']=array(
2205
			'group' => $actions['mark']['children']['flagged']['group'],
2206
			'caption' => 'Flagged',
2207
			'icon' => 'unread_flagged_small',
2208
			'onExecute' => 'javaScript:app.mail.mail_flag',
2209
		);
2210
		$actions['mark']['children']['unflagged']=array(
2211
			'group' => $actions['mark']['children']['flagged']['group'],
2212
			'caption' => 'Unflagged',
2213
			'icon' => 'read_flagged_small',
2214
			'onExecute' => 'javaScript:app.mail.mail_flag',
2215
		);
2216
		$actions['tracker']['toolbarDefault'] = true;
2217
		$actions['forward']['toolbarDefault'] = true;
2218
2219
		$compose = $actions['composeasnew'];
2220
		unset($actions['composeasnew']);
2221
2222
		$actions2 = array_reverse($actions,true);
2223
		$actions2['composeasnew']= $compose;
2224
		return array_reverse($actions2,true);
2225
	}
2226
2227
	/**
2228
	 * helper function to create the attachment block/table
2229
	 *
2230
	 * @param array $attachments array with the attachments information
2231
	 * @param string $rowID rowid of the message
2232
	 * @param int $uid uid of the message
2233
	 * @param string $mailbox mailbox identifier
2234
	 * @param boolean $_returnFullHTML flag wether to return HTML or data array
2235
	 * @return array|string data array or html or empty string
2236
	 */
2237
	static function createAttachmentBlock($attachments, $rowID, $uid, $mailbox,$_returnFullHTML=false)
2238
	{
2239
		$attachmentHTMLBlock='';
2240
		$attachmentHTML = array();
2241
		if (is_array($attachments) && count($attachments) > 0) {
2242
			$url_img_vfs = Api\Html::image('filemanager','navbar', lang('Filemanager'), ' height="16"');
2243
			$url_img_vfs_save_all = Api\Html::image('mail','save_all', lang('Save all'));
2244
2245
			foreach ($attachments as $key => $value)
2246
			{
2247
				$attachmentHTML[$key]['filename']= ($value['name'] ? ( $value['filename'] ? $value['filename'] : $value['name'] ) : lang('(no subject)'));
2248
				$attachmentHTML[$key]['filename'] = Api\Translation::convert_jsonsafe($attachmentHTML[$key]['filename'],'utf-8');
2249
				//error_log(array2string($value));
2250
				//error_log(strtoupper($value['mimeType']) .'<->'. Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']));
2251
				if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']);
2252
				$attachmentHTML[$key]['type']=$value['mimeType'];
2253
				$attachmentHTML[$key]['mimetype'] = Api\MimeMagic::mime2label($value['mimeType']);
2254
				$hA = self::splitRowID($rowID);
2255
				$uid = $hA['msgUID'];
2256
				$mailbox = $hA['folder'];
2257
				$acc_id = $hA['profileID'];
2258
2259
				$attachmentHTML[$key]['mime_data'] = Link::set_data($value['mimeType'], 'EGroupware\\Api\\Mail::getAttachmentAccount', array(
2260
					$acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true
2261
				));
2262
				$attachmentHTML[$key]['size']=Vfs::hsize($value['size']);
2263
				$attachmentHTML[$key]['attachment_number']=$key;
2264
				$attachmentHTML[$key]['partID']=$value['partID'];
2265
				$attachmentHTML[$key]['mail_id'] = $rowID;
2266
				$attachmentHTML[$key]['winmailFlag']=$value['is_winmail'];
2267
				$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "mail_DisplayNone";
2268
				// reset mode array as it should be considered differently for
2269
				// each attachment
2270
				$mode = array();
2271
				switch(strtoupper($value['mimeType']))
2272
				{
2273
					case 'MESSAGE/RFC822':
2274
						$linkData = array
2275
						(
2276
							'menuaction'	=> 'mail.mail_ui.displayMessage',
2277
							'mode'		=> 'display', //message/rfc822 attachments should be opened in display mode
2278
							'id'		=> $rowID,
2279
							'part'		=> $value['partID'],
2280
							'is_winmail'    => $value['is_winmail']
2281
						);
2282
						$windowName = 'displayMessage_'. $rowID.'_'.$value['partID'];
2283
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',700,egw_getWindowOuterHeight());";
2284
						break;
2285
					case 'IMAGE/JPEG':
2286
					case 'IMAGE/PNG':
2287
					case 'IMAGE/GIF':
2288
					case 'IMAGE/BMP':
2289
						// set mode for media mimetypes because we need
2290
						// to structure a download url to be used maybe in expose.
2291
						$mode = array(
2292
							'mode' => 'save'
2293
						);
2294
					case 'APPLICATION/PDF':
2295
					case 'TEXT/PLAIN':
2296
					case 'TEXT/HTML':
2297
					case 'TEXT/DIRECTORY':
2298
						$sfxMimeType = $value['mimeType'];
2299
						$buff = explode('.',$value['name']);
2300
						$suffix = '';
2301
						if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2302
						if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2303
						if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD')
2304
						{
2305
							$attachments[$key]['mimeType'] = $sfxMimeType;
2306
							$value['mimeType'] = strtoupper($sfxMimeType);
2307
						}
2308
					case 'TEXT/X-VCARD':
2309
					case 'TEXT/VCARD':
2310
					case 'TEXT/CALENDAR':
2311
					case 'TEXT/X-VCALENDAR':
2312
						$linkData = array_merge(array
2313
						(
2314
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2315
							'id'		=> $rowID,
2316
							'part'		=> $value['partID'],
2317
							'is_winmail'    => $value['is_winmail'],
2318
							'mailbox'   => base64_encode($mailbox),
2319
						) , $mode);
2320
						$windowName = 'displayAttachment_'. $uid;
2321
						$reg = '800x600';
2322
						// handle calendar/vcard
2323
						if (strtoupper($value['mimeType'])=='TEXT/CALENDAR')
2324
						{
2325
							$windowName = 'displayEvent_'. $rowID;
2326
							$reg2 = Link::get_registry('calendar','view_popup');
2327
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2328
						}
2329
						if (strtoupper($value['mimeType'])=='TEXT/X-VCARD' || strtoupper($value['mimeType'])=='TEXT/VCARD')
2330
						{
2331
							$windowName = 'displayContact_'. $rowID;
2332
							$reg2 = Link::get_registry('addressbook','add_popup');
2333
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2334
						}
2335
						// apply to action
2336
						list($width,$height) = explode('x',(!empty($reg2) ? $reg2 : $reg));
2337
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',$width,$height);";
2338
						break;
2339
					default:
2340
						$linkData = array
2341
						(
2342
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2343
							'id'		=> $rowID,
2344
							'part'		=> $value['partID'],
2345
							'is_winmail'    => $value['is_winmail'],
2346
							'mailbox'   => base64_encode($mailbox),
2347
						);
2348
						$linkView = "window.location.href = '".Egw::link('/index.php',$linkData)."';";
2349
						break;
2350
				}
2351
				// we either use mime_data for server-side supported mime-types or mime_url for client-side or download
2352
				if (empty($attachmentHTML[$key]['mime_data']))
2353
				{
2354
					$attachmentHTML[$key]['mime_url'] = Egw::link('/index.php',$linkData);
2355
					unset($attachmentHTML[$key]['mime_data']);
2356
				}
2357
				$attachmentHTML[$key]['windowName'] = $windowName;
2358
2359
				//error_log(__METHOD__.__LINE__.$linkView);
2360
				$attachmentHTML[$key]['link_view'] = '<a href="#" ." title="'.$attachmentHTML[$key]['filename'].'" onclick="'.$linkView.' return false;"><b>'.
2361
					($value['name'] ? $value['name'] : lang('(no subject)')).
2362
					'</b></a>';
2363
2364
				$linkData = array
2365
				(
2366
					'menuaction'	=> 'mail.mail_ui.getAttachment',
2367
					'mode'		=> 'save',
2368
					'id'		=> $rowID,
2369
					'part'		=> $value['partID'],
2370
					'is_winmail'    => $value['is_winmail'],
2371
					'mailbox'   => base64_encode($mailbox),
2372
				);
2373
				$attachmentHTML[$key]['link_save'] ="<a href='".Egw::link('/index.php',$linkData)."' title='".$attachmentHTML[$key]['filename']."'>".Api\Html::image('mail','fileexport')."</a>";
2374
2375
				if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
2376
				{
2377
					$link_vfs_save = Egw::link('/index.php',array(
2378
						'menuaction' => 'filemanager.filemanager_select.select',
2379
						'mode' => 'saveas',
2380
						'name' => $value['name'],
2381
						'mime' => strtolower($value['mimeType']),
2382
						'method' => 'mail.mail_ui.vfsSaveAttachment',
2383
						'id' => $rowID.'::'.$value['partID'].'::'.$value['is_winmail'],
2384
						'label' => lang('Save'),
2385
					));
2386
					$vfs_save = "<a href='#' onclick=\"egw_openWindowCentered('$link_vfs_save','vfs_save_attachment','640','570',window.outerWidth/2,window.outerHeight/2); return false;\">$url_img_vfs</a>";
2387
					// add save-all icon for first attachment
2388
					if (!$key && count($attachments) > 1)
2389
					{
2390
						$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "";
2391
						foreach ($attachments as $ikey => $value)
2392
						{
2393
							//$rowID
2394
							$ids["id[$ikey]"] = $rowID.'::'.$value['partID'].'::'.$value['is_winmail'].'::'.$value['name'];
2395
						}
2396
						$link_vfs_save = Egw::link('/index.php',array(
2397
							'menuaction' => 'filemanager.filemanager_select.select',
2398
							'mode' => 'select-dir',
2399
							'method' => 'mail.mail_ui.vfsSaveAttachment',
2400
							'label' => lang('Save all'),
2401
						)+$ids);
2402
						$vfs_save .= "<a href='#' onclick=\"egw_openWindowCentered('$link_vfs_save','vfs_save_attachment','640','530',window.outerWidth/2,window.outerHeight/2); return false;\">$url_img_vfs_save_all</a>";
2403
					}
2404
					$attachmentHTML[$key]['link_save'] .= $vfs_save;
2405
					//error_log(__METHOD__.__LINE__.$attachmentHTML[$key]['link_save']);
2406
				}
2407
			}
2408
			$attachmentHTMLBlock="<table width='100%'>";
2409
			foreach ((array)$attachmentHTML as $row)
2410
			{
2411
				$attachmentHTMLBlock .= "<tr><td><div class='useEllipsis'>".$row['link_view'].'</div></td>';
2412
				$attachmentHTMLBlock .= "<td>".$row['mimetype'].'</td>';
2413
				$attachmentHTMLBlock .= "<td>".$row['size'].'</td>';
2414
				$attachmentHTMLBlock .= "<td>".$row['link_save'].'</td></tr>';
2415
			}
2416
			$attachmentHTMLBlock .= "</table>";
2417
		}
2418
		if (!$_returnFullHTML)
2419
		{
2420
			foreach ((array)$attachmentHTML as $ikey => $value)
2421
			{
2422
				unset($attachmentHTML[$ikey]['link_view']);
2423
				unset($attachmentHTML[$ikey]['link_save']);
2424
			}
2425
		}
2426
		return ($_returnFullHTML?$attachmentHTMLBlock:$attachmentHTML);
2427
	}
2428
2429
	/**
2430
	 * fetch vacation info from active Server using icServer object
2431
	 *
2432
	 * @param array $cachedVacations an array of cached vacations for an user
2433
	 * @return array|boolean array with vacation on success or false on failure
2434
	 */
2435
	function gatherVacation($cachedVacations = array())
2436
	{
2437
		$isVacationEnabled = $this->mail_bo->icServer->acc_sieve_enabled && ($this->mail_bo->icServer->acc_sieve_host||$this->mail_bo->icServer->acc_imap_host);
2438
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
2439
2440
		if ($isVacationEnabled)
2441
		{
2442
			$sieveServer = $this->mail_bo->icServer;
2443
			try
2444
			{
2445
				$sieveServer->retrieveRules();
2446
				$vacation = $sieveServer->getVacation();
2447
2448
				$cachedVacations = array($sieveServer->acc_id => $vacation) + (array)$cachedVacations;
2449
				// Set vacation to the instance cache for particular account with expiration of one day
2450
				Api\Cache::setCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations, 60*60*24);
2451
			}
2452
			catch (PEAR_Exception $ex)
0 ignored issues
show
Bug introduced by
The class PEAR_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
2453
			{
2454
				$this->callWizard($ex->getMessage(), true, 'error');
2455
			}
2456
		}
2457
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Vacation retrieved:'.array2string($vacation));
2458
		return $vacation;
2459
	}
2460
2461
	/**
2462
	 * gather Info on how to display the quota info
2463
	 *
2464
	 * @param int $_usage amount of usage in Kb
2465
	 * @param int $_limit amount of limit in Kb
2466
	 * @return array  returns an array of info used for quota
2467
	 *		array(
2468
	 *			class		=> string,
2469
	 *			text		=> string,
2470
	 *			percent		=> string,
2471
	 *			freespace	=> integer
2472
	 *		)
2473
	 */
2474
	function quotaDisplay($_usage, $_limit)
2475
	{
2476
		$percent = $_limit == 0 ? 100 : round(($_usage*100)/$_limit);
2477
		$limit = Mail::show_readable_size($_limit*1024);
2478
		$usage = Mail::show_readable_size($_usage*1024);
2479
2480
		if ($_limit > 0)
2481
		{
2482
			$text = $usage .'/'.$limit;
2483
			switch ($percent)
2484
			{
2485
				case 90:
2486
					$class ='mail-index_QuotaRed';
2487
					break;
2488
				case 80:
2489
					$class ='mail-index_QuotaYellow';
2490
					break;
2491
				default:
2492
					$class ='mail-index_QuotaGreen';
2493
			}
2494
		}
2495
		else
2496
		{
2497
			$text = $usage;
2498
			$class ='mail-index_QuotaGreen';
2499
		}
2500
		return array (
2501
			'class'		=> $class,
2502
			'text'		=> lang('Quota: %1',$text),
2503
			'percent'	=> $percent,
2504
			'freespace'	=> $_limit*1024 - $_usage*1024
2505
		);
2506
	}
2507
2508
	/**
2509
	 * display image
2510
	 *
2511
	 * all params are passed as GET Parameters
2512
	 */
2513
	function displayImage()
2514
	{
2515
		$uid	= $_GET['uid'];
2516
		$cid	= base64_decode($_GET['cid']);
2517
		$partID = urldecode($_GET['partID']);
2518
		if (!empty($_GET['mailbox'])) $mailbox  = base64_decode($_GET['mailbox']);
2519
2520
		//error_log(__METHOD__.__LINE__.":$uid, $cid, $partID");
2521
		$this->mail_bo->reopen($mailbox);
2522
2523
		$attachment = $this->mail_bo->getAttachmentByCID($uid, $cid, $partID, true);	// true get contents as stream
2524
2525
		$this->mail_bo->closeConnection();
2526
2527
		$GLOBALS['egw']->session->commit_session();
2528
2529
		if ($attachment)
2530
		{
2531
			header("Content-Type: ". $attachment->getType());
2532
			header('Content-Disposition: inline; filename="'. $attachment->getDispositionParameter('filename') .'"');
2533
			//header("Expires: 0");
2534
			// the next headers are for IE and SSL
2535
			//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2536
			//header("Pragma: public");
2537
			Api\Session::cache_control(true);
2538
			echo $attachment->getContents();
2539
		}
2540
		else
2541
		{
2542
			// send a 404 Not found
2543
			header("HTTP/1.1 404 Not found");
2544
		}
2545
		exit();
2546
	}
2547
2548
	function getAttachment()
2549
	{
2550
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2551
2552
		$hA = self::splitRowID($rowID);
2553
		$uid = $hA['msgUID'];
2554
		$mailbox = $hA['folder'];
2555
		$icServerID = $hA['profileID'];
2556
		$rememberServerID = $this->mail_bo->profileID;
2557
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2558
		{
2559
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2560
			$this->changeProfile($icServerID);
2561
		}
2562
		$part		= $_GET['part'];
2563
		$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
2564
2565
		$this->mail_bo->reopen($mailbox);
2566
		$attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false);
2567
		$this->mail_bo->closeConnection();
2568
		if ($rememberServerID != $this->mail_bo->profileID)
2569
		{
2570
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2571
			$this->changeProfile($rememberServerID);
2572
		}
2573
2574
		$GLOBALS['egw']->session->commit_session();
2575
		//error_log(__METHOD__.print_r($_GET,true));
2576
		if ($_GET['mode'] != "save")
2577
		{
2578 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/DIRECTORY' || empty($attachment['type']))
2579
			{
2580
				$sfxMimeType = $attachment['type'];
2581
				$buff = explode('.',$attachment['filename']);
2582
				$suffix = '';
2583
				if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2584
				if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2585
				$attachment['type'] = $sfxMimeType;
2586
				if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD') $attachment['type'] = strtoupper($sfxMimeType);
2587
			}
2588
			//error_log(__METHOD__.print_r($attachment,true));
2589
			if (strtoupper($attachment['type']) == 'TEXT/CALENDAR' || strtoupper($attachment['type']) == 'TEXT/X-VCALENDAR')
2590
			{
2591
				//error_log(__METHOD__."about to call calendar_ical");
2592
				$calendar_ical = new calendar_ical();
2593
				$eventid = $calendar_ical->search($attachment['attachment'],-1);
2594
				//error_log(__METHOD__.array2string($eventid));
2595
				if (!$eventid) $eventid = -1;
2596
				$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
2597
				//error_log(__METHOD__.$event);
2598 View Code Duplication
				if ((int)$event > 0)
2599
				{
2600
					$vars = array(
2601
						'menuaction'      => 'calendar.calendar_uiforms.edit',
2602
						'cal_id'      => $event,
2603
					);
2604
					Egw::redirect_link('../index.php',$vars);
2605
				}
2606
				//Import failed, download content anyway
2607
			}
2608 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/X-VCARD' || strtoupper($attachment['type']) == 'TEXT/VCARD')
2609
			{
2610
				$addressbook_vcal = new addressbook_vcal();
2611
				// double \r\r\n seems to end a vcard prematurely, so we set them to \r\n
2612
				//error_log(__METHOD__.__LINE__.$attachment['attachment']);
2613
				$attachment['attachment'] = str_replace("\r\r\n", "\r\n", $attachment['attachment']);
2614
				$vcard = $addressbook_vcal->vcardtoegw($attachment['attachment'], $attachment['charset']);
2615
				if ($vcard['uid'])
2616
				{
2617
					$vcard['uid'] = trim($vcard['uid']);
2618
					//error_log(__METHOD__.__LINE__.print_r($vcard,true));
2619
					$contact = $addressbook_vcal->find_contact($vcard,false);
2620
				}
2621
				if (!$contact) $contact = null;
2622
				// 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))
2623
				if ($contact || count($vcard)>2)
2624
				{
2625
					$contact = $addressbook_vcal->addVCard($attachment['attachment'],(is_array($contact)?array_shift($contact):$contact),true,$attachment['charset']);
2626
				}
2627
				if ((int)$contact > 0)
2628
				{
2629
					$vars = array(
2630
						'menuaction'	=> 'addressbook.addressbook_ui.edit',
2631
						'contact_id'	=> $contact,
2632
					);
2633
					Egw::redirect_link('../index.php',$vars);
2634
				}
2635
				//Import failed, download content anyway
2636
			}
2637
		}
2638
		//error_log(__METHOD__.__LINE__.'->'.array2string($attachment));
2639
		$filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part));
2640
		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 safe() as the parameter $length expects a reference.
Loading history...
2641
		echo $attachment['attachment'];
2642
2643
		exit();
2644
	}
2645
2646
2647
	/**
2648
	 * save messages on disk or filemanager, or display it in popup
2649
	 *
2650
	 * all params are passed as GET Parameters
2651
	 */
2652
	function saveMessage()
2653
	{
2654
		$display = false;
2655
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2656
		if(isset($_GET['part'])) $partID = $_GET['part'];
2657
		if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display	= $_GET['location'];
2658
2659
		$hA = self::splitRowID($rowID);
2660
		$uid = $hA['msgUID'];
2661
		$mailbox = $hA['folder'];
2662
		$icServerID = $hA['profileID'];
2663
		$rememberServerID = $this->mail_bo->profileID;
2664
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2665
		{
2666
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2667
			$this->changeProfile($icServerID);
2668
		}
2669
2670
		$this->mail_bo->reopen($mailbox);
2671
2672
		$message = $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox);
2673
2674
		$this->mail_bo->closeConnection();
2675
		if ($rememberServerID != $this->mail_bo->profileID)
2676
		{
2677
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2678
			$this->changeProfile($rememberServerID);
2679
		}
2680
2681
		$GLOBALS['egw']->session->commit_session();
2682
		if (!$display)
2683
		{
2684
			$headers = Horde_Mime_Headers::parseHeaders($message);
2685
			$subject = str_replace('$$','__',Mail::decode_header($headers['SUBJECT']));
2686
			Api\Header\Content::safe($message, $subject.".eml", $mime='message/rfc822', $size=0, true, true);
0 ignored issues
show
Bug introduced by
$mime = 'message/rfc822' cannot be passed to safe() as the parameter $mime expects a reference.
Loading history...
Bug introduced by
$size = 0 cannot be passed to safe() as the parameter $length expects a reference.
Loading history...
2687
			echo $message;
2688
		}
2689
		else
2690
		{
2691
			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 safe() as the parameter $mime expects a reference.
Loading history...
Bug introduced by
$size = 0 cannot be passed to safe() as the parameter $length expects a reference.
Loading history...
Bug introduced by
The variable $subject seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
2692
			print '<pre>'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'</pre>';
2693
		}
2694
	}
2695
2696
	/**
2697
	 * Save an Message in the vfs
2698
	 *
2699
	 * @param string|array $ids use splitRowID, to separate values
2700
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2701
	 * @param boolean $close Return javascript to close the window
2702
	 * @return string|boolean javascript eg. to close the selector window if $close is true, or success/fail if $close is false
2703
	 */
2704
	function vfsSaveMessage($ids,$path, $close = true)
2705
	{
2706
		//error_log(__METHOD__.' IDs:'.array2string($ids).' SaveToPath:'.$path);
2707
2708 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2709
		{
2710
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2711
		}
2712
		Api\Translation::add_app('mail');
2713
2714
		$rememberServerID = $this->mail_bo->profileID;
2715
		foreach((array)$ids as $id)
2716
		{
2717
			$hA = self::splitRowID($id);
2718
			$uid = $hA['msgUID'];
2719
			$mailbox = $hA['folder'];
2720
			$icServerID = $hA['profileID'];
2721
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2722
			{
2723
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2724
				$this->changeProfile($icServerID);
2725
			}
2726
			$message = $this->mail_bo->getMessageRawBody($uid, $partID='', $mailbox);
2727
			$err=null;
2728
			if(Vfs::is_dir($path))
2729
			{
2730
				$headers = $this->mail_bo->getMessageHeader($uid,$partID,true,false,$mailbox);
2731
				$file = $path . '/'.preg_replace('/[\f\n\t\v\\:*#?<>\|]/',"_",$headers['SUBJECT']).'.eml';
2732
			}
2733
			else
2734
			{
2735
				$file = $path;
2736
			}
2737
			if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
2738
			{
2739
				$err .= lang('Error saving %1!',$file);
2740
				$succeeded = false;
2741
			}
2742
			else
2743
			{
2744
				$succeeded = true;
2745
			}
2746
			if ($fp) fclose($fp);
2747
			if ($succeeded)
2748
			{
2749
				unset($headers['SUBJECT']);//already in filename
2750
				$infoSection = Mail::createHeaderInfoSection($headers, 'SUPPRESS', false);
2751
				$props = array(array('name' => 'comment','val' => $infoSection));
2752
				Vfs::proppatch($file,$props);
2753
			}
2754
		}
2755
		if ($rememberServerID != $this->mail_bo->profileID)
2756
		{
2757
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2758
			$this->changeProfile($rememberServerID);
2759
		}
2760
2761
		if($close)
2762
		{
2763
			Framework::window_close(($err?$err:null));
2764
		}
2765
		else
2766
		{
2767
			return $succeeded;
2768
		}
2769
	}
2770
2771
	/**
2772
	 * Save an attachment in the vfs
2773
	 *
2774
	 * @param string|array $ids '::' delimited mailbox::uid::part-id::is_winmail::name (::name for multiple id's)
2775
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2776
	 * @return string javascript eg. to close the selector window
2777
	 */
2778
	function vfsSaveAttachment($ids,$path)
2779
	{
2780
		//error_log(__METHOD__.__LINE__.'("'.array2string($ids).'","'.$path."\")');");
2781
2782 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2783
		{
2784
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2785
		}
2786
		$err=null;
2787
		$dupe_count = array();
2788
		$rememberServerID = $this->mail_bo->profileID;
2789
2790
		/**
2791
		 * Extract all parameteres from the given id
2792
		 * @param int $id message id ('::' delimited mailbox::uid::part-id::is_winmail::name)
2793
		 *
2794
		 * @return array an array of parameters
2795
		 */
2796
		$getParams = function ($id) {
2797
			list($app,$user,$serverID,$mailbox,$uid,$part,$is_winmail,$name) = explode('::',$id,8);
2798
			$lId = implode('::',array($app,$user,$serverID,$mailbox,$uid));
2799
			$hA = mail_ui::splitRowID($lId);
2800
			return array(
2801
				'is_winmail' => $is_winmail == "null" || !$is_winmail?false:$is_winmail,
2802
				'user' => $user,
2803
				'name' => $name,
2804
				'part' => $part,
2805
				'uid' => $hA['msgUID'],
2806
				'mailbox' => $hA['folder'],
2807
				'icServer' => $hA['profileID']
2808
			);
2809
		};
2810
2811
		//Examine the first attachment to see if attachment
2812
		//is winmail.dat embedded attachments.
2813
		$isMultipleDownload=is_array($ids);
2814
		$p = $getParams((is_array($ids)?$ids[0]:$ids));
2815
		if ($p['is_winmail'])
2816
		{
2817 View Code Duplication
			if ($p['icServer'] && $p['icServer'] != $this->mail_bo->profileID)
2818
			{
2819
				$this->changeProfile($p['icServer']);
2820
			}
2821
			$this->mail_bo->reopen($p['mailbox']);
2822
			// retrieve all embedded attachments at once
2823
			// avoids to fetch heavy winmail.dat content
2824
			// for each file.
2825
			$attachments = $this->mail_bo->getTnefAttachments($p['uid'],$p['part']);
2826
		}
2827
2828
		foreach((array)$ids as $id)
2829
		{
2830
			$params = $getParams($id);
2831
			// when downloading a single file, name is not set
2832
			if (!$params['name']&&isset($_GET['name'])&&!$isMultipleDownload) $params['name'] = $_GET['name'];
2833 View Code Duplication
			if ($params['icServer'] && $params['icServer'] != $this->mail_bo->profileID)
2834
			{
2835
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2836
				$this->changeProfile($params['icServer']);
2837
			}
2838
			//error_log(__METHOD__.__LINE__.array2string($hA));
2839
			$this->mail_bo->reopen($params['mailbox']);
2840
			if ($params['is_winmail'])
2841
			{
2842
				// Try to find the right content for file id
2843
				foreach ($attachments as $key => $val)
0 ignored issues
show
Bug introduced by
The expression $attachments of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2844
				{
2845
					if ($key == $params['is_winmail']) $attachment = $val;
2846
				}
2847
			}
2848
			else
2849
			{
2850
				$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
2851
			}
2852
2853
			$file = $params['name'];
2854
			// when $isMultipleDownload the path holds no filename
2855
			while(Vfs::file_exists($path.($file && $isMultipleDownload ? '/'.$file : '')))
2856
			{
2857
				$dupe_count[$params['name']]++;
2858
				$file = pathinfo($params['name'], PATHINFO_FILENAME) .
2859
					' ('.($dupe_count[$params['name']] + 1).')' . '.' .
2860
					pathinfo($params['name'], PATHINFO_EXTENSION);
2861
			}
2862
			$params['name'] = $file;
2863
			//error_log(__METHOD__.__LINE__.array2string($attachment));
2864
			// when $isMultipleDownload the path holds no filename
2865
			if (!($fp = Vfs::fopen($file=$path.($params['name'] && $isMultipleDownload ? '/'.$params['name'] : ''),'wb')) ||
2866
				!fwrite($fp,$attachment['attachment']))
2867
			{
2868
				$err .= lang('Error saving %1!',$file);
2869
			}
2870
			if ($fp)
2871
			{
2872
				fclose($fp);
2873
			}
2874
		}
2875
		$this->mail_bo->closeConnection();
2876
		if ($rememberServerID != $this->mail_bo->profileID)
2877
		{
2878
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2879
			$this->changeProfile($rememberServerID);
2880
		}
2881
		Framework::window_close(($err?$err:null));
2882
	}
2883
2884
	/**
2885
	 * Zip all attachments and send to user
2886
	 * @param string $message_id = null
2887
	 */
2888
	function download_zip($message_id=null)
2889
	{
2890
		//error_log(__METHOD__.__LINE__.array2string($_GET));
2891
		// First, get all attachment IDs
2892
		if(isset($_GET['id'])) $message_id	= $_GET['id'];
2893
		//error_log(__METHOD__.__LINE__.$message_id);
2894
		$rememberServerID = $this->mail_bo->profileID;
2895
		if(!is_numeric($message_id))
2896
		{
2897
			$hA = self::splitRowID($message_id);
2898
			$message_id = $hA['msgUID'];
2899
			$mailbox = $hA['folder'];
2900
			$icServerID = $hA['profileID'];
2901
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2902
			{
2903
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2904
				$this->changeProfile($icServerID);
2905
			}
2906
		}
2907
		else
2908
		{
2909
			$mailbox = $this->mail_bo->sessionData['mailbox'];
2910
		}
2911
		// always fetch all, even inline (images)
2912
		$fetchEmbeddedImages = true;
2913
		$attachments = $this->mail_bo->getMessageAttachments($message_id,null, null, $fetchEmbeddedImages, true,true,$mailbox);
2914
		// put them in VFS so they can be zipped
2915
		$header = $this->mail_bo->getMessageHeader($message_id,'',true,false,$mailbox);
2916
		//get_home_dir may fetch the users startfolder if set; if not writeable, action will fail. TODO: use temp_dir
2917
		$homedir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
2918
		$temp_path = $homedir/*Vfs::get_home_dir()*/ . "/.mail_$message_id";
2919
		if(Vfs::is_dir($temp_path)) Vfs::remove ($temp_path);
2920
2921
		// Add subject to path, so it gets used as the file name, replacing ':'
2922
		// as it seems to cause an error
2923
		$path = $temp_path . '/' . ($header['SUBJECT'] ? Vfs::encodePathComponent(str_replace(':','-', $header['SUBJECT'])) : lang('mail')) .'/';
2924
		if(!Vfs::mkdir($path, 0700, true))
2925
		{
2926
			echo "Unable to open temp directory $path";
2927
			return;
2928
		}
2929
2930
		$file_list = array();
2931
		$dupe_count = array();
2932
		$this->mail_bo->reopen($mailbox);
2933
		if ($attachments[0]['is_winmail'] && $attachments[0]['is_winmail']!='null')
2934
		{
2935
			$tnefAttachments = $this->mail_bo->getTnefAttachments($message_id, $attachments[0]['partID'],true);
2936
		}
2937
		foreach($attachments as $file)
2938
		{
2939
			if ($file['is_winmail'])
2940
			{
2941
				// Try to find the right content for file id
2942
				foreach ($tnefAttachments as $key => $val)
0 ignored issues
show
Bug introduced by
The expression $tnefAttachments of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2943
				{
2944
					error_log(__METHOD__.' winmail = '.$key);
2945
					if ($key == $file['is_winmail']) $attachment = $val;
2946
				}
2947
			}
2948
			else
2949
			{
2950
				$attachment = $this->mail_bo->getAttachment($message_id,$file['partID'],$file['is_winmail'],false,true);
2951
			}
2952
			$success=true;
2953
			if (empty($file['filename'])) $file['filename'] = $file['name'];
2954
			if(in_array($path.$file['filename'], $file_list))
2955
			{
2956
				$dupe_count[$path.$file['filename']]++;
2957
				$file['filename'] = pathinfo($file['filename'], PATHINFO_FILENAME) .
2958
					' ('.($dupe_count[$path.$file['filename']] + 1).')' . '.' .
2959
					pathinfo($file['filename'], PATHINFO_EXTENSION);
2960
			}
2961
			// Strip special characters to make sure the files are visible for all OS (windows has issues)
2962
			$target_name = iconv($file['charset'] ? $file['charset'] : $GLOBALS['egw_info']['server']['system_charset'], 'ASCII//IGNORE', $file['filename']);
2963
			
2964
			if (!($fp = Vfs::fopen($path.$target_name,'wb')) ||
2965
				!(!fseek($attachment['attachment'], 0, SEEK_SET) && stream_copy_to_stream($attachment['attachment'], $fp)))
2966
			{
2967
				$success=false;
2968
				Framework::message("Unable to zip {$target_name}",'error');
2969
			}
2970
			if ($success) $file_list[] = $path.$target_name;
2971
			if ($fp) fclose($fp);
2972
		}
2973
		$this->mail_bo->closeConnection();
2974
		if ($rememberServerID != $this->mail_bo->profileID)
2975
		{
2976
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2977
			$this->changeProfile($rememberServerID);
2978
		}
2979
2980
		// Zip it up
2981
		Vfs::download_zip($file_list);
2982
2983
		// Clean up
2984
		Vfs::remove($temp_path);
2985
2986
		exit();
2987
	}
2988
2989
	function get_load_email_data($uid, $partID, $mailbox,$htmlOptions=null)
2990
	{
2991
		// seems to be needed, as if we open a mail from notification popup that is
2992
		// located in a different folder, we experience: could not parse message
2993
		$this->mail_bo->reopen($mailbox);
2994
		$this->mailbox = $mailbox;
2995
		$this->uid = $uid;
2996
		$this->partID = $partID;
2997
		$bufferHtmlOptions = $this->mail_bo->htmlOptions;
2998
		if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions;
2999
		// fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice
3000
		$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
3001
		$calendar_part = null;
3002
		$bodyParts	= $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
3003
3004
		// for meeting requests (multipart alternative with text/calendar part) let calendar render it
3005
		if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
3006
		{
3007
			$charset = $calendar_part->getContentTypeParameter('charset');
3008
			$this->mail_bo->fetchPartContents($uid, $calendar_part);
3009
			Api\Cache::setSession('calendar', 'ical', array(
3010
				'charset' => $charset ? $charset : 'utf-8',
3011
				'attachment' => $calendar_part->getContents(),
3012
				'method' => $calendar_part->getContentTypeParameter('method'),
3013
			));
3014
			$this->mail_bo->htmlOptions = $bufferHtmlOptions;
3015
			Api\Translation::add_app('calendar');
3016
			return ExecMethod('calendar.calendar_uiforms.meeting',
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod() has been deprecated with message: use autoloadable class-names, instanciate and call method or use static methods

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

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

Loading history...
3017
				array('event'=>null,'msg'=>'','useSession'=>true)
3018
			);
3019
		}
3020
		// Compose the content of the frame
3021
		$frameHtml =
3022
			$this->get_email_header($this->mail_bo->getStyles($bodyParts)).
3023
			$this->showBody($this->getdisplayableBody($bodyParts,true,false), false);
3024
		//IE10 eats away linebreaks preceeded by a whitespace in PRE sections
3025
		$frameHtml = str_replace(" \r\n","\r\n",$frameHtml);
3026
		$this->mail_bo->htmlOptions = $bufferHtmlOptions;
3027
3028
		return $frameHtml;
3029
	}
3030
3031
	static function get_email_header($additionalStyle='')
3032
	{
3033
		// egw_info[flags][css] already include <style> tags
3034
		$GLOBALS['egw_info']['flags']['css'] = preg_replace('|</?style[^>]*>|i', '', $additionalStyle);
3035
		$GLOBALS['egw_info']['flags']['nofooter']=true;
3036
		$GLOBALS['egw_info']['flags']['nonavbar']=true;
3037
		// do NOT include any default CSS
3038
		Framework::includeCSS('mail', 'preview', true, true);
3039
3040
		// load preview.js to activate mailto links
3041
		Framework::includeJS('/mail/js/preview.js');
3042
3043
		// send CSP and content-type header
3044
		return $GLOBALS['egw']->framework->header();
3045
	}
3046
3047
	function showBody(&$body, $print=true,$fullPageTags=true)
3048
	{
3049
		$BeginBody = '<div class="mailDisplayBody">
3050
<table width="100%" style="table-layout:fixed"><tr><td class="td_display">';
3051
3052
		$EndBody = '</td></tr></table></div>';
3053
		if ($fullPageTags) $EndBody .= "</body></html>";
3054
		if ($print)	{
3055
			print $BeginBody. $body .$EndBody;
3056
		} else {
3057
			return $BeginBody. $body .$EndBody;
3058
		}
3059
	}
3060
3061
	function &getdisplayableBody($_bodyParts,$modifyURI=true,$useTidy = true)
3062
	{
3063
		$bodyParts	= $_bodyParts;
3064
3065
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
3066
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
3067
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
3068
3069
		$body = '';
3070
3071
		//error_log(__METHOD__.array2string($bodyParts)); //exit;
3072
		if (empty($bodyParts)) return "";
3073
		foreach((array)$bodyParts as $singleBodyPart) {
3074
			if (!isset($singleBodyPart['body'])) {
3075
				$singleBodyPart['body'] = $this->getdisplayableBody($singleBodyPart,$modifyURI,$useTidy);
3076
				$body .= $singleBodyPart['body'];
3077
				continue;
3078
			}
3079
			$bodyPartIsSet = strlen(trim($singleBodyPart['body']));
3080
			if (!$bodyPartIsSet)
3081
			{
3082
				$body .= '';
3083
				continue;
3084
			}
3085
			if(!empty($body)) {
3086
				$body .= '<hr style="border:dotted 1px silver;">';
3087
			}
3088
			//error_log($singleBodyPart['body']);
3089
			//error_log(__METHOD__.__LINE__.' CharSet:'.$singleBodyPart['charSet'].' mimeType:'.$singleBodyPart['mimeType']);
3090
			// some characterreplacements, as they fail to translate
3091
			$sar = array(
3092
				'@(\x84|\x93|\x94)@',
3093
				'@(\x96|\x97|\x1a)@',
3094
				'@(\x82|\x91|\x92)@',
3095
				'@(\x85)@',
3096
				'@(\x86)@',
3097
				'@(\x99)@',
3098
				'@(\xae)@',
3099
			);
3100
			$rar = array(
3101
				'"',
3102
				'-',
3103
				'\'',
3104
				'...',
3105
				'&',
3106
				'(TM)',
3107
				'(R)',
3108
			);
3109
3110
			if(($singleBodyPart['mimeType'] == 'text/html' || $singleBodyPart['mimeType'] == 'text/plain') &&
3111
				strtoupper($singleBodyPart['charSet']) != 'UTF-8')
3112
			{
3113
				$singleBodyPart['body'] = preg_replace($sar,$rar,$singleBodyPart['body']);
3114
			}
3115
			//error_log(__METHOD__.__LINE__.'reports:'.$singleBodyPart['charSet']);
3116
			if ($singleBodyPart['charSet']=='us-ascii')
3117
			{
3118
				$orgCharSet=$singleBodyPart['charSet'];
3119
				$singleBodyPart['charSet'] = Api\Translation::detect_encoding($singleBodyPart['body']);
3120
				error_log(__METHOD__.__LINE__.'reports:'.$orgCharSet.' but seems to be:'.$singleBodyPart['charSet']);
3121
			}
3122
			$singleBodyPart['body'] = Api\Translation::convert_jsonsafe($singleBodyPart['body'],$singleBodyPart['charSet']);
3123
			//error_log(__METHOD__.__LINE__.array2string($singleBodyPart));
3124
			if($singleBodyPart['mimeType'] == 'text/plain')
3125
			{
3126
				$newBody	= @htmlentities($singleBodyPart['body'],ENT_QUOTES, strtoupper(Mail::$displayCharset));
3127
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3128
				// if empty and charset is utf8 try sanitizing the string in question
3129
				if (empty($newBody) && strtolower($singleBodyPart['charSet'])=='utf-8') $newBody = @htmlentities(iconv('utf-8', 'utf-8', $singleBodyPart['body']),ENT_QUOTES, strtoupper(Mail::$displayCharset));
3130
				// if the conversion to htmlentities fails somehow, try without specifying the charset, which defaults to iso-
3131
				if (empty($newBody)) $newBody    = htmlentities($singleBodyPart['body'],ENT_QUOTES);
3132
3133
				// search http[s] links and make them as links available again
3134
				// to understand what's going on here, have a look at
3135
				// http://www.php.net/manual/en/function.preg-replace.php
3136
3137
				// create links for websites
3138
				if ($modifyURI) $newBody = Api\Html::activate_links($newBody);
3139
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3140
				// redirect links for websites if you use no cookies
3141
				#if (!($GLOBALS['egw_info']['server']['usecookies']))
3142
				#	$newBody = preg_replace("/href=(\"|\')((http(s?):\/\/)|(www\.))([\w,\-,\/,\?,\=,\.,&amp;,!\n,\%,@,\(,\),\*,#,:,~,\+]+)(\"|\')/ie",
3143
				#		"'href=\"$webserverURL/redirect.php?go='.@htmlentities(urlencode('http$4://$5$6'),ENT_QUOTES,\"Mail::$displayCharset\").'\"'", $newBody);
3144
3145
				// create links for email addresses
3146
				//TODO:if ($modifyURI) $this->parseEmail($newBody);
3147
				// create links for inline images
3148
				if ($modifyURI)
3149
				{
3150
					$newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain');
3151
				}
3152
3153
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3154
				// to display a mailpart of mimetype plain/text, may be better taged as preformatted
3155
				#$newBody	= nl2br($newBody);
3156
				// since we do not display the message as HTML anymore we may want to insert good linebreaking (for visibility).
3157
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3158
				// dont break lines that start with > (&gt; as the text was processed with htmlentities before)
3159
				$newBody	= "<pre>".Mail::wordwrap($newBody,90,"\n",'&gt;')."</pre>";
3160
			}
3161
			else
3162
			{
3163
				$alreadyHtmlLawed=false;
3164
				$newBody	= $singleBodyPart['body'];
3165
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3166
				#error_log(print_r($newBody,true));
3167 View Code Duplication
				if ($useTidy && extension_loaded('tidy'))
3168
				{
3169
					$tidy = new tidy();
3170
					$cleaned = $tidy->repairString($newBody, Mail::$tidy_config,'utf8');
3171
					// Found errors. Strip it all so there's some output
3172
					if($tidy->getStatus() == 2)
3173
					{
3174
						error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$tidy->errorBuffer);
0 ignored issues
show
Bug introduced by
The property errorBuffer does not seem to exist in tidy.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3175
					}
3176
					else
3177
					{
3178
						$newBody = $cleaned;
3179
					}
3180
					if (!$preserveHTML)	// ToDo KL: $preserveHTML is NOT initialised, so always if is dead code
3181
					{
3182
						// filter only the 'body', as we only want that part, if we throw away the Api\Html
3183
						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.
Loading history...
3184
						if ($matches[2])
3185
						{
3186
							$hasOther = true;
3187
							$newBody = $matches[2];
3188
						}
3189
					}
3190
				}
3191
				else
3192
				{
3193
					// htmLawed filter only the 'body'
3194
					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.
Loading history...
3195
					if ($matches[2])
3196
					{
3197
						$hasOther = true;
3198
						$newBody = $matches[2];
3199
					}
3200
					$htmLawed = new Api\Html\HtmLawed();
3201
					// the next line should not be needed, but produces better results on HTML 2 Text conversion,
3202
					// as we switched off HTMLaweds tidy functionality
3203
					$newBody = str_replace(array('&amp;amp;','<DIV><BR></DIV>',"<DIV>&nbsp;</DIV>",'<div>&nbsp;</div>'),array('&amp;','<BR>','<BR>','<BR>'),$newBody);
3204
					$newBody = $htmLawed->run($newBody,Mail::$htmLawed_config);
3205
					if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3];
3206
					$alreadyHtmlLawed=true;
3207
				}
3208
				// do the cleanup, set for the use of purifier
3209
				//$newBodyBuff = $newBody;
3210
				/* if (!$alreadyHtmlLawed)*/ Mail::getCleanHTML($newBody);
3211
/*
3212
				// 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
3213
				if (strtoupper(Mail::$displayCharset) == 'UTF-8')
3214
				{
3215
					$test = @json_encode($newBody);
3216
					//error_log(__METHOD__.__LINE__.' ->'.strlen($singleBodyPart['body']).' Error:'.json_last_error().'<- BodyPart:#'.$test.'#');
3217
					if (($test=="null" || $test === false || is_null($test)) && strlen($newBody)>0)
3218
					{
3219
						$newBody = $newBodyBuff;
3220
						$tv = Mail::$htmLawed_config['tidy'];
3221
						Mail::$htmLawed_config['tidy'] = 0;
3222
						Mail::getCleanHTML($newBody);
3223
						Mail::$htmLawed_config['tidy'] = $tv;
3224
					}
3225
				}
3226
*/
3227
				// removes stuff between http and ?http
3228
				$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';    // only http:// gets removed, other protocolls are shown
3229
				$newBody = preg_replace('~'.$Protocol.'[^>]*\?'.$Protocol.'~sim','$1',$newBody); // removes stuff between http:// and ?http://
3230
				// TRANSFORM MAILTO LINKS TO EMAILADDRESS ONLY, WILL BE SUBSTITUTED BY parseEmail TO CLICKABLE LINK
3231
				$newBody = preg_replace('/(?<!"|href=|href\s=\s|href=\s|href\s=)'.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
3232
					"\\1@\\2.\\3",
3233
					$newBody);
3234
3235
				// redirect links for websites if you use no cookies
3236
				#if (!($GLOBALS['egw_info']['server']['usecookies'])) { //do it all the time, since it does mask the mailadresses in urls
3237
					//TODO:if ($modifyURI) $this->parseHREF($newBody);
3238
				#}
3239
				// create links for inline images
3240
				if ($modifyURI)
3241
				{
3242
					$newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID);
3243
				}
3244
				// email addresses / mailto links get now activated on client-side
3245
			}
3246
3247
			$body .= $newBody;
3248
		}
3249
		// create links for windows shares
3250
		// \\\\\\\\ == '\\' in real life!! :)
3251
		$body = preg_replace("/(\\\\\\\\)([\w,\\\\,-]+)/i",
3252
			"<a href=\"file:$1$2\" target=\"_blank\"><font color=\"blue\">$1$2</font></a>", $body);
3253
3254
		$body = preg_replace($nonDisplayAbleCharacters,'',$body);
3255
3256
		return $body;
3257
	}
3258
3259
	/**
3260
	 * Resolve inline images from CID to proper url
3261
	 *
3262
	 * @param string $_body message content
3263
	 * @param string $_mailbox mail folder
3264
	 * @param string $_uid uid
3265
	 * @param string $_partID part id
3266
	 * @param string $_messageType = 'html', message type is either html or plain
3267
	 * @return string message body including all CID images replaced
3268
	 */
3269
	public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html')
3270
	{
3271
		if ($_messageType === 'plain')
3272
		{
3273
			return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain');
3274
		}
3275
		else
3276
		{
3277
			foreach(array('src','url','background') as $type)
3278
			{
3279
				$_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type);
3280
			}
3281
			return $_body;
3282
		}
3283
	}
3284
3285
	/**
3286
	 * Replace CID with proper type of content understandable by browser
3287
	 *
3288
	 * @param type $_body content of message
3289
	 * @param type $_mailbox mail box
3290
	 * @param type $_uid uid
3291
	 * @param type $_partID part id
3292
	 * @param type $_type = 'src' type of inline image that needs to be resolved and replaced
3293
	 *	- types: {plain|src|url|background}
3294
	 * @return string returns body content including all CID replacements
3295
	 */
3296
	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src')
3297
	{
3298
		/**
3299
		 * Callback for preg_replace_callback function
3300
		 * returns matched CID replacement string based on given type
3301
		 * @param array $matches
3302
		 * @param string $_mailbox
3303
		 * @param string $_uid
3304
		 * @param string $_partID
3305
		 * @param string $_type
3306
		 * @return string|boolean returns the replace
3307
		*/
3308
		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type)
3309
		{
3310
			if (!$_type)	return false;
3311
			$CID = '';
3312
			// Build up matches according to selected type
3313
			switch ($_type)
3314
			{
3315
				case "plain":
3316
					$CID = $matches[1];
3317
					break;
3318
				case "src":
3319
					// as src:cid contains some kind of url, it is likely to be urlencoded
3320
					$CID = urldecode($matches[2]);
3321
					break;
3322
				case "url":
3323
					$CID = $matches[1];
3324
					break;
3325
				case "background":
3326
					$CID = $matches[2];
3327
					break;
3328
			}
3329
3330
			static $cache = array();	// some caching, if mails containing the same image multiple times
3331
3332
			if (is_array($matches) && $CID)
3333
			{
3334
				$linkData = array (
3335
					'menuaction'    => 'mail.mail_ui.displayImage',
3336
					'uid'		=> $_uid,
3337
					'mailbox'	=> base64_encode($_mailbox),
3338
					'cid'		=> base64_encode($CID),
3339
					'partID'	=> $_partID,
3340
				);
3341
				$imageURL = Egw::link('/index.php', $linkData);
3342
				// to test without data uris, comment the if close incl. it's body
3343
				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
3344
				{
3345
					if (!isset($cache[$imageURL]))
3346
					{
3347
						if ($_type !="background")
3348
						{
3349
							$bo = Mail::getInstance(false, mail_ui::$icServerID);
3350
							$attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID);
3351
3352
							// only use data uri for "smaller" images, as otherwise the first display of the mail takes to long
3353
							if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192)	// msie=8 allows max 32k data uris
0 ignored issues
show
Bug introduced by
The class Horde_Mime_Part does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3354
							{
3355
								$bo->fetchPartContents($_uid, $attachment);
3356
								$cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents());
3357
							}
3358
							else
3359
							{
3360
								$cache[$imageURL] = $imageURL;
3361
							}
3362
						}
3363
						else
3364
						{
3365
							$cache[$imageURL] = $imageURL;
3366
						}
3367
					}
3368
					$imageURL = $cache[$imageURL];
3369
				}
3370
3371
				// Decides the final result of replacement according to the type
3372
				switch ($_type)
3373
				{
3374
					case "plain":
3375
						return '<img src="'.$imageURL.'" />';
3376
					case "src":
3377
						return 'src="'.$imageURL.'"';
3378
					case "url":
3379
						return 'url('.$imageURL.');';
3380
					case "background":
3381
						return 'background="'.$imageURL.'"';
3382
				}
3383
			}
3384
			return false;
3385
		};
3386
3387
		// return new body content base on chosen type
3388
		switch($_type)
3389
		{
3390
			case"plain":
3391
				return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body);
3392
			case "src":
3393
				return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3394
			case "url":
3395
				return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body);
3396
			case "background":
3397
				return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3398
		}
3399
	}
3400
3401
	/**
3402
	 * importMessage
3403
	 * @param array $content = null an array of content
3404
	 */
3405
	function importMessage($content=null)
3406
	{
3407
		//error_log(__METHOD__.__LINE__.$this->mail_bo->getDraftFolder());
3408
3409
		if (!empty($content))
3410
		{
3411
			//error_log(__METHOD__.__LINE__.array2string($content));
3412
			if ($content['vfsfile'])
3413
			{
3414
				$file = $content['vfsfile'] = array(
3415
					'name' => Vfs::basename($content['vfsfile']),
3416
					'type' => Vfs::mime_content_type($content['vfsfile']),
3417
					'file' => Vfs::PREFIX.$content['vfsfile'],
3418
					'size' => filesize(Vfs::PREFIX.$content['vfsfile']),
3419
				);
3420
			}
3421
			else
3422
			{
3423
				$file = $content['uploadForImport'];
3424
			}
3425
			$destination = $content['FOLDER'][0];
3426
3427
			if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
3428
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3429
			{
3430
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3431
				$this->changeProfile($icServerID);
3432
			}
3433
			//error_log(__METHOD__.__LINE__.self::$delimiter.array2string($destination));
3434
			$importID = Mail::getRandomString();
3435
			$importFailed = false;
3436
			try
3437
			{
3438
				$messageUid = $this->importMessageToFolder($file,$destination,$importID);
3439
			    $linkData = array
3440
			    (
3441
					'id' => $this->createRowID($destination, $messageUid, true),
3442
			    );
3443
			}
3444
			catch (Api\Exception\WrongUserinput $e)
3445
			{
3446
					$importFailed=true;
3447
					$content['msg']		= $e->getMessage();
3448
			}
3449
			if (!$importFailed)
3450
			{
3451
				list($width, $height) = explode('x', Link::get_registry('mail', 'add_popup'));
3452
				if ($width > 0 && $height > 0) Api\Json\Response::get()->call('resizeTo', $width, $height);
3453
				ExecMethod2('mail.mail_ui.displayMessage',$linkData);
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod2() has been deprecated with message: use autoloadable class-names, instanciate and call method or use static methods

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

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

Loading history...
3454
				return;
3455
			}
3456
		}
3457
		if (!is_array($content)) $content = array();
3458
		if (empty($content['FOLDER'])) $content['FOLDER']=(array)$this->mail_bo->getDraftFolder();
3459
		if (!empty($content['FOLDER']))
3460
		{
3461
			$compose = new mail_compose();
3462
			$sel_options['FOLDER'] = $compose->ajax_searchFolder(0,true);
3463
		}
3464
3465
		$etpl = new Etemplate('mail.importMessage');
3466
		$etpl->setElementAttribute('uploadForImport','onFinish','app.mail.uploadForImport');
3467
		$etpl->exec('mail.mail_ui.importMessage',$content,$sel_options,array(),array(),2);
3468
	}
3469
3470
	/**
3471
	 * importMessageToFolder
3472
	 *
3473
	 * @param array $_formData Array with information of name, type, file and size
3474
	 * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if
3475
	 *					folder is modified
3476
	 * @param string $importID ID for the imported message, used by attachments to identify them unambiguously
3477
	 * @return mixed $messageUID or exception
3478
	 */
3479
	function importMessageToFolder($_formData,&$_folder,$importID='')
3480
	{
3481
		$importfailed = false;
3482
		//error_log(__METHOD__.__LINE__.array2string($_formData));
3483
		if (empty($_formData['file'])) $_formData['file'] = $_formData['tmp_name'];
3484
		// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
3485
		try
3486
		{
3487
			$tmpFileName = Mail::checkFileBasics($_formData,$importID);
3488
		}
3489
		catch (Api\Exception\WrongUserinput $e)
3490
		{
3491
			$importfailed = true;
3492
			$alert_msg .= $e->getMessage();
3493
		}
3494
		// -----------------------------------------------------------------------
3495
		if ($importfailed === false)
3496
		{
3497
			$mailObject = new Api\Mailer();
3498
			try
3499
			{
3500
				$this->mail_bo->parseFileIntoMailObject($mailObject, $tmpFileName);
3501
			}
3502
			catch (Api\Exception\AssertionFailed $e)
3503
			{
3504
				$importfailed = true;
3505
				$alert_msg .= $e->getMessage();
3506
			}
3507
			$this->mail_bo->openConnection();
3508
			if (empty($_folder))
3509
			{
3510
				$importfailed = true;
3511
				$alert_msg .= lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);
3512
			}
3513
			$delimiter = $this->mail_bo->getHierarchyDelimiter();
3514
			if($_folder=='INBOX'.$delimiter) $_folder='INBOX';
3515
			if ($importfailed === false)
3516
			{
3517
				if ($this->mail_bo->folderExists($_folder,true)) {
3518
					try
3519
					{
3520
						$messageUid = $this->mail_bo->appendMessage($_folder,
3521
							$mailObject->getRaw(),
3522
							null,'\\Seen');
3523
					}
3524
					catch (Api\Exception\WrongUserinput $e)
3525
					{
3526
						$importfailed = true;
3527
						$alert_msg .= lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$_formData['name'],$_folder,$e->getMessage());
3528
					}
3529
				}
3530
				else
3531
				{
3532
					$importfailed = true;
3533
					$alert_msg .= lang("Import of message %1 failed. Destination Folder %2 does not exist.",$_formData['name'],$_folder);
3534
				}
3535
			}
3536
		}
3537
		// set the url to open when refreshing
3538
		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...
3539
		{
3540
			throw new Api\Exception\WrongUserinput($alert_msg);
3541
		}
3542
		else
3543
		{
3544
			return $messageUid;
3545
		}
3546
	}
3547
3548
	/**
3549
	 * importMessageFromVFS2DraftAndEdit
3550
	 *
3551
	 * @param array $formData Array with information of name, type, file and size; file is required,
3552
	 *                               name, type and size may be set here to meet the requirements
3553
	 *						Example: $formData['name']	= 'a_email.eml';
3554
	 *								 $formData['type']	= 'message/rfc822';
3555
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3556
	 *								 $formData['size']	= 2136;
3557
	 * @return void
3558
	 */
3559
	function importMessageFromVFS2DraftAndEdit($formData='')
3560
	{
3561
		$this->importMessageFromVFS2DraftAndDisplay($formData,'edit');
3562
	}
3563
3564
	/**
3565
	 * importMessageFromVFS2DraftAndDisplay
3566
	 *
3567
	 * @param array $formData Array with information of name, type, file and size; file is required,
3568
	 *                               name, type and size may be set here to meet the requirements
3569
	 *						Example: $formData['name']	= 'a_email.eml';
3570
	 *								 $formData['type']	= 'message/rfc822';
3571
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3572
	 *								 $formData['size']	= 2136;
3573
	 * @param string $mode mode to open ImportedMessage display and edit are supported
3574
	 * @return void
3575
	 */
3576
	function importMessageFromVFS2DraftAndDisplay($formData='',$mode='display')
3577
	{
3578
		if (empty($formData)) if (isset($_REQUEST['formData'])) $formData = $_REQUEST['formData'];
3579
		//error_log(__METHOD__.__LINE__.':'.array2string($formData).' Mode:'.$mode.'->'.function_backtrace());
3580
		$draftFolder = $this->mail_bo->getDraftFolder(false);
3581
		$importID = Mail::getRandomString();
3582
3583
		// handling for mime-data hash
3584
		if (!empty($formData['data']))
3585
		{
3586
			$formData['file'] = 'egw-data://'.$formData['data'];
3587
		}
3588
		// name should be set to meet the requirements of checkFileBasics
3589
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['name']))
3590
		{
3591
			$buff = explode('/',$formData['file']);
3592
			if (is_array($buff)) $formData['name'] = array_pop($buff); // take the last part as name
3593
		}
3594
		// type should be set to meet the requirements of checkFileBasics
3595
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['type']))
3596
		{
3597
			$buff = explode('.',$formData['file']);
3598
			$suffix = '';
3599
			if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
3600
			if (!empty($suffix)) $formData['type'] = Api\MimeMagic::ext2mime($suffix);
3601
		}
3602
		// size should be set to meet the requirements of checkFileBasics
3603
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && !isset($formData['size']))
3604
		{
3605
			$formData['size'] = strlen($formData['file']); // set some size, to meet requirements of checkFileBasics
3606
		}
3607
		try
3608
		{
3609
			$messageUid = $this->importMessageToFolder($formData,$draftFolder,$importID);
3610
			$linkData = array
3611
			(
3612
		        'menuaction'    => ($mode=='display'?'mail.mail_ui.displayMessage':'mail.mail_compose.composeFromDraft'),
3613
				'id'		=> $this->createRowID($draftFolder,$messageUid,true),
3614
				'deleteDraftOnClose' => 1,
3615
			);
3616
			if ($mode!='display')
3617
			{
3618
				unset($linkData['deleteDraftOnClose']);
3619
				$linkData['method']	='importMessageToMergeAndSend';
3620
			}
3621
			else
3622
			{
3623
				$linkData['mode']=$mode;
3624
			}
3625
			Egw::redirect_link('/index.php',$linkData);
3626
		}
3627
		catch (Api\Exception\WrongUserinput $e)
3628
		{
3629
			Framework::window_close($e->getMessage());
3630
		}
3631
	}
3632
3633
	/**
3634
	 * loadEmailBody
3635
	 *
3636
	 * @param string _messageID UID
3637
	 *
3638
	 * @return xajax response
3639
	 */
3640
	function loadEmailBody($_messageID=null,$_partID=null,$_htmloptions=null)
3641
	{
3642
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3643
		if (!$_messageID && !empty($_GET['_messageID'])) $_messageID = $_GET['_messageID'];
3644
		if (!$_partID && !empty($_GET['_partID'])) $_partID = $_GET['_partID'];
3645
		if (!$_htmloptions && !empty($_GET['_htmloptions'])) $_htmloptions = $_GET['_htmloptions'];
3646
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageID,true).",$_partID,$_htmloptions");
3647
		if (empty($_messageID)) return "";
3648
		$uidA = self::splitRowID($_messageID);
3649
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
3650
		$messageID = $uidA['msgUID'];
3651
		$icServerID = $uidA['profileID'];
3652
		//something went wrong. there is a $_messageID but no $messageID: means $_messageID is crippeled
3653
		if (empty($messageID)) return "";
3654
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
3655
		{
3656
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3657
			$this->changeProfile($icServerID);
3658
		}
3659
3660
		$bodyResponse = $this->get_load_email_data($messageID,$_partID,$folder,$_htmloptions);
3661
		Api\Session::cache_control(true);
3662
		//error_log(array2string($bodyResponse));
3663
		echo $bodyResponse;
3664
3665
	}
3666
3667
	/**
3668
	 * ajax_setFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3669
	 * gets the counters and sets the text of a treenode if needed (unread Messages found)
3670
	 * @param array $_folder folders to refresh its unseen message counters
3671
	 * @return nothing
3672
	 */
3673
	function ajax_setFolderStatus($_folder)
3674
	{
3675
		Api\Translation::add_app('mail');
3676
		//error_log(__METHOD__.__LINE__.array2string($_folder));
3677
		if ($_folder)
3678
		{
3679
			$this->mail_bo->getHierarchyDelimiter(false);
3680
			$oA = array();
3681
			foreach ($_folder as $_folderName)
3682
			{
3683
				list($profileID,$folderName) = explode(self::$delimiter,$_folderName,2);
3684
				if (is_numeric($profileID))
3685
				{
3686
					if ($profileID != $this->mail_bo->profileID) continue; // only current connection
3687
					if ($folderName)
3688
					{
3689
						try
3690
						{
3691
							$fS = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3692
						}
3693
						catch (Exception $e)
3694
						{
3695
							if (Mail::$debug) error_log(__METHOD__,' ()'.$e->getMessage ());
3696
							continue;
3697
						}
3698
						if (in_array($fS['shortDisplayName'],Mail::$autoFolders)) $fS['shortDisplayName']=lang($fS['shortDisplayName']);
3699
						//error_log(__METHOD__.__LINE__.array2string($fS));
3700
						if ($fS['unseen'])
3701
						{
3702
							$oA[$_folderName] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3703
						}
3704
						if ($fS['unseen']==0 && $fS['shortDisplayName'])
3705
						{
3706
							$oA[$_folderName] = $fS['shortDisplayName'];
3707
						}
3708
					}
3709
				}
3710
			}
3711
			//error_log(__METHOD__.__LINE__.array2string($oA));
3712
			if ($oA)
3713
			{
3714
				$response = Api\Json\Response::get();
3715
				$response->call('app.mail.mail_setFolderStatus',$oA);
3716
			}
3717
		}
3718
	}
3719
3720
	/**
3721
	 * ajax_addFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3722
	 * @param string $_parentFolderName folder to add a folder to
3723
	 * @param string $_newName new foldername
3724
	 * @return nothing
3725
	 */
3726
	function ajax_addFolder($_parentFolderName, $_newName)
3727
	{
3728
		//error_log(__METHOD__.__LINE__.' ParentFolderName:'.array2string($_parentFolderName).' NewName/Folder:'.array2string($_newName));
3729
		$errorMessage='';
3730
		if ($_parentFolderName)
3731
		{
3732
			$created = false;
3733
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_parentFolderName);
3734
			//the conversion is handeled by horde, frontend interaction is all utf-8
3735
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3736
			list($profileID,$parentFolderName) = explode(self::$delimiter,$decodedFolderName,2);
3737
			if (is_numeric($profileID))
3738
			{
3739
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3740
				$del = $this->mail_bo->getHierarchyDelimiter(false);
3741
				//$del = $prefix = '';
3742
				//$nameSpace = $this->mail_bo->_getNameSpaces();
3743
				//error_log(__METHOD__.__LINE__.array2string($nameSpace));
3744
				// we expect something like that: data may differ!
3745
				//$nameSpace = Array(
3746
				//	[0] => Array([prefix_present] => [prefix] => [delimiter] => /[type] => personal)
3747
				//	[1] => Array([prefix_present] => 1[prefix] => Other Users/[delimiter] => /[type] => others)
3748
				//	[2] => Array([prefix_present] => 1[prefix] => Shared Folders/[delimiter] => /[type] => shared)
3749
				//)
3750
				//
3751
				/*
3752
				foreach ($nameSpace as $nSp)
3753
				{
3754
					error_log(__METHOD__.__LINE__.array2string($nSp));
3755
					// personal is assumed to be the default
3756
					if ($nSp['type']=='personal')
3757
					{
3758
						$prefix = $nSp['prefix'];
3759
						$del = $nSp['delimiter'];
3760
					}
3761
					if ($parentFolderName && $nSp['prefix_present'] && stripos($parentFolderName,$nSp['prefix'])!==false && stripos($parentFolderName,$nSp['prefix'])<=strlen($nSp['delimiter']))
3762
					{
3763
						$prefix = $nSp['prefix'];
3764
						$del = $nSp['delimiter'];
3765
						break;
3766
					}
3767
					if (empty($parentFolderName) && !$nSp['prefix_present'])
3768
					{
3769
						$del = $nSp['delimiter'];
3770
						break;
3771
					}
3772
				}
3773
3774
				if (empty($del)) $del = $this->mail_bo->getHierarchyDelimiter(false);
3775
				*/
3776
				$nA = explode($del,$_newName);
3777
3778
				//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3779
				if (!!empty($parentFolderName)) $oldFolderInfo = $this->mail_bo->getFolderStatus($parentFolderName,false);
3780
				//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3781
3782
				$this->mail_bo->reopen('INBOX');
3783
				$parentName = $parentFolderName;
3784
				// if newName has delimiter ($del) in it, we need to create the subtree
3785
				if (!empty($nA))
3786
				{
3787
					$c=0;
3788
					foreach($nA as $sTName)
3789
					{
3790
						$error=null;
3791
						if(($parentFolderName = $this->mail_bo->createFolder($parentFolderName, $sTName, $error)))
3792
						{
3793
							$c++;
3794
						}
3795
						else
3796
						{
3797
							$errorMessage .= $error;
3798
						}
3799
					}
3800
					if ($c==count($nA)) $created=true;
3801
				}
3802
				if (!empty($parentName)) $this->mail_bo->reopen($parentName);
3803
			}
3804
			//error_log(__METHOD__.__LINE__.array2string($oA));
3805
			if ($created===true)
3806
			{
3807
				$this->mail_bo->resetFolderObjectCache($profileID);
3808
				$response = Api\Json\Response::get();
3809
				if ( $oldFolderInfo['shortDisplayName'])
3810
				{
3811
					$nodeInfo = array($_parentFolderName=>$oldFolderInfo['shortDisplayName']);
3812
				}
3813
				else
3814
				{
3815
					$nodeInfo = array($profileID=>lang('INBOX'));
3816
				}
3817
				$response->call('app.mail.mail_reloadNode',$nodeInfo);
3818
			}
3819
			else
3820
			{
3821
				if ($errorMessage)
3822
				{
3823
					$response = Api\Json\Response::get();
3824
					$response->call('egw.message',$errorMessage);
3825
				}
3826
			}
3827
		}
3828
	}
3829
3830
	/**
3831
	 * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3832
	 * @param string $_folderName folder to rename and refresh
3833
	 * @param string $_newName new foldername
3834
	 * @return nothing
3835
	 */
3836
	function ajax_renameFolder($_folderName, $_newName)
3837
	{
3838
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName).' NewName:'.array2string($_newName));
3839
		if ($_folderName)
3840
		{
3841
			Api\Translation::add_app('mail');
3842
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3843
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3844
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3845
			$oA = array();
3846
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3847
			$hasChildren = false;
3848
			if (is_numeric($profileID))
3849
			{
3850
				if ($profileID != $this->mail_bo->profileID) $this->changeProfile ($profileID);
3851
				$pA = explode($del,$folderName);
3852
				array_pop($pA);
3853
				$parentFolder = implode($del,$pA);
3854
				if (strtoupper($folderName)!= 'INBOX')
3855
				{
3856
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3857
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false);
3858
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3859
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3860
					{
3861
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3862
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3863
						$nameSpace = $this->mail_bo->_getNameSpaces();
3864
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3865
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3866
						$fragments = array();
3867
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3868
						foreach ($subFolders as $k => $folder)
3869
						{
3870
							// we do not monitor failure or success on subfolders
3871
							if ($folder == $folderName)
3872
							{
3873
								unset($subFolders[$k]);
3874
							}
3875
							else
3876
							{
3877
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3878
								$fragments[$profileID.self::$delimiter.$folder] = substr($folder,strlen($folderName));
3879
							}
3880
						}
3881
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($fragments));
3882
					}
3883
3884
					$this->mail_bo->reopen('INBOX');
3885
					$success = false;
3886
					try
3887
					{
3888 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3889
						{
3890
							$this->mail_bo->resetFolderObjectCache($profileID);
3891
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3892
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3893
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3894
							$success = true;
3895
						}
3896
					}
3897
					catch (Exception $e)
3898
					{
3899
						$newFolderName=$folderName;
3900
						$msg = $e->getMessage();
3901
					}
3902
					$this->mail_bo->reopen($newFolderName);
3903
					$fS = $this->mail_bo->getFolderStatus($newFolderName,false);
3904
					//error_log(__METHOD__.__LINE__.array2string($fS));
3905 View Code Duplication
					if ($hasChildren)
3906
					{
3907
						$subFolders = $this->mail_bo->getMailBoxesRecursive($newFolderName, $delimiter, $prefix);
3908
						foreach ($subFolders as $k => $folder)
3909
						{
3910
							// we do not monitor failure or success on subfolders
3911
							if ($folder == $folderName)
3912
							{
3913
								unset($subFolders[$k]);
3914
							}
3915
							else
3916
							{
3917
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
3918
							}
3919
						}
3920
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
3921
					}
3922
3923
					$oA[$_folderName]['id'] = $profileID.self::$delimiter.$newFolderName;
3924
					$oA[$_folderName]['olddesc'] = $oldFolderInfo['shortDisplayName'];
3925 View Code Duplication
					if ($fS['unseen'])
3926
					{
3927
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3928
3929
					}
3930
					else
3931
					{
3932
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'];
3933
					}
3934
					foreach($fragments as $oldFolderName => $fragment)
3935
					{
3936
						//error_log(__METHOD__.__LINE__.':'.$oldFolderName.'->'.$profileID.self::$delimiter.$newFolderName.$fragment);
3937
						$oA[$oldFolderName]['id'] = $profileID.self::$delimiter.$newFolderName.$fragment;
3938
						$oA[$oldFolderName]['olddesc'] = '#skip-user-interaction-message#';
3939
						$fS = $this->mail_bo->getFolderStatus($newFolderName.$fragment,false);
3940 View Code Duplication
						if ($fS['unseen'])
3941
						{
3942
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3943
3944
						}
3945
						else
3946
						{
3947
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'];
3948
						}
3949
					}
3950
				}
3951
			}
3952 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
3953
			{
3954
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
3955
				$this->mail_bo->saveSessionData();
3956
			}
3957
			//error_log(__METHOD__.__LINE__.array2string($oA));
3958
			$response = Api\Json\Response::get();
3959
			if ($oA && $success)
3960
			{
3961
				$response->call('app.mail.mail_setLeaf',$oA);
3962
			}
3963
			else
3964
			{
3965
				$response->call('egw.refresh',lang('failed to rename %1 ! Reason: %2',$oldFolderName,$msg),'mail');
0 ignored issues
show
Bug introduced by
The variable $oldFolderName seems to be defined by a foreach iteration on line 3934. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
3966
			}
3967
		}
3968
	}
3969
3970
	/**
3971
	 * reload node
3972
	 *
3973
	 * @param string _folderName  folder to reload
3974
	 * @param boolean $_subscribedOnly = true
3975
	 * @return void
3976
	 */
3977
	function ajax_reloadNode($_folderName,$_subscribedOnly=true)
3978
	{
3979
		Api\Translation::add_app('mail');
3980
		$oldPrefForSubscribedOnly = !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'];
3981
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3982
		list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3983
		if ($profileID != $this->mail_bo->profileID) $this->changeProfile($profileID);
3984
3985
		// if pref and required mode dont match -> reset the folderObject cache to ensure
3986
		// that we get what we request
3987
		if ($_subscribedOnly != $oldPrefForSubscribedOnly) $this->mail_bo->resetFolderObjectCache($profileID);
3988
3989
		if (!empty($folderName))
3990
		{
3991
			$parentFolder=(!empty($folderName)?$folderName:'INBOX');
3992
			$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
3993
			if ($folderInfo['unseen'])
3994
			{
3995
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'].' ('.$folderInfo['unseen'].')';
3996
			}
3997
			if ($folderInfo['unseen']==0 && $folderInfo['shortDisplayName'])
3998
			{
3999
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'];
4000
			}
4001
4002
			$refreshData = array(
4003
				$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4004
		}
4005
		else
4006
		{
4007
			$refreshData = array(
4008
				$profileID=>lang('INBOX')//string with no meaning lateron
4009
			);
4010
		}
4011
		// Send full info back in the response
4012
		$response = Api\Json\Response::get();
4013
		foreach($refreshData as $folder => &$name)
4014
		{
4015
			$name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true);
4016
		}
4017
		$response->call('app.mail.mail_reloadNode',$refreshData);
4018
4019
	}
4020
4021
	/**
4022
	 * ResolveWinmail fetches the encoded attachments
4023
	 * from winmail.dat and will response expected structure back
4024
	 * to client in order to display them.
4025
	 *
4026
	 * Note: this ajax function should only be called via
4027
	 * nm mail selection as it does not support profile change
4028
	 * and uses the current available ic_server connection.
4029
	 *
4030
	 * @param type $_rowid row id from nm
4031
	 *
4032
	 */
4033
	function ajax_resolveWinmail ($_rowid)
4034
	{
4035
		$response = Api\Json\Response::get();
4036
4037
		$idParts = self::splitRowID($_rowid);
4038
		$uid = $idParts['msgUID'];
4039
		$mbox = $idParts['folder'];
4040
4041
		$attachments = $this->mail_bo->getMessageAttachments($uid, null, null, false,true,true,$mbox);
4042
		if (is_array($attachments))
4043
		{
4044
			$attachments = $this->createAttachmentBlock($attachments, $_rowid, $uid, $mbox, false);
4045
			$response->data($attachments);
4046
		}
4047
		else
4048
		{
4049
			$response->call('egw.message', lang('Can not resolve the winmail.dat attachment!'));
4050
		}
4051
	}
4052
4053
	/**
4054
	 * move folder
4055
	 *
4056
	 * @param string _folderName  folder to vove
4057
	 * @param string _target target folder
4058
	 *
4059
	 * @return void
4060
	 */
4061
	function ajax_MoveFolder($_folderName, $_target)
4062
	{
4063
		if (Mail::$debug) error_log(__METHOD__.__LINE__."Move Folder: $_folderName to Target: $_target");
4064
		if ($_folderName)
4065
		{
4066
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4067
			$_newLocation2 = $this->mail_bo->decodeEntityFolderName($_target);
4068
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4069
			list($newProfileID,$_newLocation) = explode(self::$delimiter,$_newLocation2,2);
4070
			if ($profileID != $this->mail_bo->profileID || $profileID != $newProfileID) $this->changeProfile($profileID);
4071
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4072
			$hasChildren = false;
4073
			if (is_numeric($profileID))
4074
			{
4075
				$pA = explode($del,$folderName);
4076
				$namePart = array_pop($pA);
4077
				$_newName = $namePart;
4078
				$oldParentFolder = implode($del,$pA);
4079
				$parentFolder = $_newLocation;
4080
4081
				if (strtoupper($folderName)!= 'INBOX' &&
4082
					(($oldParentFolder === $parentFolder) || //$oldParentFolder == $parentFolder means move on same level
4083
					(($oldParentFolder != $parentFolder &&
4084
					strlen($parentFolder)>0 && strlen($folderName)>0 &&
4085
					strpos($parentFolder,$folderName)===false)))) // indicates that we move the older up the tree within its own branch
4086
				{
4087
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
4088
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4089
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4090
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4091
					{
4092
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4093
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4094
						$nameSpace = $this->mail_bo->_getNameSpaces();
4095
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4096
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4097
4098
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4099
						foreach ($subFolders as $k => $folder)
4100
						{
4101
							// we do not monitor failure or success on subfolders
4102
							if ($folder == $folderName)
4103
							{
4104
								unset($subFolders[$k]);
4105
							}
4106
							else
4107
							{
4108
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
4109
							}
4110
						}
4111
					}
4112
4113
					$this->mail_bo->reopen('INBOX');
4114
					$success = false;
4115
					try
4116
					{
4117 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
4118
						{
4119
							$this->mail_bo->resetFolderObjectCache($profileID);
4120
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
4121
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
4122
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
4123
							$this->mail_bo->resetFolderObjectCache($profileID);
4124
							$success = true;
4125
						}
4126
					}
4127
					catch (Exception $e)
4128
					{
4129
						$newFolderName=$folderName;
4130
						$msg = $e->getMessage();
4131
					}
4132
					$this->mail_bo->reopen($parentFolder);
4133
					$this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4134
					//error_log(__METHOD__.__LINE__.array2string($fS));
4135 View Code Duplication
					if ($hasChildren)
4136
					{
4137
						$subFolders = $this->mail_bo->getMailBoxesRecursive($parentFolder, $delimiter, $prefix);
4138
						foreach ($subFolders as $k => $folder)
4139
						{
4140
							// we do not monitor failure or success on subfolders
4141
							if ($folder == $folderName)
4142
							{
4143
								unset($subFolders[$k]);
4144
							}
4145
							else
4146
							{
4147
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
4148
							}
4149
						}
4150
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4151
					}
4152
				}
4153
			}
4154 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4155
			{
4156
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
4157
				$this->mail_bo->saveSessionData();
4158
			}
4159
			//error_log(__METHOD__.__LINE__.array2string($oA));
4160
			$response = Api\Json\Response::get();
4161
			if ($success)
4162
			{
4163
				Api\Translation::add_app('mail');
4164
4165
				$oldFolderInfo = $this->mail_bo->getFolderStatus($oldParentFolder,false,false,false);
4166
				$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4167
				$refreshData = array(
4168
					$profileID.self::$delimiter.$oldParentFolder=>$oldFolderInfo['shortDisplayName'],
4169
					$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4170
				// if we move the folder within the same parent-branch of the tree, there is no need no refresh the upper part
4171 View Code Duplication
				if (strlen($parentFolder)>strlen($oldParentFolder) && strpos($parentFolder,$oldParentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$parentFolder]);
4172 View Code Duplication
				if (count($refreshData)>1 && strlen($oldParentFolder)>strlen($parentFolder) && strpos($oldParentFolder,$parentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$oldParentFolder]);
4173
4174
				// Send full info back in the response
4175
				foreach($refreshData as $folder => &$name)
4176
				{
4177
					$name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true);
4178
				}
4179
				$response->call('app.mail.mail_reloadNode',$refreshData);
4180
4181
			}
4182
			else
4183
			{
4184
				$response->call('egw.refresh',lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');
4185
			}
4186
		}
4187
	}
4188
4189
	/**
4190
	 * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4191
	 * @param string $_folderName folder to delete
4192
	 * @param boolean $_return = false wheter return the success value (true) or send response to client (false)
4193
	 * @return nothing
4194
	 */
4195
	function ajax_deleteFolder($_folderName, $_return = false)
4196
	{
4197
		//error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName));
4198
		$success = false;
4199
		if ($_folderName)
4200
		{
4201
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4202
			$oA = array();
4203
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4204
			if (is_numeric($profileID) && $profileID != $this->mail_bo->profileID) $this->changeProfile ($profileID);
4205
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4206
			$hasChildren = false;
4207
			if (is_numeric($profileID))
4208
			{
4209
				$pA = explode($del,$folderName);
4210
				array_pop($pA);
4211
				if (strtoupper($folderName)!= 'INBOX')
4212
				{
4213
					//error_log(__METHOD__.__LINE__."$folderName,  implode($del,$pA), $_newName");
4214
					$oA = array();
4215
					$subFolders = array();
4216
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4217
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4218
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4219
					{
4220
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4221
						$ftD = array();
4222
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4223
						$nameSpace = $this->mail_bo->_getNameSpaces();
4224
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4225
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4226
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4227
						//error_log(__METHOD__.__LINE__.'->'."$folderName, $delimiter, $prefix");
4228
						foreach ($subFolders as $k => $f)
4229
						{
4230
							$ftD[substr_count($f,$delimiter)][]=$f;
4231
						}
4232
						krsort($ftD,SORT_NUMERIC);//sort per level
4233
						//we iterate per level of depth of the subtree, deepest nesting is to be deleted first, and then up the tree
4234
						foreach($ftD as $k => $lc)//collection per level
4235
						{
4236
							foreach($lc as $f)//folders contained in that level
4237
							{
4238
								try
4239
								{
4240
									//error_log(__METHOD__.__LINE__.array2string($f).'<->'.$folderName);
4241
									$this->mail_bo->deleteFolder($f);
4242
									$success = true;
4243
									if ($f==$folderName) $oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4244
								}
4245
								catch (Exception $e)
4246
								{
4247
									$msg .= ($msg?' ':'').lang("Failed to delete %1. Server responded:",$f).$e->getMessage();
4248
									$success = false;
4249
								}
4250
							}
4251
						}
4252
					}
4253
					else
4254
					{
4255
						try
4256
						{
4257
							$this->mail_bo->deleteFolder($folderName);
4258
							$success = true;
4259
							$oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4260
						}
4261
						catch (Exception $e)
4262
						{
4263
							$msg = $e->getMessage();
4264
							$success = false;
4265
						}
4266
					}
4267
				}
4268
				else
4269
				{
4270
					$msg = lang("refused to delete folder INBOX");
4271
				}
4272
			}
4273
			if ($_return) return $success;
4274
			$response = Api\Json\Response::get();
4275
			if ($success)
4276
			{
4277
				//error_log(__METHOD__.__LINE__.array2string($oA));
4278
				$response->call('app.mail.mail_removeLeaf',$oA);
4279
			}
4280
			else
4281
			{
4282
				$response->call('egw.refresh',lang('failed to delete %1 ! Reason: %2',$oldFolderInfo['shortDisplayName'],$msg),'mail');
4283
			}
4284
		}
4285
	}
4286
4287
	/**
4288
	 * empty changeProfile - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4289
	 *
4290
	 * Made static to NOT call __construct, as it would connect to old server, before going to new one
4291
	 *
4292
	 * @param int $icServerID New profile / server ID
4293
	 * @param bool $getFolders The client needs the folders for the profile
4294
	 * @return nothing
4295
	 */
4296
	public static function ajax_changeProfile($icServerID, $getFolders = true, $exec_id=null)
4297
	{
4298
		$response = Api\Json\Response::get();
4299
4300
		$previous_id = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
4301
4302
		if ($icServerID && $icServerID != $previous_id)
4303
		{
4304
			$mail_ui = new mail_ui(false);	// do NOT run constructor, as we call changeProfile anyway
4305
			try
4306
			{
4307
				$mail_ui->changeProfile($icServerID);
4308
				// if we have an eTemplate exec_id, also send changed actions
4309
				if ($exec_id && ($actions = $mail_ui->get_actions()))
4310
				{
4311
					$response->generic('assign', array(
4312
						'etemplate_exec_id' => $exec_id,
4313
						'id' => 'nm',
4314
						'key' => 'actions',
4315
						'value' => $actions,
4316
					));
4317
				}
4318
			}
4319
			catch (Exception $e) {
4320
				self::callWizard($e->getMessage(),true, 'error');
4321
			}
4322
		}
4323
		else
4324
		{
4325
			$mail_ui = new mail_ui(true);	// run constructor
4326
		}
4327
	}
4328
4329
	/**
4330
	 * ajax_refreshVacationNotice - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4331
	 *	Note: only the activeProfile VacationNotice is refreshed
4332
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4333
	 *						if other than active profile; nothing is done!
4334
	 * @return nothing
4335
	 */
4336
	public static function ajax_refreshVacationNotice($icServerID=null)
4337
	{
4338
		//Get vacation from cache if it's available
4339
		$cachedVacations = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid']);
4340
		$vacation = $cachedVacations[$icServerID];
4341
4342
		if (!$vacation)
4343
		{
4344
			try
4345
			{
4346
				// Create mail app object
4347
				$mail = new mail_ui();
4348
4349
				if (empty($icServerID)) $icServerID = $mail->Mail->profileID;
4350
				if ($icServerID != $mail->Mail->profileID) return;
4351
4352
				$vacation = $mail->gatherVacation($cachedVacations);
4353
			} catch (Exception $e) {
4354
				$vacation=false;
4355
				error_log(__METHOD__.__LINE__." ".$e->getMessage());
4356
				unset($e);
4357
			}
4358
		}
4359
4360
		if($vacation) {
4361
			if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date'))
4362
			{
4363
				$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
4364
				$refreshData['vacationnotice'] = lang('Vacation notice is active');
4365
				$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 DateTime::server2user() has too many arguments starting with 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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4366
				if ($vacation['status'] == 'by_date' && $vacation['end_date']+ 24*3600 < time())$refreshData = '';
4367
			}
4368
		}
4369
		if ($vacation==false)
4370
		{
4371
			$refreshData['vacationnotice'] =  '';
4372
			$refreshData['vacationrange'] =  '';
4373
		}
4374
		$response = Api\Json\Response::get();
4375
		$response->call('app.mail.mail_refreshVacationNotice',$refreshData);
4376
	}
4377
4378
	/**
4379
	 * ajax_refreshFilters - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4380
	 *	Note: only the activeProfile Filters are refreshed
4381
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4382
	 *						if other than active profile; nothing is done!
4383
	 * @return nothing
4384
	 */
4385
	function ajax_refreshFilters($icServerID=null)
4386
	{
4387
		//error_log(__METHOD__.__LINE__.array2string($icServerId));
4388
		if (empty($icServerID)) $icServerID = $this->mail_bo->profileID;
4389
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4390
		{
4391
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4392
			if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4393
		}
4394 View Code Duplication
		if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
4395
		{
4396
			unset($this->searchTypes['quick']);
4397
			unset($this->searchTypes['quickwithcc']);
4398
		}
4399 View Code Duplication
		if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
4400
		{
4401
			$this->statusTypes = array_merge($this->statusTypes,array(
4402
				'keyword1'	=> 'important',//lang('important'),
4403
				'keyword2'	=> 'job',	//lang('job'),
4404
				'keyword3'	=> 'personal',//lang('personal'),
4405
				'keyword4'	=> 'to do',	//lang('to do'),
4406
				'keyword5'	=> 'later',	//lang('later'),
4407
			));
4408
		}
4409
		else
4410
		{
4411
			$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
4412
			foreach($keywords as &$k)
4413
			{
4414
				if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
4415
			}
4416
		}
4417
4418
		$response = Api\Json\Response::get();
4419
		$response->call('app.mail.mail_refreshCatIdOptions',$this->searchTypes);
4420
		$response->call('app.mail.mail_refreshFilterOptions',$this->statusTypes);
4421
		$response->call('app.mail.mail_refreshFilter2Options',array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list')));
4422
4423
	}
4424
4425
	/**
4426
	 * This function asks quota from IMAP server and makes the
4427
	 * result as JSON response to send it to mail_sendQuotaDisplay
4428
	 * function in client side.
4429
	 *
4430
	 * @param string $icServerID = null
4431
	 *
4432
	 */
4433
	function ajax_refreshQuotaDisplay($icServerID=null)
4434
	{
4435
		Api\Translation::add_app('mail');
4436
		if (is_null($icServerID)) $icServerID = $this->mail_bo->profileID;
4437
		$rememberServerID = $this->mail_bo->profileID;
4438
		try
4439
		{
4440
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4441
			{
4442
				$this->changeProfile($icServerID);
4443
			}
4444
			$quota = $this->mail_bo->getQuotaRoot();
4445
		} catch (Exception $e) {
4446
			$quota['limit'] = 'NOT SET';
4447
			error_log(__METHOD__.__LINE__." ".$e->getMessage());
4448
			unset($e);
4449
		}
4450
4451
		if($quota !== false && $quota['limit'] != 'NOT SET') {
4452
			$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
4453
			$quotaMin = $quotainfo['freespace']/pow(1024, 2);
4454
			$content = array (
4455
				'quota'				=> $quotainfo['text'],
4456
				'quotainpercent'	=> (string)$quotainfo['percent'],
4457
				'quotaclass'		=> $quotainfo['class'],
4458
				'quotanotsupported'	=> "",
4459
				'profileid'			=> $icServerID,
4460
				'quotawarning'		=> $quotaMin < 30 ? true : false
4461
			);
4462
		}
4463
		else
4464
		{
4465
			$content = array (
4466
				'quota'				=> lang("Quota not provided by server"),
4467
				'quotaclass'		=> "mail_DisplayNone",
4468
				'quotanotsupported'	=> "mail_DisplayNone"
4469
			);
4470
		}
4471
		if ($rememberServerID != $this->mail_bo->profileID)
4472
		{
4473
			try
4474
			{
4475
				$this->changeProfile($rememberServerID);
4476
			} catch (Exception $e) {
4477
				unset($e);
4478
			}
4479
		}
4480
		$response = Api\Json\Response::get();
4481
		$response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content));
4482
	}
4483
4484
	/**
4485
	 * Empty spam/junk folder
4486
	 *
4487
	 * @param string $icServerID id of the server to empty its junkFolder
4488
	 * @param string $selectedFolder seleted(active) folder by nm filter
4489
	 * @return nothing
4490
	 */
4491 View Code Duplication
	function ajax_emptySpam($icServerID, $selectedFolder)
4492
	{
4493
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4494
		Api\Translation::add_app('mail');
4495
		$response = Api\Json\Response::get();
4496
		$rememberServerID = $this->mail_bo->profileID;
4497
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4498
		{
4499
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4500
			$this->changeProfile($icServerID);
4501
		}
4502
		$junkFolder = $this->mail_bo->getJunkFolder();
4503
		if(!empty($junkFolder)) {
4504
			if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4505
			{
4506
				// Lock the tree if the active folder is junk folder
4507
				$response->call('app.mail.lock_tree');
4508
			}
4509
			$this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately');
4510
4511
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4512
			$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.
Loading history...
4513
			$fStatus = array(
4514
				$icServerID.self::$delimiter.$junkFolder => lang($fShortName)
4515
			);
4516
			//Call to reset folder status counter, after junkFolder triggered not from Junk folder
4517
			//-as we don't have junk folder specific information available on client-side we need to deal with it on server
4518
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4519
		}
4520
		if ($rememberServerID != $this->mail_bo->profileID)
4521
		{
4522
			$oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false);
4523
			$response->call('egw.message',lang('empty junk'));
4524
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName']));
4525
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4526
			$this->changeProfile($rememberServerID);
4527
		}
4528
		else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4529
		{
4530
			$response->call('egw.refresh',lang('empty junk'),'mail');
4531
		}
4532
	}
4533
4534
	/**
4535
	 * Empty trash folder
4536
	 *
4537
	 * @param string $icServerID id of the server to empty its trashFolder
4538
	 * @param string $selectedFolder seleted(active) folder by nm filter
4539
	 * @return nothing
4540
	 */
4541 View Code Duplication
	function ajax_emptyTrash($icServerID, $selectedFolder)
4542
	{
4543
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4544
		Api\Translation::add_app('mail');
4545
		$response = Api\Json\Response::get();
4546
		$rememberServerID = $this->mail_bo->profileID;
4547
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4548
		{
4549
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4550
			$this->changeProfile($icServerID);
4551
		}
4552
		$trashFolder = $this->mail_bo->getTrashFolder();
4553
		if(!empty($trashFolder)) {
4554
			if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4555
			{
4556
				// Lock the tree if the active folder is Trash folder
4557
				$response->call('app.mail.lock_tree');
4558
			}
4559
			$this->mail_bo->compressFolder($trashFolder);
4560
4561
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4562
			$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.
Loading history...
4563
			$fStatus = array(
4564
				$icServerID.self::$delimiter.$trashFolder => lang($fShortName)
4565
			);
4566
			//Call to reset folder status counter, after emptyTrash triggered not from Trash folder
4567
			//-as we don't have trash folder specific information available on client-side we need to deal with it on server
4568
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4569
		}
4570
		if ($rememberServerID != $this->mail_bo->profileID)
4571
		{
4572
			$oldFolderInfo = $this->mail_bo->getFolderStatus($trashFolder,false,false,false);
4573
			$response->call('egw.message',lang('empty trash'));
4574
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$trashFolder=>$oldFolderInfo['shortDisplayName']));
4575
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4576
			$this->changeProfile($rememberServerID);
4577
		}
4578
		else if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4579
		{
4580
			$response->call('egw.refresh',lang('empty trash'),'mail');
4581
		}
4582
	}
4583
4584
	/**
4585
	 * compress folder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4586
	 * fetches the current folder from session and compresses it
4587
	 * @param string $_folderName id of the folder to compress
4588
	 * @return nothing
4589
	 */
4590
	function ajax_compressFolder($_folderName)
4591
	{
4592
		//error_log(__METHOD__.__LINE__.' '.$_folderName);
4593
		Api\Translation::add_app('mail');
4594
4595
		$this->mail_bo->restoreSessionData();
4596
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4597
		list($icServerID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4598
4599
		if (empty($folderName)) $folderName = $this->mail_bo->sessionData['mailbox'];
4600
		if ($this->mail_bo->folderExists($folderName))
4601
		{
4602
			$rememberServerID = $this->mail_bo->profileID;
4603
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4604
			{
4605
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4606
				$this->changeProfile($icServerID);
4607
			}
4608
			if(!empty($_folderName)) {
4609
				$this->mail_bo->compressFolder($folderName);
4610
			}
4611
			if ($rememberServerID != $this->mail_bo->profileID)
4612
			{
4613
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4614
				$this->changeProfile($rememberServerID);
4615
			}
4616
			$response = Api\Json\Response::get();
4617
			$response->call('egw.refresh',lang('compress folder').': '.$folderName,'mail');
4618
		}
4619
	}
4620
4621
	/**
4622
	 * sendMDN, ...
4623
	 *
4624
	 * @param array _messageList list of UID's
4625
	 *
4626
	 * @return nothing
4627
	 */
4628
	function ajax_sendMDN($_messageList)
4629
	{
4630
		if(Mail::$debug) error_log(__METHOD__."->".array2string($_messageList));
4631
		$uidA = self::splitRowID($_messageList['msg'][0]);
4632
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4633
		$this->mail_bo->sendMDN($uidA['msgUID'],$folder);
4634
	}
4635
4636
	/**
4637
	 * flag messages as read, unread, flagged, ...
4638
	 *
4639
	 * @param string _flag name of the flag
4640
	 * @param array _messageList list of UID's
4641
	 * @param bool _sendJsonResponse tell fuction to send the JsonResponse
4642
	 *
4643
	 * @return xajax response
4644
	 */
4645
	function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
4646
	{
4647
		if(Mail::$debug) error_log(__METHOD__."->".$_flag.':'.array2string($_messageList));
4648
		Api\Translation::add_app('mail');
4649
		$alreadyFlagged=false;
4650
		$flag2check='';
4651
		$filter2toggle = $query = array();
4652
		if ($_messageList=='all' || !empty($_messageList['msg']))
4653
		{
4654
			if (isset($_messageList['all']) && $_messageList['all'])
4655
			{
4656
				// we have both messageIds AND allFlag folder information
4657
				$uidA = self::splitRowID($_messageList['msg'][0]);
4658
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4659
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4660
				{
4661
					$query = $_messageList['activeFilters'];
4662
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4663
					{
4664
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4665
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4666
						{
4667
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4668
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4669
						}
4670
						//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
4671
						$cutoffdate = $cutoffdate2 = null;
4672
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4673
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4674
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4675
						$filter = array(
4676
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4677
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4678
							'string' => $query['search'],
4679
							'status' => 'any',//this is a status change. status will be manipulated later on
4680
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4681
						);
4682
						if ($query['enddate']||$query['startdate']) {
4683
							$filter['range'] = "BETWEEN";
4684
							if ($cutoffdate) {
4685
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4686
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4687
							}
4688
							if ($cutoffdate2) {
4689
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4690
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4691
							}
4692
						}
4693
						$filter2toggle = $filter;
4694
					}
4695
					else
4696
					{
4697
						$filter = $filter2toggle = array();
4698
					}
4699
					// flags read,flagged,label1,label2,label3,label4,label5 can be toggled: handle this when all mails in a folder
4700
					// should be affected serverside. here.
4701
					$messageList = $messageListForToggle = array();
4702
					$flag2check = ($_flag=='read'?'seen':$_flag);
4703
					if (in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4704
						!($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))
4705
					{
4706
						$filter2toggle['status'] = array('un'.$_flag);
4707 View Code Duplication
						if ($query['filter'] && $query['filter']!='any')
4708
						{
4709
							$filter2toggle['status'][] = $query['filter'];
4710
						}
4711
						$rByUid = true;
4712
						$reverse = 1;
4713
						$_sRt = $this->mail_bo->getSortedList(
4714
							$folder,
4715
							$sort=0,
4716
							$reverse,
4717
							$filter2toggle,
4718
							$rByUid,
4719
							false
4720
						);
4721
						$messageListForToggle = $_sRt['match']->ids;
4722
						$filter['status'] = array($_flag);
4723 View Code Duplication
						if ($query['filter'] && $query['filter'] !='any')
4724
						{
4725
							$filter['status'][] = $query['filter'];
4726
						}
4727
						$rByUid=true;
4728
						$reverse = 1;
4729
						$_sR = $this->mail_bo->getSortedList(
4730
							$folder,
4731
							$sort=0,
4732
							$reverse,
4733
							$filter,
4734
							$rByUid,
4735
							false
4736
						);
4737
						$messageList = $_sR['match']->ids;
4738 View Code Duplication
						if (count($messageListForToggle)>0)
4739
						{
4740
							$flag2set = (strtolower($_flag));
4741
							if(Mail::$debug) error_log(__METHOD__.__LINE__." toggle un$_flag -> $flag2set ".array2string($filter2toggle).array2string($messageListForToggle));
4742
							$this->mail_bo->flagMessages($flag2set, $messageListForToggle,$folder);
4743
						}
4744 View Code Duplication
						if (count($messageList)>0)
4745
						{
4746
							$flag2set = 'un'.$_flag;
4747
							if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag -> $flag2set ".array2string($filter).array2string($messageList));
4748
							$this->mail_bo->flagMessages($flag2set, $messageList,$folder);
4749
						}
4750
						$alreadyFlagged=true;
4751
					}
4752
					elseif (!empty($filter) &&
4753
						(!in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) ||
4754
						(in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4755
						($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))))
4756
					{
4757
						if ($query['filter'] && $query['filter'] !='any')
4758
						{
4759
							$filter['status'] = $query['filter'];
4760
							// since we toggle and we toggle by the filtered flag we must must change _flag
4761
							$_flag = ($query['filter']=='unseen' && $_flag=='read' ? 'read' : ($query['filter']=='seen'&& $_flag=='read'?'unread':($_flag==$query['filter']?'un'.$_flag:$_flag)));
4762
						}
4763
						if(Mail::$debug) error_log(__METHOD__.__LINE__." flag all with $_flag on filter used:".array2string($filter));
4764
						$rByUid = true;
4765
						$reverse = 1;
4766
						$_sR = $this->mail_bo->getSortedList(
4767
							$folder,
4768
							$sort=0,
4769
							$reverse,
4770
							$filter,
4771
							$rByUid,
4772
							false
4773
						);
4774
						$messageList = $_sR['match']->ids;
4775
						unset($_messageList['all']);
4776
						$_messageList['msg'] = array();
4777
					}
4778
					else
4779
					{
4780
						if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag all ".array2string($filter));
4781
						$alreadyFlagged=true;
4782
						$uidA = self::splitRowID($_messageList['msg'][0]);
4783
						$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4784
						$this->mail_bo->flagMessages($_flag, 'all', $folder);
4785
					}
4786
				}
4787
			}
4788
			else
4789
			{
4790
				$uidA = self::splitRowID($_messageList['msg'][0]);
4791
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4792
			}
4793
			if (!$alreadyFlagged)
4794
			{
4795 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4796
				{
4797
					$hA = self::splitRowID($rowID);
4798
					$messageList[] = $hA['msgUID'];
4799
				}
4800 View Code Duplication
				if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag in $folder:".array2string(((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList)));
4801
				$this->mail_bo->flagMessages($_flag, ((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList),$folder);
4802
			}
4803
		}
4804
		else
4805
		{
4806
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4807
		}
4808
4809
		if ($_sendJsonResponse)
4810
		{
4811
			$flag=array(
4812
				'label1'	=> 'important',//lang('important'),
4813
				'label2'	=> 'job',	//lang('job'),
4814
				'label3'	=> 'personal',//lang('personal'),
4815
				'label4'	=> 'to do',	//lang('to do'),
4816
				'label5'	=> 'later',	//lang('later'),
4817
			);
4818
			$response = Api\Json\Response::get();
4819
			if (isset($_messageList['msg']) && $_messageList['popup'])
4820
			{
4821
				$response->call('egw.refresh',lang('flagged %1 messages as %2 in %3',$_messageList['msg'],lang(($flag[$_flag]?$flag[$_flag]:$_flag)),$folder),'mail', $_messageList['msg'], 'update');
4822
			}
4823
			else if ((isset($_messageList['all']) && $_messageList['all']) || ($query['filter'] && ($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false)))
4824
			{
4825
				$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)),$folder),'mail');
4826
			}
4827
			else
4828
			{
4829
				$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)),$folder));
4830
			}
4831
		}
4832
	}
4833
4834
	/**
4835
	 * delete messages
4836
	 *
4837
	 * @param array _messageList list of UID's
4838
	 * @param string _forceDeleteMethod - method of deletion to be enforced
4839
	 * @return xajax response
4840
	 */
4841
	function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
4842
	{
4843
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageList,true).' Method:'.$_forceDeleteMethod);
4844
		$error = null;
4845
		$filtered =  false;
4846
		if ($_messageList=='all' || !empty($_messageList['msg']))
4847
		{
4848
			if (isset($_messageList['all']) && $_messageList['all'])
4849
			{
4850
				// we have both messageIds AND allFlag folder information
4851
				$uidA = self::splitRowID($_messageList['msg'][0]);
4852
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4853
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4854
				{
4855
					$query = $_messageList['activeFilters'];
4856 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4857
					{
4858
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4859
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4860
						{
4861
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4862
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4863
						}
4864
						$filtered =  true;
4865
						$cutoffdate = $cutoffdate2 = null;
4866
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4867
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4868
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4869
						$filter = array(
4870
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4871
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4872
							'string' => $query['search'],
4873
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4874
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4875
						);
4876
						if ($query['enddate']||$query['startdate']) {
4877
							$filter['range'] = "BETWEEN";
4878
							if ($cutoffdate) {
4879
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4880
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4881
							}
4882
							if ($cutoffdate2) {
4883
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4884
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4885
							}
4886
						}
4887
					}
4888
					else
4889
					{
4890
						$filter = array();
4891
					}
4892
					//error_log(__METHOD__.__LINE__."->".print_r($filter,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4893
					$reverse = 1;
4894
					$rByUid = true;
4895
					$_sR = $this->mail_bo->getSortedList(
4896
						$folder,
4897
						$sort=0,
4898
						$reverse,
4899
						$filter,
4900
						$rByUid,
4901
						false
4902
					);
4903
					$messageList = $_sR['match']->ids;
4904
				}
4905
				else
4906
				{
4907
					$messageList='all';
4908
				}
4909
				try
4910
				{
4911
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4912
					$this->mail_bo->deleteMessages(($messageList=='all' ? 'all':$messageList),$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4913
				}
4914
				catch (Api\Exception $e)
4915
				{
4916
					$error = str_replace('"',"'",$e->getMessage());
4917
				}
4918
			}
4919
			else
4920
			{
4921
				$uidA = self::splitRowID($_messageList['msg'][0]);
4922
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4923 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4924
				{
4925
					$hA = self::splitRowID($rowID);
4926
					$messageList[] = $hA['msgUID'];
4927
				}
4928
				try
4929
				{
4930
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4931
					$this->mail_bo->deleteMessages($messageList,$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4932
				}
4933
				catch (Api\Exception $e)
4934
				{
4935
					$error = str_replace('"',"'",$e->getMessage());
4936
				}
4937
			}
4938
			$response = Api\Json\Response::get();
4939
			if (empty($error))
4940
			{
4941
				$response->call('app.mail.mail_deleteMessagesShowResult',array('egw_message'=>lang('deleted %1 messages in %2',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($_messageList['msg'])),$folder),'msg'=>$_messageList['msg']));
4942
			}
4943
			else
4944
			{
4945
				$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));
4946
				$response->call('app.mail.mail_retryForcedDelete',array('response'=>$error,'messageList'=>$_messageList));
4947
			}
4948
		}
4949
		else
4950
		{
4951
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4952
		}
4953
	}
4954
4955
	/**
4956
	 * copy messages
4957
	 *
4958
	 * @param array _folderName target folder
4959
	 * @param array _messageList list of UID's
4960
	 * @param string _copyOrMove method to use copy or move allowed
4961
	 * @param string _move2ArchiveMarker marker to indicate if a move 2 archive was triggered
4962
	 *
4963
	 * @return xajax response
4964
	 */
4965
	function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_move2ArchiveMarker='_')
4966
	{
4967
		if(Mail::$debug) error_log(__METHOD__."->".$_folderName.':'.print_r($_messageList,true).' Method:'.$_copyOrMove.' ArchiveMarker:'.$_move2ArchiveMarker);
4968
		Api\Translation::add_app('mail');
4969
		$folderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4970
		// only copy or move are supported as method
4971
		if (!($_copyOrMove=='copy' || $_copyOrMove=='move')) $_copyOrMove='copy';
4972
		list($targetProfileID,$targetFolder) = explode(self::$delimiter,$folderName,2);
4973
		// check if move2archive was called with the correct archiveFolder
4974
		$archiveFolder = $this->mail_bo->getArchiveFolder();
4975
		if ($_move2ArchiveMarker=='2' && $targetFolder != $archiveFolder)
4976
		{
4977
			error_log(__METHOD__.__LINE__."#Move to Archive called with:"."$targetProfileID,$targetFolder");
4978
			$targetProfileID = $this->mail_bo->profileID;
4979
			$targetFolder = $archiveFolder;
4980
			error_log(__METHOD__.__LINE__."#Fixed ArchiveFolder:"."$targetProfileID,$targetFolder");
4981
		}
4982
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
4983
		$changeFolderActions = false;
4984
		//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder");
4985
		//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
4986
		if (!isset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]))
4987
		{
4988
			//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]));
4989
			if ($lastFoldersUsedForMoveCont[$targetProfileID] && count($lastFoldersUsedForMoveCont[$targetProfileID])>3)
4990
			{
4991
				$keys = array_keys($lastFoldersUsedForMoveCont[$targetProfileID]);
4992
				foreach( $keys as &$f)
4993
				{
4994
					if (count($lastFoldersUsedForMoveCont[$targetProfileID])>9) unset($lastFoldersUsedForMoveCont[$targetProfileID][$f]);
4995
					else break;
4996
				}
4997
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID]));
4998
			}
4999
			//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder = $_folderName");
5000
			$lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]=$folderName;
5001
			$changeFolderActions = true;
5002
		}
5003
		$filtered = false;
5004
		if ($_messageList=='all' || !empty($_messageList['msg']))
5005
		{
5006
			$error=false;
5007
			if (isset($_messageList['all']) && $_messageList['all'])
5008
			{
5009
				// we have both messageIds AND allFlag folder information
5010
				$uidA = self::splitRowID($_messageList['msg'][0]);
5011
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5012
				$sourceProfileID = $uidA['profileID'];
5013
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
5014
				{
5015
					$query = $_messageList['activeFilters'];
5016 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
5017
					{
5018
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
5019
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
5020
						{
5021
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
5022
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
5023
						}
5024
						$filtered = true;
5025
						$cutoffdate = $cutoffdate2 = null;
5026
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
5027
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
5028
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
5029
						$filter = array(
5030
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
5031
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
5032
							'string' => $query['search'],
5033
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
5034
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
5035
						);
5036
						if ($query['enddate']||$query['startdate']) {
5037
							$filter['range'] = "BETWEEN";
5038
							if ($cutoffdate) {
5039
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
5040
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
5041
							}
5042
							if ($cutoffdate2) {
5043
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
5044
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
5045
							}
5046
						}
5047
					}
5048
					else
5049
					{
5050
						$filter = array();
5051
					}
5052
					$reverse = 1;
5053
					$rByUid = true;
5054
					$_sR = $this->mail_bo->getSortedList(
5055
						$folder,
5056
						$sort=0,
5057
						$reverse,
5058
						$filter,
5059
						$rByUid=true,
0 ignored issues
show
Bug introduced by
$rByUid = true cannot be passed to getsortedlist() as the parameter $resultByUid expects a reference.
Loading history...
5060
						false
5061
					);
5062
					$messageList = $_sR['match']->ids;
5063
					foreach($messageList as $uID)
5064
					{
5065
						//error_log(__METHOD__.__LINE__.$uID);
5066
						if ($_copyOrMove=='move')
5067
						{
5068
							$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false);
5069
						}
5070
					}
5071
				}
5072
				else
5073
				{
5074
					$messageList='all';
5075
				}
5076
				try
5077
				{
5078
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
5079
					$this->mail_bo->moveMessages($targetFolder,$messageList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
5080
				}
5081
				catch (Api\Exception $e)
5082
				{
5083
					$error = str_replace('"',"'",$e->getMessage());
5084
				}
5085
			}
5086
			else
5087
			{
5088
				$messageList = array();
5089
				while(count($_messageList['msg']) > 0)
5090
				{
5091
					$uidA = self::splitRowID($_messageList['msg'][0]);
5092
					$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
5093
					$sourceProfileID = $uidA['profileID'];
5094
					$moveList = array();
5095
					foreach($_messageList['msg'] as $rowID)
5096
					{
5097
						$hA = self::splitRowID($rowID);
5098
5099
						// If folder changes, stop and move what we've got
5100
						if($hA['folder'] != $folder) break;
5101
5102
						array_shift($_messageList['msg']);
5103
						$messageList[] = $hA['msgUID'];
5104
						$moveList[] = $hA['msgUID'];
5105
						if ($_copyOrMove=='move')
5106
						{
5107
							$helpvar = explode(self::$delimiter,$rowID);
5108
							array_shift($helpvar);
5109
							$messageListForRefresh[]= implode(self::$delimiter,$helpvar);
5110
						}
5111
					}
5112
					try
5113
					{
5114
						//error_log(__METHOD__.__LINE__."->".print_r($moveList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
5115
						$this->mail_bo->moveMessages($targetFolder,$moveList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
5116
					}
5117
					catch (Api\Exception $e)
5118
					{
5119
						$error = str_replace('"',"'",$e->getMessage());
5120
					}
5121
				}
5122
			}
5123
5124
			$response = Api\Json\Response::get();
5125
			if ($error)
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type false|string is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
5126
			{
5127
				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...
5128
				{
5129
					unset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]);
5130
					$changeFolderActions = true;
5131
				}
5132
				$response->call('egw.message',$error,"error");
5133
			}
5134
			else
5135
			{
5136
				if ($_copyOrMove=='copy')
5137
				{
5138
					$response->call('egw.message',lang('copied %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),$folder,$targetFolder));
5139
				}
5140
				else
5141
				{
5142
					$response->call('egw.refresh',lang('moved %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),$folder,$targetFolder),'mail',$messageListForRefresh,'delete');
5143
				}
5144
			}
5145
			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...
5146
			{
5147
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5148
				Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
5149
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::get_actions());
5150
				$response->call('app.mail.mail_rebuildActionsOnList',$actionsnew);
5151
			}
5152
		}
5153
		else
5154
		{
5155
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5156
		}
5157
	}
5158
5159
	/**
5160
	 * Autoloading function to load branches of tree node
5161
	 * of management folder tree
5162
	 *
5163
	 * @param type $_id
5164
	 */
5165
	function ajax_folderMgmtTree_autoloading ($_id = null)
5166
	{
5167
		$mail_ui = new mail_ui();
5168
		$id = $_id? $_id : $_GET['id'];
5169
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,true,false,false,false));
5170
	}
5171
5172
	/**
5173
	 * Main function to handle folder management dialog
5174
	 *
5175
	 * @param array $content content of dialog
5176
	 */
5177
	function folderManagement (array $content = null)
5178
	{
5179
		$dtmpl = new Etemplate('mail.folder_management');
5180
		$profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
5181
		$sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false);
5182
5183
		if (!is_array($content))
5184
		{
5185
			$content = array ('acc_id' => $profileID);
5186
		}
5187
5188
		$readonlys = array();
5189
		// Preserv
5190
		$preserv = array(
5191
			'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side
5192
		);
5193
		$dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2);
5194
	}
5195
5196
	/**
5197
	 * Function to delete folder for management longTask dialog
5198
	 * it sends successfully deleted folder as response to be
5199
	 * used in long task response handler.
5200
	 *
5201
	 * @param type $_folderName
5202
	 */
5203
	function ajax_folderMgmt_delete ($_folderName)
5204
	{
5205
		if ($_folderName)
5206
		{
5207
			$success = $this->ajax_deleteFolder($_folderName,true);
5208
			$response = Api\Json\Response::get();
5209
			list(,$folderName) = explode(self::$delimiter, $_folderName);
5210
			if ($success)
5211
			{
5212
				$res = $folderName;
5213
			}
5214
			else
5215
			{
5216
				$res = lang("Failed to delete %1",$folderName);
5217
			}
5218
			$response->data($res);
5219
		}
5220
	}
5221
}
5222