Completed
Push — 16.1 ( a3973f...a3dd6a )
by Hadi
49:10 queued 35:11
created

mail_ui::findNode()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 6
nc 5
nop 3
dl 0
loc 16
rs 8.2222
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware - Mail - interface class
4
 *
5
 * @link http://www.egroupware.org
6
 * @package mail
7
 * @author EGroupware GmbH [[email protected]]
8
 * @copyright (c) 2013-2016 by EGroupware GmbH <info-AT-egroupware.org>
9
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
10
 * @version $Id$
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Link;
15
use EGroupware\Api\Framework;
16
use EGroupware\Api\Egw;
17
use EGroupware\Api\Vfs;
18
use EGroupware\Api\Etemplate;
19
use EGroupware\Api\Etemplate\KeyManager;
20
use EGroupware\Api\Mail;
21
22
/**
23
 * Mail User Interface
24
 *
25
 * As we do NOT want to connect to previous imap server, when a profile change is triggered
26
 * by user get_rows and ajax_changeProfile are not static methods and instanciates there own
27
 * mail_ui object.
28
 *
29
 * If they detect a profile change is to be triggered they call:
30
 *		$mail_ui = new mail_ui(false);	// not call constructor / connect to imap server
31
 *		$mail_ui->changeProfile($_profileID);
32
 * If no profile change is needed they just call:
33
 *		$mail_ui = new mail_ui();
34
 * Afterwards they use $mail_ui instead of $this.
35
 */
36
class mail_ui
37
{
38
	/**
39
	 * Methods callable via menuaction
40
	 *
41
	 * @var array
42
	 */
43
	var $public_functions = array
44
	(
45
		'index' => True,
46
		'displayHeader'	=> True,
47
		'displayMessage'	=> True,
48
		'displayImage'		=> True,
49
		'getAttachment'		=> True,
50
		'download_zip'		=> True,
51
		'saveMessage'	=> True,
52
		'vfsSaveAttachment' => True,
53
		'vfsSaveMessage' => True,
54
		'loadEmailBody'	=> True,
55
		'importMessage'	=> True,
56
		'importMessageFromVFS2DraftAndDisplay'=>True,
57
		'subscription'	=> True,
58
		'folderManagement' => true,
59
	);
60
61
	/**
62
	 * current icServerID
63
	 *
64
	 * @var int
65
	 */
66
	static $icServerID;
67
68
	/**
69
	 * delimiter - used to separate profileID from foldertreestructure, and separate keyinformation in rowids
70
	 *
71
	 * @var string
72
	 */
73
	static $delimiter = '::';
74
75
	/**
76
	 * nextMatch name for index
77
	 *
78
	 * @var string
79
	 */
80
	static $nm_index = 'nm';
81
82
	/**
83
	 * instance of Mail
84
	 *
85
	 * @var Mail
86
	 */
87
	var $mail_bo;
88
89
	/**
90
	 * definition of available / supported search types
91
	 *
92
	 * @var array
93
	 */
94
	var $searchTypes = array(
95
		'quick'		=> 'quicksearch',	// lang('quicksearch')
96
		'quickwithcc'=> 'quicksearch (with cc)',	// lang('quicksearch (with cc)')
97
		'subject'	=> 'subject',		// lang('subject')
98
		'body'		=> 'message body',	// lang('message body')
99
		'from'		=> 'from',			// lang('from')
100
		'to'		=> 'to',			// lang('to')
101
		'cc'		=> 'cc',			// lang('cc')
102
		'text'		=> 'whole message',	// lang('whole message')
103
		'larger'		=> 'greater than',	// lang('greater than')
104
		'smaller'		=> 'less than',	// lang('less than')
105
		'bydate' 	=> 'Selected date range (with quicksearch)',// lang('Selected date range (with quicksearch)')
106
	);
107
108
	/**
109
	 * definition of available / supported status types
110
	 *
111
	 * @var array
112
	 */
113
	var $statusTypes = array(
114
		'any'		=> 'any status',// lang('any status')
115
		'flagged'	=> 'flagged',	// lang('flagged')
116
		'unseen'	=> 'unread',	// lang('unread')
117
		'answered'	=> 'replied',	// lang('replied')
118
		'seen'		=> 'read',		// lang('read')
119
		'deleted'	=> 'deleted',	// lang('deleted')
120
	);
121
122
	/**
123
	 * Constructor
124
	 *
125
	 * @param boolean $run_constructor =true false: no not run constructor and therefore do NOT connect to imap server
126
	 */
127
	function __construct($run_constructor=true)
128
	{
129
		$this->mail_tree = new mail_tree($this);
130
		if (!$run_constructor) return;
131
132
		if (Mail::$debugTimes) $starttime = microtime (true);
133
		// no autohide of the sidebox, as we use it for folderlist now.
134
		unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
135
136 View Code Duplication
		if (isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']) && !empty($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']))
137
		{
138
			self::$icServerID = (int)$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
139
		}
140
		if ($_GET["resetConnection"])
141
		{
142
			unset($_GET["resetConnection"]);
143
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Connection Reset triggered: for Profile with ID:'.self::$icServerID);
144
			Mail::unsetCachedObjects(self::$icServerID);
145
		}
146
147
		try {
148
			$this->mail_bo = Mail::getInstance(true,self::$icServerID, true, false, true);
149 View Code Duplication
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
150
			//error_log(__METHOD__.__LINE__.array2string($this->mail_bo->icServer));
151
152
			// RegEx to minimize extra openConnection
153
			$needle = '/^(?!mail)/';
154
			if (!preg_match($needle,$_GET['menuaction']) && !Api\Json\Request::isJSONRequest())
155
			{
156
				//error_log(__METHOD__.__LINE__.' Fetched IC Server openConnection:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
157
				//openConnection gathers SpecialUseFolderInformation and Delimiter Info
158
				$this->mail_bo->openConnection(self::$icServerID);
159
			}
160
		}
161
		catch (Exception $e)
162
		{
163
			// we need this to handle failed JSONRequests
164
			if (Api\Json\Request::isJSONRequest() && $_GET['menuaction'] != 'mail.mail_ui.index')
165
			{
166
				$response = Api\Json\Response::get();
167
				$response->call('egw.message',$e->getMessage(),'error');
168
			}
169
			// redirect to mail wizard to handle it (redirect works for ajax too), unless index is called. we want the sidebox
170
			if ($_GET['menuaction'] != 'mail.mail_ui.index') self::callWizard($e->getMessage(),true,'error',false);
171
		}
172
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
173
	}
174
175
	/**
176
	 * callWizard
177
	 *
178
	 * @param string $message
179
	 * @param boolean $exit If true, will call exit() after opening the wizardpopup
180
	 * @param string $msg_type = 'success' message type
181
	 */
182
	static function callWizard($message, $exit=true, $msg_type='success',$reset_sidebox_on_index=true)
183
	{
184
		//error_log(__METHOD__."('$message', $exit) ".function_backtrace());
185
		$linkData=(self::$icServerID ? array(
186
				'menuaction' => 'mail.mail_wizard.edit',
187
				'acc_id' => self::$icServerID,
188
			) : array(
189
				'menuaction' => 'mail.mail_wizard.add',
190
			)) + array(
191
				'msg' => $message,
192
				'msg_type' => $msg_type
193
			);
194
195
		if (Api\Json\Response::isJSONResponse())
196
		{
197
			$response = Api\Json\Response::get();
198
			$windowName = "editMailAccount".self::$icServerID;
199
			$response->call("egw.open_link", Egw::link('/index.php', $linkData), $windowName, "600x480",null,true);
200
			Framework::message($message, 'error');
201
			if ($_GET['menuaction'] == 'mail.mail_ui.index' && $reset_sidebox_on_index)
202
			{
203
				$response->call('framework.setSidebox','mail',array(),'md5');
204
			}
205
			if ($exit)
206
			{
207
				exit();
208
			}
209
		}
210
		else	// regular GET request eg. in idots template
211
		{
212
			$windowName = "editMailAccount".self::$icServerID;
213
			Framework::popup(Framework::link('/index.php',$linkData),$windowName);
214
			$GLOBALS['egw']->framework->render($message,'',true);
215
			if ($exit)
216
			{
217
				exit();
218
			}
219
		}
220
	}
221
222
	/**
223
	 * changeProfile
224
	 *
225
	 * @param int $_icServerID
226
	 * @param boolean $unsetCache
227
	 *
228
	 * @throws Api\Exception
229
	 */
230
	function changeProfile($_icServerID,$unsetCache=false)
231
	{
232
		if (Mail::$debugTimes) $starttime = microtime (true);
233
		if (self::$icServerID != $_icServerID)
234
		{
235
			self::$icServerID = $_icServerID;
236
		}
237
		if (Mail::$debug) error_log(__METHOD__.__LINE__.'->'.self::$icServerID.'<->'.$_icServerID);
238
239
		if ($unsetCache) Mail::unsetCachedObjects(self::$icServerID);
240
		$this->mail_bo = Mail::getInstance(false,self::$icServerID,true, false, true);
241 View Code Duplication
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
242
		// no icServer Object: something failed big time
243
		if (!isset($this->mail_bo) || !isset($this->mail_bo->icServer) || $this->mail_bo->icServer->ImapServerId<>$_icServerID)
244
		{
245
			self::$icServerID = $_icServerID;
246
			throw new Api\Exception('Profile change failed!');
247
		}
248
249
		// save session varchar
250
		$oldicServerID =& Api\Cache::getSession('mail','activeProfileID');
251
		if ($oldicServerID <> self::$icServerID) $this->mail_bo->openConnection(self::$icServerID);
252
		if (true) $oldicServerID = self::$icServerID;
253
		if (!Mail::storeActiveProfileIDToPref($this->mail_bo->icServer, self::$icServerID, true ))
0 ignored issues
show
Bug Best Practice introduced by
The expression \EGroupware\Api\Mail::st...elf::$icServerID, true) of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
254
		{
255
			throw new Api\Exception(__METHOD__." failed to change Profile to $_icServerID");
256
		}
257
258
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
259
	}
260
261
	/**
262
	 * Ajax function to request next branch of a tree branch
263
	 */
264
	static function ajax_tree_autoloading ($_id = null)
265
	{
266
		$mail_ui = new mail_ui();
267
		$id = $_id ? $_id : $_GET['id'];
268
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,false));
269
	}
270
271
	/**
272
	 * Subscription popup window
273
	 *
274
	 * @param array $content
275
	 * @param type $msg
276
	 */
277
	function subscription(array $content=null ,$msg=null)
278
	{
279
		$stmpl = new Etemplate('mail.subscribe');
280
281
		if(is_array($content))
282
		{
283
			$profileId = $content['profileId'];
284
		}
285
		elseif (!($profileId = (int)$_GET['acc_id']))
286
		{
287
			Framework::window_close('Missing acc_id!');
288
		}
289
		// Initial tree's options, the rest would be loaded dynamicaly by autoloading,
290
		// triggered from client-side. Also, we keep this here as
291
		$sel_options['foldertree'] =  $this->mail_tree->getTree(null,$profileId,1,true,false,true);
292
293
		//Get all subscribed folders
294
		// as getting all subscribed folders is very fast operation
295
		// we can use it to get a comparison base for folders which
296
		// got subscribed or unsubscribed by the user
297
		try {
298
			$subscribed = $this->mail_bo->icServer->listSubscribedMailboxes('',0,true);
299
		} catch (Exception $ex) {
300
			Framework::message($ex->getMessage());
301
		}
302
303
		if (!is_array($content))
304
		{
305
			$content['foldertree'] = array();
306
307
			foreach ($subscribed as $folder)
308
			{
309
				$folderName = $profileId . self::$delimiter . $folder['MAILBOX'];
310
				array_push($content['foldertree'], $folderName);
311
			}
312
		}
313
		else
314
		{
315
			list($button) = @each($content['button']);
316
			switch ($button)
317
			{
318
				case 'save':
319
				case 'apply':
320
				{
321
					// do not let user (un)subscribe namespace roots eg. "other", "user" or "INBOX", same for tree-root/account itself
322
					$namespace_roots = array($profileId);
323
					foreach($this->mail_bo->_getNameSpaces() as $namespace)
324
					{
325
						$namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']);
326
					}
327
					$to_unsubscribe = $to_subscribe = array();
328
					foreach ($content['foldertree'] as $path => $value)
329
					{
330
						list(,$node) = explode($profileId.self::$delimiter, $path);
331
						if ($node)
332
						{
333
							if (is_array($subscribed) && $subscribed[$node] && !$value['value']) $to_unsubscribe []= $node;
334
							if (is_array($subscribed) && !$subscribed[$node] && $value['value']) $to_subscribe [] = $node;
335
							if ($value['value']) $cont[] = $path;
336
						}
337
338
					}
339
					$content['foldertree'] = $cont;
340
					// set foldertree options to basic node in order to avoid initial autoloading
341
					// from client side, as no options would trigger that.
342
					$sel_options['foldertree'] = array('id' => '0', 'item'=> array());
343
					foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox)
344
					{
345
						if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true))
346
						{
347
							continue;
348
						}
349
						$subscribe = in_array($mailbox, $to_subscribe);
350
						try {
351
							$this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe);
352
						}
353
						catch (Exception $ex)
354
						{
355
							$msg_type = 'error';
356
							if ($subscribe)
357
							{
358
								$msg .= lang('Failed to subscribe folder %1!', $mailbox).' '.$ex->getMessage();
359
							}
360
							else
361
							{
362
								$msg .= lang('Failed to unsubscribe folder %1!', $mailbox).' '.$ex->getMessage();
363
							}
364
						}
365
					}
366
					if (!isset($msg))
367
					{
368
						$msg_type = 'success';
369
						if ($to_subscribe || $to_unsubscribe)
370
						{
371
							$msg = lang('Subscription successfully saved.');
372
						}
373
						else
374
						{
375
							$msg = lang('Nothing to change.');
376
						}
377
					}
378
					// update foldertree in main window
379
					$parentFolder='INBOX';
380
					$refreshData = array(
381
						$profileId => lang($parentFolder),
382
					);
383
					$response = Api\Json\Response::get();
384
					foreach($refreshData as $folder => &$name)
385
					{
386
						$name = $this->mail_tree->getTree($folder, $profileId,1,true,true,true);
387
					}
388
					// give success/error message to opener and popup itself
389
					//$response->call('opener.app.mail.subscription_refresh',$refreshData);
390
					$response->call('opener.app.mail.mail_reloadNode',$refreshData);
391
392
					Framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type);
393
					if ($button == 'apply')
394
					{
395
						Framework::message($msg, $msg_type);
396
						break;
397
					}
398
				}
399
				case 'cancel':
400
				{
401
					Framework::window_close();
402
				}
403
			}
404
		}
405
406
		$preserv['profileId'] = $profileId;
407
408
		$readonlys = array();
409
410
		$stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2);
411
	}
412
413
	/**
414
	 * Main mail page
415
	 *
416
	 * @param array $content
417
	 * @param string $msg
418
	 */
419
	function index(array $content=null,$msg=null)
420
	{
421
		//error_log(__METHOD__.__LINE__.array2string($content));
422
		try	{
423
				if (!isset($this->mail_bo)) throw new Api\Exception\WrongUserinput(lang('Initialization of mail failed. Please use the Wizard to cope with the problem.'));
424
				//error_log(__METHOD__.__LINE__.function_backtrace());
425
				if (Mail::$debugTimes) $starttime = microtime (true);
426
				$this->mail_bo->restoreSessionData();
427
				$sessionFolder = $this->mail_bo->sessionData['mailbox'];
428
				if ($this->mail_bo->folderExists($sessionFolder))
429
				{
430
					$this->mail_bo->reopen($sessionFolder); // needed to fetch full set of capabilities
431
				}
432
				else
433
				{
434
					$sessionFolder = $this->mail_bo->sessionData['mailbox'] = 'INBOX';
435
				}
436
				//error_log(__METHOD__.__LINE__.' SessionFolder:'.$sessionFolder.' isToSchema:'.$toSchema);
437
				if (!is_array($content))
438
				{
439
					$content = array(
440
						self::$nm_index => Api\Cache::getSession('mail', 'index'),
441
					);
442
					if (!is_array($content[self::$nm_index]))
443
					{
444
						// These only set on first load
445
						$content[self::$nm_index] = array(
446
							'filter'         => 'any',	// filter is used to choose the mailbox
447
							'lettersearch'   => false,	// I  show a lettersearch
448
							'searchletter'   =>	false,	// I0 active letter of the lettersearch or false for [all]
449
							'start'          =>	0,		// IO position in list
450
							'order'          =>	'date',	// IO name of the column to sort after (optional for the sortheaders)
451
							'sort'           =>	'DESC',	// IO direction of the sort: 'ASC' or 'DESC'
452
						);
453
					}
454
					if (Api\Header\UserAgent::mobile()) $content[self::$nm_index]['header_row'] = 'mail.index.header_right';
455
				}
456
457
				// These must always be set, even if $content is an array
458
				$content[self::$nm_index]['cat_is_select'] = true;    // Category select is just a normal selectbox
459
				$content[self::$nm_index]['no_filter2'] = false;       // Disable second filter
460
				$content[self::$nm_index]['actions'] = self::get_actions();
461
				$content[self::$nm_index]['row_id'] = 'row_id';	     // is a concatenation of trim($GLOBALS['egw_info']['user']['account_id']):profileID:base64_encode(FOLDERNAME):uid
462
				$content[self::$nm_index]['placeholder_actions'] = array('composeasnew');
463
				$content[self::$nm_index]['get_rows'] = 'mail_ui::get_rows';
464
				$content[self::$nm_index]['num_rows'] = 0;      // Do not send any rows with initial request
465
				$content[self::$nm_index]['default_cols'] = 'status,attachments,subject,address,date,size';	// I  columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
466
				$content[self::$nm_index]['csv_fields'] = false;
467
				if ($msg)
468
				{
469
					$content['msg'] = $msg;
470
				}
471
				else
472
				{
473
					unset($msg);
474
					unset($content['msg']);
475
				}
476
				// call getQuotaRoot asynchronously in getRows by initiating a client Server roundtrip
477
				$quota = false;//$this->mail_bo->getQuotaRoot();
478
				if($quota !== false && $quota['limit'] != 'NOT SET') {
479
					$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
480
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = $quotainfo['text'];
481
					$content[self::$nm_index]['quotainpercent'] = $sel_options[self::$nm_index]['quotainpercent'] =  (string)$quotainfo['percent'];
482
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = $quotainfo['class'];
483
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "";
484
				} else {
485
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = lang("Quota not provided by server");
486
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = "mail_DisplayNone";
487
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "mail_DisplayNone";
488
				}
489
				// call gatherVacation asynchronously in getRows by initiating a client Server roundtrip
490
				$vacation = false;//$this->gatherVacation();
491
				//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
492
				if($vacation) {
493
					if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date' && $vacation['end_date'] > time()))
494
					{
495
						$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']/*.' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat']!='24'?'h:i a':'H:i')*/;
496
						$content[self::$nm_index]['vacationnotice'] = $sel_options[self::$nm_index]['vacationnotice'] = lang('Vacation notice is active');
497
						$content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['vacationrange'] = ($vacation['status']=='by_date'? Api\DateTime::server2user($vacation['start_date'],$dtfrmt,true).($vacation['end_date']>$vacation['start_date']?'->'.Api\DateTime::server2user($vacation['end_date']+ 24*3600-1,$dtfrmt,true):''):'');
0 ignored issues
show
Unused Code introduced by
The call to DateTime::server2user() has too many arguments starting with true.

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

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

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

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

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

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

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

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

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

Loading history...
818
			error_log(__METHOD__.__LINE__."()". lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));
819
			Framework::message(lang('Folder %1 %2 failed!',$_folderName,$_status));
820
		}
821
	}
822
823
	/**
824
	 * Ajax callback to fetch folders for given profile
825
	 *
826
	 * We currently load all folders of a given profile, tree can also load parts of a tree.
827
	 *
828
	 * @param string $_nodeID if of node whose children are requested
829
	 * @param boolean $_subscribedOnly flag to tell whether to fetch all or only subscribed (default)
830
	 */
831
	public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null)
832
	{
833
		$nodeID = $_GET['id'];
834
		if (!is_null($_nodeID)) $nodeID = $_nodeID;
835
		$subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
836
		$fetchCounters = !is_null($_nodeID);
837
		list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2);
838
839
		if (!empty($_folderName)) $fetchCounters = true;
840
841
		// Check if it is called for refresh root
842
		// then we need to reinitialized the index tree
843
		if(!$nodeID && !$_profileID)
844
		{
845
			$data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
846
		}
847
		else
848
		{
849
			$data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
850
		}
851
		if (!is_null($_nodeID)) return $data;
852
		Etemplate\Widget\Tree::send_quote_json($data);
853
	}
854
855
	/**
856
	 * findNode - helper function to return only a branch of the tree
857
	 *
858
	 * @param array $_out out array (to be searched)
859
	 * @param string $_nodeID node to search for
860
	 * @param boolean $childElements return node itself, or only its child items
861
	 * @return array structured subtree
862
	 */
863
	static function findNode($_out, $_nodeID, $childElements = false)
864
	{
865
		foreach($_out['item'] as $node)
866
		{
867
			if (strcmp($node['id'],$_nodeID)===0)
868
			{
869
				//error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']);
870
				return ($childElements?$node['item']:$node);
871
			}
872
			elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id']))
873
			{
874
				//error_log(__METHOD__.__LINE__.' descend into '.$node['id']);
875
				return self::findNode($node,$_nodeID,$childElements);
876
			}
877
		}
878
	}
879
880
881
	/**
882
	 * Get actions / context menu for index
883
	 *
884
	 * Changes here, require to log out, as $content[self::$nm_index] get stored in session!
885
	 * @return array see nextmatch_widget::egw_actions()
886
	 */
887
	private function get_actions()
888
	{
889
		static $accArray=array(); // buffer identity names on single request
890
		// duplicated from mail_hooks
891
		static $deleteOptions = array(
892
			'move_to_trash'		=> 'move to trash',
893
			'mark_as_deleted'	=> 'mark as deleted',
894
			'remove_immediately' =>	'remove immediately',
895
		);
896
		// todo: real hierarchical folder list
897
		$lastFolderUsedForMove = null;
898
		$moveactions = array();
899
		$archiveFolder = $this->mail_bo->getArchiveFolder();
900
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
901
		//error_log(__METHOD__.__LINE__." StoredFolders->".array2string($lastFoldersUsedForMoveCont));
902
		//error_log(__METHOD__.__LINE__.' ProfileId:'.$this->mail_bo->profileID." StoredFolders->(".count($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]).") ".array2string($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]));
903
		if (is_null($accArray))
904
		{
905
			foreach(Mail\Account::search($only_current_user=true, false) as $acc_id => $accountObj)
906
			{
907
				//error_log(__METHOD__.__LINE__.array2string($accountObj));
908
				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...
909
				{
910
					// not to be used for IMAP Foldertree, as there is no Imap host
911
					continue;
912
				}
913
				$identity_name = Mail\Account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']);
914
				$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
915
			}
916
		}
917
		if (!is_array($lastFoldersUsedForMoveCont)) $lastFoldersUsedForMoveCont=array();
918
		foreach (array_keys($lastFoldersUsedForMoveCont) as $pid)
919
		{
920
			if ($this->mail_bo->profileID==$pid && isset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]))
