Completed
Push — master ( 60c34e...a41896 )
by Klaus
22:56
created

mail_ui::getdisplayableBody()   F

Complexity

Conditions 25
Paths 292

Size

Total Lines 197
Code Lines 88

Duplication

Lines 41
Ratio 20.81 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 25
eloc 88
c 1
b 1
f 0
nc 292
nop 3
dl 41
loc 197
rs 3.5466

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 Stylite AG [[email protected]]
8
 * @copyright (c) 2013-2016 by Stylite AG <info-AT-stylite.de>
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;
1 ignored issue
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
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)
1 ignored issue
show
Bug Best Practice introduced by
The expression $msg of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 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...
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
593
		if ($this->mail_bo->mailPreferences['previewPane']) $etpl->setElementAttribute('splitter', 'template', 'mail.index.nosplitter');
594
595
		return $etpl->exec('mail.mail_ui.index',$content,$sel_options,$readonlys,$preserv);
596
	}
597
598
	/**
599
	 * Get tree actions / context menu for tree
600
	 *
601
	 * Changes here, may require to log out, as $content[self::$nm_index] get stored in session!
602
	 * @param {boolean} $imap_actions set to false if you want to avoid to talk to the imap-server
603
	 * @return array
604
	 */
605
	function get_tree_actions($imap_actions=true)
606
	{
607
		// Start at 2 so auto-added copy+paste actions show up as second group
608
		// Needed because there's no 'select all' action to push things down
609
		$group=1;
610
		// Set tree actions
611
		$tree_actions = array(
612
			'drop_move_mail' => array(
613
				'type' => 'drop',
614
				'acceptedTypes' => 'mail',
615
				'icon' => 'move',
616
				'caption' => 'Move to',
617
				'onExecute' => 'javaScript:app.mail.mail_move'
618
			),
619
			'drop_copy_mail' => array(
620
				'type' => 'drop',
621
				'acceptedTypes' => 'mail',
622
				'icon' => 'copy',
623
				'caption' => 'Copy to',
624
				'onExecute' => 'javaScript:app.mail.mail_copy'
625
			),
626
			'drop_cancel' => array(
627
				'icon' => 'cancel',
628
				'caption' => 'Cancel',
629
				'acceptedTypes' => 'mail',
630
				'type' => 'drop',
631
			),
632
			'drop_move_folder' => array(
633
				'caption' => 'Move folder',
634
				'hideOnDisabled' => true,
635
				'type' => 'drop',
636
				'acceptedTypes' => 'mailFolder',
637
				'onExecute' => 'javaScript:app.mail.mail_MoveFolder'
638
			),
639
			// Tree does support this one
640
			'add' => array(
641
				'caption' => 'Add Folder',
642
				'onExecute' => 'javaScript:app.mail.mail_AddFolder',
643
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
644
				'group'		=> $group,
645
			),
646
			'edit' => array(
647
				'caption' => 'Rename Folder',
648
				'onExecute' => 'javaScript:app.mail.mail_RenameFolder',
649
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
650
				'group'		=> $group,
651
			),
652
			'move' => array(
653
				'caption' => 'Move Folder',
654
				'type' => 'drag',
655
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
656
				'dragType' => array('mailFolder'),
657
				'group'		=> $group,
658
			),
659
			'delete' => array(
660
				'caption' => 'Delete Folder',
661
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
662
				'onExecute' => 'javaScript:app.mail.mail_DeleteFolder',
663
				'group'		=> $group,
664
			),
665
			'subscribe' => array(
666
				'caption' => 'Subscribe folder ...',
667
				//'icon' => 'configure',
668
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
669
				'onExecute' => 'javaScript:app.mail.edit_subscribe',
670
				'group'		=> $group
671
			),
672
			'unsubscribe' => array(
673
				'caption' => 'Unsubscribe folder',
674
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
675
				'onExecute' => 'javaScript:app.mail.unsubscribe_folder',
676
				'group'		=> $group,
677
			),
678
			'foldermanagement' => array(
679
				'caption' => 'Folder Management ...',
680
				'icon' => 'folder_management',
681
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
682
				'onExecute' => 'javaScript:app.mail.folderManagement',
683
				'group'		=> $group,
684
				'hideOnMobile' => true
685
			),
686
			'sieve' => array(
687
				'caption' => 'Mail filter',
688
				'onExecute' => 'javaScript:app.mail.edit_sieve',
689
690
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
691
				'icon' => 'mail/filter',	// funnel
692
				'hideOnMobile' => true
693
			),
694
			'vacation' => array(
695
				'caption' => 'Vacation notice',
696
				'icon' => 'mail/navbar',	// mail as in admin
697
				'onExecute' => 'javaScript:app.mail.edit_vacation',
698
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
699
			),
700
			'edit_account' => array(
701
				'caption' => 'Edit account ...',
702
				'icon' => 'configure',
703
				'onExecute' => 'javaScript:app.mail.edit_account',
704
			),
705
			'edit_acl'	=> array(
706
				'caption' => 'Edit folder ACL ...',
707
				'icon'	=> 'lock',
708
				'enabled'	=> 'javaScript:app.mail.acl_enabled',
709
				'onExecute' => 'javaScript:app.mail.edit_acl',
710
			),
711
		);
712
		// the preference prefaskformove controls actually if there is a popup on target or not
713
		// if there are multiple options there is a popup on target, 0 for prefaskformove means
714
		// that only move is available; 1 stands for move and cancel; 2 (should be the default if
715
		// not set); so we are assuming this, when not set
716
		if (isset($this->mail_bo->mailPreferences['prefaskformove']))
717
		{
718
			switch ($this->mail_bo->mailPreferences['prefaskformove'])
719
			{
720
				case 0:
721
					unset($tree_actions['drop_copy_mail']);
722
					unset($tree_actions['drop_cancel']);
723
					break;
724
				case 1:
725
					unset($tree_actions['drop_copy_mail']);
726
					break;
727
				default:
728
					// everything is fine
729
			}
730
		}
731
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
732
		if ($this->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
733
		{
734
			unset($tree_actions['subscribe']);
735
			unset($tree_actions['unsubscribe']);
736
		}
737
		++$group;	// put delete in own group
738
		switch($GLOBALS['egw_info']['user']['preferences']['mail']['deleteOptions'])
739
		{
740
			case 'move_to_trash':
741
				$tree_actions['empty_trash'] = array(
742
					'caption' => 'empty trash',
743
					'icon' => 'dhtmlxtree/MailFolderTrash',
744
					'onExecute' => 'javaScript:app.mail.mail_emptyTrash',
745
					'group'	=> $group,
746
				);
747
				break;
748
			case 'mark_as_deleted':
749
				$tree_actions['compress_folder'] = array(
750
					'caption' => 'compress folder',
751
					'icon' => 'dhtmlxtree/MailFolderTrash',
752
					'onExecute' => 'javaScript:app.mail.mail_compressFolder',
753
					'group'	=> $group,
754
				);
755
				break;
756
		}
757
		$junkFolder = ($imap_actions?$this->mail_bo->getJunkFolder():null);
758
759
		//error_log(__METHOD__.__LINE__.$junkFolder);
760
		if ($junkFolder && !empty($junkFolder))
761
		{
762
			$tree_actions['empty_spam'] = array(
763
				'caption' => 'empty junk',
764
				'icon' => 'dhtmlxtree/MailFolderJunk',
765
				'enabled'	=> 'javaScript:app.mail.spamfolder_enabled',
766
				'onExecute' => 'javaScript:app.mail.mail_emptySpam',
767
				'group'	=> $group,
768
			);
769
		}
770
		$tree_actions['sieve']['group']	= $tree_actions['vacation']['group'] = ++$group;	// new group for filter
771
		$tree_actions['edit_account']['group'] = $tree_actions['edit_acl']['group']	= ++$group;
772
773
774
		// enforce global (group-specific) ACL
775
		if (!mail_hooks::access('aclmanagement'))
776
		{
777
			unset($tree_actions['edit_acl']);
778
		}
779
		if (!mail_hooks::access('editfilterrules'))
780
		{
781
			unset($tree_actions['sieve']);
782
		}
783
		if (!mail_hooks::access('absentnotice'))
784
		{
785
			unset($tree_actions['vacation']);
786
		}
787
		if (!mail_hooks::access('managefolders'))
788
		{
789
			unset($tree_actions['add']);
790
			unset($tree_actions['move']);
791
			unset($tree_actions['delete']);
792
			unset($tree_actions['foldermanagement']);
793
			// manage folders should not affect the ability to subscribe or unsubscribe
794
			// to existing folders, it should only affect add/rename/move/delete
795
		}
796
		return $tree_actions;
797
	}
798
799
	/**
800
	 * Ajax callback to subscribe / unsubscribe a Mailbox of an account
801
	 *
802
	 * @param {int} $_acc_id profile Id of selected mailbox
803
	 * @param {string} $_folderName name of mailbox needs to be subcribe or unsubscribed
804
	 * @param {boolean} $_status set true for subscribe and false to unsubscribe
805
	 */
806
	public function ajax_foldersubscription($_acc_id,$_folderName, $_status)
807
	{
808
		//Change the Mail object to related profileId
809
		$this->changeProfile($_acc_id);
810
		try{
811
			$this->mail_bo->icServer->subscribeMailbox($_folderName, $_status);
812
			$this->mail_bo->resetFolderObjectCache($_acc_id);
813
			$this->ajax_reloadNode($_acc_id,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
814
		} 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...
815
			error_log(__METHOD__.__LINE__."()". lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));
816
			Framework::message(lang('Folder %1 %2 failed!',$_folderName,$_status));
817
		}
818
	}
819
820
	/**
821
	 * Ajax callback to fetch folders for given profile
822
	 *
823
	 * We currently load all folders of a given profile, tree can also load parts of a tree.
824
	 *
825
	 * @param string $_nodeID if of node whos children are requested
826
	 * @param boolean $_subscribedOnly flag to tell wether to fetch all or only subscribed (default)
827
	 */
828
	public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null)
829
	{
830
		$nodeID = $_GET['id'];
831
		if (!is_null($_nodeID)) $nodeID = $_nodeID;
832
		$subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
833
		$fetchCounters = !is_null($_nodeID);
834
		list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2);
835
836
		if (!empty($_folderName)) $fetchCounters = true;
837
838
		// Check if it is called for refresh root
839
		// then we need to reinitialized the index tree
840
		if(!$nodeID && !$_profileID)
841
		{
842
			$data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
843
		}
844
		else
845
		{
846
			$data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
847
		}
848
		if (!is_null($_nodeID)) return $data;
849
		Etemplate\Widget\Tree::send_quote_json($data);
850
	}
851
852
	/**
853
	 * findNode - helper function to return only a branch of the tree
854
	 *
855
	 * @param array $_out out array (to be searched)
856
	 * @param string $_nodeID node to search for
857
	 * @param boolean $childElements return node itself, or only its child items
858
	 * @return array structured subtree
859
	 */
860
	static function findNode($_out, $_nodeID, $childElements = false)
861
	{
862
		foreach($_out['item'] as $node)
863
		{
864
			if (strcmp($node['id'],$_nodeID)===0)
865
			{
866
				//error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']);
867
				return ($childElements?$node['item']:$node);
868
			}
869
			elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id']))
870
			{
871
				//error_log(__METHOD__.__LINE__.' descend into '.$node['id']);
872
				return self::findNode($node,$_nodeID,$childElements);
873
			}
874
		}
875
	}
876
877
878
	/**
879
	 * Get actions / context menu for index
880
	 *
881
	 * Changes here, require to log out, as $content[self::$nm_index] get stored in session!
882
	 * @return array see nextmatch_widget::egw_actions()
883
	 */
884
	private function get_actions()
885
	{
886
		static $accArray=array(); // buffer identity names on single request
887
		// duplicated from mail_hooks
888
		static $deleteOptions = array(
889
			'move_to_trash'		=> 'move to trash',
890
			'mark_as_deleted'	=> 'mark as deleted',
891
			'remove_immediately' =>	'remove immediately',
892
		);
893
		// todo: real hierarchical folder list
894
		$lastFolderUsedForMove = null;
895
		$moveactions = array();
896
		$archiveFolder = $this->mail_bo->getArchiveFolder();
897
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
898
		//error_log(__METHOD__.__LINE__." StoredFolders->".array2string($lastFoldersUsedForMoveCont));
899
		//error_log(__METHOD__.__LINE__.' ProfileId:'.$this->mail_bo->profileID." StoredFolders->(".count($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]).") ".array2string($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]));
900
		if (is_null($accArray))
901
		{
902
			foreach(Mail\Account::search($only_current_user=true, false) as $acc_id => $accountObj)
903
			{
904
				//error_log(__METHOD__.__LINE__.array2string($accountObj));
905
				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...
906
				{
907
					// not to be used for IMAP Foldertree, as there is no Imap host
908
					continue;
909
				}
910
				$identity_name = Mail\Account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']);
911
				$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
912
			}
913
		}
914
		if (!is_array($lastFoldersUsedForMoveCont)) $lastFoldersUsedForMoveCont=array();
915
		foreach (array_keys($lastFoldersUsedForMoveCont) as $pid)
916
		{
917
			if ($this->mail_bo->profileID==$pid && isset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]))
918
			{
919
				$_folder = $this->mail_bo->icServer->getCurrentMailbox();
920
				//error_log(__METHOD__.__LINE__.' '.$_folder."<->".$lastFoldersUsedForMoveCont[$this->mail_bo->profileID].function_backtrace());
921
				$counter =1;
922
				foreach ($lastFoldersUsedForMoveCont[$this->mail_bo->profileID] as $i => $lastFolderUsedForMoveCont)
923
				{
924
					$moveaction = 'move_';
925
					if ($_folder!=$i)
926
					{
927
						$moveaction .= $lastFolderUsedForMoveCont;
928
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
929
						//error_log(__METHOD__.__LINE__.'#'.$currentArchiveActionKey);
930
						if ($this->mail_bo->folderExists($i)) // only 10 entries per mailaccount.Control this on setting the buffered folders
931
						{
932
							$fS['profileID'] = $this->mail_bo->profileID;
933
							$fS['profileName'] = $accArray[$this->mail_bo->profileID];
934
							$fS['shortDisplayName'] = $i;
935
							$moveactions[$moveaction] = $fS;
936
							$counter ++;
937
						}
938
						else
939
						{
940
							unset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID][$i]);
941
						}
942
						//error_log(array2string($moveactions[$moveaction]));
943
					}
944
				}
945
			}
946
			elseif ($this->mail_bo->profileID!=$pid && isset($lastFoldersUsedForMoveCont[$pid]) && !empty($lastFoldersUsedForMoveCont[$pid]))
947
			{
948
				$counter =1;
949
				foreach ($lastFoldersUsedForMoveCont[$pid] as $i => $lastFolderUsedForMoveCont)
950
				{
951
					//error_log(__METHOD__.__LINE__."$i => $lastFolderUsedForMoveCont");
952
					if (!empty($lastFolderUsedForMoveCont)) // only 10 entries per mailaccount.Control this on setting the buffered folders
953
					{
954
						$moveaction = 'move_'.$lastFolderUsedForMoveCont;
955
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
956
						$fS = array();
957
						$fS['profileID'] = $pid;
958
						$fS['profileName'] = $accArray[$pid];
959
						$fS['shortDisplayName'] = $i;
960
						$moveactions[$moveaction] = $fS;
961
						$counter ++;
962
					}
963
				}
964
			}
965
		}
966
		Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
967
		$group = 0;
968
		$actions =  array(
969
			'open' => array(
970
				'caption' => lang('Open'),
971
				'icon' => 'view',
972
				'group' => ++$group,
973
				'onExecute' => Api\Header\UserAgent::mobile()?'javaScript:app.mail.mobileView':'javaScript:app.mail.mail_open',
974
				'allowOnMultiple' => false,
975
				'default' => true,
976
				'mobileViewTemplate' => 'view?'.filemtime(Api\Etemplate\Widget\Template::rel2path('/mail/templates/mobile/view.xet'))
977
			),
978
			'reply' => array(
979
				'caption' => 'Reply',
980
				'icon' => 'mail_reply',
981
				'group' => ++$group,
982
				'onExecute' => 'javaScript:app.mail.mail_compose',
983
				'allowOnMultiple' => false,
984
				'toolbarDefault' => true
985
			),
986
			'reply_all' => array(
987
				'caption' => 'Reply All',
988
				'icon' => 'mail_replyall',
989
				'group' => $group,
990
				'onExecute' => 'javaScript:app.mail.mail_compose',
991
				'allowOnMultiple' => false,
992
			),
993
			'forward' => array(
994
				'caption' => 'Forward',
995
				'icon' => 'mail_forward',
996
				'group' => $group,
997
				'children' => array(
998
					'forwardinline' => array(
999
						'caption' => 'Inline',
1000
						'icon' => 'mail_forward',
1001
						'group' => $group,
1002
						'hint' => 'forward inline',
1003
						'onExecute' => 'javaScript:app.mail.mail_compose',
1004
						'allowOnMultiple' => false,
1005
						'toolbarDefault' => true
1006
					),
1007
					'forwardasattach' => array(
1008
						'caption' => 'Attachment',
1009
						'hint' => 'forward as attachment',
1010
						'icon' => 'mail_forward_attach',
1011
						'group' => $group,
1012
						'onExecute' => 'javaScript:app.mail.mail_compose',
1013
					),
1014
				),
1015
				'hideOnMobile' => true
1016
			),
1017
			'composeasnew' => array(
1018
				'caption' => 'Compose',
1019
				'icon' => 'new',
1020
				'hint' => 'Compose as new',
1021
				'group' => $group,
1022
				'onExecute' => 'javaScript:app.mail.mail_compose',
1023
				'allowOnMultiple' => false,
1024
			)
1025
		);
1026
		$macounter=0;
1027
		if (!empty($moveactions))
1028
		{
1029
			//error_log(__METHOD__.__LINE__.array2string($moveactions));
1030
			$children=array();
1031
			$pID=0;
1032
			foreach ($moveactions as $moveaction => $lastFolderUsedForMove)
1033
			{
1034
				$group = ($pID != $lastFolderUsedForMove['profileID'] && $macounter>0? $group+1 : $group);
1035
				//error_log(__METHOD__.__LINE__."#$pID != ".$lastFolderUsedForMove['profileID']."#".$macounter.'#'.$groupCounter.'#');
1036
				$children = array_merge($children,
1037
					array(
1038
						$moveaction => array(
1039
							'caption' => (!empty($lastFolderUsedForMove['profileName'])?$lastFolderUsedForMove['profileName']:'('.$lastFolderUsedForMove['profileID'].')').': '.(isset($lastFolderUsedForMove['shortDisplayName'])?$lastFolderUsedForMove['shortDisplayName']:''),
1040
							'icon' => 'move',
1041
							'group' => $group,
1042
							'onExecute' => 'javaScript:app.mail.mail_move2folder',
1043
							'allowOnMultiple' => true,
1044
						)
1045
					)
1046
				);
1047
				$pID = $lastFolderUsedForMove['profileID'];
1048
				$macounter++;
1049
			}
1050
			$actions['moveto'] =	array(
1051
				'caption' => lang('Move selected to'),
1052
				'icon' => 'move',
1053
				'group' => $group,
1054
				'children' => $children,
1055
			);
1056
1057
		} else {
1058
			$group++;
1059
		}
1060
1061
		//error_log(__METHOD__.__LINE__.$archiveFolder);
1062
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter.$archiveFolder] = array( //toarchive
1063
			'caption' => 'Move to archive',
1064
			'hint' => 'move selected mails to archive',
1065
			'icon' => 'archive',
1066
			'group' => $group++,
1067
			'enabled' => 'javaScript:app.mail.archivefolder_enabled',
1068
			//'hideOnDisabled' => true, // does not work as expected on message-list
1069
			'onExecute' => 'javaScript:app.mail.mail_move2folder',
1070
			'shortcut' => KeyManager::shortcut(KeyManager::V, true, true),
1071
			'allowOnMultiple' => true,
1072
			'toolbarDefault' => false
1073
		);
