Completed
Push — master ( 3e1f76...0e9327 )
by Klaus
234:48 queued 208:11
created

mail_zpush::egw_settings()   B

Complexity

Conditions 3
Paths 2

Size

Total Lines 79
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 35
nc 2
nop 1
dl 0
loc 79
rs 8.8701
c 1
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * EGroupware - Mail - interface class for activesync implementation
4
 *
5
 * @link http://www.egroupware.org
6
 * @package mail
7
 * @author Stylite AG [[email protected]]
8
 * @author Ralf Becker <[email protected]>
9
 * @author Philip Herbert <[email protected]>
10
 * @copyright (c) 2014-16 by Stylite AG <info-AT-stylite.de>
11
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
12
 * @version $Id$
13
 */
14
15
use EGroupware\Api;
16
use EGroupware\Api\Mail;
17
18
/**
19
 * mail eSync plugin
20
 *
21
 * Plugin creates a device specific file to map alphanumeric folder names to nummeric id's.
22
 */
23
class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, activesync_plugin_meeting_response, activesync_plugin_search_mailbox
24
{
25
	/**
26
	 * var activesync_backend
27
	 */
28
	private $backend;
29
30
	/**
31
	 * Instance of Mail
32
	 *
33
	 * @var Mail
34
	 */
35
	private $mail;
36
37
	/**
38
	 * Provides the ability to change the line ending
39
	 * @var string
40
	 */
41
	public static $LE = "\n";
42
43
	/**
44
	 * Integer id of trash folder
45
	 *
46
	 * @var mixed
47
	 */
48
	private $_wasteID = false;
49
50
	/**
51
	 * Integer id of sent folder
52
	 *
53
	 * @var mixed
54
	 */
55
	private $_sentID = false;
56
57
	/**
58
	 * Integer id of current mail account / connection
59
	 *
60
	 * @var int
61
	 */
62
	private $account;
63
64
	private $folders;
65
66
	private $messages;
0 ignored issues
show
Unused Code introduced by
The property $messages is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
67
68
	static $profileID;
69
70
	// to control how deep one may dive into the past
71
	const PAST_LIMIT = 178;
72
73
	/**
74
	 * debugLevel - enables more debug
75
	 *
76
	 * @var int
77
	 */
78
	private $debugLevel = 0;
79
80
	/**
81
	 * Constructor
82
	 *
83
	 * @param activesync_backend $backend
84
	 */
85
	public function __construct(activesync_backend $backend)
86
	{
87
		if ($GLOBALS['egw_setup']) return;
88
89
		//$this->debugLevel=2;
90
		$this->backend = $backend;
91
		if (!isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']))
92
		{
93
			if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Noprefs set: using 0 as default');
94
			// globals preferences add appname varname value
95
			$GLOBALS['egw']->preferences->add('activesync','mail-ActiveSyncProfileID',0,'user');
96
			// save prefs
97
			$GLOBALS['egw']->preferences->save_repository(true);
98
		}
99 View Code Duplication
		if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' ActiveProfileID:'.array2string(self::$profileID));
100
101
		if (is_null(self::$profileID))
102
		{
103 View Code Duplication
			if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' self::ProfileID isNUll:'.array2string(self::$profileID));
104
			self::$profileID =& Api\Cache::getSession('mail','activeSyncProfileID');
105 View Code Duplication
			if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' ActiveProfileID (after reading Cache):'.array2string(self::$profileID));
106
		}
107
		if (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']))
108
		{
109
			if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Pref for ProfileID:'.array2string($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']));
110
			if ($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'] == 'G')
111
			{
112
				self::$profileID = 'G'; // this should trigger the fetch of the first negative profile (or if no negative profile is available the firstb there is)
113
			}
114
			else
115
			{
116
				self::$profileID = (int)$GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'];
117
			}
118
		}
119 View Code Duplication
		if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Profile Selected (after reading Prefs):'.array2string(self::$profileID));
120
121
		// verify we are on an existing profile, if not running in setup (settings can not be static according to interface!)
122
		if (!isset($GLOBALS['egw_setup']))
123
		{
124
			try {
125
				Mail\Account::read(self::$profileID);
126
			}
127
			catch(Exception $e) {
128
				unset($e);
129
				self::$profileID = Mail\Account::get_default_acc_id();
130
			}
131
		}
132
		if ($this->debugLevel>0) error_log(__METHOD__.'::'.__LINE__.' ProfileSelected:'.self::$profileID);
133
		//$this->debugLevel=0;
134
	}
135
136
	/**
137
	 * Populates $settings for the preferences
138
	 *
139
	 * @param array|string $hook_data
140
	 * @return array
141
	 */
142
	function egw_settings($hook_data)
143
	{
144
		//error_log(__METHOD__.__LINE__.array2string($hook_data));
145
		$identities = array();
146
		if (!isset($hook_data['setup']) && in_array($hook_data['type'], array('user', 'group')))
147
		{
148
			$identities = iterator_to_array(Mail\Account::search((int)$hook_data['account_id']));
149
		}
150
		$identities += array(
151
			'G' => lang('Primary Profile'),
152
		);
153
154
		$settings['mail-ActiveSyncProfileID'] = array(
155
			'type'   => 'select',
156
			'label'  => 'eMail Account to sync',
157
			'name'   => 'mail-ActiveSyncProfileID',
158
			'help'   => 'eMail Account to sync ',
159
			'values' => $identities,
160
			'default'=> 'G',
161
			'xmlrpc' => True,
162
			'admin'  => False,
163
		);
164
		$settings['mail-allowSendingInvitations'] = array(
165
			'type'   => 'select',
166
			'label'  => 'allow sending of calendar invitations using this profile?',
167
			'name'   => 'mail-allowSendingInvitations',
168
			'help'   => 'control the sending of calendar invitations while using this profile',
169
			'values' => array(
170
				'sendifnocalnotif'=>'only send if there is no notification in calendar',
171
				'send'=>'yes, always send',
172
				'nosend'=>'no, do not send',
173
			),
174
			'xmlrpc' => True,
175
			'default' => 'sendifnocalnotif',
176
			'admin'  => False,
177
		);
178
		$settings['mail-maximumSyncRange'] = array(
179
			'type'   => 'input',
180
			'label'  => lang('How many days to sync in the past when client does not specify a date-range (default %1)', self::PAST_LIMIT),
181
			'name'   => 'mail-maximumSyncRange',
182
			'help'   => 'if the client sets no sync range, you may override the setting (preventing client crash that may be caused by too many mails/too much data). If you want to sync way-back into the past: set a large number',
183
			'xmlrpc' => True,
184
			'admin'  => False,
185
		);
186
187
/*
188
		$sigOptions = array(
189
				'send'=>'yes, always add EGroupware signatures to outgoing mails',
190
				'nosend'=>'no, never add EGroupware signatures to outgoing mails',
191
			);
192
		if (!isset($hook_data['setup']) && in_array($hook_data['type'], array('user', 'group'))&&$hook_data['account_id'])
193
		{
194
			$pID=self::$profileID;
195
			if ($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']=='G')
196
			{
197
				$pID=Mail\Account::get_default_acc_id();
198
			}
199
			$acc = Mail\Account::read($pID);
200
			error_log(__METHOD__.__LINE__.':'.$pID.'->'.array2string($acc));
201
			$Identities = Mail\Account::identities($pID);
202
			foreach($Identities as &$identity)
203
			{
204
				$Identity = self::identity_name($identity);
205
			}
206
			error_log(__METHOD__.__LINE__.array2string($Identities));
207
		}
208
		$settings['mail-useSignature'] = array(
209
			'type'   => 'select',
210
			'label'  => 'control if and which available signature is added to outgoing mails',
211
			'name'   => 'mail-useSignature',
212
			'help'   => 'control the use of signatures',
213
			'values' => $sigOptions,
214
			'xmlrpc' => True,
215
			'default' => 'nosend',
216
			'admin'  => False,
217
		);
218
*/
219
		return $settings;
220
	}
221
222
	/**
223
	 * Verify preferences
224
	 *
225
	 * @param array|string $hook_data
226
	 * @return array with error-messages from all plugins
227
	 */
228
	function verify_settings($hook_data)
229
	{
230
		$errors = array();
231
232
		// check if an eSync eMail profile is set (might not be set as default or forced!)
233
		if (isset($hook_data['prefs']['mail-ActiveSyncProfileID']) || $hook_data['type'] == 'user')
234
		{
235
			// eSync and eMail translations are not (yet) loaded
236
			Api\Translation::add_app('activesync');
237
			Api\Translation::add_app('mail');
238
239
			// inject preference to verify and call constructor
240
			$GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'] =
241
				$hook_data['prefs']['mail-ActiveSyncProfileID'];
242
			$this->__construct($this->backend);
243
244
			try {
245
				$this->_connect(0,true);
246
				$this->_disconnect();
247
248
				if (!$this->_wasteID) $errors[] = lang('No valid %1 folder configured!', '<b>'.lang('trash').'</b>');
249
				if (!$this->_sentID) $errors[] = lang('No valid %1 folder configured!', '<b>'.lang('send').'</b>');
250
			}
251
			catch(Exception $e) {
252
				$errors[] = lang('Can not open IMAP connection').': '.$e->getMessage();
253
			}
254
			if ($errors)
255
			{
256
				$errors[] = '<b>'.lang('eSync will FAIL without a working eMail configuration!').'</b>';
257
			}
258
		}
259
		//error_log(__METHOD__.'('.array2string($hook_data).') returning '.array2string($errors));
260
		return $errors;
261
	}
262
263
	/**
264
	 * Open IMAP connection
265
	 *
266
	 * @param int $account integer id of account to use
267
	 * @todo support different accounts
268
	 */
269
	private function _connect($account=0)
270
	{
271
		if (!$account) $account = self::$profileID ? self::$profileID : 0;
272
		if ($this->mail && $this->account != $account) $this->_disconnect();
273
274
		$this->_wasteID = false;
275
		$this->_sentID = false;
276
277
		if (!$this->mail)
278
		{
279
			$this->account = $account;
280
			// todo: tell mail which account to use
281
			//error_log(__METHOD__.__LINE__.' create object with ProfileID:'.array2string(self::$profileID));
282
			$this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
283 View Code Duplication
			if (self::$profileID == 0 && isset($this->mail->icServer->ImapServerId) && !empty($this->mail->icServer->ImapServerId)) self::$profileID = $this->mail->icServer->ImapServerId;
284
		}
285 View Code Duplication
		else
286
		{
287
			//error_log(__METHOD__.__LINE__." connect with profileID: ".self::$profileID);
288
			if (self::$profileID == 0 && isset($this->mail->icServer->ImapServerId) && !empty($this->mail->icServer->ImapServerId)) self::$profileID = $this->mail->icServer->ImapServerId;
289
		}
290
		$this->mail->openConnection(self::$profileID,false);
0 ignored issues
show
Unused Code introduced by
The call to Mail::openConnection() has too many arguments starting with false.

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...
291
292
		$this->_wasteID = $this->mail->getTrashFolder(false);
293
		//error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID);
294
		$this->_sentID = $this->mail->getSentFolder(false);
295
		$this->mail->getOutboxFolder(true);
296
		//error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID);
297
		//error_log(__METHOD__.__LINE__.' Connection Status for ProfileID:'.self::$profileID.'->'.$this->mail->icServer->_connected);
298
	}
299
300
	/**
301
	 * Close IMAP connection
302
	 */
303
	private function _disconnect()
304
	{
305
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__);
306
		if ($this->mail) $this->mail->closeConnection();
307
308
		unset($this->mail);
309
		unset($this->account);
310
		unset($this->folders);
311
	}
312
313
	/**
314
	 *  GetFolderList
315
	 *
316
	 *  @ToDo loop over available email accounts
317
	 */
318
	public function GetFolderList()
319
	{
320
		$folderlist = array();
321
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__);
322
		/*foreach($available_accounts as $account)*/ $account = 0;
323
		{
324
			$this->_connect($account);
325
			if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false,$_alwaysGetDefaultFolders=true);
326
			if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($this->folders));
327
328
			foreach ($this->folders as $folder => $folderObj) {
329
				if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' folder='.$folder);
330
				$folderlist[] = $f = array(
331
					'id'     => $this->createID($account,$folder),
332
					'mod'    => $folderObj->shortDisplayName,
333
					'parent' => $this->getParentID($account,$folder),
334
				);
335
				if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() returning ".array2string($f));
336
			}
337
		}
338
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() returning ".array2string($folderlist));
339
340
		return $folderlist;
341
	}