921
			{
922
				$_folder = $this->mail_bo->icServer->getCurrentMailbox();
923
				//error_log(__METHOD__.__LINE__.' '.$_folder."<->".$lastFoldersUsedForMoveCont[$this->mail_bo->profileID].function_backtrace());
924
				$counter =1;
925
				foreach ($lastFoldersUsedForMoveCont[$this->mail_bo->profileID] as $i => $lastFolderUsedForMoveCont)
926
				{
927
					$moveaction = 'move_';
928
					if ($_folder!=$i)
929
					{
930
						$moveaction .= $lastFolderUsedForMoveCont;
931
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
932
						//error_log(__METHOD__.__LINE__.'#'.$currentArchiveActionKey);
933
						if ($this->mail_bo->folderExists($i)) // only 10 entries per mailaccount.Control this on setting the buffered folders
934
						{
935
							$fS['profileID'] = $this->mail_bo->profileID;
936
							$fS['profileName'] = $accArray[$this->mail_bo->profileID];
937
							$fS['shortDisplayName'] = $i;
938
							$moveactions[$moveaction] = $fS;
939
							$counter ++;
940
						}
941
						else
942
						{
943
							unset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID][$i]);
944
						}
945
						//error_log(array2string($moveactions[$moveaction]));
946
					}
947
				}
948
			}
949
			elseif ($this->mail_bo->profileID!=$pid && isset($lastFoldersUsedForMoveCont[$pid]) && !empty($lastFoldersUsedForMoveCont[$pid]))
950
			{
951
				$counter =1;
952
				foreach ($lastFoldersUsedForMoveCont[$pid] as $i => $lastFolderUsedForMoveCont)
953
				{
954
					//error_log(__METHOD__.__LINE__."$i => $lastFolderUsedForMoveCont");
955
					if (!empty($lastFolderUsedForMoveCont)) // only 10 entries per mailaccount.Control this on setting the buffered folders
956
					{
957
						$moveaction = 'move_'.$lastFolderUsedForMoveCont;
958
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
959
						$fS = array();
960
						$fS['profileID'] = $pid;
961
						$fS['profileName'] = $accArray[$pid];
962
						$fS['shortDisplayName'] = $i;
963
						$moveactions[$moveaction] = $fS;
964
						$counter ++;
965
					}
966
				}
967
			}
968
		}
969
		Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
970
		$group = 0;
971
		$actions =  array(
972
			'open' => array(
973
				'caption' => lang('Open'),
974
				'icon' => 'view',
975
				'group' => ++$group,
976
				'onExecute' => Api\Header\UserAgent::mobile()?'javaScript:app.mail.mobileView':'javaScript:app.mail.mail_open',
977
				'allowOnMultiple' => false,
978
				'default' => true,
979
				'mobileViewTemplate' => 'view?'.filemtime(Api\Etemplate\Widget\Template::rel2path('/mail/templates/mobile/view.xet'))
980
			),
981
			'reply' => array(
982
				'caption' => 'Reply',
983
				'icon' => 'mail_reply',
984
				'group' => ++$group,
985
				'onExecute' => 'javaScript:app.mail.mail_compose',
986
				'allowOnMultiple' => false,
987
				'toolbarDefault' => true
988
			),
989
			'reply_all' => array(
990
				'caption' => 'Reply All',
991
				'icon' => 'mail_replyall',
992
				'group' => $group,
993
				'onExecute' => 'javaScript:app.mail.mail_compose',
994
				'allowOnMultiple' => false,
995
			),
996
			'forward' => array(
997
				'caption' => 'Forward',
998
				'icon' => 'mail_forward',
999
				'group' => $group,
1000
				'children' => array(
1001
					'forwardinline' => array(
1002
						'caption' => 'Inline',
1003
						'icon' => 'mail_forward',
1004
						'group' => $group,
1005
						'hint' => 'forward inline',
1006
						'onExecute' => 'javaScript:app.mail.mail_compose',
1007
						'allowOnMultiple' => false,
1008
						'toolbarDefault' => true
1009
					),
1010
					'forwardasattach' => array(
1011
						'caption' => 'Attachment',
1012
						'hint' => 'forward as attachment',
1013
						'icon' => 'mail_forward_attach',
1014
						'group' => $group,
1015
						'onExecute' => 'javaScript:app.mail.mail_compose',
1016
					),
1017
				),
1018
				'hideOnMobile' => true
1019
			),
1020
			'composeasnew' => array(
1021
				'caption' => 'Compose',
1022
				'icon' => 'new',
1023
				'hint' => 'Compose as new',
1024
				'group' => $group,
1025
				'onExecute' => 'javaScript:app.mail.mail_compose',
1026
				'allowOnMultiple' => false,
1027
			)
1028
		);
1029
		$macounter=0;
1030
		if (!empty($moveactions))
1031
		{
1032
			//error_log(__METHOD__.__LINE__.array2string($moveactions));
1033
			$children=array();
1034
			$pID=0;
1035
			foreach ($moveactions as $moveaction => $lastFolderUsedForMove)
1036
			{
1037
				$group = ($pID != $lastFolderUsedForMove['profileID'] && $macounter>0? $group+1 : $group);
1038
				//error_log(__METHOD__.__LINE__."#$pID != ".$lastFolderUsedForMove['profileID']."#".$macounter.'#'.$groupCounter.'#');
1039
				$children = array_merge($children,
1040
					array(
1041
						$moveaction => array(
1042
							'caption' => (!empty($lastFolderUsedForMove['profileName'])?$lastFolderUsedForMove['profileName']:'('.$lastFolderUsedForMove['profileID'].')').': '.(isset($lastFolderUsedForMove['shortDisplayName'])?$lastFolderUsedForMove['shortDisplayName']:''),
1043
							'icon' => 'move',
1044
							'group' => $group,
1045
							'onExecute' => 'javaScript:app.mail.mail_move2folder',
1046
							'allowOnMultiple' => true,
1047
						)
1048
					)
1049
				);
1050
				$pID = $lastFolderUsedForMove['profileID'];
1051
				$macounter++;
1052
			}
1053
			$actions['moveto'] =	array(
1054
				'caption' => lang('Move selected to'),
1055
				'icon' => 'move',
1056
				'group' => $group,
1057
				'children' => $children,
1058
			);
1059
1060
		} else {
1061
			$group++;
1062
		}
1063
1064
		//error_log(__METHOD__.__LINE__.$archiveFolder);
1065
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter.$archiveFolder] = array( //toarchive
1066
			'caption' => 'Move to archive',
1067
			'hint' => 'move selected mails to archive',
1068
			'icon' => 'archive',
1069
			'group' => $group++,
1070
			'enabled' => 'javaScript:app.mail.archivefolder_enabled',
1071
			//'hideOnDisabled' => true, // does not work as expected on message-list
1072
			'onExecute' => 'javaScript:app.mail.mail_move2folder',
1073
			'shortcut' => KeyManager::shortcut(KeyManager::V, true, true),
1074
			'allowOnMultiple' => true,
1075
			'toolbarDefault' => false
1076
		);
1077
1078
		$actions += array(
1079
			'infolog' => array(
1080
				'caption' => 'InfoLog',
1081
				'hint' => 'Save as InfoLog',
1082
				'icon' => 'infolog/navbar',
1083
				'group' => ++$group,
1084
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1085
				'popup' => Link::get_registry('infolog', 'add_popup'),
1086
				'allowOnMultiple' => false,
1087
				'toolbarDefault' => true
1088
			),
1089
			'tracker' => array(
1090
				'caption' => 'Tracker',
1091
				'hint' => 'Save as ticket',
1092
				'group' => $group,
1093
				'icon' => 'tracker/navbar',
1094
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1095
				'popup' => Link::get_registry('tracker', 'add_popup'),
1096
				'mail_import' => Api\Hooks::single(array('location' => 'mail_import'),'tracker'),
1097
				'allowOnMultiple' => false,
1098
			),
1099
			'calendar' => array(
1100
				'caption' => 'Calendar',
1101
				'hint' => 'Save as Calendar',
1102
				'icon' => 'calendar/navbar',
1103
				'group' => $group,
1104
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1105
				'popup' => Link::get_registry('calendar', 'add_popup'),
1106
				'allowOnMultiple' => false,
1107
				'toolbarDefault' => true
1108
			),
1109
			'print' => array(
1110
				'caption' => 'Print',
1111
				'group' => ++$group,
1112
				'onExecute' => 'javaScript:app.mail.mail_print',
1113
				'allowOnMultiple' => false,
1114
				'hideOnMobile' => true
1115
			),
1116
			'save' => array(
1117
				'caption' => 'Save',
1118
				'group' => $group,
1119
				'icon' => 'fileexport',
1120
				'children' => array(
1121
					'save2disk' => array(
1122
						'caption' => 'Save to disk',
1123
						'hint' => 'Save message to disk',
1124
						'group' => $group,
1125
						'icon' => 'fileexport',
1126
						'onExecute' => 'javaScript:app.mail.mail_save',
1127
						'allowOnMultiple' => false,
1128
						'hideOnMobile' => true
1129
					),
1130
					'save2filemanager' => array(
1131
						'caption' => 'Filemanager',
1132
						'hint' => 'Save to filemanager',
1133
						'group' => $group,
1134
						'icon' => 'filemanager/navbar',
1135
						'onExecute' => 'javaScript:app.mail.mail_save2fm',
1136
						'allowOnMultiple' => false,
1137
					),
1138
				),
1139
				'hideOnMobile' => true
1140
			),
1141
			'view' => array(
1142
				'caption' => 'View',
1143
				'group' => $group,
1144
				'icon' => 'kmmsgread',
1145
				'children' => array(
1146
					'header' => array(
1147
						'caption' => 'Header',
1148
						'hint' => 'View header lines',
1149
						'group' => $group,
1150
						'icon' => 'kmmsgread',
1151
						'onExecute' => 'javaScript:app.mail.mail_header',
1152
						'allowOnMultiple' => false,
1153
					),
1154
					'mailsource' => array(
1155
						'caption' => 'Source',
1156
						'hint' => 'View full Mail Source',
1157
						'group' => $group,
1158
						'icon' => 'source',
1159
						'onExecute' => 'javaScript:app.mail.mail_mailsource',
1160
						'allowOnMultiple' => false,
1161
					),
1162
					'openastext' => array(
1163
						'caption' => lang('Text mode'),
1164
						'hint' => 'Open in Text mode',
1165
						'group' => ++$group,
1166
						'icon' => 'textmode',
1167
						'onExecute' => 'javaScript:app.mail.mail_openAsText',
1168
						'allowOnMultiple' => false,
1169
					),
1170
					'openashtml' => array(
1171
						'caption' => lang('HTML mode'),
1172
						'hint' => 'Open in HTML mode',
1173
						'group' => $group,
1174
						'icon' => 'htmlmode',
1175
						'onExecute' => 'javaScript:app.mail.mail_openAsHtml',
1176
						'allowOnMultiple' => false,
1177
					),
1178
				),
1179
				'hideOnMobile' => true
1180
			),
1181
			'mark' => array(
1182
				'caption' => 'Set / Remove Flags',
1183
				'icon' => 'read_small',
1184
				'group' => ++$group,
1185
				'children' => array(
1186
					// icons used from http://creativecommons.org/licenses/by-sa/3.0/
1187
					// Artist: Led24
1188
					// Iconset Homepage: http://led24.de/iconset
1189
					// License: CC Attribution 3.0
1190
					'setLabel' => array(
1191
						'caption' => 'Set / Remove Labels',
1192
						'icon' => 'tag_message',
1193
						'group' => ++$group,
1194
						// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
1195
						'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
1196
						'hideOnDisabled' => true,
1197
						'children' => array(
1198
							'unlabel' => array(
1199
								'group' => ++$group,
1200
								'caption' => "<font color='#ff0000'>".lang('remove all')."</font>",
1201
								'icon' => 'mail_label',
1202
								'onExecute' => 'javaScript:app.mail.mail_flag',
1203
								'shortcut' => KeyManager::shortcut(KeyManager::_0, true, true),
1204
							),
1205
							'label1' => array(
1206
								'group' => ++$group,
1207
								'caption' => "<font color='#ff0000'>".lang('important')."</font>",
1208
								'icon' => 'mail_label1',
1209
								'onExecute' => 'javaScript:app.mail.mail_flag',
1210
								'shortcut' => KeyManager::shortcut(KeyManager::_1, true, true),
1211
							),
1212
							'label2' => array(
1213
								'group' => $group,
1214
								'caption' => "<font color='#ff8000'>".lang('job')."</font>",
1215
								'icon' => 'mail_label2',
1216
								'onExecute' => 'javaScript:app.mail.mail_flag',
1217
								'shortcut' => KeyManager::shortcut(KeyManager::_2, true, true),
1218
							),
1219
							'label3' => array(
1220
								'group' => $group,
1221
								'caption' => "<font color='#008000'>".lang('personal')."</font>",
1222
								'icon' => 'mail_label3',
1223
								'onExecute' => 'javaScript:app.mail.mail_flag',
1224
								'shortcut' => KeyManager::shortcut(KeyManager::_3, true, true),
1225
							),
1226
							'label4' => array(
1227
								'group' => $group,
1228
								'caption' => "<font color='#0000ff'>".lang('to do')."</font>",
1229
								'icon' => 'mail_label4',
1230
								'onExecute' => 'javaScript:app.mail.mail_flag',
1231
								'shortcut' => KeyManager::shortcut(KeyManager::_4, true, true),
1232
							),
1233
							'label5' => array(
1234
								'group' => $group,
1235
								'caption' => "<font color='#8000ff'>".lang('later')."</font>",
1236
								'icon' => 'mail_label5',
1237
								'onExecute' => 'javaScript:app.mail.mail_flag',
1238
								'shortcut' => KeyManager::shortcut(KeyManager::_5, true, true),
1239
							),
1240
						),
1241
					),
1242
					// modified icons from http://creativecommons.org/licenses/by-sa/3.0/
1243
					'flagged' => array(
1244
						'group' => ++$group,
1245
						'caption' => 'Flag / Unflag',
1246
						'icon' => 'unread_flagged_small',
1247
						'onExecute' => 'javaScript:app.mail.mail_flag',
1248
						'hint' => 'Flag or Unflag a mail',
1249
						'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
1250
						'toolbarDefault' => true
1251
					),
1252
					'read' => array(
1253
						'group' => $group,
1254
						'caption' => 'Read / Unread',
1255
						'icon' => 'read_small',
1256
						'onExecute' => 'javaScript:app.mail.mail_flag',
1257
						'shortcut' => KeyManager::shortcut(KeyManager::U, true, true),
1258
1259
					),
1260
					'readall' => array(
1261
						'group' => ++$group,
1262
						'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
1263
						'icon' => 'read_small',
1264
						'onExecute' => 'javaScript:app.mail.mail_flag',
1265
						'hint' => 'mark all messages in folder as read',
1266
						'toolbarDefault' => false
1267
					),
1268
					'undelete' => array(
1269
						'group' => $group,
1270
						'caption' => 'Undelete',
1271
						'icon' => 'revert',
1272
						'onExecute' => 'javaScript:app.mail.mail_flag',
1273
					),
1274
				),
1275
			),
1276
			'delete' => array(
1277
				'caption' => 'Delete',
1278
				'hint' => $deleteOptions[$this->mail_bo->mailPreferences['deleteOptions']],
1279
				'group' => ++$group,
1280
				'onExecute' => 'javaScript:app.mail.mail_delete',
1281
				'toolbarDefault' => true
1282
			),
1283
			'drag_mail' => array(
1284
				'dragType' => array('mail'),
1285
				'type' => 'drag',
1286
				//'onExecute' => 'javaScript:app.mail.mail_dragStart',
1287
			)
1288
		);
1289
		//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
1290
		// 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
1291 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
1292
		{
1293
			unset($actions['infolog']);
1294
		}
1295 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['tracker']))
1296
		{
1297
			unset($actions['tracker']);
1298
		}
1299 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['calendar']))
1300
		{
1301
			unset($actions['calendar']);
1302
		}
1303
		return $actions;
1304
	}
1305
1306
	/**
1307
	 * Callback to fetch the rows for the nextmatch widget
1308
	 *
1309
	 * Function is static to not automatic call constructor in case profile is changed.
1310
	 *
1311
	 * @param array $query
1312
	 * @param array &$rows
1313
	 * @param array &$readonlys
1314
	 */
1315
	public static function get_rows(&$query,&$rows,&$readonlys)
1316
	{
1317
		unset($readonlys);	// not used, but required by function signature
1318
1319
		// handle possible profile change in get_rows
1320
		if (!empty($query['selectedFolder']))
1321
		{
1322
			list($_profileID,$folderName) = explode(self::$delimiter, $query['selectedFolder'], 2);
1323
			if (is_numeric(($_profileID)) && $_profileID != $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])
1324
			{
1325
				try {
1326
					$mail_ui = new mail_ui(false);	// do NOT run constructor, as we change profile anyway
1327
					$mail_ui->changeProfile($_profileID);
1328
					$query['actions'] = $mail_ui->get_actions();
1329
				}
1330
				catch(Exception $e)
1331
				{
1332
					unset($e);
1333
					$rows=array();
1334
					return 0;
1335
				}
1336
				if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
1337
			}
1338
		}
1339
		if (!isset($mail_ui))
1340
		{
1341
			try
1342
			{
1343
				$mail_ui = new mail_ui(true);	// run constructor for current profile
1344
			}
1345
			catch(Exception $e)
1346
			{
1347
				unset($e);
1348
				$rows=array();
1349
				return 0;
1350
			}
1351
			if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
1352
		}
1353
		//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
1354
		//Mail::$debugTimes=true;
1355
		if (Mail::$debugTimes) $starttime = microtime(true);
1356
		//$query['search'] is the phrase in the searchbox
1357
1358
		$mail_ui->mail_bo->restoreSessionData();
1359
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$query['selectedFolder'];
1360
1361
		$sRToFetch = null;
1362
		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...
1363
		if (strpos($_folderName,self::$delimiter)!==false)
1364
		{
1365
			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...
1366
			unset($app);
1367
		}
1368
		//save selected Folder to sessionData (mailbox)->currentFolder
1369
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$_folderName;
1370
		$toSchema = false;//decides to select list schema with column to selected (if false fromaddress is default)
1371
		if ($mail_ui->mail_bo->folderExists($_folderName))
1372
		{
1373
			$toSchema = $mail_ui->mail_bo->isDraftFolder($_folderName,false)||$mail_ui->mail_bo->isSentFolder($_folderName,false)||$mail_ui->mail_bo->isTemplateFolder($_folderName,false);
1374
		}
1375
		else
1376
		{
1377
			// take the extra time on failure
1378
			if (!$mail_ui->mail_bo->folderExists($_folderName,true))
1379
			{
1380
				//error_log(__METHOD__.__LINE__.' Test on Folder:'.$_folderName.' failed; Using INBOX instead');
1381
				$query['selectedFolder']=$mail_ui->mail_bo->sessionData['mailbox']=$_folderName='INBOX';
1382
			}
1383
		}
1384
		$rowsFetched['messages'] = null;
1385
		$offset = $query['start']+1; // we always start with 1
1386
		$maxMessages = $query['num_rows'];
1387
		//error_log(__METHOD__.__LINE__.array2string($query));
1388
		$sort = ($query['order']=='address'?($toSchema?'toaddress':'fromaddress'):$query['order']);
1389
		if (!empty($query['search'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
1390
		{
1391
			if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1392
			{
1393
				Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
1394
				if (!isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1395
				{
1396
					Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]=true;
1397
				}
1398
			}
1399
			//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
1400
			$cutoffdate = $cutoffdate2 = null;
1401
			if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
1402
			if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
1403
			//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
1404
			$filter = array(
1405
				'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
1406
				'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
1407
				'string' => $query['search'],
1408
				'status' => 'any',
1409
				//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
1410
			);
1411
			if ($query['enddate']||$query['startdate']) {
1412
				$filter['range'] = "BETWEEN";
1413
				if ($cutoffdate) {
1414
					$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
1415
					if (empty($cutoffdate2)) $filter['range'] = "SINCE";
1416
				}
1417
				if ($cutoffdate2) {
1418
					$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
1419
					if (empty($cutoffdate)) $filter['range'] = "BEFORE";
1420
				}
1421
			}
1422
		}
1423
		else
1424
		{
1425
			$filter = array();
1426
		}
1427
		if ($query['filter'])
1428
		{
1429
			$filter['status'] = $query['filter'];
1430
		}
1431
		$reverse = ($query['sort']=='ASC'?false:true);
1432
		$prefchanged = false;
1433 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']) || ($query['cat_id'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']))
1434
		{
1435
			//error_log(__METHOD__.__LINE__.' Changing userPref ActivesearchType:'.$query['cat_id']);
1436
			$GLOBALS['egw']->preferences->add('mail','ActiveSearchType',$query['cat_id'],'user');
1437
			$prefchanged = true;
1438
		}
1439 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']) || ($query['filter2'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']))
1440
		{
1441
			$GLOBALS['egw']->preferences->add('mail','ShowDetails',$query['filter2'],'user');
1442
			$prefchanged = true;
1443
		}
1444
		if ($prefchanged)
1445
		{
1446
			// save prefs
1447
			$GLOBALS['egw']->preferences->save_repository(true);
1448
		}
1449
		//error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($mail_ui->sessionData['messageFilter']));
1450
/*
1451
$cutoffdate = Api\DateTime::to('now','ts')-(3600*24*6);//SINCE, enddate
1452
$cutoffdate2 = Api\DateTime::to('now','ts')-(3600*24*3);//BEFORE, startdate
1453
$filter['range'] = "BETWEEN";// we support SINCE, BEFORE, BETWEEN and ON
1454
$filter['since'] = date("d-M-Y", $cutoffdate);
1455
$filter['before']= date("d-M-Y", $cutoffdate2);
1456
*/
1457
		try
