Completed
Push — master ( 0e83ef...caafb0 )
by Klaus
42:35 queued 19:54
created

mail_ui::callWizard()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

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

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

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

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

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

could be turned into

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

This is much more concise to read.

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

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

These else branches can be removed.

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

could be turned into

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

This is much more concise to read.

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