342
343
    /**
344
     * Sends a message which is passed as rfc822. You basically can do two things
345
     * 1) Send the message to an SMTP server as-is
346
     * 2) Parse the message yourself, and send it some other way
347
     * It is up to you whether you want to put the message in the sent items folder. If you
348
     * want it in 'sent items', then the next sync on the 'sent items' folder should return
349
     * the new message as any other new message in a folder.
350
     *
351
     * @param array $smartdata = IMAP-SendMail: SyncSendMail (
352
     *        (S) clientid => SendMail-30722448149304
353
     *        (S) saveinsent => empty
354
     *        (S) replacemime => null
355
     *        (S) accountid => null
356
     *        (S) source => SyncSendMailSource (
357
     *                                (S) folderid => 101000000000
358
     *                                (S) itemid => 33776
359
     *                                (S) longid => null
360
     *                                (S) instanceid => null
361
     *                                unsetVars(Array) size: 0
362
     *                                flags => false
363
     *                                content => null
364
     *                        )
365
     *        (S) mime => Date: Tue, 23 Jun 2015 14:13:23 +0200
366
     *Subject: AW: Blauer himmel
367
     *....
368
     *        (S) replyflag => true
369
     *        (S) forwardflag => null
370
     *        unsetVars(Array) size: 0
371
     *        flags => false
372
     *        content => null
373
     *)
374
	 *
375
     * @return boolean true on success, false on error
376
     *
377
     * @see eg. BackendIMAP::SendMail()
378
     * @todo implement either here or in mail backend
379
     * 	(maybe sending here and storing to sent folder in plugin, as sending is supposed to always work in EGroupware)
380
     */
381
	public function SendMail($smartdata)
382
	{
383
		//$this->debugLevel=2;
384
		$ClientSideMeetingRequest = false;
385
		$allowSendingInvitations = 'sendifnocalnotif';
386
		if (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-allowSendingInvitations']) &&
387
			$GLOBALS['egw_info']['user']['preferences']['activesync']['mail-allowSendingInvitations']=='nosend')
388
		{
389
			$allowSendingInvitations = false;
390
		}
391
		elseif (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-allowSendingInvitations']) &&
392
			$GLOBALS['egw_info']['user']['preferences']['activesync']['mail-allowSendingInvitations']!='nosend')
393
		{
394
			$allowSendingInvitations = $GLOBALS['egw_info']['user']['preferences']['activesync']['mail-allowSendingInvitations'];
395
		}
396
		$smartdata_task = ($smartdata->replyflag?'reply':($smartdata->forwardflag?'forward':'new'));
397
398
   		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__ . (isset($smartdata->mime) ? $smartdata->mime : ""). "task: ".(isset($smartdata_task) ? $smartdata_task : "")." itemid: ".(isset($smartdata->source->itemid) ? $smartdata->source->itemid : "")." folder: ".(isset($smartdata->source->folderid) ? $smartdata->source->folderid : ""));
399
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__."): Smartdata = ".array2string($smartdata));
400
		//error_log("IMAP-Sendmail: Smartdata = ".array2string($smartdata));
401
402
		// initialize our Mail
403
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
404
		$activeMailProfiles = $this->mail->getAccountIdentities(self::$profileID);
405
		// use the standardIdentity
406
		$activeMailProfile = Mail::getStandardIdentityForProfile($activeMailProfiles,self::$profileID);
407
408
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.")".' ProfileID:'.self::$profileID.' ActiveMailProfile:'.array2string($activeMailProfile));
409
410
		// initialize the new Api\Mailer object for sending
411
		$mailObject = new Api\Mailer(self::$profileID);
412
		$this->mail->parseRawMessageIntoMailObject($mailObject,$smartdata->mime);
413
		// Horde SMTP Class uses utf-8 by default. as we set charset always to utf-8
414
		$mailObject->Sender  = $activeMailProfile['ident_email'];
415
		$mailObject->setFrom($activeMailProfile['ident_email'],Mail::generateIdentityString($activeMailProfile,false));
416
		$mailObject->addHeader('X-Mailer', 'mail-Activesync');
417
418
419
		// prepare addressee list; moved the adding of addresses to the mailobject down
420
		// to
421
422 View Code Duplication
		foreach(Mail::parseAddressList($mailObject->getHeader("To")) as $addressObject) {
0 ignored issues
show
Bug introduced by
It seems like $mailObject->getHeader('To') targeting EGroupware\Api\Mailer::getHeader() can also be of type array; however, EGroupware\Api\Mail::parseAddressList() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
423
			if (!$addressObject->valid) continue;
424
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") Header Sentmail To: ".array2string($addressObject) );
425
			//$mailObject->AddAddress($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
426
			$toMailAddr[] = imap_rfc822_write_address($addressObject->mailbox, $addressObject->host, $addressObject->personal);
427
		}
428
		// CC
429 View Code Duplication
		foreach(Mail::parseAddressList($mailObject->getHeader("Cc")) as $addressObject) {
0 ignored issues
show
Bug introduced by
It seems like $mailObject->getHeader('Cc') targeting EGroupware\Api\Mailer::getHeader() can also be of type array; however, EGroupware\Api\Mail::parseAddressList() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
430
			if (!$addressObject->valid) continue;
431
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") Header Sentmail CC: ".array2string($addressObject) );
432
			//$mailObject->AddCC($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
433
			$ccMailAddr[] = imap_rfc822_write_address($addressObject->mailbox, $addressObject->host, $addressObject->personal);
434
		}
435
		// BCC
436 View Code Duplication
		foreach(Mail::parseAddressList($mailObject->getHeader("Bcc")) as $addressObject) {
0 ignored issues
show
Bug introduced by
It seems like $mailObject->getHeader('Bcc') targeting EGroupware\Api\Mailer::getHeader() can also be of type array; however, EGroupware\Api\Mail::parseAddressList() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
437
			if (!$addressObject->valid) continue;
438
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") Header Sentmail BCC: ".array2string($addressObject) );
439
			//$mailObject->AddBCC($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
440
			$bccMailAddr[] = imap_rfc822_write_address($addressObject->mailbox, $addressObject->host, $addressObject->personal);
441
		}
442
		$mailObject->clearAllRecipients();
443
444
		$use_orgbody = false;
445
446
		$k = 'Content-Type';
447
		$ContentType =$mailObject->getHeader('Content-Type');
448
		//error_log(__METHOD__.__LINE__." Header Sentmail original Header (filtered): " . $k.  " = ".trim($ContentType));
449
		// if the message is a multipart message, then we should use the sent body
450
		if (preg_match("/multipart/i", $ContentType)) {
451
			$use_orgbody = true;
452
		}
453
454
		// save the original content-type header for the body part when forwarding
455
		if ($smartdata_task == 'forward' && $smartdata->source->itemid && !$use_orgbody) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
456
			//continue; // ignore
457
		}
458
		// horde/egw_ mailer does everything as utf-8, the following should not be needed
459
		//$org_charset = $ContentType;
460
		//$ContentType = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $ContentType);
461
		// if the message is a multipart message, then we should use the sent body
462
		if (($smartdata_task == 'new' || $smartdata_task == 'reply' || $smartdata_task == 'forward') &&
463
			((isset($smartdata->replacemime) && $smartdata->replacemime == true) ||
464
			$k == "Content-Type" && preg_match("/multipart/i", $ContentType))) {
465
			$use_orgbody = true;
466
		}
467
		$Body =  $AltBody = "";
468
		// get body of the transmitted message
469
		// if this is a simple message, no structure at all
470
		if (preg_match("/text/i", $ContentType))
471
		{
472
			$simpleBodyType = (preg_match("/html/i", $ContentType)?'text/html':'text/plain');
473
			$bodyObj = $mailObject->findBody(preg_match("/html/i", $ContentType) ? 'html' : 'plain');
474
			$body = preg_replace("/(<|&lt;)*(([\w\.,-.,_.,0-9.]+)@([\w\.,-.,_.,0-9.]+))(>|&gt;)*/i","[$2]", $bodyObj ?$bodyObj->getContents() : null);
475
			if  ($simpleBodyType == "text/plain")
476
			{
477
				$Body = $body;
478
				$AltBody = "<pre>".nl2br($body)."</pre>";
479
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") fetched Body as :". $simpleBodyType.'=> Created AltBody');
480
			}
481
			else
482
			{
483
				$AltBody = $body;
484
				$Body =  trim(Api\Mail\Html::convertHTMLToText($body));
485
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") fetched Body as :". $simpleBodyType.'=> Created Body');
486
			}
487
		}
488
		else
489
		{
490
			// if this is a structured message
491
			// prefer plain over html
492
			$Body = preg_replace("/(<|&lt;)*(([\w\.,-.,_.,0-9.]+)@([\w\.,-.,_.,0-9.]+))(>|&gt;)*/i","[$2]",
493
				($text_body = $mailObject->findBody('plain')) ? $text_body->getContents() : null);
494
			$AltBody = preg_replace("/(<|&lt;)*(([\w\.,-.,_.,0-9.]+)@([\w\.,-.,_.,0-9.]+))(>|&gt;)*/i","[$2]",
495
				($html_body = $mailObject->findBody('html')) ? $html_body->getContents() : null);
496
		}
497
		if ($this->debugLevel>1 && $Body) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") fetched Body as with MessageContentType:". $ContentType.'=>'.$Body);
498
		if ($this->debugLevel>1 && $AltBody) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") fetched AltBody as with MessageContentType:". $ContentType.'=>'.$AltBody);
499
		//error_log(__METHOD__.__LINE__.array2string($mailObject));
500
		// if this is a multipart message with a boundary, we must use the original body
501
		//if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' mailObject after Inital Parse:'.array2string($mailObject));
502
        if ($use_orgbody) {
503
    	    ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") use_orgbody = true ContentType:".$ContentType);
504
 			// if it is a ClientSideMeetingRequest, we report it as send at all times
505
			if (($cal_body = $mailObject->findBody('calendar')) &&
506
				($cSMRMethod = $cal_body->getContentTypeParameter('method')))
507
			{
508
				if ($cSMRMethod == 'REPLY' && class_exists('calendar_ical'))
509
				{
510
					$organizer = calendar_ical::getIcalOrganizer($cal_body->getContents());
511
				}
512
				if ($this->debugLevel) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") we have a Client Side Meeting Request from organizer=$organizer");
513
				$ClientSideMeetingRequest = true;
514
			}
515
        }
516
		// now handle the addressee list
517
		$toCount = 0;
518
		//error_log(__METHOD__.__LINE__.array2string($toMailAddr));
519
		foreach((array)$toMailAddr as $address) {
520
			foreach(Mail::parseAddressList((get_magic_quotes_gpc()?stripslashes($address):$address)) as $addressObject) {
521
				$emailAddress = $addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : '');
522
				if ($ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' &&
523
					calendar_boupdate::email_update_requested($emailAddress, isset($cSMRMethod) ? $cSMRMethod : 'REQUEST',
524
						$organizer && !strcasecmp($emailAddress, $organizer) ? 'CHAIR' : ''))
525
				{
526
					ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") skiping mail to organizer '$organizer', as it will be send by calendar app");
527
					continue;
528
				}
529
				$mailObject->AddAddress($emailAddress, $addressObject->personal);
530
				$toCount++;
531
			}
532
		}
533
		$ccCount = 0;
534 View Code Duplication
		foreach((array)$ccMailAddr as $address) {
535
			foreach(Mail::parseAddressList((get_magic_quotes_gpc()?stripslashes($address):$address)) as $addressObject) {
536
				$emailAddress = $addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : '');
537
				if ($ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' && calendar_boupdate::email_update_requested($emailAddress)) continue;
538
				$mailObject->AddCC($emailAddress, $addressObject->personal);
539
				$ccCount++;
540
			}
541
		}
542
		$bccCount = 0;
543 View Code Duplication
		foreach((array)$bccMailAddr as $address) {
544
			foreach(Mail::parseAddressList((get_magic_quotes_gpc()?stripslashes($address):$address)) as $addressObject) {
545
				$emailAddress = $addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : '');
546
				if ($ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' && calendar_boupdate::email_update_requested($emailAddress)) continue;
547
				$mailObject->AddBCC($emailAddress, $addressObject->personal);
548
				$bccCount++;
549
			}
550
		}
551
		// typical organizer reply will end here with nothing send --> return true, because we suppressed the send above
552
		if ($toCount+$ccCount+$bccCount == 0)
553
		{
554
			return $ClientSideMeetingRequest && $allowSendingInvitations === 'sendifnocalnotif' && $organizer ? true : 0; // noone to send mail to
555
		}
