Completed
Push — 16.1 ( b39e15...d98aac )
by Klaus
18:52
created

mail_zpush::createID()   D

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 1
Ratio 7.14 %

Importance

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

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

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

Loading history...
1412
			return array();
1413
		}
1414
	}
1415
1416
	/**
1417
	 * Fetch headers for one or all mail of a folder using optional cutoffdate
1418
	 *
1419
	 * Headers of last fetchMessage call of complate folder are cached in static $headers,
1420
	 * to allow to use them without fetching them again.
1421
	 * Next call clears cache
1422
	 *
1423
	 * @param int $folderid
1424
	 * @param int $cutoffdate timestamp with cutoffdate
1425
	 * @param string $_id =null uid of single message to fetch
1426
	 * @param boolean $return_all_headers =false true: additinal contain all headers eg. "subject"
1427
	 * @return array uid => array StatMessage($folderid, $_id)
1428
	 */
1429
	private function fetchMessages($folderid, $cutoffdate=NULL, $_id=NULL, $return_all_headers=false)
1430
	{
1431
		static $headers = array();
1432
1433
		if ($this->debugLevel>1) $gstarttime = microtime (true);
1434
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__);
1435
		$rv_messages = array();
1436
		// if the message is still available within the class, we use it instead of fetching it again
1437
		if ($_id && isset($headers[$_id]) && is_array($headers[$_id]))
1438
		{
1439
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." the message ".$_id[0]." is still available within the class, we use it instead of fetching it again");
1440
			$rv_messages = array('header'=>array($headers[$_id]));
1441
		}
1442
		else
1443
		{
1444
			$headers = array();	// clear cache to not use too much memory
1445
1446
			if ($this->debugLevel>1) $starttime = microtime (true);
1447
			$this->_connect($this->account);
1448 View Code Duplication
			if ($this->debugLevel>1)
1449
			{
1450
				$endtime = microtime(true) - $starttime;
1451
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " connect took : ".$endtime.' for account:'.$this->account);
1452
			}
1453
			$messagelist = $_filter = array();
1454
			// if not connected, any further action must fail
1455
			if (!empty($cutoffdate)) $_filter = array('status'=>array('UNDELETED'),'range'=>"SINCE",'date'=> date("d-M-Y", $cutoffdate));
1456
			if ($this->debugLevel>1) $starttime = microtime (true);
1457
			$account = $_folderName = $id = null;
1458
			$this->splitID($folderid,$account,$_folderName,$id);
1459 View Code Duplication
			if ($this->debugLevel>1)
1460
			{
1461
				$endtime = microtime(true) - $starttime;
1462
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " splitID took : ".$endtime.' for FolderID:'.$folderid);
1463
			}
1464
			if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id);
1465
			if ($this->debugLevel>1) $starttime = microtime (true);
1466
			$_numberOfMessages = (empty($cutoffdate)?250:99999);
1467
			$rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=1, $_numberOfMessages, $_sort=0, $_reverse=false, $_filter, $_id);
1468 View Code Duplication
			if ($this->debugLevel>1)
1469
			{
1470
				$endtime = microtime(true) - $starttime;
1471
				ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " getHeaders call took : ".$endtime.' for FolderID:'.$_folderName);
1472
			}
1473
		}
1474
		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...
1475
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Result:'.array2string($rv_messages));
1476
		$messagelist = array();
1477
		if (!isset($rv_messages['header'])||empty($rv_messages['header'])) return $messagelist;
1478
		//if ($_returnModHash) $messageFolderHash = array();
1479
		foreach ((array)$rv_messages['header'] as $k => $vars)
1480
		{
1481 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to process:'.$vars['uid'].' Subject:'.$vars['subject']);
1482
			$headers[$vars['uid']] = $vars;
1483 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' MailID:'.$k.'->'.array2string($vars));
1484
			if (!empty($vars['deleted'])) continue; // cut of deleted messages
1485
			if ($cutoffdate && $vars['date'] < $cutoffdate) continue; // message is out of range for cutoffdate, ignore it
1486 View Code Duplication
			if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to report:'.$vars['uid'].' Subject:'.$vars['subject']);
1487
			$mess = $return_all_headers ? $vars : array();
1488
			$mess["mod"] = self::doFlagsMod($vars).$vars['date'];
1489
			$mess["id"] = $vars['uid'];
1490
			// 'seen' aka 'read' is the only flag we want to know about
1491
			$mess["flags"] = 0;