1074
1075
		$actions += array(
1076
			'infolog' => array(
1077
				'caption' => 'InfoLog',
1078
				'hint' => 'Save as InfoLog',
1079
				'icon' => 'infolog/navbar',
1080
				'group' => ++$group,
1081
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1082
				'popup' => Link::get_registry('infolog', 'add_popup'),
1083
				'allowOnMultiple' => false,
1084
				'toolbarDefault' => true
1085
			),
1086
			'tracker' => array(
1087
				'caption' => 'Tracker',
1088
				'hint' => 'Save as ticket',
1089
				'group' => $group,
1090
				'icon' => 'tracker/navbar',
1091
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1092
				'popup' => Link::get_registry('tracker', 'add_popup'),
1093
				'mail_import' => Api\Hooks::single(array('location' => 'mail_import'),'tracker'),
1094
				'allowOnMultiple' => false,
1095
			),
1096
			'calendar' => array(
1097
				'caption' => 'Calendar',
1098
				'hint' => 'Save as Calendar',
1099
				'icon' => 'calendar/navbar',
1100
				'group' => $group,
1101
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1102
				'popup' => Link::get_registry('calendar', 'add_popup'),
1103
				'allowOnMultiple' => false,
1104
				'toolbarDefault' => true
1105
			),
1106
			'print' => array(
1107
				'caption' => 'Print',
1108
				'group' => ++$group,
1109
				'onExecute' => 'javaScript:app.mail.mail_print',
1110
				'allowOnMultiple' => false,
1111
				'hideOnMobile' => true
1112
			),
1113
			'save' => array(
1114
				'caption' => 'Save',
1115
				'group' => $group,
1116
				'icon' => 'fileexport',
1117
				'children' => array(
1118
					'save2disk' => array(
1119
						'caption' => 'Save to disk',
1120
						'hint' => 'Save message to disk',
1121
						'group' => $group,
1122
						'icon' => 'fileexport',
1123
						'onExecute' => 'javaScript:app.mail.mail_save',
1124
						'allowOnMultiple' => false,
1125
						'hideOnMobile' => true
1126
					),
1127
					'save2filemanager' => array(
1128
						'caption' => 'Filemanager',
1129
						'hint' => 'Save message to filemanager',
1130
						'group' => $group,
1131
						'icon' => 'filemanager/navbar',
1132
						'onExecute' => 'javaScript:app.mail.mail_save2fm',
1133
						'allowOnMultiple' => false,
1134
					),
1135
				),
1136
				'hideOnMobile' => true
1137
			),
1138
			'view' => array(
1139
				'caption' => 'View',
1140
				'group' => $group,
1141
				'icon' => 'kmmsgread',
1142
				'children' => array(
1143
					'header' => array(
1144
						'caption' => 'Header',
1145
						'hint' => 'View header lines',
1146
						'group' => $group,
1147
						'icon' => 'kmmsgread',
1148
						'onExecute' => 'javaScript:app.mail.mail_header',
1149
						'allowOnMultiple' => false,
1150
					),
1151
					'mailsource' => array(
1152
						'caption' => 'Source',
1153
						'hint' => 'View full Mail Source',
1154
						'group' => $group,
1155
						'icon' => 'source',
1156
						'onExecute' => 'javaScript:app.mail.mail_mailsource',
1157
						'allowOnMultiple' => false,
1158
					),
1159
					'openastext' => array(
1160
						'caption' => lang('Text mode'),
1161
						'hint' => 'Open in Text mode',
1162
						'group' => ++$group,
1163
						'icon' => 'textmode',
1164
						'onExecute' => 'javaScript:app.mail.mail_openAsText',
1165
						'allowOnMultiple' => false,
1166
					),
1167
					'openashtml' => array(
1168
						'caption' => lang('HTML mode'),
1169
						'hint' => 'Open in HTML mode',
1170
						'group' => $group,
1171
						'icon' => 'htmlmode',
1172
						'onExecute' => 'javaScript:app.mail.mail_openAsHtml',
1173
						'allowOnMultiple' => false,
1174
					),
1175
				),
1176
				'hideOnMobile' => true
1177
			),
1178
			'mark' => array(
1179
				'caption' => 'Set / Remove Flags',
1180
				'icon' => 'read_small',
1181
				'group' => ++$group,
1182
				'children' => array(
1183
					// icons used from http://creativecommons.org/licenses/by-sa/3.0/
1184
					// Artist: Led24
1185
					// Iconset Homepage: http://led24.de/iconset
1186
					// License: CC Attribution 3.0
1187
					'setLabel' => array(
1188
						'caption' => 'Set / Remove Labels',
1189
						'icon' => 'tag_message',
1190
						'group' => ++$group,
1191
						// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
1192
						'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
1193
						'hideOnDisabled' => true,
1194
						'children' => array(
1195
							'unlabel' => array(
1196
								'group' => ++$group,
1197
								'caption' => "<font color='#ff0000'>".lang('remove all')."</font>",
1198
								'icon' => 'mail_label',
1199
								'onExecute' => 'javaScript:app.mail.mail_flag',
1200
								'shortcut' => KeyManager::shortcut(KeyManager::_0, true, true),
1201
							),
1202
							'label1' => array(
1203
								'group' => ++$group,
1204
								'caption' => "<font color='#ff0000'>".lang('important')."</font>",
1205
								'icon' => 'mail_label1',
1206
								'onExecute' => 'javaScript:app.mail.mail_flag',
1207
								'shortcut' => KeyManager::shortcut(KeyManager::_1, true, true),
1208
							),
1209
							'label2' => array(
1210
								'group' => $group,
1211
								'caption' => "<font color='#ff8000'>".lang('job')."</font>",
1212
								'icon' => 'mail_label2',
1213
								'onExecute' => 'javaScript:app.mail.mail_flag',
1214
								'shortcut' => KeyManager::shortcut(KeyManager::_2, true, true),
1215
							),
1216
							'label3' => array(
1217
								'group' => $group,
1218
								'caption' => "<font color='#008000'>".lang('personal')."</font>",
1219
								'icon' => 'mail_label3',
1220
								'onExecute' => 'javaScript:app.mail.mail_flag',
1221
								'shortcut' => KeyManager::shortcut(KeyManager::_3, true, true),
1222
							),
1223
							'label4' => array(
1224
								'group' => $group,
1225
								'caption' => "<font color='#0000ff'>".lang('to do')."</font>",
1226
								'icon' => 'mail_label4',
1227
								'onExecute' => 'javaScript:app.mail.mail_flag',
1228
								'shortcut' => KeyManager::shortcut(KeyManager::_4, true, true),
1229
							),
1230
							'label5' => array(
1231
								'group' => $group,
1232
								'caption' => "<font color='#8000ff'>".lang('later')."</font>",
1233
								'icon' => 'mail_label5',
1234
								'onExecute' => 'javaScript:app.mail.mail_flag',
1235
								'shortcut' => KeyManager::shortcut(KeyManager::_5, true, true),
1236
							),
1237
						),
1238
					),
1239
					// modified icons from http://creativecommons.org/licenses/by-sa/3.0/
1240
					'flagged' => array(
1241
						'group' => ++$group,
1242
						'caption' => 'Flag / Unflag',
1243
						'icon' => 'unread_flagged_small',
1244
						'onExecute' => 'javaScript:app.mail.mail_flag',
1245
						'hint' => 'Flag or Unflag a mail',
1246
						'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
1247
						'toolbarDefault' => true
1248
					),
1249
					'read' => array(
1250
						'group' => $group,
1251
						'caption' => 'Read / Unread',
1252
						'icon' => 'read_small',
1253
						'onExecute' => 'javaScript:app.mail.mail_flag',
1254
						'shortcut' => KeyManager::shortcut(KeyManager::U, true, true),
1255
1256
					),
1257
					'readall' => array(
1258
						'group' => ++$group,
1259
						'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
1260
						'icon' => 'read_small',
1261
						'onExecute' => 'javaScript:app.mail.mail_flag',
1262
						'hint' => 'mark all messages in folder as read',
1263
						'toolbarDefault' => false
1264
					),
1265
					'undelete' => array(
1266
						'group' => $group,
1267
						'caption' => 'Undelete',
1268
						'icon' => 'revert',
1269
						'onExecute' => 'javaScript:app.mail.mail_flag',
1270
					),
1271
				),
1272
			),
1273
			'delete' => array(
1274
				'caption' => 'Delete',
1275
				'hint' => $deleteOptions[$this->mail_bo->mailPreferences['deleteOptions']],
1276
				'group' => ++$group,
1277
				'onExecute' => 'javaScript:app.mail.mail_delete',
1278
				'toolbarDefault' => true
1279
			),
1280
			'drag_mail' => array(
1281
				'dragType' => array('mail'),
1282
				'type' => 'drag',
1283
				//'onExecute' => 'javaScript:app.mail.mail_dragStart',
1284
			)
1285
		);
1286
		//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
1287
		// 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
1288 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
1289
		{
1290
			unset($actions['infolog']);
1291
		}
1292 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['tracker']))
1293
		{
1294
			unset($actions['tracker']);
1295
		}
1296 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['calendar']))
1297
		{
1298
			unset($actions['calendar']);
1299
		}
1300
		return $actions;
1301
	}
1302
1303
	/**
1304
	 * Callback to fetch the rows for the nextmatch widget
1305
	 *
1306
	 * Function is static to not automatic call constructor in case profile is changed.
1307
	 *
1308
	 * @param array $query
1309
	 * @param array &$rows
1310
	 * @param array &$readonlys
1311
	 */
1312
	public static function get_rows(&$query,&$rows,&$readonlys)
1313
	{
1314
		unset($readonlys);	// not used, but required by function signature
1315
1316
		// handle possible profile change in get_rows
1317
		if (!empty($query['selectedFolder']))
1318
		{
1319
			list($_profileID,$folderName) = explode(self::$delimiter, $query['selectedFolder'], 2);
1320
			if (is_numeric(($_profileID)) && $_profileID != $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])
1321
			{
1322
				try {
1323
					$mail_ui = new mail_ui(false);	// do NOT run constructor, as we change profile anyway
1324
					$mail_ui->changeProfile($_profileID);
1325
					$query['actions'] = $mail_ui->get_actions();
1326
				}
1327
				catch(Exception $e)
1328
				{
1329
					unset($e);
1330
					$rows=array();
1331
					return 0;
1332
				}
1333
				if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
1334
			}
1335
		}
1336
		if (!isset($mail_ui))
1337
		{
1338
			try
1339
			{
1340
				$mail_ui = new mail_ui(true);	// run constructor for current profile
1341
			}
1342
			catch(Exception $e)
1343
			{
1344
				unset($e);
1345
				$rows=array();
1346
				return 0;
1347
			}
1348
			if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
1349
		}
1350
		//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
1351
		//Mail::$debugTimes=true;
1352
		if (Mail::$debugTimes) $starttime = microtime(true);
1353
		//$query['search'] is the phrase in the searchbox
1354
1355
		$mail_ui->mail_bo->restoreSessionData();
1356
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$query['selectedFolder'];
1357
		$mail_ui->mail_bo->saveSessionData();
1358
1359
		$sRToFetch = null;
1360
		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...
1361
		if (strpos($_folderName,self::$delimiter)!==false)
1362
		{
1363
			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...
1364
			unset($app);
1365
		}
1366
		//save selected Folder to sessionData (mailbox)->currentFolder
1367
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$_folderName;
1368
		$toSchema = false;//decides to select list schema with column to selected (if false fromaddress is default)
1369
		if ($mail_ui->mail_bo->folderExists($_folderName))
1370
		{
1371
			$toSchema = $mail_ui->mail_bo->isDraftFolder($_folderName,false)||$mail_ui->mail_bo->isSentFolder($_folderName,false)||$mail_ui->mail_bo->isTemplateFolder($_folderName,false);
1372
		}
1373
		else
1374
		{
1375
			// take the extra time on failure
1376
			if (!$mail_ui->mail_bo->folderExists($_folderName,true))
1377
			{
1378
				//error_log(__METHOD__.__LINE__.' Test on Folder:'.$_folderName.' failed; Using INBOX instead');
1379
				$query['selectedFolder']=$mail_ui->mail_bo->sessionData['mailbox']=$_folderName='INBOX';
1380
			}
1381
		}
1382
		$mail_ui->mail_bo->saveSessionData();
1383
		$rowsFetched['messages'] = null;
1384
		$offset = $query['start']+1; // we always start with 1
1385
		$maxMessages = $query['num_rows'];
1386
		//error_log(__METHOD__.__LINE__.array2string($query));
1387
		$sort = ($query['order']=='address'?($toSchema?'toaddress':'fromaddress'):$query['order']);
1388
		if (!empty($query['search'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
1389
		{
1390
			if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1391
			{
1392
				Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
1393
				if (!isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1394
				{
1395
					Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]=true;
1396
				}
1397
			}
1398
			//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
1399
			$cutoffdate = $cutoffdate2 = null;
1400
			if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
1401
			if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
1402
			//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
1403
			$filter = array(
1404
				'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
1405
				'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
1406
				'string' => $query['search'],
1407
				'status' => 'any',
1408
				//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
1409
			);
1410
			if ($query['enddate']||$query['startdate']) {
1411
				$filter['range'] = "BETWEEN";
1412
				if ($cutoffdate) {
1413
					$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
1414
					if (empty($cutoffdate2)) $filter['range'] = "SINCE";
1415
				}
1416
				if ($cutoffdate2) {
1417
					$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
1418
					if (empty($cutoffdate)) $filter['range'] = "BEFORE";
1419
				}
1420
			}
1421
		}
1422
		else
1423
		{
1424
			$filter = array();
1425
		}
1426
		if ($query['filter'])
1427
		{
1428
			$filter['status'] = $query['filter'];
1429
		}
1430
		$reverse = ($query['sort']=='ASC'?false:true);
1431
		$prefchanged = false;
1432 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']) || ($query['cat_id'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']))
1433
		{
1434
			//error_log(__METHOD__.__LINE__.' Changing userPref ActivesearchType:'.$query['cat_id']);
1435
			$GLOBALS['egw']->preferences->add('mail','ActiveSearchType',$query['cat_id'],'user');
1436
			$prefchanged = true;
1437
		}
1438 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']) || ($query['filter2'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']))
1439
		{
1440
			$GLOBALS['egw']->preferences->add('mail','ShowDetails',$query['filter2'],'user');
1441
			$prefchanged = true;
1442
		}
1443
		if ($prefchanged)
1444
		{
1445
			// save prefs
1446
			$GLOBALS['egw']->preferences->save_repository(true);
1447
		}
1448
		//error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($mail_ui->sessionData['messageFilter']));
1449
/*
1450
$cutoffdate = Api\DateTime::to('now','ts')-(3600*24*6);//SINCE, enddate
1451
$cutoffdate2 = Api\DateTime::to('now','ts')-(3600*24*3);//BEFORE, startdate
1452
$filter['range'] = "BETWEEN";// we support SINCE, BEFORE, BETWEEN and ON
1453
$filter['since'] = date("d-M-Y", $cutoffdate);
1454
$filter['before']= date("d-M-Y", $cutoffdate2);
1455
*/
1456
		try
1457
		{
1458
			if ($maxMessages > 75)
1459
			{
1460
				$rByUid = true;
1461
				$_sR = $mail_ui->mail_bo->getSortedList(
1462
					$_folderName,
1463
					$sort,
1464
					$reverse,
1465
					$filter,
1466
					$rByUid
1467
				);
1468
				$rowsFetched['messages'] = $_sR['count'];
1469
				$ids = $_sR['match']->ids;
1470
				// if $sR is false, something failed fundamentally
1471
				if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
1472
				$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
1473
				$sRToFetch = $sR;//array_slice($sR,0,50); // we fetch only the headers of a subset of the fetched uids
1474
				//error_log(__METHOD__.__LINE__.' Rows fetched (UID only):'.count($sR).' Data:'.array2string($sR));
1475
				$maxMessages = 75;
1476
				$sortResultwH['header'] = array();
1477
				if (count($sRToFetch)>0)
1478
				{
1479
					//error_log(__METHOD__.__LINE__.' Headers to fetch with UIDs:'.count($sRToFetch).' Data:'.array2string($sRToFetch));
1480
					$sortResult = array();
1481
					// fetch headers
1482
					$sortResultwH = $mail_ui->mail_bo->getHeaders(
1483
						$_folderName,
1484
						$offset,
1485
						$maxMessages,
1486
						$sort,
1487
						$reverse,
1488
						$filter,
1489
						$sRToFetch,
1490
						true, //cacheResult
1491
						($query['filter2']?true:false) // fetchPreview
1492
					);
1493
				}
1494
			}
1495
			else
1496
			{
1497
				$sortResult = array();
1498
				// fetch headers
1499
				$sortResultwH = $mail_ui->mail_bo->getHeaders(
1500
					$_folderName,
1501
					$offset,
1502
					$maxMessages,
1503
					$sort,
1504
					$reverse,
1505
					$filter,
1506
					null, // this uids only
1507
					true, // cacheResult
1508
					($query['filter2']?true:false) // fetchPreview
1509
				);
1510
				$rowsFetched['messages'] = $sortResultwH['info']['total'];
1511
			}
1512
		}
1513
		catch (Exception $e)
1514
		{
1515
			$sortResultwH=array();
1516
			$sR=array();
1517
			self::callWizard($e->getMessage(), false, 'error');
1518
		}
1519
		$response = Api\Json\Response::get();
1520
		// unlock immediately after fetching the rows
1521
		if (stripos($_GET['menuaction'],'ajax_get_rows')!==false)
1522
		{
1523
			//error_log(__METHOD__.__LINE__.' unlock tree ->'.$_GET['menuaction']);
1524
			$response->call('app.mail.unlock_tree');
1525
		}
1526
1527
		if (is_array($sR) && count($sR)>0)
1528
		{
1529
			foreach ((array)$sR as $key => $v)
1530
			{
1531
				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...
1532
				{
1533
					$sortResult['header'][] = $sortResultwH['header'][$key];
1534
				}
1535
				else
1536
				{
1537
					if (!empty($v)) $sortResult['header'][] = array('uid'=>$v);
1538
				}
1539
			}
1540
		}
1541
		else
1542
		{
1543
			$sortResult = $sortResultwH;
1544
		}
1545
		$rowsFetched['rowsFetched'] = count($sortResult['header']);
1546
		if (empty($rowsFetched['messages'])) $rowsFetched['messages'] = $rowsFetched['rowsFetched'];
1547
1548
		//error_log(__METHOD__.__LINE__.' Rows fetched:'.$rowsFetched.' Data:'.array2string($sortResult));
1549
		$cols = array('row_id','uid','status','attachments','subject','address','toaddress','fromaddress','ccaddress','additionaltoaddress','date','size','modified','bodypreview');
1550
		if ($GLOBALS['egw_info']['user']['preferences']['common']['select_mode']=='EGW_SELECTMODE_TOGGLE') unset($cols[0]);
1551
		$rows = $mail_ui->header2gridelements($sortResult['header'],$cols, $_folderName, $folderType=$toSchema);
1552
		//error_log(__METHOD__.__LINE__.array2string($rows));
1553
1554
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName.' Start:'.$query['start'].' NumRows:'.$query['num_rows'],__METHOD__.__LINE__);
1555
		return $rowsFetched['messages'];
1556
	}
1557
1558
	/**
1559
	 * function createRowID - create a unique rowID for the grid
1560
	 *
1561
	 * @param string $_folderName used to ensure the uniqueness of the uid over all folders
1562
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1563
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1564
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1565
	 */
1566
	function createRowID($_folderName, $message_uid, $_prependApp=false)
1567
	{
1568
		return self::generateRowID($this->mail_bo->profileID, $_folderName, $message_uid, $_prependApp);
1569
	}
1570
1571
	/**
1572
	 * static function generateRowID - create a unique rowID for the grid
1573
	 *
1574
	 * @param integer $_profileID profile ID for the rowid to be used
1575
	 * @param string $_folderName to ensure the uniqueness of the uid over all folders
1576
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1577
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1578
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1579
	 */
1580
	static function generateRowID($_profileID, $_folderName, $message_uid, $_prependApp=false)
1581
	{
1582
		return ($_prependApp?'mail'.self::$delimiter:'').trim($GLOBALS['egw_info']['user']['account_id']).self::$delimiter.$_profileID.self::$delimiter.base64_encode($_folderName).self::$delimiter.$message_uid;
1583
	}
1584
1585
	/**
1586
	 * function splitRowID - split the rowID into its parts
1587
	 *
1588
	 * @param string $_rowID string - a colon separated string in the form accountID:profileID:folder:message_uid
1589
	 * @return array populated named result array (accountID,profileID,folder,msgUID)
1590
	 */
1591
	static function splitRowID($_rowID)
1592
	{
1593
		$res = explode(self::$delimiter,$_rowID);
1594
		// as a rowID is perceeded by app::, should be mail!
1595
		//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));
1596
		if (count($res)==4 && is_numeric($res[0]) )
1597
		{
1598
			// we have an own created rowID; prepend app=mail
1599
			array_unshift($res,'mail');
1600
		}
1601
		return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
1602
	}
1603
1604
	/**
1605
	 * Get actions for preview toolbar
1606
	 *
1607
	 * @return array
1608
	 */
1609
	function get_toolbar_actions()
1610
	{
1611
		$actions = $this->get_actions();
1612
		$arrActions = array('composeasnew', 'reply', 'reply_all', 'forward', 'flagged', 'delete', 'print',
1613
			'infolog', 'tracker', 'calendar', 'save', 'view', 'read', 'label1',	'label2', 'label3',	'label4', 'label5');
1614
		foreach( $arrActions as &$act)
1615
		{
1616
			//error_log(__METHOD__.__LINE__.' '.$act.'->'.array2string($actions[$act]));
1617
			switch ($act)
1618
			{
1619
				case 'forward':
1620
					$actionsenabled[$act]=$actions[$act];
1621
					break;
1622
				case 'save':
1623
					$actionsenabled[$act]=$actions[$act];
1624
1625
					break;
1626
				case 'view':
1627
					$actionsenabled[$act]=$actions[$act];
1628
					break;
1629
				case 'flagged':
1630
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1631
					break;
1632
				case 'read':
1633
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1634
					break;
1635 View Code Duplication
				case 'label1':
1636
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('important');
1637
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1638
					break;
1639 View Code Duplication
				case 'label2':
1640
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('job');
1641
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1642
					break;
1643 View Code Duplication
				case 'label3':
1644
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('personal');
1645
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1646
					break;
1647 View Code Duplication
				case 'label4':
1648
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('to do');
1649
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1650
					break;
1651 View Code Duplication
				case 'label5':
1652
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('later');
1653
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1654
					break;
1655
				default:
1656
					if (isset($actions[$act])) $actionsenabled[$act]=$actions[$act];
1657
			}
1658
		}
1659
		unset($actionsenabled['drag_mail']);
1660
		//error_log(array2string($actionsenabled['view']));
1661
		unset($actionsenabled['view']['children']['openastext']);//not supported in preview
1662
		unset($actionsenabled['view']['children']['openashtml']);//not supported in preview
1663
1664
		return $actionsenabled;
1665
	}