556
		if ($ClientSideMeetingRequest === true && $allowSendingInvitations===false) return true;
557
		// as we use our mailer (horde mailer) it is detecting / setting the mimetype by itself while creating the mail
558
/*
559
		if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' retrieved Body:'.$body);
560
		$body = str_replace("\r",((preg_match("^text/html^i", $ContentType))?'<br>':""),$body); // what is this for?
561
		if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' retrieved Body (modified):'.$body);
562
*/
563
		// add signature!! -----------------------------------------------------------------
564
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ActiveMailProfile:'.array2string($activeMailProfile));
565
		try
566
		{
567
			$acc = Mail\Account::read($this->mail->icServer->ImapServerId);
568
			//error_log(__METHOD__.__LINE__.array2string($acc));
569
			$_signature = Mail\Account::read_identity($acc['ident_id'],true);
570
		}
571
		catch (Exception $e)
572
		{
573
			$_signature=array();
574
		}
575
		$signature = $_signature['ident_signature'];
576
		if ((isset($preferencesArray['disableRulerForSignatureSeparation']) &&
0 ignored issues
show
Bug introduced by
The variable $preferencesArray seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
577
			$preferencesArray['disableRulerForSignatureSeparation']) ||
0 ignored issues
show
Bug introduced by
The variable $preferencesArray 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...
578
			empty($signature) || trim(Api\Mail\Html::convertHTMLToText($signature)) =='')
579
		{
580
			$disableRuler = true;
581
		}
582
		$beforePlain = $beforeHtml = "";
583
		$beforeHtml = ($disableRuler ?'&nbsp;<br>':'&nbsp;<br><hr style="border:dotted 1px silver; width:90%; border:dotted 1px silver;">');
584
		$beforePlain = ($disableRuler ?"\r\n\r\n":"\r\n\r\n-- \r\n");
585
		$sigText = Mail::merge($signature,array($GLOBALS['egw']->accounts->id2name($GLOBALS['egw_info']['user']['account_id'],'person_id')));
586
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Signature to use:'.$sigText);
587
		$sigTextHtml = $beforeHtml.$sigText;
588
		$sigTextPlain = $beforePlain.Api\Mail\Html::convertHTMLToText($sigText);
589
		$isreply = $isforward = false;
590
		// reply ---------------------------------------------------------------------------
591
		if ($smartdata_task == 'reply' && isset($smartdata->source->itemid) &&
592
			isset($smartdata->source->folderid) && $smartdata->source->itemid && $smartdata->source->folderid &&
593
			(!isset($smartdata->replacemime) ||
594
			(isset($smartdata->replacemime) && $smartdata->replacemime == false)))
595
		{
596
			// now get on, and fetch the original mail
597
			$uid = $smartdata->source->itemid;
598
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") IMAP Smartreply is called with FolderID:".$smartdata->source->folderid.' and ItemID:'.$smartdata->source->itemid);
599
			$this->splitID($smartdata->source->folderid, $account, $folder);
600
601
			$this->mail->reopen($folder);
602
			$bodyStruct = $this->mail->getMessageBody($uid, 'html_only');
603
			$bodyBUFFHtml = $this->mail->getdisplayableBody($this->mail,$bodyStruct,true,false);
604
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' html_only:'.$bodyBUFFHtml);
605 View Code Duplication
		    if ($bodyBUFFHtml != "" && (is_array($bodyStruct) && $bodyStruct[0]['mimeType']=='text/html')) {
606
				// may be html
607
				if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:html (fetched with html_only):'.$bodyBUFFHtml);
608
				$AltBody = $AltBody."</br>".$bodyBUFFHtml.$sigTextHtml;
609
				$isreply = true;
610
			}
611
			// plain text Message part
612
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:plain, fetch text:');
613
			// if the new part of the message is html, we must preserve it, and handle that the original mail is text/plain
614
			$bodyStruct = $this->mail->getMessageBody($uid,'never_display');//'never_display');
615
			$bodyBUFF = $this->mail->getdisplayableBody($this->mail,$bodyStruct,false,false);
616 View Code Duplication
			if ($bodyBUFF != "" && (is_array($bodyStruct) && $bodyStruct[0]['mimeType']=='text/plain')) {
617
				if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:plain (fetched with never_display):'.$bodyBUFF);
618
				$Body = $Body."\r\n".$bodyBUFF.$sigTextPlain;
619
				$isreply = true;
620
			}
621 View Code Duplication
			if (!empty($bodyBUFF) && empty($bodyBUFFHtml) && !empty($AltBody))
622
			{
623
				$isreply = true;
624
				$AltBody = $AltBody."</br><pre>".nl2br($bodyBUFF).'</pre>'.$sigTextHtml;
625
				if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." no Api\Html Body found use modified plaintext body for txt/html: ".$AltBody);
626
			}
627
		}
628
629
		// how to forward and other prefs
630
		$preferencesArray =& $GLOBALS['egw_info']['user']['preferences']['mail'];
631
632
		// forward -------------------------------------------------------------------------
633
		if ($smartdata_task == 'forward' && isset($smartdata->source->itemid) &&
634
			isset($smartdata->source->folderid) && $smartdata->source->itemid && $smartdata->source->folderid &&
635
			(!isset($smartdata->replacemime) ||
636
			(isset($smartdata->replacemime) && $smartdata->replacemime == false)))
637
		{
638
			//force the default for the forwarding -> asmail
639
			if (is_array($preferencesArray)) {
640
				if (!array_key_exists('message_forwarding',$preferencesArray)
641
					|| !isset($preferencesArray['message_forwarding'])
642
					|| empty($preferencesArray['message_forwarding'])) $preferencesArray['message_forwarding'] = 'asmail';
643
			} else {
644
				$preferencesArray['message_forwarding'] = 'asmail';
645
			}
646
			// construct the uid of the message out of the itemid - seems to be the uid, no construction needed
647
			$uid = $smartdata->source->itemid;
648
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.")IMAP Smartfordward is called with FolderID:".$smartdata->source->folderid.' and ItemID:'.$smartdata->source->itemid);
649
			$this->splitID($smartdata->source->folderid, $account, $folder);
650
651
			$this->mail->reopen($folder);
652
            // receive entire mail (header + body)
653
			// get message headers for specified message
654
			$headers	= $this->mail->getMessageEnvelope($uid, $_partID, true, $folder);
655
			// build a new mime message, forward entire old mail as file
656
			if ($preferencesArray['message_forwarding'] == 'asmail')
657
			{
658
				$rawHeader      = $this->mail->getMessageRawHeader($smartdata->source->itemid, $_partID,$folder);
659
				$rawBody        = $this->mail->getMessageRawBody($smartdata->source->itemid, $_partID,$folder);
660
				$mailObject->AddStringAttachment($rawHeader.$rawBody, $headers['SUBJECT'].'.eml', 'message/rfc822');
661
				$AltBody = $AltBody."</br>".lang("See Attachments for Content of the Orignial Mail").$sigTextHtml;
662
				$Body = $Body."\r\n".lang("See Attachments for Content of the Orignial Mail").$sigTextPlain;
663
				$isforward = true;
664
			}
665
			else
666
			{
667
				// now get on, and fetch the original mail
668
				$uid = $smartdata->source->itemid;
669
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") IMAP Smartreply is called with FolderID:".$smartdata->source->folderid.' and ItemID:'.$smartdata->source->itemid);
670
				$this->splitID($smartdata->source->folderid, $account, $folder);
671
672
				$this->mail->reopen($folder);
673
				$bodyStruct = $this->mail->getMessageBody($uid, 'html_only');
674
				$bodyBUFFHtml = $this->mail->getdisplayableBody($this->mail,$bodyStruct,true,false);
675
				if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' html_only:'.$bodyBUFFHtml);
676 View Code Duplication
				if ($bodyBUFFHtml != "" && (is_array($bodyStruct) && $bodyStruct[0]['mimeType']=='text/html')) {
677
					// may be html
678
					if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:html (fetched with html_only):'.$bodyBUFFHtml);
679
					$AltBody = $AltBody."</br>".$bodyBUFFHtml.$sigTextHtml;
680
					$isforward = true;
681
				}
682
				// plain text Message part
683
				if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:plain, fetch text:');
684
				// if the new part of the message is html, we must preserve it, and handle that the original mail is text/plain
685
				$bodyStruct = $this->mail->getMessageBody($uid,'never_display');//'never_display');
686
				$bodyBUFF = $this->mail->getdisplayableBody($this->mail,$bodyStruct,false,false);
687 View Code Duplication
				if ($bodyBUFF != "" && (is_array($bodyStruct) && $bodyStruct[0]['mimeType']=='text/plain')) {
688
					if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") MIME Body".' Type:plain (fetched with never_display):'.$bodyBUFF);
689
					$Body = $Body."\r\n".$bodyBUFF.$sigTextPlain;
690
					$isforward = true;
691
				}
692 View Code Duplication
				if (!empty($bodyBUFF) && empty($bodyBUFFHtml) && !empty($AltBody))
693
				{
694
					$AltBody = $AltBody."</br><pre>".nl2br($bodyBUFF).'</pre>'.$sigTextHtml;
695
					if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." no html Body found use modified plaintext body for txt/html: ".$AltBody);
696
					$isforward = true;
697
				}
698
				// get all the attachments and add them too.
699
				// start handle Attachments
700
				//												$_uid, $_partID=null, Horde_Mime_Part $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=false, $resolveTNEF=true, $_folderName=''
701
				$attachments = $this->mail->getMessageAttachments($uid, null,          null,								true,						false,				 true			, $folder);
702
				$attachmentNames = false;
703
				if (is_array($attachments) && count($attachments)>0)
704
				{
705
					ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' gather Attachments for BodyCreation of/for MessageID:'.$uid.' found:'.count($attachments));
706
					foreach((array)$attachments as $key => $attachment)
707
					{
708 View Code Duplication
						if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Key:'.$key.'->'.array2string($attachment));
709
						$attachmentNames .= $attachment['name']."\n";
710
						$attachmentData	= $this->mail->getAttachment($uid, $attachment['partID'],0,false,false,$folder);
711
						/*$x =*/ $mailObject->AddStringAttachment($attachmentData['attachment'], $attachment['name'], $attachment['mimeType']);
712
						ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' added part with number:'.$x);
713
					}
714
				}
715
			}
716
		} // end forward
717
		// add signature, in case its not already added in forward or reply
718
		if (!$isreply && !$isforward)
719
		{
720
				$Body = $Body.$sigTextPlain;
721
				$AltBody = $AltBody.$sigTextHtml;
722
		}
723
		// now set the body
724 View Code Duplication
		if ($AltBody && ($html_body = $mailObject->findBody('html')))
725
		{
726
			if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' -> '.$AltBody);
727
			$html_body->setContents($AltBody,array('encoding'=>Horde_Mime_Part::DEFAULT_ENCODING));
728
		}
729 View Code Duplication
		if ($Body && ($text_body = $mailObject->findBody('plain')))
730
		{
731
			if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' -> '.$Body);
732
			$text_body->setContents($Body,array('encoding'=>Horde_Mime_Part::DEFAULT_ENCODING));
733
		}
734
		//advanced debugging
735
		// Horde SMTP Class uses utf-8 by default.
736
        //ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SendMail: parsed message: ". print_r($message,1));
737
		if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__."): MailObject:".array2string($mailObject));
738
739
		// set a higher timeout for big messages
740
		@set_time_limit(120);
741
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' -> '.' about to send ....');
742
		// send
743
		$send = true;
744
		try {
745
			$mailObject->Send();
746
		}
747
		catch(Exception $e) {
748
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") The email could not be sent. Last-SMTP-error: ". $e->getMessage());
749
			$send = false;
750
		}
751
752
		if (( $smartdata_task == 'reply' || $smartdata_task == 'forward') && $send == 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...
753
		{
754
			$uid = $smartdata->source->itemid;
755
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' tASK:'.$smartdata_task." FolderID:".$smartdata->source->folderid.' and ItemID:'.$smartdata->source->itemid);
756
			$this->splitID($smartdata->source->folderid, $account, $folder);
757
			//error_log(__METHOD__.__LINE__.' Folder:'.$folder.' Uid:'.$uid);
758
			$this->mail->reopen($folder);
759
			// if the draft folder is a starting part of the messages folder, the draft message will be deleted after the send
760
			// unless your templatefolder is a subfolder of your draftfolder, and the message is in there
761
			if ($this->mail->isDraftFolder($folder) && !$this->mail->isTemplateFolder($folder))
762
			{
763
				$this->mail->deleteMessages(array($uid),$folder);
764
			} else {
765
				$this->mail->flagMessages("answered", array($uid),$folder);
766
				if ($smartdata_task== "forward")
767
				{
768
					$this->mail->flagMessages("forwarded", array($uid),$folder);
769
				}
770
			}
771
		}