1492
			// outlook supports additional flags, set them to 0
1493
			if($vars["seen"]) $mess["flags"] = 1;
1494 View Code Duplication
			if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($mess));
1495
			$messagelist[$vars['uid']] = $mess;
1496
			unset($mess);
1497
		}
1498
		if ($this->debugLevel>1)
1499
		{
1500
			$endtime = microtime(true) - $gstarttime;
1501
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " total time used : ".$endtime.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id);
1502
		}
1503
		return $messagelist;
1504
	}
1505
1506
	/**
1507
	 * Prepare headeinfo on a message to return some standardized string to tell which flags are set for a message
1508
	 *
1509
	 * AS currently only supports flagged, answered/replied and forwarded flags.
1510
	 * Seen/read is in under flags key of stat!
1511
	 *
1512
	 * @param array $headerFlags  - array to process, a full return array from getHeaders
1513
	 * @link https://sourceforge.net/p/zimbrabackend/code/HEAD/tree/zimbra-backend/branches/z-push-2/zimbra.php#l11652
1514
	 * @return string string of a representation of supported flags
1515
	 */
1516
	static function doFlagsMod($headerFlags)
1517
	{
1518
		$flags = 'nnn';
1519
		if ($headerFlags['flagged']) $flags[0] = 'f';
1520
		if ($headerFlags['answered']) $flags[1] = 'a';
1521
		if ($headerFlags['forwarded']) $flags[2] = 'f';
1522
		//ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.'('.array2string($headerFlags).') returning '.array2string($flags));
1523
		return $flags;
1524
	}
1525
1526
	/**
1527
	 * Search mailbox for a given pattern
1528
	 *
1529
	 * @param object $_searchquery holds information specifying the query with GetDataArray it holds
1530
	 * 		[searchname] => MAILBOX
1531
	 * 		[searchfolderid] => 101000000000
1532
	 * 		[searchfreetext] => somesearchtexgt
1533
	 * 		[searchdatereceivedgreater] => 1
1534
	 * 		[searchvaluegreater] => 2015-07-06T22:00:00.000Z
1535
	 * 		[searchdatereceivedless] => 1
1536
	 * 		[searchvalueless] => 2015-07-14T15:11:00.000Z
1537
	 * 		[searchrebuildresults] => 1
1538
	 * 		[searchrange] => 0-99
1539
	 * 		[bodypref] => Array([1] => BodyPreference Object([unsetdata:protected] => Array([truncationsize] => [allornone] => [preview] => )[SO_internalid:StateObject:private] => [data:protected] =>
1540
	 * 			 Array([truncationsize] => 2147483647)[changed:protected] => 1))
1541
	 * 				[mimesupport] => 2)
1542
	 * @return array(["range"] = $_searchquery->GetSearchRange(), ['searchtotal'] = count of results,
1543
	 *			array("class" => "Email",
1544
	 *					"longid" => folderid.':'.uid',
1545
	 *					"folderid"	=> folderid,
1546
	 *					), ....
1547
	 *		)
1548
	 */
1549
	public function getSearchResultsMailbox($_searchquery)
1550
	{
1551
		//$this->debugLevel=1;
1552
		$searchquery=$_searchquery->GetDataArray();
1553
		if (!is_array($searchquery)) return array();
1554 View Code Duplication
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($searchquery));
1555
1556
		if (isset($searchquery['searchrebuildresults'])) {
1557
			$rebuildresults = $searchquery['searchrebuildresults'];
1558
		} else {
1559
			$rebuildresults = false;
1560
		}
1561
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'RebuildResults ['.$rebuildresults.']' );
1562
1563
		if (isset($searchquery['deeptraversal'])) {
1564
			$deeptraversal = $searchquery['deeptraversal'];
1565
		} else {
1566
			$deeptraversal = false;
1567
		}
1568
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'DeepTraversal ['.$deeptraversal.']' );
1569
1570
		if (isset($searchquery['searchrange'])) {
1571
			$range = explode("-",$_searchquery->GetSearchRange());
1572
			$start =$range[0] + 1;
1573
			$limit = $range[1] - $range[0] + 1;
1574
		} else {
1575
			$range = false;
1576
		}
1577
		if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,  'Range ['.print_r($range, true).']' );
1578
1579
		//foreach($searchquery['query'] as $k => $value) {
1580
		//	$query = $value;
1581
		//}
1582
		if (isset($searchquery['searchfolderid']))