1666
1667
	/**
1668
	 * function header2gridelements - to populate the grid elements with the collected Data
1669
	 *
1670
	 * @param array $_headers headerdata to process
1671
	 * @param array $cols cols to populate
1672
	 * @param array $_folderName to ensure the uniqueness of the uid over all folders
1673
	 * @param array $_folderType used to determine if we need to populate from/to
1674
	 * @return array populated result array
1675
	 */
1676
	public function header2gridelements($_headers, $cols, $_folderName, $_folderType=0)
1677
	{
1678
		if (Mail::$debugTimes) $starttime = microtime(true);
1679
		$rv = array();
1680
		$i=0;
1681
		foreach((array)$_headers as $header)
1682
		{
1683
			$i++;
1684
			$data = array();
1685
			//error_log(__METHOD__.array2string($header));
1686
			$message_uid = $header['uid'];
1687
			$data['uid'] = $message_uid;
1688
			$data['row_id']=$this->createRowID($_folderName,$message_uid);
1689
1690
			$flags = "";
1691
			if(!empty($header['recent'])) $flags .= "R";
1692
			if(!empty($header['flagged'])) $flags .= "F";
1693
			if(!empty($header['answered'])) $flags .= "A";
1694
			if(!empty($header['forwarded'])) $flags .= "W";
1695
			if(!empty($header['deleted'])) $flags .= "D";
1696
			if(!empty($header['seen'])) $flags .= "S";
1697
			if(!empty($header['label1'])) $flags .= "1";
1698
			if(!empty($header['label2'])) $flags .= "2";
1699
			if(!empty($header['label3'])) $flags .= "3";
1700
			if(!empty($header['label4'])) $flags .= "4";
1701
			if(!empty($header['label5'])) $flags .= "5";
1702
1703
			$data["status"] = "<span class=\"status_img\"></span>";
1704
			//error_log(__METHOD__.array2string($header).' Flags:'.$flags);
1705
1706
			// the css for this row
1707
			$is_recent=false;
1708
			$css_styles = array("mail");
1709
			if ($header['deleted']) {
1710
				$css_styles[] = 'deleted';
1711
			}
1712
			if ($header['recent'] && !($header['deleted'] || $header['seen'] || $header['answered'] || $header['forwarded'])) {
1713
				$css_styles[] = 'recent';
1714
				$is_recent=true;
1715
			}
1716
			if ($header['priority'] < 3) {
1717
				$css_styles[] = 'prio_high';
1718
			}
1719
			if ($header['flagged']) {
1720
				$css_styles[] = 'flagged';
1721
			}
1722
			if (!$header['seen']) {
1723
				$css_styles[] = 'unseen'; // different status image for recent // solved via css !important
1724
			}
1725
			if ($header['answered']) {
1726
				$css_styles[] = 'replied';
1727
			}
1728
			if ($header['forwarded']) {
1729
				$css_styles[] = 'forwarded';
1730
			}
1731
			if ($header['label1']) {
1732
				$css_styles[] = 'labelone';
1733
			}
1734
			if ($header['label2']) {
1735
				$css_styles[] = 'labeltwo';
1736
			}
1737
			if ($header['label3']) {
1738
				$css_styles[] = 'labelthree';
1739
			}
1740
			if ($header['label4']) {
1741
				$css_styles[] = 'labelfour';
1742
			}
1743
			if ($header['label5']) {
1744
				$css_styles[] = 'labelfive';
1745
			}
1746
1747
			//error_log(__METHOD__.array2string($css_styles));
1748
1749
			if (in_array("subject", $cols))
1750
			{
1751
				// filter out undisplayable characters
1752
				$search = array('[\016]','[\017]',
1753
					'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
1754
					'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
1755
				$replace = '';
1756
1757
				$header['subject'] = preg_replace($search,$replace,$header['subject']);
1758
				// curly brackets get messed up by the template!
1759
1760
				if (!empty($header['subject'])) {
1761
					// make the subject shorter if it is to long
1762
					$subject = $header['subject'];
1763
				} else {
1764
					$subject = '('. lang('no subject') .')';
1765
				}
1766
1767
				$data["subject"] = $subject; // the mailsubject
1768
			}
1769
1770
			$imageHTMLBlock = '';
1771
			//error_log(__METHOD__.__LINE__.array2string($header));
1772
			if (in_array("attachments", $cols))
1773
			{
1774
				if($header['mimetype'] == 'multipart/mixed' ||
1775
					$header['mimetype'] == 'multipart/signed' ||
1776
					$header['mimetype'] == 'multipart/related' ||
1777
					$header['mimetype'] == 'multipart/report' ||
1778
					$header['mimetype'] == 'text/calendar' ||
1779
					$header['mimetype'] == 'text/html' ||
1780
					substr($header['mimetype'],0,11) == 'application' ||
1781
					substr($header['mimetype'],0,5) == 'audio' ||
1782
					substr($header['mimetype'],0,5) == 'video' ||
1783
					$header['mimetype'] == 'multipart/alternative')
1784
				{
1785
					$image = Api\Html::image('mail','attach');
1786
					$imageHTMLBlock = '';
1787
					$datarowid = $this->createRowID($_folderName,$message_uid,true);
1788
					$attachments = $header['attachments'];
1789
					if (count($attachments)<1)
1790
					{
1791
						$image = '&nbsp;';
1792
					}
1793
					if (count($attachments)==1)
1794
					{
1795
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1796
						$image = Api\Html::image('mail','attach',$attachments[0]['name'].(!empty($attachments[0]['mimeType'])?' ('.$attachments[0]['mimeType'].')':''));
1797
					}
1798
					if (count($attachments)>1)
1799
					{
1800
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1801
						$image = Api\Html::image('mail','attach',lang('%1 attachments',count($attachments)));
1802
					}
1803
1804
					$attachmentFlag = $image;
1805
				} else {
1806
					$attachmentFlag ='&nbsp;';
1807
				}
1808
				// show priority flag
1809
				if ($header['priority'] < 3) {
1810
					 $image = Api\Html::image('mail','prio_high');
1811
				} elseif ($header['priority'] > 3) {
1812
					$image = Api\Html::image('mail','prio_low');
1813
				} else {
1814
					$image = '';
1815
				}
1816
				// show a flag for flagged messages
1817
				$imageflagged ='';
1818
				if ($header['flagged'])
1819
				{
1820
					$imageflagged = Api\Html::image('mail','unread_flagged_small');
1821
				}
1822
				$data["attachments"] = $image.$attachmentFlag.$imageflagged; // icon for attachments available
1823
			}
1824
1825
			// sent or draft or template folder -> to address
1826
			if (in_array("toaddress", $cols))
1827
			{
1828
				// sent or drafts or template folder means foldertype > 0, use to address instead of from
1829
				$data["toaddress"] = $header['to_address'];//Mail::htmlentities($header['to_address'],$this->charset);
1830
			}
1831
1832
			if (in_array("additionaltoaddress", $cols))
1833
			{
1834
				$data['additionaltoaddress'] = $header['additional_to_addresses'];
1835
			}
1836
			//fromaddress
1837
			if (in_array("fromaddress", $cols))
1838
			{
1839
				$data["fromaddress"] = $header['sender_address'];
1840
			}
1841
			if (in_array("ccaddress", $cols))
1842
			{
1843
				$data['ccaddress'] = $header['cc_addresses'];
1844
			}
1845
			if (in_array("date", $cols))
1846
			{
1847
				$data["date"] = $header['date'];
1848
			}
1849
			if (in_array("modified", $cols))
1850
			{
1851
				$data["modified"] = $header['internaldate'];
1852
			}
1853
1854
			if (in_array("size", $cols))
1855
				$data["size"] = $header['size']; /// size
1856
1857
			$data["class"] = implode(' ', $css_styles);
1858
			//translate style-classes back to flags
1859
			$data['flags'] = Array();
1860
			if ($header['seen']) $data["flags"]['read'] = 'read';
1861
			foreach ($css_styles as &$flag) {
1862
				if ($flag!='mail')
1863
				{
1864
					if ($flag=='labelone') {$data["flags"]['label1'] = 'label1';}
1865
					elseif ($flag=='labeltwo') {$data["flags"]['label2'] = 'label2';}
1866
					elseif ($flag=='labelthree') {$data["flags"]['label3'] = 'label3';}
1867
					elseif ($flag=='labelfour') {$data["flags"]['label4'] = 'label4';}
1868
					elseif ($flag=='labelfive') {$data["flags"]['label5'] = 'label5';}
1869
					elseif ($flag=='unseen') {unset($data["flags"]['read']);}
1870
					else $data["flags"][$flag] = $flag;
1871
				}
1872
			}
1873
			if ($header['disposition-notification-to']) $data['dispositionnotificationto'] = $header['disposition-notification-to'];
1874
			if (($header['mdnsent']||$header['mdnnotsent']|$header['seen'])&&isset($data['dispositionnotificationto'])) unset($data['dispositionnotificationto']);
1875
			$data['attachmentsBlock'] = $imageHTMLBlock;
1876
			$data['address'] = ($_folderType?$data["toaddress"]:$data["fromaddress"]);
1877
			if (in_array("bodypreview", $cols)&&$header['bodypreview'])
1878
			{
1879
				$data["bodypreview"] = $header['bodypreview'];
1880
			}
1881
			$rv[] = $data;
1882
			//error_log(__METHOD__.__LINE__.array2string($data));
1883
		}
1884
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName,__METHOD__.__LINE__);
1885
1886
		// ToDo: call this ONLY if labels change
1887
		Etemplate\Widget::setElementAttribute('toolbar', 'actions', $this->get_toolbar_actions());
1888
1889
		return $rv;
1890
	}
1891
1892
	/**
1893
	 * display messages header lines
1894
	 *
1895
	 * all params are passed as GET Parameters
1896
	 */
1897
	function displayHeader()
1898
	{
1899
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
1900
		if(isset($_GET['part'])) $partID = $_GET['part'];
1901
1902
		$hA = self::splitRowID($rowID);
1903
		$uid = $hA['msgUID'];
1904
		$mailbox = $hA['folder'];
1905
		$icServerID = $hA['profileID'];
1906
		$rememberServerID = $this->mail_bo->profileID;
1907
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1908
		{
1909
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1910
			$this->changeProfile($icServerID);
1911
		}
1912
1913
		$this->mail_bo->reopen($mailbox);
1914
		$headers_in	= $this->mail_bo->getMessageRawHeader($uid, $partID);
1915
1916
		// add line breaks to $rawheaders
1917
		$newRawHeaders = explode("\n",$headers_in);
1918
		reset($newRawHeaders);
1919
1920
		// reset $rawheaders
1921
		$rawheaders 	= "";
1922
		// create it new, with good line breaks
1923
		reset($newRawHeaders);
1924
		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...
1925
			$rawheaders .= wordwrap($value, 90, "\n     ");
1926
		}
1927
1928
		$this->mail_bo->closeConnection();
1929
		if ($rememberServerID != $this->mail_bo->profileID)
1930
		{
1931
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
1932
			$this->changeProfile($rememberServerID);
1933
		}
1934
1935
		header('Content-type: text/html; charset=iso-8859-1');
1936
		print '<pre>'. htmlspecialchars($rawheaders, ENT_NOQUOTES, 'iso-8859-1') .'</pre>';
1937
1938
	}
1939
1940
	/**
1941
	 * display messages
1942
	 * @param array $_requesteddata etemplate content
1943
	 * all params are passed as GET Parameters, but can be passed via ExecMethod2 as array too
1944
	 */
1945
	function displayMessage($_requesteddata = null)
1946
	{
1947
		if (is_null($_requesteddata)) $_requesteddata = $_GET;
1948
1949
		$preventRedirect=false;
1950
		if(isset($_requesteddata['id'])) $rowID	= $_requesteddata['id'];
1951
		if(isset($_requesteddata['part'])) $partID = $_requesteddata['part']!='null'?$_requesteddata['part']:null;
1952
		if(isset($_requesteddata['mode'])) $preventRedirect   = (($_requesteddata['mode']=='display' || $_requesteddata['mode'] == 'print')?true:false);
1953
1954
		$hA = self::splitRowID($rowID);
1955
		$uid = $hA['msgUID'];
1956
		$mailbox = $hA['folder'];
1957
		$icServerID = $hA['profileID'];
1958
		$rememberServerID = $this->mail_bo->profileID;
1959
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1960
		{
1961
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1962
			$this->changeProfile($icServerID);
1963
		}
1964
		$htmlOptions = $this->mail_bo->htmlOptions;
1965
		if (!empty($_requesteddata['tryastext'])) $htmlOptions  = "only_if_no_text";
1966
		if (!empty($_requesteddata['tryashtml'])) $htmlOptions  = "always_display";
1967
1968
		//error_log(__METHOD__.__LINE__.array2string($hA));
1969
		if (($this->mail_bo->isDraftFolder($mailbox)) && $_requesteddata['mode'] == 'print')
1970
		{
1971
			$response = Api\Json\Response::get();
1972
			$response->call('app.mail.print_for_compose', $rowID);
1973
		}
1974
		if (!$preventRedirect && ($this->mail_bo->isDraftFolder($mailbox) || $this->mail_bo->isTemplateFolder($mailbox)))
1975
		{
1976
			Egw::redirect_link('/index.php',array('menuaction'=>'mail.mail_compose.compose','id'=>$rowID,'from'=>'composefromdraft'));
1977
		}
1978
		$this->mail_bo->reopen($mailbox);
1979
		// retrieve the flags of the message, before touching it.
1980
		try
1981
		{
1982
			$headers	= $this->mail_bo->getMessageHeader($uid, $partID,true,true,$mailbox);
1983
		}
1984
		catch (Api\Exception $e)
1985
		{
1986
			$error_msg[] = lang("ERROR: Message could not be displayed.");
1987
			$error_msg[] = lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);
1988
			Framework::message($e->getMessage(), 'error');
1989
		}
1990
		if (!empty($uid)) $this->mail_bo->getFlags($uid);
1991
		$envelope	= $this->mail_bo->getMessageEnvelope($uid, $partID,true,$mailbox);
1992
		//error_log(__METHOD__.__LINE__.array2string($envelope));
1993
		$this->mail_bo->getMessageRawHeader($uid, $partID,$mailbox);
1994
		$fetchEmbeddedImages = false;
1995
		// if we are in HTML so its likely that we should show the embedded images; as a result
1996
		// we do NOT want to see those, that are embedded in the list of attachments
1997
		if ($htmlOptions !='always_display') $fetchEmbeddedImages = true;
1998
		$attachments	= $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox);
1999
		//error_log(__METHOD__.__LINE__.array2string($attachments));
2000
		$attachmentHTMLBlock = self::createAttachmentBlock($attachments, $rowID, $uid, $mailbox);
2001
2002
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2003
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2004
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2005
2006
		//error_log(__METHOD__.__LINE__.$mailBody);
2007
		$this->mail_bo->closeConnection();
2008
		//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
2009
		$etpl = new Etemplate('mail.display');
2010
		$subject = $this->mail_bo->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false);
2011
2012
		// Set up data for taglist widget(s)
2013
		if ($envelope['FROM']==$envelope['SENDER']) unset($envelope['SENDER']);
2014
		$sel_options = array();
2015
		foreach(array('SENDER','FROM','TO','CC','BCC') as $field)
2016
		{
2017
			if (!isset($envelope[$field])) continue;
2018
			foreach($envelope[$field] as $field_data)
2019
			{
2020
				//error_log(__METHOD__.__LINE__.array2string($field_data));
2021
				$content[$field][] = $field_data;
2022
				$sel_options[$field][] = array(
2023
					// taglist requires these - not optional
2024
					'id' => $field_data,
2025
					'label' => str_replace('"',"'",$field_data),
2026
				);
2027
			}
2028
		}
2029
		$actionsenabled = $this->getDisplayToolbarActions();
2030
		$content['displayToolbaractions'] = json_encode($actionsenabled);
2031
		if (empty($subject)) $subject = lang('no subject');
2032
		$content['msg'] = (is_array($error_msg)?implode("<br>",$error_msg):$error_msg);
2033
		// Send mail ID so we can use it for actions
2034
		$content['mail_id'] = $rowID;
2035
		if (!is_array($headers) || !isset($headers['DATE']))
2036
		{
2037
			$headers['DATE'] = (is_array($envelope)&&$envelope['DATE']?$envelope['DATE']:'');
2038
		}
2039
		$content['mail_displaydate'] = Mail::_strtotime($headers['DATE'],'ts',true);
2040
		$content['mail_displaysubject'] = $subject;
2041
		$linkData = array('menuaction'=>"mail.mail_ui.loadEmailBody","_messageID"=>$rowID);
2042
		if (!empty($partID)) $linkData['_partID']=$partID;
2043
		if ($htmlOptions != $this->mail_bo->htmlOptions) $linkData['_htmloptions']=$htmlOptions;
2044
		$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
2045
		$content['mail_displayattachments'] = $attachmentHTMLBlock;
2046
		$content['mail_id']=$rowID;
2047
		$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
2048
		$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
2049
2050
		// DRAG attachments actions
2051
		$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
2052
			'file_drag' => array(
2053
				'dragType' => 'file',
2054
				'type' => 'drag',
2055
				'onExecute' => 'javaScript:app.mail.drag_attachment'
2056
			)
2057
		));
2058
		$readonlys = $preserv = $content;
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
		$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
2066
	}
2067
2068
	/**
2069
	 * Build actions for display toolbar
2070
	 */
2071
	function getDisplayToolbarActions ()
2072
	{
2073
		$actions = $this->get_toolbar_actions();
2074
		$actions['mark']['children']['flagged']=array(
2075
			'group' => $actions['mark']['children']['flagged']['group'],
2076
			'caption' => 'Flagged',
2077
			'icon' => 'unread_flagged_small',
2078
			'onExecute' => 'javaScript:app.mail.mail_flag',
2079
		);
2080
		$actions['mark']['children']['unflagged']=array(
2081
			'group' => $actions['mark']['children']['flagged']['group'],
2082
			'caption' => 'Unflagged',
2083
			'icon' => 'read_flagged_small',
2084
			'onExecute' => 'javaScript:app.mail.mail_flag',
2085
		);
2086
		$actions['tracker']['toolbarDefault'] = true;
2087
		$actions['forward']['toolbarDefault'] = true;
2088
2089
		$compose = $actions['composeasnew'];
2090
		unset($actions['composeasnew']);
2091
2092
		$actions2 = array_reverse($actions,true);
2093
		$actions2['composeasnew']= $compose;
2094
		return array_reverse($actions2,true);
2095
	}
2096
2097
	/**
2098
	 * helper function to create the attachment block/table
2099
	 *
2100
	 * @param array $attachments array with the attachments information
2101
	 * @param string $rowID rowid of the message
2102
	 * @param int $uid uid of the message
2103
	 * @param string $mailbox mailbox identifier
2104
	 * @param boolean $_returnFullHTML flag wether to return HTML or data array
2105
	 * @return array|string data array or html or empty string
2106
	 */
2107
	static function createAttachmentBlock($attachments, $rowID, $uid, $mailbox,$_returnFullHTML=false)
