Completed
Push — 16.1 ( 884fd0...05ede2 )
by Hadi
37:36 queued 20:31
created

mail_ui   F

Complexity

Total Complexity 1011

Size/Duplication

Total Lines 5176
Duplicated Lines 9.08 %

Coupling/Cohesion

Components 1
Dependencies 33

Importance

Changes 0
Metric Value
wmc 1011
lcom 1
cbo 33
dl 470
loc 5176
rs 0.5217
c 0
b 0
f 0

63 Methods

Rating   Name   Duplication   Size   Complexity  
A ajax_tree_autoloading() 0 6 2
D subscription() 0 135 28
F changeProfile() 1 30 13
F __construct() 5 47 15
C callWizard() 0 39 7
D saveMessage() 0 43 10
C vfsSaveMessage() 4 66 16
F vfsSaveAttachment() 13 105 30
B displayImage() 0 34 3
F index() 25 181 47
F get_tree_actions() 0 193 14
A ajax_foldersubscription() 0 13 3
C ajax_foldertree() 0 23 7
B findNode() 0 16 7
B ajax_spamAction() 0 57 8
B getSpamActions() 0 32 3
F get_actions() 12 428 28
F get_rows() 11 247 63
A createRowID() 0 4 1
A generateRowID() 0 4 2
A splitRowID() 0 12 3
C get_toolbar_actions() 20 61 15
F header2gridelements() 0 215 74
C displayHeader() 0 42 7
F displayMessage() 0 122 35
B getDisplayToolbarActions() 0 25 1
D createAttachmentBlock() 0 191 40
B gatherVacation() 0 25 5
B quotaDisplay() 0 33 5
F getAttachment() 47 97 28
F download_zip() 0 100 23
B get_load_email_data() 0 41 6
A get_email_header() 0 15 1
A showBody() 0 13 3
F getdisplayableBody() 41 197 25
A resolve_inline_images() 0 15 3
D resolve_inline_image_byType() 0 104 22
D importMessage() 0 64 13
D importMessageToFolder() 0 68 11
A importMessageFromVFS2DraftAndEdit() 0 4 1
F importMessageFromVFS2DraftAndDisplay() 0 56 16
C loadEmailBody() 0 26 12
C ajax_setFolderStatus() 0 46 13
D ajax_addFolder() 0 103 13
F ajax_renameFolder() 48 133 21
D ajax_reloadNode() 0 43 9
A ajax_resolveWinmail() 0 19 2
F ajax_MoveFolder() 33 127 29
D ajax_deleteFolder() 0 91 17
B ajax_changeProfile() 0 32 6
D ajax_refreshVacationNotice() 0 41 14
D ajax_refreshFilters() 23 39 9
C ajax_refreshQuotaDisplay() 0 50 10
C ajax_emptySpam() 42 42 7
C ajax_emptyTrash() 42 42 7
C ajax_compressFolder() 0 30 7
A ajax_sendMDN() 0 7 2
F ajax_flagMessages() 26 188 78
F ajax_deleteMessages() 41 113 41
F ajax_copyMessages() 36 193 62
A ajax_folderMgmtTree_autoloading() 0 6 2
A folderManagement() 0 18 3
A ajax_folderMgmt_delete() 0 18 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like mail_ui often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use mail_ui, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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