1583
		{
1584
			$folderid = $searchquery['searchfolderid'];
1585
		}
1586
/*
1587
		// other types may be possible - we support quicksearch first (freeText in subject and from (or TO in Sent Folder))
1588
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[self::$profileID]))
1589
		{
1590
			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);
1591
			if (!isset(Mail::$supportsORinQuery[self::$profileID])) Mail::$supportsORinQuery[self::$profileID]=true;
1592
		}
1593
*/
1594
		if (isset($searchquery['searchfreetext']))
1595
		{
1596
			$searchText = $searchquery['searchfreetext'];
1597
		}
1598
		if (!$folderid)
1599
		{
1600
			$_folderName = ($this->mail->sessionData['mailbox']?$this->mail->sessionData['mailbox']:'INBOX');
1601
			$folderid = $this->createID($account=0,$_folderName);
1602
		}
1603
		$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...
1604
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ProfileID:'.self::$profileID.' FolderID:'.$folderid.' Foldername:'.$_folderName);
1605
		$this->_connect($account);
1606
		// this should not be needed ???
1607
		Mail::$supportsORinQuery[self::$profileID]=true; // trigger quicksearch (if possible)
1608
		$_filter = array('type'=> (Mail::$supportsORinQuery[self::$profileID]?'quick':'subject'),
1609
						 'string'=> $searchText,
1610
						 'status'=>'any'
1611
						);
1612
1613
		if (isset($searchquery['searchdatereceivedgreater']) || isset($searchquery['searchdatereceivedless']))
1614
		{
1615
		/*
1616
		 *	We respect only the DATEPART of the RANGE specified
1617
		 * 		[searchdatereceivedgreater] => 1
1618
		 * 		[searchvaluegreater] => 2015-07-06T22:00:00.000Z , SINCE
1619
		 * 		[searchdatereceivedless] => 1
1620
		 * 		[searchvalueless] => 2015-07-14T15:11:00.000Z , BEFORE
1621
		 */
1622
			$_filter['range'] = "BETWEEN";
1623
			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...
1624
			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...
1625
			$_filter['before'] = date("d-M-Y", Api\DateTime::to($beforedate,'ts'));
1626
			$_filter['since'] = date("d-M-Y", Api\DateTime::to($sincedate,'ts'));
1627
		}
1628
		//$_filter[] = array('type'=>"SINCE",'string'=> date("d-M-Y", $cutoffdate));
1629
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter));
1630
		$rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=($range?$start:1), $_numberOfMessages=($limit?$limit:9999999), $_sort=0, $_reverse=false, $_filter, $_id=NULL);
1631
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages));
1632
		$list=array();
1633
1634
		$cnt = count($rv_messages['header']);
1635
		//$list['status'] = 1;
1636
		$list['searchtotal'] = $cnt;
1637
		$list["range"] = $_searchquery->GetSearchRange();
1638
		foreach((array)$rv_messages['header'] as $i => $vars)
1639
		{
1640
			$list[] = array(
1641
				"class" => "Email",
1642
				"longid" => $folderid.':'.$vars['uid'],
1643
				"folderid"	=> $folderid,
1644
			);
1645
		}
1646
		//error_log(__METHOD__.__LINE__.array2string($list));
1647
		//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($list));
1648
		return $list;
1649
	}
1650
1651
	/**
1652
	 * Get ID of parent Folder or '0' for folders in root
1653
	 *
1654
	 * @param int $account
1655
	 * @param string $folder
1656
	 * @return string
1657
	 */
1658
	private function getParentID($account,$folder)
1659
	{
1660
		$this->_connect($account);
1661
		if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false);
1662
1663
		$mailFolder = $this->folders[$folder];
1664
		if (!isset($mailFolder)) return false;
1665
		$delimiter = (isset($mailFolder->delimiter)?$mailFolder->delimiter:$this->mail->getHierarchyDelimiter());
1666
		$parent = explode($delimiter,$folder);
1667
		array_pop($parent);
1668
		$parent = implode($delimiter,$parent);
1669
1670
		$id = $parent && $this->folders[$parent] ? $this->createID($account, $parent) : '0';
1671
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$folder') --> parent=$parent --> $id");
1672
		return $id;
1673
	}
1674
1675
	/**
1676
	 * Get Information about a folder
1677
	 *
1678
	 * @param string $id
1679
	 * @return SyncFolder|boolean false on error
1680
	 */