1458
		{
1459
			if ($maxMessages > 75)
1460
			{
1461
				$rByUid = true;
1462
				$_sR = $mail_ui->mail_bo->getSortedList(
1463
					$_folderName,
1464
					$sort,
1465
					$reverse,
1466
					$filter,
1467
					$rByUid
1468
				);
1469
				$rowsFetched['messages'] = $_sR['count'];
1470
				$ids = $_sR['match']->ids;
1471
				// if $sR is false, something failed fundamentally
1472
				if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
1473
				$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
1474
				$sRToFetch = $sR;//array_slice($sR,0,50); // we fetch only the headers of a subset of the fetched uids
1475
				//error_log(__METHOD__.__LINE__.' Rows fetched (UID only):'.count($sR).' Data:'.array2string($sR));
1476
				$maxMessages = 75;
1477
				$sortResultwH['header'] = array();
1478
				if (count($sRToFetch)>0)
1479
				{
1480
					//error_log(__METHOD__.__LINE__.' Headers to fetch with UIDs:'.count($sRToFetch).' Data:'.array2string($sRToFetch));
1481
					$sortResult = array();
1482
					// fetch headers
1483
					$sortResultwH = $mail_ui->mail_bo->getHeaders(
1484
						$_folderName,
1485
						$offset,
1486
						$maxMessages,
1487
						$sort,
1488
						$reverse,
1489
						$filter,
1490
						$sRToFetch,
1491
						true, //cacheResult
1492
						($query['filter2']?true:false) // fetchPreview
1493
					);
1494
				}
1495
			}
1496
			else
1497
			{
1498
				$sortResult = array();
1499
				// fetch headers
1500
				$sortResultwH = $mail_ui->mail_bo->getHeaders(
1501
					$_folderName,
1502
					$offset,
1503
					$maxMessages,
1504
					$sort,
1505
					$reverse,
1506
					$filter,
1507
					null, // this uids only
1508
					true, // cacheResult
1509
					($query['filter2']?true:false) // fetchPreview
1510
				);
1511
				$rowsFetched['messages'] = $sortResultwH['info']['total'];
1512
			}
1513
		}
1514
		catch (Exception $e)
1515
		{
1516
			$sortResultwH=array();
1517
			$sR=array();
1518
			self::callWizard($e->getMessage(), false, 'error');
1519
		}
1520
		$response = Api\Json\Response::get();
1521
		// unlock immediately after fetching the rows
1522
		if (stripos($_GET['menuaction'],'ajax_get_rows')!==false)
1523
		{
1524
			//error_log(__METHOD__.__LINE__.' unlock tree ->'.$_GET['menuaction']);
1525
			$response->call('app.mail.unlock_tree');
1526
		}
1527
1528
		if (is_array($sR) && count($sR)>0)
1529
		{
1530
			foreach ((array)$sR as $key => $v)
1531
			{
1532
				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...
1533
				{
1534
					$sortResult['header'][] = $sortResultwH['header'][$key];
1535
				}
1536
				else
1537
				{
1538
					if (!empty($v)) $sortResult['header'][] = array('uid'=>$v);
1539
				}
1540
			}
1541
		}
1542
		else
1543
		{
1544
			$sortResult = $sortResultwH;
1545
		}
1546
		$rowsFetched['rowsFetched'] = count($sortResult['header']);
1547
		if (empty($rowsFetched['messages'])) $rowsFetched['messages'] = $rowsFetched['rowsFetched'];
1548
1549
		//error_log(__METHOD__.__LINE__.' Rows fetched:'.$rowsFetched.' Data:'.array2string($sortResult));
1550
		$cols = array('row_id','uid','status','attachments','subject','address','toaddress','fromaddress','ccaddress','additionaltoaddress','date','size','modified','bodypreview');
1551
		if ($GLOBALS['egw_info']['user']['preferences']['common']['select_mode']=='EGW_SELECTMODE_TOGGLE') unset($cols[0]);
1552
		$rows = $mail_ui->header2gridelements($sortResult['header'],$cols, $_folderName, $folderType=$toSchema);
1553
1554
		// Save the session (since we are committing session) at the end
1555
		// to make sure all necessary data are stored in session.
1556
		// e.g.: Link:: get_data which is used to read attachments data.
1557
		$mail_ui->mail_bo->saveSessionData();
1558
1559
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName.' Start:'.$query['start'].' NumRows:'.$query['num_rows'],__METHOD__.__LINE__);
1560
		return $rowsFetched['messages'];
1561
	}
1562
1563
	/**
1564
	 * function createRowID - create a unique rowID for the grid
1565
	 *
1566
	 * @param string $_folderName used to ensure the uniqueness of the uid over all folders
1567
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1568
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1569
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1570
	 */
1571
	function createRowID($_folderName, $message_uid, $_prependApp=false)
1572
	{
1573
		return self::generateRowID($this->mail_bo->profileID, $_folderName, $message_uid, $_prependApp);
1574
	}
1575
1576
	/**
1577
	 * static function generateRowID - create a unique rowID for the grid
1578
	 *
1579
	 * @param integer $_profileID profile ID for the rowid to be used
1580
	 * @param string $_folderName to ensure the uniqueness of the uid over all folders
1581
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1582
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1583
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1584
	 */
1585
	static function generateRowID($_profileID, $_folderName, $message_uid, $_prependApp=false)
1586
	{
1587
		return ($_prependApp?'mail'.self::$delimiter:'').trim($GLOBALS['egw_info']['user']['account_id']).self::$delimiter.$_profileID.self::$delimiter.base64_encode($_folderName).self::$delimiter.$message_uid;
1588
	}
1589
1590
	/**
1591
	 * function splitRowID - split the rowID into its parts
1592
	 *
1593
	 * @param string $_rowID string - a colon separated string in the form accountID:profileID:folder:message_uid
1594
	 * @return array populated named result array (accountID,profileID,folder,msgUID)
1595
	 */
1596
	static function splitRowID($_rowID)
1597
	{
1598
		$res = explode(self::$delimiter,$_rowID);
1599
		// as a rowID is perceeded by app::, should be mail!
1600
		//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));
1601
		if (count($res)==4 && is_numeric($res[0]) )
1602
		{
1603
			// we have an own created rowID; prepend app=mail
1604
			array_unshift($res,'mail');
1605
		}
1606
		return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
1607
	}
1608
1609
	/**
1610
	 * Get actions for preview toolbar
1611
	 *
1612
	 * @return array
1613
	 */
1614
	function get_toolbar_actions()
1615
	{
1616
		$actions = $this->get_actions();
1617
		$arrActions = array('composeasnew', 'reply', 'reply_all', 'forward', 'flagged', 'delete', 'print',
1618
			'infolog', 'tracker', 'calendar', 'save', 'view', 'read', 'label1',	'label2', 'label3',	'label4', 'label5');
1619
		foreach( $arrActions as &$act)
1620
		{
1621
			//error_log(__METHOD__.__LINE__.' '.$act.'->'.array2string($actions[$act]));
1622
			switch ($act)
1623
			{
1624
				case 'forward':
1625
					$actionsenabled[$act]=$actions[$act];
1626
					break;
1627
				case 'save':
1628
					$actionsenabled[$act]=$actions[$act];
1629
1630
					break;
1631
				case 'view':
1632
					$actionsenabled[$act]=$actions[$act];
1633
					break;
1634
				case 'flagged':
1635
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1636
					break;
1637
				case 'read':
1638
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1639
					break;
1640 View Code Duplication
				case 'label1':
1641
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('important');
1642
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1643
					break;
1644 View Code Duplication
				case 'label2':
1645
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('job');
1646
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1647
					break;
1648 View Code Duplication
				case 'label3':
1649
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('personal');
1650
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1651
					break;
1652 View Code Duplication
				case 'label4':
1653
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('to do');
1654
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1655
					break;
1656 View Code Duplication
				case 'label5':
1657
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('later');
1658
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1659
					break;
1660
				default:
1661
					if (isset($actions[$act])) $actionsenabled[$act]=$actions[$act];
1662
			}
1663
		}
1664
		unset($actionsenabled['drag_mail']);
1665
		//error_log(array2string($actionsenabled['view']));
1666
		unset($actionsenabled['view']['children']['openastext']);//not supported in preview
1667
		unset($actionsenabled['view']['children']['openashtml']);//not supported in preview
1668
1669
		return $actionsenabled;
1670
	}
1671
1672
	/**
1673
	 * function header2gridelements - to populate the grid elements with the collected Data
1674
	 *
1675
	 * @param array $_headers headerdata to process
1676
	 * @param array $cols cols to populate
1677
	 * @param array $_folderName to ensure the uniqueness of the uid over all folders
1678
	 * @param array $_folderType used to determine if we need to populate from/to
1679
	 * @return array populated result array
1680
	 */
1681
	public function header2gridelements($_headers, $cols, $_folderName, $_folderType=0)
1682
	{
1683
		if (Mail::$debugTimes) $starttime = microtime(true);
1684
		$rv = array();
1685
		$i=0;
1686
		foreach((array)$_headers as $header)
1687
		{
1688
			$i++;
1689
			$data = array();
1690
			//error_log(__METHOD__.array2string($header));
1691
			$message_uid = $header['uid'];
1692
			$data['uid'] = $message_uid;
1693
			$data['row_id']=$this->createRowID($_folderName,$message_uid);
1694
1695
			$flags = "";
1696
			if(!empty($header['recent'])) $flags .= "R";
1697
			if(!empty($header['flagged'])) $flags .= "F";
1698
			if(!empty($header['answered'])) $flags .= "A";
1699
			if(!empty($header['forwarded'])) $flags .= "W";
1700
			if(!empty($header['deleted'])) $flags .= "D";
1701
			if(!empty($header['seen'])) $flags .= "S";
1702
			if(!empty($header['label1'])) $flags .= "1";
1703
			if(!empty($header['label2'])) $flags .= "2";
1704
			if(!empty($header['label3'])) $flags .= "3";
1705
			if(!empty($header['label4'])) $flags .= "4";
1706
			if(!empty($header['label5'])) $flags .= "5";
1707
1708
			$data["status"] = "<span class=\"status_img\"></span>";
1709
			//error_log(__METHOD__.array2string($header).' Flags:'.$flags);
1710
1711
			// the css for this row
1712
			$is_recent=false;
1713
			$css_styles = array("mail");
1714
			if ($header['deleted']) {
1715
				$css_styles[] = 'deleted';
1716
			}
1717
			if ($header['recent'] && !($header['deleted'] || $header['seen'] || $header['answered'] || $header['forwarded'])) {
1718
				$css_styles[] = 'recent';
1719
				$is_recent=true;
1720
			}
1721
			if ($header['priority'] < 3) {
1722
				$css_styles[] = 'prio_high';
1723
			}
1724
			if ($header['flagged']) {
1725
				$css_styles[] = 'flagged';
1726
			}
1727
			if (!$header['seen']) {
1728
				$css_styles[] = 'unseen'; // different status image for recent // solved via css !important
1729
			}
1730
			if ($header['answered']) {
1731
				$css_styles[] = 'replied';
1732
			}
1733
			if ($header['forwarded']) {
1734
				$css_styles[] = 'forwarded';
1735
			}
1736
			if ($header['label1']) {
1737
				$css_styles[] = 'labelone';
1738
			}
1739
			if ($header['label2']) {
1740
				$css_styles[] = 'labeltwo';
1741
			}
1742
			if ($header['label3']) {
1743
				$css_styles[] = 'labelthree';
1744
			}
1745
			if ($header['label4']) {
1746
				$css_styles[] = 'labelfour';
1747
			}
1748
			if ($header['label5']) {
1749
				$css_styles[] = 'labelfive';
1750
			}
1751
1752
			//error_log(__METHOD__.array2string($css_styles));
1753
1754
			if (in_array("subject", $cols))
1755
			{
1756
				// filter out undisplayable characters
1757
				$search = array('[\016]','[\017]',
1758
					'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
1759
					'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
1760
				$replace = '';
1761
1762
				$header['subject'] = preg_replace($search,$replace,$header['subject']);
1763
				// curly brackets get messed up by the template!
1764
1765
				if (!empty($header['subject'])) {
1766
					// make the subject shorter if it is to long
1767
					$subject = $header['subject'];
1768
				} else {
1769
					$subject = '('. lang('no subject') .')';
1770
				}
1771
1772
				$data["subject"] = $subject; // the mailsubject
1773
			}
1774
1775
			$imageHTMLBlock = '';
1776
			//error_log(__METHOD__.__LINE__.array2string($header));
1777
			if (in_array("attachments", $cols))
1778
			{
1779
				if($header['mimetype'] == 'multipart/mixed' ||
1780
					$header['mimetype'] == 'multipart/signed' ||
1781
					$header['mimetype'] == 'multipart/related' ||
1782
					$header['mimetype'] == 'multipart/report' ||
1783
					$header['mimetype'] == 'text/calendar' ||
1784
					$header['mimetype'] == 'text/html' ||
1785
					substr($header['mimetype'],0,11) == 'application' ||
1786
					substr($header['mimetype'],0,5) == 'audio' ||
1787
					substr($header['mimetype'],0,5) == 'video' ||
1788
					$header['mimetype'] == 'multipart/alternative')
1789
				{
1790
					$image = Api\Html::image('mail','attach');
1791
					$imageHTMLBlock = '';
1792
					$datarowid = $this->createRowID($_folderName,$message_uid,true);
1793
					$attachments = $header['attachments'];
1794
					if (count($attachments)<1)
1795
					{
1796
						$image = '&nbsp;';
1797
					}
1798
					if (count($attachments)==1)
1799
					{
1800
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1801
						$image = Api\Html::image('mail','attach',$attachments[0]['name'].(!empty($attachments[0]['mimeType'])?' ('.$attachments[0]['mimeType'].')':''));
1802
					}
1803
					if (count($attachments)>1)
1804
					{
1805
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1806
						$image = Api\Html::image('mail','attach',lang('%1 attachments',count($attachments)));
1807
					}
1808
1809
					$attachmentFlag = $image;
1810
				} else {
1811
					$attachmentFlag ='&nbsp;';
1812
				}
1813
				// show priority flag
1814
				if ($header['priority'] < 3) {
1815
					 $image = Api\Html::image('mail','prio_high');
1816
				} elseif ($header['priority'] > 3) {
1817
					$image = Api\Html::image('mail','prio_low');
1818
				} else {
1819
					$image = '';
1820
				}
1821
				// show a flag for flagged messages
1822
				$imageflagged ='';
1823
				if ($header['flagged'])
1824
				{
1825
					$imageflagged = Api\Html::image('mail','unread_flagged_small');
1826
				}
1827
				$data["attachments"] = $image.$attachmentFlag.$imageflagged; // icon for attachments available
1828
			}
1829
1830
			// sent or draft or template folder -> to address
1831
			if (in_array("toaddress", $cols))
1832
			{
1833
				// sent or drafts or template folder means foldertype > 0, use to address instead of from
1834
				$data["toaddress"] = $header['to_address'];//Mail::htmlentities($header['to_address'],$this->charset);
1835
			}
1836
1837
			if (in_array("additionaltoaddress", $cols))
1838
			{
1839
				$data['additionaltoaddress'] = $header['additional_to_addresses'];
1840
			}
1841
			//fromaddress
1842
			if (in_array("fromaddress", $cols))
1843
			{
1844
				$data["fromaddress"] = $header['sender_address'];
1845
			}
1846
			if (in_array("ccaddress", $cols))
1847
			{
1848
				$data['ccaddress'] = $header['cc_addresses'];
1849
			}
1850
			if (in_array("date", $cols))
1851
			{
1852
				$data["date"] = $header['date'];
1853
			}
1854
			if (in_array("modified", $cols))
1855
			{
1856
				$data["modified"] = $header['internaldate'];
1857
			}
1858
1859
			if (in_array("size", $cols))
1860
				$data["size"] = $header['size']; /// size
1861
1862
			$data["class"] = implode(' ', $css_styles);
1863
			//translate style-classes back to flags
1864
			$data['flags'] = Array();
1865
			if ($header['seen']) $data["flags"]['read'] = 'read';
1866
			foreach ($css_styles as &$flag) {
1867
				if ($flag!='mail')
1868
				{
1869
					if ($flag=='labelone') {$data["flags"]['label1'] = 'label1';}
1870
					elseif ($flag=='labeltwo') {$data["flags"]['label2'] = 'label2';}
1871
					elseif ($flag=='labelthree') {$data["flags"]['label3'] = 'label3';}
1872
					elseif ($flag=='labelfour') {$data["flags"]['label4'] = 'label4';}
1873
					elseif ($flag=='labelfive') {$data["flags"]['label5'] = 'label5';}
1874
					elseif ($flag=='unseen') {unset($data["flags"]['read']);}
1875
					else $data["flags"][$flag] = $flag;
1876
				}
1877
			}
1878
			if ($header['disposition-notification-to']) $data['dispositionnotificationto'] = $header['disposition-notification-to'];
1879
			if (($header['mdnsent']||$header['mdnnotsent']|$header['seen'])&&isset($data['dispositionnotificationto'])) unset($data['dispositionnotificationto']);
1880
			$data['attachmentsBlock'] = $imageHTMLBlock;
1881
			$data['address'] = ($_folderType?$data["toaddress"]:$data["fromaddress"]);
1882
			if (in_array("bodypreview", $cols)&&$header['bodypreview'])
1883
			{
1884
				$data["bodypreview"] = $header['bodypreview'];
1885
			}
1886
			$rv[] = $data;
1887
			//error_log(__METHOD__.__LINE__.array2string($data));
1888
		}
1889
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName,__METHOD__.__LINE__);
1890
1891
		// ToDo: call this ONLY if labels change
1892
		Etemplate\Widget::setElementAttribute('toolbar', 'actions', $this->get_toolbar_actions());
1893
1894
		return $rv;
1895
	}
1896
1897
	/**
1898
	 * display messages header lines
1899
	 *
1900
	 * all params are passed as GET Parameters
1901
	 */
1902
	function displayHeader()
1903
	{
1904
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
1905
		if(isset($_GET['part'])) $partID = $_GET['part'];
1906
1907
		$hA = self::splitRowID($rowID);
1908
		$uid = $hA['msgUID'];
1909
		$mailbox = $hA['folder'];
1910
		$icServerID = $hA['profileID'];
1911
		$rememberServerID = $this->mail_bo->profileID;
1912
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1913
		{
1914
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1915
			$this->changeProfile($icServerID);
1916
		}
1917
1918
		$this->mail_bo->reopen($mailbox);
1919
		$headers_in	= $this->mail_bo->getMessageRawHeader($uid, $partID);
1920
1921
		// add line breaks to $rawheaders
1922
		$newRawHeaders = explode("\n",$headers_in);
1923
		reset($newRawHeaders);
1924
1925
		// reset $rawheaders
1926
		$rawheaders 	= "";
1927
		// create it new, with good line breaks
1928
		reset($newRawHeaders);
1929
		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...
1930
			$rawheaders .= wordwrap($value, 90, "\n     ");
1931
		}
1932
1933
		$this->mail_bo->closeConnection();
1934
		if ($rememberServerID != $this->mail_bo->profileID)
1935
		{
1936
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
1937
			$this->changeProfile($rememberServerID);
1938
		}
1939
1940
		header('Content-type: text/html; charset=iso-8859-1');
1941
		print '<pre>'. htmlspecialchars($rawheaders, ENT_NOQUOTES, 'iso-8859-1') .'</pre>';
1942
1943
	}
1944
1945
	/**
1946
	 * display messages
1947
	 * @param array $_requesteddata etemplate content
1948
	 * all params are passed as GET Parameters, but can be passed via ExecMethod2 as array too
1949
	 */
1950
	function displayMessage($_requesteddata = null)
1951
	{
1952
		if (is_null($_requesteddata)) $_requesteddata = $_GET;
1953
1954
		$preventRedirect=false;
1955
		if(isset($_requesteddata['id'])) $rowID	= $_requesteddata['id'];
1956
		if(isset($_requesteddata['part'])) $partID = $_requesteddata['part']!='null'?$_requesteddata['part']:null;
1957
		if(isset($_requesteddata['mode'])) $preventRedirect   = (($_requesteddata['mode']=='display' || $_requesteddata['mode'] == 'print')?true:false);
1958
1959
		$hA = self::splitRowID($rowID);
1960
		$uid = $hA['msgUID'];
1961
		$mailbox = $hA['folder'];
1962
		$icServerID = $hA['profileID'];
1963
		$rememberServerID = $this->mail_bo->profileID;
1964
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1965
		{
1966
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1967
			$this->changeProfile($icServerID);
1968
		}
1969
		$htmlOptions = $this->mail_bo->htmlOptions;
1970
		if (!empty($_requesteddata['tryastext'])) $htmlOptions  = "only_if_no_text";
1971
		if (!empty($_requesteddata['tryashtml'])) $htmlOptions  = "always_display";
1972
1973
		//error_log(__METHOD__.__LINE__.array2string($hA));
1974
		if (($this->mail_bo->isDraftFolder($mailbox)) && $_requesteddata['mode'] == 'print')
1975
		{
1976
			$response = Api\Json\Response::get();
1977
			$response->call('app.mail.print_for_compose', $rowID);
1978
		}
1979
		if (!$preventRedirect && ($this->mail_bo->isDraftFolder($mailbox) || $this->mail_bo->isTemplateFolder($mailbox)))
1980
		{
1981
			Egw::redirect_link('/index.php',array('menuaction'=>'mail.mail_compose.compose','id'=>$rowID,'from'=>'composefromdraft'));
1982
		}
1983
		$this->mail_bo->reopen($mailbox);
1984
		// retrieve the flags of the message, before touching it.
1985
		try
1986
		{
1987
			$headers	= $this->mail_bo->getMessageHeader($uid, $partID,true,true,$mailbox);
1988
		}
1989
		catch (Api\Exception $e)
1990
		{
1991
			$error_msg[] = lang("ERROR: Message could not be displayed.");
1992
			$error_msg[] = lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);
1993
			Framework::message($e->getMessage(), 'error');
1994
		}
1995
		if (!empty($uid)) $this->mail_bo->getFlags($uid);
1996
		$envelope	= $this->mail_bo->getMessageEnvelope($uid, $partID,true,$mailbox);
1997
		//error_log(__METHOD__.__LINE__.array2string($envelope));
1998
		$this->mail_bo->getMessageRawHeader($uid, $partID,$mailbox);
1999
		$fetchEmbeddedImages = false;
2000
		// if we are in HTML so its likely that we should show the embedded images; as a result
2001
		// we do NOT want to see those, that are embedded in the list of attachments
2002
		if ($htmlOptions !='always_display') $fetchEmbeddedImages = true;
2003
		$attachments	= $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox);
2004
		//error_log(__METHOD__.__LINE__.array2string($attachments));
2005
		$attachmentHTMLBlock = self::createAttachmentBlock($attachments, $rowID, $uid, $mailbox);
2006
2007
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2008
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2009
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2010
2011
		//error_log(__METHOD__.__LINE__.$mailBody);
2012
		$this->mail_bo->closeConnection();
2013
		//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
2014
		$etpl = new Etemplate('mail.display');
2015
		$subject = $this->mail_bo->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false);
2016
2017
		// Set up data for taglist widget(s)
2018
		if ($envelope['FROM']==$envelope['SENDER']) unset($envelope['SENDER']);
2019
		$sel_options = array();
2020
		foreach(array('SENDER','FROM','TO','CC','BCC') as $field)