2108
	{
2109
		$attachmentHTMLBlock='';
2110
		$attachmentHTML = array();
2111
		if (is_array($attachments) && count($attachments) > 0) {
2112
			$url_img_vfs = Api\Html::image('filemanager','navbar', lang('Filemanager'), ' height="16"');
2113
			$url_img_vfs_save_all = Api\Html::image('mail','save_all', lang('Save all'));
2114
2115
			foreach ($attachments as $key => $value)
2116
			{
2117
				$attachmentHTML[$key]['filename']= ($value['name'] ? ( $value['filename'] ? $value['filename'] : $value['name'] ) : lang('(no subject)'));
2118
				$attachmentHTML[$key]['filename'] = Api\Translation::convert_jsonsafe($attachmentHTML[$key]['filename'],'utf-8');
2119
				//error_log(array2string($value));
2120
				//error_log(strtoupper($value['mimeType']) .'<->'. Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']));
2121
				if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']);
2122
				$attachmentHTML[$key]['type']=$value['mimeType'];
2123
				$attachmentHTML[$key]['mimetype'] = Api\MimeMagic::mime2label($value['mimeType']);
2124
				$hA = self::splitRowID($rowID);
2125
				$uid = $hA['msgUID'];
2126
				$mailbox = $hA['folder'];
2127
				$acc_id = $hA['profileID'];
2128
2129
				$attachmentHTML[$key]['mime_data'] = Link::set_data($value['mimeType'], 'EGroupware\\Api\\Mail::getAttachmentAccount', array(
2130
					$acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true
2131
				));
2132
				$attachmentHTML[$key]['size']=Vfs::hsize($value['size']);
2133
				$attachmentHTML[$key]['attachment_number']=$key;
2134
				$attachmentHTML[$key]['partID']=$value['partID'];
2135
				$attachmentHTML[$key]['mail_id'] = $rowID;
2136
				$attachmentHTML[$key]['winmailFlag']=$value['is_winmail'];
2137
				$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "mail_DisplayNone";
2138
2139
				switch(strtoupper($value['mimeType']))
2140
				{
2141
					case 'MESSAGE/RFC822':
2142
						$linkData = array
2143
						(
2144
							'menuaction'	=> 'mail.mail_ui.displayMessage',
2145
							'mode'		=> 'display', //message/rfc822 attachments should be opened in display mode
2146
							'id'		=> $rowID,
2147
							'part'		=> $value['partID'],
2148
							'is_winmail'    => $value['is_winmail']
2149
						);
2150
						$windowName = 'displayMessage_'. $rowID.'_'.$value['partID'];
2151
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',700,egw_getWindowOuterHeight());";
2152
						break;
2153
					case 'IMAGE/JPEG':
2154
					case 'IMAGE/PNG':
2155
					case 'IMAGE/GIF':
2156
					case 'IMAGE/BMP':
2157
					case 'APPLICATION/PDF':
2158
					case 'TEXT/PLAIN':
2159
					case 'TEXT/HTML':
2160
					case 'TEXT/DIRECTORY':
2161
						$sfxMimeType = $value['mimeType'];
2162
						$buff = explode('.',$value['name']);
2163
						$suffix = '';
2164
						if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2165
						if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2166
						if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD')
2167
						{
2168
							$attachments[$key]['mimeType'] = $sfxMimeType;
2169
							$value['mimeType'] = strtoupper($sfxMimeType);
2170
						}
2171
					case 'TEXT/X-VCARD':
2172
					case 'TEXT/VCARD':
2173
					case 'TEXT/CALENDAR':
2174
					case 'TEXT/X-VCALENDAR':
2175
						$linkData = array
2176
						(
2177
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2178
							'id'		=> $rowID,
2179
							'part'		=> $value['partID'],
2180
							'is_winmail'    => $value['is_winmail'],
2181
							'mailbox'   => base64_encode($mailbox),
2182
						);
2183
						$windowName = 'displayAttachment_'. $uid;
2184
						$reg = '800x600';
2185
						// handle calendar/vcard
2186
						if (strtoupper($value['mimeType'])=='TEXT/CALENDAR')
2187
						{
2188
							$windowName = 'displayEvent_'. $rowID;
2189
							$reg2 = Link::get_registry('calendar','view_popup');
2190
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2191
						}
2192
						if (strtoupper($value['mimeType'])=='TEXT/X-VCARD' || strtoupper($value['mimeType'])=='TEXT/VCARD')
2193
						{
2194
							$windowName = 'displayContact_'. $rowID;
2195
							$reg2 = Link::get_registry('addressbook','add_popup');
2196
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2197
						}
2198
						// apply to action
2199
						list($width,$height) = explode('x',(!empty($reg2) ? $reg2 : $reg));
2200
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',$width,$height);";
2201
						break;
2202
					default:
2203
						$linkData = array
2204
						(
2205
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2206
							'id'		=> $rowID,
2207
							'part'		=> $value['partID'],
2208
							'is_winmail'    => $value['is_winmail'],
2209
							'mailbox'   => base64_encode($mailbox),
2210
						);
2211
						$linkView = "window.location.href = '".Egw::link('/index.php',$linkData)."';";
2212
						break;
2213
				}
2214
				// we either use mime_data for server-side supported mime-types or mime_url for client-side or download
2215
				if (empty($attachmentHTML[$key]['mime_data']))
2216
				{
2217
					$attachmentHTML[$key]['mime_url'] = Egw::link('/index.php',$linkData);
2218
					unset($attachmentHTML[$key]['mime_data']);
2219
				}
2220
				$attachmentHTML[$key]['windowName'] = $windowName;
2221
2222
				//error_log(__METHOD__.__LINE__.$linkView);
2223
				$attachmentHTML[$key]['link_view'] = '<a href="#" ." title="'.$attachmentHTML[$key]['filename'].'" onclick="'.$linkView.' return false;"><b>'.
2224
					($value['name'] ? $value['name'] : lang('(no subject)')).
2225
					'</b></a>';
2226
2227
				$linkData = array
2228
				(
2229
					'menuaction'	=> 'mail.mail_ui.getAttachment',
2230
					'mode'		=> 'save',
2231
					'id'		=> $rowID,
2232
					'part'		=> $value['partID'],
2233
					'is_winmail'    => $value['is_winmail'],
2234
					'mailbox'   => base64_encode($mailbox),
2235
				);
2236
				$attachmentHTML[$key]['link_save'] ="<a href='".Egw::link('/index.php',$linkData)."' title='".$attachmentHTML[$key]['filename']."'>".Api\Html::image('mail','fileexport')."</a>";
2237
2238
				if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
2239
				{
2240
					$link_vfs_save = Egw::link('/index.php',array(
2241
						'menuaction' => 'filemanager.filemanager_select.select',
2242
						'mode' => 'saveas',
2243
						'name' => $value['name'],
2244
						'mime' => strtolower($value['mimeType']),
2245
						'method' => 'mail.mail_ui.vfsSaveAttachment',
2246
						'id' => $rowID.'::'.$value['partID'].'::'.$value['is_winmail'],
2247
						'label' => lang('Save'),
2248
					));
2249
					$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>";
2250
					// add save-all icon for first attachment
2251
					if (!$key && count($attachments) > 1)
2252
					{
2253
						$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "";
2254
						foreach ($attachments as $ikey => $value)
2255
						{
2256
							//$rowID
2257
							$ids["id[$ikey]"] = $rowID.'::'.$value['partID'].'::'.$value['is_winmail'].'::'.$value['name'];
2258
						}
2259
						$link_vfs_save = Egw::link('/index.php',array(
2260
							'menuaction' => 'filemanager.filemanager_select.select',
2261
							'mode' => 'select-dir',
2262
							'method' => 'mail.mail_ui.vfsSaveAttachment',
2263
							'label' => lang('Save all'),
2264
						)+$ids);
2265
						$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>";
2266
					}
2267
					$attachmentHTML[$key]['link_save'] .= $vfs_save;
2268
					//error_log(__METHOD__.__LINE__.$attachmentHTML[$key]['link_save']);
2269
				}
2270
			}
2271
			$attachmentHTMLBlock="<table width='100%'>";
2272
			foreach ((array)$attachmentHTML as $row)
2273
			{
2274
				$attachmentHTMLBlock .= "<tr><td><div class='useEllipsis'>".$row['link_view'].'</div></td>';
2275
				$attachmentHTMLBlock .= "<td>".$row['mimetype'].'</td>';
2276
				$attachmentHTMLBlock .= "<td>".$row['size'].'</td>';
2277
				$attachmentHTMLBlock .= "<td>".$row['link_save'].'</td></tr>';
2278
			}
2279
			$attachmentHTMLBlock .= "</table>";
2280
		}
2281
		if (!$_returnFullHTML)
2282
		{
2283
			foreach ((array)$attachmentHTML as $ikey => $value)
2284
			{
2285
				unset($attachmentHTML[$ikey]['link_view']);
2286
				unset($attachmentHTML[$ikey]['link_save']);
2287
			}
2288
		}
2289
		return ($_returnFullHTML?$attachmentHTMLBlock:$attachmentHTML);
2290
	}
2291
2292
	/**
2293
	 * fetch vacation info from active Server using icServer object
2294
	 *
2295
	 * @param array $cachedVacations an array of cached vacations for an user
2296
	 * @return array|boolean array with vacation on success or false on failure
2297
	 */
2298
	function gatherVacation($cachedVacations = array())
2299
	{
2300
		$isVacationEnabled = $this->mail_bo->icServer->acc_sieve_enabled && ($this->mail_bo->icServer->acc_sieve_host||$this->mail_bo->icServer->acc_imap_host);
2301
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
2302
2303
		if ($isVacationEnabled)
2304
		{
2305
			$sieveServer = $this->mail_bo->icServer;
2306
			try
2307
			{
2308
				$sieveServer->retrieveRules();
2309
				$vacation = $sieveServer->getVacation();
2310
2311
				$cachedVacations = array($sieveServer->acc_id => $vacation) + (array)$cachedVacations;
2312
				// Set vacation to the instance cache for particular account with expiration of one day
2313
				Api\Cache::setCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations, 60*60*24);
2314
			}
2315
			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...
2316
			{
2317
				$this->callWizard($ex->getMessage(), true, 'error');
2318
			}
2319
		}
2320
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Vacation retrieved:'.array2string($vacation));
2321
		return $vacation;
2322
	}
2323
2324
	/**
2325
	 * gather Info on how to display the quota info
2326
	 *
2327
	 * @param $_usage int
2328
	 * @param $_limit int
2329
	 * @return array - info used for quota array(class=>string,text=>string,$percent=>string)
2330
	 */
2331
	function quotaDisplay($_usage, $_limit)
2332
	{
2333
2334
		if($_limit == 0) {
2335
			$quotaPercent=100;
2336
		} else {
2337
			$quotaPercent=round(($_usage*100)/$_limit);
2338
		}
2339
2340
		$quotaLimit=Mail::show_readable_size($_limit*1024);
2341
		$quotaUsage=Mail::show_readable_size($_usage*1024);
2342
2343
2344
		if($quotaPercent > 90 && $_limit>0) {
2345
			$quotaBG='mail-index_QuotaRed';
2346
		} elseif($quotaPercent > 80 && $_limit>0) {
2347
			$quotaBG='mail-index_QuotaYellow';
2348
		} else {
2349
			$quotaBG='mail-index_QuotaGreen';
2350
		}
2351
2352
		if($_limit > 0) {
2353
			$quotaText = $quotaUsage .'/'.$quotaLimit;
2354
		} else {
2355
			$quotaText = $quotaUsage;
2356
		}
2357
2358
		if($quotaPercent > 50) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2359
		} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
2360
		}
2361
		$quota['class'] = $quotaBG;
2362
		$quota['text'] = lang('Quota: %1',$quotaText);
2363
		$quota['percent'] = (string)round(($_usage*100)/$_limit);
2364
		return $quota;
2365
	}
2366
2367
	/**
2368
	 * display image
2369
	 *
2370
	 * all params are passed as GET Parameters
2371
	 */
2372
	function displayImage()
2373
	{
2374
		$uid	= $_GET['uid'];
2375
		$cid	= base64_decode($_GET['cid']);
2376
		$partID = urldecode($_GET['partID']);
2377
		if (!empty($_GET['mailbox'])) $mailbox  = base64_decode($_GET['mailbox']);
2378
2379
		//error_log(__METHOD__.__LINE__.":$uid, $cid, $partID");
2380
		$this->mail_bo->reopen($mailbox);
2381
2382
		$attachment = $this->mail_bo->getAttachmentByCID($uid, $cid, $partID, true);	// true get contents as stream
2383
2384
		$this->mail_bo->closeConnection();
2385
2386
		$GLOBALS['egw']->session->commit_session();
2387
2388
		if ($attachment)
2389
		{
2390
			header("Content-Type: ". $attachment->getType());
2391
			header('Content-Disposition: inline; filename="'. $attachment->getDispositionParameter('filename') .'"');
2392
			//header("Expires: 0");
2393
			// the next headers are for IE and SSL
2394
			//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2395
			//header("Pragma: public");
2396
			Api\Session::cache_control(true);
2397
			echo $attachment->getContents();
2398
		}
2399
		else
2400
		{
2401
			// send a 404 Not found
2402
			header("HTTP/1.1 404 Not found");
2403
		}
2404
		exit();
2405
	}
2406
2407
	function getAttachment()
2408
	{
2409
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2410
2411
		$hA = self::splitRowID($rowID);
2412
		$uid = $hA['msgUID'];
2413
		$mailbox = $hA['folder'];
2414
		$icServerID = $hA['profileID'];
2415
		$rememberServerID = $this->mail_bo->profileID;
2416
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2417
		{
2418
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2419
			$this->changeProfile($icServerID);
2420
		}
2421
		$part		= $_GET['part'];
2422
		$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
2423
2424
		$this->mail_bo->reopen($mailbox);
2425
		$attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false);
2426
		$this->mail_bo->closeConnection();
2427
		if ($rememberServerID != $this->mail_bo->profileID)
2428
		{
2429
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2430
			$this->changeProfile($rememberServerID);
2431
		}
2432
2433
		$GLOBALS['egw']->session->commit_session();
2434
		//error_log(__METHOD__.print_r($_GET,true));
2435
		if ($_GET['mode'] != "save")
2436
		{
2437 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/DIRECTORY' || empty($attachment['type']))
2438
			{
2439
				$sfxMimeType = $attachment['type'];
2440
				$buff = explode('.',$attachment['filename']);
2441
				$suffix = '';
2442
				if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2443
				if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2444
				$attachment['type'] = $sfxMimeType;
2445
				if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD') $attachment['type'] = strtoupper($sfxMimeType);
2446
			}
2447
			//error_log(__METHOD__.print_r($attachment,true));
2448
			if (strtoupper($attachment['type']) == 'TEXT/CALENDAR' || strtoupper($attachment['type']) == 'TEXT/X-VCALENDAR')
2449
			{
2450
				//error_log(__METHOD__."about to call calendar_ical");
2451
				$calendar_ical = new calendar_ical();
2452
				$eventid = $calendar_ical->search($attachment['attachment'],-1);
2453
				//error_log(__METHOD__.array2string($eventid));
2454
				if (!$eventid) $eventid = -1;
2455
				$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
2456
				//error_log(__METHOD__.$event);
2457 View Code Duplication
				if ((int)$event > 0)
2458
				{
2459
					$vars = array(
2460
						'menuaction'      => 'calendar.calendar_uiforms.edit',
2461
						'cal_id'      => $event,
2462
					);
2463
					Egw::redirect_link('../index.php',$vars);
2464
				}
2465
				//Import failed, download content anyway
2466
			}
2467 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/X-VCARD' || strtoupper($attachment['type']) == 'TEXT/VCARD')
2468
			{
2469
				$addressbook_vcal = new addressbook_vcal();
2470
				// double \r\r\n seems to end a vcard prematurely, so we set them to \r\n
2471
				//error_log(__METHOD__.__LINE__.$attachment['attachment']);
2472
				$attachment['attachment'] = str_replace("\r\r\n", "\r\n", $attachment['attachment']);
2473
				$vcard = $addressbook_vcal->vcardtoegw($attachment['attachment'], $attachment['charset']);
2474
				if ($vcard['uid'])
2475
				{
2476
					$vcard['uid'] = trim($vcard['uid']);
2477
					//error_log(__METHOD__.__LINE__.print_r($vcard,true));
2478
					$contact = $addressbook_vcal->find_contact($vcard,false);
2479
				}
2480
				if (!$contact) $contact = null;
2481
				// 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))
2482
				if ($contact || count($vcard)>2)
2483
				{
2484
					$contact = $addressbook_vcal->addVCard($attachment['attachment'],(is_array($contact)?array_shift($contact):$contact),true,$attachment['charset']);
2485
				}
2486
				if ((int)$contact > 0)
2487
				{
2488
					$vars = array(
2489
						'menuaction'	=> 'addressbook.addressbook_ui.edit',
2490
						'contact_id'	=> $contact,
2491
					);
2492
					Egw::redirect_link('../index.php',$vars);
2493
				}
2494
				//Import failed, download content anyway
2495
			}
2496
		}
2497
		//error_log(__METHOD__.__LINE__.'->'.array2string($attachment));
2498
		$filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part));
2499
		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...
2500
		echo $attachment['attachment'];
2501
2502
		exit();
2503
	}
2504
2505
2506
	/**
2507
	 * save messages on disk or filemanager, or display it in popup
2508
	 *
2509
	 * all params are passed as GET Parameters
2510
	 */
2511
	function saveMessage()
2512
	{
2513
		$display = false;
2514
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2515
		if(isset($_GET['part'])) $partID = $_GET['part'];
2516
		if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display	= $_GET['location'];
2517
2518
		$hA = self::splitRowID($rowID);
2519
		$uid = $hA['msgUID'];
2520
		$mailbox = $hA['folder'];
2521
		$icServerID = $hA['profileID'];
2522
		$rememberServerID = $this->mail_bo->profileID;
2523
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2524
		{
2525
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2526
			$this->changeProfile($icServerID);
2527
		}
2528
2529
		$this->mail_bo->reopen($mailbox);
2530
2531
		$message = $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox);
2532
2533
		$this->mail_bo->closeConnection();
2534
		if ($rememberServerID != $this->mail_bo->profileID)
2535
		{
2536
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2537
			$this->changeProfile($rememberServerID);
2538
		}
2539
2540
		$GLOBALS['egw']->session->commit_session();
2541
		if (!$display)
2542
		{
2543
			$headers = Horde_Mime_Headers::parseHeaders($message);
2544
			$subject = str_replace('$$','__',Mail::decode_header($headers['SUBJECT']));
2545
			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...
2546
			echo $message;
2547
		}
2548
		else
2549
		{
2550
			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...
2551
			print '<pre>'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'</pre>';
2552
		}
2553
	}
2554
2555
	/**
2556
	 * Save an Message in the vfs
2557
	 *
2558
	 * @param string|array $ids use splitRowID, to separate values
2559
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2560
	 * @param boolean $close Return javascript to close the window
2561
	 * @return string|boolean javascript eg. to close the selector window if $close is true, or success/fail if $close is false
2562
	 */
2563
	function vfsSaveMessage($ids,$path, $close = true)
2564
	{
2565
		//error_log(__METHOD__.' IDs:'.array2string($ids).' SaveToPath:'.$path);
2566
2567 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2568
		{
2569
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2570
		}
2571
		Api\Translation::add_app('mail');
2572
2573
		$rememberServerID = $this->mail_bo->profileID;
2574
		foreach((array)$ids as $id)
2575
		{
2576
			$hA = self::splitRowID($id);
2577
			$uid = $hA['msgUID'];
2578
			$mailbox = $hA['folder'];
2579
			$icServerID = $hA['profileID'];
2580
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2581
			{
2582
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2583
				$this->changeProfile($icServerID);
2584
			}
2585
			$message = $this->mail_bo->getMessageRawBody($uid, $partID='', $mailbox);
2586
			$err=null;
2587
			if(Vfs::is_dir($path))
2588
			{
2589
				$headers = $this->mail_bo->getMessageHeader($uid,$partID,true,false,$mailbox);
2590
				$file = $path . '/'.preg_replace('/[\f\n\t\v\\:*#?<>\|]/',"_",$headers['SUBJECT']).'.eml';
2591
			}
2592
			else
2593
			{
2594
				$file = $path;
2595
			}
2596
			if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
2597
			{
2598
				$err .= lang('Error saving %1!',$file);
2599
				$succeeded = false;
2600
			}
2601
			else
2602
			{
2603
				$succeeded = true;
2604
			}
2605
			if ($fp) fclose($fp);
2606
			if ($succeeded)
2607
			{
2608
				unset($headers['SUBJECT']);//already in filename
2609
				$infoSection = Mail::createHeaderInfoSection($headers, 'SUPPRESS', false);
2610
				$props = array(array('name' => 'comment','val' => $infoSection));
2611
				Vfs::proppatch($file,$props);
2612
			}
2613
		}
2614
		if ($rememberServerID != $this->mail_bo->profileID)
2615
		{
2616
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2617
			$this->changeProfile($rememberServerID);
2618
		}
2619
2620
		if($close)
2621
		{
2622
			Framework::window_close(($err?$err:null));
2623
		}
2624
		else
2625
		{
2626
			return $succeeded;
2627
		}
2628
	}
2629
2630
	/**
2631
	 * Save an attachment in the vfs
2632
	 *
2633
	 * @param string|array $ids '::' delimited mailbox::uid::part-id::is_winmail::name (::name for multiple id's)
2634
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2635
	 * @return string javascript eg. to close the selector window
2636
	 */
2637
	function vfsSaveAttachment($ids,$path)
2638
	{
2639
		//error_log(__METHOD__.__LINE__.'("'.array2string($ids).'","'.$path."\")');");
2640
2641 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2642
		{
2643
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2644
		}
2645
		$err=null;
2646
		$dupe_count = array();
2647
		$rememberServerID = $this->mail_bo->profileID;
2648
2649
		/**
2650
		 * Extract all parameteres from the given id
2651
		 * @param int $id message id ('::' delimited mailbox::uid::part-id::is_winmail::name)
2652
		 *
2653
		 * @return array an array of parameters
2654
		 */
2655
		$getParams = function ($id) {
2656
			list($app,$user,$serverID,$mailbox,$uid,$part,$is_winmail,$name) = explode('::',$id,8);
2657
			$lId = implode('::',array($app,$user,$serverID,$mailbox,$uid));
2658
			$hA = mail_ui::splitRowID($lId);
2659
			return array(
2660
				'is_winmail' => $is_winmail == "null" || !$is_winmail?false:$is_winmail,
2661
				'user' => $user,
2662
				'name' => $name,
2663
				'part' => $part,
2664
				'uid' => $hA['msgUID'],
2665
				'mailbox' => $hA['folder'],
2666
				'icServer' => $hA['profileID']
2667
			);
2668
		};
2669
2670
		//Examine the first attachment to see if attachment
2671
		//is winmail.dat embedded attachments.
2672
		$isMultipleDownload=is_array($ids);
2673
		$p = $getParams((is_array($ids)?$ids[0]:$ids));
2674
		if ($p['is_winmail'])
2675
		{
2676 View Code Duplication
			if ($p['icServer'] && $p['icServer'] != $this->mail_bo->profileID)
2677
			{
2678
				$this->changeProfile($p['icServer']);
2679
			}
2680
			$this->mail_bo->reopen($p['mailbox']);
2681
			// Retrive all embedded attachments at once
2682
			// avoids to fetch heavy winmail.dat content
2683
			// for each file.
2684
			$attachments = $this->mail_bo->getTnefAttachments($p['uid'],$p['part']);
2685
		}
2686
2687
		foreach((array)$ids as $id)
2688
		{
2689
			$params = $getParams($id);
2690
			// when downloading a single file, name is not set
2691
			if (!$params['name']&&isset($_GET['name'])&&!$isMultipleDownload) $params['name'] = $_GET['name'];
2692 View Code Duplication
			if ($params['icServer'] && $params['icServer'] != $this->mail_bo->profileID)
2693
			{
2694
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2695
				$this->changeProfile($params['icServer']);
2696
			}
2697
			//error_log(__METHOD__.__LINE__.array2string($hA));
2698
			$this->mail_bo->reopen($params['mailbox']);
2699
			if ($params['is_winmail'])
2700
			{
2701
				// Try to find the right content for file id
2702
				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...
2703
				{
2704
					if ($key == $params['is_winmail']) $attachment = $val;
2705
				}
2706
			}
2707
			else
2708
			{
2709
				$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
2710
			}
2711
2712
			$file = $params['name'];
2713
			// when $isMultipleDownload the path holds no filename
2714
			while(Vfs::file_exists($path.($file && $isMultipleDownload ? '/'.$file : '')))
2715
			{
2716
				$dupe_count[$params['name']]++;
2717
				$file = pathinfo($params['name'], PATHINFO_FILENAME) .
2718
					' ('.($dupe_count[$params['name']] + 1).')' . '.' .
2719
					pathinfo($params['name'], PATHINFO_EXTENSION);
2720
			}
2721
			$params['name'] = $file;
2722
			//error_log(__METHOD__.__LINE__.array2string($attachment));
2723
			// when $isMultipleDownload the path holds no filename
2724
			if (!($fp = Vfs::fopen($file=$path.($params['name'] && $isMultipleDownload ? '/'.$params['name'] : ''),'wb')) ||
2725
				!fwrite($fp,$attachment['attachment']))
2726
			{
2727
				$err .= lang('Error saving %1!',$file);
2728
			}
2729
			if ($fp)
2730
			{
2731
				fclose($fp);
2732
			}
2733
		}
2734
		$this->mail_bo->closeConnection();
2735
		if ($rememberServerID != $this->mail_bo->profileID)
2736
		{
2737
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2738
			$this->changeProfile($rememberServerID);
2739
		}
2740
		Framework::window_close(($err?$err:null));
2741
	}