1681
	public function GetFolder($id)
1682
	{
1683
		static $last_id = null;
1684
		static $folderObj = null;
1685
		if (isset($last_id) && $last_id === $id) return $folderObj;
1686
1687
		try {
1688
			$account = $folder = null;
1689
			$this->splitID($id, $account, $folder);
1690
		}
1691
		catch(Exception $e) {
1692
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' failed for '.$e->getMessage());
1693
			return $folderObj=false;
1694
		}
1695
		$this->_connect($account);
1696
		if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false);
1697
1698
		$mailFolder = $this->folders[$folder];
1699
		if (!isset($mailFolder)) return $folderObj=false;
1700
1701
		$folderObj = new SyncFolder();
1702
		$folderObj->serverid = $id;
1703
		$folderObj->parentid = $this->getParentID($account,$folder);
1704
		$folderObj->displayname = $mailFolder->shortDisplayName;
1705
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." ID: $id, Account:$account, Folder:$folder");
1706
		// get folder-type
1707
		foreach($this->folders as $inbox => $mailFolder) break;
1708
		if ($folder == $inbox)
0 ignored issues
show
Bug introduced by
The variable $inbox seems to be defined by a foreach iteration on line 1707. 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...
1709
		{
1710
			$folderObj->type = SYNC_FOLDER_TYPE_INBOX;
1711
		}
1712
		elseif($this->mail->isDraftFolder($folder, false, true))
1713
		{
1714
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isDraft');
1715
			$folderObj->type = SYNC_FOLDER_TYPE_DRAFTS;
1716
			$folderObj->parentid = 0; // required by devices
1717
		}
1718
		elseif($this->mail->isTrashFolder($folder, false, true))
1719
		{
1720
			$folderObj->type = SYNC_FOLDER_TYPE_WASTEBASKET;
1721
			$this->_wasteID = $folder;
1722
			//error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID);
1723
			$folderObj->parentid = 0; // required by devices
1724
		}
1725
		elseif($this->mail->isSentFolder($folder, false, true))
1726
		{
1727
			$folderObj->type = SYNC_FOLDER_TYPE_SENTMAIL;
1728
			$folderObj->parentid = 0; // required by devices
1729
			$this->_sentID = $folder;
1730
			//error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID);
1731
		}
1732
		elseif($this->mail->isOutbox($folder, false, true))
1733
		{
1734
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOutbox');
1735
			$folderObj->type = SYNC_FOLDER_TYPE_OUTBOX;
1736
			$folderObj->parentid = 0; // required by devices
1737
		}
1738
		else
1739
		{
1740
			//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOther Folder'.$folder);
1741
			$folderObj->type = SYNC_FOLDER_TYPE_USER_MAIL;
1742
		}
1743
1744
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($id) --> $folder --> type=$folderObj->type, parentID=$folderObj->parentid, displayname=$folderObj->displayname");
1745
		return $folderObj;
1746
	}
1747
1748
	/**
1749
	 * Return folder stats. This means you must return an associative array with the
1750
	 * following properties:
1751
	 *
1752
	 * "id" => The server ID that will be used to identify the folder. It must be unique, and not too long
1753
	 *		 How long exactly is not known, but try keeping it under 20 chars or so. It must be a string.
1754
	 * "parent" => The server ID of the parent of the folder. Same restrictions as 'id' apply.
1755
	 * "mod" => This is the modification signature. It is any arbitrary string which is constant as long as
1756
	 *		  the folder has not changed. In practice this means that 'mod' can be equal to the folder name
1757
	 *		  as this is the only thing that ever changes in folders. (the type is normally constant)
1758
	 *
1759
	 * @return array with values for keys 'id', 'mod' and 'parent'
1760
	 */
1761
	public function StatFolder($id)
1762
	{
1763
		$folder = $this->GetFolder($id);
1764
1765
		$stat = array(
1766
			'id'     => $id,
1767
			'mod'    => $folder->displayname,
1768
			'parent' => $folder->parentid,
1769
		);
1770
1771
		return $stat;
1772
	}
1773
1774
1775
	/**
1776
	 * Return a changes array
1777
	 *
1778
	 * if changes occurr default diff engine computes the actual changes
1779
	 *
1780
	 * @param string $folderid
1781
	 * @param string &$syncstate on call old syncstate, on return new syncstate
1782
	 * @return array|boolean false if $folderid not found, array() if no changes or array(array("type" => "fakeChange"))
1783
	 */