772
773
		$asf = ($send ? true:false); // initalize accordingly
774
		if (/*($smartdata->saveinsent==1 || !isset($smartdata->saveinsent)) && */  $send==true && $this->mail->mailPreferences['sendOptions'] != 'send_only')
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...
775
		{
776
			$asf = false;
777
			$sentFolder = $this->mail->getSentFolder();
778
			if ($this->_sentID) {
779
				$folderArray[] = $this->_sentID;
780
			}
781
			else if(isset($sentFolder) && $sentFolder != 'none')
782
			{
783
				$folderArray[] = $sentFolder;
784
			}
785
			// No Sent folder set, try defaults
786
			else
787
			{
788
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__.") IMAP-SendMail: No Sent mailbox set");
789
				// we dont try guessing
790
				$asf = true;
791
			}
792
			if (count($folderArray) > 0) {
793
				foreach((array)$bccMailAddr as $address) {
794
					foreach(Mail::parseAddressList((get_magic_quotes_gpc()?stripslashes($address):$address)) as $addressObject) {
795
						$emailAddress = $addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : '');
796
						$mailAddr[] = array($emailAddress, $addressObject->personal);
797
					}
798
				}
799
				$BCCmail='';
800
				if (count($mailAddr)>0) $BCCmail = $mailObject->AddrAppend("Bcc",$mailAddr);
801
				foreach($folderArray as $folderName) {
802 View Code Duplication
					if($this->mail->isSentFolder($folderName)) {
803
						$flags = '\\Seen';
804
					} elseif($this->mail->isDraftFolder($folderName)) {
805
						$flags = '\\Draft';
806
					} else {
807
						$flags = '';
808
					}
809
					$asf = true;
810
					//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.'->'.array2string($this->mail->icServer));
811
					$this->mail->openConnection(self::$profileID,false);
812
					if ($this->mail->folderExists($folderName)) {
813
						try
814
						{
815
							$this->mail->appendMessage($folderName,$mailObject->getRaw(), null,
816
									$flags);
817
						}
818
						catch (Api\Exception\WrongUserinput $e)
819
						{
820
							//$asf = false;
821
							ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.'->'.lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$mailObject->getHeader('Subject'),$folderName,$e->getMessage()));
822
						}
823
					}
824
					else
825
					{
826
						//$asf = false;
827
						ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.'->'.lang("Import of message %1 failed. Destination Folder %2 does not exist.",$mailObject->getHeader('Subject'),$folderName));
828
					}
829
			        ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(".__LINE__."): Outgoing mail saved in configured 'Sent' folder '".$folderName."': ". (($asf)?"success":"failed"));
830
				}
831
				//$this->mail->closeConnection();
832
			}
833
		}
834
835
		$this->debugLevel=0;
836
837
		if ($send && $asf)
838
		{
839
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' -> send successfully');
840
			return true;
841
		}
842
		else
843
		{
844
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." returning ".($ClientSideMeetingRequest ? true : 120)." (MailSubmissionFailed)".($ClientSideMeetingRequest ?" is ClientSideMeetingRequest (we ignore the failure)":""));
845
			return ($ClientSideMeetingRequest ? true : 120);   //MAIL Submission failed, see MS-ASCMD
846
		}
847
	}
848
849
	/**
850
	 * For meeting requests (iCal attachments with method='request') we call calendar plugin with iCal to get SyncMeetingRequest object,
851
	 * and do NOT return the attachment itself!
852
	 *
853
	 * @param string $folderid
854
	 * @param string $id
855
	 * @param ContentParameters $contentparameters  parameters of the requested message (truncation, mimesupport etc)
856
	 *  object with attributes foldertype, truncation, rtftruncation, conflict, filtertype, bodypref, deletesasmoves, filtertype, contentclass, mimesupport, conversationmode
857
	 *  bodypref object with attributes: ]truncationsize, allornone, preview
858
	 * @return $messageobject|boolean false on error
859
	 */
860
	public function GetMessage($folderid, $id, $contentparameters)
861
	{
862
		//$this->debugLevel=4;
863
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' FolderID:'.$folderid.' ID:'.$id.' ContentParams='.array2string($contentparameters));
864
		$truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
865
		$mimesupport = $contentparameters->GetMimeSupport();
866
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() truncsize=$truncsize, mimeSupport=".array2string($mimesupport));
867
		$bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */
868
869
		// fix for z-push bug returning additional bodypreference type 4, even if only 1 is requested and mimessupport = 0
870
		if (!$mimesupport && ($key = array_search('4', $bodypreference))) unset($bodypreference[$key]);
871
872
		//$this->debugLevel=4;
873
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
874
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' FolderID:'.$folderid.' ID:'.$id.' TruncSize:'.$truncsize.' Bodypreference: '.array2string($bodypreference));
875
		$account = $_folderName = $xid = null;
876
		$this->splitID($folderid,$account,$_folderName,$xid);
877
		$this->mail->reopen($_folderName);
878
		$messages = $this->fetchMessages($folderid, NULL, $id, true);	// true: return all headers
879
		$headers = $messages[$id];
880
		if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($headers));
881
		// StatMessage should reopen the folder in question, so we dont need folderids in the following statements.
882
		if ($headers)
883
		{
884
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." Message $id with stat ".array2string($headers));
885
			// initialize the object
886
			$output = new SyncMail();
887
			//$rawHeaders = $this->mail->getMessageRawHeader($id);
888
			// simple style
889
			// start AS12 Stuff (bodypreference === false) case = old behaviour
890 View Code Duplication
			if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__. ' for message with ID:'.$id.' with headers:'.array2string($headers));
891
892
			if ($bodypreference === false) {
893
				$bodyStruct = $this->mail->getMessageBody($id, 'only_if_no_text', '', null, true,$_folderName);
894
				$raw_body = $this->mail->getdisplayableBody($this->mail,$bodyStruct,false,false);
895
				//$body = html_entity_decode($body,ENT_QUOTES,$this->mail->detect_encoding($body));
896
				if (stripos($raw_body,'<style')!==false) $body = preg_replace("/<style.*?<\/style>/is", "", $raw_body); // in case there is only a html part
897
				// remove all other html
898
				$body = strip_tags($raw_body);
899
				if(strlen($body) > $truncsize) {
900
					$body = Utils::Utf8_truncate($body, $truncsize);
901
					$output->bodytruncated = 1;
902
				}
903
				else
904
				{
905
					$output->bodytruncated = 0;
906
				}
907
				$output->bodysize = strlen($body);
908
				$output->body = $body;
909
			}
910
			else // style with bodypreferences
911
			{
912
				//Select body type preference
913
				$bpReturnType = 1;//SYNC_BODYPREFERENCE_PLAIN;
914
				if ($bodypreference !== false) {
915
					// bodypreference can occur multiple times
916
					// usually we would use Utils::GetBodyPreferenceBestMatch($bodypreference);
917
					$bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference);
918
/*
919
					foreach($bodypreference as $bpv)
920
					{
921
						// we use the last, or MIMEMESSAGE when present
922
						$bpReturnType = $bpv;
923
						if ($bpReturnType==SYNC_BODYPREFERENCE_MIME) break;
924
					}
925
*/
926
				}
927
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." getBodyPreferenceBestMatch: ".array2string($bpReturnType));
928
				// set the protocoll class
929
				$output->asbody = new SyncBaseBody();
930
931
				// return full mime-message without any (charset) conversation directly as stream
932
				if ($bpReturnType==SYNC_BODYPREFERENCE_MIME)
933
				{
934
					//SYNC_BODYPREFERENCE_MIME
935
					$output->asbody->type = SYNC_BODYPREFERENCE_MIME;
936
					$stream = $this->mail->getMessageRawBody($id, '', $_folderName, true);
937
					$fstat = fstat($stream);
938
					fseek($stream, 0, SEEK_SET);
939
					$output->asbody->data = $stream;
940
					$output->asbody->estimatedDataSize = $fstat['size'];
941
					ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." bodypreference 4=SYNC_BODYPREFERENCE_MIME=full mime message requested, size=$fstat[size]");
942
				}
943
				else
944
				{
945
					// fetch the body (try to gather data only once)
946
					$css ='';
947
					$bodyStruct = $this->mail->getMessageBody($id, 'html_only', '', null, true,$_folderName);
948
					if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' html_only Struct:'.array2string($bodyStruct));
949
					$body = $this->mail->getdisplayableBody($this->mail,$bodyStruct,true,false);
950
					if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' html_only:'.$body);
951
					if ($body != "" && (is_array($bodyStruct) && $bodyStruct[0]['mimeType']=='text/html')) {
952
						// may be html
953
						if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "MIME Body".' Type:html (fetched with html_only)');
954
						$css = $this->mail->getStyles($bodyStruct);
955
						$output->nativebodytype=2;
956
					} else {
957
						// plain text Message
958
						if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "MIME Body".' Type:plain, fetch text (HTML, if no text available)');
959
						$output->nativebodytype=1;
960
						$bodyStruct = $this->mail->getMessageBody($id,'never_display', '', null, true,$_folderName); //'only_if_no_text');
961
						if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' plain text Struct:'.array2string($bodyStruct));
962
						$body = $this->mail->getdisplayableBody($this->mail,$bodyStruct,false,false);
963
						if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' never display html(plain text only):'.$body);
964
					}
965
					// whatever format decode (using the correct encoding)
966 View Code Duplication
					if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__."MIME Body".' Type:'.($output->nativebodytype==2?' html ':' plain ').$body);
967
					//$body = html_entity_decode($body,ENT_QUOTES,$this->mail->detect_encoding($body));
968
					// prepare plaintextbody
969
					$plainBody='';
970
					if ($output->nativebodytype == 2)
971
					{
972
						$bodyStructplain = $this->mail->getMessageBody($id,'never_display', '', null, true,$_folderName); //'only_if_no_text');
973
						if(isset($bodyStructplain[0])&&isset($bodyStructplain[0]['error'])&&$bodyStructplain[0]['error']==1)
974
						{
975
							$plainBody = Api\Mail\Html::convertHTMLToText($body); // always display with preserved HTML
976
						}
977
						else
978
						{
979
							$plainBody = $this->mail->getdisplayableBody($this->mail,$bodyStructplain,false,false);
980
						}
981
					}
982
					//if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "MIME Body".$body);
983
					$plainBody = preg_replace("/<style.*?<\/style>/is", "", (strlen($plainBody)?$plainBody:$body));
984
					// remove all other html
985
					$plainBody = preg_replace("/<br.*>/is","\r\n",$plainBody);
986
					$plainBody = strip_tags($plainBody);
987
					if ($this->debugLevel>3 && $output->nativebodytype==1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Plain Text:'.$plainBody);
988
					//$body = str_replace("\n","\r\n", str_replace("\r","",$body)); // do we need that?
989
990
					if ($bpReturnType==2) //SYNC_BODYPREFERENCE_HTML
991
					{
992
						if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "HTML Body with requested pref 2");
993
						// Send HTML if requested and native type was html
994
						$output->asbody->type = 2;
995
						$htmlbody = '<html>'.
996
							'<head>'.
997
							'<meta name="Generator" content="Z-Push">'.
998
							'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'.
999
							$css.
1000
							'</head>'.
1001
							'<body>';
1002
						if ($output->nativebodytype==2)
1003
						{
1004
							if ($css) Api\Mail\Html::replaceTagsCompletley($body,'style');
1005
							// as we fetch html, and body is HTML, we may not need to handle this
1006
							$htmlbody .= $body;
1007
						}
1008
						else
1009
						{
1010
							// html requested but got only plaintext, so fake html
1011
							$htmlbody .= str_replace("\n","<BR>",str_replace("\r","<BR>", str_replace("\r\n","<BR>",$plainBody)));
1012
						}
1013
						$htmlbody .= '</body>'.
1014
								'</html>';
1015
1016 View Code Duplication
						if(isset($truncsize) && strlen($htmlbody) > $truncsize)
1017
						{
1018
							$htmlbody = Utils::Utf8_truncate($htmlbody,$truncsize);
1019
							$output->asbody->truncated = 1;
1020
						}
1021
						// output->nativebodytype is used as marker that the original message was of type ... but is now converted to, as type 2 is requested.
1022
						$output->nativebodytype = 2;
1023
						$output->asbody->data = StringStreamWrapper::Open($htmlbody);
1024
						$output->asbody->estimatedDataSize = strlen($htmlbody);
1025
					}