2742
2743
	/**
2744
	 * Zip all attachments and send to user
2745
	 * @param string $message_id = null
2746
	 */
2747
	function download_zip($message_id=null)
2748
	{
2749
		//error_log(__METHOD__.__LINE__.array2string($_GET));
2750
		// First, get all attachment IDs
2751
		if(isset($_GET['id'])) $message_id	= $_GET['id'];
2752
		//error_log(__METHOD__.__LINE__.$message_id);
2753
		$rememberServerID = $this->mail_bo->profileID;
2754
		if(!is_numeric($message_id))
2755
		{
2756
			$hA = self::splitRowID($message_id);
2757
			$message_id = $hA['msgUID'];
2758
			$mailbox = $hA['folder'];
2759
			$icServerID = $hA['profileID'];
2760
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2761
			{
2762
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2763
				$this->changeProfile($icServerID);
2764
			}
2765
		}
2766
		else
2767
		{
2768
			$mailbox = $this->mail_bo->sessionData['mailbox'];
2769
		}
2770
		// always fetch all, even inline (images)
2771
		$fetchEmbeddedImages = true;
2772
		$attachments = $this->mail_bo->getMessageAttachments($message_id,null, null, $fetchEmbeddedImages, true,true,$mailbox);
2773
		// put them in VFS so they can be zipped
2774
		$header = $this->mail_bo->getMessageHeader($message_id,'',true,false,$mailbox);
2775
		//get_home_dir may fetch the users startfolder if set; if not writeable, action will fail. TODO: use temp_dir
2776
		$homedir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
2777
		$temp_path = $homedir/*Vfs::get_home_dir()*/ . "/.mail_$message_id";
2778
		if(Vfs::is_dir($temp_path)) Vfs::remove ($temp_path);
2779
2780
		// Add subject to path, so it gets used as the file name
2781
		$path = $temp_path . '/' . ($header['SUBJECT'] ? Vfs::encodePathComponent($header['SUBJECT']) : lang('mail')) .'/';
2782
		if(!Vfs::mkdir($path, 0700, true))
2783
		{
2784
			Framework::message("Unable to open temp directory $path",'error');
2785
			return;
2786
		}
2787
2788
		$file_list = array();
2789
		$dupe_count = array();
2790
		$this->mail_bo->reopen($mailbox);
2791
		if ($attachments[0]['is_winmail'] && $attachments[0]['is_winmail']!='null')
2792
		{
2793
			$tnefAttachments = $this->mail_bo->getTnefAttachments($message_id, $attachments[0]['partID'],true);
2794
		}
2795
		foreach($attachments as $file)
2796
		{
2797
			if ($file['is_winmail'])
2798
			{
2799
				// Try to find the right content for file id
2800
				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...
2801
				{
2802
					error_log(__METHOD__.' winmail = '.$key);
2803
					if ($key == $file['is_winmail']) $attachment = $val;
2804
				}
2805
			}
2806
			else
2807
			{
2808
				$attachment = $this->mail_bo->getAttachment($message_id,$file['partID'],$file['is_winmail'],false,true);
2809
			}
2810
			$success=true;
2811
			if (empty($file['filename'])) $file['filename'] = $file['name'];
2812
			if(in_array($path.$file['filename'], $file_list))
2813
			{
2814
				$dupe_count[$path.$file['filename']]++;
2815
				$file['filename'] = pathinfo($file['filename'], PATHINFO_FILENAME) .
2816
					' ('.($dupe_count[$path.$file['filename']] + 1).')' . '.' .
2817
					pathinfo($file['filename'], PATHINFO_EXTENSION);
2818
			}
2819
			if (!($fp = Vfs::fopen($path.$file['filename'],'wb')) ||
2820
				!(!fseek($attachment['attachment'], 0, SEEK_SET) && stream_copy_to_stream($attachment['attachment'], $fp)))
2821
			{
2822
				$success=false;
2823
				Framework::message("Unable to zip {$file['filename']}",'error');
2824
			}
2825
			if ($success) $file_list[] = $path.$file['filename'];
2826
			if ($fp) fclose($fp);
2827
		}
2828
		$this->mail_bo->closeConnection();
2829
		if ($rememberServerID != $this->mail_bo->profileID)
2830
		{
2831
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2832
			$this->changeProfile($rememberServerID);
2833
		}
2834
2835
		// Zip it up
2836
		Vfs::download_zip($file_list);
2837
2838
		// Clean up
2839
		Vfs::remove($temp_path);
2840
2841
		exit();
2842
	}
2843
2844
	function get_load_email_data($uid, $partID, $mailbox,$htmlOptions=null)
2845
	{
2846
		// seems to be needed, as if we open a mail from notification popup that is
2847
		// located in a different folder, we experience: could not parse message
2848
		$this->mail_bo->reopen($mailbox);
2849
		$this->mailbox = $mailbox;
2850
		$this->uid = $uid;
2851
		$this->partID = $partID;
2852
		$bufferHtmlOptions = $this->mail_bo->htmlOptions;
2853
		if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions;
2854
		// fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice
2855
		$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
2856
		$bodyParts	= $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox);
2857
2858
		//error_log(__METHOD__.__LINE__.array2string($bodyParts));
2859
		// attachments here are only fetched to determine if there is a meeting request
2860
		// and if. use the appropriate action. so we do not need embedded images
2861
		$fetchEmbeddedImages = false;
2862
		$attachments = (array)$this->mail_bo->getMessageAttachments($uid, $partID, $structure, $fetchEmbeddedImages, true,true,$mailbox);
2863
		//error_log(__METHOD__.__LINE__.array2string($attachments));
2864
		foreach ($attachments as &$attach)
2865
		{
2866
			if (strtolower($attach['mimeType']) == 'text/calendar' &&
2867
				isset($GLOBALS['egw_info']['user']['apps']['calendar']) &&
2868
				($attachment = $this->mail_bo->getAttachment($uid, $attach['partID'],$attach['is_winmail'],(strtolower($attach['mimeType']) == 'text/calendar'?false:true))))
2869
			{
2870
				//error_log(__METHOD__.__LINE__.array2string($attachment));
2871
				Api\Cache::setSession('calendar', 'ical', array(
2872
					'charset' => $attach['charset'] ? $attach['charset'] : 'utf-8',
2873
					'attachment' => $attachment['attachment'],
2874
					'method' => $attach['method'],
2875
					'sender' => $mailbox,
2876
				));
2877
				$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2878
				Api\Translation::add_app('calendar');
2879
				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...
2880
					array('event'=>null,'msg'=>'','useSession'=>true)
2881
				);
2882
			}
2883
		}
2884
		// Compose the content of the frame
2885
		$frameHtml =
2886
			$this->get_email_header($this->mail_bo->getStyles($bodyParts)).
2887
			$this->showBody($this->getdisplayableBody($bodyParts,true,false), false);
2888
		//IE10 eats away linebreaks preceeded by a whitespace in PRE sections
2889
		$frameHtml = str_replace(" \r\n","\r\n",$frameHtml);
2890
		$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2891
2892
		return $frameHtml;
2893
	}
2894
2895
	static function get_email_header($additionalStyle='')
2896
	{
2897
		// egw_info[flags][css] already include <style> tags
2898
		$GLOBALS['egw_info']['flags']['css'] = preg_replace('|</?style[^>]*>|i', '', $additionalStyle);
2899
		$GLOBALS['egw_info']['flags']['nofooter']=true;
2900
		$GLOBALS['egw_info']['flags']['nonavbar']=true;
2901
		// do NOT include any default CSS
2902
		Framework::includeCSS('mail', 'preview', true, true);
2903
2904
		// load preview.js to activate mailto links
2905
		Framework::includeJS('/mail/js/preview.js');
2906
2907
		// send CSP and content-type header
2908
		return $GLOBALS['egw']->framework->header();
2909
	}
2910
2911
	function showBody(&$body, $print=true,$fullPageTags=true)
2912
	{
2913
		$BeginBody = '<div class="mailDisplayBody">
2914
<table width="100%" style="table-layout:fixed"><tr><td class="td_display">';
2915
2916
		$EndBody = '</td></tr></table></div>';
2917
		if ($fullPageTags) $EndBody .= "</body></html>";
2918
		if ($print)	{
2919
			print $BeginBody. $body .$EndBody;
2920
		} else {
2921
			return $BeginBody. $body .$EndBody;
2922
		}
2923
	}
2924
2925
	function &getdisplayableBody($_bodyParts,$modifyURI=true,$useTidy = true)
2926
	{
2927
		$bodyParts	= $_bodyParts;
2928
2929
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2930
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2931
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2932
2933
		$body = '';
2934
2935
		//error_log(__METHOD__.array2string($bodyParts)); //exit;
2936
		if (empty($bodyParts)) return "";
2937
		foreach((array)$bodyParts as $singleBodyPart) {
2938
			if (!isset($singleBodyPart['body'])) {
2939
				$singleBodyPart['body'] = $this->getdisplayableBody($singleBodyPart,$modifyURI,$useTidy);
2940
				$body .= $singleBodyPart['body'];
2941
				continue;
2942
			}
2943
			$bodyPartIsSet = strlen(trim($singleBodyPart['body']));
2944
			if (!$bodyPartIsSet)
2945
			{
2946
				$body .= '';
2947
				continue;
2948
			}
2949
			if(!empty($body)) {
2950
				$body .= '<hr style="border:dotted 1px silver;">';
2951
			}
2952
			//error_log($singleBodyPart['body']);
2953
			//error_log(__METHOD__.__LINE__.' CharSet:'.$singleBodyPart['charSet'].' mimeType:'.$singleBodyPart['mimeType']);
2954
			// some characterreplacements, as they fail to translate
2955
			$sar = array(
2956
				'@(\x84|\x93|\x94)@',
2957
				'@(\x96|\x97|\x1a)@',
2958
				'@(\x82|\x91|\x92)@',
2959
				'@(\x85)@',
2960
				'@(\x86)@',
2961
				'@(\x99)@',
2962
				'@(\xae)@',
2963
			);
2964
			$rar = array(
2965
				'"',
2966
				'-',
2967
				'\'',
2968
				'...',
2969
				'&',
2970
				'(TM)',
2971
				'(R)',
2972
			);
2973
2974
			if(($singleBodyPart['mimeType'] == 'text/html' || $singleBodyPart['mimeType'] == 'text/plain') &&
2975
				strtoupper($singleBodyPart['charSet']) != 'UTF-8')
2976
			{
2977
				$singleBodyPart['body'] = preg_replace($sar,$rar,$singleBodyPart['body']);
2978
			}
2979
			//error_log(__METHOD__.__LINE__.'reports:'.$singleBodyPart['charSet']);
2980
			if ($singleBodyPart['charSet']=='us-ascii')
2981
			{
2982
				$orgCharSet=$singleBodyPart['charSet'];
2983
				$singleBodyPart['charSet'] = Api\Translation::detect_encoding($singleBodyPart['body']);
2984
				error_log(__METHOD__.__LINE__.'reports:'.$orgCharSet.' but seems to be:'.$singleBodyPart['charSet']);
2985
			}
2986
			$singleBodyPart['body'] = Api\Translation::convert_jsonsafe($singleBodyPart['body'],$singleBodyPart['charSet']);
2987
			//error_log(__METHOD__.__LINE__.array2string($singleBodyPart));
2988
			if($singleBodyPart['mimeType'] == 'text/plain')
2989
			{
2990
				$newBody	= @htmlentities($singleBodyPart['body'],ENT_QUOTES, strtoupper(Mail::$displayCharset));
2991
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
2992
				// if empty and charset is utf8 try sanitizing the string in question
2993
				if (empty($newBody) && strtolower($singleBodyPart['charSet'])=='utf-8') $newBody = @htmlentities(iconv('utf-8', 'utf-8', $singleBodyPart['body']),ENT_QUOTES, strtoupper(Mail::$displayCharset));
2994
				// if the conversion to htmlentities fails somehow, try without specifying the charset, which defaults to iso-
2995
				if (empty($newBody)) $newBody    = htmlentities($singleBodyPart['body'],ENT_QUOTES);
2996
2997
				// search http[s] links and make them as links available again
2998
				// to understand what's going on here, have a look at
2999
				// http://www.php.net/manual/en/function.preg-replace.php
3000
3001
				// create links for websites
3002
				if ($modifyURI) $newBody = Api\Html::activate_links($newBody);
3003
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3004
				// redirect links for websites if you use no cookies
3005
				#if (!($GLOBALS['egw_info']['server']['usecookies']))
3006
				#	$newBody = preg_replace("/href=(\"|\')((http(s?):\/\/)|(www\.))([\w,\-,\/,\?,\=,\.,&amp;,!\n,\%,@,\(,\),\*,#,:,~,\+]+)(\"|\')/ie",
3007
				#		"'href=\"$webserverURL/redirect.php?go='.@htmlentities(urlencode('http$4://$5$6'),ENT_QUOTES,\"Mail::$displayCharset\").'\"'", $newBody);
3008
3009
				// create links for email addresses
3010
				//TODO:if ($modifyURI) $this->parseEmail($newBody);
3011
				// create links for inline images
3012
				if ($modifyURI)
3013
				{
3014
					$newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain');
3015
				}
3016
3017
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3018
				// to display a mailpart of mimetype plain/text, may be better taged as preformatted
3019
				#$newBody	= nl2br($newBody);
3020
				// since we do not display the message as HTML anymore we may want to insert good linebreaking (for visibility).
3021
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3022
				// dont break lines that start with > (&gt; as the text was processed with htmlentities before)
3023
				$newBody	= "<pre>".Mail::wordwrap($newBody,90,"\n",'&gt;')."</pre>";
3024
			}
3025
			else
3026
			{
3027
				$alreadyHtmlLawed=false;
3028
				$newBody	= $singleBodyPart['body'];
3029
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3030
				#error_log(print_r($newBody,true));
3031 View Code Duplication
				if ($useTidy && extension_loaded('tidy'))
3032
				{
3033
					$tidy = new tidy();
3034
					$cleaned = $tidy->repairString($newBody, Mail::$tidy_config,'utf8');
3035
					// Found errors. Strip it all so there's some output
3036
					if($tidy->getStatus() == 2)
3037
					{
3038
						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...
3039
					}
3040
					else
3041
					{
3042
						$newBody = $cleaned;
3043
					}
3044
					if (!$preserveHTML)	// ToDo KL: $preserveHTML is NOT initialised, so always if is dead code
3045
					{
3046
						// filter only the 'body', as we only want that part, if we throw away the Api\Html
3047
						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...
3048
						if ($matches[2])
3049
						{
3050
							$hasOther = true;
3051
							$newBody = $matches[2];
3052
						}
3053
					}
3054
				}
3055
				else
3056
				{
3057
					// htmLawed filter only the 'body'
3058
					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...
3059
					if ($matches[2])
3060
					{
3061
						$hasOther = true;
3062
						$newBody = $matches[2];
3063
					}
3064
					$htmLawed = new Api\Html\HtmLawed();
3065
					// the next line should not be needed, but produces better results on HTML 2 Text conversion,
3066
					// as we switched off HTMLaweds tidy functionality
3067
					$newBody = str_replace(array('&amp;amp;','<DIV><BR></DIV>',"<DIV>&nbsp;</DIV>",'<div>&nbsp;</div>'),array('&amp;','<BR>','<BR>','<BR>'),$newBody);
3068
					$newBody = $htmLawed->run($newBody,Mail::$htmLawed_config);
3069
					if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3];
3070
					$alreadyHtmlLawed=true;
3071
				}
3072
				// do the cleanup, set for the use of purifier
3073
				//$newBodyBuff = $newBody;
3074
				/* if (!$alreadyHtmlLawed)*/ Mail::getCleanHTML($newBody);
3075
/*
3076
				// 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
3077
				if (strtoupper(Mail::$displayCharset) == 'UTF-8')
3078
				{
3079
					$test = @json_encode($newBody);
3080
					//error_log(__METHOD__.__LINE__.' ->'.strlen($singleBodyPart['body']).' Error:'.json_last_error().'<- BodyPart:#'.$test.'#');
3081
					if (($test=="null" || $test === false || is_null($test)) && strlen($newBody)>0)
3082
					{
3083
						$newBody = $newBodyBuff;
3084
						$tv = Mail::$htmLawed_config['tidy'];
3085
						Mail::$htmLawed_config['tidy'] = 0;
3086
						Mail::getCleanHTML($newBody);
3087
						Mail::$htmLawed_config['tidy'] = $tv;
3088
					}
3089
				}
3090
*/
3091
				// removes stuff between http and ?http
3092
				$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';    // only http:// gets removed, other protocolls are shown
3093
				$newBody = preg_replace('~'.$Protocol.'[^>]*\?'.$Protocol.'~sim','$1',$newBody); // removes stuff between http:// and ?http://
3094
				// TRANSFORM MAILTO LINKS TO EMAILADDRESS ONLY, WILL BE SUBSTITUTED BY parseEmail TO CLICKABLE LINK
3095
				$newBody = preg_replace('/(?<!"|href=|href\s=\s|href=\s|href\s=)'.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
3096
					"\\1@\\2.\\3",
3097
					$newBody);
3098
3099
				// redirect links for websites if you use no cookies
3100
				#if (!($GLOBALS['egw_info']['server']['usecookies'])) { //do it all the time, since it does mask the mailadresses in urls
3101
					//TODO:if ($modifyURI) $this->parseHREF($newBody);
3102
				#}
3103
				// create links for inline images
3104
				if ($modifyURI)
3105
				{
3106
					$newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID);
3107
				}
3108
				// email addresses / mailto links get now activated on client-side
3109
			}
3110
3111
			$body .= $newBody;
3112
		}
3113
		// create links for windows shares
3114
		// \\\\\\\\ == '\\' in real life!! :)
3115
		$body = preg_replace("/(\\\\\\\\)([\w,\\\\,-]+)/i",
3116
			"<a href=\"file:$1$2\" target=\"_blank\"><font color=\"blue\">$1$2</font></a>", $body);
3117
3118
		$body = preg_replace($nonDisplayAbleCharacters,'',$body);
3119
3120
		return $body;
3121
	}
3122
3123
	/**
3124
	 * Resolve inline images from CID to proper url
3125
	 *
3126
	 * @param string $_body message content
3127
	 * @param string $_mailbox mail folder
3128
	 * @param string $_uid uid
3129
	 * @param string $_partID part id
3130
	 * @param string $_messageType = 'html', message type is either html or plain
3131
	 * @return string message body including all CID images replaced
3132
	 */
3133
	public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html')
3134
	{
3135
		if ($_messageType === 'plain')
3136
		{
3137
			return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain');
3138
		}
3139
		else
3140
		{
3141
			foreach(array('src','url','background') as $type)
3142
			{
3143
				$_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type);
3144
			}
3145
			return $_body;
3146
		}
3147
	}
3148
3149
	/**
3150
	 * Replace CID with proper type of content understandable by browser
3151
	 *
3152
	 * @param type $_body content of message
3153
	 * @param type $_mailbox mail box
3154
	 * @param type $_uid uid
3155
	 * @param type $_partID part id
3156
	 * @param type $_type = 'src' type of inline image that needs to be resolved and replaced
3157
	 *	- types: {plain|src|url|background}
3158
	 * @return string returns body content including all CID replacements
3159
	 */
3160
	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src')
3161
	{
3162
		/**
3163
		 * Callback for preg_replace_callback function
3164
		 * returns matched CID replacement string based on given type
3165
		 * @param array $matches
3166
		 * @param string $_mailbox
3167
		 * @param string $_uid
3168
		 * @param string $_partID
3169
		 * @param string $_type
3170
		 * @return string|boolean returns the replace
3171
		*/
3172
		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type)