1784
	function AlterPingChanges($folderid, &$syncstate)
1785
	{
1786
		$account = $folder = null;
1787
		$this->splitID($folderid, $account, $folder);
1788
		if (is_numeric($account)) $type = 'mail';
1789
1790
		if ($type != 'mail') return false;
1791
1792 View Code Duplication
		if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
1793
		if (!$this->mail->folderIsSelectable($folder))
1794
		{
1795
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not select folder $folder returning fake state");
1796
			$syncstate = "M:".'0'."-R:".'0'."-U:".'0'."-NUID:".'0'."-UIDV:".'0';
1797
			return array();
1798
		}
1799
1800
		$this->mail->reopen($folder);
1801
1802
		if (!($status = $this->mail->getFolderStatus($folder,$ignoreStatusCache=true)))
1803
		{
1804
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not stat folder $folder ");
1805
			return false;
1806
		}
1807
		$syncstate = "M:". $status['messages'] ."-R:". $status['recent'] ."-U:". $status['unseen']."-NUID:".$status['uidnext']."-UIDV:".$status['uidvalidity'];
1808
1809
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($folderid, ...) $folder ($account) returning ".array2string($syncstate));
1810
		return array();
1811
	}
1812
1813
	/**
1814
	 * Should return a wastebasket folder if there is one. This is used when deleting
1815
	 * items; if this function returns a valid folder ID, then all deletes are handled
1816
	 * as moves and are sent to your backend as a move. If it returns FALSE, then deletes
1817
	 * are always handled as real deletes and will be sent to your importer as a DELETE
1818
	 */
1819
	function GetWasteBasket()
1820
	{
1821
		$this->_connect($this->account);
1822
		$id = $this->createID($account=0, $this->_wasteID);
1823
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__."() account=$this->account returned $id for folder $this->_wasteID");
1824
		return $id;
1825
	}
1826
1827
    /**
1828
     * Called when the user has requested to delete (really delete) a message. Usually
1829
     * this means just unlinking the file its in or somesuch. After this call has succeeded, a call to
1830
     * GetMessageList() should no longer list the message. If it does, the message will be re-sent to the mobile
1831
     * as it will be seen as a 'new' item. This means that if this method is not implemented, it's possible to
1832
     * delete messages on the PDA, but as soon as a sync is done, the item will be resynched to the mobile
1833
     *
1834
     * @param string              $folderid             id of the folder
1835
     * @param string              $id                   id of the message
1836
     * @param ContentParameters   $contentParameters
1837
     *
1838
     * @access public
1839
     * @return boolean                      status of the operation
1840
     * @throws StatusException              could throw specific SYNC_STATUS_* exceptions
1841
     */
1842
    public function DeleteMessage($folderid, $id, $contentParameters)
1843
	{
1844
		unset($contentParameters);	// not used, but required by function signature
1845
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: (fid: '$folderid'  id: '$id' )");
1846
		/*
1847
		$this->imap_reopenFolder($folderid);
1848
		$s1 = @imap_delete ($this->_mbox, $id, FT_UID);
1849
		$s11 = @imap_setflag_full($this->_mbox, $id, "\\Deleted", FT_UID);
1850
		$s2 = @imap_expunge($this->_mbox);
1851
		*/
1852
		// we may have to split folderid
1853
		$account = $folder = null;
1854
		$this->splitID($folderid, $account, $folder);
1855
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' '.$folderid.'->'.$folder);
1856
		$_messageUID = (array)$id;
1857
1858
		$this->_connect($this->account);
1859
		$this->mail->reopen($folder);
1860
		try
1861
		{
1862
			$rv = $this->mail->deleteMessages($_messageUID, $folder);
1863
		}
1864
		catch (Api\Exception $e)
1865
		{
1866
			$error = $e->getMessage();
1867
			ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." $_messageUID, $folder ->".$error);
1868
			// if the server thinks the message does not exist report deletion as success
1869
			if (stripos($error,'[NONEXISTENT]')!==false) return true;
1870
			return false;
1871
		}
1872
1873
		// this may be a bit rude, it may be sufficient that GetMessageList does not list messages flagged as deleted
1874
		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...
1875
		{
1876
			// ignore mark as deleted -> Expunge!
1877
			//$this->mail->icServer->expunge(); // do not expunge as GetMessageList does not List messages flagged as deleted
1878
		}
1879
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: $rv");
1880
1881
		return $rv;