1026
					else
1027
					{
1028
						// Send Plaintext as Fallback or if original body is plainttext
1029
						if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "Plaintext Body:".$plainBody);
1030
						/* we use plainBody (set above) instead
1031
						$bodyStruct = $this->mail->getMessageBody($id,'only_if_no_text'); //'never_display');
1032
						$plain = $this->mail->getdisplayableBody($this->mail,$bodyStruct);
1033
						$plain = html_entity_decode($plain,ENT_QUOTES,$this->mail->detect_encoding($plain));
1034
						$plain = strip_tags($plain);
1035
						//$plain = str_replace("\n","\r\n",str_replace("\r","",$plain));
1036
						*/
1037
						$output->asbody->type = 1;
1038
						$output->nativebodytype = 1;
1039 View Code Duplication
						if(isset($truncsize) &&
1040
							strlen($plainBody) > $truncsize)
1041
						{
1042
							$plainBody = Utils::Utf8_truncate($plainBody, $truncsize);
1043
							$output->asbody->truncated = 1;
1044
						}
1045
						$output->asbody->data = StringStreamWrapper::Open((string)$plainBody !== '' ? $plainBody : ' ');
1046
						$output->asbody->estimatedDataSize = strlen($plainBody);
1047
					}
1048
					// In case we have nothing for the body, send at least a blank...
1049
					// dw2412 but only in case the body is not rtf!
1050
					if ($output->asbody->type != 3 && !isset($output->asbody->data))
1051
					{
1052
						$output->asbody->data = StringStreamWrapper::Open(" ");
1053
						$output->asbody->estimatedDataSize = 1;
1054
					}
1055
				}
1056
			}
1057
			// end AS12 Stuff
1058
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' gather Header info:'.$headers['subject'].' from:'.$headers['date']);
1059
			$output->read = $headers["flags"];
1060
1061
			$output->flag = new SyncMailFlags();
1062
			if ($headers['flagged'] == 1)
1063
			{
1064
				$output->flag->flagstatus = 2;
1065
				//$output->flag->flagtype = "Flag for Follow up";
1066
			} else {
1067
				$output->flag->flagstatus = 0;
1068
			}
1069
			if ($headers['answered'])
1070
			{
1071
				$output->lastverexecuted = AS_REPLYTOSENDER;
1072
			}
1073
			elseif ($headers['forwarded'])
1074
			{
1075
				$output->lastverexecuted = AS_FORWARD;
1076
			}
1077
			$output->subject = $headers['subject'];
1078
			$output->importance = $headers['priority'] > 3 ? 0 :
1079
				($headers['priority'] < 3 ? 2 : 1) ;
1080
			$output->datereceived = $this->mail->_strtotime($headers['date'],'ts',true);
1081
			$output->to = $headers['to_address'];
1082
			if ($headers['to']) $output->displayto = $headers['to_address']; //$headers['FETCHED_HEADER']['to_name']
1083
			$output->from = $headers['sender_address'];
1084
			if (isset($headers['cc_addresses']) && $headers['cc_addresses']) $output->cc = $headers['cc_addresses'];
1085
			if (isset($headers['reply_to_address']) && $headers['reply_to_address']) $output->reply_to = $headers['reply_to_address'];
1086
1087
			$output->messageclass = "IPM.Note";
1088
			if (stripos($headers['mimetype'],'multipart')!== false &&
1089
				stripos($headers['mimetype'],'signed')!== false)
1090
			{
1091
				$output->messageclass = "IPM.Note.SMIME.MultipartSigned";
1092
			}
1093
			if (Request::GetProtocolVersion() >= 12.0) {
1094
				$output->contentclass = "urn:content-classes:message";
1095
			}
1096
1097
			// start handle Attachments (include text/calendar multipart alternative)
1098
			$attachments = $this->mail->getMessageAttachments($id, $_partID='', $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=true, true, $_folderName);
1099
			// Attachments should not needed for MIME messages, so skip this part if bpReturnType==4
1100
			if (/*$bpReturnType != SYNC_BODYPREFERENCE_MIME &&*/ is_array($attachments) && count($attachments)>0)
1101
			{
1102
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' gather Attachments for MessageID:'.$id.' found:'.count($attachments));
1103
				//error_log(__METHOD__.__LINE__.array2string($attachments));
1104
				foreach ($attachments as $key => $attach)
1105
				{
1106 View Code Duplication
					if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Key:'.$key.'->'.array2string($attach));
1107
1108
					// pass meeting requests to calendar plugin
1109
					if (strtolower($attach['mimeType']) == 'text/calendar' && strtolower($attach['method']) == 'request' &&
1110
						isset($GLOBALS['egw_info']['user']['apps']['calendar']) &&
1111
						($attachment = $this->mail->getAttachment($id, $attach['partID'],0,false,false,$_folderName)) &&
1112
						($output->meetingrequest = calendar_zpush::meetingRequest($attachment['attachment'])))
1113
					{
1114
						//overwrite the globalobjId from calendar object, as: if you delete the mail, that is
1115
						//the meeting-request its using the globalobjid as reference and deletes both:
1116
						//mail AND meeting. we dont want this. accepting meeting requests with the mobile does nothing
1117
						$output->meetingrequest->globalobjid = activesync_backend::uid2globalObjId($id);
1118
						$output->messageclass = "IPM.Schedule.Meeting.Request";
1119
						//$output->messageclass = "IPM.Schedule.Meeting";
1120
						unset($attachment);
1121
						continue;	// do NOT add attachment as attachment
1122
					}
1123
					if (Request::GetProtocolVersion() >= 12.0) {
1124
						$attachment = new SyncBaseAttachment();
1125
						if (!isset($output->asattachments) || !is_array($output->asattachments))
1126
							$output->asattachments = array();
1127
						$attachment->estimatedDataSize = $attach['size'];
1128
						$attachment->method = 1;
1129
						$attachment->filereference = $folderid . ":" . $id . ":" . $attach['partID'];
1130
					} else {
1131
						$attachment = new SyncAttachment();
1132
						if (!isset($output->attachments) || !is_array($output->attachments))
1133
							$output->attachments = array();
1134
						$attachment->attsize = $attach['size'];
1135
						$attachment->attmethod = 1;
1136
						$attachment->attname = $folderid . ":" . $id . ":" . $attach['partID'];//$key;
1137
					}
1138
1139
					$attachment->displayname = $attach['name'];
1140
					//error_log(__METHOD__.__LINE__.'->'.$folderid . ":" . $id . ":" . $attach['partID']);
1141
1142
					$attachment->attoid = "";//isset($part->headers['content-id']) ? trim($part->headers['content-id']) : "";
1143
					//$attachment->isinline=0; // if not inline, do not use isinline
1144
					if (!empty($attach['cid']) && $attach['cid'] <> 'NIL' )
1145
					{
1146
						if ($bpReturnType != 4 && $attach['disposition'] == 'inline')
1147
						{
1148
							$attachment->isinline = true;
1149
						}
1150
						if (Request::GetProtocolVersion() >= 12.0) {
1151
							$attachment->method=1;
1152
							$attachment->contentid= str_replace(array("<",">"), "",$attach['cid']);
1153
						} else {
1154
							$attachment->attmethod=6;
1155
							$attachment->attoid = str_replace(array("<",">"), "",$attach['cid']);
1156
						}
1157
						//	ZLog::Write(LOGLEVEL_DEBUG, "'".$part->headers['content-id']."'  ".$attachment->contentid);
1158
						$attachment->contenttype = trim($attach['mimeType']);
1159
						//	ZLog::Write(LOGLEVEL_DEBUG, "'".$part->headers['content-type']."'  ".$attachment->contentid);
1160
					}
1161
					if (Request::GetProtocolVersion() >= 12.0) {
1162
						array_push($output->asattachments, $attachment);
1163
					} else {
1164
						array_push($output->attachments, $attachment);
1165
					}
1166
					unset($attachment);
1167
				}
1168
			}
1169
			//$this->debugLevel=0;
1170
			// end handle Attachments
1171
			unset($attachments);
1172
1173
            // Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx
1174
            $output->internetcpid = INTERNET_CPID_UTF8;
1175
1176 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($output));
1177
//$this->debugLevel=0;
1178
			return $output;
1179
		}
1180
//$this->debugLevel=0;
1181
		return false;
1182
	}
1183
1184
	/**
1185
	 * Process response to meeting request
1186
	 *
1187
	 * mail plugin only extracts the iCal attachment and let's calendar plugin deal with adding it
1188
	 *
1189
	 * @see BackendDiff::MeetingResponse()
1190
	 * @param string $folderid folder of meeting request mail
1191
	 * @param int|string $requestid uid of mail with meeting request
1192
	 * @param int $response 1=accepted, 2=tentative, 3=decline
1193
	 * @return int|boolean id of calendar item, false on error
1194
	 */
1195
	function MeetingResponse($folderid, $requestid, $response)
1196
	{
1197
		if (!class_exists('calendar_zpush'))
1198
		{
1199
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(...) no EGroupware calendar installed!");
1200
			return null;
1201
		}
1202
		if (!($stat = $this->StatMessage($folderid, $requestid)))
1203
		{
1204
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) returning FALSE (can NOT stat message)");
1205
			return false;
1206
		}
1207
		$ret = false;
1208
		foreach($this->mail->getMessageAttachments($requestid, $_partID='', $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=true) as $key => $attach)
1209
		{
1210
			if (strtolower($attach['mimeType']) == 'text/calendar' && strtolower($attach['method']) == 'request' &&
1211
				($attachment = $this->mail->getAttachment($requestid, $attach['partID'],0,false)))
1212
			{
1213
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) iCal found, calling now backend->MeetingResponse('$attachment[attachment]')");
1214
1215
				// calling backend again with iCal attachment, to let calendar add the event
1216
				$ret = $this->backend->MeetingResponse($attachment['attachment'],
1217
					$this->backend->createID('calendar',$GLOBALS['egw_info']['user']['account_id']),
1218
					$response);
1219
				break;
1220
			}
1221
		}
1222
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) returning ".array2string($ret));
1223
		return $ret;
1224
	}
1225
1226
	/**
1227
	 * GetAttachmentData
1228
	 * Should return attachment data for the specified attachment. The passed attachment identifier is
1229
	 * the exact string that is returned in the 'AttName' property of an SyncAttachment. So, you should
1230
	 * encode any information you need to find the attachment in that 'attname' property.
1231
	 *
1232
     * @param string $fid - id
1233
     * @param string $attname - should contain (folder)id
1234
	 * @return SyncItemOperationsAttachment-object
1235
	 */
1236
	function GetAttachmentData($fid,$attname) {
1237
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')");
1238
		return $this->_GetAttachmentData($fid,$attname);
1239
	}
1240
1241
	/**
1242
	 * ItemOperationsGetAttachmentData
1243
	 * Should return attachment data for the specified attachment. The passed attachment identifier is
1244
	 * the exact string that is returned in the 'AttName' property of an SyncAttachment. So, you should
1245
	 * encode any information you need to find the attachment in that 'attname' property.
1246
	 *
1247
     * @param string $fid - id
1248
     * @param string $attname - should contain (folder)id
1249
	 * @return SyncItemOperationsAttachment-object
1250
	 */
1251
	function ItemOperationsGetAttachmentData($fid,$attname) {
1252
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')");
1253
		return $this->_GetAttachmentData($fid,$attname);
1254
	}
1255
1256
	/**
1257
	 * _GetAttachmentData implements
1258
	 * -ItemOperationsGetAttachmentData
1259
	 * -GetAttachmentData
1260
	 *
1261
     * @param string $fid - id
1262
     * @param string $attname - should contain (folder)id
1263
	 * @return SyncItemOperationsAttachment-object
1264
	 */
1265
	private function _GetAttachmentData($fid,$attname)
1266
	{
1267
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')".function_backtrace());
1268
		//error_log(__METHOD__.__LINE__." Fid: $fid (attname: '$attname')");
1269
		list($folderid, $id, $part) = explode(":", $attname);
1270
1271
		$this->splitID($folderid, $account, $folder);
1272
1273
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
1274
1275
		$this->mail->reopen($folder);
1276
		$attachment = $this->mail->getAttachment($id,$part,0,false,true,$folder);
1277
		$SIOattachment = new SyncItemOperationsAttachment();
1278
		fseek($attachment['attachment'], 0, SEEK_SET);	// z-push requires stream seeked to start
1279
		$SIOattachment->data = $attachment['attachment'];
1280
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname') Data:".$attachment['attachment']);
1281
		if (isset($attachment['type']) )
1282
			$SIOattachment->contenttype = $attachment['type'];
1283
1284
		unset($attachment);
1285
1286
		return $SIOattachment;
1287
	}