3173
		{
3174
			if (!$_type)	return false;
3175
			$CID = '';
3176
			// Build up matches according to selected type
3177
			switch ($_type)
3178
			{
3179
				case "plain":
3180
					$CID = $matches[1];
3181
					break;
3182
				case "src":
3183
					// as src:cid contains some kind of url, it is likely to be urlencoded
3184
					$CID = urldecode($matches[2]);
3185
					break;
3186
				case "url":
3187
					$CID = $matches[1];
3188
					break;
3189
				case "background":
3190
					$CID = $matches[2];
3191
					break;
3192
			}
3193
3194
			static $cache = array();	// some caching, if mails containing the same image multiple times
3195
3196
			if (is_array($matches) && $CID)
3197
			{
3198
				$linkData = array (
3199
					'menuaction'    => 'mail.mail_ui.displayImage',
3200
					'uid'		=> $_uid,
3201
					'mailbox'	=> base64_encode($_mailbox),
3202
					'cid'		=> base64_encode($CID),
3203
					'partID'	=> $_partID,
3204
				);
3205
				$imageURL = Egw::link('/index.php', $linkData);
3206
				// to test without data uris, comment the if close incl. it's body
3207
				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
3208
				{
3209
					if (!isset($cache[$imageURL]))
3210
					{
3211
						if ($_type !="background")
3212
						{
3213
							$bo = Mail::getInstance(false, mail_ui::$icServerID);
3214
							$attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID);
3215
3216
							// only use data uri for "smaller" images, as otherwise the first display of the mail takes to long
3217
							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...
3218
							{
3219
								$bo->fetchPartContents($_uid, $attachment);
3220
								$cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents());
3221
							}
3222
							else
3223
							{
3224
								$cache[$imageURL] = $imageURL;
3225
							}
3226
						}
3227
						else
3228
						{
3229
							$cache[$imageURL] = $imageURL;
3230
						}
3231
					}
3232
					$imageURL = $cache[$imageURL];
3233
				}
3234
3235
				// Decides the final result of replacement according to the type
3236
				switch ($_type)
3237
				{
3238
					case "plain":
3239
						return '<img src="'.$imageURL.'" />';
3240
					case "src":
3241
						return 'src="'.$imageURL.'"';
3242
					case "url":
3243
						return 'url('.$imageURL.');';
3244
					case "background":
3245
						return 'background="'.$imageURL.'"';
3246
				}
3247
			}
3248
			return false;
3249
		};
3250
3251
		// return new body content base on chosen type
3252
		switch($_type)
3253
		{
3254
			case"plain":
3255
				return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body);
3256
			case "src":
3257
				return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3258
			case "url":
3259
				return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body);
3260
			case "background":
3261
				return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3262
		}
3263
	}
3264
3265
	/**
3266
	 * importMessage
3267
	 * @param array $content = null an array of content
3268
	 */
3269
	function importMessage($content=null)
3270
	{
3271
		//error_log(__METHOD__.__LINE__.$this->mail_bo->getDraftFolder());
3272
3273
		if (!empty($content))
3274
		{
3275
			//error_log(__METHOD__.__LINE__.array2string($content));
3276
			if ($content['vfsfile'])
3277
			{
3278
				$file = $content['vfsfile'] = array(
3279
					'name' => Vfs::basename($content['vfsfile']),
3280
					'type' => Vfs::mime_content_type($content['vfsfile']),
3281
					'file' => Vfs::PREFIX.$content['vfsfile'],
3282
					'size' => filesize(Vfs::PREFIX.$content['vfsfile']),
3283
				);
3284
			}
3285
			else
3286
			{
3287
				$file = $content['uploadForImport'];
3288
			}
3289
			$destination = $content['FOLDER'][0];
3290
3291
			if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
3292
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3293
			{
3294
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3295
				$this->changeProfile($icServerID);
3296
			}
3297
			//error_log(__METHOD__.__LINE__.self::$delimiter.array2string($destination));
3298
			$importID = Mail::getRandomString();
3299
			$importFailed = false;
3300
			try
3301
			{
3302
				$messageUid = $this->importMessageToFolder($file,$destination,$importID);
3303
			    $linkData = array
3304
			    (
3305
					'id' => $this->createRowID($destination, $messageUid, true),
3306
			    );
3307
			}
3308
			catch (Api\Exception\WrongUserinput $e)
3309
			{
3310
					$importFailed=true;
3311
					$content['msg']		= $e->getMessage();
3312
			}
3313
			if (!$importFailed)
3314
			{
3315
				list($width, $height) = explode('x', Link::get_registry('mail', 'add_popup'));
3316
				if ($width > 0 && $height > 0) Api\Json\Response::get()->call('resizeTo', $width, $height);
3317
				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...
3318
				return;
3319
			}
3320
		}
3321
		if (!is_array($content)) $content = array();
3322
		if (empty($content['FOLDER'])) $content['FOLDER']=(array)$this->mail_bo->getDraftFolder();
3323
		if (!empty($content['FOLDER'])) $sel_options['FOLDER']=mail_compose::ajax_searchFolder(0,true);
3324
3325
		$etpl = new Etemplate('mail.importMessage');
3326
		$etpl->setElementAttribute('uploadForImport','onFinish','app.mail.uploadForImport');
3327
		$etpl->exec('mail.mail_ui.importMessage',$content,$sel_options,array(),array(),2);
3328
	}
3329
3330
	/**
3331
	 * importMessageToFolder
3332
	 *
3333
	 * @param array $_formData Array with information of name, type, file and size
3334
	 * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if
3335
	 *					folder is modified
3336
	 * @param string $importID ID for the imported message, used by attachments to identify them unambiguously
3337
	 * @return mixed $messageUID or exception
3338
	 */
3339
	function importMessageToFolder($_formData,&$_folder,$importID='')
3340
	{
3341
		$importfailed = false;
3342
		//error_log(__METHOD__.__LINE__.array2string($_formData));
3343
		if (empty($_formData['file'])) $_formData['file'] = $_formData['tmp_name'];
3344
		// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
3345
		try
3346
		{
3347
			$tmpFileName = Mail::checkFileBasics($_formData,$importID);
3348
		}
3349
		catch (Api\Exception\WrongUserinput $e)
3350
		{
3351
			$importfailed = true;
3352
			$alert_msg .= $e->getMessage();
3353
		}
3354
		// -----------------------------------------------------------------------
3355
		if ($importfailed === false)
3356
		{
3357
			$mailObject = new Api\Mailer();
3358
			try
3359
			{
3360
				$this->mail_bo->parseFileIntoMailObject($mailObject, $tmpFileName);
3361
			}
3362
			catch (Api\Exception\AssertionFailed $e)
3363
			{
3364
				$importfailed = true;
3365
				$alert_msg .= $e->getMessage();
3366
			}
3367
			$this->mail_bo->openConnection();
3368
			if (empty($_folder))
3369
			{
3370
				$importfailed = true;
3371
				$alert_msg .= lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);
3372
			}
3373
			$delimiter = $this->mail_bo->getHierarchyDelimiter();
3374
			if($_folder=='INBOX'.$delimiter) $_folder='INBOX';
3375
			if ($importfailed === false)
3376
			{
3377
				if ($this->mail_bo->folderExists($_folder,true)) {
3378
					try
3379
					{
3380
						$messageUid = $this->mail_bo->appendMessage($_folder,
3381
							$mailObject->getRaw(),
3382
							null,'\\Seen');
3383
					}
3384
					catch (Api\Exception\WrongUserinput $e)
3385
					{
3386
						$importfailed = true;
3387
						$alert_msg .= lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$_formData['name'],$_folder,$e->getMessage());
3388
					}
3389
				}
3390
				else
3391
				{
3392
					$importfailed = true;
3393
					$alert_msg .= lang("Import of message %1 failed. Destination Folder %2 does not exist.",$_formData['name'],$_folder);
3394
				}
3395
			}
3396
		}
3397
		// set the url to open when refreshing
3398
		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...
3399
		{
3400
			throw new Api\Exception\WrongUserinput($alert_msg);
3401
		}
3402
		else
3403
		{
3404
			return $messageUid;
3405
		}
3406
	}
3407
3408
	/**
3409
	 * importMessageFromVFS2DraftAndEdit
3410
	 *
3411
	 * @param array $formData Array with information of name, type, file and size; file is required,
3412
	 *                               name, type and size may be set here to meet the requirements
3413
	 *						Example: $formData['name']	= 'a_email.eml';
3414
	 *								 $formData['type']	= 'message/rfc822';
3415
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3416
	 *								 $formData['size']	= 2136;
3417
	 * @return void
3418
	 */
3419
	function importMessageFromVFS2DraftAndEdit($formData='')
3420
	{
3421
		$this->importMessageFromVFS2DraftAndDisplay($formData,'edit');
3422
	}
3423
3424
	/**
3425
	 * importMessageFromVFS2DraftAndDisplay
3426
	 *
3427
	 * @param array $formData Array with information of name, type, file and size; file is required,
3428
	 *                               name, type and size may be set here to meet the requirements
3429
	 *						Example: $formData['name']	= 'a_email.eml';
3430
	 *								 $formData['type']	= 'message/rfc822';
3431
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3432
	 *								 $formData['size']	= 2136;
3433
	 * @param string $mode mode to open ImportedMessage display and edit are supported
3434
	 * @return void
3435
	 */
3436
	function importMessageFromVFS2DraftAndDisplay($formData='',$mode='display')
3437
	{
3438
		if (empty($formData)) if (isset($_REQUEST['formData'])) $formData = $_REQUEST['formData'];
3439
		//error_log(__METHOD__.__LINE__.':'.array2string($formData).' Mode:'.$mode.'->'.function_backtrace());
3440
		$draftFolder = $this->mail_bo->getDraftFolder(false);
3441
		$importID = Mail::getRandomString();
3442
3443
		// handling for mime-data hash
3444
		if (!empty($formData['data']))
3445
		{
3446
			$formData['file'] = 'egw-data://'.$formData['data'];
3447
		}
3448
		// name should be set to meet the requirements of checkFileBasics
3449
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['name']))
3450
		{
3451
			$buff = explode('/',$formData['file']);
3452
			if (is_array($buff)) $formData['name'] = array_pop($buff); // take the last part as name
3453
		}
3454
		// type should be set to meet the requirements of checkFileBasics
3455
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['type']))
3456
		{
3457
			$buff = explode('.',$formData['file']);
3458
			$suffix = '';
3459
			if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
3460
			if (!empty($suffix)) $formData['type'] = Api\MimeMagic::ext2mime($suffix);
3461
		}
3462
		// size should be set to meet the requirements of checkFileBasics
3463
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && !isset($formData['size']))
3464
		{
3465
			$formData['size'] = strlen($formData['file']); // set some size, to meet requirements of checkFileBasics
3466
		}
3467
		try
3468
		{
3469
			$messageUid = $this->importMessageToFolder($formData,$draftFolder,$importID);
3470
			$linkData = array
3471
			(
3472
		        'menuaction'    => ($mode=='display'?'mail.mail_ui.displayMessage':'mail.mail_compose.composeFromDraft'),
3473
				'id'		=> $this->createRowID($draftFolder,$messageUid,true),
3474
				'deleteDraftOnClose' => 1,
3475
			);
3476
			if ($mode!='display')
3477
			{
3478
				unset($linkData['deleteDraftOnClose']);
3479
				$linkData['method']	='importMessageToMergeAndSend';
3480
			}
3481
			else
3482
			{
3483
				$linkData['mode']=$mode;
3484
			}
3485
			Egw::redirect_link('/index.php',$linkData);
3486
		}
3487
		catch (Api\Exception\WrongUserinput $e)
3488
		{
3489
			Framework::window_close($e->getMessage());
3490
		}
3491
	}
3492
3493
	/**
3494
	 * loadEmailBody
3495
	 *
3496
	 * @param string _messageID UID
3497
	 *
3498
	 * @return xajax response
3499
	 */
3500
	function loadEmailBody($_messageID=null,$_partID=null,$_htmloptions=null)
3501
	{
3502
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3503
		if (!$_messageID && !empty($_GET['_messageID'])) $_messageID = $_GET['_messageID'];
3504
		if (!$_partID && !empty($_GET['_partID'])) $_partID = $_GET['_partID'];
3505
		if (!$_htmloptions && !empty($_GET['_htmloptions'])) $_htmloptions = $_GET['_htmloptions'];
3506
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageID,true).",$_partID,$_htmloptions");
3507
		if (empty($_messageID)) return "";
3508
		$uidA = self::splitRowID($_messageID);
3509
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
3510
		$messageID = $uidA['msgUID'];
3511
		$icServerID = $uidA['profileID'];
3512
		//something went wrong. there is a $_messageID but no $messageID: means $_messageID is crippeled
3513
		if (empty($messageID)) return "";
3514
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
3515
		{
3516
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3517
			$this->changeProfile($icServerID);
3518
		}
3519
3520
		$bodyResponse = $this->get_load_email_data($messageID,$_partID,$folder,$_htmloptions);
3521
		Api\Session::cache_control(true);
3522
		//error_log(array2string($bodyResponse));
3523
		echo $bodyResponse;
3524
3525
	}
3526
3527
	/**
3528
	 * ajax_setFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3529
	 * gets the counters and sets the text of a treenode if needed (unread Messages found)
3530
	 * @param array $_folder folders to refresh its unseen message counters
3531
	 * @return nothing
3532
	 */
3533
	function ajax_setFolderStatus($_folder)
3534
	{
3535
		Api\Translation::add_app('mail');
3536
		//error_log(__METHOD__.__LINE__.array2string($_folder));
3537
		if ($_folder)
3538
		{
3539
			$this->mail_bo->getHierarchyDelimiter(false);
3540
			$oA = array();
3541
			foreach ($_folder as $_folderName)
3542
			{
3543
				list($profileID,$folderName) = explode(self::$delimiter,$_folderName,2);
3544
				if (is_numeric($profileID))
3545
				{
3546
					if ($profileID != $this->mail_bo->profileID) continue; // only current connection
3547
					if ($folderName)
3548
					{
3549
						try
3550
						{
3551
							$fS = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3552
						}
3553
						catch (Exception $e)
3554
						{
3555
							continue;
3556
						}
3557
						if (in_array($fS['shortDisplayName'],Mail::$autoFolders)) $fS['shortDisplayName']=lang($fS['shortDisplayName']);
3558
						//error_log(__METHOD__.__LINE__.array2string($fS));
3559
						if ($fS['unseen'])
3560
						{
3561
							$oA[$_folderName] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3562
						}
3563
						if ($fS['unseen']==0 && $fS['shortDisplayName'])
3564
						{
3565
							$oA[$_folderName] = $fS['shortDisplayName'];
3566
						}
3567
					}
3568
				}
3569
			}
3570
			//error_log(__METHOD__.__LINE__.array2string($oA));
3571
			if ($oA)
3572
			{
3573
				$response = Api\Json\Response::get();
3574
				$response->call('app.mail.mail_setFolderStatus',$oA);
3575
			}
3576
		}
3577
	}
3578
3579
	/**
3580
	 * ajax_addFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3581
	 * @param string $_parentFolderName folder to add a folder to
3582
	 * @param string $_newName new foldername
3583
	 * @return nothing
3584
	 */
3585
	function ajax_addFolder($_parentFolderName, $_newName)
3586
	{
3587
		//error_log(__METHOD__.__LINE__.' ParentFolderName:'.array2string($_parentFolderName).' NewName/Folder:'.array2string($_newName));
3588
		$errorMessage='';
3589
		if ($_parentFolderName)
3590
		{
3591
			$created = false;
3592
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_parentFolderName);
3593
			//the conversion is handeled by horde, frontend interaction is all utf-8
3594
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3595
			list($profileID,$parentFolderName) = explode(self::$delimiter,$decodedFolderName,2);
3596
			if (is_numeric($profileID))
3597
			{
3598
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3599
				$del = $this->mail_bo->getHierarchyDelimiter(false);
3600
				//$del = $prefix = '';
3601
				//$nameSpace = $this->mail_bo->_getNameSpaces();
3602
				//error_log(__METHOD__.__LINE__.array2string($nameSpace));
3603
				// we expect something like that: data may differ!
3604
				//$nameSpace = Array(
3605
				//	[0] => Array([prefix_present] => [prefix] => [delimiter] => /[type] => personal)
3606
				//	[1] => Array([prefix_present] => 1[prefix] => Other Users/[delimiter] => /[type] => others)
3607
				//	[2] => Array([prefix_present] => 1[prefix] => Shared Folders/[delimiter] => /[type] => shared)
3608
				//)
3609
				//
3610
				/*
3611
				foreach ($nameSpace as $nSp)
3612
				{
3613
					error_log(__METHOD__.__LINE__.array2string($nSp));
3614
					// personal is assumed to be the default
3615
					if ($nSp['type']=='personal')
3616
					{
3617
						$prefix = $nSp['prefix'];
3618
						$del = $nSp['delimiter'];
3619
					}
3620
					if ($parentFolderName && $nSp['prefix_present'] && stripos($parentFolderName,$nSp['prefix'])!==false && stripos($parentFolderName,$nSp['prefix'])<=strlen($nSp['delimiter']))
3621
					{
3622
						$prefix = $nSp['prefix'];
3623
						$del = $nSp['delimiter'];
3624
						break;
3625
					}
3626
					if (empty($parentFolderName) && !$nSp['prefix_present'])
3627
					{
3628
						$del = $nSp['delimiter'];
3629
						break;
3630
					}
3631
				}
3632
3633
				if (empty($del)) $del = $this->mail_bo->getHierarchyDelimiter(false);
3634
				*/
3635
				$nA = explode($del,$_newName);
3636
3637
				//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3638
				if (!!empty($parentFolderName)) $oldFolderInfo = $this->mail_bo->getFolderStatus($parentFolderName,false);
3639
				//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3640
3641
				$this->mail_bo->reopen('INBOX');
3642
				$parentName = $parentFolderName;
3643
				// if newName has delimiter ($del) in it, we need to create the subtree
3644
				if (!empty($nA))
3645
				{
3646
					$c=0;
3647
					foreach($nA as $sTName)
3648
					{
3649
						$error=null;
3650
						if(($parentFolderName = $this->mail_bo->createFolder($parentFolderName, $sTName, $error)))
3651
						{
3652
							$c++;
3653
						}
3654
						else
3655
						{
3656
							$errorMessage .= $error;
3657
						}
3658
					}
3659
					if ($c==count($nA)) $created=true;
3660
				}
3661
				if (!empty($parentName)) $this->mail_bo->reopen($parentName);
3662
			}
3663
			//error_log(__METHOD__.__LINE__.array2string($oA));
3664
			if ($created===true)
3665
			{
3666
				$this->mail_bo->resetFolderObjectCache($profileID);
3667
				$response = Api\Json\Response::get();
3668
				if ( $oldFolderInfo['shortDisplayName'])
3669
				{
3670
					$nodeInfo = array($_parentFolderName=>$oldFolderInfo['shortDisplayName']);
3671
				}
3672
				else
3673
				{
3674
					$nodeInfo = array($profileID=>lang('INBOX'));
3675
				}
3676
				$response->call('app.mail.mail_reloadNode',$nodeInfo);
3677
			}
3678
			else
3679
			{
3680
				if ($errorMessage)
3681
				{
3682
					$response = Api\Json\Response::get();
3683
					$response->call('egw.message',$errorMessage);
3684
				}
3685
			}
3686
		}
3687
	}
3688
3689
	/**
3690
	 * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3691
	 * @param string $_folderName folder to rename and refresh
3692
	 * @param string $_newName new foldername
3693
	 * @return nothing
3694
	 */
3695
	function ajax_renameFolder($_folderName, $_newName)
3696
	{
3697
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName).' NewName:'.array2string($_newName));
3698
		if ($_folderName)
3699
		{
3700
			Api\Translation::add_app('mail');
3701
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3702
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3703
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3704
			$oA = array();
3705
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3706
			$hasChildren = false;
3707
			if (is_numeric($profileID))
3708
			{
3709
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3710
				$pA = explode($del,$folderName);
3711
				array_pop($pA);
3712
				$parentFolder = implode($del,$pA);
3713
				if (strtoupper($folderName)!= 'INBOX')
3714
				{
3715
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3716
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false);
3717
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3718
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3719
					{
3720
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3721
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3722
						$nameSpace = $this->mail_bo->_getNameSpaces();
3723
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3724
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3725
						$fragments = array();
3726
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3727
						foreach ($subFolders as $k => $folder)
3728
						{
3729
							// we do not monitor failure or success on subfolders
3730
							if ($folder == $folderName)
3731
							{
3732
								unset($subFolders[$k]);
3733
							}
3734
							else
3735
							{
3736
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3737
								$fragments[$profileID.self::$delimiter.$folder] = substr($folder,strlen($folderName));
3738
							}
3739
						}
3740
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($fragments));
3741
					}
3742
3743
					$this->mail_bo->reopen('INBOX');
3744
					$success = false;
3745
					try
3746
					{
3747 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3748
						{
3749
							$this->mail_bo->resetFolderObjectCache($profileID);
3750
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3751
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3752
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3753
							$success = true;
3754
						}
3755
					}
3756
					catch (Exception $e)
3757
					{
3758
						$newFolderName=$folderName;
3759
						$msg = $e->getMessage();
3760
					}
3761
					$this->mail_bo->reopen($newFolderName);
3762
					$fS = $this->mail_bo->getFolderStatus($newFolderName,false);
3763
					//error_log(__METHOD__.__LINE__.array2string($fS));
3764 View Code Duplication
					if ($hasChildren)
3765
					{
3766
						$subFolders = $this->mail_bo->getMailBoxesRecursive($newFolderName, $delimiter, $prefix);
3767
						foreach ($subFolders as $k => $folder)
3768
						{
3769
							// we do not monitor failure or success on subfolders
3770
							if ($folder == $folderName)
3771
							{
3772
								unset($subFolders[$k]);
3773
							}
3774
							else
3775
							{
3776
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
3777
							}
3778
						}
3779
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
3780
					}
3781
3782
					$oA[$_folderName]['id'] = $profileID.self::$delimiter.$newFolderName;
3783
					$oA[$_folderName]['olddesc'] = $oldFolderInfo['shortDisplayName'];
3784 View Code Duplication
					if ($fS['unseen'])
3785
					{
3786
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3787
3788
					}
3789
					else
3790
					{
3791
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'];
3792
					}
3793
					foreach($fragments as $oldFolderName => $fragment)
3794
					{
3795
						//error_log(__METHOD__.__LINE__.':'.$oldFolderName.'->'.$profileID.self::$delimiter.$newFolderName.$fragment);
3796
						$oA[$oldFolderName]['id'] = $profileID.self::$delimiter.$newFolderName.$fragment;
3797
						$oA[$oldFolderName]['olddesc'] = '#skip-user-interaction-message#';
3798
						$fS = $this->mail_bo->getFolderStatus($newFolderName.$fragment,false);
3799 View Code Duplication
						if ($fS['unseen'])
3800
						{
3801
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3802
3803
						}
3804
						else
3805
						{
3806
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'];
3807
						}
3808
					}
3809
				}