1882
	}
1883
1884
    /**
1885
     * Changes the 'read' flag of a message on disk. The $flags
1886
     * parameter can only be '1' (read) or '0' (unread). After a call to
1887
     * SetReadFlag(), GetMessageList() should return the message with the
1888
     * new 'flags' but should not modify the 'mod' parameter. If you do
1889
     * change 'mod', simply setting the message to 'read' on the mobile will trigger
1890
     * a full resync of the item from the server.
1891
     *
1892
     * @param string              $folderid            id of the folder
1893
     * @param string              $id                  id of the message
1894
     * @param int                 $flags               read flag of the message
1895
     * @param ContentParameters   $contentParameters
1896
     *
1897
     * @access public
1898
     * @return boolean                      status of the operation
1899
     * @throws StatusException              could throw specific SYNC_STATUS_* exceptions
1900
     */
1901
    public function SetReadFlag($folderid, $id, $flags, $contentParameters)
1902
	{
1903
		unset($contentParameters);	// not used, but required by function signature
1904
		// ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag: (fid: '$folderid'  id: '$id'  flags: '$flags' )");
1905
		$account = $folder = null;
1906
		$this->splitID($folderid, $account, $folder);
1907
1908
		$_messageUID = (array)$id;
1909
		$this->_connect($this->account);
1910
		$rv = $this->mail->flagMessages((($flags) ? "read" : "unread"), $_messageUID,$folder);
1911
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags) ? "read" : "unread") . "-->". $rv);
1912
1913
		return $rv;
1914
	}
1915
1916
	/**
1917
	 *  Creates or modifies a folder
1918
	 *
1919
	 * @param string $id of the parent folder
1920
	 * @param string $oldid => if empty -> new folder created, else folder is to be renamed
1921
	 * @param string $displayname => new folder name (to be created, or to be renamed to)
1922
	 * @param string $type folder type, ignored in IMAP
1923
	 *
1924
	 * @throws StatusException              could throw specific SYNC_FSSTATUS_* exceptions
1925
	 * @return array|boolean stat array or false on error
1926
	 */
1927
	public function ChangeFolder($id, $oldid, $displayname, $type)
1928
	{
1929
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$id', '$oldid', '$displayname', $type)");
1930
		$account = $parent_id = null;
1931
		$this->splitID($id, $account, $parentFolder, $app);
1932
1933
		$parent_id = $this->folder2hash($account, $parentFolder);
1934
		$old_hash = $oldFolder = null;
1935
1936
		if (empty($oldid))
1937
		{
1938
			$action = 'create';
1939
		}
1940
		else
1941
		{
1942
			$action = 'rename';
1943
			$this->splitID($oldid, $account, $oldFolder, $app);
1944
			$old_hash = $this->folder2hash($account, $oldFolder);
1945
		}
1946
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.":{$action}Folder('$id'=>($parentFolder ($parent_id)), '$oldid'".($oldid?"=>($oldFolder ($old_hash))":'').", '$displayname', $type)");
1947
		$this->_connect($this->account);
1948
		try
1949
		{
1950
			if ($action=='rename')
1951
			{
1952
				$newFolderName = $this->mail->renameFolder($oldFolder, $parentFolder, $displayname);
1953
			}
1954
			elseif ($action=='create')
1955
			{
1956
				$error=null;
1957
				$newFolderName = $this->mail->createFolder($parentFolder, $displayname, $error);
1958
			}
1959
		}
1960
		catch (\Exception $e)
1961
		{
1962
			//throw new Exception(__METHOD__." $action failed for $oldFolder ($action: $displayname) with error:".$e->getMessage());
1963
			return false;
1964
		}
1965
		$newHash = $this->rename_folder_hash($account, $old_hash, $newFolderName);
1966
		$newID = $this->createID($account, $newHash);
1967
1968
		ZLog::Write(LOGLEVEL_DEBUG,":{$action}Folder('$id'=>($parentFolder), '$oldid'".($oldid?"=>($oldFolder)":'').", '$displayname' => $newFolderName (ID:$newID))");
1969
		return $this->StatFolder($newID);
1970
	}
1971
1972
	/**
1973
	 * Deletes (really delete) a Folder
1974
	 *
1975
	 * @param string $id of the folder to delete
1976
	 * @param string $parentid (=false) of the folder to delete, may be false/not set
1977
	 *
1978
	 * @throws StatusException              could throw specific SYNC_FSSTATUS_* exceptions
1979
	 * @return boolean true or false on error
1980
	 */