1288
1289
	/**
1290
	 * StatMessage should return message stats, analogous to the folder stats (StatFolder). Entries are:
1291
	 *
1292
	 * 'id'	 => Server unique identifier for the message. Again, try to keep this short (under 20 chars)
1293
	 * 'flags'	 => simply '0' for unread, '1' for read
1294
	 * 'mod'	=> modification signature. As soon as this signature changes, the item is assumed to be completely
1295
	 *			 changed, and will be sent to the PDA as a whole. Normally you can use something like the modification
1296
	 *			 time for this field, which will change as soon as the contents have changed.
1297
	 *
1298
	 * @param string $folderid
1299
	 * @param int $id id (uid) of message
1300
	 * @return array
1301
	 */
1302
	public function StatMessage($folderid, $id)
1303
	{
1304
		$messages = $this->fetchMessages($folderid, NULL, $id);
1305
		//ZLog::Write(LOGLEVEL_DEBUG, __METHOD__."('$folderid','$id') returning ".array2string($messages[$id]));
1306
		return $messages[$id];
1307
	}
1308
1309
	/**
1310
	 * Called when a message has been changed on the mobile.
1311
	 * Added support for FollowUp flag
1312
	 *
1313
	 * @param string              $folderid            id of the folder
1314
	 * @param string              $id                  id of the message
1315
	 * @param SyncXXX             $message             the SyncObject containing a message
1316
	 * @param ContentParameters   $contentParameters
1317
	 *
1318
	 * @access public
1319
	 * @return array                        same return value as StatMessage()
1320
	 * @throws StatusException              could throw specific SYNC_STATUS_* exceptions
1321
	 */
1322
	function ChangeMessage($folderid, $id, $message, $contentParameters)
1323
	{
1324
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." $folderid, $id,".array2string($message).",".array2string($contentParameters));
1325
		//unset($folderid, $id, $message, $contentParameters);
1326
		$account = $folder = null;
1327
		$this->splitID($folderid, $account, $folder);
1328
		if (isset($message->flag)) {
1329
			if (isset($message->flag->flagstatus) && $message->flag->flagstatus == 2) {
1330
				$rv = $this->mail->flagMessages((($message->flag->flagstatus == 2) ? "flagged" : "unflagged"), $id,$folder);
1331
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." -> set ".array2string($id).' in Folder '.$folder." as " . (($message->flag->flagstatus == 2) ? "flagged" : "unflagged") . "-->". $rv);
1332
			} else {
1333
				$rv = $this->mail->flagMessages("unflagged", $id,$folder);
1334
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." -> set ".array2string($id).' in Folder '.$folder." as " . "unflagged" . "-->". $rv);
1335
			}
1336
		}
1337
		return $this->StatMessage($folderid, $id);
1338
	}
1339
1340
	/**
1341
	 * This function is called when the user moves an item on the PDA. You should do whatever is needed
1342
	 * to move the message on disk. After this call, StatMessage() and GetMessageList() should show the items
1343
	 * to have a new parent. This means that it will disappear from GetMessageList() will not return the item
1344
	 * at all on the source folder, and the destination folder will show the new message
1345
	 *
1346
	 * @param string              $folderid            id of the source folder
1347
	 * @param string              $id                  id of the message
1348
	 * @param string              $newfolderid         id of the destination folder
1349
	 * @param ContentParameters   $contentParameters
1350
	 *
1351
	 * @return boolean                      status of the operation
1352
	 * @throws StatusException              could throw specific SYNC_MOVEITEMSSTATUS_* exceptions
1353
	 */
1354
	public function MoveMessage($folderid, $id, $newfolderid, $contentParameters)
1355
	{
1356
		unset($contentParameters);	// not used, but required by function signature
1357
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-MoveMessage: (sfid: '$folderid'  id: '$id'  dfid: '$newfolderid' )");
1358
		$account = $srcFolder = $destFolder = null;
1359
		$this->splitID($folderid, $account, $srcFolder);
1360
		$this->splitID($newfolderid, $account, $destFolder);
1361
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-MoveMessage: (SourceFolder: '$srcFolder'  id: '$id'  DestFolder: '$destFolder' )");
1362
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
1363
		$this->mail->reopen($destFolder);
1364
		$status = $this->mail->getFolderStatus($destFolder);
1365
		$uidNext = $status['uidnext'];
1366
		$this->mail->reopen($srcFolder);
1367
1368
		// move message
1369
		$rv = $this->mail->moveMessages($destFolder,(array)$id,true,$srcFolder,true);
1370
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.": New Status of $destFolder :".array2string($status).", ReturnValOf moveMessage".array2string($rv)); // this may be true, so try using the nextUID value by examine
1371
		// return the new id "as string"
1372
		return ($rv===true ? $uidNext : $rv[$id]) . "";
1373
	}
1374
1375
	/**
1376
	 *  Get all messages of a folder with optional cutoffdate
1377
	 *
1378
	 *  @param int $cutoffdate =null timestamp with cutoffdate, default 12 weeks
1379
	 */
1380
	public function GetMessageList($folderid, $cutoffdate=NULL)
1381
	{
1382
		if ($cutoffdate > 0)
1383
		{
1384
			ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.' for Folder:'.$folderid.' SINCE:'.$cutoffdate.'/'.date("d-M-Y", $cutoffdate));
1385
		}
1386
		else
1387
		{
1388
			$maximumSyncRangeInDays = self::PAST_LIMIT; // corresponds to our default value
1389 View Code Duplication
			if (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-maximumSyncRange']))
1390
			{
1391
				$maximumSyncRangeInDays = $GLOBALS['egw_info']['user']['preferences']['activesync']['mail-maximumSyncRange'];
1392
			}
1393
			$cutoffdate = (is_numeric($maximumSyncRangeInDays) ? Api\DateTime::to('now','ts')-(3600*24*$maximumSyncRangeInDays):null);
1394
			if (is_numeric($maximumSyncRangeInDays)) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' Client set no truncationdate. Using '.$maximumSyncRangeInDays.' days.'.date("d-M-Y", $cutoffdate));
1395
		}
1396
		return $this->fetchMessages($folderid, $cutoffdate);
1397
	}
1398
1399
	/**
1400
	 * Fetch headers for one or all mail of a folder using optional cutoffdate
1401
	 *
1402
	 * Headers of last fetchMessage call of complate folder are cached in static $headers,
1403
	 * to allow to use them without fetching them again.
1404
	 * Next call clears cache
1405
	 *
1406
	 * @param int $folderid
1407
	 * @param int $cutoffdate timestamp with cutoffdate
1408
	 * @param string $_id =null uid of single message to fetch
1409
	 * @param boolean $return_all_headers =false true: additinal contain all headers eg. "subject"
1410
	 * @return array uid => array StatMessage($folderid, $_id)
1411
	 */
1412
	private function fetchMessages($folderid, $cutoffdate=NULL, $_id=NULL, $return_all_headers=false)
1413
	{
1414
		static $headers = array();
1415
1416
		if ($this->debugLevel>1) $gstarttime = microtime (true);
1417
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__);
1418
		$rv_messages = array();
1419
		// if the message is still available within the class, we use it instead of fetching it again
1420
		if ($_id && isset($headers[$_id]) && is_array($headers[$_id]))
1 ignored issue
show
Bug Best Practice introduced by
The expression $_id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1421
		{
1422
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." the message ".$_id[0]." is still available within the class, we use it instead of fetching it again");
1423
			$rv_messages = array('header'=>array($headers[$_id]));
1424
		}
1425
		else
1426
		{
1427
			$headers = array();	// clear cache to not use too much memory
1428
1429
			if ($this->debugLevel>1) $starttime = microtime (true);
1430
			$this->_connect($this->account);
1431 View Code Duplication
			if ($this->debugLevel>1)
1432
			{
1433
				$endtime = microtime(true) - $starttime;
1434
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " connect took : ".$endtime.' for account:'.$this->account);
1435
			}
1436
			$messagelist = $_filter = array();
1437
			// if not connected, any further action must fail
1438
			if (!empty($cutoffdate)) $_filter = array('status'=>array('UNDELETED'),'range'=>"SINCE",'date'=> date("d-M-Y", $cutoffdate));
1439
			if ($this->debugLevel>1) $starttime = microtime (true);
1440
			$account = $_folderName = $id = null;
1441
			$this->splitID($folderid,$account,$_folderName,$id);
1442 View Code Duplication
			if ($this->debugLevel>1)
1443
			{
1444
				$endtime = microtime(true) - $starttime;
1445
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " splitID took : ".$endtime.' for FolderID:'.$folderid);
1446
			}
1447
			if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id);
1448
			if ($this->debugLevel>1) $starttime = microtime (true);
1449
			$_numberOfMessages = (empty($cutoffdate)?250:99999);
1450
			$rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=1, $_numberOfMessages, $_sort=0, $_reverse=false, $_filter, $_id);
1451 View Code Duplication
			if ($this->debugLevel>1)
1452
			{
1453
				$endtime = microtime(true) - $starttime;
1454
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " getHeaders call took : ".$endtime.' for FolderID:'.$_folderName);
1455
			}
1456
		}
1457
		if ($_id == NULL && $this->debugLevel>1)  ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." found :". count($rv_messages['header']));
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $_id of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1458
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Result:'.array2string($rv_messages));
1459
		$messagelist = array();
1460
		if (!isset($rv_messages['header'])||empty($rv_messages['header'])) return $messagelist;
1461
		//if ($_returnModHash) $messageFolderHash = array();
1462
		foreach ((array)$rv_messages['header'] as $k => $vars)
1463
		{
1464 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to process:'.$vars['uid'].' Subject:'.$vars['subject']);
1465
			$headers[$vars['uid']] = $vars;
1466 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' MailID:'.$k.'->'.array2string($vars));
1467
			if (!empty($vars['deleted'])) continue; // cut of deleted messages
1468
			if ($cutoffdate && $vars['date'] < $cutoffdate) continue; // message is out of range for cutoffdate, ignore it
1 ignored issue
show
Bug Best Practice introduced by
The expression $cutoffdate of type integer|null is loosely compared to true; 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...
1469 View Code Duplication
			if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to report:'.$vars['uid'].' Subject:'.$vars['subject']);
1470
			$mess = $return_all_headers ? $vars : array();
1471
			$mess["mod"] = self::doFlagsMod($vars).$vars['date'];
1472
			$mess["id"] = $vars['uid'];
1473
			// 'seen' aka 'read' is the only flag we want to know about
1474
			$mess["flags"] = 0;
1475
			// outlook supports additional flags, set them to 0
1476
			if($vars["seen"]) $mess["flags"] = 1;
1477 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($mess));
1478
			$messagelist[$vars['uid']] = $mess;
1479
			unset($mess);
1480
		}
1481
		if ($this->debugLevel>1)
1482
		{
1483
			$endtime = microtime(true) - $gstarttime;
1484
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " total time used : ".$endtime.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id);
1485
		}
1486
		return $messagelist;
1487
	}
1488
1489
	/**
1490
	 * Prepare headeinfo on a message to return some standardized string to tell which flags are set for a message
1491
	 *
1492
	 * AS currently only supports flagged, answered/replied and forwarded flags.
1493
	 * Seen/read is in under flags key of stat!
1494
	 *
1495
	 * @param array $headerFlags  - array to process, a full return array from getHeaders
1496
	 * @link https://sourceforge.net/p/zimbrabackend/code/HEAD/tree/zimbra-backend/branches/z-push-2/zimbra.php#l11652
1497
	 * @return string string of a representation of supported flags
1498
	 */
1499
	static function doFlagsMod($headerFlags)
1500
	{
1501
		$flags = 'nnn';
1502
		if ($headerFlags['flagged']) $flags[0] = 'f';
1503
		if ($headerFlags['answered']) $flags[1] = 'a';
1504
		if ($headerFlags['forwarded']) $flags[2] = 'f';
1505
		//ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.'('.array2string($headerFlags).') returning '.array2string($flags));
1506
		return $flags;
1507
	}