2021
		{
2022
			if (!isset($envelope[$field])) continue;
2023
			foreach($envelope[$field] as $field_data)
2024
			{
2025
				//error_log(__METHOD__.__LINE__.array2string($field_data));
2026
				$content[$field][] = $field_data;
2027
				$sel_options[$field][] = array(
2028
					// taglist requires these - not optional
2029
					'id' => $field_data,
2030
					'label' => str_replace('"',"'",$field_data),
2031
				);
2032
			}
2033
		}
2034
		$actionsenabled = $this->getDisplayToolbarActions();
2035
		$content['displayToolbaractions'] = json_encode($actionsenabled);
2036
		if (empty($subject)) $subject = lang('no subject');
2037
		$content['msg'] = (is_array($error_msg)?implode("<br>",$error_msg):$error_msg);
2038
		// Send mail ID so we can use it for actions
2039
		$content['mail_id'] = $rowID;
2040
		if (!is_array($headers) || !isset($headers['DATE']))
2041
		{
2042
			$headers['DATE'] = (is_array($envelope)&&$envelope['DATE']?$envelope['DATE']:'');
2043
		}
2044
		$content['mail_displaydate'] = Mail::_strtotime($headers['DATE'],'ts',true);
2045
		$content['mail_displaysubject'] = $subject;
2046
		$linkData = array('menuaction'=>"mail.mail_ui.loadEmailBody","_messageID"=>$rowID);
2047
		if (!empty($partID)) $linkData['_partID']=$partID;
2048
		if ($htmlOptions != $this->mail_bo->htmlOptions) $linkData['_htmloptions']=$htmlOptions;
2049
		$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
2050
		$content['mail_displayattachments'] = $attachmentHTMLBlock;
2051
		$content['mail_id']=$rowID;
2052
		$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
2053
		$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
2054
2055
		// DRAG attachments actions
2056
		$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
2057
			'file_drag' => array(
2058
				'dragType' => 'file',
2059
				'type' => 'drag',
2060
				'onExecute' => 'javaScript:app.mail.drag_attachment'
2061
			)
2062
		));
2063
		$readonlys = $preserv = $content;
2064
		if ($rememberServerID != $this->mail_bo->profileID)
2065
		{
2066
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2067
			$this->changeProfile($rememberServerID);
2068
		}
2069
2070
		$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
2071
	}
2072
2073
	/**
2074
	 * Build actions for display toolbar
2075
	 */
2076
	function getDisplayToolbarActions ()
2077
	{
2078
		$actions = $this->get_toolbar_actions();
2079
		$actions['mark']['children']['flagged']=array(
2080
			'group' => $actions['mark']['children']['flagged']['group'],
2081
			'caption' => 'Flagged',
2082
			'icon' => 'unread_flagged_small',
2083
			'onExecute' => 'javaScript:app.mail.mail_flag',
2084
		);
2085
		$actions['mark']['children']['unflagged']=array(
2086
			'group' => $actions['mark']['children']['flagged']['group'],
2087
			'caption' => 'Unflagged',
2088
			'icon' => 'read_flagged_small',
2089
			'onExecute' => 'javaScript:app.mail.mail_flag',
2090
		);
2091
		$actions['tracker']['toolbarDefault'] = true;
2092
		$actions['forward']['toolbarDefault'] = true;
2093
2094
		$compose = $actions['composeasnew'];
2095
		unset($actions['composeasnew']);
2096
2097
		$actions2 = array_reverse($actions,true);
2098
		$actions2['composeasnew']= $compose;
2099
		return array_reverse($actions2,true);
2100
	}
2101
2102
	/**
2103
	 * helper function to create the attachment block/table
2104
	 *
2105
	 * @param array $attachments array with the attachments information
2106
	 * @param string $rowID rowid of the message
2107
	 * @param int $uid uid of the message
2108
	 * @param string $mailbox mailbox identifier
2109
	 * @param boolean $_returnFullHTML flag wether to return HTML or data array
2110
	 * @return array|string data array or html or empty string
2111
	 */
2112
	static function createAttachmentBlock($attachments, $rowID, $uid, $mailbox,$_returnFullHTML=false)
2113
	{
2114
		$attachmentHTMLBlock='';
2115
		$attachmentHTML = array();
2116
		if (is_array($attachments) && count($attachments) > 0) {
2117
			$url_img_vfs = Api\Html::image('filemanager','navbar', lang('Filemanager'), ' height="16"');
2118
			$url_img_vfs_save_all = Api\Html::image('mail','save_all', lang('Save all'));
2119
2120
			foreach ($attachments as $key => $value)
2121
			{
2122
				$attachmentHTML[$key]['filename']= ($value['name'] ? ( $value['filename'] ? $value['filename'] : $value['name'] ) : lang('(no subject)'));
2123
				$attachmentHTML[$key]['filename'] = Api\Translation::convert_jsonsafe($attachmentHTML[$key]['filename'],'utf-8');
2124
				//error_log(array2string($value));
2125
				//error_log(strtoupper($value['mimeType']) .'<->'. Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']));
2126
				if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']);
2127
				$attachmentHTML[$key]['type']=$value['mimeType'];
2128
				$attachmentHTML[$key]['mimetype'] = Api\MimeMagic::mime2label($value['mimeType']);
2129
				$hA = self::splitRowID($rowID);
2130
				$uid = $hA['msgUID'];
2131
				$mailbox = $hA['folder'];
2132
				$acc_id = $hA['profileID'];
2133
2134
				$attachmentHTML[$key]['mime_data'] = Link::set_data($value['mimeType'], 'EGroupware\\Api\\Mail::getAttachmentAccount', array(
2135
					$acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true
2136
				));
2137
				$attachmentHTML[$key]['size']=Vfs::hsize($value['size']);
2138
				$attachmentHTML[$key]['attachment_number']=$key;
2139
				$attachmentHTML[$key]['partID']=$value['partID'];
2140
				$attachmentHTML[$key]['mail_id'] = $rowID;
2141
				$attachmentHTML[$key]['winmailFlag']=$value['is_winmail'];
2142
				$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "mail_DisplayNone";
2143
				// reset mode array as it should be considered differently for
2144
				// each attachment
2145
				$mode = array();
2146
				switch(strtoupper($value['mimeType']))
2147
				{
2148
					case 'MESSAGE/RFC822':
2149
						$linkData = array
2150
						(
2151
							'menuaction'	=> 'mail.mail_ui.displayMessage',
2152
							'mode'		=> 'display', //message/rfc822 attachments should be opened in display mode
2153
							'id'		=> $rowID,
2154
							'part'		=> $value['partID'],
2155
							'is_winmail'    => $value['is_winmail']
2156
						);
2157
						$windowName = 'displayMessage_'. $rowID.'_'.$value['partID'];
2158
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',700,egw_getWindowOuterHeight());";
2159
						break;
2160
					case 'IMAGE/JPEG':
2161
					case 'IMAGE/PNG':
2162
					case 'IMAGE/GIF':
2163
					case 'IMAGE/BMP':
2164
						// set mode for media mimetypes because we need
2165
						// to structure a download url to be used maybe in expose.
2166
						$mode = array(
2167
							'mode' => 'save'
2168
						);
2169
					case 'APPLICATION/PDF':
2170
					case 'TEXT/PLAIN':
2171
					case 'TEXT/HTML':
2172
					case 'TEXT/DIRECTORY':
2173
						$sfxMimeType = $value['mimeType'];
2174
						$buff = explode('.',$value['name']);
2175
						$suffix = '';
2176
						if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2177
						if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2178
						if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD')
2179
						{
2180
							$attachments[$key]['mimeType'] = $sfxMimeType;
2181
							$value['mimeType'] = strtoupper($sfxMimeType);
2182
						}
2183
					case 'TEXT/X-VCARD':
2184
					case 'TEXT/VCARD':
2185
					case 'TEXT/CALENDAR':
2186
					case 'TEXT/X-VCALENDAR':
2187
						$linkData = array_merge(array
2188
						(
2189
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2190
							'id'		=> $rowID,
2191
							'part'		=> $value['partID'],
2192
							'is_winmail'    => $value['is_winmail'],
2193
							'mailbox'   => base64_encode($mailbox),
2194
						) , $mode);
2195
						$windowName = 'displayAttachment_'. $uid;
2196
						$reg = '800x600';
2197
						// handle calendar/vcard
2198
						if (strtoupper($value['mimeType'])=='TEXT/CALENDAR')
2199
						{
2200
							$windowName = 'displayEvent_'. $rowID;
2201
							$reg2 = Link::get_registry('calendar','view_popup');
2202
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2203
						}
2204
						if (strtoupper($value['mimeType'])=='TEXT/X-VCARD' || strtoupper($value['mimeType'])=='TEXT/VCARD')
2205
						{
2206
							$windowName = 'displayContact_'. $rowID;
2207
							$reg2 = Link::get_registry('addressbook','add_popup');
2208
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2209
						}
2210
						// apply to action
2211
						list($width,$height) = explode('x',(!empty($reg2) ? $reg2 : $reg));
2212
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',$width,$height);";
2213
						break;
2214
					default:
2215
						$linkData = array
2216
						(
2217
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2218
							'id'		=> $rowID,
2219
							'part'		=> $value['partID'],
2220
							'is_winmail'    => $value['is_winmail'],
2221
							'mailbox'   => base64_encode($mailbox),
2222
						);
2223
						$linkView = "window.location.href = '".Egw::link('/index.php',$linkData)."';";
2224
						break;
2225
				}
2226
				// we either use mime_data for server-side supported mime-types or mime_url for client-side or download
2227
				if (empty($attachmentHTML[$key]['mime_data']))
2228
				{
2229
					$attachmentHTML[$key]['mime_url'] = Egw::link('/index.php',$linkData);
2230
					unset($attachmentHTML[$key]['mime_data']);
2231
				}
2232
				$attachmentHTML[$key]['windowName'] = $windowName;
2233
2234
				//error_log(__METHOD__.__LINE__.$linkView);
2235
				$attachmentHTML[$key]['link_view'] = '<a href="#" ." title="'.$attachmentHTML[$key]['filename'].'" onclick="'.$linkView.' return false;"><b>'.
2236
					($value['name'] ? $value['name'] : lang('(no subject)')).
2237
					'</b></a>';
2238
2239
				$linkData = array
2240
				(
2241
					'menuaction'	=> 'mail.mail_ui.getAttachment',
2242
					'mode'		=> 'save',
2243
					'id'		=> $rowID,
2244
					'part'		=> $value['partID'],
2245
					'is_winmail'    => $value['is_winmail'],
2246
					'mailbox'   => base64_encode($mailbox),
2247
				);
2248
				$attachmentHTML[$key]['link_save'] ="<a href='".Egw::link('/index.php',$linkData)."' title='".$attachmentHTML[$key]['filename']."'>".Api\Html::image('mail','fileexport')."</a>";
2249
2250
				if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
2251
				{
2252
					$link_vfs_save = Egw::link('/index.php',array(
2253
						'menuaction' => 'filemanager.filemanager_select.select',
2254
						'mode' => 'saveas',
2255
						'name' => $value['name'],
2256
						'mime' => strtolower($value['mimeType']),
2257
						'method' => 'mail.mail_ui.vfsSaveAttachment',
2258
						'id' => $rowID.'::'.$value['partID'].'::'.$value['is_winmail'],
2259
						'label' => lang('Save'),
2260
					));
2261
					$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>";
2262
					// add save-all icon for first attachment
2263
					if (!$key && count($attachments) > 1)
2264
					{
2265
						$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "";
2266
						foreach ($attachments as $ikey => $value)
2267
						{
2268
							//$rowID
2269
							$ids["id[$ikey]"] = $rowID.'::'.$value['partID'].'::'.$value['is_winmail'].'::'.$value['name'];
2270
						}
2271
						$link_vfs_save = Egw::link('/index.php',array(
2272
							'menuaction' => 'filemanager.filemanager_select.select',
2273
							'mode' => 'select-dir',
2274
							'method' => 'mail.mail_ui.vfsSaveAttachment',
2275
							'label' => lang('Save all'),
2276
						)+$ids);
2277
						$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>";
2278
					}
2279
					$attachmentHTML[$key]['link_save'] .= $vfs_save;
2280
					//error_log(__METHOD__.__LINE__.$attachmentHTML[$key]['link_save']);
2281
				}
2282
			}
2283
			$attachmentHTMLBlock="<table width='100%'>";
2284
			foreach ((array)$attachmentHTML as $row)
2285
			{
2286
				$attachmentHTMLBlock .= "<tr><td><div class='useEllipsis'>".$row['link_view'].'</div></td>';
2287
				$attachmentHTMLBlock .= "<td>".$row['mimetype'].'</td>';
2288
				$attachmentHTMLBlock .= "<td>".$row['size'].'</td>';
2289
				$attachmentHTMLBlock .= "<td>".$row['link_save'].'</td></tr>';
2290
			}
2291
			$attachmentHTMLBlock .= "</table>";
2292
		}
2293
		if (!$_returnFullHTML)
2294
		{
2295
			foreach ((array)$attachmentHTML as $ikey => $value)
2296
			{
2297
				unset($attachmentHTML[$ikey]['link_view']);
2298
				unset($attachmentHTML[$ikey]['link_save']);
2299
			}
2300
		}
2301
		return ($_returnFullHTML?$attachmentHTMLBlock:$attachmentHTML);
2302
	}
2303
2304
	/**
2305
	 * fetch vacation info from active Server using icServer object
2306
	 *
2307
	 * @param array $cachedVacations an array of cached vacations for an user
2308
	 * @return array|boolean array with vacation on success or false on failure
2309
	 */
2310
	function gatherVacation($cachedVacations = array())
2311
	{
2312
		$isVacationEnabled = $this->mail_bo->icServer->acc_sieve_enabled && ($this->mail_bo->icServer->acc_sieve_host||$this->mail_bo->icServer->acc_imap_host);
2313
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
2314
2315
		if ($isVacationEnabled)
2316
		{
2317
			$sieveServer = $this->mail_bo->icServer;
2318
			try
2319
			{
2320
				$sieveServer->retrieveRules();
2321
				$vacation = $sieveServer->getVacation();
2322
2323
				$cachedVacations = array($sieveServer->acc_id => $vacation) + (array)$cachedVacations;
2324
				// Set vacation to the instance cache for particular account with expiration of one day
2325
				Api\Cache::setCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations, 60*60*24);
2326
			}
2327
			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...
2328
			{
2329
				$this->callWizard($ex->getMessage(), true, 'error');
2330
			}
2331
		}
2332
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Vacation retrieved:'.array2string($vacation));
2333
		return $vacation;
2334
	}
2335
2336
	/**
2337
	 * gather Info on how to display the quota info
2338
	 *
2339
	 * @param int $_usage amount of usage in Kb
2340
	 * @param int $_limit amount of limit in Kb
2341
	 * @return array  returns an array of info used for quota
2342
	 *		array(
2343
	 *			class		=> string,
2344
	 *			text		=> string,
2345
	 *			percent		=> string,
2346
	 *			freespace	=> integer
2347
	 *		)
2348
	 */
2349
	function quotaDisplay($_usage, $_limit)
2350
	{
2351
		$percent = $_limit == 0 ? 100 : round(($_usage*100)/$_limit);
2352
		$limit = Mail::show_readable_size($_limit*1024);
2353
		$usage = Mail::show_readable_size($_usage*1024);
2354
2355
		if ($_limit > 0)
2356
		{
2357
			$text = $usage .'/'.$limit;
2358
			switch ($percent)
2359
			{
2360
				case 90:
2361
					$class ='mail-index_QuotaRed';
2362
					break;
2363
				case 80:
2364
					$class ='mail-index_QuotaYellow';
2365
					break;
2366
				default:
2367
					$class ='mail-index_QuotaGreen';
2368
			}
2369
		}
2370
		else
2371
		{
2372
			$text = $usage;
2373
			$class ='mail-index_QuotaGreen';
2374
		}
2375
		return array (
2376
			'class'		=> $class,
2377
			'text'		=> lang('Quota: %1',$text),
2378
			'percent'	=> $percent,
2379
			'freespace'	=> $_limit*1024 - $_usage*1024
2380
		);
2381
	}
2382
2383
	/**
2384
	 * display image
2385
	 *
2386
	 * all params are passed as GET Parameters
2387
	 */
2388
	function displayImage()
2389
	{
2390
		$uid	= $_GET['uid'];
2391
		$cid	= base64_decode($_GET['cid']);
2392
		$partID = urldecode($_GET['partID']);
2393
		if (!empty($_GET['mailbox'])) $mailbox  = base64_decode($_GET['mailbox']);
2394
2395
		//error_log(__METHOD__.__LINE__.":$uid, $cid, $partID");
2396
		$this->mail_bo->reopen($mailbox);
2397
2398
		$attachment = $this->mail_bo->getAttachmentByCID($uid, $cid, $partID, true);	// true get contents as stream
2399
2400
		$this->mail_bo->closeConnection();
2401
2402
		$GLOBALS['egw']->session->commit_session();
2403
2404
		if ($attachment)
2405
		{
2406
			header("Content-Type: ". $attachment->getType());
2407
			header('Content-Disposition: inline; filename="'. $attachment->getDispositionParameter('filename') .'"');
2408
			//header("Expires: 0");
2409
			// the next headers are for IE and SSL
2410
			//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2411
			//header("Pragma: public");
2412
			Api\Session::cache_control(true);
2413
			echo $attachment->getContents();
2414
		}
2415
		else
2416
		{
2417
			// send a 404 Not found
2418
			header("HTTP/1.1 404 Not found");
2419
		}
2420
		exit();
2421
	}
2422
2423
	function getAttachment()
2424
	{
2425
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2426
2427
		$hA = self::splitRowID($rowID);
2428
		$uid = $hA['msgUID'];
2429
		$mailbox = $hA['folder'];
2430
		$icServerID = $hA['profileID'];
2431
		$rememberServerID = $this->mail_bo->profileID;
2432
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2433
		{
2434
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2435
			$this->changeProfile($icServerID);
2436
		}
2437
		$part		= $_GET['part'];
2438
		$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
2439
2440
		$this->mail_bo->reopen($mailbox);
2441
		$attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false);
2442
		$this->mail_bo->closeConnection();
2443
		if ($rememberServerID != $this->mail_bo->profileID)
2444
		{
2445
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2446
			$this->changeProfile($rememberServerID);
2447
		}
2448
2449
		$GLOBALS['egw']->session->commit_session();
2450
		//error_log(__METHOD__.print_r($_GET,true));
2451
		if ($_GET['mode'] != "save")
2452
		{
2453 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/DIRECTORY' || empty($attachment['type']))
2454
			{
2455
				$sfxMimeType = $attachment['type'];
2456
				$buff = explode('.',$attachment['filename']);
2457
				$suffix = '';
2458
				if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2459
				if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2460
				$attachment['type'] = $sfxMimeType;
2461
				if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD') $attachment['type'] = strtoupper($sfxMimeType);
2462
			}
2463
			//error_log(__METHOD__.print_r($attachment,true));
2464
			if (strtoupper($attachment['type']) == 'TEXT/CALENDAR' || strtoupper($attachment['type']) == 'TEXT/X-VCALENDAR')
2465
			{
2466
				//error_log(__METHOD__."about to call calendar_ical");
2467
				$calendar_ical = new calendar_ical();
2468
				$eventid = $calendar_ical->search($attachment['attachment'],-1);
2469
				//error_log(__METHOD__.array2string($eventid));
2470
				if (!$eventid) $eventid = -1;
2471
				$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
2472
				//error_log(__METHOD__.$event);
2473 View Code Duplication
				if ((int)$event > 0)
2474
				{
2475
					$vars = array(
2476
						'menuaction'      => 'calendar.calendar_uiforms.edit',
2477
						'cal_id'      => $event,
2478
					);
2479
					Egw::redirect_link('../index.php',$vars);
2480
				}
2481
				//Import failed, download content anyway
2482
			}
2483 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/X-VCARD' || strtoupper($attachment['type']) == 'TEXT/VCARD')
2484
			{
2485
				$addressbook_vcal = new addressbook_vcal();
2486
				// double \r\r\n seems to end a vcard prematurely, so we set them to \r\n
2487
				//error_log(__METHOD__.__LINE__.$attachment['attachment']);
2488
				$attachment['attachment'] = str_replace("\r\r\n", "\r\n", $attachment['attachment']);
2489
				$vcard = $addressbook_vcal->vcardtoegw($attachment['attachment'], $attachment['charset']);
2490
				if ($vcard['uid'])
2491
				{
2492
					$vcard['uid'] = trim($vcard['uid']);
2493
					//error_log(__METHOD__.__LINE__.print_r($vcard,true));
2494
					$contact = $addressbook_vcal->find_contact($vcard,false);
2495
				}
2496
				if (!$contact) $contact = null;
2497
				// 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))
2498
				if ($contact || count($vcard)>2)
2499
				{
2500
					$contact = $addressbook_vcal->addVCard($attachment['attachment'],(is_array($contact)?array_shift($contact):$contact),true,$attachment['charset']);
2501
				}
2502
				if ((int)$contact > 0)
2503
				{
2504
					$vars = array(
2505
						'menuaction'	=> 'addressbook.addressbook_ui.edit',
2506
						'contact_id'	=> $contact,
2507
					);
2508
					Egw::redirect_link('../index.php',$vars);
2509
				}
2510
				//Import failed, download content anyway
2511
			}
2512
		}
2513
		//error_log(__METHOD__.__LINE__.'->'.array2string($attachment));
2514
		$filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part));
2515
		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...
2516
		echo $attachment['attachment'];
2517
2518
		exit();
2519
	}
2520
2521
2522
	/**
2523
	 * save messages on disk or filemanager, or display it in popup
2524
	 *
2525
	 * all params are passed as GET Parameters
2526
	 */
2527
	function saveMessage()
2528
	{
2529
		$display = false;
2530
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2531
		if(isset($_GET['part'])) $partID = $_GET['part'];
2532
		if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display	= $_GET['location'];
2533
2534
		$hA = self::splitRowID($rowID);
2535
		$uid = $hA['msgUID'];
2536
		$mailbox = $hA['folder'];
2537
		$icServerID = $hA['profileID'];
2538
		$rememberServerID = $this->mail_bo->profileID;
2539
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2540
		{
2541
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2542
			$this->changeProfile($icServerID);
2543
		}
2544
2545
		$this->mail_bo->reopen($mailbox);
2546
2547
		$message = $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox);
2548
2549
		$this->mail_bo->closeConnection();
2550
		if ($rememberServerID != $this->mail_bo->profileID)
2551
		{
2552
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2553
			$this->changeProfile($rememberServerID);
2554
		}
2555
2556
		$GLOBALS['egw']->session->commit_session();
2557
		if (!$display)
2558
		{
2559
			$headers = Horde_Mime_Headers::parseHeaders($message);
2560
			$subject = str_replace('$$','__',Mail::decode_header($headers['SUBJECT']));
2561
			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...
2562
			echo $message;
2563
		}
2564
		else
2565
		{
2566
			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...
2567
			print '<pre>'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'</pre>';
2568
		}