1981
	public function DeleteFolder($id, $parentid=false)
1982
	{
1983
		$account = $parent_id = $app = null;
1984
		$this->splitID($id, $account, $folder, $app);
1985
		$old_hash = $this->folder2hash($account, $folder);
1986
		if ($parentid) $this->splitID($parentid, $account, $parentfolder, $app);
0 ignored issues
show
Bug Best Practice introduced by
The expression $parentid of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1987
		ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."( '$id (-> $folder)','$parentid ".($parentid?'(->'.$parentfolder.')':'')."') called!");
1988
		$ret = $this->mail->deleteFolder($folder);
1989
		if ($ret) $newHash = $this->rename_folder_hash($account, $old_hash, "##Dele#edFolder#$folder##");
1990
		return $ret;
1991
	}
1992
1993
	/**
1994
	 * modify olflags (outlook style) flag of a message
1995
	 *
1996
	 * @param $folderid
1997
	 * @param $id
1998
	 * @param $flags
1999
	 *
2000
	 *
2001
	 * @DESC The $flags parameter must contains the poommailflag Object
2002
	 */
2003
	function ChangeMessageFlag($folderid, $id, $flags)
2004
	{
2005
		$_messageUID = (array)$id;
2006
		$this->_connect($this->account);
2007
		$account = $folder = null;
2008
		$this->splitID($folderid, $account, $folder);
2009
		$rv = $this->mail->flagMessages((($flags->flagstatus == 2) ? "flagged" : "unflagged"), $_messageUID,$folder);
2010
		ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetFlaggedFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags->flagstatus == 2) ? "flagged" : "unflagged") . "-->". $rv);
2011
2012
		return $rv;
2013
	}
2014
2015
	/**
2016
	 * Create a max. 32 hex letter ID, current 20 chars are used
2017
	 *
2018
	 * @param int $account mail account id
2019
	 * @param string $folder
2020
	 * @param int $id =0
2021
	 * @return string
2022
	 * @throws Api\Exception\WrongParameter
2023
	 */
2024
	private function createID($account,$folder,$id=0)
2025
	{
2026
		if (!is_numeric($folder))
2027
		{
2028
			// convert string $folder in numeric id
2029
			$folder = $this->folder2hash($account,$f=$folder);
2030
		}
2031
2032
		$str = $this->backend->createID($account, $folder, $id);
2033
2034 View Code Duplication
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($account,'$f',$id) type=$account, folder=$folder --> '$str'");
2035
2036
		return $str;
2037
	}
2038
2039
	/**
2040
	 * Split an ID string into $app, $account $folder and $appid
2041
	 *
2042
	 * @param string $str
2043
	 * @param int &$account mail account id
2044
	 * @param string &$folder
2045
	 * @param int &$appid=null (for mail=mail is to be expected)
2046
	 * @throws Api\Exception\WrongParameter
2047
	 */
2048
	private function splitID($str,&$account,&$folder,&$appid=null)
2049
	{
2050
		$this->backend->splitID($str, $account, $folder, $appid);
2051
2052
		// convert numeric folder-id back to folder name
2053
		$folder = $this->hash2folder($account,$f=$folder);
2054
2055 View Code Duplication
		if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$str','$account','$folder',$appid)");
2056
	}
2057
2058
	/**
2059
	 * Methods to convert (hierarchical) folder names to nummerical id's
2060
	 *
2061
	 * This is currently done by storing a serialized array in the device specific
2062
	 * state directory.
2063
	 */
2064
2065
	/**
2066
	 * Convert folder string to nummeric hash
2067
	 *
2068
	 * @param int $account
2069
	 * @param string $folder
2070
	 * @return int
2071
	 */
2072
	private function folder2hash($account,$folder)
2073
	{
2074
		if(!isset($this->folderHashes)) $this->readFolderHashes();
2075
2076
		if (($index = array_search($folder, (array)$this->folderHashes[$account])) === false)
2077
		{
2078
			// new hash
2079
			$this->folderHashes[$account][] = $folder;
2080
			$index = array_search($folder, (array)$this->folderHashes[$account]);
2081
2082
			// maybe later storing in on class destruction only
2083
			$this->storeFolderHashes();
2084
		}
2085
		return $index;
2086
	}
2087
2088
	/**
2089
	 * Convert numeric hash to folder string
2090
	 *
2091
	 * @param int $account
2092
	 * @param int $index
2093
	 * @return string NULL if not used so far
2094
	 */