1508
1509
	/**
1510
	 * Search mailbox for a given pattern
1511
	 *
1512
	 * @param object $_searchquery holds information specifying the query with GetDataArray it holds
1513
	 * 		[searchname] => MAILBOX
1514
	 * 		[searchfolderid] => 101000000000
1515
	 * 		[searchfreetext] => somesearchtexgt
1516
	 * 		[searchdatereceivedgreater] => 1
1517
	 * 		[searchvaluegreater] => 2015-07-06T22:00:00.000Z
1518
	 * 		[searchdatereceivedless] => 1
1519
	 * 		[searchvalueless] => 2015-07-14T15:11:00.000Z
1520
	 * 		[searchrebuildresults] => 1
1521
	 * 		[searchrange] => 0-99
1522
	 * 		[bodypref] => Array([1] => BodyPreference Object([unsetdata:protected] => Array([truncationsize] => [allornone] => [preview] => )[SO_internalid:StateObject:private] => [data:protected] =>
1523
	 * 			 Array([truncationsize] => 2147483647)[changed:protected] => 1))
1524
	 * 				[mimesupport] => 2)
1525
	 * @return array(["range"] = $_searchquery->GetSearchRange(), ['searchtotal'] = count of results,
1526
	 *			array("class" => "Email",
1527
	 *					"longid" => folderid.':'.uid',
1528
	 *					"folderid"	=> folderid,
1529
	 *					), ....
1530
	 *		)
1531
	 */
1532
	public function getSearchResultsMailbox($_searchquery)
1533
	{
1534
		//$this->debugLevel=1;
1535
		$searchquery=$_searchquery->GetDataArray();
1536
		if (!is_array($searchquery)) return array();
1537 View Code Duplication
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($searchquery));
1538
1539
		if (isset($searchquery['searchrebuildresults'])) {
1540
			$rebuildresults = $searchquery['searchrebuildresults'];
1541
		} else {
1542
			$rebuildresults = false;
1543
		}
1544
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'RebuildResults ['.$rebuildresults.']' );
1545
1546
		if (isset($searchquery['deeptraversal'])) {
1547
			$deeptraversal = $searchquery['deeptraversal'];
1548
		} else {
1549
			$deeptraversal = false;
1550
		}
1551
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'DeepTraversal ['.$deeptraversal.']' );
1552
1553
		if (isset($searchquery['searchrange'])) {
1554
			$range = explode("-",$_searchquery->GetSearchRange());
1555
			$start =$range[0] + 1;
1556
			$limit = $range[1] - $range[0] + 1;
1557
		} else {
1558
			$range = false;
1559
		}
1560
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'Range ['.print_r($range, true).']' );
1561
1562
		//foreach($searchquery['query'] as $k => $value) {
1563
		//	$query = $value;
1564
		//}
1565
		if (isset($searchquery['searchfolderid']))
1566
		{
1567
			$folderid = $searchquery['searchfolderid'];
1568
		}
1569
/*
1570
		// other types may be possible - we support quicksearch first (freeText in subject and from (or TO in Sent Folder))
1571
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[self::$profileID]))
1572
		{
1573
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*10);
1574
			if (!isset(Mail::$supportsORinQuery[self::$profileID])) Mail::$supportsORinQuery[self::$profileID]=true;
1575
		}
1576
*/
1577
		if (isset($searchquery['searchfreetext']))
1578
		{
1579
			$searchText = $searchquery['searchfreetext'];
1580
		}
1581
		if (!$folderid)
1582
		{
1583
			$_folderName = ($this->mail->sessionData['mailbox']?$this->mail->sessionData['mailbox']:'INBOX');
1584
			$folderid = $this->createID($account=0,$_folderName);
1585
		}
1586
		$rv = $this->splitID($folderid,$account,$_folderName,$id);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rv is correct as $this->splitID($folderid...unt, $_folderName, $id) (which targets mail_zpush::splitID()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1587
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ProfileID:'.self::$profileID.' FolderID:'.$folderid.' Foldername:'.$_folderName);
1588
		$this->_connect($account);
1589
		// this should not be needed ???
1590
		Mail::$supportsORinQuery[self::$profileID]=true; // trigger quicksearch (if possible)
1591
		$_filter = array('type'=> (Mail::$supportsORinQuery[self::$profileID]?'quick':'subject'),
1592
						 'string'=> $searchText,
1593
						 'status'=>'any'
1594
						);
1595
1596
		if (isset($searchquery['searchdatereceivedgreater']) || isset($searchquery['searchdatereceivedless']))
1597
		{
1598
		/*
1599
		 *	We respect only the DATEPART of the RANGE specified
1600
		 * 		[searchdatereceivedgreater] => 1
1601
		 * 		[searchvaluegreater] => 2015-07-06T22:00:00.000Z , SINCE
1602
		 * 		[searchdatereceivedless] => 1
1603
		 * 		[searchvalueless] => 2015-07-14T15:11:00.000Z , BEFORE
1604
		 */
1605
			$_filter['range'] = "BETWEEN";
1606
			list($sincedate,$crap) = explode('T',$searchquery['searchvaluegreater']);
0 ignored issues
show
Unused Code introduced by
The assignment to $crap 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...
1607
			list($beforedate,$crap) = explode('T',$searchquery['searchvalueless']);
0 ignored issues
show
Unused Code introduced by
The assignment to $crap 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...
1608
			$_filter['before'] = date("d-M-Y", Api\DateTime::to($beforedate,'ts'));
1609
			$_filter['since'] = date("d-M-Y", Api\DateTime::to($sincedate,'ts'));
1610
		}
1611
		//$_filter[] = array('type'=>"SINCE",'string'=> date("d-M-Y", $cutoffdate));
1612
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter));
1613
		$rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=($range?$start:1), $_numberOfMessages=($limit?$limit:9999999), $_sort=0, $_reverse=false, $_filter, $_id=NULL);
1614
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages));
1615
		$list=array();
1616
1617
		$cnt = count($rv_messages['header']);
1618
		//$list['status'] = 1;
1619
		$list['searchtotal'] = $cnt;
1620
		$list["range"] = $_searchquery->GetSearchRange();
1621
		foreach((array)$rv_messages['header'] as $i => $vars)
1622
		{
1623
			$list[] = array(
1624
				"class" => "Email",
1625
				"longid" => $folderid.':'.$vars['uid'],
1626
				"folderid"	=> $folderid,
1627
			);
1628
		}
1629
		//error_log(__METHOD__.__LINE__.array2string($list));
1630
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($list));
1631
		return $list;
1632
	}
1633
1634
	/**
1635
	 * Get ID of parent Folder or '0' for folders in root
1636
	 *
1637
	 * @param int $account
1638
	 * @param string $folder
1639
	 * @return string
1640
	 */
1641
	private function getParentID($account,$folder)
1642
	{
1643
		$this->_connect($account);
1644
		if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false);
1645
1646
		$mailFolder = $this->folders[$folder];
1647
		if (!isset($mailFolder)) return false;
1648
		$delimiter = (isset($mailFolder->delimiter)?$mailFolder->delimiter:$this->mail->getHierarchyDelimiter());
1649
		$parent = explode($delimiter,$folder);
1650
		array_pop($parent);
1651
		$parent = implode($delimiter,$parent);
1652
1653
		$id = $parent ? $this->createID($account, $parent) : '0';
1654
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$folder') --> parent=$parent --> $id");
1655
		return $id;
1656
	}
1657
1658
	/**
1659
	 * Get Information about a folder
1660
	 *
1661
	 * @param string $id
1662
	 * @return SyncFolder|boolean false on error
1663
	 */
1664
	public function GetFolder($id)
1665
	{
1666
		static $last_id = null;
1667
		static $folderObj = null;
1668
		if (isset($last_id) && $last_id === $id) return $folderObj;
1669
1670
		try {
1671
			$account = $folder = null;
1672
			$this->splitID($id, $account, $folder);
1673
		}
1674
		catch(Exception $e) {
1675
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' failed for '.$e->getMessage());
1676
			return $folderObj=false;
1677
		}
1678
		$this->_connect($account);
1679
		if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false);
1680
1681
		$mailFolder = $this->folders[$folder];
1682
		if (!isset($mailFolder)) return $folderObj=false;
1683
1684
		$folderObj = new SyncFolder();
1685
		$folderObj->serverid = $id;
1686
		$folderObj->parentid = $this->getParentID($account,$folder);
1687
		$folderObj->displayname = $mailFolder->shortDisplayName;
1688
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." ID: $id, Account:$account, Folder:$folder");
1689
		// get folder-type
1690
		foreach($this->folders as $inbox => $mailFolder) break;
1691
		if ($folder == $inbox)
0 ignored issues
show
Bug introduced by
The variable $inbox seems to be defined by a foreach iteration on line 1690. 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...
1692
		{
1693
			$folderObj->type = SYNC_FOLDER_TYPE_INBOX;
1694
		}
1695
		elseif($this->mail->isDraftFolder($folder, false, true))
1696
		{
1697
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isDraft');
1698
			$folderObj->type = SYNC_FOLDER_TYPE_DRAFTS;
1699
			$folderObj->parentid = 0; // required by devices
1700
		}
1701
		elseif($this->mail->isTrashFolder($folder, false, true))
1702
		{
1703
			$folderObj->type = SYNC_FOLDER_TYPE_WASTEBASKET;
1704
			$this->_wasteID = $folder;
1705
			//error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID);
1706
			$folderObj->parentid = 0; // required by devices
1707
		}
1708
		elseif($this->mail->isSentFolder($folder, false, true))
1709
		{
1710
			$folderObj->type = SYNC_FOLDER_TYPE_SENTMAIL;
1711
			$folderObj->parentid = 0; // required by devices
1712
			$this->_sentID = $folder;
1713
			//error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID);
1714
		}
1715
		elseif($this->mail->isOutbox($folder, false, true))
1716
		{
1717
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOutbox');
1718
			$folderObj->type = SYNC_FOLDER_TYPE_OUTBOX;
1719
			$folderObj->parentid = 0; // required by devices
1720
		}
1721
		else
1722
		{
1723
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOther Folder'.$folder);
1724
			$folderObj->type = SYNC_FOLDER_TYPE_USER_MAIL;
1725
		}
1726
1727
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($id) --> $folder --> type=$folderObj->type, parentID=$folderObj->parentid, displayname=$folderObj->displayname");
1728
		return $folderObj;
1729
	}
1730
1731
	/**
1732
	 * Return folder stats. This means you must return an associative array with the
1733
	 * following properties:
1734
	 *
1735
	 * "id" => The server ID that will be used to identify the folder. It must be unique, and not too long
1736
	 *		 How long exactly is not known, but try keeping it under 20 chars or so. It must be a string.
1737
	 * "parent" => The server ID of the parent of the folder. Same restrictions as 'id' apply.
1738
	 * "mod" => This is the modification signature. It is any arbitrary string which is constant as long as
1739
	 *		  the folder has not changed. In practice this means that 'mod' can be equal to the folder name
1740
	 *		  as this is the only thing that ever changes in folders. (the type is normally constant)
1741
	 *
1742
	 * @return array with values for keys 'id', 'mod' and 'parent'
1743
	 */
1744
	public function StatFolder($id)
1745
	{
1746
		$folder = $this->GetFolder($id);
1747
1748
		$stat = array(
1749
			'id'     => $id,
1750
			'mod'    => $folder->displayname,
1751
			'parent' => $folder->parentid,
1752
		);
1753
1754
		return $stat;
1755
	}
1756
1757
1758
	/**
1759
	 * Return a changes array
1760
	 *
1761
	 * if changes occurr default diff engine computes the actual changes
1762
	 *
1763
	 * @param string $folderid
1764
	 * @param string &$syncstate on call old syncstate, on return new syncstate
1765
	 * @return array|boolean false if $folderid not found, array() if no changes or array(array("type" => "fakeChange"))
1766
	 */
1767
	function AlterPingChanges($folderid, &$syncstate)
1768
	{
1769
		$account = $folder = null;
1770
		$this->splitID($folderid, $account, $folder);
1771
		if (is_numeric($account)) $type = 'mail';
1772
1773
		if ($type != 'mail') return false;
1774
1775
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
1776
1777
        $this->mail->reopen($folder);
1778
1779
		if (!($status = $this->mail->getFolderStatus($folder,$ignoreStatusCache=true)))
1780
		{
1781
            ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not stat folder $folder ");
1782
            return false;
1783
        }
1784
		$syncstate = "M:". $status['messages'] ."-R:". $status['recent'] ."-U:". $status['unseen']."-NUID:".$status['uidnext']."-UIDV:".$status['uidvalidity'];
1785
1786
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($folderid, ...) $folder ($account) returning ".array2string($syncstate));
1787
		return array();
1788
	}
1789
1790
	/**
1791
	 * Should return a wastebasket folder if there is one. This is used when deleting
1792
	 * items; if this function returns a valid folder ID, then all deletes are handled
1793
	 * as moves and are sent to your backend as a move. If it returns FALSE, then deletes
1794
	 * are always handled as real deletes and will be sent to your importer as a DELETE
1795
	 */