2569
	}
2570
2571
	/**
2572
	 * Save an Message in the vfs
2573
	 *
2574
	 * @param string|array $ids use splitRowID, to separate values
2575
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2576
	 * @param boolean $close Return javascript to close the window
2577
	 * @return string|boolean javascript eg. to close the selector window if $close is true, or success/fail if $close is false
2578
	 */
2579
	function vfsSaveMessage($ids,$path, $close = true)
2580
	{
2581
		//error_log(__METHOD__.' IDs:'.array2string($ids).' SaveToPath:'.$path);
2582
2583 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2584
		{
2585
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2586
		}
2587
		Api\Translation::add_app('mail');
2588
2589
		$rememberServerID = $this->mail_bo->profileID;
2590
		foreach((array)$ids as $id)
2591
		{
2592
			$hA = self::splitRowID($id);
2593
			$uid = $hA['msgUID'];
2594
			$mailbox = $hA['folder'];
2595
			$icServerID = $hA['profileID'];
2596
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2597
			{
2598
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2599
				$this->changeProfile($icServerID);
2600
			}
2601
			$message = $this->mail_bo->getMessageRawBody($uid, $partID='', $mailbox);
2602
			$err=null;
2603
			if(Vfs::is_dir($path))
2604
			{
2605
				$headers = $this->mail_bo->getMessageHeader($uid,$partID,true,false,$mailbox);
2606
				$file = $path . '/'.preg_replace('/[\f\n\t\v\\:*#?<>\|]/',"_",$headers['SUBJECT']).'.eml';
2607
			}
2608
			else
2609
			{
2610
				$file = $path;
2611
			}
2612
			if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
2613
			{
2614
				$err .= lang('Error saving %1!',$file);
2615
				$succeeded = false;
2616
			}
2617
			else
2618
			{
2619
				$succeeded = true;
2620
			}
2621
			if ($fp) fclose($fp);
2622
			if ($succeeded)
2623
			{
2624
				unset($headers['SUBJECT']);//already in filename
2625
				$infoSection = Mail::createHeaderInfoSection($headers, 'SUPPRESS', false);
2626
				$props = array(array('name' => 'comment','val' => $infoSection));
2627
				Vfs::proppatch($file,$props);
2628
			}
2629
		}
2630
		if ($rememberServerID != $this->mail_bo->profileID)
2631
		{
2632
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2633
			$this->changeProfile($rememberServerID);
2634
		}
2635
2636
		if($close)
2637
		{
2638
			Framework::window_close(($err?$err:null));
2639
		}
2640
		else
2641
		{
2642
			return $succeeded;
2643
		}
2644
	}
2645
2646
	/**
2647
	 * Save an attachment in the vfs
2648
	 *
2649
	 * @param string|array $ids '::' delimited mailbox::uid::part-id::is_winmail::name (::name for multiple id's)
2650
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2651
	 * @return string javascript eg. to close the selector window
2652
	 */
2653
	function vfsSaveAttachment($ids,$path)
2654
	{
2655
		//error_log(__METHOD__.__LINE__.'("'.array2string($ids).'","'.$path."\")');");
2656
2657 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2658
		{
2659
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2660
		}
2661
		$err=null;
2662
		$dupe_count = array();
2663
		$rememberServerID = $this->mail_bo->profileID;
2664
2665
		/**
2666
		 * Extract all parameteres from the given id
2667
		 * @param int $id message id ('::' delimited mailbox::uid::part-id::is_winmail::name)
2668
		 *
2669
		 * @return array an array of parameters
2670
		 */
2671
		$getParams = function ($id) {
2672
			list($app,$user,$serverID,$mailbox,$uid,$part,$is_winmail,$name) = explode('::',$id,8);
2673
			$lId = implode('::',array($app,$user,$serverID,$mailbox,$uid));
2674
			$hA = mail_ui::splitRowID($lId);
2675
			return array(
2676
				'is_winmail' => $is_winmail == "null" || !$is_winmail?false:$is_winmail,
2677
				'user' => $user,
2678
				'name' => $name,
2679
				'part' => $part,
2680
				'uid' => $hA['msgUID'],
2681
				'mailbox' => $hA['folder'],
2682
				'icServer' => $hA['profileID']
2683
			);
2684
		};
2685
2686
		//Examine the first attachment to see if attachment
2687
		//is winmail.dat embedded attachments.
2688
		$isMultipleDownload=is_array($ids);
2689
		$p = $getParams((is_array($ids)?$ids[0]:$ids));
2690
		if ($p['is_winmail'])
2691
		{
2692 View Code Duplication
			if ($p['icServer'] && $p['icServer'] != $this->mail_bo->profileID)
2693
			{
2694
				$this->changeProfile($p['icServer']);
2695
			}
2696
			$this->mail_bo->reopen($p['mailbox']);
2697
			// Retrive all embedded attachments at once
2698
			// avoids to fetch heavy winmail.dat content
2699
			// for each file.
2700
			$attachments = $this->mail_bo->getTnefAttachments($p['uid'],$p['part']);
2701
		}
2702
2703
		foreach((array)$ids as $id)
2704
		{
2705
			$params = $getParams($id);
2706
			// when downloading a single file, name is not set
2707
			if (!$params['name']&&isset($_GET['name'])&&!$isMultipleDownload) $params['name'] = $_GET['name'];
2708 View Code Duplication
			if ($params['icServer'] && $params['icServer'] != $this->mail_bo->profileID)
2709
			{
2710
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2711
				$this->changeProfile($params['icServer']);
2712
			}
2713
			//error_log(__METHOD__.__LINE__.array2string($hA));
2714
			$this->mail_bo->reopen($params['mailbox']);
2715
			if ($params['is_winmail'])
2716
			{
2717
				// Try to find the right content for file id
2718
				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...
2719
				{
2720
					if ($key == $params['is_winmail']) $attachment = $val;
2721
				}
2722
			}
2723
			else
2724
			{
2725
				$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
2726
			}
2727
2728
			$file = $params['name'];
2729
			// when $isMultipleDownload the path holds no filename
2730
			while(Vfs::file_exists($path.($file && $isMultipleDownload ? '/'.$file : '')))
2731
			{
2732
				$dupe_count[$params['name']]++;
2733
				$file = pathinfo($params['name'], PATHINFO_FILENAME) .
2734
					' ('.($dupe_count[$params['name']] + 1).')' . '.' .
2735
					pathinfo($params['name'], PATHINFO_EXTENSION);
2736
			}
2737
			$params['name'] = $file;
2738
			//error_log(__METHOD__.__LINE__.array2string($attachment));
2739
			// when $isMultipleDownload the path holds no filename
2740
			if (!($fp = Vfs::fopen($file=$path.($params['name'] && $isMultipleDownload ? '/'.$params['name'] : ''),'wb')) ||
2741
				!fwrite($fp,$attachment['attachment']))
2742
			{
2743
				$err .= lang('Error saving %1!',$file);
2744
			}
2745
			if ($fp)
2746
			{
2747
				fclose($fp);
2748
			}
2749
		}
2750
		$this->mail_bo->closeConnection();
2751
		if ($rememberServerID != $this->mail_bo->profileID)
2752
		{
2753
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2754
			$this->changeProfile($rememberServerID);
2755
		}
2756
		Framework::window_close(($err?$err:null));
2757
	}
2758
2759
	/**
2760
	 * Zip all attachments and send to user
2761
	 * @param string $message_id = null
2762
	 */
2763
	function download_zip($message_id=null)
2764
	{
2765
		//error_log(__METHOD__.__LINE__.array2string($_GET));
2766
		// First, get all attachment IDs
2767
		if(isset($_GET['id'])) $message_id	= $_GET['id'];
2768
		//error_log(__METHOD__.__LINE__.$message_id);
2769
		$rememberServerID = $this->mail_bo->profileID;
2770
		if(!is_numeric($message_id))
2771
		{
2772
			$hA = self::splitRowID($message_id);
2773
			$message_id = $hA['msgUID'];
2774
			$mailbox = $hA['folder'];
2775
			$icServerID = $hA['profileID'];
2776
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2777
			{
2778
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2779
				$this->changeProfile($icServerID);
2780
			}
2781
		}
2782
		else
2783
		{
2784
			$mailbox = $this->mail_bo->sessionData['mailbox'];
2785
		}
2786
		// always fetch all, even inline (images)
2787
		$fetchEmbeddedImages = true;
2788
		$attachments = $this->mail_bo->getMessageAttachments($message_id,null, null, $fetchEmbeddedImages, true,true,$mailbox);
2789
		// put them in VFS so they can be zipped
2790
		$header = $this->mail_bo->getMessageHeader($message_id,'',true,false,$mailbox);
2791
		//get_home_dir may fetch the users startfolder if set; if not writeable, action will fail. TODO: use temp_dir
2792
		$homedir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
2793
		$temp_path = $homedir/*Vfs::get_home_dir()*/ . "/.mail_$message_id";
2794
		if(Vfs::is_dir($temp_path)) Vfs::remove ($temp_path);
2795
2796
		// Add subject to path, so it gets used as the file name, replacing ':'
2797
		// as it seems to cause an error
2798
		$path = $temp_path . '/' . ($header['SUBJECT'] ? Vfs::encodePathComponent(str_replace(':','-', $header['SUBJECT'])) : lang('mail')) .'/';
2799
		if(!Vfs::mkdir($path, 0700, true))
2800
		{
2801
			echo "Unable to open temp directory $path";
2802
			return;
2803
		}
2804
2805
		$file_list = array();
2806
		$dupe_count = array();
2807
		$this->mail_bo->reopen($mailbox);
2808
		if ($attachments[0]['is_winmail'] && $attachments[0]['is_winmail']!='null')
2809
		{
2810
			$tnefAttachments = $this->mail_bo->getTnefAttachments($message_id, $attachments[0]['partID'],true);
2811
		}
2812
		foreach($attachments as $file)
2813
		{
2814
			if ($file['is_winmail'])
2815
			{
2816
				// Try to find the right content for file id
2817
				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...
2818
				{
2819
					error_log(__METHOD__.' winmail = '.$key);
2820
					if ($key == $file['is_winmail']) $attachment = $val;
2821
				}
2822
			}
2823
			else
2824
			{
2825
				$attachment = $this->mail_bo->getAttachment($message_id,$file['partID'],$file['is_winmail'],false,true);
2826
			}
2827
			$success=true;
2828
			if (empty($file['filename'])) $file['filename'] = $file['name'];
2829
			if(in_array($path.$file['filename'], $file_list))
2830
			{
2831
				$dupe_count[$path.$file['filename']]++;
2832
				$file['filename'] = pathinfo($file['filename'], PATHINFO_FILENAME) .
2833
					' ('.($dupe_count[$path.$file['filename']] + 1).')' . '.' .
2834
					pathinfo($file['filename'], PATHINFO_EXTENSION);
2835
			}
2836
			// Strip special characters to make sure the files are visible for all OS (windows has issues)
2837
			$target_name = iconv($file['charset'] ? $file['charset'] : $GLOBALS['egw_info']['server']['system_charset'], 'ASCII//IGNORE', $file['filename']);
2838
			
2839
			if (!($fp = Vfs::fopen($path.$target_name,'wb')) ||
2840
				!(!fseek($attachment['attachment'], 0, SEEK_SET) && stream_copy_to_stream($attachment['attachment'], $fp)))
2841
			{
2842
				$success=false;
2843
				Framework::message("Unable to zip {$target_name}",'error');
2844
			}
2845
			if ($success) $file_list[] = $path.$target_name;
2846
			if ($fp) fclose($fp);
2847
		}
2848
		$this->mail_bo->closeConnection();
2849
		if ($rememberServerID != $this->mail_bo->profileID)
2850
		{
2851
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2852
			$this->changeProfile($rememberServerID);
2853
		}
2854
2855
		// Zip it up
2856
		Vfs::download_zip($file_list);
2857
2858
		// Clean up
2859
		Vfs::remove($temp_path);
2860
2861
		exit();
2862
	}
2863
2864
	function get_load_email_data($uid, $partID, $mailbox,$htmlOptions=null)
2865
	{
2866
		// seems to be needed, as if we open a mail from notification popup that is
2867
		// located in a different folder, we experience: could not parse message
2868
		$this->mail_bo->reopen($mailbox);
2869
		$this->mailbox = $mailbox;
2870
		$this->uid = $uid;
2871
		$this->partID = $partID;
2872
		$bufferHtmlOptions = $this->mail_bo->htmlOptions;
2873
		if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions;
2874
		// fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice
2875
		$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
2876
		$calendar_part = null;
2877
		$bodyParts	= $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
2878
2879
		// for meeting requests (multipart alternative with text/calendar part) let calendar render it
2880
		if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
2881
		{
2882
			$charset = $calendar_part->getContentTypeParameter('charset');
2883
			$this->mail_bo->fetchPartContents($uid, $calendar_part);
2884
			Api\Cache::setSession('calendar', 'ical', array(
2885
				'charset' => $charset ? $charset : 'utf-8',
2886
				'attachment' => $calendar_part->getContents(),
2887
				'method' => $calendar_part->getContentTypeParameter('method'),
2888
			));
2889
			$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2890
			Api\Translation::add_app('calendar');
2891
			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...
2892
				array('event'=>null,'msg'=>'','useSession'=>true)
2893
			);
2894
		}
2895
		// Compose the content of the frame
2896
		$frameHtml =
2897
			$this->get_email_header($this->mail_bo->getStyles($bodyParts)).
2898
			$this->showBody($this->getdisplayableBody($bodyParts,true,false), false);
2899
		//IE10 eats away linebreaks preceeded by a whitespace in PRE sections
2900
		$frameHtml = str_replace(" \r\n","\r\n",$frameHtml);
2901
		$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2902
2903
		return $frameHtml;
2904
	}
2905
2906
	static function get_email_header($additionalStyle='')
2907
	{
2908
		// egw_info[flags][css] already include <style> tags
2909
		$GLOBALS['egw_info']['flags']['css'] = preg_replace('|</?style[^>]*>|i', '', $additionalStyle);
2910
		$GLOBALS['egw_info']['flags']['nofooter']=true;
2911
		$GLOBALS['egw_info']['flags']['nonavbar']=true;
2912
		// do NOT include any default CSS
2913
		Framework::includeCSS('mail', 'preview', true, true);
2914
2915
		// load preview.js to activate mailto links
2916
		Framework::includeJS('/mail/js/preview.js');
2917
2918
		// send CSP and content-type header
2919
		return $GLOBALS['egw']->framework->header();
2920
	}
2921
2922
	function showBody(&$body, $print=true,$fullPageTags=true)
2923
	{
2924
		$BeginBody = '<div class="mailDisplayBody">
2925
<table width="100%" style="table-layout:fixed"><tr><td class="td_display">';
2926
2927
		$EndBody = '</td></tr></table></div>';
2928
		if ($fullPageTags) $EndBody .= "</body></html>";
2929
		if ($print)	{
2930
			print $BeginBody. $body .$EndBody;
2931
		} else {
2932
			return $BeginBody. $body .$EndBody;
2933
		}
2934
	}
2935
2936
	function &getdisplayableBody($_bodyParts,$modifyURI=true,$useTidy = true)
2937
	{
2938
		$bodyParts	= $_bodyParts;
2939
2940
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2941
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2942
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2943
2944
		$body = '';
2945
2946
		//error_log(__METHOD__.array2string($bodyParts)); //exit;
2947
		if (empty($bodyParts)) return "";
2948
		foreach((array)$bodyParts as $singleBodyPart) {
2949
			if (!isset($singleBodyPart['body'])) {
2950
				$singleBodyPart['body'] = $this->getdisplayableBody($singleBodyPart,$modifyURI,$useTidy);
2951
				$body .= $singleBodyPart['body'];
2952
				continue;
2953
			}
2954
			$bodyPartIsSet = strlen(trim($singleBodyPart['body']));
2955
			if (!$bodyPartIsSet)
2956
			{
2957
				$body .= '';
2958
				continue;
2959
			}
2960
			if(!empty($body)) {
2961
				$body .= '<hr style="border:dotted 1px silver;">';
2962
			}
2963
			//error_log($singleBodyPart['body']);
2964
			//error_log(__METHOD__.__LINE__.' CharSet:'.$singleBodyPart['charSet'].' mimeType:'.$singleBodyPart['mimeType']);
2965
			// some characterreplacements, as they fail to translate
2966
			$sar = array(
2967
				'@(\x84|\x93|\x94)@',
2968
				'@(\x96|\x97|\x1a)@',
2969
				'@(\x82|\x91|\x92)@',
2970
				'@(\x85)@',
2971
				'@(\x86)@',
2972
				'@(\x99)@',
2973
				'@(\xae)@',
2974
			);
2975
			$rar = array(
2976
				'"',
2977
				'-',
2978
				'\'',
2979
				'...',
2980
				'&',
2981
				'(TM)',
2982
				'(R)',
2983
			);
2984
2985
			if(($singleBodyPart['mimeType'] == 'text/html' || $singleBodyPart['mimeType'] == 'text/plain') &&
2986
				strtoupper($singleBodyPart['charSet']) != 'UTF-8')
2987
			{
2988
				$singleBodyPart['body'] = preg_replace($sar,$rar,$singleBodyPart['body']);
2989
			}
2990
			//error_log(__METHOD__.__LINE__.'reports:'.$singleBodyPart['charSet']);
2991
			if ($singleBodyPart['charSet']=='us-ascii')
2992
			{
2993
				$orgCharSet=$singleBodyPart['charSet'];
2994
				$singleBodyPart['charSet'] = Api\Translation::detect_encoding($singleBodyPart['body']);
2995
				error_log(__METHOD__.__LINE__.'reports:'.$orgCharSet.' but seems to be:'.$singleBodyPart['charSet']);
2996
			}
2997
			$singleBodyPart['body'] = Api\Translation::convert_jsonsafe($singleBodyPart['body'],$singleBodyPart['charSet']);
2998
			//error_log(__METHOD__.__LINE__.array2string($singleBodyPart));
2999
			if($singleBodyPart['mimeType'] == 'text/plain')
3000
			{
3001
				$newBody	= @htmlentities($singleBodyPart['body'],ENT_QUOTES, strtoupper(Mail::$displayCharset));
3002
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3003
				// if empty and charset is utf8 try sanitizing the string in question
3004
				if (empty($newBody) && strtolower($singleBodyPart['charSet'])=='utf-8') $newBody = @htmlentities(iconv('utf-8', 'utf-8', $singleBodyPart['body']),ENT_QUOTES, strtoupper(Mail::$displayCharset));
3005
				// if the conversion to htmlentities fails somehow, try without specifying the charset, which defaults to iso-
3006
				if (empty($newBody)) $newBody    = htmlentities($singleBodyPart['body'],ENT_QUOTES);
3007
3008
				// search http[s] links and make them as links available again
3009
				// to understand what's going on here, have a look at
3010
				// http://www.php.net/manual/en/function.preg-replace.php
3011
3012
				// create links for websites
3013
				if ($modifyURI) $newBody = Api\Html::activate_links($newBody);
3014
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3015
				// redirect links for websites if you use no cookies
3016
				#if (!($GLOBALS['egw_info']['server']['usecookies']))
3017
				#	$newBody = preg_replace("/href=(\"|\')((http(s?):\/\/)|(www\.))([\w,\-,\/,\?,\=,\.,&amp;,!\n,\%,@,\(,\),\*,#,:,~,\+]+)(\"|\')/ie",
3018
				#		"'href=\"$webserverURL/redirect.php?go='.@htmlentities(urlencode('http$4://$5$6'),ENT_QUOTES,\"Mail::$displayCharset\").'\"'", $newBody);
3019
3020
				// create links for email addresses
3021
				//TODO:if ($modifyURI) $this->parseEmail($newBody);
3022
				// create links for inline images
3023
				if ($modifyURI)
3024
				{
3025
					$newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain');
3026
				}
3027
3028
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3029
				// to display a mailpart of mimetype plain/text, may be better taged as preformatted
3030
				#$newBody	= nl2br($newBody);
3031
				// since we do not display the message as HTML anymore we may want to insert good linebreaking (for visibility).
3032
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3033
				// dont break lines that start with > (&gt; as the text was processed with htmlentities before)
3034
				$newBody	= "<pre>".Mail::wordwrap($newBody,90,"\n",'&gt;')."</pre>";
3035
			}
3036
			else
3037
			{
3038
				$alreadyHtmlLawed=false;
3039
				$newBody	= $singleBodyPart['body'];
3040
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3041
				#error_log(print_r($newBody,true));
3042 View Code Duplication
				if ($useTidy && extension_loaded('tidy'))
3043
				{
3044
					$tidy = new tidy();
3045
					$cleaned = $tidy->repairString($newBody, Mail::$tidy_config,'utf8');
3046
					// Found errors. Strip it all so there's some output
3047
					if($tidy->getStatus() == 2)
3048
					{
3049
						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...
3050
					}
3051
					else
3052
					{
3053
						$newBody = $cleaned;
3054
					}
3055
					if (!$preserveHTML)	// ToDo KL: $preserveHTML is NOT initialised, so always if is dead code
3056
					{
3057
						// filter only the 'body', as we only want that part, if we throw away the Api\Html
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
					}
3065
				}
3066
				else
3067
				{
3068
					// htmLawed filter only the 'body'
3069
					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...
3070
					if ($matches[2])
3071
					{
3072
						$hasOther = true;
3073
						$newBody = $matches[2];
3074
					}
3075
					$htmLawed = new Api\Html\HtmLawed();
3076
					// the next line should not be needed, but produces better results on HTML 2 Text conversion,
3077
					// as we switched off HTMLaweds tidy functionality
3078
					$newBody = str_replace(array('&amp;amp;','<DIV><BR></DIV>',"<DIV>&nbsp;</DIV>",'<div>&nbsp;</div>'),array('&amp;','<BR>','<BR>','<BR>'),$newBody);
3079
					$newBody = $htmLawed->run($newBody,Mail::$htmLawed_config);
3080
					if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3];
3081
					$alreadyHtmlLawed=true;
3082
				}
3083
				// do the cleanup, set for the use of purifier
3084
				//$newBodyBuff = $newBody;
3085
				/* if (!$alreadyHtmlLawed)*/ Mail::getCleanHTML($newBody);
3086
/*
3087
				// 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
3088
				if (strtoupper(Mail::$displayCharset) == 'UTF-8')
3089
				{
3090
					$test = @json_encode($newBody);
3091
					//error_log(__METHOD__.__LINE__.' ->'.strlen($singleBodyPart['body']).' Error:'.json_last_error().'<- BodyPart:#'.$test.'#');
3092
					if (($test=="null" || $test === false || is_null($test)) && strlen($newBody)>0)
3093
					{
3094
						$newBody = $newBodyBuff;
3095
						$tv = Mail::$htmLawed_config['tidy'];
3096
						Mail::$htmLawed_config['tidy'] = 0;
3097
						Mail::getCleanHTML($newBody);
3098
						Mail::$htmLawed_config['tidy'] = $tv;
3099
					}
3100
				}
3101
*/
3102
				// removes stuff between http and ?http
3103
				$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';    // only http:// gets removed, other protocolls are shown
3104
				$newBody = preg_replace('~'.$Protocol.'[^>]*\?'.$Protocol.'~sim','$1',$newBody); // removes stuff between http:// and ?http://
3105
				// TRANSFORM MAILTO LINKS TO EMAILADDRESS ONLY, WILL BE SUBSTITUTED BY parseEmail TO CLICKABLE LINK
3106
				$newBody = preg_replace('/(?<!"|href=|href\s=\s|href=\s|href\s=)'.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
3107
					"\\1@\\2.\\3",
3108
					$newBody);
3109
3110
				// redirect links for websites if you use no cookies
3111
				#if (!($GLOBALS['egw_info']['server']['usecookies'])) { //do it all the time, since it does mask the mailadresses in urls
3112
					//TODO:if ($modifyURI) $this->parseHREF($newBody);
3113
				#}
3114
				// create links for inline images
3115
				if ($modifyURI)
3116
				{
3117
					$newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID);
3118
				}
3119
				// email addresses / mailto links get now activated on client-side
3120
			}