2095
	private function hash2folder($account,$index)
2096
	{
2097
		if(!isset($this->folderHashes)) $this->readFolderHashes();
2098
2099
		return isset($this->folderHashes[$account]) ? $this->folderHashes[$account][$index] : null;
2100
	}
2101
2102
	/**
2103
	 * Rename or create a folder in hash table
2104
	 *
2105
	 * @param int $account
2106
	 * @param int $index or null to create
2107
	 * @param string $new_name
2108
	 * @return int $index or new hash if $index is not found
2109
	 */
2110
	private function rename_folder_hash($account, $index, $new_name)
2111
	{
2112
		if ((string)$index === '' || !$this->hash2folder($account, $index))
2113
		{
2114
			return $this->folder2hash($account, $new_name);
2115
		}
2116
		$this->folderHashes[$account][$index] = $new_name;
2117
		$this->storeFolderHashes();
2118
		return $index;
2119
	}
2120
2121
	private $folderHashes;
2122
2123
	/**
2124
	 * Statemaschine instance used to store folders
2125
	 *
2126
	 * @var activesync_statemaschine
2127
	 */
2128
	private $fh_state_maschine;
2129
2130
	/**
2131
	 * state_type (and _key) used to store folder hashes
2132
	 */
2133
	const FOLDER_STATE_TYPE = 'folder_hashes';
2134
2135
	/**
2136
	 * Read hashfile from state dir
2137
	 */
2138
	private function readFolderHashes()
2139
	{
2140
		if (!isset($this->fh_state_maschine))
2141
		{
2142
			$this->fh_state_maschine = new activesync_statemachine($this->backend);
2143
		}
2144
		try {
2145
			$this->folderHashes = $this->fh_state_maschine->getState(Request::GetDeviceID(),
2146
				self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0);
2147
		}
2148
		catch (Exception $e) {
2149
			unset($e);
2150
			if ((file_exists($file = $this->hashFile()) || file_exists($file = $this->hashFile(true))) &&
2151
				($hashes = file_get_contents($file)))
2152
			{
2153
				$this->folderHashes = json_decode($hashes,true);
2154
				// fallback in case hashes have been serialized instead of being json-encoded
2155
				if (json_last_error()!=JSON_ERROR_NONE)
2156
				{
2157
					//error_log(__METHOD__.__LINE__." error decoding with json");
2158
					$this->folderHashes = unserialize($hashes);
2159
				}
2160
				// store folder-hashes to state
2161
				$this->storeFolderHashes();
2162
			}
2163
			else
2164
			{
2165
				$this->folderHashes = array();
2166
			}
2167
		}
2168
	}
2169
2170
	/**
2171
	 * Store hashfile via state-maschine
2172
	 *
2173
	 * return int|boolean false on error
2174
	 */
2175
	private function storeFolderHashes()
2176
	{
2177
		if (!isset($this->fh_state_maschine))
2178
		{
2179
			$this->fh_state_maschine = new activesync_statemachine($this->backend);
2180
		}
2181
		try {
2182
			$this->fh_state_maschine->setState($this->folderHashes, Request::GetDeviceID(),
2183
				self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0);
2184
		}
2185
		catch (Exception $e) {
2186
			_egw_log_exception($e);
2187
			return false;
2188
		}
2189
		return true;
2190
	}
2191
2192
	/**
2193
	 * Get name of hashfile in state dir
2194
	 *
2195
	 * New z-push 2 directory is in activesync_statemachine::getDeviceDirectory.
2196
	 * On request this function also returns, but never creates (!), old z-push 1 directory.
2197
	 *
2198
	 * @param boolean $old =false true: return old / pre-15 hash-file
2199
	 * @throws Api\Exception\AssertionFailed
2200
	 */
2201
	private function hashFile($old=false)
2202
	{
2203
		if (!($dev_id=Request::GetDeviceID()))
2204
		{
2205
			throw new Api\Exception\AssertionFailed(__METHOD__."() no DeviceID set!");
2206
		}
2207
		if ($old)
2208
		{
2209
			return STATE_DIR.$dev_id.'/'.$dev_id.'.hashes';
2210
		}
2211
		$dir = activesync_statemachine::getDeviceDirectory($dev_id);
2212
2213
		return $dir.'/'.$dev_id.'.hashes';
2214
	}
2215
}
2216