3810
			}
3811 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
3812
			{
3813
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
3814
				$this->mail_bo->saveSessionData();
3815
			}
3816
			//error_log(__METHOD__.__LINE__.array2string($oA));
3817
			$response = Api\Json\Response::get();
3818
			if ($oA && $success)
3819
			{
3820
				$response->call('app.mail.mail_setLeaf',$oA);
3821
			}
3822
			else
3823
			{
3824
				$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 3793. 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...
3825
			}
3826
		}
3827
	}
3828
3829
	/**
3830
	 * reload node
3831
	 *
3832
	 * @param string _folderName  folder to reload
3833
	 * @param boolean $_subscribedOnly = true
3834
	 * @return void
3835
	 */
3836
	function ajax_reloadNode($_folderName,$_subscribedOnly=true)
3837
	{
3838
		Api\Translation::add_app('mail');
3839
		$oldPrefForSubscribedOnly = !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'];
3840
3841
		// prefs are plain prefs; we discussed an approach to have user only prefs, and
3842
		// set them on rightclick action on foldertree
3843
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
3844
3845
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3846
		$this->mail_bo->getHierarchyDelimiter(false);
3847
		list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3848
		// if pref and required mode dont match -> reset the folderObject cache to ensure
3849
		// that we get what we request
3850
		if ($_subscribedOnly != $oldPrefForSubscribedOnly) $this->mail_bo->resetFolderObjectCache($profileID);
3851
		if ($profileID != $this->mail_bo->profileID) return; // only current connection
3852
		if (!empty($folderName))
3853
		{
3854
			$parentFolder=(!empty($folderName)?$folderName:'INBOX');
3855
			$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
3856
			if ($folderInfo['unseen'])
3857
			{
3858
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'].' ('.$folderInfo['unseen'].')';
3859
			}
3860
			if ($folderInfo['unseen']==0 && $folderInfo['shortDisplayName'])
3861
			{
3862
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'];
3863
			}
3864
3865
			$refreshData = array(
3866
				$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
3867
		}
3868
		else
3869
		{
3870
			$refreshData = array(
3871
				$profileID=>lang('INBOX')//string with no meaning lateron
3872
			);
3873
		}
3874
		// Send full info back in the response
3875
		$response = Api\Json\Response::get();
3876
		foreach($refreshData as $folder => &$name)
3877
		{
3878
			$name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true);
3879
		}
3880
		$response->call('app.mail.mail_reloadNode',$refreshData);
3881
3882
	}
3883
3884
	/**
3885
	 * ResolveWinmail fetches the encoded attachments
3886
	 * from winmail.dat and will response expected structure back
3887
	 * to client in order to display them.
3888
	 *
3889
	 * Note: this ajax function should only be called via
3890
	 * nm mail selection as it does not support profile change
3891
	 * and uses the current available ic_server connection.
3892
	 *
3893
	 * @param type $_rowid row id from nm
3894
	 *
3895
	 */
3896
	function ajax_resolveWinmail ($_rowid)
3897
	{
3898
		$response = Api\Json\Response::get();
3899
3900
		$idParts = self::splitRowID($_rowid);
3901
		$uid = $idParts['msgUID'];
3902
		$mbox = $idParts['folder'];
3903
3904
		$attachments = $this->mail_bo->getMessageAttachments($uid, null, null, false,true,true,$mbox);
3905
		if (is_array($attachments))
3906
		{
3907
			$attachments = $this->createAttachmentBlock($attachments, $_rowid, $uid, $mbox, false);
3908
			$response->data($attachments);
3909
		}
3910
		else
3911
		{
3912
			$response->call('egw.message', lang('Can not resolve the winmail.dat attachment!'));
3913
		}
3914
	}
3915
3916
	/**
3917
	 * move folder
3918
	 *
3919
	 * @param string _folderName  folder to vove
3920
	 * @param string _target target folder
3921
	 *
3922
	 * @return void
3923
	 */
3924
	function ajax_MoveFolder($_folderName, $_target)
3925
	{
3926
		if (Mail::$debug) error_log(__METHOD__.__LINE__."Move Folder: $_folderName to Target: $_target");
3927
		if ($_folderName)
3928
		{
3929
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3930
			$_newLocation2 = $this->mail_bo->decodeEntityFolderName($_target);
3931
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3932
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3933
			list($newProfileID,$_newLocation) = explode(self::$delimiter,$_newLocation2,2);
3934
			$hasChildren = false;
3935
			if (is_numeric($profileID))
3936
			{
3937
				if ($profileID != $this->mail_bo->profileID || $profileID != $newProfileID) return; // only current connection
3938
				$pA = explode($del,$folderName);
3939
				$namePart = array_pop($pA);
3940
				$_newName = $namePart;
3941
				$oldParentFolder = implode($del,$pA);
3942
				$parentFolder = $_newLocation;
3943
3944
				if (strtoupper($folderName)!= 'INBOX' &&
3945
					(($oldParentFolder === $parentFolder) || //$oldParentFolder == $parentFolder means move on same level
3946
					(($oldParentFolder != $parentFolder &&
3947
					strlen($parentFolder)>0 && strlen($folderName)>0 &&
3948
					strpos($parentFolder,$folderName)===false)))) // indicates that we move the older up the tree within its own branch
3949
				{
3950
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3951
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3952
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3953
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3954
					{
3955
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3956
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3957
						$nameSpace = $this->mail_bo->_getNameSpaces();
3958
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3959
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3960
3961
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3962
						foreach ($subFolders as $k => $folder)
3963
						{
3964
							// we do not monitor failure or success on subfolders
3965
							if ($folder == $folderName)
3966
							{
3967
								unset($subFolders[$k]);
3968
							}
3969
							else
3970
							{
3971
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3972
							}
3973
						}
3974
					}
3975
3976
					$this->mail_bo->reopen('INBOX');
3977
					$success = false;
3978
					try
3979
					{
3980 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3981
						{
3982
							$this->mail_bo->resetFolderObjectCache($profileID);
3983
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3984
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3985
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3986
							$this->mail_bo->resetFolderObjectCache($profileID);
3987
							$success = true;
3988
						}
3989
					}
3990
					catch (Exception $e)
3991
					{
3992
						$newFolderName=$folderName;
3993
						$msg = $e->getMessage();
3994
					}
3995
					$this->mail_bo->reopen($parentFolder);
3996
					$this->mail_bo->getFolderStatus($parentFolder,false,false,false);
3997
					//error_log(__METHOD__.__LINE__.array2string($fS));
3998 View Code Duplication
					if ($hasChildren)
3999
					{
4000
						$subFolders = $this->mail_bo->getMailBoxesRecursive($parentFolder, $delimiter, $prefix);
4001
						foreach ($subFolders as $k => $folder)
4002
						{
4003
							// we do not monitor failure or success on subfolders
4004
							if ($folder == $folderName)
4005
							{
4006
								unset($subFolders[$k]);
4007
							}
4008
							else
4009
							{
4010
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
4011
							}
4012
						}
4013
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4014
					}
4015
				}
4016
			}
4017 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4018
			{
4019
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
4020
				$this->mail_bo->saveSessionData();
4021
			}
4022
			//error_log(__METHOD__.__LINE__.array2string($oA));
4023
			$response = Api\Json\Response::get();
4024
			if ($success)
4025
			{
4026
				Api\Translation::add_app('mail');
4027
4028
				$oldFolderInfo = $this->mail_bo->getFolderStatus($oldParentFolder,false,false,false);
4029
				$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4030
				$refreshData = array(
4031
					$profileID.self::$delimiter.$oldParentFolder=>$oldFolderInfo['shortDisplayName'],
4032
					$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4033
				// if we move the folder within the same parent-branch of the tree, there is no need no refresh the upper part
4034 View Code Duplication
				if (strlen($parentFolder)>strlen($oldParentFolder) && strpos($parentFolder,$oldParentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$parentFolder]);
4035 View Code Duplication
				if (count($refreshData)>1 && strlen($oldParentFolder)>strlen($parentFolder) && strpos($oldParentFolder,$parentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$oldParentFolder]);
4036
4037
				// Send full info back in the response
4038
				foreach($refreshData as $folder => &$name)
4039
				{
4040
					$name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true);
4041
				}
4042
				$response->call('app.mail.mail_reloadNode',$refreshData);
4043
4044
			}
4045
			else
4046
			{
4047
				$response->call('egw.refresh',lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');
4048
			}
4049
		}
4050
	}
4051
4052
	/**
4053
	 * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4054
	 * @param string $_folderName folder to delete
4055
	 * @param boolean $_return = false wheter return the success value (true) or send response to client (false)
4056
	 * @return nothing
4057
	 */
4058
	function ajax_deleteFolder($_folderName, $_return = false)
4059
	{
4060
		//error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName));
4061
		$success = false;
4062
		if ($_folderName)
4063
		{
4064
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4065
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4066
			$oA = array();
4067
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4068
			$hasChildren = false;
4069
			if (is_numeric($profileID))
4070
			{
4071
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
4072
				$pA = explode($del,$folderName);
4073
				array_pop($pA);
4074
				if (strtoupper($folderName)!= 'INBOX')
4075
				{
4076
					//error_log(__METHOD__.__LINE__."$folderName,  implode($del,$pA), $_newName");
4077
					$oA = array();
4078
					$subFolders = array();
4079
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4080
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4081
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4082
					{
4083
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4084
						$ftD = array();
4085
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4086
						$nameSpace = $this->mail_bo->_getNameSpaces();
4087
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4088
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4089
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4090
						//error_log(__METHOD__.__LINE__.'->'."$folderName, $delimiter, $prefix");
4091
						foreach ($subFolders as $k => $f)
4092
						{
4093
							$ftD[substr_count($f,$delimiter)][]=$f;
4094
						}
4095
						krsort($ftD,SORT_NUMERIC);//sort per level
4096
						//we iterate per level of depth of the subtree, deepest nesting is to be deleted first, and then up the tree
4097
						foreach($ftD as $k => $lc)//collection per level
4098
						{
4099
							foreach($lc as $f)//folders contained in that level
4100
							{
4101
								try
4102
								{
4103
									//error_log(__METHOD__.__LINE__.array2string($f).'<->'.$folderName);
4104
									$this->mail_bo->deleteFolder($f);
4105
									$success = true;
4106
									if ($f==$folderName) $oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4107
								}
4108
								catch (Exception $e)
4109
								{
4110
									$msg .= ($msg?' ':'').lang("Failed to delete %1. Server responded:",$f).$e->getMessage();
4111
									$success = false;
4112
								}
4113
							}
4114
						}
4115
					}
4116
					else
4117
					{
4118
						try
4119
						{
4120
							$this->mail_bo->deleteFolder($folderName);
4121
							$success = true;
4122
							$oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4123
						}
4124
						catch (Exception $e)
4125
						{
4126
							$msg = $e->getMessage();
4127
							$success = false;
4128
						}
4129
					}
4130
				}
4131
				else
4132
				{
4133
					$msg = lang("refused to delete folder INBOX");
4134
				}
4135
			}
4136
			if ($_return) return $success;
4137
			$response = Api\Json\Response::get();
4138
			if ($success)
4139
			{
4140
				//error_log(__METHOD__.__LINE__.array2string($oA));
4141
				$response->call('app.mail.mail_removeLeaf',$oA);
4142
			}
4143
			else
4144
			{
4145
				$response->call('egw.refresh',lang('failed to delete %1 ! Reason: %2',$oldFolderInfo['shortDisplayName'],$msg),'mail');
4146
			}
4147
		}
4148
	}
4149
4150
	/**
4151
	 * empty changeProfile - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4152
	 *
4153
	 * Made static to NOT call __construct, as it would connect to old server, before going to new one
4154
	 *
4155
	 * @param int $icServerID New profile / server ID
4156
	 * @param bool $getFolders The client needs the folders for the profile
4157
	 * @return nothing
4158
	 */
4159
	public static function ajax_changeProfile($icServerID, $getFolders = true, $exec_id=null)
4160
	{
4161
		$response = Api\Json\Response::get();
4162
4163
		$previous_id = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
4164
4165
		if ($icServerID && $icServerID != $previous_id)
4166
		{
4167
			$mail_ui = new mail_ui(false);	// do NOT run constructor, as we call changeProfile anyway
4168
			try
4169
			{
4170
				$mail_ui->changeProfile($icServerID);
4171
				// if we have an eTemplate exec_id, also send changed actions
4172
				if ($exec_id && ($actions = $mail_ui->get_actions()))
4173
				{
4174
					$response->generic('assign', array(
4175
						'etemplate_exec_id' => $exec_id,
4176
						'id' => 'nm',
4177
						'key' => 'actions',
4178
						'value' => $actions,
4179
					));
4180
				}
4181
			}
4182
			catch (Exception $e) {
4183
				self::callWizard($e->getMessage(),true, 'error');
4184
			}
4185
		}
4186
		else
4187
		{
4188
			$mail_ui = new mail_ui(true);	// run constructor
4189
		}
4190
4191
		// Send full info back in the response
4192
		if($getFolders)
4193
		{
4194
			Api\Translation::add_app('mail');
4195
4196
			$refreshData = array(
4197
				$icServerID => $mail_ui->mail_tree->getTree(null,$icServerID,1,false,!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
4198
			);
4199
			$response->call('app.mail.mail_reloadNode',$refreshData);
4200
		}
4201
	}
4202
4203
	/**
4204
	 * ajax_refreshVacationNotice - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4205
	 *	Note: only the activeProfile VacationNotice is refreshed
4206
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4207
	 *						if other than active profile; nothing is done!
4208
	 * @return nothing
4209
	 */
4210
	public static function ajax_refreshVacationNotice($icServerID=null)
4211
	{
4212
		//Get vacation from cache if it's available
4213
		$cachedVacations = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid']);
4214
		$vacation = $cachedVacations[$icServerID];
4215
4216
		if (!$vacation)
4217
		{
4218
			try
4219
			{
4220
				// Create mail app object
4221
				$mail = new mail_ui();
4222
4223
				if (empty($icServerID)) $icServerID = $mail->Mail->profileID;
4224
				if ($icServerID != $mail->Mail->profileID) return;
4225
4226
				$vacation = $mail->gatherVacation($cachedVacations);
4227
			} catch (Exception $e) {
4228
				$vacation=false;
4229
				error_log(__METHOD__.__LINE__." ".$e->getMessage());
4230
				unset($e);
4231
			}
4232
		}
4233
4234
		if($vacation) {
4235
			if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date'))
4236
			{
4237
				$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
4238
				$refreshData['vacationnotice'] = lang('Vacation notice is active');
4239
				$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...
4240
				if ($vacation['status'] == 'by_date' && $vacation['end_date']+ 24*3600 < time())$refreshData = '';
4241
			}
4242
		}
4243
		if ($vacation==false)
4244
		{
4245
			$refreshData['vacationnotice'] =  '';
4246
			$refreshData['vacationrange'] =  '';
4247
		}
4248
		$response = Api\Json\Response::get();
4249
		$response->call('app.mail.mail_refreshVacationNotice',$refreshData);
4250
	}
4251
4252
	/**
4253
	 * ajax_refreshFilters - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4254
	 *	Note: only the activeProfile Filters are refreshed
4255
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4256
	 *						if other than active profile; nothing is done!
4257
	 * @return nothing
4258
	 */
4259
	function ajax_refreshFilters($icServerID=null)
4260
	{
4261
		//error_log(__METHOD__.__LINE__.array2string($icServerId));
4262
		if (empty($icServerID)) $icServerID = $this->mail_bo->profileID;
4263
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4264
		{
4265
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4266
			if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4267
		}
4268 View Code Duplication
		if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
4269
		{
4270
			unset($this->searchTypes['quick']);
4271
			unset($this->searchTypes['quickwithcc']);
4272
		}
4273 View Code Duplication
		if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
4274
		{
4275
			$this->statusTypes = array_merge($this->statusTypes,array(
4276
				'keyword1'	=> 'important',//lang('important'),
4277
				'keyword2'	=> 'job',	//lang('job'),
4278
				'keyword3'	=> 'personal',//lang('personal'),
4279
				'keyword4'	=> 'to do',	//lang('to do'),
4280
				'keyword5'	=> 'later',	//lang('later'),
4281
			));
4282
		}
4283
		else
4284
		{
4285
			$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
4286
			foreach($keywords as &$k)
4287
			{
4288
				if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
4289
			}
4290
		}
4291
4292
		$response = Api\Json\Response::get();
4293
		$response->call('app.mail.mail_refreshCatIdOptions',$this->searchTypes);
4294
		$response->call('app.mail.mail_refreshFilterOptions',$this->statusTypes);
4295
		$response->call('app.mail.mail_refreshFilter2Options',array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list')));
4296
4297
	}
4298
4299
	/**
4300
	 * ajax_refreshQuotaDisplay - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4301
	 *
4302
	 * @return nothing
4303
	 */
4304
	function ajax_refreshQuotaDisplay($icServerID=null)
4305
	{
4306
		//error_log(__METHOD__.__LINE__.array2string($icServerID));
4307
		Api\Translation::add_app('mail');
4308
		if (is_null($icServerID)) $icServerID = $this->mail_bo->profileID;
4309
		$rememberServerID = $this->mail_bo->profileID;
4310
		try
4311
		{
4312
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4313
			{
4314
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4315
				$this->changeProfile($icServerID);
4316
			}
4317
			$quota = $this->mail_bo->getQuotaRoot();
4318
		} catch (Exception $e) {
4319
			$quota['limit'] = 'NOT SET';
4320
			error_log(__METHOD__.__LINE__." ".$e->getMessage());
4321
			unset($e);
4322
		}
4323
4324
		if($quota !== false && $quota['limit'] != 'NOT SET') {
4325
			$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
4326
			$content['quota'] = $sel_options[self::$nm_index]['quota'] = $quotainfo['text'];
4327
			$content['quotainpercent'] = $sel_options[self::$nm_index]['quotainpercent'] =  (string)$quotainfo['percent'];
4328
			$content['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = $quotainfo['class'];
4329
			$content['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "";
4330
		} else {
4331
			$content['quota'] = $sel_options[self::$nm_index]['quota'] = lang("Quota not provided by server");
4332
			$content['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = "mail_DisplayNone";
4333
			$content['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "mail_DisplayNone";
4334
		}
4335
		if ($rememberServerID != $this->mail_bo->profileID)
4336
		{
4337
			try
4338
			{
4339
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4340
				$this->changeProfile($rememberServerID);
4341
			} catch (Exception $e) {
4342
				//error_log(__METHOD__.__LINE__." ".$e->getMessage());
4343
				unset($e);
4344
			}
4345
		}
4346
		$response = Api\Json\Response::get();
4347
		$response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content));
4348
	}
4349
4350
	/**
4351
	 * Empty spam/junk folder
4352
	 *
4353
	 * @param string $icServerID id of the server to empty its junkFolder
4354
	 * @param string $selectedFolder seleted(active) folder by nm filter
4355
	 * @return nothing
4356
	 */
4357 View Code Duplication
	function ajax_emptySpam($icServerID, $selectedFolder)
4358
	{
4359
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4360
		Api\Translation::add_app('mail');
4361
		$response = Api\Json\Response::get();
4362
		$rememberServerID = $this->mail_bo->profileID;
4363
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4364
		{
4365
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4366
			$this->changeProfile($icServerID);
4367
		}
4368
		$junkFolder = $this->mail_bo->getJunkFolder();
4369
		if(!empty($junkFolder)) {
4370
			if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4371
			{
4372
				// Lock the tree if the active folder is junk folder
4373
				$response->call('app.mail.lock_tree');
4374
			}
4375
			$this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately');
4376
4377
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4378
			$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...
4379
			$fStatus = array(
4380
				$icServerID.self::$delimiter.$junkFolder => lang($fShortName)
4381
			);
4382
			//Call to reset folder status counter, after junkFolder triggered not from Junk folder
4383
			//-as we don't have junk folder specific information available on client-side we need to deal with it on server
4384
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4385
		}
4386
		if ($rememberServerID != $this->mail_bo->profileID)
4387
		{
4388
			$oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false);
4389
			$response->call('egw.message',lang('empty junk'));
4390
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName']));
4391
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4392
			$this->changeProfile($rememberServerID);
4393
		}
4394
		else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4395
		{
4396
			$response->call('egw.refresh',lang('empty junk'),'mail');
4397
		}
4398
	}
4399
4400
	/**
4401
	 * Empty trash folder
4402
	 *
4403
	 * @param string $icServerID id of the server to empty its trashFolder
4404
	 * @param string $selectedFolder seleted(active) folder by nm filter
4405
	 * @return nothing
4406
	 */
4407 View Code Duplication
	function ajax_emptyTrash($icServerID, $selectedFolder)
4408
	{
4409
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4410
		Api\Translation::add_app('mail');
4411
		$response = Api\Json\Response::get();
4412
		$rememberServerID = $this->mail_bo->profileID;
4413
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4414
		{
4415
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4416
			$this->changeProfile($icServerID);
4417
		}
4418
		$trashFolder = $this->mail_bo->getTrashFolder();
4419
		if(!empty($trashFolder)) {
4420
			if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4421
			{
4422
				// Lock the tree if the active folder is Trash folder
4423
				$response->call('app.mail.lock_tree');
4424
			}
4425
			$this->mail_bo->compressFolder($trashFolder);
4426
4427
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4428
			$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...
4429
			$fStatus = array(
4430
				$icServerID.self::$delimiter.$trashFolder => lang($fShortName)
4431
			);
4432
			//Call to reset folder status counter, after emptyTrash triggered not from Trash folder
4433
			//-as we don't have trash folder specific information available on client-side we need to deal with it on server
4434
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4435
		}
4436
		if ($rememberServerID != $this->mail_bo->profileID)
4437
		{
4438
			$oldFolderInfo = $this->mail_bo->getFolderStatus($trashFolder,false,false,false);
4439
			$response->call('egw.message',lang('empty trash'));
4440
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$trashFolder=>$oldFolderInfo['shortDisplayName']));
4441
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4442
			$this->changeProfile($rememberServerID);
4443
		}
4444
		else if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4445
		{
4446
			$response->call('egw.refresh',lang('empty trash'),'mail');
4447
		}
4448
	}
4449
4450
	/**
4451
	 * compress folder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4452
	 * fetches the current folder from session and compresses it
4453
	 * @param string $_folderName id of the folder to compress
4454
	 * @return nothing
4455
	 */
4456
	function ajax_compressFolder($_folderName)
4457
	{
4458
		//error_log(__METHOD__.__LINE__.' '.$_folderName);
4459
		Api\Translation::add_app('mail');
4460
4461
		$this->mail_bo->restoreSessionData();
4462
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4463
		list($icServerID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4464
4465
		if (empty($folderName)) $folderName = $this->mail_bo->sessionData['mailbox'];
4466
		if ($this->mail_bo->folderExists($folderName))
4467
		{
4468
			$rememberServerID = $this->mail_bo->profileID;
4469
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4470
			{
4471
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4472
				$this->changeProfile($icServerID);
4473
			}
4474
			if(!empty($_folderName)) {
4475
				$this->mail_bo->compressFolder($folderName);
4476
			}
4477
			if ($rememberServerID != $this->mail_bo->profileID)
4478
			{
4479
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4480
				$this->changeProfile($rememberServerID);
4481
			}
4482
			$response = Api\Json\Response::get();
4483
			$response->call('egw.refresh',lang('compress folder').': '.$folderName,'mail');
4484
		}
4485
	}
4486
4487
	/**
4488
	 * sendMDN, ...
4489
	 *
4490
	 * @param array _messageList list of UID's
4491
	 *
4492
	 * @return nothing
4493
	 */
4494
	function ajax_sendMDN($_messageList)
4495
	{
4496
		if(Mail::$debug) error_log(__METHOD__."->".array2string($_messageList));
4497
		$uidA = self::splitRowID($_messageList['msg'][0]);
4498
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4499
		$this->mail_bo->sendMDN($uidA['msgUID'],$folder);
4500
	}
4501
4502
	/**
4503
	 * flag messages as read, unread, flagged, ...
4504
	 *
4505
	 * @param string _flag name of the flag
4506
	 * @param array _messageList list of UID's
4507
	 * @param bool _sendJsonResponse tell fuction to send the JsonResponse
4508
	 *
4509
	 * @return xajax response
4510
	 */
4511
	function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
4512
	{
4513
		if(Mail::$debug) error_log(__METHOD__."->".$_flag.':'.array2string($_messageList));
4514
		Api\Translation::add_app('mail');
4515
		$alreadyFlagged=false;
4516
		$flag2check='';
4517
		$filter2toggle = $query = array();
4518
		if ($_messageList=='all' || !empty($_messageList['msg']))
4519
		{
4520
			if (isset($_messageList['all']) && $_messageList['all'])
4521
			{
4522
				// we have both messageIds AND allFlag folder information
4523
				$uidA = self::splitRowID($_messageList['msg'][0]);
4524
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4525
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4526
				{
4527
					$query = $_messageList['activeFilters'];
4528
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4529
					{
4530
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4531
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4532
						{
4533
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4534
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4535
						}
4536
						//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
4537
						$cutoffdate = $cutoffdate2 = null;
4538
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4539
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4540
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4541
						$filter = array(
4542
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4543
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4544
							'string' => $query['search'],
4545
							'status' => 'any',//this is a status change. status will be manipulated later on
4546
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4547
						);
4548
						if ($query['enddate']||$query['startdate']) {
4549
							$filter['range'] = "BETWEEN";
4550
							if ($cutoffdate) {
4551
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4552
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4553
							}
4554
							if ($cutoffdate2) {
4555
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4556
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4557
							}
4558
						}
4559
						$filter2toggle = $filter;
4560
					}
4561
					else
4562
					{
4563
						$filter = $filter2toggle = array();
4564
					}
4565
					// flags read,flagged,label1,label2,label3,label4,label5 can be toggled: handle this when all mails in a folder
4566
					// should be affected serverside. here.
4567
					$messageList = $messageListForToggle = array();
4568
					$flag2check = ($_flag=='read'?'seen':$_flag);
4569
					if (in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4570
						!($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))
4571
					{
4572
						$filter2toggle['status'] = array('un'.$_flag);
4573 View Code Duplication
						if ($query['filter'] && $query['filter']!='any')
4574
						{
4575
							$filter2toggle['status'][] = $query['filter'];
4576
						}
4577
						$rByUid = true;
4578
						$reverse = 1;
4579
						$_sRt = $this->mail_bo->getSortedList(
4580
							$folder,
4581
							$sort=0,
4582
							$reverse,
4583
							$filter2toggle,
4584
							$rByUid,
4585
							false
4586
						);
4587
						$messageListForToggle = $_sRt['match']->ids;
4588
						$filter['status'] = array($_flag);
4589 View Code Duplication
						if ($query['filter'] && $query['filter'] !='any')
4590
						{
4591
							$filter['status'][] = $query['filter'];
4592
						}
4593
						$rByUid=true;
4594
						$reverse = 1;
4595
						$_sR = $this->mail_bo->getSortedList(
4596
							$folder,
4597
							$sort=0,
4598
							$reverse,
4599
							$filter,
4600
							$rByUid,
4601
							false
4602
						);
4603
						$messageList = $_sR['match']->ids;
4604 View Code Duplication
						if (count($messageListForToggle)>0)
4605
						{
4606
							$flag2set = (strtolower($_flag));
4607
							if(Mail::$debug) error_log(__METHOD__.__LINE__." toggle un$_flag -> $flag2set ".array2string($filter2toggle).array2string($messageListForToggle));
4608
							$this->mail_bo->flagMessages($flag2set, $messageListForToggle,$folder);
4609
						}
4610 View Code Duplication
						if (count($messageList)>0)
4611
						{
4612
							$flag2set = 'un'.$_flag;
4613
							if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag -> $flag2set ".array2string($filter).array2string($messageList));
4614
							$this->mail_bo->flagMessages($flag2set, $messageList,$folder);
4615
						}
4616
						$alreadyFlagged=true;
4617
					}
4618
					elseif (!empty($filter) &&
4619
						(!in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) ||
4620
						(in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4621
						($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))))
4622
					{
4623
						if ($query['filter'] && $query['filter'] !='any')
4624
						{
4625
							$filter['status'] = $query['filter'];
4626
							// since we toggle and we toggle by the filtered flag we must must change _flag
4627
							$_flag = ($query['filter']=='unseen' && $_flag=='read' ? 'read' : ($query['filter']=='seen'&& $_flag=='read'?'unread':($_flag==$query['filter']?'un'.$_flag:$_flag)));
4628
						}
4629
						if(Mail::$debug) error_log(__METHOD__.__LINE__." flag all with $_flag on filter used:".array2string($filter));
4630
						$rByUid = true;
4631
						$reverse = 1;
4632
						$_sR = $this->mail_bo->getSortedList(
4633
							$folder,
4634
							$sort=0,
4635
							$reverse,
4636
							$filter,
4637
							$rByUid,
4638
							false
4639
						);
4640
						$messageList = $_sR['match']->ids;
4641
						unset($_messageList['all']);
4642
						$_messageList['msg'] = array();
4643
					}
4644
					else
4645
					{
4646
						if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag all ".array2string($filter));
4647
						$alreadyFlagged=true;
4648
						$uidA = self::splitRowID($_messageList['msg'][0]);
4649
						$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4650
						$this->mail_bo->flagMessages($_flag, 'all', $folder);
4651
					}
4652
				}
4653
			}
4654
			else
4655
			{
4656
				$uidA = self::splitRowID($_messageList['msg'][0]);
4657
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4658
			}
4659
			if (!$alreadyFlagged)
4660
			{
4661 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4662
				{
4663
					$hA = self::splitRowID($rowID);
4664
					$messageList[] = $hA['msgUID'];
4665
				}
4666
				if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag in $folder:".array2string(((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList)));
4667
				$this->mail_bo->flagMessages($_flag, ((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList),$folder);
4668
			}
4669
		}
4670
		else
4671
		{
4672
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4673
		}
4674
4675
		if ($_sendJsonResponse)
4676
		{
4677
			$flag=array(
4678
				'label1'	=> 'important',//lang('important'),
4679
				'label2'	=> 'job',	//lang('job'),
4680
				'label3'	=> 'personal',//lang('personal'),
4681
				'label4'	=> 'to do',	//lang('to do'),
4682
				'label5'	=> 'later',	//lang('later'),
4683
			);
4684
			$response = Api\Json\Response::get();
4685
			if (isset($_messageList['msg']) && $_messageList['popup'])
4686
			{
4687
				$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');
4688
			}
4689
			else if ((isset($_messageList['all']) && $_messageList['all']) || ($query['filter'] && ($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false)))
4690
			{
4691
				$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');
4692
			}
4693
			else
4694
			{
4695
				$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));
4696
			}
4697
		}
4698
	}
4699
4700
	/**
4701
	 * delete messages
4702
	 *
4703
	 * @param array _messageList list of UID's
4704
	 * @param string _forceDeleteMethod - method of deletion to be enforced
4705
	 * @return xajax response
4706
	 */
4707
	function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
4708
	{
4709
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageList,true).' Method:'.$_forceDeleteMethod);
4710
		$error = null;