3121
3122
			$body .= $newBody;
3123
		}
3124
		// create links for windows shares
3125
		// \\\\\\\\ == '\\' in real life!! :)
3126
		$body = preg_replace("/(\\\\\\\\)([\w,\\\\,-]+)/i",
3127
			"<a href=\"file:$1$2\" target=\"_blank\"><font color=\"blue\">$1$2</font></a>", $body);
3128
3129
		$body = preg_replace($nonDisplayAbleCharacters,'',$body);
3130
3131
		return $body;
3132
	}
3133
3134
	/**
3135
	 * Resolve inline images from CID to proper url
3136
	 *
3137
	 * @param string $_body message content
3138
	 * @param string $_mailbox mail folder
3139
	 * @param string $_uid uid
3140
	 * @param string $_partID part id
3141
	 * @param string $_messageType = 'html', message type is either html or plain
3142
	 * @return string message body including all CID images replaced
3143
	 */
3144
	public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html')
3145
	{
3146
		if ($_messageType === 'plain')
3147
		{
3148
			return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain');
3149
		}
3150
		else
3151
		{
3152
			foreach(array('src','url','background') as $type)
3153
			{
3154
				$_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type);
3155
			}
3156
			return $_body;
3157
		}
3158
	}
3159
3160
	/**
3161
	 * Replace CID with proper type of content understandable by browser
3162
	 *
3163
	 * @param type $_body content of message
3164
	 * @param type $_mailbox mail box
3165
	 * @param type $_uid uid
3166
	 * @param type $_partID part id
3167
	 * @param type $_type = 'src' type of inline image that needs to be resolved and replaced
3168
	 *	- types: {plain|src|url|background}
3169
	 * @return string returns body content including all CID replacements
3170
	 */
3171
	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src')
3172
	{
3173
		/**
3174
		 * Callback for preg_replace_callback function
3175
		 * returns matched CID replacement string based on given type
3176
		 * @param array $matches
3177
		 * @param string $_mailbox
3178
		 * @param string $_uid
3179
		 * @param string $_partID
3180
		 * @param string $_type
3181
		 * @return string|boolean returns the replace
3182
		*/
3183
		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type)
3184
		{
3185
			if (!$_type)	return false;
3186
			$CID = '';
3187
			// Build up matches according to selected type
3188
			switch ($_type)
3189
			{
3190
				case "plain":
3191
					$CID = $matches[1];
3192
					break;
3193
				case "src":
3194
					// as src:cid contains some kind of url, it is likely to be urlencoded
3195
					$CID = urldecode($matches[2]);
3196
					break;
3197
				case "url":
3198
					$CID = $matches[1];
3199
					break;
3200
				case "background":
3201
					$CID = $matches[2];
3202
					break;
3203
			}
3204
3205
			static $cache = array();	// some caching, if mails containing the same image multiple times
3206
3207
			if (is_array($matches) && $CID)
3208
			{
3209
				$linkData = array (
3210
					'menuaction'    => 'mail.mail_ui.displayImage',
3211
					'uid'		=> $_uid,
3212
					'mailbox'	=> base64_encode($_mailbox),
3213
					'cid'		=> base64_encode($CID),
3214
					'partID'	=> $_partID,
3215
				);
3216
				$imageURL = Egw::link('/index.php', $linkData);
3217
				// to test without data uris, comment the if close incl. it's body
3218
				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
3219
				{
3220
					if (!isset($cache[$imageURL]))
3221
					{
3222
						if ($_type !="background")
3223
						{
3224
							$bo = Mail::getInstance(false, mail_ui::$icServerID);
3225
							$attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID);
3226
3227
							// only use data uri for "smaller" images, as otherwise the first display of the mail takes to long
3228
							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...
3229
							{
3230
								$bo->fetchPartContents($_uid, $attachment);
3231
								$cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents());
3232
							}
3233
							else
3234
							{
3235
								$cache[$imageURL] = $imageURL;
3236
							}
3237
						}
3238
						else
3239
						{
3240
							$cache[$imageURL] = $imageURL;
3241
						}
3242
					}
3243
					$imageURL = $cache[$imageURL];
3244
				}
3245
3246
				// Decides the final result of replacement according to the type
3247
				switch ($_type)
3248
				{
3249
					case "plain":
3250
						return '<img src="'.$imageURL.'" />';
3251
					case "src":
3252
						return 'src="'.$imageURL.'"';
3253
					case "url":
3254
						return 'url('.$imageURL.');';
3255
					case "background":
3256
						return 'background="'.$imageURL.'"';
3257
				}
3258
			}
3259
			return false;
3260
		};
3261
3262
		// return new body content base on chosen type
3263
		switch($_type)
3264
		{
3265
			case"plain":
3266
				return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body);
3267
			case "src":
3268
				return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3269
			case "url":
3270
				return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body);
3271
			case "background":
3272
				return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3273
		}
3274
	}
3275
3276
	/**
3277
	 * importMessage
3278
	 * @param array $content = null an array of content
3279
	 */
3280
	function importMessage($content=null)
3281
	{
3282
		//error_log(__METHOD__.__LINE__.$this->mail_bo->getDraftFolder());
3283
3284
		if (!empty($content))
3285
		{
3286
			//error_log(__METHOD__.__LINE__.array2string($content));
3287
			if ($content['vfsfile'])
3288
			{
3289
				$file = $content['vfsfile'] = array(
3290
					'name' => Vfs::basename($content['vfsfile']),
3291
					'type' => Vfs::mime_content_type($content['vfsfile']),
3292
					'file' => Vfs::PREFIX.$content['vfsfile'],
3293
					'size' => filesize(Vfs::PREFIX.$content['vfsfile']),
3294
				);
3295
			}
3296
			else
3297
			{
3298
				$file = $content['uploadForImport'];
3299
			}
3300
			$destination = $content['FOLDER'][0];
3301
3302
			if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
3303
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3304
			{
3305
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3306
				$this->changeProfile($icServerID);
3307
			}
3308
			//error_log(__METHOD__.__LINE__.self::$delimiter.array2string($destination));
3309
			$importID = Mail::getRandomString();
3310
			$importFailed = false;
3311
			try
3312
			{
3313
				$messageUid = $this->importMessageToFolder($file,$destination,$importID);
3314
			    $linkData = array
3315
			    (
3316
					'id' => $this->createRowID($destination, $messageUid, true),
3317
			    );
3318
			}
3319
			catch (Api\Exception\WrongUserinput $e)
3320
			{
3321
					$importFailed=true;
3322
					$content['msg']		= $e->getMessage();
3323
			}
3324
			if (!$importFailed)
3325
			{
3326
				list($width, $height) = explode('x', Link::get_registry('mail', 'add_popup'));
3327
				if ($width > 0 && $height > 0) Api\Json\Response::get()->call('resizeTo', $width, $height);
3328
				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...
3329
				return;
3330
			}
3331
		}
3332
		if (!is_array($content)) $content = array();
3333
		if (empty($content['FOLDER'])) $content['FOLDER']=(array)$this->mail_bo->getDraftFolder();
3334
		if (!empty($content['FOLDER']))
3335
		{
3336
			$compose = new mail_compose();
3337
			$sel_options['FOLDER'] = $compose->ajax_searchFolder(0,true);
3338
		}
3339
3340
		$etpl = new Etemplate('mail.importMessage');
3341
		$etpl->setElementAttribute('uploadForImport','onFinish','app.mail.uploadForImport');
3342
		$etpl->exec('mail.mail_ui.importMessage',$content,$sel_options,array(),array(),2);
3343
	}
3344
3345
	/**
3346
	 * importMessageToFolder
3347
	 *
3348
	 * @param array $_formData Array with information of name, type, file and size
3349
	 * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if
3350
	 *					folder is modified
3351
	 * @param string $importID ID for the imported message, used by attachments to identify them unambiguously
3352
	 * @return mixed $messageUID or exception
3353
	 */
3354
	function importMessageToFolder($_formData,&$_folder,$importID='')
3355
	{
3356
		$importfailed = false;
3357
		//error_log(__METHOD__.__LINE__.array2string($_formData));
3358
		if (empty($_formData['file'])) $_formData['file'] = $_formData['tmp_name'];
3359
		// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
3360
		try
3361
		{
3362
			$tmpFileName = Mail::checkFileBasics($_formData,$importID);
3363
		}
3364
		catch (Api\Exception\WrongUserinput $e)
3365
		{
3366
			$importfailed = true;
3367
			$alert_msg .= $e->getMessage();
3368
		}
3369
		// -----------------------------------------------------------------------
3370
		if ($importfailed === false)
3371
		{
3372
			$mailObject = new Api\Mailer();
3373
			try
3374
			{
3375
				$this->mail_bo->parseFileIntoMailObject($mailObject, $tmpFileName);
3376
			}
3377
			catch (Api\Exception\AssertionFailed $e)
3378
			{
3379
				$importfailed = true;
3380
				$alert_msg .= $e->getMessage();
3381
			}
3382
			$this->mail_bo->openConnection();
3383
			if (empty($_folder))
3384
			{
3385
				$importfailed = true;
3386
				$alert_msg .= lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);
3387
			}
3388
			$delimiter = $this->mail_bo->getHierarchyDelimiter();
3389
			if($_folder=='INBOX'.$delimiter) $_folder='INBOX';
3390
			if ($importfailed === false)
3391
			{
3392
				if ($this->mail_bo->folderExists($_folder,true)) {
3393
					try
3394
					{
3395
						$messageUid = $this->mail_bo->appendMessage($_folder,
3396
							$mailObject->getRaw(),
3397
							null,'\\Seen');
3398
					}
3399
					catch (Api\Exception\WrongUserinput $e)
3400
					{
3401
						$importfailed = true;
3402
						$alert_msg .= lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$_formData['name'],$_folder,$e->getMessage());
3403
					}
3404
				}
3405
				else
3406
				{
3407
					$importfailed = true;
3408
					$alert_msg .= lang("Import of message %1 failed. Destination Folder %2 does not exist.",$_formData['name'],$_folder);
3409
				}
3410
			}
3411
		}
3412
		// set the url to open when refreshing
3413
		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...
3414
		{
3415
			throw new Api\Exception\WrongUserinput($alert_msg);
3416
		}
3417
		else
3418
		{
3419
			return $messageUid;
3420
		}
3421
	}
3422
3423
	/**
3424
	 * importMessageFromVFS2DraftAndEdit
3425
	 *
3426
	 * @param array $formData Array with information of name, type, file and size; file is required,
3427
	 *                               name, type and size may be set here to meet the requirements
3428
	 *						Example: $formData['name']	= 'a_email.eml';
3429
	 *								 $formData['type']	= 'message/rfc822';
3430
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3431
	 *								 $formData['size']	= 2136;
3432
	 * @return void
3433
	 */
3434
	function importMessageFromVFS2DraftAndEdit($formData='')
3435
	{
3436
		$this->importMessageFromVFS2DraftAndDisplay($formData,'edit');
3437
	}
3438
3439
	/**
3440
	 * importMessageFromVFS2DraftAndDisplay
3441
	 *
3442
	 * @param array $formData Array with information of name, type, file and size; file is required,
3443
	 *                               name, type and size may be set here to meet the requirements
3444
	 *						Example: $formData['name']	= 'a_email.eml';
3445
	 *								 $formData['type']	= 'message/rfc822';
3446
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3447
	 *								 $formData['size']	= 2136;
3448
	 * @param string $mode mode to open ImportedMessage display and edit are supported
3449
	 * @return void
3450
	 */
3451
	function importMessageFromVFS2DraftAndDisplay($formData='',$mode='display')
3452
	{
3453
		if (empty($formData)) if (isset($_REQUEST['formData'])) $formData = $_REQUEST['formData'];
3454
		//error_log(__METHOD__.__LINE__.':'.array2string($formData).' Mode:'.$mode.'->'.function_backtrace());
3455
		$draftFolder = $this->mail_bo->getDraftFolder(false);
3456
		$importID = Mail::getRandomString();
3457
3458
		// handling for mime-data hash
3459
		if (!empty($formData['data']))
3460
		{
3461
			$formData['file'] = 'egw-data://'.$formData['data'];
3462
		}
3463
		// name should be set to meet the requirements of checkFileBasics
3464
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['name']))
3465
		{
3466
			$buff = explode('/',$formData['file']);
3467
			if (is_array($buff)) $formData['name'] = array_pop($buff); // take the last part as name
3468
		}
3469
		// type should be set to meet the requirements of checkFileBasics
3470
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['type']))
3471
		{
3472
			$buff = explode('.',$formData['file']);
3473
			$suffix = '';
3474
			if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
3475
			if (!empty($suffix)) $formData['type'] = Api\MimeMagic::ext2mime($suffix);
3476
		}
3477
		// size should be set to meet the requirements of checkFileBasics
3478
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && !isset($formData['size']))
3479
		{
3480
			$formData['size'] = strlen($formData['file']); // set some size, to meet requirements of checkFileBasics
3481
		}
3482
		try
3483
		{
3484
			$messageUid = $this->importMessageToFolder($formData,$draftFolder,$importID);
3485
			$linkData = array
3486
			(
3487
		        'menuaction'    => ($mode=='display'?'mail.mail_ui.displayMessage':'mail.mail_compose.composeFromDraft'),
3488
				'id'		=> $this->createRowID($draftFolder,$messageUid,true),
3489
				'deleteDraftOnClose' => 1,
3490
			);
3491
			if ($mode!='display')
3492
			{
3493
				unset($linkData['deleteDraftOnClose']);
3494
				$linkData['method']	='importMessageToMergeAndSend';
3495
			}
3496
			else
3497
			{
3498
				$linkData['mode']=$mode;
3499
			}
3500
			Egw::redirect_link('/index.php',$linkData);
3501
		}
3502
		catch (Api\Exception\WrongUserinput $e)
3503
		{
3504
			Framework::window_close($e->getMessage());
3505
		}
3506
	}
3507
3508
	/**
3509
	 * loadEmailBody
3510
	 *
3511
	 * @param string _messageID UID
3512
	 *
3513
	 * @return xajax response
3514
	 */
3515
	function loadEmailBody($_messageID=null,$_partID=null,$_htmloptions=null)
3516
	{
3517
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3518
		if (!$_messageID && !empty($_GET['_messageID'])) $_messageID = $_GET['_messageID'];
3519
		if (!$_partID && !empty($_GET['_partID'])) $_partID = $_GET['_partID'];
3520
		if (!$_htmloptions && !empty($_GET['_htmloptions'])) $_htmloptions = $_GET['_htmloptions'];
3521
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageID,true).",$_partID,$_htmloptions");
3522
		if (empty($_messageID)) return "";
3523
		$uidA = self::splitRowID($_messageID);
3524
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
3525
		$messageID = $uidA['msgUID'];
3526
		$icServerID = $uidA['profileID'];
3527
		//something went wrong. there is a $_messageID but no $messageID: means $_messageID is crippeled
3528
		if (empty($messageID)) return "";
3529
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
3530
		{
3531
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3532
			$this->changeProfile($icServerID);
3533
		}
3534
3535
		$bodyResponse = $this->get_load_email_data($messageID,$_partID,$folder,$_htmloptions);
3536
		Api\Session::cache_control(true);
3537
		//error_log(array2string($bodyResponse));
3538
		echo $bodyResponse;
3539
3540
	}
3541
3542
	/**
3543
	 * ajax_setFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3544
	 * gets the counters and sets the text of a treenode if needed (unread Messages found)
3545
	 * @param array $_folder folders to refresh its unseen message counters
3546
	 * @return nothing
3547
	 */
3548
	function ajax_setFolderStatus($_folder)
3549
	{
3550
		Api\Translation::add_app('mail');
3551
		//error_log(__METHOD__.__LINE__.array2string($_folder));
3552
		if ($_folder)
3553
		{
3554
			$this->mail_bo->getHierarchyDelimiter(false);
3555
			$oA = array();
3556
			foreach ($_folder as $_folderName)
3557
			{
3558
				list($profileID,$folderName) = explode(self::$delimiter,$_folderName,2);
3559
				if (is_numeric($profileID))
3560
				{
3561
					if ($profileID != $this->mail_bo->profileID) continue; // only current connection
3562
					if ($folderName)
3563
					{
3564
						try
3565
						{
3566
							$fS = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3567
						}
3568
						catch (Exception $e)
3569
						{
3570
							if (Mail::$debug) error_log(__METHOD__,' ()'.$e->getMessage ());
3571
							continue;
3572
						}
3573
						if (in_array($fS['shortDisplayName'],Mail::$autoFolders)) $fS['shortDisplayName']=lang($fS['shortDisplayName']);
3574
						//error_log(__METHOD__.__LINE__.array2string($fS));
3575
						if ($fS['unseen'])
3576
						{
3577
							$oA[$_folderName] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3578
						}
3579
						if ($fS['unseen']==0 && $fS['shortDisplayName'])
3580
						{
3581
							$oA[$_folderName] = $fS['shortDisplayName'];
3582
						}
3583
					}
3584
				}
3585
			}
3586
			//error_log(__METHOD__.__LINE__.array2string($oA));
3587
			if ($oA)
3588
			{
3589
				$response = Api\Json\Response::get();
3590
				$response->call('app.mail.mail_setFolderStatus',$oA);
3591
			}
3592
		}
3593
	}
3594
3595
	/**
3596
	 * ajax_addFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3597
	 * @param string $_parentFolderName folder to add a folder to
3598
	 * @param string $_newName new foldername
3599
	 * @return nothing
3600
	 */
3601
	function ajax_addFolder($_parentFolderName, $_newName)
3602
	{
3603
		//error_log(__METHOD__.__LINE__.' ParentFolderName:'.array2string($_parentFolderName).' NewName/Folder:'.array2string($_newName));
3604
		$errorMessage='';
3605
		if ($_parentFolderName)
3606
		{
3607
			$created = false;
3608
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_parentFolderName);
3609
			//the conversion is handeled by horde, frontend interaction is all utf-8
3610
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3611
			list($profileID,$parentFolderName) = explode(self::$delimiter,$decodedFolderName,2);
3612
			if (is_numeric($profileID))
3613
			{
3614
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3615
				$del = $this->mail_bo->getHierarchyDelimiter(false);
3616
				//$del = $prefix = '';
3617
				//$nameSpace = $this->mail_bo->_getNameSpaces();
3618
				//error_log(__METHOD__.__LINE__.array2string($nameSpace));
3619
				// we expect something like that: data may differ!
3620
				//$nameSpace = Array(
3621
				//	[0] => Array([prefix_present] => [prefix] => [delimiter] => /[type] => personal)
3622
				//	[1] => Array([prefix_present] => 1[prefix] => Other Users/[delimiter] => /[type] => others)
3623
				//	[2] => Array([prefix_present] => 1[prefix] => Shared Folders/[delimiter] => /[type] => shared)
3624
				//)
3625
				//
3626
				/*
3627
				foreach ($nameSpace as $nSp)
3628
				{
3629
					error_log(__METHOD__.__LINE__.array2string($nSp));
3630
					// personal is assumed to be the default
3631
					if ($nSp['type']=='personal')
3632
					{
3633
						$prefix = $nSp['prefix'];
3634
						$del = $nSp['delimiter'];
3635
					}
3636
					if ($parentFolderName && $nSp['prefix_present'] && stripos($parentFolderName,$nSp['prefix'])!==false && stripos($parentFolderName,$nSp['prefix'])<=strlen($nSp['delimiter']))
3637
					{
3638
						$prefix = $nSp['prefix'];
3639
						$del = $nSp['delimiter'];
3640
						break;
3641
					}
3642
					if (empty($parentFolderName) && !$nSp['prefix_present'])
3643
					{
3644
						$del = $nSp['delimiter'];
3645
						break;
3646
					}
3647
				}
3648
3649
				if (empty($del)) $del = $this->mail_bo->getHierarchyDelimiter(false);
3650
				*/
3651
				$nA = explode($del,$_newName);
3652
3653
				//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3654
				if (!!empty($parentFolderName)) $oldFolderInfo = $this->mail_bo->getFolderStatus($parentFolderName,false);
3655
				//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3656
3657
				$this->mail_bo->reopen('INBOX');
3658
				$parentName = $parentFolderName;
3659
				// if newName has delimiter ($del) in it, we need to create the subtree
3660
				if (!empty($nA))
3661
				{
3662
					$c=0;
3663
					foreach($nA as $sTName)
3664
					{
3665
						$error=null;
3666
						if(($parentFolderName = $this->mail_bo->createFolder($parentFolderName, $sTName, $error)))
3667
						{
3668
							$c++;
3669
						}
3670
						else
3671
						{
3672
							$errorMessage .= $error;
3673
						}
3674
					}
3675
					if ($c==count($nA)) $created=true;
3676
				}
3677
				if (!empty($parentName)) $this->mail_bo->reopen($parentName);
3678
			}
3679
			//error_log(__METHOD__.__LINE__.array2string($oA));
3680
			if ($created===true)
3681
			{
3682
				$this->mail_bo->resetFolderObjectCache($profileID);
3683
				$response = Api\Json\Response::get();
3684
				if ( $oldFolderInfo['shortDisplayName'])
3685
				{
3686
					$nodeInfo = array($_parentFolderName=>$oldFolderInfo['shortDisplayName']);
3687
				}
3688
				else
3689
				{
3690
					$nodeInfo = array($profileID=>lang('INBOX'));
3691
				}
3692
				$response->call('app.mail.mail_reloadNode',$nodeInfo);
3693
			}
3694
			else
3695
			{
3696
				if ($errorMessage)
3697
				{
3698
					$response = Api\Json\Response::get();
3699
					$response->call('egw.message',$errorMessage);
3700
				}
3701
			}
3702
		}
3703
	}