1796
	function GetWasteBasket()
1797
	{
1798
		$this->_connect($this->account);
1799
		$id = $this->createID($account=0, $this->_wasteID);
1800
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__."() account=$this->account returned $id for folder $this->_wasteID");
1801
		return $id;
1802
	}
1803
1804
    /**
1805
     * Called when the user has requested to delete (really delete) a message. Usually
1806
     * this means just unlinking the file its in or somesuch. After this call has succeeded, a call to
1807
     * GetMessageList() should no longer list the message. If it does, the message will be re-sent to the mobile
1808
     * as it will be seen as a 'new' item. This means that if this method is not implemented, it's possible to
1809
     * delete messages on the PDA, but as soon as a sync is done, the item will be resynched to the mobile
1810
     *
1811
     * @param string              $folderid             id of the folder
1812
     * @param string              $id                   id of the message
1813
     * @param ContentParameters   $contentParameters
1814
     *
1815
     * @access public
1816
     * @return boolean                      status of the operation
1817
     * @throws StatusException              could throw specific SYNC_STATUS_* exceptions
1818
     */
1819
    public function DeleteMessage($folderid, $id, $contentParameters)
1820
	{
1821
		unset($contentParameters);	// not used, but required by function signature
1822
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: (fid: '$folderid'  id: '$id' )");
1823
		/*
1824
		$this->imap_reopenFolder($folderid);
1825
		$s1 = @imap_delete ($this->_mbox, $id, FT_UID);
1826
		$s11 = @imap_setflag_full($this->_mbox, $id, "\\Deleted", FT_UID);
1827
		$s2 = @imap_expunge($this->_mbox);
1828
		*/
1829
		// we may have to split folderid
1830
		$account = $folder = null;
1831
		$this->splitID($folderid, $account, $folder);
1832
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' '.$folderid.'->'.$folder);
1833
		$_messageUID = (array)$id;
1834
1835
		$this->_connect($this->account);
1836
		$this->mail->reopen($folder);
1837
		try
1838
		{
1839
			$rv = $this->mail->deleteMessages($_messageUID, $folder);
1840
		}
1841
		catch (Api\Exception $e)
1842
		{
1843
			$error = $e->getMessage();
1844
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." $_messageUID, $folder ->".$error);
1845
			// if the server thinks the message does not exist report deletion as success
1846
			if (stripos($error,'[NONEXISTENT]')!==false) return true;
1847
			return false;
1848
		}
1849
1850
		// this may be a bit rude, it may be sufficient that GetMessageList does not list messages flagged as deleted
1851
		if ($this->mail->mailPreferences['deleteOptions'] == 'mark_as_deleted')
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
1852
		{
1853
			// ignore mark as deleted -> Expunge!
1854
			//$this->mail->icServer->expunge(); // do not expunge as GetMessageList does not List messages flagged as deleted
1855
		}
1856
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: $rv");
1857
1858
		return $rv;
1859
	}
1860
1861
    /**
1862
     * Changes the 'read' flag of a message on disk. The $flags
1863
     * parameter can only be '1' (read) or '0' (unread). After a call to
1864
     * SetReadFlag(), GetMessageList() should return the message with the
1865
     * new 'flags' but should not modify the 'mod' parameter. If you do
1866
     * change 'mod', simply setting the message to 'read' on the mobile will trigger
1867
     * a full resync of the item from the server.
1868
     *
1869
     * @param string              $folderid            id of the folder
1870
     * @param string              $id                  id of the message
1871
     * @param int                 $flags               read flag of the message
1872
     * @param ContentParameters   $contentParameters
1873
     *
1874
     * @access public
1875
     * @return boolean                      status of the operation
1876
     * @throws StatusException              could throw specific SYNC_STATUS_* exceptions
1877
     */
1878
    public function SetReadFlag($folderid, $id, $flags, $contentParameters)
1879
	{
1880
		unset($contentParameters);	// not used, but required by function signature
1881
		// ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag: (fid: '$folderid'  id: '$id'  flags: '$flags' )");
1882
		$account = $folder = null;
1883
		$this->splitID($folderid, $account, $folder);
1884
1885
		$_messageUID = (array)$id;
1886
		$this->_connect($this->account);
1887
		$rv = $this->mail->flagMessages((($flags) ? "read" : "unread"), $_messageUID,$folder);
1888
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags) ? "read" : "unread") . "-->". $rv);
1889
1890
		return $rv;
1891
	}
1892
1893
	/**
1894
	 *  Creates or modifies a folder
1895
	 *
1896
	 * @param string $id of the parent folder
1897
	 * @param string $oldid => if empty -> new folder created, else folder is to be renamed
1898
	 * @param string $displayname => new folder name (to be created, or to be renamed to)
1899
	 * @param string $type folder type, ignored in IMAP
1900
	 *
1901
	 * @return array|boolean stat array or false on error
1902
	 */
1903
	public function ChangeFolder($id, $oldid, $displayname, $type)
1904
	{
1905
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$id', '$oldid', '$displayname', $type) NOT supported!");
1906
		return false;
1907
	}
1908
1909
	/**
1910
	 * Deletes (really delete) a Folder
1911
	 *
1912
	 * @param string $parentid of the folder to delete
1913
	 * @param string $id of the folder to delete
1914
	 *
1915
	 * @return
1916
	 * @TODO check what is to be returned
1917
	 */
1918
	public function DeleteFolder($parentid, $id)
1919
	{
1920
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$parentid', '$id') NOT supported!");
1921
		return false;
1922
	}
1923
1924
	/**
1925
	 * modify olflags (outlook style) flag of a message
1926
	 *
1927
	 * @param $folderid
1928
	 * @param $id
1929
	 * @param $flags
1930
	 *
1931
	 *
1932
	 * @DESC The $flags parameter must contains the poommailflag Object
1933
	 */
1934
	function ChangeMessageFlag($folderid, $id, $flags)
1935
	{
1936
		$_messageUID = (array)$id;
1937
		$this->_connect($this->account);
1938
		$account = $folder = null;
1939
		$this->splitID($folderid, $account, $folder);
1940
		$rv = $this->mail->flagMessages((($flags->flagstatus == 2) ? "flagged" : "unflagged"), $_messageUID,$folder);
1941
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetFlaggedFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags->flagstatus == 2) ? "flagged" : "unflagged") . "-->". $rv);
1942
1943
		return $rv;
1944
	}
1945
1946
	/**
1947
	 * Create a max. 32 hex letter ID, current 20 chars are used
1948
	 *
1949
	 * @param int $account mail account id
1950
	 * @param string $folder
1951
	 * @param int $id =0
1952
	 * @return string
1953
	 * @throws Api\Exception\WrongParameter
1954
	 */
1955
	private function createID($account,$folder,$id=0)
1956
	{
1957
		if (!is_numeric($folder))
1958
		{
1959
			// convert string $folder in numeric id
1960
			$folder = $this->folder2hash($account,$f=$folder);
1961
		}
1962
1963
		$str = $this->backend->createID($account, $folder, $id);
1964
1965 View Code Duplication
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($account,'$f',$id) type=$account, folder=$folder --> '$str'");
1966
1967
		return $str;
1968
	}
1969
1970
	/**
1971
	 * Split an ID string into $app, $folder and $id
1972
	 *
1973
	 * @param string $str
1974
	 * @param int &$account mail account id
1975
	 * @param string &$folder
1976
	 * @param int &$id=null
1977
	 * @throws Api\Exception\WrongParameter
1978
	 */
1979
	private function splitID($str,&$account,&$folder,&$id=null)
1980
	{
1981
		$this->backend->splitID($str, $account, $folder, $id);
1982
1983
		// convert numeric folder-id back to folder name
1984
		$folder = $this->hash2folder($account,$f=$folder);
1985
1986 View Code Duplication
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$str','$account','$folder',$id)");
1987
	}
1988
1989
	/**
1990
	 * Methods to convert (hierarchical) folder names to nummerical id's
1991
	 *
1992
	 * This is currently done by storing a serialized array in the device specific
1993
	 * state directory.
1994
	 */
1995
1996
	/**
1997
	 * Convert folder string to nummeric hash
1998
	 *
1999
	 * @param int $account
2000
	 * @param string $folder
2001
	 * @return int
2002
	 */
2003
	private function folder2hash($account,$folder)
2004
	{
2005
		if(!isset($this->folderHashes)) $this->readFolderHashes();
2006
2007
		if (($index = array_search($folder, (array)$this->folderHashes[$account])) === false)
2008
		{
2009
			// new hash
2010
			$this->folderHashes[$account][] = $folder;
2011
			$index = array_search($folder, (array)$this->folderHashes[$account]);
2012
2013
			// maybe later storing in on class destruction only
2014
			$this->storeFolderHashes();
2015
		}
2016
		return $index;
2017
	}
2018
2019
	/**
2020
	 * Convert numeric hash to folder string
2021
	 *
2022
	 * @param int $account
2023
	 * @param int $index
2024
	 * @return string NULL if not used so far
2025
	 */
2026
	private function hash2folder($account,$index)
2027
	{
2028
		if(!isset($this->folderHashes)) $this->readFolderHashes();
2029
2030
		return isset($this->folderHashes[$account]) ? $this->folderHashes[$account][$index] : null;
2031
	}
2032
2033
	private $folderHashes;
2034
2035
	/**
2036
	 * Statemaschine instance used to store folders
2037
	 *
2038
	 * @var activesync_statemaschine
2039
	 */
2040
	private $fh_state_maschine;
2041
2042
	/**
2043
	 * state_type (and _key) used to store folder hashes
2044
	 */
2045
	const FOLDER_STATE_TYPE = 'folder_hashes';
2046
2047
	/**
2048
	 * Read hashfile from state dir
2049
	 */
2050
	private function readFolderHashes()
2051
	{
2052
		if (!isset($this->fh_state_maschine))
2053
		{
2054
			$this->fh_state_maschine = new activesync_statemachine($this->backend);
2055
		}
2056
		try {
2057
			$this->folderHashes = $this->fh_state_maschine->getState(Request::GetDeviceID(),
2058
				self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0);
2059
		}
2060
		catch (Exception $e) {
2061
			unset($e);
2062
			if ((file_exists($file = $this->hashFile()) || file_exists($file = $this->hashFile(true))) &&
2063
				($hashes = file_get_contents($file)))
2064
			{
2065
				$this->folderHashes = json_decode($hashes,true);
2066
				// fallback in case hashes have been serialized instead of being json-encoded
2067
				if (json_last_error()!=JSON_ERROR_NONE)
2068
				{
2069
					//error_log(__METHOD__.__LINE__." error decoding with json");
2070
					$this->folderHashes = unserialize($hashes);
2071
				}
2072
				// store folder-hashes to state
2073
				$this->storeFolderHashes();
2074
			}
2075
			else
2076
			{
2077
				$this->folderHashes = array();
2078
			}
2079
		}
2080
	}
2081
2082
	/**
2083
	 * Store hashfile via state-maschine
2084
	 *
2085
	 * return int|boolean false on error
2086
	 */
2087
	private function storeFolderHashes()
2088
	{
2089
		if (!isset($this->fh_state_maschine))
2090
		{
2091
			$this->fh_state_maschine = new activesync_statemachine($this->backend);
2092
		}
2093
		try {
2094
			$this->fh_state_maschine->setState($this->folderHashes, Request::GetDeviceID(),
2095
				self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0);
2096
		}
2097
		catch (Exception $e) {
2098
			_egw_log_exception($e);
2099
			return false;
2100
		}
2101
		return true;
2102
	}
2103
2104
	/**
2105
	 * Get name of hashfile in state dir
2106
	 *
2107
	 * New z-push 2 directory is in activesync_statemachine::getDeviceDirectory.
2108
	 * On request this function also returns, but never creates (!), old z-push 1 directory.
2109
	 *
2110
	 * @param boolean $old =false true: return old / pre-15 hash-file
2111
	 * @throws Api\Exception\AssertionFailed
2112
	 */
2113
	private function hashFile($old=false)
2114
	{
2115
		if (!($dev_id=Request::GetDeviceID()))
2116
		{
2117
			throw new Api\Exception\AssertionFailed(__METHOD__."() no DeviceID set!");
2118
		}
2119
		if ($old)
2120
		{
2121
			return STATE_DIR.$dev_id.'/'.$dev_id.'.hashes';
2122
		}
2123
		$dir = activesync_statemachine::getDeviceDirectory($dev_id);
2124
2125
		return $dir.'/'.$dev_id.'.hashes';
2126
	}
2127
}
2128