4711
		$filtered =  false;
4712
		if ($_messageList=='all' || !empty($_messageList['msg']))
4713
		{
4714
			if (isset($_messageList['all']) && $_messageList['all'])
4715
			{
4716
				// we have both messageIds AND allFlag folder information
4717
				$uidA = self::splitRowID($_messageList['msg'][0]);
4718
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4719
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4720
				{
4721
					$query = $_messageList['activeFilters'];
4722 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4723
					{
4724
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4725
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4726
						{
4727
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4728
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4729
						}
4730
						$filtered =  true;
4731
						$cutoffdate = $cutoffdate2 = null;
4732
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4733
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4734
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4735
						$filter = array(
4736
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4737
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4738
							'string' => $query['search'],
4739
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4740
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4741
						);
4742
						if ($query['enddate']||$query['startdate']) {
4743
							$filter['range'] = "BETWEEN";
4744
							if ($cutoffdate) {
4745
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4746
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4747
							}
4748
							if ($cutoffdate2) {
4749
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4750
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4751
							}
4752
						}
4753
					}
4754
					else
4755
					{
4756
						$filter = array();
4757
					}
4758
					//error_log(__METHOD__.__LINE__."->".print_r($filter,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4759
					$reverse = 1;
4760
					$rByUid = true;
4761
					$_sR = $this->mail_bo->getSortedList(
4762
						$folder,
4763
						$sort=0,
4764
						$reverse,
4765
						$filter,
4766
						$rByUid,
4767
						false
4768
					);
4769
					$messageList = $_sR['match']->ids;
4770
				}
4771
				else
4772
				{
4773
					$messageList='all';
4774
				}
4775
				try
4776
				{
4777
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4778
					$this->mail_bo->deleteMessages(($messageList=='all' ? 'all':$messageList),$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4779
				}
4780
				catch (Api\Exception $e)
4781
				{
4782
					$error = str_replace('"',"'",$e->getMessage());
4783
				}
4784
			}
4785
			else
4786
			{
4787
				$uidA = self::splitRowID($_messageList['msg'][0]);
4788
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4789 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4790
				{
4791
					$hA = self::splitRowID($rowID);
4792
					$messageList[] = $hA['msgUID'];
4793
				}
4794
				try
4795
				{
4796
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4797
					$this->mail_bo->deleteMessages($messageList,$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4798
				}
4799
				catch (Api\Exception $e)
4800
				{
4801
					$error = str_replace('"',"'",$e->getMessage());
4802
				}
4803
			}
4804
			$response = Api\Json\Response::get();
4805
			if (empty($error))
4806
			{
4807
				$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']));
4808
			}
4809
			else
4810
			{
4811
				$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));
4812
				$response->call('app.mail.mail_retryForcedDelete',array('response'=>$error,'messageList'=>$_messageList));
4813
			}
4814
		}
4815
		else
4816
		{
4817
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4818
		}
4819
	}
4820
4821
	/**
4822
	 * copy messages
4823
	 *
4824
	 * @param array _folderName target folder
4825
	 * @param array _messageList list of UID's
4826
	 * @param string _copyOrMove method to use copy or move allowed
4827
	 * @param string _move2ArchiveMarker marker to indicate if a move 2 archive was triggered
4828
	 *
4829
	 * @return xajax response
4830
	 */
4831
	function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_move2ArchiveMarker='_')
4832
	{
4833
		if(Mail::$debug) error_log(__METHOD__."->".$_folderName.':'.print_r($_messageList,true).' Method:'.$_copyOrMove.' ArchiveMarker:'.$_move2ArchiveMarker);
4834
		Api\Translation::add_app('mail');
4835
		$folderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4836
		// only copy or move are supported as method
4837
		if (!($_copyOrMove=='copy' || $_copyOrMove=='move')) $_copyOrMove='copy';
4838
		list($targetProfileID,$targetFolder) = explode(self::$delimiter,$folderName,2);
4839
		// check if move2archive was called with the correct archiveFolder
4840
		$archiveFolder = $this->mail_bo->getArchiveFolder();
4841
		if ($_move2ArchiveMarker=='2' && $targetFolder != $archiveFolder)
4842
		{
4843
			error_log(__METHOD__.__LINE__."#Move to Archive called with:"."$targetProfileID,$targetFolder");
4844
			$targetProfileID = $this->mail_bo->profileID;
4845
			$targetFolder = $archiveFolder;
4846
			error_log(__METHOD__.__LINE__."#Fixed ArchiveFolder:"."$targetProfileID,$targetFolder");
4847
		}
4848
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
4849
		$changeFolderActions = false;
4850
		//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder");
4851
		//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
4852
		if (!isset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]))
4853
		{
4854
			//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]));
4855
			if ($lastFoldersUsedForMoveCont[$targetProfileID] && count($lastFoldersUsedForMoveCont[$targetProfileID])>3)
4856
			{
4857
				$keys = array_keys($lastFoldersUsedForMoveCont[$targetProfileID]);
4858
				foreach( $keys as &$f)
4859
				{
4860
					if (count($lastFoldersUsedForMoveCont[$targetProfileID])>9) unset($lastFoldersUsedForMoveCont[$targetProfileID][$f]);
4861
					else break;
4862
				}
4863
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID]));
4864
			}
4865
			//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder = $_folderName");
4866
			$lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]=$folderName;
4867
			$changeFolderActions = true;
4868
		}
4869
		$filtered = false;
4870
		if ($_messageList=='all' || !empty($_messageList['msg']))
4871
		{
4872
			$error=false;
4873
			if (isset($_messageList['all']) && $_messageList['all'])
4874
			{
4875
				// we have both messageIds AND allFlag folder information
4876
				$uidA = self::splitRowID($_messageList['msg'][0]);
4877
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4878
				$sourceProfileID = $uidA['profileID'];
4879
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4880
				{
4881
					$query = $_messageList['activeFilters'];
4882 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4883
					{
4884
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4885
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4886
						{
4887
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4888
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4889
						}
4890
						$filtered = true;
4891
						$cutoffdate = $cutoffdate2 = null;
4892
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4893
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4894
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4895
						$filter = array(
4896
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4897
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4898
							'string' => $query['search'],
4899
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4900
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4901
						);
4902
						if ($query['enddate']||$query['startdate']) {
4903
							$filter['range'] = "BETWEEN";
4904
							if ($cutoffdate) {
4905
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4906
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4907
							}
4908
							if ($cutoffdate2) {
4909
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4910
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4911
							}
4912
						}
4913
					}
4914
					else
4915
					{
4916
						$filter = array();
4917
					}
4918
					$reverse = 1;
4919
					$rByUid = true;
4920
					$_sR = $this->mail_bo->getSortedList(
4921
						$folder,
4922
						$sort=0,
4923
						$reverse,
4924
						$filter,
4925
						$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...
4926
						false
4927
					);
4928
					$messageList = $_sR['match']->ids;
4929
					foreach($messageList as $uID)
4930
					{
4931
						//error_log(__METHOD__.__LINE__.$uID);
4932
						if ($_copyOrMove=='move')
4933
						{
4934
							$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false);
4935
						}
4936
					}
4937
				}
4938
				else
4939
				{
4940
					$messageList='all';
4941
				}
4942
				try
4943
				{
4944
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4945
					$this->mail_bo->moveMessages($targetFolder,$messageList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4946
				}
4947
				catch (Api\Exception $e)
4948
				{
4949
					$error = str_replace('"',"'",$e->getMessage());
4950
				}
4951
			}
4952
			else
4953
			{
4954
				$messageList = array();
4955
				while(count($_messageList['msg']) > 0)
4956
				{
4957
					$uidA = self::splitRowID($_messageList['msg'][0]);
4958
					$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4959
					$sourceProfileID = $uidA['profileID'];
4960
					$moveList = array();
4961
					foreach($_messageList['msg'] as $rowID)
4962
					{
4963
						$hA = self::splitRowID($rowID);
4964
4965
						// If folder changes, stop and move what we've got
4966
						if($hA['folder'] != $folder) break;
4967
4968
						array_shift($_messageList['msg']);
4969
						$messageList[] = $hA['msgUID'];
4970
						$moveList[] = $hA['msgUID'];
4971
						if ($_copyOrMove=='move')
4972
						{
4973
							$helpvar = explode(self::$delimiter,$rowID);
4974
							array_shift($helpvar);
4975
							$messageListForRefresh[]= implode(self::$delimiter,$helpvar);
4976
						}
4977
					}
4978
					try
4979
					{
4980
						//error_log(__METHOD__.__LINE__."->".print_r($moveList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4981
						$this->mail_bo->moveMessages($targetFolder,$moveList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4982
					}
4983
					catch (Api\Exception $e)
4984
					{
4985
						$error = str_replace('"',"'",$e->getMessage());
4986
					}
4987
				}
4988
			}
4989
4990
			$response = Api\Json\Response::get();
4991
			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...
4992
			{
4993
				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...
4994
				{
4995
					unset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]);
4996
					$changeFolderActions = true;
4997
				}
4998
				$response->call('egw.message',$error,"error");
4999
			}
5000
			else
5001
			{
5002
				if ($_copyOrMove=='copy')
5003
				{
5004
					$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));
5005
				}
5006
				else
5007
				{
5008
					$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');
5009
				}
5010
			}
5011
			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...
5012
			{
5013
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5014
				Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
5015
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::get_actions());
5016
				$response->call('app.mail.mail_rebuildActionsOnList',$actionsnew);
5017
			}
5018
		}
5019
		else
5020
		{
5021
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5022
		}
5023
	}
5024
5025
	/**
5026
	 * Autoloading function to load branches of tree node
5027
	 * of management folder tree
5028
	 *
5029
	 * @param type $_id
5030
	 */
5031
	function ajax_folderMgmtTree_autoloading ($_id = null)
5032
	{
5033
		$mail_ui = new mail_ui();
5034
		$id = $_id? $_id : $_GET['id'];
5035
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,true,false,false,false));
5036
	}
5037
5038
	/**
5039
	 * Main function to handle folder management dialog
5040
	 *
5041
	 * @param array $content content of dialog
5042
	 */
5043
	function folderManagement (array $content = null)
5044
	{
5045
		$dtmpl = new Etemplate('mail.folder_management');
5046
		$profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
5047
		$sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false);
5048
5049
		if (!is_array($content))
5050
		{
5051
			$content = array ('acc_id' => $profileID);
5052
		}
5053
5054
		$readonlys = array();
5055
		// Preserv
5056
		$preserv = array(
5057
			'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side
5058
		);
5059
		$dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2);
5060
	}
5061
5062
	/**
5063
	 * Function to delete folder for management longTask dialog
5064
	 * it sends successfully deleted folder as response to be
5065
	 * used in long task response handler.
5066
	 *
5067
	 * @param type $_folderName
5068
	 */
5069
	function ajax_folderMgmt_delete ($_folderName)
5070
	{
5071
		if ($_folderName)
5072
		{
5073
			$success = $this->ajax_deleteFolder($_folderName,true);
5074
			$response = Api\Json\Response::get();
5075
			list(,$folderName) = explode(self::$delimiter, $_folderName);
5076
			if ($success)
5077
			{
5078
				$res = $folderName;
5079
			}
5080
			else
5081
			{
5082
				$res = lang("Failed to delete %1",$folderName);
5083
			}
5084
			$response->data($res);
5085
		}
5086
	}
5087
}
5088