3704
3705
	/**
3706
	 * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3707
	 * @param string $_folderName folder to rename and refresh
3708
	 * @param string $_newName new foldername
3709
	 * @return nothing
3710
	 */
3711
	function ajax_renameFolder($_folderName, $_newName)
3712
	{
3713
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName).' NewName:'.array2string($_newName));
3714
		if ($_folderName)
3715
		{
3716
			Api\Translation::add_app('mail');
3717
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3718
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3719
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3720
			$oA = array();
3721
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3722
			$hasChildren = false;
3723
			if (is_numeric($profileID))
3724
			{
3725
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3726
				$pA = explode($del,$folderName);
3727
				array_pop($pA);
3728
				$parentFolder = implode($del,$pA);
3729
				if (strtoupper($folderName)!= 'INBOX')
3730
				{
3731
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3732
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false);
3733
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3734
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3735
					{
3736
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3737
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3738
						$nameSpace = $this->mail_bo->_getNameSpaces();
3739
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3740
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3741
						$fragments = array();
3742
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3743
						foreach ($subFolders as $k => $folder)
3744
						{
3745
							// we do not monitor failure or success on subfolders
3746
							if ($folder == $folderName)
3747
							{
3748
								unset($subFolders[$k]);
3749
							}
3750
							else
3751
							{
3752
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3753
								$fragments[$profileID.self::$delimiter.$folder] = substr($folder,strlen($folderName));
3754
							}
3755
						}
3756
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($fragments));
3757
					}
3758
3759
					$this->mail_bo->reopen('INBOX');
3760
					$success = false;
3761
					try
3762
					{
3763 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3764
						{
3765
							$this->mail_bo->resetFolderObjectCache($profileID);
3766
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3767
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3768
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3769
							$success = true;
3770
						}
3771
					}
3772
					catch (Exception $e)
3773
					{
3774
						$newFolderName=$folderName;
3775
						$msg = $e->getMessage();
3776
					}
3777
					$this->mail_bo->reopen($newFolderName);
3778
					$fS = $this->mail_bo->getFolderStatus($newFolderName,false);
3779
					//error_log(__METHOD__.__LINE__.array2string($fS));
3780 View Code Duplication
					if ($hasChildren)
3781
					{
3782
						$subFolders = $this->mail_bo->getMailBoxesRecursive($newFolderName, $delimiter, $prefix);
3783
						foreach ($subFolders as $k => $folder)
3784
						{
3785
							// we do not monitor failure or success on subfolders
3786
							if ($folder == $folderName)
3787
							{
3788
								unset($subFolders[$k]);
3789
							}
3790
							else
3791
							{
3792
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
3793
							}
3794
						}
3795
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
3796
					}
3797
3798
					$oA[$_folderName]['id'] = $profileID.self::$delimiter.$newFolderName;
3799
					$oA[$_folderName]['olddesc'] = $oldFolderInfo['shortDisplayName'];
3800 View Code Duplication
					if ($fS['unseen'])
3801
					{
3802
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3803
3804
					}
3805
					else
3806
					{
3807
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'];
3808
					}
3809
					foreach($fragments as $oldFolderName => $fragment)
3810
					{
3811
						//error_log(__METHOD__.__LINE__.':'.$oldFolderName.'->'.$profileID.self::$delimiter.$newFolderName.$fragment);
3812
						$oA[$oldFolderName]['id'] = $profileID.self::$delimiter.$newFolderName.$fragment;
3813
						$oA[$oldFolderName]['olddesc'] = '#skip-user-interaction-message#';
3814
						$fS = $this->mail_bo->getFolderStatus($newFolderName.$fragment,false);
3815 View Code Duplication
						if ($fS['unseen'])
3816
						{
3817
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3818
3819
						}
3820
						else
3821
						{
3822
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'];
3823
						}
3824
					}
3825
				}
3826
			}
3827 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
3828
			{
3829
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
3830
				$this->mail_bo->saveSessionData();
3831
			}
3832
			//error_log(__METHOD__.__LINE__.array2string($oA));
3833
			$response = Api\Json\Response::get();
3834
			if ($oA && $success)
3835
			{
3836
				$response->call('app.mail.mail_setLeaf',$oA);
3837
			}
3838
			else
3839
			{
3840
				$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 3809. 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...
3841
			}
3842
		}
3843
	}
3844
3845
	/**
3846
	 * reload node
3847
	 *
3848
	 * @param string _folderName  folder to reload
3849
	 * @param boolean $_subscribedOnly = true
3850
	 * @return void
3851
	 */
3852
	function ajax_reloadNode($_folderName,$_subscribedOnly=true)
3853
	{
3854
		Api\Translation::add_app('mail');
3855
		$oldPrefForSubscribedOnly = !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'];
3856
3857
		// prefs are plain prefs; we discussed an approach to have user only prefs, and
3858
		// set them on rightclick action on foldertree
3859
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
3860
3861
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3862
		$this->mail_bo->getHierarchyDelimiter(false);
3863
		list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3864
		// if pref and required mode dont match -> reset the folderObject cache to ensure
3865
		// that we get what we request
3866
		if ($_subscribedOnly != $oldPrefForSubscribedOnly) $this->mail_bo->resetFolderObjectCache($profileID);
3867
		if ($profileID != $this->mail_bo->profileID) return; // only current connection
3868
		if (!empty($folderName))
3869
		{
3870
			$parentFolder=(!empty($folderName)?$folderName:'INBOX');
3871
			$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
3872
			if ($folderInfo['unseen'])
3873
			{
3874
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'].' ('.$folderInfo['unseen'].')';
3875
			}
3876
			if ($folderInfo['unseen']==0 && $folderInfo['shortDisplayName'])
3877
			{
3878
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'];
3879
			}
3880
3881
			$refreshData = array(
3882
				$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
3883
		}
3884
		else
3885
		{
3886
			$refreshData = array(
3887
				$profileID=>lang('INBOX')//string with no meaning lateron
3888
			);
3889
		}
3890
		// Send full info back in the response
3891
		$response = Api\Json\Response::get();
3892
		foreach($refreshData as $folder => &$name)
3893
		{
3894
			$name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true);
3895
		}
3896
		$response->call('app.mail.mail_reloadNode',$refreshData);
3897
3898
	}
3899
3900
	/**
3901
	 * ResolveWinmail fetches the encoded attachments
3902
	 * from winmail.dat and will response expected structure back
3903
	 * to client in order to display them.
3904
	 *
3905
	 * Note: this ajax function should only be called via
3906
	 * nm mail selection as it does not support profile change
3907
	 * and uses the current available ic_server connection.
3908
	 *
3909
	 * @param type $_rowid row id from nm
3910
	 *
3911
	 */
3912
	function ajax_resolveWinmail ($_rowid)
3913
	{
3914
		$response = Api\Json\Response::get();
3915
3916
		$idParts = self::splitRowID($_rowid);
3917
		$uid = $idParts['msgUID'];
3918
		$mbox = $idParts['folder'];
3919
3920
		$attachments = $this->mail_bo->getMessageAttachments($uid, null, null, false,true,true,$mbox);
3921
		if (is_array($attachments))
3922
		{
3923
			$attachments = $this->createAttachmentBlock($attachments, $_rowid, $uid, $mbox, false);
3924
			$response->data($attachments);
3925
		}
3926
		else
3927
		{
3928
			$response->call('egw.message', lang('Can not resolve the winmail.dat attachment!'));
3929
		}
3930
	}
3931
3932
	/**
3933
	 * move folder
3934
	 *
3935
	 * @param string _folderName  folder to vove
3936
	 * @param string _target target folder
3937
	 *
3938
	 * @return void
3939
	 */
3940
	function ajax_MoveFolder($_folderName, $_target)
3941
	{
3942
		if (Mail::$debug) error_log(__METHOD__.__LINE__."Move Folder: $_folderName to Target: $_target");
3943
		if ($_folderName)
3944
		{
3945
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3946
			$_newLocation2 = $this->mail_bo->decodeEntityFolderName($_target);
3947
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3948
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3949
			list($newProfileID,$_newLocation) = explode(self::$delimiter,$_newLocation2,2);
3950
			$hasChildren = false;
3951
			if (is_numeric($profileID))
3952
			{
3953
				if ($profileID != $this->mail_bo->profileID || $profileID != $newProfileID) return; // only current connection
3954
				$pA = explode($del,$folderName);
3955
				$namePart = array_pop($pA);
3956
				$_newName = $namePart;
3957
				$oldParentFolder = implode($del,$pA);
3958
				$parentFolder = $_newLocation;
3959
3960
				if (strtoupper($folderName)!= 'INBOX' &&
3961
					(($oldParentFolder === $parentFolder) || //$oldParentFolder == $parentFolder means move on same level
3962
					(($oldParentFolder != $parentFolder &&
3963
					strlen($parentFolder)>0 && strlen($folderName)>0 &&
3964
					strpos($parentFolder,$folderName)===false)))) // indicates that we move the older up the tree within its own branch
3965
				{
3966
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3967
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3968
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3969
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3970
					{
3971
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3972
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3973
						$nameSpace = $this->mail_bo->_getNameSpaces();
3974
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3975
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3976
3977
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3978
						foreach ($subFolders as $k => $folder)
3979
						{
3980
							// we do not monitor failure or success on subfolders
3981
							if ($folder == $folderName)
3982
							{
3983
								unset($subFolders[$k]);
3984
							}
3985
							else
3986
							{
3987
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3988
							}
3989
						}
3990
					}
3991
3992
					$this->mail_bo->reopen('INBOX');
3993
					$success = false;
3994
					try
3995
					{
3996 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3997
						{
3998
							$this->mail_bo->resetFolderObjectCache($profileID);
3999
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
4000
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
4001
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
4002
							$this->mail_bo->resetFolderObjectCache($profileID);
4003
							$success = true;
4004
						}
4005
					}
4006
					catch (Exception $e)
4007
					{
4008
						$newFolderName=$folderName;
4009
						$msg = $e->getMessage();
4010
					}
4011
					$this->mail_bo->reopen($parentFolder);
4012
					$this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4013
					//error_log(__METHOD__.__LINE__.array2string($fS));
4014 View Code Duplication
					if ($hasChildren)
4015
					{
4016
						$subFolders = $this->mail_bo->getMailBoxesRecursive($parentFolder, $delimiter, $prefix);
4017
						foreach ($subFolders as $k => $folder)
4018
						{
4019
							// we do not monitor failure or success on subfolders
4020
							if ($folder == $folderName)
4021
							{
4022
								unset($subFolders[$k]);
4023
							}
4024
							else
4025
							{
4026
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
4027
							}
4028
						}
4029
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4030
					}
4031
				}
4032
			}
4033 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4034
			{
4035
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
4036
				$this->mail_bo->saveSessionData();
4037
			}
4038
			//error_log(__METHOD__.__LINE__.array2string($oA));
4039
			$response = Api\Json\Response::get();
4040
			if ($success)
4041
			{
4042
				Api\Translation::add_app('mail');
4043
4044
				$oldFolderInfo = $this->mail_bo->getFolderStatus($oldParentFolder,false,false,false);
4045
				$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4046
				$refreshData = array(
4047
					$profileID.self::$delimiter.$oldParentFolder=>$oldFolderInfo['shortDisplayName'],
4048
					$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4049
				// if we move the folder within the same parent-branch of the tree, there is no need no refresh the upper part
4050 View Code Duplication
				if (strlen($parentFolder)>strlen($oldParentFolder) && strpos($parentFolder,$oldParentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$parentFolder]);
4051 View Code Duplication
				if (count($refreshData)>1 && strlen($oldParentFolder)>strlen($parentFolder) && strpos($oldParentFolder,$parentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$oldParentFolder]);
4052
4053
				// Send full info back in the response
4054
				foreach($refreshData as $folder => &$name)
4055
				{
4056
					$name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true);
4057
				}
4058
				$response->call('app.mail.mail_reloadNode',$refreshData);
4059
4060
			}
4061
			else
4062
			{
4063
				$response->call('egw.refresh',lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');
4064
			}
4065
		}
4066
	}
4067
4068
	/**
4069
	 * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4070
	 * @param string $_folderName folder to delete
4071
	 * @param boolean $_return = false wheter return the success value (true) or send response to client (false)
4072
	 * @return nothing
4073
	 */
4074
	function ajax_deleteFolder($_folderName, $_return = false)
4075
	{
4076
		//error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName));
4077
		$success = false;
4078
		if ($_folderName)
4079
		{
4080
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4081
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4082
			$oA = array();
4083
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4084
			$hasChildren = false;
4085
			if (is_numeric($profileID))
4086
			{
4087
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
4088
				$pA = explode($del,$folderName);
4089
				array_pop($pA);
4090
				if (strtoupper($folderName)!= 'INBOX')
4091
				{
4092
					//error_log(__METHOD__.__LINE__."$folderName,  implode($del,$pA), $_newName");
4093
					$oA = array();
4094
					$subFolders = array();
4095
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4096
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4097
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4098
					{
4099
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4100
						$ftD = array();
4101
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4102
						$nameSpace = $this->mail_bo->_getNameSpaces();
4103
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4104
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4105
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4106
						//error_log(__METHOD__.__LINE__.'->'."$folderName, $delimiter, $prefix");
4107
						foreach ($subFolders as $k => $f)
4108
						{
4109
							$ftD[substr_count($f,$delimiter)][]=$f;
4110
						}
4111
						krsort($ftD,SORT_NUMERIC);//sort per level
4112
						//we iterate per level of depth of the subtree, deepest nesting is to be deleted first, and then up the tree
4113
						foreach($ftD as $k => $lc)//collection per level
4114
						{
4115
							foreach($lc as $f)//folders contained in that level
4116
							{
4117
								try
4118
								{
4119
									//error_log(__METHOD__.__LINE__.array2string($f).'<->'.$folderName);
4120
									$this->mail_bo->deleteFolder($f);
4121
									$success = true;
4122
									if ($f==$folderName) $oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4123
								}
4124
								catch (Exception $e)
4125
								{
4126
									$msg .= ($msg?' ':'').lang("Failed to delete %1. Server responded:",$f).$e->getMessage();
4127
									$success = false;
4128
								}
4129
							}
4130
						}
4131
					}
4132
					else
4133
					{
4134
						try
4135
						{
4136
							$this->mail_bo->deleteFolder($folderName);
4137
							$success = true;
4138
							$oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4139
						}
4140
						catch (Exception $e)
4141
						{
4142
							$msg = $e->getMessage();
4143
							$success = false;
4144
						}
4145
					}
4146
				}
4147
				else
4148
				{
4149
					$msg = lang("refused to delete folder INBOX");
4150
				}
4151
			}
4152
			if ($_return) return $success;
4153
			$response = Api\Json\Response::get();
4154
			if ($success)
4155
			{
4156
				//error_log(__METHOD__.__LINE__.array2string($oA));
4157
				$response->call('app.mail.mail_removeLeaf',$oA);
4158
			}
4159
			else
4160
			{
4161
				$response->call('egw.refresh',lang('failed to delete %1 ! Reason: %2',$oldFolderInfo['shortDisplayName'],$msg),'mail');
4162
			}
4163
		}
4164
	}
4165
4166
	/**
4167
	 * empty changeProfile - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4168
	 *
4169
	 * Made static to NOT call __construct, as it would connect to old server, before going to new one
4170
	 *
4171
	 * @param int $icServerID New profile / server ID
4172
	 * @param bool $getFolders The client needs the folders for the profile
4173
	 * @return nothing
4174
	 */
4175
	public static function ajax_changeProfile($icServerID, $getFolders = true, $exec_id=null)
4176
	{
4177
		$response = Api\Json\Response::get();
4178
4179
		$previous_id = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
4180
4181
		if ($icServerID && $icServerID != $previous_id)
4182
		{
4183
			$mail_ui = new mail_ui(false);	// do NOT run constructor, as we call changeProfile anyway
4184
			try
4185
			{
4186
				$mail_ui->changeProfile($icServerID);
4187
				// if we have an eTemplate exec_id, also send changed actions
4188
				if ($exec_id && ($actions = $mail_ui->get_actions()))
4189
				{
4190
					$response->generic('assign', array(
4191
						'etemplate_exec_id' => $exec_id,
4192
						'id' => 'nm',
4193
						'key' => 'actions',
4194
						'value' => $actions,
4195
					));
4196
				}
4197
			}
4198
			catch (Exception $e) {
4199
				self::callWizard($e->getMessage(),true, 'error');
4200
			}
4201
		}
4202
		else
4203
		{
4204
			$mail_ui = new mail_ui(true);	// run constructor
4205
		}
4206
	}
4207
4208
	/**
4209
	 * ajax_refreshVacationNotice - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4210
	 *	Note: only the activeProfile VacationNotice is refreshed
4211
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4212
	 *						if other than active profile; nothing is done!
4213
	 * @return nothing
4214
	 */
4215
	public static function ajax_refreshVacationNotice($icServerID=null)
4216
	{
4217
		//Get vacation from cache if it's available
4218
		$cachedVacations = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid']);
4219
		$vacation = $cachedVacations[$icServerID];
4220
4221
		if (!$vacation)
4222
		{
4223
			try
4224
			{
4225
				// Create mail app object
4226
				$mail = new mail_ui();
4227
4228
				if (empty($icServerID)) $icServerID = $mail->Mail->profileID;
4229
				if ($icServerID != $mail->Mail->profileID) return;
4230
4231
				$vacation = $mail->gatherVacation($cachedVacations);
4232
			} catch (Exception $e) {
4233
				$vacation=false;
4234
				error_log(__METHOD__.__LINE__." ".$e->getMessage());
4235
				unset($e);
4236
			}
4237
		}
4238
4239
		if($vacation) {
4240
			if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date'))
4241
			{
4242
				$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
4243
				$refreshData['vacationnotice'] = lang('Vacation notice is active');
4244
				$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...
4245
				if ($vacation['status'] == 'by_date' && $vacation['end_date']+ 24*3600 < time())$refreshData = '';
4246
			}
4247
		}
4248
		if ($vacation==false)
4249
		{
4250
			$refreshData['vacationnotice'] =  '';
4251
			$refreshData['vacationrange'] =  '';
4252
		}
4253
		$response = Api\Json\Response::get();
4254
		$response->call('app.mail.mail_refreshVacationNotice',$refreshData);
4255
	}
4256
4257
	/**
4258
	 * ajax_refreshFilters - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4259
	 *	Note: only the activeProfile Filters are refreshed
4260
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4261
	 *						if other than active profile; nothing is done!
4262
	 * @return nothing
4263
	 */
4264
	function ajax_refreshFilters($icServerID=null)
4265
	{
4266
		//error_log(__METHOD__.__LINE__.array2string($icServerId));
4267
		if (empty($icServerID)) $icServerID = $this->mail_bo->profileID;
4268
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4269
		{
4270
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4271
			if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4272
		}
4273 View Code Duplication
		if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
4274
		{
4275
			unset($this->searchTypes['quick']);
4276
			unset($this->searchTypes['quickwithcc']);
4277
		}
4278 View Code Duplication
		if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
4279
		{
4280
			$this->statusTypes = array_merge($this->statusTypes,array(
4281
				'keyword1'	=> 'important',//lang('important'),
4282
				'keyword2'	=> 'job',	//lang('job'),
4283
				'keyword3'	=> 'personal',//lang('personal'),
4284
				'keyword4'	=> 'to do',	//lang('to do'),
4285
				'keyword5'	=> 'later',	//lang('later'),
4286
			));
4287
		}
4288
		else
4289
		{
4290
			$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
4291
			foreach($keywords as &$k)
4292
			{
4293
				if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
4294
			}
4295
		}
4296
4297
		$response = Api\Json\Response::get();
4298
		$response->call('app.mail.mail_refreshCatIdOptions',$this->searchTypes);
4299
		$response->call('app.mail.mail_refreshFilterOptions',$this->statusTypes);
4300
		$response->call('app.mail.mail_refreshFilter2Options',array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list')));
4301
4302
	}
4303
4304
	/**
4305
	 * This function asks quota from IMAP server and makes the
4306
	 * result as JSON response to send it to mail_sendQuotaDisplay
4307
	 * function in client side.
4308
	 *
4309
	 * @param string $icServerID = null
4310
	 *
4311
	 */
4312
	function ajax_refreshQuotaDisplay($icServerID=null)
4313
	{
4314
		Api\Translation::add_app('mail');
4315
		if (is_null($icServerID)) $icServerID = $this->mail_bo->profileID;
4316
		$rememberServerID = $this->mail_bo->profileID;
4317
		try
4318
		{
4319
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4320
			{
4321
				$this->changeProfile($icServerID);
4322
			}
4323
			$quota = $this->mail_bo->getQuotaRoot();
4324
		} catch (Exception $e) {
4325
			$quota['limit'] = 'NOT SET';
4326
			error_log(__METHOD__.__LINE__." ".$e->getMessage());
4327
			unset($e);
4328
		}
4329
4330
		if($quota !== false && $quota['limit'] != 'NOT SET') {
4331
			$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
4332
			$quotaMin = $quotainfo['freespace']/pow(1024, 2);
4333
			$content = array (
4334
				'quota'				=> $quotainfo['text'],
4335
				'quotainpercent'	=> (string)$quotainfo['percent'],
4336
				'quotaclass'		=> $quotainfo['class'],
4337
				'quotanotsupported'	=> "",
4338
				'profileid'			=> $icServerID,
4339
				'quotawarning'		=> $quotaMin < 30 ? true : false
4340
			);
4341
		}
4342
		else
4343
		{
4344
			$content = array (
4345
				'quota'				=> lang("Quota not provided by server"),
4346
				'quotaclass'		=> "mail_DisplayNone",
4347
				'quotanotsupported'	=> "mail_DisplayNone"
4348
			);
4349
		}
4350
		if ($rememberServerID != $this->mail_bo->profileID)
4351
		{
4352
			try
4353
			{
4354
				$this->changeProfile($rememberServerID);
4355
			} catch (Exception $e) {
4356
				unset($e);
4357
			}
4358
		}
4359
		$response = Api\Json\Response::get();
4360
		$response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content));
4361
	}
4362
4363
	/**
4364
	 * Empty spam/junk folder
4365
	 *
4366
	 * @param string $icServerID id of the server to empty its junkFolder
4367
	 * @param string $selectedFolder seleted(active) folder by nm filter
4368
	 * @return nothing
4369
	 */
4370 View Code Duplication
	function ajax_emptySpam($icServerID, $selectedFolder)
4371
	{
4372
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4373
		Api\Translation::add_app('mail');
4374
		$response = Api\Json\Response::get();
4375
		$rememberServerID = $this->mail_bo->profileID;
4376
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4377
		{
4378
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4379
			$this->changeProfile($icServerID);
4380
		}
4381
		$junkFolder = $this->mail_bo->getJunkFolder();
4382
		if(!empty($junkFolder)) {
4383
			if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4384
			{
4385
				// Lock the tree if the active folder is junk folder
4386
				$response->call('app.mail.lock_tree');
4387
			}
4388
			$this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately');
4389
4390
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4391
			$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...
4392
			$fStatus = array(
4393
				$icServerID.self::$delimiter.$junkFolder => lang($fShortName)
4394
			);
4395
			//Call to reset folder status counter, after junkFolder triggered not from Junk folder
4396
			//-as we don't have junk folder specific information available on client-side we need to deal with it on server
4397
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4398
		}
4399
		if ($rememberServerID != $this->mail_bo->profileID)
4400
		{
4401
			$oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false);
4402
			$response->call('egw.message',lang('empty junk'));
4403
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName']));
4404
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4405
			$this->changeProfile($rememberServerID);
4406
		}
4407
		else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4408
		{
4409
			$response->call('egw.refresh',lang('empty junk'),'mail');
4410
		}
4411
	}
4412
4413
	/**
4414
	 * Empty trash folder
4415
	 *
4416
	 * @param string $icServerID id of the server to empty its trashFolder
4417
	 * @param string $selectedFolder seleted(active) folder by nm filter
4418
	 * @return nothing
4419
	 */
4420 View Code Duplication
	function ajax_emptyTrash($icServerID, $selectedFolder)
4421
	{
4422
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4423
		Api\Translation::add_app('mail');
4424
		$response = Api\Json\Response::get();
4425
		$rememberServerID = $this->mail_bo->profileID;
4426
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4427
		{
4428
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4429
			$this->changeProfile($icServerID);
4430
		}
4431
		$trashFolder = $this->mail_bo->getTrashFolder();
4432
		if(!empty($trashFolder)) {
4433
			if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4434
			{
4435
				// Lock the tree if the active folder is Trash folder
4436
				$response->call('app.mail.lock_tree');
4437
			}
4438
			$this->mail_bo->compressFolder($trashFolder);
4439
4440
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4441
			$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...
4442
			$fStatus = array(
4443
				$icServerID.self::$delimiter.$trashFolder => lang($fShortName)
4444
			);
4445
			//Call to reset folder status counter, after emptyTrash triggered not from Trash folder
4446
			//-as we don't have trash folder specific information available on client-side we need to deal with it on server
4447
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4448
		}
4449
		if ($rememberServerID != $this->mail_bo->profileID)
4450
		{
4451
			$oldFolderInfo = $this->mail_bo->getFolderStatus($trashFolder,false,false,false);
4452
			$response->call('egw.message',lang('empty trash'));
4453
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$trashFolder=>$oldFolderInfo['shortDisplayName']));
4454
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4455
			$this->changeProfile($rememberServerID);
4456
		}
4457
		else if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4458
		{
4459
			$response->call('egw.refresh',lang('empty trash'),'mail');
4460
		}
4461
	}
4462
4463
	/**
4464
	 * compress folder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4465
	 * fetches the current folder from session and compresses it
4466
	 * @param string $_folderName id of the folder to compress
4467
	 * @return nothing
4468
	 */
4469
	function ajax_compressFolder($_folderName)
4470
	{
4471
		//error_log(__METHOD__.__LINE__.' '.$_folderName);
4472
		Api\Translation::add_app('mail');
4473
4474
		$this->mail_bo->restoreSessionData();
4475
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4476
		list($icServerID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4477
4478
		if (empty($folderName)) $folderName = $this->mail_bo->sessionData['mailbox'];
4479
		if ($this->mail_bo->folderExists($folderName))
4480
		{
4481
			$rememberServerID = $this->mail_bo->profileID;
4482
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4483
			{
4484
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4485
				$this->changeProfile($icServerID);
4486
			}
4487
			if(!empty($_folderName)) {
4488
				$this->mail_bo->compressFolder($folderName);
4489
			}
4490
			if ($rememberServerID != $this->mail_bo->profileID)
4491
			{
4492
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4493
				$this->changeProfile($rememberServerID);
4494
			}
4495
			$response = Api\Json\Response::get();
4496
			$response->call('egw.refresh',lang('compress folder').': '.$folderName,'mail');
4497
		}
4498
	}
4499
4500
	/**
4501
	 * sendMDN, ...
4502
	 *
4503
	 * @param array _messageList list of UID's
4504
	 *
4505
	 * @return nothing
4506
	 */
4507
	function ajax_sendMDN($_messageList)
4508
	{
4509
		if(Mail::$debug) error_log(__METHOD__."->".array2string($_messageList));
4510
		$uidA = self::splitRowID($_messageList['msg'][0]);
4511
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4512
		$this->mail_bo->sendMDN($uidA['msgUID'],$folder);
4513
	}
4514
4515
	/**
4516
	 * flag messages as read, unread, flagged, ...
4517
	 *
4518
	 * @param string _flag name of the flag
4519
	 * @param array _messageList list of UID's
4520
	 * @param bool _sendJsonResponse tell fuction to send the JsonResponse
4521
	 *
4522
	 * @return xajax response
4523
	 */
4524
	function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
4525
	{
4526
		if(Mail::$debug) error_log(__METHOD__."->".$_flag.':'.array2string($_messageList));
4527
		Api\Translation::add_app('mail');
4528
		$alreadyFlagged=false;
4529
		$flag2check='';
4530
		$filter2toggle = $query = array();
4531
		if ($_messageList=='all' || !empty($_messageList['msg']))
4532
		{
4533
			if (isset($_messageList['all']) && $_messageList['all'])
4534
			{
4535
				// we have both messageIds AND allFlag folder information
4536
				$uidA = self::splitRowID($_messageList['msg'][0]);
4537
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4538
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4539
				{
4540
					$query = $_messageList['activeFilters'];
4541
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4542
					{
4543
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4544
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4545
						{
4546
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4547
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4548
						}
4549
						//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
4550
						$cutoffdate = $cutoffdate2 = null;
4551
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4552
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4553
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4554
						$filter = array(
4555
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4556
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4557
							'string' => $query['search'],
4558
							'status' => 'any',//this is a status change. status will be manipulated later on
4559
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4560
						);
4561
						if ($query['enddate']||$query['startdate']) {
4562
							$filter['range'] = "BETWEEN";
4563
							if ($cutoffdate) {
4564
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4565
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4566
							}
4567
							if ($cutoffdate2) {
4568
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4569
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4570
							}
4571
						}
4572
						$filter2toggle = $filter;
4573
					}
4574
					else
4575
					{
4576
						$filter = $filter2toggle = array();
4577
					}
4578
					// flags read,flagged,label1,label2,label3,label4,label5 can be toggled: handle this when all mails in a folder
4579
					// should be affected serverside. here.
4580
					$messageList = $messageListForToggle = array();
4581
					$flag2check = ($_flag=='read'?'seen':$_flag);
4582
					if (in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4583
						!($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))
4584
					{
4585
						$filter2toggle['status'] = array('un'.$_flag);
4586 View Code Duplication
						if ($query['filter'] && $query['filter']!='any')
4587
						{
4588
							$filter2toggle['status'][] = $query['filter'];
4589
						}
4590
						$rByUid = true;
4591
						$reverse = 1;
4592
						$_sRt = $this->mail_bo->getSortedList(
4593
							$folder,
4594
							$sort=0,
4595
							$reverse,
4596
							$filter2toggle,
4597
							$rByUid,
4598
							false
4599
						);
4600
						$messageListForToggle = $_sRt['match']->ids;
4601
						$filter['status'] = array($_flag);
4602 View Code Duplication
						if ($query['filter'] && $query['filter'] !='any')
4603
						{
4604
							$filter['status'][] = $query['filter'];
4605
						}
4606
						$rByUid=true;
4607
						$reverse = 1;
4608
						$_sR = $this->mail_bo->getSortedList(
4609
							$folder,
4610
							$sort=0,
4611
							$reverse,
4612
							$filter,
4613
							$rByUid,
4614
							false
4615
						);
4616
						$messageList = $_sR['match']->ids;
4617 View Code Duplication
						if (count($messageListForToggle)>0)
4618
						{
4619
							$flag2set = (strtolower($_flag));
4620
							if(Mail::$debug) error_log(__METHOD__.__LINE__." toggle un$_flag -> $flag2set ".array2string($filter2toggle).array2string($messageListForToggle));
4621
							$this->mail_bo->flagMessages($flag2set, $messageListForToggle,$folder);
4622
						}
4623 View Code Duplication
						if (count($messageList)>0)
4624
						{
4625
							$flag2set = 'un'.$_flag;
4626
							if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag -> $flag2set ".array2string($filter).array2string($messageList));
4627
							$this->mail_bo->flagMessages($flag2set, $messageList,$folder);
4628
						}
4629
						$alreadyFlagged=true;
4630
					}
4631
					elseif (!empty($filter) &&
4632
						(!in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) ||
4633
						(in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4634
						($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))))
4635
					{
4636
						if ($query['filter'] && $query['filter'] !='any')
4637
						{
4638
							$filter['status'] = $query['filter'];
4639
							// since we toggle and we toggle by the filtered flag we must must change _flag
4640
							$_flag = ($query['filter']=='unseen' && $_flag=='read' ? 'read' : ($query['filter']=='seen'&& $_flag=='read'?'unread':($_flag==$query['filter']?'un'.$_flag:$_flag)));
4641
						}
4642
						if(Mail::$debug) error_log(__METHOD__.__LINE__." flag all with $_flag on filter used:".array2string($filter));
4643
						$rByUid = true;
4644
						$reverse = 1;
4645
						$_sR = $this->mail_bo->getSortedList(
4646
							$folder,
4647
							$sort=0,
4648
							$reverse,
4649
							$filter,
4650
							$rByUid,
4651
							false
4652
						);
4653
						$messageList = $_sR['match']->ids;
4654
						unset($_messageList['all']);
4655
						$_messageList['msg'] = array();
4656
					}
4657
					else
4658
					{
4659
						if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag all ".array2string($filter));
4660
						$alreadyFlagged=true;
4661
						$uidA = self::splitRowID($_messageList['msg'][0]);
4662
						$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4663
						$this->mail_bo->flagMessages($_flag, 'all', $folder);
4664
					}
4665
				}
4666
			}
4667
			else
4668
			{
4669
				$uidA = self::splitRowID($_messageList['msg'][0]);
4670
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4671
			}
4672
			if (!$alreadyFlagged)
4673
			{
4674 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4675
				{
4676
					$hA = self::splitRowID($rowID);
4677
					$messageList[] = $hA['msgUID'];
4678
				}
4679 View Code Duplication
				if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag in $folder:".array2string(((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList)));
4680
				$this->mail_bo->flagMessages($_flag, ((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList),$folder);
4681
			}
4682
		}
4683
		else
4684
		{
4685
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4686
		}
4687
4688
		if ($_sendJsonResponse)
4689
		{
4690
			$flag=array(
4691
				'label1'	=> 'important',//lang('important'),
4692
				'label2'	=> 'job',	//lang('job'),
4693
				'label3'	=> 'personal',//lang('personal'),
4694
				'label4'	=> 'to do',	//lang('to do'),
4695
				'label5'	=> 'later',	//lang('later'),
4696
			);
4697
			$response = Api\Json\Response::get();
4698
			if (isset($_messageList['msg']) && $_messageList['popup'])
4699
			{
4700
				$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');
4701
			}
4702
			else if ((isset($_messageList['all']) && $_messageList['all']) || ($query['filter'] && ($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false)))
4703
			{
4704
				$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');
4705
			}
4706
			else
4707
			{
4708
				$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));
4709
			}
4710
		}
4711
	}
4712
4713
	/**
4714
	 * delete messages
4715
	 *
4716
	 * @param array _messageList list of UID's
4717
	 * @param string _forceDeleteMethod - method of deletion to be enforced
4718
	 * @return xajax response
4719
	 */
4720
	function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
4721
	{
4722
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageList,true).' Method:'.$_forceDeleteMethod);
4723
		$error = null;
4724
		$filtered =  false;
4725
		if ($_messageList=='all' || !empty($_messageList['msg']))
4726
		{
4727
			if (isset($_messageList['all']) && $_messageList['all'])
4728
			{
4729
				// we have both messageIds AND allFlag folder information
4730
				$uidA = self::splitRowID($_messageList['msg'][0]);
4731
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4732
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4733
				{
4734
					$query = $_messageList['activeFilters'];
4735 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4736
					{
4737
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4738
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4739
						{
4740
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4741
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4742
						}
4743
						$filtered =  true;
4744
						$cutoffdate = $cutoffdate2 = null;
4745
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4746
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4747
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4748
						$filter = array(
4749
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4750
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4751
							'string' => $query['search'],
4752
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4753
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4754
						);
4755
						if ($query['enddate']||$query['startdate']) {
4756
							$filter['range'] = "BETWEEN";
4757
							if ($cutoffdate) {
4758
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4759
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4760
							}
4761
							if ($cutoffdate2) {
4762
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4763
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4764
							}
4765
						}
4766
					}
4767
					else
4768
					{
4769
						$filter = array();
4770
					}
4771
					//error_log(__METHOD__.__LINE__."->".print_r($filter,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4772
					$reverse = 1;
4773
					$rByUid = true;
4774
					$_sR = $this->mail_bo->getSortedList(
4775
						$folder,
4776
						$sort=0,
4777
						$reverse,
4778
						$filter,
4779
						$rByUid,
4780
						false
4781
					);
4782
					$messageList = $_sR['match']->ids;
4783
				}
4784
				else
4785
				{
4786
					$messageList='all';
4787
				}
4788
				try
4789
				{
4790
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4791
					$this->mail_bo->deleteMessages(($messageList=='all' ? 'all':$messageList),$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4792
				}
4793
				catch (Api\Exception $e)
4794
				{
4795
					$error = str_replace('"',"'",$e->getMessage());
4796
				}
4797
			}
4798
			else
4799
			{
4800
				$uidA = self::splitRowID($_messageList['msg'][0]);
4801
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4802 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4803
				{
4804
					$hA = self::splitRowID($rowID);
4805
					$messageList[] = $hA['msgUID'];
4806
				}
4807
				try
4808
				{
4809
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4810
					$this->mail_bo->deleteMessages($messageList,$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4811
				}
4812
				catch (Api\Exception $e)
4813
				{
4814
					$error = str_replace('"',"'",$e->getMessage());
4815
				}
4816
			}
4817
			$response = Api\Json\Response::get();
4818
			if (empty($error))
4819
			{
4820
				$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']));
4821
			}
4822
			else
4823
			{
4824
				$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));
4825
				$response->call('app.mail.mail_retryForcedDelete',array('response'=>$error,'messageList'=>$_messageList));
4826
			}
4827
		}
4828
		else
4829
		{
4830
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4831
		}
4832
	}
4833
4834
	/**
4835
	 * copy messages
4836
	 *
4837
	 * @param array _folderName target folder
4838
	 * @param array _messageList list of UID's
4839
	 * @param string _copyOrMove method to use copy or move allowed
4840
	 * @param string _move2ArchiveMarker marker to indicate if a move 2 archive was triggered
4841
	 *
4842
	 * @return xajax response
4843
	 */
4844
	function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_move2ArchiveMarker='_')
4845
	{
4846
		if(Mail::$debug) error_log(__METHOD__."->".$_folderName.':'.print_r($_messageList,true).' Method:'.$_copyOrMove.' ArchiveMarker:'.$_move2ArchiveMarker);
4847
		Api\Translation::add_app('mail');
4848
		$folderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4849
		// only copy or move are supported as method
4850
		if (!($_copyOrMove=='copy' || $_copyOrMove=='move')) $_copyOrMove='copy';
4851
		list($targetProfileID,$targetFolder) = explode(self::$delimiter,$folderName,2);
4852
		// check if move2archive was called with the correct archiveFolder
4853
		$archiveFolder = $this->mail_bo->getArchiveFolder();
4854
		if ($_move2ArchiveMarker=='2' && $targetFolder != $archiveFolder)
4855
		{
4856
			error_log(__METHOD__.__LINE__."#Move to Archive called with:"."$targetProfileID,$targetFolder");
4857
			$targetProfileID = $this->mail_bo->profileID;
4858
			$targetFolder = $archiveFolder;
4859
			error_log(__METHOD__.__LINE__."#Fixed ArchiveFolder:"."$targetProfileID,$targetFolder");
4860
		}
4861
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
4862
		$changeFolderActions = false;
4863
		//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder");
4864
		//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
4865
		if (!isset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]))
4866
		{
4867
			//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]));
4868
			if ($lastFoldersUsedForMoveCont[$targetProfileID] && count($lastFoldersUsedForMoveCont[$targetProfileID])>3)
4869
			{
4870
				$keys = array_keys($lastFoldersUsedForMoveCont[$targetProfileID]);
4871
				foreach( $keys as &$f)
4872
				{
4873
					if (count($lastFoldersUsedForMoveCont[$targetProfileID])>9) unset($lastFoldersUsedForMoveCont[$targetProfileID][$f]);
4874
					else break;
4875
				}
4876
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID]));
4877
			}
4878
			//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder = $_folderName");
4879
			$lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]=$folderName;
4880
			$changeFolderActions = true;
4881
		}
4882
		$filtered = false;
4883
		if ($_messageList=='all' || !empty($_messageList['msg']))
4884
		{
4885
			$error=false;
4886
			if (isset($_messageList['all']) && $_messageList['all'])
4887
			{
4888
				// we have both messageIds AND allFlag folder information
4889
				$uidA = self::splitRowID($_messageList['msg'][0]);
4890
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4891
				$sourceProfileID = $uidA['profileID'];
4892
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4893
				{
4894
					$query = $_messageList['activeFilters'];
4895 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4896
					{
4897
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4898
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4899
						{
4900
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4901
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4902
						}
4903
						$filtered = true;
4904
						$cutoffdate = $cutoffdate2 = null;
4905
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4906
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4907
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4908
						$filter = array(
4909
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4910
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4911
							'string' => $query['search'],
4912
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4913
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4914
						);
4915
						if ($query['enddate']||$query['startdate']) {
4916
							$filter['range'] = "BETWEEN";
4917
							if ($cutoffdate) {
4918
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4919
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4920
							}
4921
							if ($cutoffdate2) {
4922
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4923
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4924
							}
4925
						}
4926
					}
4927
					else
4928
					{
4929
						$filter = array();
4930
					}
4931
					$reverse = 1;
4932
					$rByUid = true;
4933
					$_sR = $this->mail_bo->getSortedList(
4934
						$folder,
4935
						$sort=0,
4936
						$reverse,
4937
						$filter,
4938
						$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...
4939
						false
4940
					);
4941
					$messageList = $_sR['match']->ids;
4942
					foreach($messageList as $uID)
4943
					{
4944
						//error_log(__METHOD__.__LINE__.$uID);
4945
						if ($_copyOrMove=='move')
4946
						{
4947
							$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false);
4948
						}
4949
					}
4950
				}
4951
				else
4952
				{
4953
					$messageList='all';
4954
				}
4955
				try
4956
				{
4957
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4958
					$this->mail_bo->moveMessages($targetFolder,$messageList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4959
				}
4960
				catch (Api\Exception $e)
4961
				{
4962
					$error = str_replace('"',"'",$e->getMessage());
4963
				}
4964
			}
4965
			else
4966
			{
4967
				$messageList = array();
4968
				while(count($_messageList['msg']) > 0)
4969
				{
4970
					$uidA = self::splitRowID($_messageList['msg'][0]);
4971
					$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4972
					$sourceProfileID = $uidA['profileID'];
4973
					$moveList = array();
4974
					foreach($_messageList['msg'] as $rowID)
4975
					{
4976
						$hA = self::splitRowID($rowID);
4977
4978
						// If folder changes, stop and move what we've got
4979
						if($hA['folder'] != $folder) break;
4980
4981
						array_shift($_messageList['msg']);
4982
						$messageList[] = $hA['msgUID'];
4983
						$moveList[] = $hA['msgUID'];
4984
						if ($_copyOrMove=='move')
4985
						{
4986
							$helpvar = explode(self::$delimiter,$rowID);
4987
							array_shift($helpvar);
4988
							$messageListForRefresh[]= implode(self::$delimiter,$helpvar);
4989
						}
4990
					}
4991
					try
4992
					{
4993
						//error_log(__METHOD__.__LINE__."->".print_r($moveList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4994
						$this->mail_bo->moveMessages($targetFolder,$moveList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4995
					}
4996
					catch (Api\Exception $e)
4997
					{
4998
						$error = str_replace('"',"'",$e->getMessage());
4999
					}
5000
				}
5001
			}
5002
5003
			$response = Api\Json\Response::get();
5004
			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...
5005
			{
5006
				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...
5007
				{
5008
					unset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]);
5009
					$changeFolderActions = true;
5010
				}
5011
				$response->call('egw.message',$error,"error");
5012
			}
5013
			else
5014
			{
5015
				if ($_copyOrMove=='copy')
5016
				{
5017
					$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));
5018
				}
5019
				else
5020
				{
5021
					$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');
5022
				}
5023
			}
5024
			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...
5025
			{
5026
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5027
				Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
5028
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::get_actions());
5029
				$response->call('app.mail.mail_rebuildActionsOnList',$actionsnew);
5030
			}
5031
		}
5032
		else
5033
		{
5034
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5035
		}
5036
	}
5037
5038
	/**
5039
	 * Autoloading function to load branches of tree node
5040
	 * of management folder tree
5041
	 *
5042
	 * @param type $_id
5043
	 */
5044
	function ajax_folderMgmtTree_autoloading ($_id = null)
5045
	{
5046
		$mail_ui = new mail_ui();
5047
		$id = $_id? $_id : $_GET['id'];
5048
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,true,false,false,false));
5049
	}
5050
5051
	/**
5052
	 * Main function to handle folder management dialog
5053
	 *
5054
	 * @param array $content content of dialog
5055
	 */
5056
	function folderManagement (array $content = null)
5057
	{
5058
		$dtmpl = new Etemplate('mail.folder_management');
5059
		$profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
5060
		$sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false);
5061
5062
		if (!is_array($content))
5063
		{
5064
			$content = array ('acc_id' => $profileID);
5065
		}
5066
5067
		$readonlys = array();
5068
		// Preserv
5069
		$preserv = array(
5070
			'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side
5071
		);
5072
		$dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2);
5073
	}
5074
5075
	/**
5076
	 * Function to delete folder for management longTask dialog
5077
	 * it sends successfully deleted folder as response to be
5078
	 * used in long task response handler.
5079
	 *
5080
	 * @param type $_folderName
5081
	 */
5082
	function ajax_folderMgmt_delete ($_folderName)
5083
	{
5084
		if ($_folderName)
5085
		{
5086
			$success = $this->ajax_deleteFolder($_folderName,true);
5087
			$response = Api\Json\Response::get();
5088
			list(,$folderName) = explode(self::$delimiter, $_folderName);
5089
			if ($success)
5090
			{
5091
				$res = $folderName;
5092
			}
5093
			else
5094
			{
5095
				$res = lang("Failed to delete %1",$folderName);
5096
			}
5097
			$response->data($res);
5098
		}
5099
	}
5100
}