Passed
Push — master ( 34e8da...f497d2 )
by
unknown
06:10 queued 02:50
created
lib/request/sendmail.php 2 patches
Indentation   +123 added lines, -123 removed lines patch added patch discarded remove patch
@@ -8,127 +8,127 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class SendMail extends RequestProcessor {
11
-	/**
12
-	 * Handles the SendMail, SmartReply and SmartForward command.
13
-	 *
14
-	 * @param int $commandCode
15
-	 *
16
-	 * @return bool
17
-	 */
18
-	public function Handle($commandCode) {
19
-		$sm = new SyncSendMail();
20
-
21
-		$reply = $forward = $parent = $sendmail = $smartreply = $smartforward = false;
22
-		if (Request::GetGETCollectionId()) {
23
-			$parent = Request::GetGETCollectionId();
24
-		}
25
-		if ($commandCode == GSync::COMMAND_SMARTFORWARD) {
26
-			$forward = Request::GetGETItemId();
27
-		}
28
-		elseif ($commandCode == GSync::COMMAND_SMARTREPLY) {
29
-			$reply = Request::GetGETItemId();
30
-		}
31
-
32
-		if (self::$decoder->IsWBXML()) {
33
-			$el = self::$decoder->getElement();
34
-
35
-			if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
36
-				return false;
37
-			}
38
-
39
-			if ($el[EN_TAG] == SYNC_COMPOSEMAIL_SENDMAIL) {
40
-				$sendmail = true;
41
-			}
42
-			elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTREPLY) {
43
-				$smartreply = true;
44
-			}
45
-			elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTFORWARD) {
46
-				$smartforward = true;
47
-			}
48
-
49
-			if (!$sendmail && !$smartreply && !$smartforward) {
50
-				return false;
51
-			}
52
-
53
-			$sm->Decode(self::$decoder);
54
-		}
55
-		else {
56
-			$sm->mime = self::$decoder->GetPlainInputStream();
57
-			// no wbxml output is provided, only a http OK
58
-			$sm->saveinsent = Request::GetGETSaveInSent();
59
-		}
60
-
61
-		// Check if it is a reply or forward. Two cases are possible:
62
-		// 1. Either $smartreply or $smartforward are set after reading WBXML
63
-		// 2. Either $reply or $forward are set after getting the request parameters
64
-		if ($reply || $smartreply || $forward || $smartforward) {
65
-			// If the mobile sends an email in WBXML data the variables below
66
-			// should be set. If it is a RFC822 message, get the reply/forward message id
67
-			// from the request as they are always available there
68
-			if (!isset($sm->source)) {
69
-				$sm->source = new SyncSendMailSource();
70
-			}
71
-			if (!isset($sm->source->itemid)) {
72
-				$sm->source->itemid = Request::GetGETItemId();
73
-			}
74
-			if (!isset($sm->source->folderid)) {
75
-				$sm->source->folderid = Request::GetGETCollectionId();
76
-			}
77
-
78
-			// split long-id if it's set - it overwrites folderid and itemid
79
-			if (isset($sm->source->longid) && $sm->source->longid) {
80
-				list($sm->source->folderid, $sm->source->itemid) = Utils::SplitMessageId($sm->source->longid);
81
-			}
82
-
83
-			// Rewrite the AS folderid into a backend folderid
84
-			if (isset($sm->source->folderid)) {
85
-				$sm->source->folderid = self::$deviceManager->GetBackendIdForFolderId($sm->source->folderid);
86
-			}
87
-			if (isset($sm->source->itemid)) {
88
-				list(, $sk) = Utils::SplitMessageId($sm->source->itemid);
89
-				$sm->source->itemid = $sk;
90
-			}
91
-			// replyflag and forward flags are actually only for the correct icon.
92
-			// Even if they are a part of SyncSendMail object, they won't be streamed.
93
-			if ($smartreply || $reply) {
94
-				$sm->replyflag = true;
95
-			}
96
-			else {
97
-				$sm->forwardflag = true;
98
-			}
99
-
100
-			if (!isset($sm->source->folderid) || !$sm->source->folderid) {
101
-				SLog::Write(LOGLEVEL_ERROR, sprintf("SendMail(): No parent folder id while replying or forwarding message:'%s'", (($reply) ? $reply : $forward)));
102
-			}
103
-		}
104
-
105
-		self::$topCollector->AnnounceInformation(sprintf("SendMail(): Sending email with %d bytes", strlen($sm->mime)), true);
106
-
107
-		$statusMessage = '';
108
-
109
-		try {
110
-			$status = self::$backend->SendMail($sm);
111
-		}
112
-		catch (StatusException $se) {
113
-			$status = $se->getCode();
114
-			$statusMessage = $se->getMessage();
115
-		}
116
-
117
-		if ($status != SYNC_COMMONSTATUS_SUCCESS) {
118
-			if (self::$decoder->IsWBXML()) {
119
-				// TODO check no WBXML on SmartReply and SmartForward
120
-				self::$encoder->StartWBXML();
121
-				self::$encoder->startTag(SYNC_COMPOSEMAIL_SENDMAIL);
122
-				self::$encoder->startTag(SYNC_COMPOSEMAIL_STATUS);
123
-				self::$encoder->content($status); // TODO return the correct status
124
-				self::$encoder->endTag();
125
-				self::$encoder->endTag();
126
-			}
127
-			else {
128
-				throw new HTTPReturnCodeException($statusMessage, HTTP_CODE_500, null, LOGLEVEL_WARN);
129
-			}
130
-		}
131
-
132
-		return $status;
133
-	}
11
+    /**
12
+     * Handles the SendMail, SmartReply and SmartForward command.
13
+     *
14
+     * @param int $commandCode
15
+     *
16
+     * @return bool
17
+     */
18
+    public function Handle($commandCode) {
19
+        $sm = new SyncSendMail();
20
+
21
+        $reply = $forward = $parent = $sendmail = $smartreply = $smartforward = false;
22
+        if (Request::GetGETCollectionId()) {
23
+            $parent = Request::GetGETCollectionId();
24
+        }
25
+        if ($commandCode == GSync::COMMAND_SMARTFORWARD) {
26
+            $forward = Request::GetGETItemId();
27
+        }
28
+        elseif ($commandCode == GSync::COMMAND_SMARTREPLY) {
29
+            $reply = Request::GetGETItemId();
30
+        }
31
+
32
+        if (self::$decoder->IsWBXML()) {
33
+            $el = self::$decoder->getElement();
34
+
35
+            if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
36
+                return false;
37
+            }
38
+
39
+            if ($el[EN_TAG] == SYNC_COMPOSEMAIL_SENDMAIL) {
40
+                $sendmail = true;
41
+            }
42
+            elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTREPLY) {
43
+                $smartreply = true;
44
+            }
45
+            elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTFORWARD) {
46
+                $smartforward = true;
47
+            }
48
+
49
+            if (!$sendmail && !$smartreply && !$smartforward) {
50
+                return false;
51
+            }
52
+
53
+            $sm->Decode(self::$decoder);
54
+        }
55
+        else {
56
+            $sm->mime = self::$decoder->GetPlainInputStream();
57
+            // no wbxml output is provided, only a http OK
58
+            $sm->saveinsent = Request::GetGETSaveInSent();
59
+        }
60
+
61
+        // Check if it is a reply or forward. Two cases are possible:
62
+        // 1. Either $smartreply or $smartforward are set after reading WBXML
63
+        // 2. Either $reply or $forward are set after getting the request parameters
64
+        if ($reply || $smartreply || $forward || $smartforward) {
65
+            // If the mobile sends an email in WBXML data the variables below
66
+            // should be set. If it is a RFC822 message, get the reply/forward message id
67
+            // from the request as they are always available there
68
+            if (!isset($sm->source)) {
69
+                $sm->source = new SyncSendMailSource();
70
+            }
71
+            if (!isset($sm->source->itemid)) {
72
+                $sm->source->itemid = Request::GetGETItemId();
73
+            }
74
+            if (!isset($sm->source->folderid)) {
75
+                $sm->source->folderid = Request::GetGETCollectionId();
76
+            }
77
+
78
+            // split long-id if it's set - it overwrites folderid and itemid
79
+            if (isset($sm->source->longid) && $sm->source->longid) {
80
+                list($sm->source->folderid, $sm->source->itemid) = Utils::SplitMessageId($sm->source->longid);
81
+            }
82
+
83
+            // Rewrite the AS folderid into a backend folderid
84
+            if (isset($sm->source->folderid)) {
85
+                $sm->source->folderid = self::$deviceManager->GetBackendIdForFolderId($sm->source->folderid);
86
+            }
87
+            if (isset($sm->source->itemid)) {
88
+                list(, $sk) = Utils::SplitMessageId($sm->source->itemid);
89
+                $sm->source->itemid = $sk;
90
+            }
91
+            // replyflag and forward flags are actually only for the correct icon.
92
+            // Even if they are a part of SyncSendMail object, they won't be streamed.
93
+            if ($smartreply || $reply) {
94
+                $sm->replyflag = true;
95
+            }
96
+            else {
97
+                $sm->forwardflag = true;
98
+            }
99
+
100
+            if (!isset($sm->source->folderid) || !$sm->source->folderid) {
101
+                SLog::Write(LOGLEVEL_ERROR, sprintf("SendMail(): No parent folder id while replying or forwarding message:'%s'", (($reply) ? $reply : $forward)));
102
+            }
103
+        }
104
+
105
+        self::$topCollector->AnnounceInformation(sprintf("SendMail(): Sending email with %d bytes", strlen($sm->mime)), true);
106
+
107
+        $statusMessage = '';
108
+
109
+        try {
110
+            $status = self::$backend->SendMail($sm);
111
+        }
112
+        catch (StatusException $se) {
113
+            $status = $se->getCode();
114
+            $statusMessage = $se->getMessage();
115
+        }
116
+
117
+        if ($status != SYNC_COMMONSTATUS_SUCCESS) {
118
+            if (self::$decoder->IsWBXML()) {
119
+                // TODO check no WBXML on SmartReply and SmartForward
120
+                self::$encoder->StartWBXML();
121
+                self::$encoder->startTag(SYNC_COMPOSEMAIL_SENDMAIL);
122
+                self::$encoder->startTag(SYNC_COMPOSEMAIL_STATUS);
123
+                self::$encoder->content($status); // TODO return the correct status
124
+                self::$encoder->endTag();
125
+                self::$encoder->endTag();
126
+            }
127
+            else {
128
+                throw new HTTPReturnCodeException($statusMessage, HTTP_CODE_500, null, LOGLEVEL_WARN);
129
+            }
130
+        }
131
+
132
+        return $status;
133
+    }
134 134
 }
Please login to merge, or discard this patch.
Braces   +7 added lines, -14 removed lines patch added patch discarded remove patch
@@ -24,8 +24,7 @@  discard block
 block discarded – undo
24 24
 		}
25 25
 		if ($commandCode == GSync::COMMAND_SMARTFORWARD) {
26 26
 			$forward = Request::GetGETItemId();
27
-		}
28
-		elseif ($commandCode == GSync::COMMAND_SMARTREPLY) {
27
+		} elseif ($commandCode == GSync::COMMAND_SMARTREPLY) {
29 28
 			$reply = Request::GetGETItemId();
30 29
 		}
31 30
 
@@ -38,11 +37,9 @@  discard block
 block discarded – undo
38 37
 
39 38
 			if ($el[EN_TAG] == SYNC_COMPOSEMAIL_SENDMAIL) {
40 39
 				$sendmail = true;
41
-			}
42
-			elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTREPLY) {
40
+			} elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTREPLY) {
43 41
 				$smartreply = true;
44
-			}
45
-			elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTFORWARD) {
42
+			} elseif ($el[EN_TAG] == SYNC_COMPOSEMAIL_SMARTFORWARD) {
46 43
 				$smartforward = true;
47 44
 			}
48 45
 
@@ -51,8 +48,7 @@  discard block
 block discarded – undo
51 48
 			}
52 49
 
53 50
 			$sm->Decode(self::$decoder);
54
-		}
55
-		else {
51
+		} else {
56 52
 			$sm->mime = self::$decoder->GetPlainInputStream();
57 53
 			// no wbxml output is provided, only a http OK
58 54
 			$sm->saveinsent = Request::GetGETSaveInSent();
@@ -92,8 +88,7 @@  discard block
 block discarded – undo
92 88
 			// Even if they are a part of SyncSendMail object, they won't be streamed.
93 89
 			if ($smartreply || $reply) {
94 90
 				$sm->replyflag = true;
95
-			}
96
-			else {
91
+			} else {
97 92
 				$sm->forwardflag = true;
98 93
 			}
99 94
 
@@ -108,8 +103,7 @@  discard block
 block discarded – undo
108 103
 
109 104
 		try {
110 105
 			$status = self::$backend->SendMail($sm);
111
-		}
112
-		catch (StatusException $se) {
106
+		} catch (StatusException $se) {
113 107
 			$status = $se->getCode();
114 108
 			$statusMessage = $se->getMessage();
115 109
 		}
@@ -123,8 +117,7 @@  discard block
 block discarded – undo
123 117
 				self::$encoder->content($status); // TODO return the correct status
124 118
 				self::$encoder->endTag();
125 119
 				self::$encoder->endTag();
126
-			}
127
-			else {
120
+			} else {
128 121
 				throw new HTTPReturnCodeException($statusMessage, HTTP_CODE_500, null, LOGLEVEL_WARN);
129 122
 			}
130 123
 		}
Please login to merge, or discard this patch.
lib/request/meetingresponse.php 2 patches
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -8,103 +8,103 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class MeetingResponse extends RequestProcessor {
11
-	/**
12
-	 * Handles the MeetingResponse command.
13
-	 *
14
-	 * @param int $commandCode
15
-	 *
16
-	 * @return bool
17
-	 */
18
-	public function Handle($commandCode) {
19
-		$requests = [];
20
-
21
-		if (!self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE)) {
22
-			return false;
23
-		}
24
-
25
-		while (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUEST)) {
26
-			$req = [];
27
-			WBXMLDecoder::ResetInWhile("meetingResponseRequest");
28
-			while (WBXMLDecoder::InWhile("meetingResponseRequest")) {
29
-				if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_USERRESPONSE)) {
30
-					$req["response"] = self::$decoder->getElementContent();
31
-					if (!self::$decoder->getElementEndTag()) {
32
-						return false;
33
-					}
34
-				}
35
-
36
-				if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_FOLDERID)) {
37
-					$req["folderid"] = self::$decoder->getElementContent();
38
-					if (!self::$decoder->getElementEndTag()) {
39
-						return false;
40
-					}
41
-				}
42
-
43
-				if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUESTID)) {
44
-					$req["requestid"] = self::$decoder->getElementContent();
45
-					if (!self::$decoder->getElementEndTag()) {
46
-						return false;
47
-					}
48
-				}
49
-
50
-				$e = self::$decoder->peek();
51
-				if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
52
-					self::$decoder->getElementEndTag();
53
-
54
-					break;
55
-				}
56
-			}
57
-			array_push($requests, $req);
58
-		}
59
-
60
-		if (!self::$decoder->getElementEndTag()) {
61
-			return false;
62
-		}
63
-
64
-		// output the error code, plus the ID of the calendar item that was generated by the
65
-		// accept of the meeting response
66
-		self::$encoder->StartWBXML();
67
-		self::$encoder->startTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE);
68
-
69
-		foreach ($requests as $req) {
70
-			$status = SYNC_MEETRESPSTATUS_SUCCESS;
71
-
72
-			try {
73
-				$backendFolderId = self::$deviceManager->GetBackendIdForFolderId($req["folderid"]);
74
-
75
-				// if the source folder is an additional folder the backend has to be setup correctly
76
-				if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($backendFolderId))) {
77
-					throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id %s/%s", $req["folderid"], $backendFolderId), SYNC_MEETRESPSTATUS_SERVERERROR);
78
-				}
79
-
80
-				$calendarid = self::$backend->MeetingResponse($req["requestid"], $backendFolderId, $req["response"]);
81
-				if ($calendarid === false) {
82
-					throw new StatusException("HandleMeetingResponse() not possible", SYNC_MEETRESPSTATUS_SERVERERROR);
83
-				}
84
-			}
85
-			catch (StatusException $stex) {
86
-				$status = $stex->getCode();
87
-			}
88
-
89
-			self::$encoder->startTag(SYNC_MEETINGRESPONSE_RESULT);
90
-			self::$encoder->startTag(SYNC_MEETINGRESPONSE_REQUESTID);
91
-			self::$encoder->content($req["requestid"]);
92
-			self::$encoder->endTag();
93
-
94
-			self::$encoder->startTag(SYNC_MEETINGRESPONSE_STATUS);
95
-			self::$encoder->content($status);
96
-			self::$encoder->endTag();
97
-
98
-			if ($status == SYNC_MEETRESPSTATUS_SUCCESS && !empty($calendarid)) {
99
-				self::$encoder->startTag(SYNC_MEETINGRESPONSE_CALENDARID);
100
-				self::$encoder->content($calendarid);
101
-				self::$encoder->endTag();
102
-			}
103
-			self::$encoder->endTag();
104
-			self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true);
105
-		}
106
-		self::$encoder->endTag();
107
-
108
-		return true;
109
-	}
11
+    /**
12
+     * Handles the MeetingResponse command.
13
+     *
14
+     * @param int $commandCode
15
+     *
16
+     * @return bool
17
+     */
18
+    public function Handle($commandCode) {
19
+        $requests = [];
20
+
21
+        if (!self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE)) {
22
+            return false;
23
+        }
24
+
25
+        while (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUEST)) {
26
+            $req = [];
27
+            WBXMLDecoder::ResetInWhile("meetingResponseRequest");
28
+            while (WBXMLDecoder::InWhile("meetingResponseRequest")) {
29
+                if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_USERRESPONSE)) {
30
+                    $req["response"] = self::$decoder->getElementContent();
31
+                    if (!self::$decoder->getElementEndTag()) {
32
+                        return false;
33
+                    }
34
+                }
35
+
36
+                if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_FOLDERID)) {
37
+                    $req["folderid"] = self::$decoder->getElementContent();
38
+                    if (!self::$decoder->getElementEndTag()) {
39
+                        return false;
40
+                    }
41
+                }
42
+
43
+                if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUESTID)) {
44
+                    $req["requestid"] = self::$decoder->getElementContent();
45
+                    if (!self::$decoder->getElementEndTag()) {
46
+                        return false;
47
+                    }
48
+                }
49
+
50
+                $e = self::$decoder->peek();
51
+                if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
52
+                    self::$decoder->getElementEndTag();
53
+
54
+                    break;
55
+                }
56
+            }
57
+            array_push($requests, $req);
58
+        }
59
+
60
+        if (!self::$decoder->getElementEndTag()) {
61
+            return false;
62
+        }
63
+
64
+        // output the error code, plus the ID of the calendar item that was generated by the
65
+        // accept of the meeting response
66
+        self::$encoder->StartWBXML();
67
+        self::$encoder->startTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE);
68
+
69
+        foreach ($requests as $req) {
70
+            $status = SYNC_MEETRESPSTATUS_SUCCESS;
71
+
72
+            try {
73
+                $backendFolderId = self::$deviceManager->GetBackendIdForFolderId($req["folderid"]);
74
+
75
+                // if the source folder is an additional folder the backend has to be setup correctly
76
+                if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($backendFolderId))) {
77
+                    throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id %s/%s", $req["folderid"], $backendFolderId), SYNC_MEETRESPSTATUS_SERVERERROR);
78
+                }
79
+
80
+                $calendarid = self::$backend->MeetingResponse($req["requestid"], $backendFolderId, $req["response"]);
81
+                if ($calendarid === false) {
82
+                    throw new StatusException("HandleMeetingResponse() not possible", SYNC_MEETRESPSTATUS_SERVERERROR);
83
+                }
84
+            }
85
+            catch (StatusException $stex) {
86
+                $status = $stex->getCode();
87
+            }
88
+
89
+            self::$encoder->startTag(SYNC_MEETINGRESPONSE_RESULT);
90
+            self::$encoder->startTag(SYNC_MEETINGRESPONSE_REQUESTID);
91
+            self::$encoder->content($req["requestid"]);
92
+            self::$encoder->endTag();
93
+
94
+            self::$encoder->startTag(SYNC_MEETINGRESPONSE_STATUS);
95
+            self::$encoder->content($status);
96
+            self::$encoder->endTag();
97
+
98
+            if ($status == SYNC_MEETRESPSTATUS_SUCCESS && !empty($calendarid)) {
99
+                self::$encoder->startTag(SYNC_MEETINGRESPONSE_CALENDARID);
100
+                self::$encoder->content($calendarid);
101
+                self::$encoder->endTag();
102
+            }
103
+            self::$encoder->endTag();
104
+            self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true);
105
+        }
106
+        self::$encoder->endTag();
107
+
108
+        return true;
109
+    }
110 110
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -81,8 +81,7 @@
 block discarded – undo
81 81
 				if ($calendarid === false) {
82 82
 					throw new StatusException("HandleMeetingResponse() not possible", SYNC_MEETRESPSTATUS_SERVERERROR);
83 83
 				}
84
-			}
85
-			catch (StatusException $stex) {
84
+			} catch (StatusException $stex) {
86 85
 				$status = $stex->getCode();
87 86
 			}
88 87
 
Please login to merge, or discard this patch.
lib/request/sync.php 3 patches
Indentation   +1669 added lines, -1669 removed lines patch added patch discarded remove patch
@@ -8,1673 +8,1673 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class Sync extends RequestProcessor {
11
-	// Ignored SMS identifier
12
-	public const GSYNCIGNORESMS = "ZPISMS";
13
-	private $importer;
14
-	private $globallyExportedItems;
15
-	private $singleFolder;
16
-	private $multiFolderInfo;
17
-	private $startTagsSent = false;
18
-	private $startFolderTagSent = false;
19
-
20
-	/**
21
-	 * Handles the Sync command
22
-	 * Performs the synchronization of messages.
23
-	 *
24
-	 * @param int $commandCode
25
-	 *
26
-	 * @return bool
27
-	 */
28
-	public function Handle($commandCode) {
29
-		// Contains all requested folders (containers)
30
-		$sc = new SyncCollections();
31
-		$status = SYNC_STATUS_SUCCESS;
32
-		$wbxmlproblem = false;
33
-		$emptysync = false;
34
-		$this->singleFolder = true;
35
-		$this->multiFolderInfo = [];
36
-		$this->globallyExportedItems = 0;
37
-
38
-		// check if the hierarchySync was fully completed
39
-		if (USE_PARTIAL_FOLDERSYNC) {
40
-			if (self::$deviceManager->GetFolderSyncComplete() === false) {
41
-				SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
42
-				self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
43
-				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
44
-			}
45
-			else {
46
-				SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
47
-			}
48
-		}
49
-
50
-		// Start Synchronize
51
-		if (self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) {
52
-			// AS 1.0 sends version information in WBXML
53
-			if (self::$decoder->getElementStartTag(SYNC_VERSION)) {
54
-				$sync_version = self::$decoder->getElementContent();
55
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version));
56
-				if (!self::$decoder->getElementEndTag()) {
57
-					return false;
58
-				}
59
-			}
60
-
61
-			// Syncing specified folders
62
-			// Android still sends heartbeat sync even if all syncfolders are disabled.
63
-			// Check if Folders tag is empty (<Folders/>) and only sync if there are
64
-			// some folders in the request. See ZP-172
65
-			$startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS);
66
-			if (isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) {
67
-				while (self::$decoder->getElementStartTag(SYNC_FOLDER)) {
68
-					$actiondata = [];
69
-					$actiondata["requested"] = true;
70
-					$actiondata["clientids"] = [];
71
-					$actiondata["modifyids"] = [];
72
-					$actiondata["removeids"] = [];
73
-					$actiondata["fetchids"] = [];
74
-					$actiondata["statusids"] = [];
75
-
76
-					// read class, synckey and folderid without SyncParameters Object for now
77
-					$class = $synckey = $folderid = false;
78
-
79
-					// if there are already collections in SyncCollections, this is min. the second folder
80
-					if ($sc->HasCollections()) {
81
-						$this->singleFolder = false;
82
-					}
83
-
84
-					// for AS versions < 2.5
85
-					if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
86
-						$class = self::$decoder->getElementContent();
87
-						SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class));
88
-
89
-						if (!self::$decoder->getElementEndTag()) {
90
-							return false;
91
-						}
92
-					}
93
-
94
-					// SyncKey
95
-					if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
96
-						$synckey = "0";
97
-						if (($synckey = self::$decoder->getElementContent()) !== false) {
98
-							if (!self::$decoder->getElementEndTag()) {
99
-								return false;
100
-							}
101
-						}
102
-					}
103
-					else {
104
-						return false;
105
-					}
106
-
107
-					// FolderId
108
-					if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
109
-						$folderid = self::$decoder->getElementContent();
110
-
111
-						if (!self::$decoder->getElementEndTag()) {
112
-							return false;
113
-						}
114
-					}
115
-
116
-					// compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
117
-					if (!$folderid && $class) {
118
-						$folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class);
119
-					}
120
-
121
-					// folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update
122
-					try {
123
-						$spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid);
124
-
125
-						// TODO remove resync of folders
126
-						// this forces a resync of all states
127
-						if (!$spa instanceof SyncParameters) {
128
-							throw new StateInvalidException("Saved state are not of type SyncParameters");
129
-						}
130
-
131
-						// new/resync requested
132
-						if ($synckey == "0") {
133
-							$spa->RemoveSyncKey();
134
-							$spa->DelFolderStat();
135
-							$spa->SetMoveState(false);
136
-						}
137
-						elseif ($synckey !== false) {
138
-							if ($synckey !== $spa->GetSyncKey() && $synckey !== $spa->GetNewSyncKey()) {
139
-								SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Synckey does not match latest saved for this folder or there is a move state, removing folderstat to force Exporter setup");
140
-								$spa->DelFolderStat();
141
-							}
142
-							$spa->SetSyncKey($synckey);
143
-						}
144
-					}
145
-					catch (StateInvalidException $stie) {
146
-						$spa = new SyncParameters();
147
-						$status = SYNC_STATUS_INVALIDSYNCKEY;
148
-						self::$topCollector->AnnounceInformation("State invalid - Resync folder", $this->singleFolder);
149
-						self::$deviceManager->ForceFolderResync($folderid);
150
-						$this->saveMultiFolderInfo("exception", "StateInvalidException");
151
-					}
152
-
153
-					// update folderid.. this might be a new object
154
-					$spa->SetFolderId($folderid);
155
-					$spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($folderid));
156
-
157
-					if ($class !== false) {
158
-						$spa->SetContentClass($class);
159
-					}
160
-
161
-					// Get class for as versions >= 12.0
162
-					if (!$spa->HasContentClass()) {
163
-						try {
164
-							$spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
165
-							SLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
166
-						}
167
-						catch (NoHierarchyCacheAvailableException $nhca) {
168
-							$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
169
-							self::$deviceManager->ForceFullResync();
170
-						}
171
-					}
172
-
173
-					// done basic SPA initialization/loading -> add to SyncCollection
174
-					$sc->AddCollection($spa);
175
-					$sc->AddParameter($spa, "requested", true);
176
-
177
-					if ($spa->HasContentClass()) {
178
-						self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), $this->singleFolder);
179
-					}
180
-					else {
181
-						SLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
182
-					}
183
-
184
-					// SUPPORTED properties
185
-					if (($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) {
186
-						// ZP-481: LG phones send an empty supported tag, so only read the contents if available here
187
-						// if <Supported/> is received, it's as no supported fields would have been sent at all.
188
-						// unsure if this is the correct approach, or if in this case some default list should be used
189
-						if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) {
190
-							$supfields = [];
191
-							WBXMLDecoder::ResetInWhile("syncSupported");
192
-							while (WBXMLDecoder::InWhile("syncSupported")) {
193
-								$el = self::$decoder->getElement();
194
-
195
-								if ($el[EN_TYPE] == EN_TYPE_ENDTAG) {
196
-									break;
197
-								}
198
-
199
-								$supfields[] = $el[EN_TAG];
200
-							}
201
-							self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields);
202
-						}
203
-					}
204
-
205
-					// Deletes as moves can be an empty tag as well as have value
206
-					if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
207
-						$spa->SetDeletesAsMoves(true);
208
-						if (($dam = self::$decoder->getElementContent()) !== false) {
209
-							$spa->SetDeletesAsMoves((bool) $dam);
210
-							if (!self::$decoder->getElementEndTag()) {
211
-								return false;
212
-							}
213
-						}
214
-					}
215
-
216
-					// Get changes can be an empty tag as well as have value
217
-					// code block partly contributed by dw2412
218
-					if ($starttag = self::$decoder->getElementStartTag(SYNC_GETCHANGES)) {
219
-						$sc->AddParameter($spa, "getchanges", true);
220
-						if (($gc = self::$decoder->getElementContent()) !== false) {
221
-							$sc->AddParameter($spa, "getchanges", $gc);
222
-						}
223
-						// read the endtag if SYNC_GETCHANGES wasn't an empty tag
224
-						if ($starttag[EN_FLAGS] & EN_FLAGS_CONTENT) {
225
-							if (!self::$decoder->getElementEndTag()) {
226
-								return false;
227
-							}
228
-						}
229
-					}
230
-
231
-					if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
232
-						$ws = self::$decoder->getElementContent();
233
-						// normalize windowsize - see ZP-477
234
-						if ($ws == 0 || $ws > WINDOW_SIZE_MAX) {
235
-							$ws = WINDOW_SIZE_MAX;
236
-						}
237
-
238
-						$spa->SetWindowSize($ws);
239
-
240
-						// also announce the currently requested window size to the DeviceManager
241
-						self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize());
242
-
243
-						if (!self::$decoder->getElementEndTag()) {
244
-							return false;
245
-						}
246
-					}
247
-
248
-					// conversation mode requested
249
-					if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
250
-						$spa->SetConversationMode(true);
251
-						if (($conversationmode = self::$decoder->getElementContent()) !== false) {
252
-							$spa->SetConversationMode((bool) $conversationmode);
253
-							if (!self::$decoder->getElementEndTag()) {
254
-								return false;
255
-							}
256
-						}
257
-					}
258
-
259
-					// Do not truncate by default
260
-					$spa->SetTruncation(SYNC_TRUNCATION_ALL);
261
-
262
-					// use default conflict handling if not specified by the mobile
263
-					$spa->SetConflict(SYNC_CONFLICT_DEFAULT);
264
-
265
-					// save the current filtertype because it might have been changed on the mobile
266
-					$currentFilterType = $spa->GetFilterType();
267
-
268
-					while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
269
-						$firstOption = true;
270
-						WBXMLDecoder::ResetInWhile("syncOptions");
271
-						while (WBXMLDecoder::InWhile("syncOptions")) {
272
-							// foldertype definition
273
-							if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
274
-								$foldertype = self::$decoder->getElementContent();
275
-								SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): specified options block with foldertype '%s'", $foldertype));
276
-
277
-								// switch the foldertype for the next options
278
-								$spa->UseCPO($foldertype);
279
-
280
-								// save the current filtertype because it might have been changed on the mobile
281
-								$currentFilterType = $spa->GetFilterType();
282
-
283
-								// set to synchronize all changes. The mobile could overwrite this value
284
-								$spa->SetFilterType(SYNC_FILTERTYPE_ALL);
285
-
286
-								if (!self::$decoder->getElementEndTag()) {
287
-									return false;
288
-								}
289
-							}
290
-							// if no foldertype is defined, use default cpo
291
-							elseif ($firstOption) {
292
-								$spa->UseCPO();
293
-								// save the current filtertype because it might have been changed on the mobile
294
-								$currentFilterType = $spa->GetFilterType();
295
-								// set to synchronize all changes. The mobile could overwrite this value
296
-								$spa->SetFilterType(SYNC_FILTERTYPE_ALL);
297
-							}
298
-							$firstOption = false;
299
-
300
-							if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
301
-								$spa->SetFilterType(self::$decoder->getElementContent());
302
-								if (!self::$decoder->getElementEndTag()) {
303
-									return false;
304
-								}
305
-							}
306
-							if (self::$decoder->getElementStartTag(SYNC_TRUNCATION)) {
307
-								$spa->SetTruncation(self::$decoder->getElementContent());
308
-								if (!self::$decoder->getElementEndTag()) {
309
-									return false;
310
-								}
311
-							}
312
-							if (self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
313
-								$spa->SetRTFTruncation(self::$decoder->getElementContent());
314
-								if (!self::$decoder->getElementEndTag()) {
315
-									return false;
316
-								}
317
-							}
318
-
319
-							if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
320
-								$spa->SetMimeSupport(self::$decoder->getElementContent());
321
-								if (!self::$decoder->getElementEndTag()) {
322
-									return false;
323
-								}
324
-							}
325
-
326
-							if (self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
327
-								$spa->SetMimeTruncation(self::$decoder->getElementContent());
328
-								if (!self::$decoder->getElementEndTag()) {
329
-									return false;
330
-								}
331
-							}
332
-
333
-							if (self::$decoder->getElementStartTag(SYNC_CONFLICT)) {
334
-								$spa->SetConflict(self::$decoder->getElementContent());
335
-								if (!self::$decoder->getElementEndTag()) {
336
-									return false;
337
-								}
338
-							}
339
-
340
-							while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
341
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
342
-									$bptype = self::$decoder->getElementContent();
343
-									$spa->BodyPreference($bptype);
344
-									if (!self::$decoder->getElementEndTag()) {
345
-										return false;
346
-									}
347
-								}
348
-
349
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
350
-									$spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
351
-									if (!self::$decoder->getElementEndTag()) {
352
-										return false;
353
-									}
354
-								}
355
-
356
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
357
-									$spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
358
-									if (!self::$decoder->getElementEndTag()) {
359
-										return false;
360
-									}
361
-								}
362
-
363
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
364
-									$spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
365
-									if (!self::$decoder->getElementEndTag()) {
366
-										return false;
367
-									}
368
-								}
369
-
370
-								if (!self::$decoder->getElementEndTag()) {
371
-									return false;
372
-								}
373
-							}
374
-
375
-							if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPARTPREFERENCE)) {
376
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
377
-									$bpptype = self::$decoder->getElementContent();
378
-									$spa->BodyPartPreference($bpptype);
379
-									if (!self::$decoder->getElementEndTag()) {
380
-										return false;
381
-									}
382
-								}
383
-
384
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
385
-									$spa->BodyPartPreference($bpptype)->SetTruncationSize(self::$decoder->getElementContent());
386
-									if (!self::$decoder->getElementEndTag()) {
387
-										return false;
388
-									}
389
-								}
390
-
391
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
392
-									$spa->BodyPartPreference($bpptype)->SetAllOrNone(self::$decoder->getElementContent());
393
-									if (!self::$decoder->getElementEndTag()) {
394
-										return false;
395
-									}
396
-								}
397
-
398
-								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
399
-									$spa->BodyPartPreference($bpptype)->SetPreview(self::$decoder->getElementContent());
400
-									if (!self::$decoder->getElementEndTag()) {
401
-										return false;
402
-									}
403
-								}
404
-
405
-								if (!self::$decoder->getElementEndTag()) {
406
-									return false;
407
-								}
408
-							}
409
-
410
-							if (self::$decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_SUPPORT)) {
411
-								$spa->SetRmSupport(self::$decoder->getElementContent());
412
-								if (!self::$decoder->getElementEndTag()) {
413
-									return false;
414
-								}
415
-							}
416
-
417
-							$e = self::$decoder->peek();
418
-							if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
419
-								self::$decoder->getElementEndTag();
420
-
421
-								break;
422
-							}
423
-						}
424
-					}
425
-
426
-					// limit items to be synchronized to the mobiles if configured
427
-					$maxAllowed = self::$deviceManager->GetFilterType($spa->GetFolderId(), $spa->GetBackendFolderId());
428
-					if ($maxAllowed > SYNC_FILTERTYPE_ALL &&
429
-						(!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > $maxAllowed)) {
430
-						SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): FilterType applied globally or specifically, using value: %s", $maxAllowed));
431
-						$spa->SetFilterType($maxAllowed);
432
-					}
433
-
434
-					if ($currentFilterType != $spa->GetFilterType()) {
435
-						SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): FilterType has changed (old: '%s', new: '%s'), removing folderstat to force Exporter setup", $currentFilterType, $spa->GetFilterType()));
436
-						$spa->DelFolderStat();
437
-					}
438
-
439
-					// Check if the hierarchycache is available. If not, trigger a HierarchySync
440
-					if (self::$deviceManager->IsHierarchySyncRequired()) {
441
-						$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
442
-						SLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device");
443
-					}
444
-
445
-					if (($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) {
446
-						// We can not proceed here as the content class is unknown
447
-						if ($status != SYNC_STATUS_SUCCESS) {
448
-							SLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem.");
449
-							$wbxmlproblem = true;
450
-
451
-							break;
452
-						}
453
-
454
-						$performaction = true;
455
-
456
-						// unset the importer
457
-						$this->importer = false;
458
-
459
-						$nchanges = 0;
460
-						WBXMLDecoder::ResetInWhile("syncActions");
461
-						while (WBXMLDecoder::InWhile("syncActions")) {
462
-							// ADD, MODIFY, REMOVE or FETCH
463
-							$element = self::$decoder->getElement();
464
-
465
-							if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
466
-								self::$decoder->ungetElement($element);
467
-
468
-								break;
469
-							}
470
-
471
-							if ($status == SYNC_STATUS_SUCCESS) {
472
-								++$nchanges;
473
-							}
474
-
475
-							// Foldertype sent when syncing SMS
476
-							if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
477
-								$foldertype = self::$decoder->getElementContent();
478
-								SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype));
479
-
480
-								if (!self::$decoder->getElementEndTag()) {
481
-									return false;
482
-								}
483
-							}
484
-							else {
485
-								$foldertype = false;
486
-							}
487
-
488
-							$serverid = false;
489
-							if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
490
-								if (($serverid = self::$decoder->getElementContent()) !== false) {
491
-									if (!self::$decoder->getElementEndTag()) { // end serverid
492
-										return false;
493
-									}
494
-								}
495
-							}
496
-
497
-							if (self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
498
-								$clientid = self::$decoder->getElementContent();
499
-
500
-								if (!self::$decoder->getElementEndTag()) { // end clientid
501
-									return false;
502
-								}
503
-							}
504
-							else {
505
-								$clientid = false;
506
-							}
507
-
508
-							// Get the SyncMessage if sent
509
-							if (($el = self::$decoder->getElementStartTag(SYNC_DATA)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) {
510
-								$message = GSync::getSyncObjectFromFolderClass($spa->GetContentClass());
511
-								$message->Decode(self::$decoder);
512
-
513
-								// set Ghosted fields
514
-								$message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId()));
515
-
516
-								if (!self::$decoder->getElementEndTag()) { // end applicationdata
517
-									return false;
518
-								}
519
-							}
520
-							else {
521
-								$message = false;
522
-							}
523
-
524
-							switch ($element[EN_TAG]) {
525
-								case SYNC_FETCH:
526
-									array_push($actiondata["fetchids"], $serverid);
527
-									break;
528
-
529
-								default:
530
-									// get the importer
531
-									if ($this->importer == false) {
532
-										$status = $this->getImporter($sc, $spa, $actiondata);
533
-									}
534
-
535
-									if ($status == SYNC_STATUS_SUCCESS) {
536
-										$this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
537
-									}
538
-									else {
539
-										SLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
540
-									}
541
-									break;
542
-							}
543
-
544
-							if ($actiondata["fetchids"]) {
545
-								self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
546
-							}
547
-							else {
548
-								self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
549
-							}
550
-
551
-							if (!self::$decoder->getElementEndTag()) { // end add/change/delete/move
552
-								return false;
553
-							}
554
-						}
555
-
556
-						if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) {
557
-							SLog::Write(LOGLEVEL_INFO, sprintf("Processed '%d' incoming changes", $nchanges));
558
-							if (!$actiondata["fetchids"]) {
559
-								self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), $this->singleFolder);
560
-								$this->saveMultiFolderInfo("incoming", $nchanges);
561
-							}
562
-
563
-							try {
564
-								// Save the updated state, which is used for the exporter later
565
-								$sc->AddParameter($spa, "state", $this->importer->GetState());
566
-							}
567
-							catch (StatusException $stex) {
568
-								$status = $stex->getCode();
569
-							}
570
-						}
571
-
572
-						if (!self::$decoder->getElementEndTag()) { // end PERFORM
573
-							return false;
574
-						}
575
-					}
576
-
577
-					// save the failsave state
578
-					if (!empty($actiondata["statusids"])) {
579
-						unset($actiondata["failstate"]);
580
-						$actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state");
581
-						self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata);
582
-					}
583
-
584
-					// save actiondata
585
-					$sc->AddParameter($spa, "actiondata", $actiondata);
586
-
587
-					if (!self::$decoder->getElementEndTag()) { // end collection
588
-						return false;
589
-					}
590
-
591
-					// AS14 does not send GetChanges anymore. We should do it if there were no incoming changes
592
-					if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) {
593
-						$sc->AddParameter($spa, "getchanges", true);
594
-					}
595
-				} // END FOLDER
596
-
597
-				if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) { // end collections
598
-					return false;
599
-				}
600
-			} // end FOLDERS
601
-
602
-			if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) {
603
-				$hbinterval = self::$decoder->getElementContent();
604
-				if (!self::$decoder->getElementEndTag()) { // SYNC_HEARTBEATINTERVAL
605
-					return false;
606
-				}
607
-			}
608
-
609
-			if (self::$decoder->getElementStartTag(SYNC_WAIT)) {
610
-				$wait = self::$decoder->getElementContent();
611
-				if (!self::$decoder->getElementEndTag()) { // SYNC_WAIT
612
-					return false;
613
-				}
614
-
615
-				// internally the heartbeat interval and the wait time are the same
616
-				// heartbeat is in seconds, wait in minutes
617
-				$hbinterval = $wait * 60;
618
-			}
619
-
620
-			if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
621
-				$sc->SetGlobalWindowSize(self::$decoder->getElementContent());
622
-				SLog::Write(LOGLEVEL_DEBUG, "Sync(): Global WindowSize requested: " . $sc->GetGlobalWindowSize());
623
-				if (!self::$decoder->getElementEndTag()) { // SYNC_WINDOWSIZE
624
-					return false;
625
-				}
626
-			}
627
-
628
-			if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
629
-				$partial = true;
630
-			}
631
-			else {
632
-				$partial = false;
633
-			}
634
-
635
-			if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) { // end sync
636
-				return false;
637
-			}
638
-		}
639
-		// we did not receive a SYNCHRONIZE block - assume empty sync
640
-		else {
641
-			$emptysync = true;
642
-		}
643
-		// END SYNCHRONIZE
644
-
645
-		// check heartbeat/wait time
646
-		if (isset($hbinterval)) {
647
-			if ($hbinterval < 60 || $hbinterval > 3540) {
648
-				$status = SYNC_STATUS_INVALIDWAITORHBVALUE;
649
-				SLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval));
650
-			}
651
-		}
652
-
653
-		// Partial & Empty Syncs need saved data to proceed with synchronization
654
-		if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true)) {
655
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders."));
656
-
657
-			// Load all collections - do not overwrite existing (received!), load states, check permissions and only load confirmed states!
658
-			try {
659
-				$sc->LoadAllCollections(false, true, true, true, true);
660
-			}
661
-			catch (StateInvalidException $siex) {
662
-				$status = SYNC_STATUS_INVALIDSYNCKEY;
663
-				self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
664
-				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
665
-			}
666
-			catch (StatusException $stex) {
667
-				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
668
-				self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
669
-				$this->saveMultiFolderInfo("exception", "StatusException");
670
-			}
671
-
672
-			// update a few values
673
-			foreach ($sc as $folderid => $spa) {
674
-				// manually set getchanges parameter for this collection if it is synchronized
675
-				if ($spa->HasSyncKey()) {
676
-					$actiondata = $sc->GetParameter($spa, "actiondata");
677
-					// request changes if no other actions are executed
678
-					if (empty($actiondata["modifyids"]) && empty($actiondata["clientids"]) && empty($actiondata["removeids"])) {
679
-						$sc->AddParameter($spa, "getchanges", true);
680
-					}
681
-
682
-					// announce WindowSize to DeviceManager
683
-					self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize());
684
-				}
685
-			}
686
-			if (!$sc->HasCollections()) {
687
-				$status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
688
-			}
689
-		}
690
-		elseif (isset($hbinterval)) {
691
-			// load the hierarchy data - there are no permissions to verify so we just set it to false
692
-			if (!$sc->LoadCollection(false, true, false)) {
693
-				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
694
-				self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
695
-				$this->saveMultiFolderInfo("exception", "StatusException");
696
-			}
697
-		}
698
-
699
-		// HEARTBEAT
700
-		if ($status == SYNC_STATUS_SUCCESS && isset($hbinterval)) {
701
-			$interval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 30;
702
-
703
-			if (isset($hbinterval)) {
704
-				$sc->SetLifetime($hbinterval);
705
-			}
706
-
707
-			// states are lazy loaded - we have to make sure that they are there!
708
-			$loadstatus = SYNC_STATUS_SUCCESS;
709
-			foreach ($sc as $folderid => $spa) {
710
-				// some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362
711
-				// we do not load the state so we will never get relevant changes on the OUTBOX folder
712
-				if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) {
713
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed"));
714
-
715
-					continue;
716
-				}
717
-
718
-				$fad = [];
719
-				// if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS
720
-				// so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY
721
-				if ($loadstatus == SYNC_STATUS_SUCCESS) {
722
-					$loadstatus = $this->loadStates($sc, $spa, $fad);
723
-				}
724
-			}
725
-
726
-			if ($loadstatus == SYNC_STATUS_SUCCESS) {
727
-				$foundchanges = false;
728
-
729
-				try {
730
-					// always check for changes
731
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
732
-					$foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
733
-				}
734
-				catch (StatusException $stex) {
735
-					if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
736
-						$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
737
-					}
738
-					else {
739
-						$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
740
-						self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
741
-						$this->saveMultiFolderInfo("exception", "StatusException");
742
-					}
743
-				}
744
-
745
-				// update the waittime waited
746
-				self::$waitTime = $sc->GetWaitedSeconds();
747
-
748
-				// in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response
749
-				if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) {
750
-					// if there were changes to the SPA or CPOs we need to save this before we terminate
751
-					// only save if the state was not modified by some other request, if so, return state invalid status
752
-					foreach ($sc as $folderid => $spa) {
753
-						if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
754
-							$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
755
-						}
756
-						else {
757
-							$sc->SaveCollection($spa);
758
-						}
759
-					}
760
-
761
-					if ($status == SYNC_STATUS_SUCCESS) {
762
-						SLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection.");
763
-						self::$specialHeaders = [];
764
-						self::$specialHeaders[] = "Connection: close";
765
-
766
-						return true;
767
-					}
768
-				}
769
-
770
-				if ($foundchanges) {
771
-					foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) {
772
-						// check if there were other sync requests for a folder during the heartbeat
773
-						$spa = $sc->GetCollection($folderid);
774
-						if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
775
-							SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
776
-							$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
777
-						}
778
-						else {
779
-							SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
780
-						}
781
-					}
782
-				}
783
-			}
784
-		}
785
-
786
-		// Start the output
787
-		SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Start Output");
788
-
789
-		// global status
790
-		// SYNC_COMMONSTATUS_* start with values from 101
791
-		if ($status != SYNC_COMMONSTATUS_SUCCESS && ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED || $status > 100)) {
792
-			self::$deviceManager->AnnounceProcessStatus($folderid, $status);
793
-			$this->sendStartTags();
794
-			self::$encoder->startTag(SYNC_STATUS);
795
-			self::$encoder->content($status);
796
-			self::$encoder->endTag();
797
-			self::$encoder->endTag(); // SYNC_SYNCHRONIZE
798
-
799
-			return true;
800
-		}
801
-
802
-		// Loop through requested folders
803
-		foreach ($sc as $folderid => $spa) {
804
-			// get actiondata
805
-			$actiondata = $sc->GetParameter($spa, "actiondata");
806
-
807
-			if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) {
808
-				SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection."));
809
-
810
-				continue;
811
-			}
812
-
813
-			if (!$sc->GetParameter($spa, "requested")) {
814
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId()));
815
-				// reload state and initialize StateMachine correctly
816
-				$sc->AddParameter($spa, "state", null);
817
-				$status = $this->loadStates($sc, $spa, $actiondata);
818
-			}
819
-
820
-			// initialize exporter to get changecount
821
-			$changecount = false;
822
-			$exporter = false;
823
-			$streamimporter = false;
824
-			$newFolderStat = false;
825
-			$setupExporter = true;
826
-
827
-			// TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again
828
-			if ($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || !$spa->HasSyncKey())) {
829
-				// no need to run the exporter if the globalwindowsize is already full - if collection already has a synckey (ZP-1215)
830
-				if ($sc->GetGlobalWindowSize() == $this->globallyExportedItems && $spa->HasSyncKey()) {
831
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as GlobalWindowSize is full.", $spa->GetFolderId()));
832
-					$setupExporter = false;
833
-				}
834
-				// if the maximum request timeout is reached, stop processing other collections
835
-				if (Request::IsRequestTimeoutReached()) {
836
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as request timeout reached, omitting output for collection.", $spa->GetFolderId()));
837
-					$setupExporter = false;
838
-				}
839
-
840
-				// if max memory allocation is reached, stop processing other collections
841
-				if (Request::IsRequestMemoryLimitReached()) {
842
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as max memory allocatation reached, omitting output for collection.", $spa->GetFolderId()));
843
-					$setupExporter = false;
844
-				}
845
-
846
-				// force exporter run if there is a saved status
847
-				if ($setupExporter && self::$deviceManager->HasFolderSyncStatus($spa->GetFolderId())) {
848
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): forcing exporter setup for '%s' as a sync status is saved - ignoring backend folder stats", $spa->GetFolderId()));
849
-				}
850
-				// compare the folder statistics if the backend supports this
851
-				elseif ($setupExporter && self::$backend->HasFolderStats()) {
852
-					// check if the folder stats changed -> if not, don't setup the exporter, there are no changes!
853
-					$newFolderStat = self::$backend->GetFolderStat(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
854
-					if ($newFolderStat !== false && !$spa->IsExporterRunRequired($newFolderStat, true)) {
855
-						$changecount = 0;
856
-						$setupExporter = false;
857
-					}
858
-				}
859
-
860
-				// Do a full Exporter setup if we can't avoid it
861
-				if ($setupExporter) {
862
-					// make sure the states are loaded
863
-					$status = $this->loadStates($sc, $spa, $actiondata);
864
-
865
-					if ($status == SYNC_STATUS_SUCCESS) {
866
-						try {
867
-							// if this is an additional folder the backend has to be setup correctly
868
-							if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
869
-								throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
870
-							}
871
-
872
-							// Use the state from the importer, as changes may have already happened
873
-							$exporter = self::$backend->GetExporter($spa->GetBackendFolderId());
874
-
875
-							if ($exporter === false) {
876
-								throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
877
-							}
878
-						}
879
-						catch (StatusException $stex) {
880
-							$status = $stex->getCode();
881
-						}
882
-
883
-						try {
884
-							// Stream the messages directly to the PDA
885
-							$streamimporter = new ImportChangesStream(self::$encoder, GSync::getSyncObjectFromFolderClass($spa->GetContentClass()));
886
-
887
-							if ($exporter !== false) {
888
-								$exporter->Config($sc->GetParameter($spa, "state"));
889
-								$exporter->ConfigContentParameters($spa->GetCPO());
890
-								$exporter->InitializeExporter($streamimporter);
891
-
892
-								$changecount = $exporter->GetChangeCount();
893
-							}
894
-						}
895
-						catch (StatusException $stex) {
896
-							if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
897
-								$status = SYNC_STATUS_INVALIDSYNCKEY;
898
-							}
899
-							else {
900
-								$status = $stex->getCode();
901
-							}
902
-						}
903
-
904
-						if (!$spa->HasSyncKey()) {
905
-							self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), $this->singleFolder);
906
-							$this->saveMultiFolderInfo("queued", $changecount);
907
-							// update folder status as initialized
908
-							$spa->SetFolderSyncTotal($changecount);
909
-							$spa->SetFolderSyncRemaining($changecount);
910
-							if ($changecount > 0) {
911
-								self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
912
-							}
913
-						}
914
-						elseif ($status != SYNC_STATUS_SUCCESS) {
915
-							self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
916
-							$this->saveMultiFolderInfo("exception", "StatusException");
917
-						}
918
-						self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
919
-					}
920
-				}
921
-			}
922
-
923
-			// Get a new sync key to output to the client if any changes have been send by the mobile or a new synckey is to be sent
924
-			if (!empty($actiondata["modifyids"]) ||
925
-				!empty($actiondata["clientids"]) ||
926
-				!empty($actiondata["removeids"]) ||
927
-				(!$spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS)) {
928
-				$spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
929
-			}
930
-			// get a new synckey only if we did not reach the global limit yet
931
-			else {
932
-				// when reaching the global limit for changes of all collections, stop processing other collections (ZP-697)
933
-				if ($sc->GetGlobalWindowSize() <= $this->globallyExportedItems) {
934
-					SLog::Write(LOGLEVEL_DEBUG, "Global WindowSize for amount of exported changes reached, omitting output for collection.");
935
-
936
-					continue;
937
-				}
938
-
939
-				// get a new synckey if there are changes are we did not reach the limit yet
940
-				if ($changecount > 0) {
941
-					$spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
942
-				}
943
-			}
944
-
945
-			// Fir AS 14.0+ omit output for folder, if there were no incoming or outgoing changes and no Fetch
946
-			if (Request::GetProtocolVersion() >= 14.0 && !$spa->HasNewSyncKey() && $changecount == 0 && empty($actiondata["fetchids"]) && $status == SYNC_STATUS_SUCCESS &&
947
-					!$spa->HasConfirmationChanged() && ($newFolderStat === false || !$spa->IsExporterRunRequired($newFolderStat))) {
948
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync: No changes found for %s folder id '%s'. Omitting output.", $spa->GetContentClass(), $spa->GetFolderId()));
949
-
950
-				continue;
951
-			}
952
-
953
-			// if there are no other responses sent, we should end with a global status
954
-			if ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED && $this->startTagsSent === false) {
955
-				$this->sendStartTags();
956
-				self::$encoder->startTag(SYNC_STATUS);
957
-				self::$encoder->content($status);
958
-				self::$encoder->endTag();
959
-				self::$encoder->endTag(); // SYNC_SYNCHRONIZE
960
-
961
-				return true;
962
-			}
963
-
964
-			// there is something to send here, sync folder to output
965
-			$this->syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat);
966
-
967
-			// reset status for the next folder
968
-			$status = SYNC_STATUS_SUCCESS;
969
-		} // END foreach collection
970
-
971
-		// SYNC_FOLDERS - only if the starttag was sent
972
-		if ($this->startFolderTagSent) {
973
-			self::$encoder->endTag();
974
-		}
975
-
976
-		// Check if there was any response - in case of an empty sync request, we shouldn't send an empty answer (ZP-1241)
977
-		if (!$this->startTagsSent && $emptysync === true) {
978
-			$this->sendStartTags();
979
-			self::$encoder->startTag(SYNC_STATUS);
980
-			self::$encoder->content(SYNC_STATUS_SYNCREQUESTINCOMPLETE);
981
-			self::$encoder->endTag();
982
-		}
983
-
984
-		// SYNC_SYNCHRONIZE - only if the starttag was sent
985
-		if ($this->startTagsSent) {
986
-			self::$encoder->endTag();
987
-		}
988
-
989
-		// final top announcement for a multi-folder sync
990
-		if ($sc->GetCollectionCount() > 1) {
991
-			self::$topCollector->AnnounceInformation($this->getMultiFolderInfoLine($sc->GetCollectionCount()), true);
992
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync: Processed %d folders", $sc->GetCollectionCount()));
993
-		}
994
-
995
-		// update the waittime waited
996
-		self::$waitTime = $sc->GetWaitedSeconds();
997
-
998
-		return true;
999
-	}
1000
-
1001
-	/**
1002
-	 * Sends the SYNC_SYNCHRONIZE once per request.
1003
-	 */
1004
-	private function sendStartTags() {
1005
-		if ($this->startTagsSent === false) {
1006
-			self::$encoder->startWBXML();
1007
-			self::$encoder->startTag(SYNC_SYNCHRONIZE);
1008
-			$this->startTagsSent = true;
1009
-		}
1010
-	}
1011
-
1012
-	/**
1013
-	 * Sends the SYNC_FOLDERS once per request.
1014
-	 */
1015
-	private function sendFolderStartTag() {
1016
-		$this->sendStartTags();
1017
-		if ($this->startFolderTagSent === false) {
1018
-			self::$encoder->startTag(SYNC_FOLDERS);
1019
-			$this->startFolderTagSent = true;
1020
-		}
1021
-	}
1022
-
1023
-	/**
1024
-	 * Synchronizes a folder to the output stream. Changes for this folders are expected.
1025
-	 *
1026
-	 * @param SyncCollections     $sc
1027
-	 * @param SyncParameters      $spa
1028
-	 * @param IExportChanges      $exporter       Fully configured exporter for this folder
1029
-	 * @param int                 $changecount    Amount of changes expected
1030
-	 * @param ImportChangesStream $streamimporter Output stream
1031
-	 * @param int                 $status         current status of the folder processing
1032
-	 * @param string              $newFolderStat  the new folder stat to be set if everything was exported
1033
-	 *
1034
-	 * @throws StatusException
1035
-	 *
1036
-	 * @return int sync status code
1037
-	 */
1038
-	private function syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat) {
1039
-		$actiondata = $sc->GetParameter($spa, "actiondata");
1040
-
1041
-		// send the WBXML start tags (if not happened already)
1042
-		$this->sendFolderStartTag();
1043
-		self::$encoder->startTag(SYNC_FOLDER);
1044
-
1045
-		if ($spa->HasContentClass()) {
1046
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass()));
1047
-			// AS 12.0 devices require content class
1048
-			if (Request::GetProtocolVersion() < 12.1) {
1049
-				self::$encoder->startTag(SYNC_FOLDERTYPE);
1050
-				self::$encoder->content($spa->GetContentClass());
1051
-				self::$encoder->endTag();
1052
-			}
1053
-		}
1054
-
1055
-		self::$encoder->startTag(SYNC_SYNCKEY);
1056
-		if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
1057
-			self::$encoder->content($spa->GetNewSyncKey());
1058
-		}
1059
-		else {
1060
-			self::$encoder->content($spa->GetSyncKey());
1061
-		}
1062
-		self::$encoder->endTag();
1063
-
1064
-		self::$encoder->startTag(SYNC_FOLDERID);
1065
-		self::$encoder->content($spa->GetFolderId());
1066
-		self::$encoder->endTag();
1067
-
1068
-		self::$encoder->startTag(SYNC_STATUS);
1069
-		self::$encoder->content($status);
1070
-		self::$encoder->endTag();
1071
-
1072
-		// announce failing status to the process loop detection
1073
-		if ($status !== SYNC_STATUS_SUCCESS) {
1074
-			self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
1075
-		}
1076
-
1077
-		// Output IDs and status for incoming items & requests
1078
-		if ($status == SYNC_STATUS_SUCCESS && (
1079
-			!empty($actiondata["clientids"]) ||
1080
-				!empty($actiondata["modifyids"]) ||
1081
-				!empty($actiondata["removeids"]) ||
1082
-				!empty($actiondata["fetchids"])
1083
-		)) {
1084
-			self::$encoder->startTag(SYNC_REPLIES);
1085
-			// output result of all new incoming items
1086
-			foreach ($actiondata["clientids"] as $clientid => $serverid) {
1087
-				self::$encoder->startTag(SYNC_ADD);
1088
-				self::$encoder->startTag(SYNC_CLIENTENTRYID);
1089
-				self::$encoder->content($clientid);
1090
-				self::$encoder->endTag();
1091
-				if ($serverid) {
1092
-					self::$encoder->startTag(SYNC_SERVERENTRYID);
1093
-					self::$encoder->content($serverid);
1094
-					self::$encoder->endTag();
1095
-				}
1096
-				self::$encoder->startTag(SYNC_STATUS);
1097
-				self::$encoder->content((isset($actiondata["statusids"][$clientid]) ? $actiondata["statusids"][$clientid] : SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR));
1098
-				self::$encoder->endTag();
1099
-				self::$encoder->endTag();
1100
-			}
1101
-
1102
-			// loop through modify operations which were not a success, send status
1103
-			foreach ($actiondata["modifyids"] as $serverid) {
1104
-				if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
1105
-					self::$encoder->startTag(SYNC_MODIFY);
1106
-					self::$encoder->startTag(SYNC_SERVERENTRYID);
1107
-					self::$encoder->content($serverid);
1108
-					self::$encoder->endTag();
1109
-					self::$encoder->startTag(SYNC_STATUS);
1110
-					self::$encoder->content($actiondata["statusids"][$serverid]);
1111
-					self::$encoder->endTag();
1112
-					self::$encoder->endTag();
1113
-				}
1114
-			}
1115
-
1116
-			// loop through remove operations which were not a success, send status
1117
-			foreach ($actiondata["removeids"] as $serverid) {
1118
-				if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
1119
-					self::$encoder->startTag(SYNC_REMOVE);
1120
-					self::$encoder->startTag(SYNC_SERVERENTRYID);
1121
-					self::$encoder->content($serverid);
1122
-					self::$encoder->endTag();
1123
-					self::$encoder->startTag(SYNC_STATUS);
1124
-					self::$encoder->content($actiondata["statusids"][$serverid]);
1125
-					self::$encoder->endTag();
1126
-					self::$encoder->endTag();
1127
-				}
1128
-			}
1129
-
1130
-			if (!empty($actiondata["fetchids"])) {
1131
-				self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), $this->singleFolder);
1132
-				$this->saveMultiFolderInfo("fetching", count($actiondata["fetchids"]));
1133
-			}
1134
-
1135
-			foreach ($actiondata["fetchids"] as $id) {
1136
-				$data = false;
1137
-
1138
-				try {
1139
-					$fetchstatus = SYNC_STATUS_SUCCESS;
1140
-
1141
-					// if this is an additional folder the backend has to be setup correctly
1142
-					if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
1143
-						throw new StatusException(sprintf("HandleSync(): could not Setup() the backend to fetch in folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_OBJECTNOTFOUND);
1144
-					}
1145
-
1146
-					$data = self::$backend->Fetch($spa->GetBackendFolderId(), $id, $spa->GetCPO());
1147
-
1148
-					// check if the message is broken
1149
-					if (GSync::GetDeviceManager(false) && GSync::GetDeviceManager()->DoNotStreamMessage($id, $data)) {
1150
-						SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
1151
-						$fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1152
-					}
1153
-				}
1154
-				catch (StatusException $stex) {
1155
-					$fetchstatus = $stex->getCode();
1156
-				}
1157
-
1158
-				self::$encoder->startTag(SYNC_FETCH);
1159
-				self::$encoder->startTag(SYNC_SERVERENTRYID);
1160
-				self::$encoder->content($id);
1161
-				self::$encoder->endTag();
1162
-
1163
-				self::$encoder->startTag(SYNC_STATUS);
1164
-				self::$encoder->content($fetchstatus);
1165
-				self::$encoder->endTag();
1166
-
1167
-				if ($data !== false && $status == SYNC_STATUS_SUCCESS) {
1168
-					self::$encoder->startTag(SYNC_DATA);
1169
-					$data->Encode(self::$encoder);
1170
-					self::$encoder->endTag();
1171
-				}
1172
-				else {
1173
-					SLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
1174
-				}
1175
-				self::$encoder->endTag();
1176
-			}
1177
-			self::$encoder->endTag();
1178
-		}
1179
-
1180
-		if ($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) {
1181
-			$moreAvailableSent = false;
1182
-			$windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount);
1183
-
1184
-			// limit windowSize to the max available limit of the global window size left
1185
-			$globallyAvailable = $sc->GetGlobalWindowSize() - $this->globallyExportedItems;
1186
-			if ($changecount > $globallyAvailable && $windowSize > $globallyAvailable) {
1187
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Limit window size to %d as the global window size limit will be reached", $globallyAvailable));
1188
-				$windowSize = $globallyAvailable;
1189
-			}
1190
-			// send <MoreAvailable/> if there are more changes than fit in the folder windowsize
1191
-			// or there is a move state (another sync should be done afterwards)
1192
-			if ($changecount > $windowSize) {
1193
-				self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
1194
-				$moreAvailableSent = true;
1195
-				$spa->DelFolderStat();
1196
-			}
1197
-		}
1198
-
1199
-		// Stream outgoing changes
1200
-		if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0 && (bool) $exporter) {
1201
-			self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", (($changecount > $windowSize) ? $windowSize : $changecount)));
1202
-
1203
-			// Output message changes per folder
1204
-			self::$encoder->startTag(SYNC_PERFORM);
1205
-
1206
-			$n = 0;
1207
-			WBXMLDecoder::ResetInWhile("syncSynchronize");
1208
-			while (WBXMLDecoder::InWhile("syncSynchronize")) {
1209
-				try {
1210
-					$progress = $exporter->Synchronize();
1211
-					if (!is_array($progress)) {
1212
-						break;
1213
-					}
1214
-					++$n;
1215
-					if ($n % 10 == 0) {
1216
-						self::$topCollector->AnnounceInformation(sprintf("Streamed data of %d objects out of %d", $n, (($changecount > $windowSize) ? $windowSize : $changecount)));
1217
-					}
1218
-				}
1219
-				catch (SyncObjectBrokenException $mbe) {
1220
-					$brokenSO = $mbe->GetSyncObject();
1221
-					if (!$brokenSO) {
1222
-						SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
1223
-					}
1224
-					else {
1225
-						if (!isset($brokenSO->id)) {
1226
-							$brokenSO->id = "Unknown ID";
1227
-							SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
1228
-						}
1229
-						self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO);
1230
-					}
1231
-				}
1232
-				// something really bad happened while exporting changes
1233
-				catch (StatusException $stex) {
1234
-					$status = $stex->getCode();
1235
-					// during export we found out that the states should be thrown away (ZP-623)
1236
-					if ($status == SYNC_STATUS_INVALIDSYNCKEY) {
1237
-						self::$deviceManager->ForceFolderResync($spa->GetFolderId());
1238
-
1239
-						break;
1240
-					}
1241
-				}
1242
-
1243
-				if ($n >= $windowSize || Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
1244
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
1245
-
1246
-					break;
1247
-				}
1248
-			}
1249
-
1250
-			// $progress is not an array when exporting the last message
1251
-			// so we get the number to display from the streamimporter if it's available
1252
-			if ((bool) $streamimporter) {
1253
-				$n = $streamimporter->GetImportedMessages();
1254
-			}
1255
-
1256
-			self::$encoder->endTag();
1257
-
1258
-			// log the request timeout
1259
-			if (Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
1260
-				SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Stopping export as limits of request timeout or available memory are almost reached!");
1261
-				// Send a <MoreAvailable/> tag if we reached the request timeout or max memory, there are more changes and a moreavailable was not already send
1262
-				if (!$moreAvailableSent && ($n > $windowSize)) {
1263
-					self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
1264
-					$spa->DelFolderStat();
1265
-					$moreAvailableSent = true;
1266
-				}
1267
-			}
1268
-
1269
-			self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, ($n >= $windowSize) ? " of " . $changecount : ""), $this->singleFolder);
1270
-			$this->saveMultiFolderInfo("outgoing", $n);
1271
-			$this->saveMultiFolderInfo("queued", $changecount);
1272
-
1273
-			$this->globallyExportedItems += $n;
1274
-
1275
-			// update folder status, if there is something set
1276
-			if ($spa->GetFolderSyncRemaining() && $changecount > 0) {
1277
-				$spa->SetFolderSyncRemaining($changecount);
1278
-			}
1279
-			// changecount is initialized with 'false', so 0 means no changes!
1280
-			if ($changecount === 0 || ($changecount !== false && $changecount <= $windowSize)) {
1281
-				self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_COMPLETED);
1282
-
1283
-				// we should update the folderstat, but we recheck to see if it changed. If so, it's not updated to force another sync
1284
-				if (self::$backend->HasFolderStats()) {
1285
-					$newFolderStatAfterExport = self::$backend->GetFolderStat(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
1286
-					if ($newFolderStat === $newFolderStatAfterExport) {
1287
-						$this->setFolderStat($spa, $newFolderStat);
1288
-					}
1289
-					else {
1290
-						SLog::Write(LOGLEVEL_DEBUG, "Sync() Folderstat differs after export, force another exporter run.");
1291
-					}
1292
-				}
1293
-			}
1294
-			else {
1295
-				self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS);
1296
-			}
1297
-		}
1298
-
1299
-		self::$encoder->endTag();
1300
-
1301
-		// Save the sync state for the next time
1302
-		if ($spa->HasNewSyncKey()) {
1303
-			self::$topCollector->AnnounceInformation("Saving state");
1304
-
1305
-			try {
1306
-				if (isset($exporter) && $exporter) {
1307
-					$state = $exporter->GetState();
1308
-				}
1309
-
1310
-				// nothing exported, but possibly imported - get the importer state
1311
-				elseif ($sc->GetParameter($spa, "state") !== null) {
1312
-					$state = $sc->GetParameter($spa, "state");
1313
-				}
1314
-
1315
-				// if a new request without state information (hierarchy) save an empty state
1316
-				elseif (!$spa->HasSyncKey()) {
1317
-					$state = "";
1318
-				}
1319
-			}
1320
-			catch (StatusException $stex) {
1321
-				$status = $stex->getCode();
1322
-			}
1323
-
1324
-			if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
1325
-				self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
1326
-			}
1327
-			else {
1328
-				SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
1329
-			}
1330
-		}
1331
-
1332
-		// save SyncParameters
1333
-		if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) {
1334
-			$sc->SaveCollection($spa);
1335
-		}
1336
-
1337
-		return $status;
1338
-	}
1339
-
1340
-	/**
1341
-	 * Loads the states and writes them into the SyncCollection Object and the actiondata failstate.
1342
-	 *
1343
-	 * @param SyncCollection $sc           SyncCollection object
1344
-	 * @param SyncParameters $spa          SyncParameters object
1345
-	 * @param array          $actiondata   Actiondata array
1346
-	 * @param bool           $loadFailsave (opt) default false - indicates if the failsave states should be loaded
1347
-	 *
1348
-	 * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS
1349
-	 */
1350
-	private function loadStates($sc, $spa, &$actiondata, $loadFailsave = false) {
1351
-		$status = SYNC_STATUS_SUCCESS;
1352
-
1353
-		if ($sc->GetParameter($spa, "state") == null) {
1354
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync->loadStates(): loading states for folder '%s'", $spa->GetFolderId()));
1355
-
1356
-			try {
1357
-				$sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
1358
-
1359
-				if ($loadFailsave) {
1360
-					// if this request was made before, there will be a failstate available
1361
-					$actiondata["failstate"] = self::$deviceManager->GetStateManager()->GetSyncFailState();
1362
-				}
1363
-
1364
-				// if this is an additional folder the backend has to be setup correctly
1365
-				if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
1366
-					throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
1367
-				}
1368
-			}
1369
-			catch (StateNotFoundException $snfex) {
1370
-				$status = SYNC_STATUS_INVALIDSYNCKEY;
1371
-				self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
1372
-				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
1373
-			}
1374
-			catch (StatusException $stex) {
1375
-				$status = $stex->getCode();
1376
-				self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
1377
-				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
1378
-			}
1379
-		}
1380
-
1381
-		return $status;
1382
-	}
1383
-
1384
-	/**
1385
-	 * Initializes the importer for the SyncParameters folder, loads necessary
1386
-	 * states (incl. failsave states) and initializes the conflict detection.
1387
-	 *
1388
-	 * @param SyncCollection $sc         SyncCollection object
1389
-	 * @param SyncParameters $spa        SyncParameters object
1390
-	 * @param array          $actiondata Actiondata array
1391
-	 *
1392
-	 * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS
1393
-	 */
1394
-	private function getImporter($sc, $spa, &$actiondata) {
1395
-		SLog::Write(LOGLEVEL_DEBUG, "Sync->getImporter(): initialize importer");
1396
-		$status = SYNC_STATUS_SUCCESS;
1397
-
1398
-		// load the states with failsave data
1399
-		$status = $this->loadStates($sc, $spa, $actiondata, true);
1400
-
1401
-		try {
1402
-			if ($status == SYNC_STATUS_SUCCESS) {
1403
-				// Configure importer with last state
1404
-				$this->importer = self::$backend->GetImporter($spa->GetBackendFolderId());
1405
-
1406
-				// if something goes wrong, ask the mobile to resync the hierarchy
1407
-				if ($this->importer === false) {
1408
-					throw new StatusException(sprintf("Sync->getImporter(): no importer for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
1409
-				}
1410
-
1411
-				// if there is a valid state obtained after importing changes in a previous loop, we use that state
1412
-				if (isset($actiondata["failstate"], $actiondata["failstate"]["failedsyncstate"])) {
1413
-					$this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict());
1414
-				}
1415
-				else {
1416
-					$this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict());
1417
-				}
1418
-
1419
-				// the CPO is also needed by the importer to check if imported changes are inside the sync window - see ZP-258
1420
-				$this->importer->ConfigContentParameters($spa->GetCPO());
1421
-				$this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state"));
1422
-			}
1423
-		}
1424
-		catch (StatusException $stex) {
1425
-			$status = $stex->getCode();
1426
-		}
1427
-
1428
-		return $status;
1429
-	}
1430
-
1431
-	/**
1432
-	 * Imports a message.
1433
-	 *
1434
-	 * @param SyncParameters $spa          SyncParameters object
1435
-	 * @param array          $actiondata   Actiondata array
1436
-	 * @param int            $todo         WBXML flag indicating how message should be imported.
1437
-	 *                                     Valid values: SYNC_ADD, SYNC_MODIFY, SYNC_REMOVE
1438
-	 * @param SyncObject     $message      SyncObject message to be imported
1439
-	 * @param string         $clientid     Client message identifier
1440
-	 * @param string         $serverid     Server message identifier
1441
-	 * @param string         $foldertype   On sms sync, this says "SMS", else false
1442
-	 * @param int            $messageCount Counter of already imported messages
1443
-	 *
1444
-	 * @throws StatusException in case the importer is not available
1445
-	 *
1446
-	 * @return - message related status are returned in the actiondata
1447
-	 */
1448
-	private function importMessage($spa, &$actiondata, $todo, $message, $clientid, $serverid, $foldertype, $messageCount) {
1449
-		// the importer needs to be available!
1450
-		if ($this->importer == false) {
1451
-			throw new StatusException("Sync->importMessage(): importer not available", SYNC_STATUS_SERVERERROR);
1452
-		}
1453
-
1454
-		// mark this state as used, e.g. for HeartBeat
1455
-		self::$deviceManager->SetHeartbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter());
1456
-
1457
-		// Detect incoming loop
1458
-		// messages which were created/removed before will not have the same action executed again
1459
-		// if a message is edited we perform this action "again", as the message could have been changed on the mobile in the meantime
1460
-		$ignoreMessage = false;
1461
-		if ($actiondata["failstate"]) {
1462
-			// message was ADDED before, do NOT add it again
1463
-			if ($todo == SYNC_ADD && isset($actiondata["failstate"]["clientids"][$clientid])) {
1464
-				$ignoreMessage = true;
1465
-
1466
-				// make sure no messages are sent back
1467
-				self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0);
1468
-
1469
-				$actiondata["clientids"][$clientid] = $actiondata["failstate"]["clientids"][$clientid];
1470
-				$actiondata["statusids"][$clientid] = $actiondata["failstate"]["statusids"][$clientid];
1471
-
1472
-				SLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Incoming new message '%s' was created on the server before. Replying with known new server id: %s", $clientid, $actiondata["clientids"][$clientid]));
1473
-			}
1474
-
1475
-			// message was REMOVED before, do NOT attempt to remove it again
1476
-			if ($todo == SYNC_REMOVE && isset($actiondata["failstate"]["removeids"][$serverid])) {
1477
-				$ignoreMessage = true;
1478
-
1479
-				// make sure no messages are sent back
1480
-				self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0);
1481
-
1482
-				$actiondata["removeids"][$serverid] = $actiondata["failstate"]["removeids"][$serverid];
1483
-				$actiondata["statusids"][$serverid] = $actiondata["failstate"]["statusids"][$serverid];
1484
-
1485
-				SLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Message '%s' was deleted by the mobile before. Replying with known status: %s", $clientid, $actiondata["statusids"][$serverid]));
1486
-			}
1487
-		}
1488
-
1489
-		if (!$ignoreMessage) {
1490
-			switch ($todo) {
1491
-				case SYNC_MODIFY:
1492
-					self::$topCollector->AnnounceInformation(sprintf("Saving modified message %d", $messageCount));
1493
-
1494
-					try {
1495
-						$actiondata["modifyids"][] = $serverid;
1496
-
1497
-						// ignore sms messages
1498
-						if ($foldertype == "SMS" || stripos($serverid, self::GSYNCIGNORESMS) !== false) {
1499
-							SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1500
-							// TODO we should update the SMS
1501
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1502
-						}
1503
-						// check incoming message without logging WARN messages about errors
1504
-						elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1505
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1506
-						}
1507
-						else {
1508
-							if (isset($message->read)) {
1509
-								// Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag.
1510
-								$this->importer->ImportMessageReadFlag($serverid, $message->read);
1511
-							}
1512
-							elseif (!isset($message->flag)) {
1513
-								$this->importer->ImportMessageChange($serverid, $message);
1514
-							}
1515
-
1516
-							// email todoflags - some devices send todos flags together with read flags,
1517
-							// so they have to be handled separately
1518
-							if (isset($message->flag)) {
1519
-								$this->importer->ImportMessageChange($serverid, $message);
1520
-							}
1521
-
1522
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1523
-						}
1524
-					}
1525
-					catch (StatusException $stex) {
1526
-						$actiondata["statusids"][$serverid] = $stex->getCode();
1527
-					}
1528
-					break;
1529
-
1530
-				case SYNC_ADD:
1531
-					self::$topCollector->AnnounceInformation(sprintf("Creating new message from mobile %d", $messageCount));
1532
-
1533
-					try {
1534
-						// mark the message as new message so SyncObject->Check() can differentiate
1535
-						$message->flags = SYNC_NEWMESSAGE;
1536
-
1537
-						// ignore sms messages
1538
-						if ($foldertype == "SMS") {
1539
-							SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1540
-							// TODO we should create the SMS
1541
-							// return a fake serverid which we can identify later
1542
-							$actiondata["clientids"][$clientid] = self::GSYNCIGNORESMS . $clientid;
1543
-							$actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1544
-						}
1545
-						// check incoming message without logging WARN messages about errors
1546
-						elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1547
-							$actiondata["clientids"][$clientid] = false;
1548
-							$actiondata["statusids"][$clientid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1549
-						}
1550
-						else {
1551
-							$actiondata["clientids"][$clientid] = false;
1552
-							$actiondata["clientids"][$clientid] = $this->importer->ImportMessageChange(false, $message);
1553
-							$actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1554
-						}
1555
-					}
1556
-					catch (StatusException $stex) {
1557
-						$actiondata["statusids"][$clientid] = $stex->getCode();
1558
-					}
1559
-					break;
1560
-
1561
-				case SYNC_REMOVE:
1562
-					self::$topCollector->AnnounceInformation(sprintf("Deleting message removed on mobile %d", $messageCount));
1563
-
1564
-					try {
1565
-						$actiondata["removeids"][] = $serverid;
1566
-						// ignore sms messages
1567
-						if ($foldertype == "SMS" || stripos($serverid, self::GSYNCIGNORESMS) !== false) {
1568
-							SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1569
-							// TODO we should delete the SMS
1570
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1571
-						}
1572
-						else {
1573
-							// if message deletions are to be moved, move them
1574
-							if ($spa->GetDeletesAsMoves()) {
1575
-								$folderid = self::$backend->GetWasteBasket();
1576
-
1577
-								if ($folderid) {
1578
-									$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1579
-									$this->importer->ImportMessageMove($serverid, $folderid);
1580
-
1581
-									break;
1582
-								}
1583
-
1584
-								SLog::Write(LOGLEVEL_WARN, "Message should be moved to WasteBasket, but the Backend did not return a destination ID. Message is hard deleted now!");
1585
-							}
1586
-
1587
-							$this->importer->ImportMessageDeletion($serverid);
1588
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1589
-						}
1590
-					}
1591
-					catch (StatusException $stex) {
1592
-						if ($stex->getCode() != SYNC_MOVEITEMSSTATUS_SUCCESS) {
1593
-							$actiondata["statusids"][$serverid] = SYNC_STATUS_OBJECTNOTFOUND;
1594
-						}
1595
-					}
1596
-					break;
1597
-			}
1598
-			SLog::Write(LOGLEVEL_DEBUG, "Sync->importMessage(): message imported");
1599
-		}
1600
-	}
1601
-
1602
-	/**
1603
-	 * Keeps some interesting information about the sync process of several folders.
1604
-	 *
1605
-	 * @param mixed $key
1606
-	 * @param mixed $value
1607
-	 *
1608
-	 * @return
1609
-	 */
1610
-	private function saveMultiFolderInfo($key, $value) {
1611
-		if ($key == "incoming" || $key == "outgoing" || $key == "queued" || $key == "fetching") {
1612
-			if (!isset($this->multiFolderInfo[$key])) {
1613
-				$this->multiFolderInfo[$key] = 0;
1614
-			}
1615
-			$this->multiFolderInfo[$key] += $value;
1616
-		}
1617
-		if ($key == "exception") {
1618
-			if (!isset($this->multiFolderInfo[$key])) {
1619
-				$this->multiFolderInfo[$key] = [];
1620
-			}
1621
-			$this->multiFolderInfo[$key][] = $value;
1622
-		}
1623
-	}
1624
-
1625
-	/**
1626
-	 * Returns a single string with information about the multi folder synchronization.
1627
-	 *
1628
-	 * @param int $amountOfFolders
1629
-	 *
1630
-	 * @return string
1631
-	 */
1632
-	private function getMultiFolderInfoLine($amountOfFolders) {
1633
-		$s = $amountOfFolders . " folders";
1634
-		if (isset($this->multiFolderInfo["incoming"])) {
1635
-			$s .= ": " . $this->multiFolderInfo["incoming"] . " saved";
1636
-		}
1637
-		if (isset($this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]) && $this->multiFolderInfo["outgoing"] > 0) {
1638
-			$s .= sprintf(": Streamed %d out of %d", $this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]);
1639
-		}
1640
-		elseif (!isset($this->multiFolderInfo["outgoing"]) && !isset($this->multiFolderInfo["queued"])) {
1641
-			$s .= ": no changes";
1642
-		}
1643
-		else {
1644
-			if (isset($this->multiFolderInfo["outgoing"])) {
1645
-				$s .= "/" . $this->multiFolderInfo["outgoing"] . " streamed";
1646
-			}
1647
-			if (isset($this->multiFolderInfo["queued"])) {
1648
-				$s .= "/" . $this->multiFolderInfo["queued"] . " queued";
1649
-			}
1650
-		}
1651
-		if (isset($this->multiFolderInfo["exception"])) {
1652
-			$exceptions = array_count_values($this->multiFolderInfo["exception"]);
1653
-			foreach ($exceptions as $name => $count) {
1654
-				$s .= sprintf("-%s(%d)", $name, $count);
1655
-			}
1656
-		}
1657
-
1658
-		return $s;
1659
-	}
1660
-
1661
-	/**
1662
-	 * Sets the new folderstat and calculates & sets an expiration date for the folder stat.
1663
-	 *
1664
-	 * @param SyncParameters $spa
1665
-	 * @param string         $newFolderStat
1666
-	 *
1667
-	 * @return
1668
-	 */
1669
-	private function setFolderStat($spa, $newFolderStat) {
1670
-		$spa->SetFolderStat($newFolderStat);
1671
-		$maxTimeout = 60 * 60 * 24 * 31; // one month
1672
-
1673
-		$interval = Utils::GetFiltertypeInterval($spa->GetFilterType());
1674
-		$timeout = time() + (($interval && $interval < $maxTimeout) ? $interval : $maxTimeout);
1675
-		// randomize timeout in 12h
1676
-		$timeout -= rand(0, 43200);
1677
-		SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync()->setFolderStat() on %s: %s expiring %s", $spa->getFolderId(), $newFolderStat, date('Y-m-d H:i:s', $timeout)));
1678
-		$spa->SetFolderStatTimeout($timeout);
1679
-	}
11
+    // Ignored SMS identifier
12
+    public const GSYNCIGNORESMS = "ZPISMS";
13
+    private $importer;
14
+    private $globallyExportedItems;
15
+    private $singleFolder;
16
+    private $multiFolderInfo;
17
+    private $startTagsSent = false;
18
+    private $startFolderTagSent = false;
19
+
20
+    /**
21
+     * Handles the Sync command
22
+     * Performs the synchronization of messages.
23
+     *
24
+     * @param int $commandCode
25
+     *
26
+     * @return bool
27
+     */
28
+    public function Handle($commandCode) {
29
+        // Contains all requested folders (containers)
30
+        $sc = new SyncCollections();
31
+        $status = SYNC_STATUS_SUCCESS;
32
+        $wbxmlproblem = false;
33
+        $emptysync = false;
34
+        $this->singleFolder = true;
35
+        $this->multiFolderInfo = [];
36
+        $this->globallyExportedItems = 0;
37
+
38
+        // check if the hierarchySync was fully completed
39
+        if (USE_PARTIAL_FOLDERSYNC) {
40
+            if (self::$deviceManager->GetFolderSyncComplete() === false) {
41
+                SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
42
+                self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
43
+                $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
44
+            }
45
+            else {
46
+                SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
47
+            }
48
+        }
49
+
50
+        // Start Synchronize
51
+        if (self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) {
52
+            // AS 1.0 sends version information in WBXML
53
+            if (self::$decoder->getElementStartTag(SYNC_VERSION)) {
54
+                $sync_version = self::$decoder->getElementContent();
55
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version));
56
+                if (!self::$decoder->getElementEndTag()) {
57
+                    return false;
58
+                }
59
+            }
60
+
61
+            // Syncing specified folders
62
+            // Android still sends heartbeat sync even if all syncfolders are disabled.
63
+            // Check if Folders tag is empty (<Folders/>) and only sync if there are
64
+            // some folders in the request. See ZP-172
65
+            $startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS);
66
+            if (isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) {
67
+                while (self::$decoder->getElementStartTag(SYNC_FOLDER)) {
68
+                    $actiondata = [];
69
+                    $actiondata["requested"] = true;
70
+                    $actiondata["clientids"] = [];
71
+                    $actiondata["modifyids"] = [];
72
+                    $actiondata["removeids"] = [];
73
+                    $actiondata["fetchids"] = [];
74
+                    $actiondata["statusids"] = [];
75
+
76
+                    // read class, synckey and folderid without SyncParameters Object for now
77
+                    $class = $synckey = $folderid = false;
78
+
79
+                    // if there are already collections in SyncCollections, this is min. the second folder
80
+                    if ($sc->HasCollections()) {
81
+                        $this->singleFolder = false;
82
+                    }
83
+
84
+                    // for AS versions < 2.5
85
+                    if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
86
+                        $class = self::$decoder->getElementContent();
87
+                        SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class));
88
+
89
+                        if (!self::$decoder->getElementEndTag()) {
90
+                            return false;
91
+                        }
92
+                    }
93
+
94
+                    // SyncKey
95
+                    if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
96
+                        $synckey = "0";
97
+                        if (($synckey = self::$decoder->getElementContent()) !== false) {
98
+                            if (!self::$decoder->getElementEndTag()) {
99
+                                return false;
100
+                            }
101
+                        }
102
+                    }
103
+                    else {
104
+                        return false;
105
+                    }
106
+
107
+                    // FolderId
108
+                    if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
109
+                        $folderid = self::$decoder->getElementContent();
110
+
111
+                        if (!self::$decoder->getElementEndTag()) {
112
+                            return false;
113
+                        }
114
+                    }
115
+
116
+                    // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
117
+                    if (!$folderid && $class) {
118
+                        $folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class);
119
+                    }
120
+
121
+                    // folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update
122
+                    try {
123
+                        $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid);
124
+
125
+                        // TODO remove resync of folders
126
+                        // this forces a resync of all states
127
+                        if (!$spa instanceof SyncParameters) {
128
+                            throw new StateInvalidException("Saved state are not of type SyncParameters");
129
+                        }
130
+
131
+                        // new/resync requested
132
+                        if ($synckey == "0") {
133
+                            $spa->RemoveSyncKey();
134
+                            $spa->DelFolderStat();
135
+                            $spa->SetMoveState(false);
136
+                        }
137
+                        elseif ($synckey !== false) {
138
+                            if ($synckey !== $spa->GetSyncKey() && $synckey !== $spa->GetNewSyncKey()) {
139
+                                SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Synckey does not match latest saved for this folder or there is a move state, removing folderstat to force Exporter setup");
140
+                                $spa->DelFolderStat();
141
+                            }
142
+                            $spa->SetSyncKey($synckey);
143
+                        }
144
+                    }
145
+                    catch (StateInvalidException $stie) {
146
+                        $spa = new SyncParameters();
147
+                        $status = SYNC_STATUS_INVALIDSYNCKEY;
148
+                        self::$topCollector->AnnounceInformation("State invalid - Resync folder", $this->singleFolder);
149
+                        self::$deviceManager->ForceFolderResync($folderid);
150
+                        $this->saveMultiFolderInfo("exception", "StateInvalidException");
151
+                    }
152
+
153
+                    // update folderid.. this might be a new object
154
+                    $spa->SetFolderId($folderid);
155
+                    $spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($folderid));
156
+
157
+                    if ($class !== false) {
158
+                        $spa->SetContentClass($class);
159
+                    }
160
+
161
+                    // Get class for as versions >= 12.0
162
+                    if (!$spa->HasContentClass()) {
163
+                        try {
164
+                            $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
165
+                            SLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
166
+                        }
167
+                        catch (NoHierarchyCacheAvailableException $nhca) {
168
+                            $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
169
+                            self::$deviceManager->ForceFullResync();
170
+                        }
171
+                    }
172
+
173
+                    // done basic SPA initialization/loading -> add to SyncCollection
174
+                    $sc->AddCollection($spa);
175
+                    $sc->AddParameter($spa, "requested", true);
176
+
177
+                    if ($spa->HasContentClass()) {
178
+                        self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), $this->singleFolder);
179
+                    }
180
+                    else {
181
+                        SLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
182
+                    }
183
+
184
+                    // SUPPORTED properties
185
+                    if (($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) {
186
+                        // ZP-481: LG phones send an empty supported tag, so only read the contents if available here
187
+                        // if <Supported/> is received, it's as no supported fields would have been sent at all.
188
+                        // unsure if this is the correct approach, or if in this case some default list should be used
189
+                        if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) {
190
+                            $supfields = [];
191
+                            WBXMLDecoder::ResetInWhile("syncSupported");
192
+                            while (WBXMLDecoder::InWhile("syncSupported")) {
193
+                                $el = self::$decoder->getElement();
194
+
195
+                                if ($el[EN_TYPE] == EN_TYPE_ENDTAG) {
196
+                                    break;
197
+                                }
198
+
199
+                                $supfields[] = $el[EN_TAG];
200
+                            }
201
+                            self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields);
202
+                        }
203
+                    }
204
+
205
+                    // Deletes as moves can be an empty tag as well as have value
206
+                    if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
207
+                        $spa->SetDeletesAsMoves(true);
208
+                        if (($dam = self::$decoder->getElementContent()) !== false) {
209
+                            $spa->SetDeletesAsMoves((bool) $dam);
210
+                            if (!self::$decoder->getElementEndTag()) {
211
+                                return false;
212
+                            }
213
+                        }
214
+                    }
215
+
216
+                    // Get changes can be an empty tag as well as have value
217
+                    // code block partly contributed by dw2412
218
+                    if ($starttag = self::$decoder->getElementStartTag(SYNC_GETCHANGES)) {
219
+                        $sc->AddParameter($spa, "getchanges", true);
220
+                        if (($gc = self::$decoder->getElementContent()) !== false) {
221
+                            $sc->AddParameter($spa, "getchanges", $gc);
222
+                        }
223
+                        // read the endtag if SYNC_GETCHANGES wasn't an empty tag
224
+                        if ($starttag[EN_FLAGS] & EN_FLAGS_CONTENT) {
225
+                            if (!self::$decoder->getElementEndTag()) {
226
+                                return false;
227
+                            }
228
+                        }
229
+                    }
230
+
231
+                    if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
232
+                        $ws = self::$decoder->getElementContent();
233
+                        // normalize windowsize - see ZP-477
234
+                        if ($ws == 0 || $ws > WINDOW_SIZE_MAX) {
235
+                            $ws = WINDOW_SIZE_MAX;
236
+                        }
237
+
238
+                        $spa->SetWindowSize($ws);
239
+
240
+                        // also announce the currently requested window size to the DeviceManager
241
+                        self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize());
242
+
243
+                        if (!self::$decoder->getElementEndTag()) {
244
+                            return false;
245
+                        }
246
+                    }
247
+
248
+                    // conversation mode requested
249
+                    if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
250
+                        $spa->SetConversationMode(true);
251
+                        if (($conversationmode = self::$decoder->getElementContent()) !== false) {
252
+                            $spa->SetConversationMode((bool) $conversationmode);
253
+                            if (!self::$decoder->getElementEndTag()) {
254
+                                return false;
255
+                            }
256
+                        }
257
+                    }
258
+
259
+                    // Do not truncate by default
260
+                    $spa->SetTruncation(SYNC_TRUNCATION_ALL);
261
+
262
+                    // use default conflict handling if not specified by the mobile
263
+                    $spa->SetConflict(SYNC_CONFLICT_DEFAULT);
264
+
265
+                    // save the current filtertype because it might have been changed on the mobile
266
+                    $currentFilterType = $spa->GetFilterType();
267
+
268
+                    while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
269
+                        $firstOption = true;
270
+                        WBXMLDecoder::ResetInWhile("syncOptions");
271
+                        while (WBXMLDecoder::InWhile("syncOptions")) {
272
+                            // foldertype definition
273
+                            if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
274
+                                $foldertype = self::$decoder->getElementContent();
275
+                                SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): specified options block with foldertype '%s'", $foldertype));
276
+
277
+                                // switch the foldertype for the next options
278
+                                $spa->UseCPO($foldertype);
279
+
280
+                                // save the current filtertype because it might have been changed on the mobile
281
+                                $currentFilterType = $spa->GetFilterType();
282
+
283
+                                // set to synchronize all changes. The mobile could overwrite this value
284
+                                $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
285
+
286
+                                if (!self::$decoder->getElementEndTag()) {
287
+                                    return false;
288
+                                }
289
+                            }
290
+                            // if no foldertype is defined, use default cpo
291
+                            elseif ($firstOption) {
292
+                                $spa->UseCPO();
293
+                                // save the current filtertype because it might have been changed on the mobile
294
+                                $currentFilterType = $spa->GetFilterType();
295
+                                // set to synchronize all changes. The mobile could overwrite this value
296
+                                $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
297
+                            }
298
+                            $firstOption = false;
299
+
300
+                            if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
301
+                                $spa->SetFilterType(self::$decoder->getElementContent());
302
+                                if (!self::$decoder->getElementEndTag()) {
303
+                                    return false;
304
+                                }
305
+                            }
306
+                            if (self::$decoder->getElementStartTag(SYNC_TRUNCATION)) {
307
+                                $spa->SetTruncation(self::$decoder->getElementContent());
308
+                                if (!self::$decoder->getElementEndTag()) {
309
+                                    return false;
310
+                                }
311
+                            }
312
+                            if (self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
313
+                                $spa->SetRTFTruncation(self::$decoder->getElementContent());
314
+                                if (!self::$decoder->getElementEndTag()) {
315
+                                    return false;
316
+                                }
317
+                            }
318
+
319
+                            if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
320
+                                $spa->SetMimeSupport(self::$decoder->getElementContent());
321
+                                if (!self::$decoder->getElementEndTag()) {
322
+                                    return false;
323
+                                }
324
+                            }
325
+
326
+                            if (self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
327
+                                $spa->SetMimeTruncation(self::$decoder->getElementContent());
328
+                                if (!self::$decoder->getElementEndTag()) {
329
+                                    return false;
330
+                                }
331
+                            }
332
+
333
+                            if (self::$decoder->getElementStartTag(SYNC_CONFLICT)) {
334
+                                $spa->SetConflict(self::$decoder->getElementContent());
335
+                                if (!self::$decoder->getElementEndTag()) {
336
+                                    return false;
337
+                                }
338
+                            }
339
+
340
+                            while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
341
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
342
+                                    $bptype = self::$decoder->getElementContent();
343
+                                    $spa->BodyPreference($bptype);
344
+                                    if (!self::$decoder->getElementEndTag()) {
345
+                                        return false;
346
+                                    }
347
+                                }
348
+
349
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
350
+                                    $spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
351
+                                    if (!self::$decoder->getElementEndTag()) {
352
+                                        return false;
353
+                                    }
354
+                                }
355
+
356
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
357
+                                    $spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
358
+                                    if (!self::$decoder->getElementEndTag()) {
359
+                                        return false;
360
+                                    }
361
+                                }
362
+
363
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
364
+                                    $spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
365
+                                    if (!self::$decoder->getElementEndTag()) {
366
+                                        return false;
367
+                                    }
368
+                                }
369
+
370
+                                if (!self::$decoder->getElementEndTag()) {
371
+                                    return false;
372
+                                }
373
+                            }
374
+
375
+                            if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPARTPREFERENCE)) {
376
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
377
+                                    $bpptype = self::$decoder->getElementContent();
378
+                                    $spa->BodyPartPreference($bpptype);
379
+                                    if (!self::$decoder->getElementEndTag()) {
380
+                                        return false;
381
+                                    }
382
+                                }
383
+
384
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
385
+                                    $spa->BodyPartPreference($bpptype)->SetTruncationSize(self::$decoder->getElementContent());
386
+                                    if (!self::$decoder->getElementEndTag()) {
387
+                                        return false;
388
+                                    }
389
+                                }
390
+
391
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
392
+                                    $spa->BodyPartPreference($bpptype)->SetAllOrNone(self::$decoder->getElementContent());
393
+                                    if (!self::$decoder->getElementEndTag()) {
394
+                                        return false;
395
+                                    }
396
+                                }
397
+
398
+                                if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
399
+                                    $spa->BodyPartPreference($bpptype)->SetPreview(self::$decoder->getElementContent());
400
+                                    if (!self::$decoder->getElementEndTag()) {
401
+                                        return false;
402
+                                    }
403
+                                }
404
+
405
+                                if (!self::$decoder->getElementEndTag()) {
406
+                                    return false;
407
+                                }
408
+                            }
409
+
410
+                            if (self::$decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_SUPPORT)) {
411
+                                $spa->SetRmSupport(self::$decoder->getElementContent());
412
+                                if (!self::$decoder->getElementEndTag()) {
413
+                                    return false;
414
+                                }
415
+                            }
416
+
417
+                            $e = self::$decoder->peek();
418
+                            if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
419
+                                self::$decoder->getElementEndTag();
420
+
421
+                                break;
422
+                            }
423
+                        }
424
+                    }
425
+
426
+                    // limit items to be synchronized to the mobiles if configured
427
+                    $maxAllowed = self::$deviceManager->GetFilterType($spa->GetFolderId(), $spa->GetBackendFolderId());
428
+                    if ($maxAllowed > SYNC_FILTERTYPE_ALL &&
429
+                        (!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > $maxAllowed)) {
430
+                        SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): FilterType applied globally or specifically, using value: %s", $maxAllowed));
431
+                        $spa->SetFilterType($maxAllowed);
432
+                    }
433
+
434
+                    if ($currentFilterType != $spa->GetFilterType()) {
435
+                        SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): FilterType has changed (old: '%s', new: '%s'), removing folderstat to force Exporter setup", $currentFilterType, $spa->GetFilterType()));
436
+                        $spa->DelFolderStat();
437
+                    }
438
+
439
+                    // Check if the hierarchycache is available. If not, trigger a HierarchySync
440
+                    if (self::$deviceManager->IsHierarchySyncRequired()) {
441
+                        $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
442
+                        SLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device");
443
+                    }
444
+
445
+                    if (($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) {
446
+                        // We can not proceed here as the content class is unknown
447
+                        if ($status != SYNC_STATUS_SUCCESS) {
448
+                            SLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem.");
449
+                            $wbxmlproblem = true;
450
+
451
+                            break;
452
+                        }
453
+
454
+                        $performaction = true;
455
+
456
+                        // unset the importer
457
+                        $this->importer = false;
458
+
459
+                        $nchanges = 0;
460
+                        WBXMLDecoder::ResetInWhile("syncActions");
461
+                        while (WBXMLDecoder::InWhile("syncActions")) {
462
+                            // ADD, MODIFY, REMOVE or FETCH
463
+                            $element = self::$decoder->getElement();
464
+
465
+                            if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
466
+                                self::$decoder->ungetElement($element);
467
+
468
+                                break;
469
+                            }
470
+
471
+                            if ($status == SYNC_STATUS_SUCCESS) {
472
+                                ++$nchanges;
473
+                            }
474
+
475
+                            // Foldertype sent when syncing SMS
476
+                            if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
477
+                                $foldertype = self::$decoder->getElementContent();
478
+                                SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype));
479
+
480
+                                if (!self::$decoder->getElementEndTag()) {
481
+                                    return false;
482
+                                }
483
+                            }
484
+                            else {
485
+                                $foldertype = false;
486
+                            }
487
+
488
+                            $serverid = false;
489
+                            if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
490
+                                if (($serverid = self::$decoder->getElementContent()) !== false) {
491
+                                    if (!self::$decoder->getElementEndTag()) { // end serverid
492
+                                        return false;
493
+                                    }
494
+                                }
495
+                            }
496
+
497
+                            if (self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
498
+                                $clientid = self::$decoder->getElementContent();
499
+
500
+                                if (!self::$decoder->getElementEndTag()) { // end clientid
501
+                                    return false;
502
+                                }
503
+                            }
504
+                            else {
505
+                                $clientid = false;
506
+                            }
507
+
508
+                            // Get the SyncMessage if sent
509
+                            if (($el = self::$decoder->getElementStartTag(SYNC_DATA)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) {
510
+                                $message = GSync::getSyncObjectFromFolderClass($spa->GetContentClass());
511
+                                $message->Decode(self::$decoder);
512
+
513
+                                // set Ghosted fields
514
+                                $message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId()));
515
+
516
+                                if (!self::$decoder->getElementEndTag()) { // end applicationdata
517
+                                    return false;
518
+                                }
519
+                            }
520
+                            else {
521
+                                $message = false;
522
+                            }
523
+
524
+                            switch ($element[EN_TAG]) {
525
+                                case SYNC_FETCH:
526
+                                    array_push($actiondata["fetchids"], $serverid);
527
+                                    break;
528
+
529
+                                default:
530
+                                    // get the importer
531
+                                    if ($this->importer == false) {
532
+                                        $status = $this->getImporter($sc, $spa, $actiondata);
533
+                                    }
534
+
535
+                                    if ($status == SYNC_STATUS_SUCCESS) {
536
+                                        $this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
537
+                                    }
538
+                                    else {
539
+                                        SLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
540
+                                    }
541
+                                    break;
542
+                            }
543
+
544
+                            if ($actiondata["fetchids"]) {
545
+                                self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
546
+                            }
547
+                            else {
548
+                                self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
549
+                            }
550
+
551
+                            if (!self::$decoder->getElementEndTag()) { // end add/change/delete/move
552
+                                return false;
553
+                            }
554
+                        }
555
+
556
+                        if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) {
557
+                            SLog::Write(LOGLEVEL_INFO, sprintf("Processed '%d' incoming changes", $nchanges));
558
+                            if (!$actiondata["fetchids"]) {
559
+                                self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), $this->singleFolder);
560
+                                $this->saveMultiFolderInfo("incoming", $nchanges);
561
+                            }
562
+
563
+                            try {
564
+                                // Save the updated state, which is used for the exporter later
565
+                                $sc->AddParameter($spa, "state", $this->importer->GetState());
566
+                            }
567
+                            catch (StatusException $stex) {
568
+                                $status = $stex->getCode();
569
+                            }
570
+                        }
571
+
572
+                        if (!self::$decoder->getElementEndTag()) { // end PERFORM
573
+                            return false;
574
+                        }
575
+                    }
576
+
577
+                    // save the failsave state
578
+                    if (!empty($actiondata["statusids"])) {
579
+                        unset($actiondata["failstate"]);
580
+                        $actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state");
581
+                        self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata);
582
+                    }
583
+
584
+                    // save actiondata
585
+                    $sc->AddParameter($spa, "actiondata", $actiondata);
586
+
587
+                    if (!self::$decoder->getElementEndTag()) { // end collection
588
+                        return false;
589
+                    }
590
+
591
+                    // AS14 does not send GetChanges anymore. We should do it if there were no incoming changes
592
+                    if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) {
593
+                        $sc->AddParameter($spa, "getchanges", true);
594
+                    }
595
+                } // END FOLDER
596
+
597
+                if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) { // end collections
598
+                    return false;
599
+                }
600
+            } // end FOLDERS
601
+
602
+            if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) {
603
+                $hbinterval = self::$decoder->getElementContent();
604
+                if (!self::$decoder->getElementEndTag()) { // SYNC_HEARTBEATINTERVAL
605
+                    return false;
606
+                }
607
+            }
608
+
609
+            if (self::$decoder->getElementStartTag(SYNC_WAIT)) {
610
+                $wait = self::$decoder->getElementContent();
611
+                if (!self::$decoder->getElementEndTag()) { // SYNC_WAIT
612
+                    return false;
613
+                }
614
+
615
+                // internally the heartbeat interval and the wait time are the same
616
+                // heartbeat is in seconds, wait in minutes
617
+                $hbinterval = $wait * 60;
618
+            }
619
+
620
+            if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
621
+                $sc->SetGlobalWindowSize(self::$decoder->getElementContent());
622
+                SLog::Write(LOGLEVEL_DEBUG, "Sync(): Global WindowSize requested: " . $sc->GetGlobalWindowSize());
623
+                if (!self::$decoder->getElementEndTag()) { // SYNC_WINDOWSIZE
624
+                    return false;
625
+                }
626
+            }
627
+
628
+            if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
629
+                $partial = true;
630
+            }
631
+            else {
632
+                $partial = false;
633
+            }
634
+
635
+            if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) { // end sync
636
+                return false;
637
+            }
638
+        }
639
+        // we did not receive a SYNCHRONIZE block - assume empty sync
640
+        else {
641
+            $emptysync = true;
642
+        }
643
+        // END SYNCHRONIZE
644
+
645
+        // check heartbeat/wait time
646
+        if (isset($hbinterval)) {
647
+            if ($hbinterval < 60 || $hbinterval > 3540) {
648
+                $status = SYNC_STATUS_INVALIDWAITORHBVALUE;
649
+                SLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval));
650
+            }
651
+        }
652
+
653
+        // Partial & Empty Syncs need saved data to proceed with synchronization
654
+        if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true)) {
655
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders."));
656
+
657
+            // Load all collections - do not overwrite existing (received!), load states, check permissions and only load confirmed states!
658
+            try {
659
+                $sc->LoadAllCollections(false, true, true, true, true);
660
+            }
661
+            catch (StateInvalidException $siex) {
662
+                $status = SYNC_STATUS_INVALIDSYNCKEY;
663
+                self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
664
+                $this->saveMultiFolderInfo("exception", "StateNotFoundException");
665
+            }
666
+            catch (StatusException $stex) {
667
+                $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
668
+                self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
669
+                $this->saveMultiFolderInfo("exception", "StatusException");
670
+            }
671
+
672
+            // update a few values
673
+            foreach ($sc as $folderid => $spa) {
674
+                // manually set getchanges parameter for this collection if it is synchronized
675
+                if ($spa->HasSyncKey()) {
676
+                    $actiondata = $sc->GetParameter($spa, "actiondata");
677
+                    // request changes if no other actions are executed
678
+                    if (empty($actiondata["modifyids"]) && empty($actiondata["clientids"]) && empty($actiondata["removeids"])) {
679
+                        $sc->AddParameter($spa, "getchanges", true);
680
+                    }
681
+
682
+                    // announce WindowSize to DeviceManager
683
+                    self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize());
684
+                }
685
+            }
686
+            if (!$sc->HasCollections()) {
687
+                $status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
688
+            }
689
+        }
690
+        elseif (isset($hbinterval)) {
691
+            // load the hierarchy data - there are no permissions to verify so we just set it to false
692
+            if (!$sc->LoadCollection(false, true, false)) {
693
+                $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
694
+                self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
695
+                $this->saveMultiFolderInfo("exception", "StatusException");
696
+            }
697
+        }
698
+
699
+        // HEARTBEAT
700
+        if ($status == SYNC_STATUS_SUCCESS && isset($hbinterval)) {
701
+            $interval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 30;
702
+
703
+            if (isset($hbinterval)) {
704
+                $sc->SetLifetime($hbinterval);
705
+            }
706
+
707
+            // states are lazy loaded - we have to make sure that they are there!
708
+            $loadstatus = SYNC_STATUS_SUCCESS;
709
+            foreach ($sc as $folderid => $spa) {
710
+                // some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362
711
+                // we do not load the state so we will never get relevant changes on the OUTBOX folder
712
+                if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) {
713
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed"));
714
+
715
+                    continue;
716
+                }
717
+
718
+                $fad = [];
719
+                // if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS
720
+                // so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY
721
+                if ($loadstatus == SYNC_STATUS_SUCCESS) {
722
+                    $loadstatus = $this->loadStates($sc, $spa, $fad);
723
+                }
724
+            }
725
+
726
+            if ($loadstatus == SYNC_STATUS_SUCCESS) {
727
+                $foundchanges = false;
728
+
729
+                try {
730
+                    // always check for changes
731
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
732
+                    $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
733
+                }
734
+                catch (StatusException $stex) {
735
+                    if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
736
+                        $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
737
+                    }
738
+                    else {
739
+                        $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
740
+                        self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
741
+                        $this->saveMultiFolderInfo("exception", "StatusException");
742
+                    }
743
+                }
744
+
745
+                // update the waittime waited
746
+                self::$waitTime = $sc->GetWaitedSeconds();
747
+
748
+                // in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response
749
+                if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) {
750
+                    // if there were changes to the SPA or CPOs we need to save this before we terminate
751
+                    // only save if the state was not modified by some other request, if so, return state invalid status
752
+                    foreach ($sc as $folderid => $spa) {
753
+                        if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
754
+                            $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
755
+                        }
756
+                        else {
757
+                            $sc->SaveCollection($spa);
758
+                        }
759
+                    }
760
+
761
+                    if ($status == SYNC_STATUS_SUCCESS) {
762
+                        SLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection.");
763
+                        self::$specialHeaders = [];
764
+                        self::$specialHeaders[] = "Connection: close";
765
+
766
+                        return true;
767
+                    }
768
+                }
769
+
770
+                if ($foundchanges) {
771
+                    foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) {
772
+                        // check if there were other sync requests for a folder during the heartbeat
773
+                        $spa = $sc->GetCollection($folderid);
774
+                        if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
775
+                            SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
776
+                            $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
777
+                        }
778
+                        else {
779
+                            SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
780
+                        }
781
+                    }
782
+                }
783
+            }
784
+        }
785
+
786
+        // Start the output
787
+        SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Start Output");
788
+
789
+        // global status
790
+        // SYNC_COMMONSTATUS_* start with values from 101
791
+        if ($status != SYNC_COMMONSTATUS_SUCCESS && ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED || $status > 100)) {
792
+            self::$deviceManager->AnnounceProcessStatus($folderid, $status);
793
+            $this->sendStartTags();
794
+            self::$encoder->startTag(SYNC_STATUS);
795
+            self::$encoder->content($status);
796
+            self::$encoder->endTag();
797
+            self::$encoder->endTag(); // SYNC_SYNCHRONIZE
798
+
799
+            return true;
800
+        }
801
+
802
+        // Loop through requested folders
803
+        foreach ($sc as $folderid => $spa) {
804
+            // get actiondata
805
+            $actiondata = $sc->GetParameter($spa, "actiondata");
806
+
807
+            if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) {
808
+                SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection."));
809
+
810
+                continue;
811
+            }
812
+
813
+            if (!$sc->GetParameter($spa, "requested")) {
814
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId()));
815
+                // reload state and initialize StateMachine correctly
816
+                $sc->AddParameter($spa, "state", null);
817
+                $status = $this->loadStates($sc, $spa, $actiondata);
818
+            }
819
+
820
+            // initialize exporter to get changecount
821
+            $changecount = false;
822
+            $exporter = false;
823
+            $streamimporter = false;
824
+            $newFolderStat = false;
825
+            $setupExporter = true;
826
+
827
+            // TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again
828
+            if ($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || !$spa->HasSyncKey())) {
829
+                // no need to run the exporter if the globalwindowsize is already full - if collection already has a synckey (ZP-1215)
830
+                if ($sc->GetGlobalWindowSize() == $this->globallyExportedItems && $spa->HasSyncKey()) {
831
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as GlobalWindowSize is full.", $spa->GetFolderId()));
832
+                    $setupExporter = false;
833
+                }
834
+                // if the maximum request timeout is reached, stop processing other collections
835
+                if (Request::IsRequestTimeoutReached()) {
836
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as request timeout reached, omitting output for collection.", $spa->GetFolderId()));
837
+                    $setupExporter = false;
838
+                }
839
+
840
+                // if max memory allocation is reached, stop processing other collections
841
+                if (Request::IsRequestMemoryLimitReached()) {
842
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as max memory allocatation reached, omitting output for collection.", $spa->GetFolderId()));
843
+                    $setupExporter = false;
844
+                }
845
+
846
+                // force exporter run if there is a saved status
847
+                if ($setupExporter && self::$deviceManager->HasFolderSyncStatus($spa->GetFolderId())) {
848
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): forcing exporter setup for '%s' as a sync status is saved - ignoring backend folder stats", $spa->GetFolderId()));
849
+                }
850
+                // compare the folder statistics if the backend supports this
851
+                elseif ($setupExporter && self::$backend->HasFolderStats()) {
852
+                    // check if the folder stats changed -> if not, don't setup the exporter, there are no changes!
853
+                    $newFolderStat = self::$backend->GetFolderStat(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
854
+                    if ($newFolderStat !== false && !$spa->IsExporterRunRequired($newFolderStat, true)) {
855
+                        $changecount = 0;
856
+                        $setupExporter = false;
857
+                    }
858
+                }
859
+
860
+                // Do a full Exporter setup if we can't avoid it
861
+                if ($setupExporter) {
862
+                    // make sure the states are loaded
863
+                    $status = $this->loadStates($sc, $spa, $actiondata);
864
+
865
+                    if ($status == SYNC_STATUS_SUCCESS) {
866
+                        try {
867
+                            // if this is an additional folder the backend has to be setup correctly
868
+                            if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
869
+                                throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
870
+                            }
871
+
872
+                            // Use the state from the importer, as changes may have already happened
873
+                            $exporter = self::$backend->GetExporter($spa->GetBackendFolderId());
874
+
875
+                            if ($exporter === false) {
876
+                                throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
877
+                            }
878
+                        }
879
+                        catch (StatusException $stex) {
880
+                            $status = $stex->getCode();
881
+                        }
882
+
883
+                        try {
884
+                            // Stream the messages directly to the PDA
885
+                            $streamimporter = new ImportChangesStream(self::$encoder, GSync::getSyncObjectFromFolderClass($spa->GetContentClass()));
886
+
887
+                            if ($exporter !== false) {
888
+                                $exporter->Config($sc->GetParameter($spa, "state"));
889
+                                $exporter->ConfigContentParameters($spa->GetCPO());
890
+                                $exporter->InitializeExporter($streamimporter);
891
+
892
+                                $changecount = $exporter->GetChangeCount();
893
+                            }
894
+                        }
895
+                        catch (StatusException $stex) {
896
+                            if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
897
+                                $status = SYNC_STATUS_INVALIDSYNCKEY;
898
+                            }
899
+                            else {
900
+                                $status = $stex->getCode();
901
+                            }
902
+                        }
903
+
904
+                        if (!$spa->HasSyncKey()) {
905
+                            self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), $this->singleFolder);
906
+                            $this->saveMultiFolderInfo("queued", $changecount);
907
+                            // update folder status as initialized
908
+                            $spa->SetFolderSyncTotal($changecount);
909
+                            $spa->SetFolderSyncRemaining($changecount);
910
+                            if ($changecount > 0) {
911
+                                self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
912
+                            }
913
+                        }
914
+                        elseif ($status != SYNC_STATUS_SUCCESS) {
915
+                            self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
916
+                            $this->saveMultiFolderInfo("exception", "StatusException");
917
+                        }
918
+                        self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
919
+                    }
920
+                }
921
+            }
922
+
923
+            // Get a new sync key to output to the client if any changes have been send by the mobile or a new synckey is to be sent
924
+            if (!empty($actiondata["modifyids"]) ||
925
+                !empty($actiondata["clientids"]) ||
926
+                !empty($actiondata["removeids"]) ||
927
+                (!$spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS)) {
928
+                $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
929
+            }
930
+            // get a new synckey only if we did not reach the global limit yet
931
+            else {
932
+                // when reaching the global limit for changes of all collections, stop processing other collections (ZP-697)
933
+                if ($sc->GetGlobalWindowSize() <= $this->globallyExportedItems) {
934
+                    SLog::Write(LOGLEVEL_DEBUG, "Global WindowSize for amount of exported changes reached, omitting output for collection.");
935
+
936
+                    continue;
937
+                }
938
+
939
+                // get a new synckey if there are changes are we did not reach the limit yet
940
+                if ($changecount > 0) {
941
+                    $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
942
+                }
943
+            }
944
+
945
+            // Fir AS 14.0+ omit output for folder, if there were no incoming or outgoing changes and no Fetch
946
+            if (Request::GetProtocolVersion() >= 14.0 && !$spa->HasNewSyncKey() && $changecount == 0 && empty($actiondata["fetchids"]) && $status == SYNC_STATUS_SUCCESS &&
947
+                    !$spa->HasConfirmationChanged() && ($newFolderStat === false || !$spa->IsExporterRunRequired($newFolderStat))) {
948
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync: No changes found for %s folder id '%s'. Omitting output.", $spa->GetContentClass(), $spa->GetFolderId()));
949
+
950
+                continue;
951
+            }
952
+
953
+            // if there are no other responses sent, we should end with a global status
954
+            if ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED && $this->startTagsSent === false) {
955
+                $this->sendStartTags();
956
+                self::$encoder->startTag(SYNC_STATUS);
957
+                self::$encoder->content($status);
958
+                self::$encoder->endTag();
959
+                self::$encoder->endTag(); // SYNC_SYNCHRONIZE
960
+
961
+                return true;
962
+            }
963
+
964
+            // there is something to send here, sync folder to output
965
+            $this->syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat);
966
+
967
+            // reset status for the next folder
968
+            $status = SYNC_STATUS_SUCCESS;
969
+        } // END foreach collection
970
+
971
+        // SYNC_FOLDERS - only if the starttag was sent
972
+        if ($this->startFolderTagSent) {
973
+            self::$encoder->endTag();
974
+        }
975
+
976
+        // Check if there was any response - in case of an empty sync request, we shouldn't send an empty answer (ZP-1241)
977
+        if (!$this->startTagsSent && $emptysync === true) {
978
+            $this->sendStartTags();
979
+            self::$encoder->startTag(SYNC_STATUS);
980
+            self::$encoder->content(SYNC_STATUS_SYNCREQUESTINCOMPLETE);
981
+            self::$encoder->endTag();
982
+        }
983
+
984
+        // SYNC_SYNCHRONIZE - only if the starttag was sent
985
+        if ($this->startTagsSent) {
986
+            self::$encoder->endTag();
987
+        }
988
+
989
+        // final top announcement for a multi-folder sync
990
+        if ($sc->GetCollectionCount() > 1) {
991
+            self::$topCollector->AnnounceInformation($this->getMultiFolderInfoLine($sc->GetCollectionCount()), true);
992
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync: Processed %d folders", $sc->GetCollectionCount()));
993
+        }
994
+
995
+        // update the waittime waited
996
+        self::$waitTime = $sc->GetWaitedSeconds();
997
+
998
+        return true;
999
+    }
1000
+
1001
+    /**
1002
+     * Sends the SYNC_SYNCHRONIZE once per request.
1003
+     */
1004
+    private function sendStartTags() {
1005
+        if ($this->startTagsSent === false) {
1006
+            self::$encoder->startWBXML();
1007
+            self::$encoder->startTag(SYNC_SYNCHRONIZE);
1008
+            $this->startTagsSent = true;
1009
+        }
1010
+    }
1011
+
1012
+    /**
1013
+     * Sends the SYNC_FOLDERS once per request.
1014
+     */
1015
+    private function sendFolderStartTag() {
1016
+        $this->sendStartTags();
1017
+        if ($this->startFolderTagSent === false) {
1018
+            self::$encoder->startTag(SYNC_FOLDERS);
1019
+            $this->startFolderTagSent = true;
1020
+        }
1021
+    }
1022
+
1023
+    /**
1024
+     * Synchronizes a folder to the output stream. Changes for this folders are expected.
1025
+     *
1026
+     * @param SyncCollections     $sc
1027
+     * @param SyncParameters      $spa
1028
+     * @param IExportChanges      $exporter       Fully configured exporter for this folder
1029
+     * @param int                 $changecount    Amount of changes expected
1030
+     * @param ImportChangesStream $streamimporter Output stream
1031
+     * @param int                 $status         current status of the folder processing
1032
+     * @param string              $newFolderStat  the new folder stat to be set if everything was exported
1033
+     *
1034
+     * @throws StatusException
1035
+     *
1036
+     * @return int sync status code
1037
+     */
1038
+    private function syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat) {
1039
+        $actiondata = $sc->GetParameter($spa, "actiondata");
1040
+
1041
+        // send the WBXML start tags (if not happened already)
1042
+        $this->sendFolderStartTag();
1043
+        self::$encoder->startTag(SYNC_FOLDER);
1044
+
1045
+        if ($spa->HasContentClass()) {
1046
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass()));
1047
+            // AS 12.0 devices require content class
1048
+            if (Request::GetProtocolVersion() < 12.1) {
1049
+                self::$encoder->startTag(SYNC_FOLDERTYPE);
1050
+                self::$encoder->content($spa->GetContentClass());
1051
+                self::$encoder->endTag();
1052
+            }
1053
+        }
1054
+
1055
+        self::$encoder->startTag(SYNC_SYNCKEY);
1056
+        if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
1057
+            self::$encoder->content($spa->GetNewSyncKey());
1058
+        }
1059
+        else {
1060
+            self::$encoder->content($spa->GetSyncKey());
1061
+        }
1062
+        self::$encoder->endTag();
1063
+
1064
+        self::$encoder->startTag(SYNC_FOLDERID);
1065
+        self::$encoder->content($spa->GetFolderId());
1066
+        self::$encoder->endTag();
1067
+
1068
+        self::$encoder->startTag(SYNC_STATUS);
1069
+        self::$encoder->content($status);
1070
+        self::$encoder->endTag();
1071
+
1072
+        // announce failing status to the process loop detection
1073
+        if ($status !== SYNC_STATUS_SUCCESS) {
1074
+            self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
1075
+        }
1076
+
1077
+        // Output IDs and status for incoming items & requests
1078
+        if ($status == SYNC_STATUS_SUCCESS && (
1079
+            !empty($actiondata["clientids"]) ||
1080
+                !empty($actiondata["modifyids"]) ||
1081
+                !empty($actiondata["removeids"]) ||
1082
+                !empty($actiondata["fetchids"])
1083
+        )) {
1084
+            self::$encoder->startTag(SYNC_REPLIES);
1085
+            // output result of all new incoming items
1086
+            foreach ($actiondata["clientids"] as $clientid => $serverid) {
1087
+                self::$encoder->startTag(SYNC_ADD);
1088
+                self::$encoder->startTag(SYNC_CLIENTENTRYID);
1089
+                self::$encoder->content($clientid);
1090
+                self::$encoder->endTag();
1091
+                if ($serverid) {
1092
+                    self::$encoder->startTag(SYNC_SERVERENTRYID);
1093
+                    self::$encoder->content($serverid);
1094
+                    self::$encoder->endTag();
1095
+                }
1096
+                self::$encoder->startTag(SYNC_STATUS);
1097
+                self::$encoder->content((isset($actiondata["statusids"][$clientid]) ? $actiondata["statusids"][$clientid] : SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR));
1098
+                self::$encoder->endTag();
1099
+                self::$encoder->endTag();
1100
+            }
1101
+
1102
+            // loop through modify operations which were not a success, send status
1103
+            foreach ($actiondata["modifyids"] as $serverid) {
1104
+                if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
1105
+                    self::$encoder->startTag(SYNC_MODIFY);
1106
+                    self::$encoder->startTag(SYNC_SERVERENTRYID);
1107
+                    self::$encoder->content($serverid);
1108
+                    self::$encoder->endTag();
1109
+                    self::$encoder->startTag(SYNC_STATUS);
1110
+                    self::$encoder->content($actiondata["statusids"][$serverid]);
1111
+                    self::$encoder->endTag();
1112
+                    self::$encoder->endTag();
1113
+                }
1114
+            }
1115
+
1116
+            // loop through remove operations which were not a success, send status
1117
+            foreach ($actiondata["removeids"] as $serverid) {
1118
+                if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
1119
+                    self::$encoder->startTag(SYNC_REMOVE);
1120
+                    self::$encoder->startTag(SYNC_SERVERENTRYID);
1121
+                    self::$encoder->content($serverid);
1122
+                    self::$encoder->endTag();
1123
+                    self::$encoder->startTag(SYNC_STATUS);
1124
+                    self::$encoder->content($actiondata["statusids"][$serverid]);
1125
+                    self::$encoder->endTag();
1126
+                    self::$encoder->endTag();
1127
+                }
1128
+            }
1129
+
1130
+            if (!empty($actiondata["fetchids"])) {
1131
+                self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), $this->singleFolder);
1132
+                $this->saveMultiFolderInfo("fetching", count($actiondata["fetchids"]));
1133
+            }
1134
+
1135
+            foreach ($actiondata["fetchids"] as $id) {
1136
+                $data = false;
1137
+
1138
+                try {
1139
+                    $fetchstatus = SYNC_STATUS_SUCCESS;
1140
+
1141
+                    // if this is an additional folder the backend has to be setup correctly
1142
+                    if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
1143
+                        throw new StatusException(sprintf("HandleSync(): could not Setup() the backend to fetch in folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_OBJECTNOTFOUND);
1144
+                    }
1145
+
1146
+                    $data = self::$backend->Fetch($spa->GetBackendFolderId(), $id, $spa->GetCPO());
1147
+
1148
+                    // check if the message is broken
1149
+                    if (GSync::GetDeviceManager(false) && GSync::GetDeviceManager()->DoNotStreamMessage($id, $data)) {
1150
+                        SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
1151
+                        $fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1152
+                    }
1153
+                }
1154
+                catch (StatusException $stex) {
1155
+                    $fetchstatus = $stex->getCode();
1156
+                }
1157
+
1158
+                self::$encoder->startTag(SYNC_FETCH);
1159
+                self::$encoder->startTag(SYNC_SERVERENTRYID);
1160
+                self::$encoder->content($id);
1161
+                self::$encoder->endTag();
1162
+
1163
+                self::$encoder->startTag(SYNC_STATUS);
1164
+                self::$encoder->content($fetchstatus);
1165
+                self::$encoder->endTag();
1166
+
1167
+                if ($data !== false && $status == SYNC_STATUS_SUCCESS) {
1168
+                    self::$encoder->startTag(SYNC_DATA);
1169
+                    $data->Encode(self::$encoder);
1170
+                    self::$encoder->endTag();
1171
+                }
1172
+                else {
1173
+                    SLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
1174
+                }
1175
+                self::$encoder->endTag();
1176
+            }
1177
+            self::$encoder->endTag();
1178
+        }
1179
+
1180
+        if ($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) {
1181
+            $moreAvailableSent = false;
1182
+            $windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount);
1183
+
1184
+            // limit windowSize to the max available limit of the global window size left
1185
+            $globallyAvailable = $sc->GetGlobalWindowSize() - $this->globallyExportedItems;
1186
+            if ($changecount > $globallyAvailable && $windowSize > $globallyAvailable) {
1187
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Limit window size to %d as the global window size limit will be reached", $globallyAvailable));
1188
+                $windowSize = $globallyAvailable;
1189
+            }
1190
+            // send <MoreAvailable/> if there are more changes than fit in the folder windowsize
1191
+            // or there is a move state (another sync should be done afterwards)
1192
+            if ($changecount > $windowSize) {
1193
+                self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
1194
+                $moreAvailableSent = true;
1195
+                $spa->DelFolderStat();
1196
+            }
1197
+        }
1198
+
1199
+        // Stream outgoing changes
1200
+        if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0 && (bool) $exporter) {
1201
+            self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", (($changecount > $windowSize) ? $windowSize : $changecount)));
1202
+
1203
+            // Output message changes per folder
1204
+            self::$encoder->startTag(SYNC_PERFORM);
1205
+
1206
+            $n = 0;
1207
+            WBXMLDecoder::ResetInWhile("syncSynchronize");
1208
+            while (WBXMLDecoder::InWhile("syncSynchronize")) {
1209
+                try {
1210
+                    $progress = $exporter->Synchronize();
1211
+                    if (!is_array($progress)) {
1212
+                        break;
1213
+                    }
1214
+                    ++$n;
1215
+                    if ($n % 10 == 0) {
1216
+                        self::$topCollector->AnnounceInformation(sprintf("Streamed data of %d objects out of %d", $n, (($changecount > $windowSize) ? $windowSize : $changecount)));
1217
+                    }
1218
+                }
1219
+                catch (SyncObjectBrokenException $mbe) {
1220
+                    $brokenSO = $mbe->GetSyncObject();
1221
+                    if (!$brokenSO) {
1222
+                        SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
1223
+                    }
1224
+                    else {
1225
+                        if (!isset($brokenSO->id)) {
1226
+                            $brokenSO->id = "Unknown ID";
1227
+                            SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
1228
+                        }
1229
+                        self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO);
1230
+                    }
1231
+                }
1232
+                // something really bad happened while exporting changes
1233
+                catch (StatusException $stex) {
1234
+                    $status = $stex->getCode();
1235
+                    // during export we found out that the states should be thrown away (ZP-623)
1236
+                    if ($status == SYNC_STATUS_INVALIDSYNCKEY) {
1237
+                        self::$deviceManager->ForceFolderResync($spa->GetFolderId());
1238
+
1239
+                        break;
1240
+                    }
1241
+                }
1242
+
1243
+                if ($n >= $windowSize || Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
1244
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
1245
+
1246
+                    break;
1247
+                }
1248
+            }
1249
+
1250
+            // $progress is not an array when exporting the last message
1251
+            // so we get the number to display from the streamimporter if it's available
1252
+            if ((bool) $streamimporter) {
1253
+                $n = $streamimporter->GetImportedMessages();
1254
+            }
1255
+
1256
+            self::$encoder->endTag();
1257
+
1258
+            // log the request timeout
1259
+            if (Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
1260
+                SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Stopping export as limits of request timeout or available memory are almost reached!");
1261
+                // Send a <MoreAvailable/> tag if we reached the request timeout or max memory, there are more changes and a moreavailable was not already send
1262
+                if (!$moreAvailableSent && ($n > $windowSize)) {
1263
+                    self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
1264
+                    $spa->DelFolderStat();
1265
+                    $moreAvailableSent = true;
1266
+                }
1267
+            }
1268
+
1269
+            self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, ($n >= $windowSize) ? " of " . $changecount : ""), $this->singleFolder);
1270
+            $this->saveMultiFolderInfo("outgoing", $n);
1271
+            $this->saveMultiFolderInfo("queued", $changecount);
1272
+
1273
+            $this->globallyExportedItems += $n;
1274
+
1275
+            // update folder status, if there is something set
1276
+            if ($spa->GetFolderSyncRemaining() && $changecount > 0) {
1277
+                $spa->SetFolderSyncRemaining($changecount);
1278
+            }
1279
+            // changecount is initialized with 'false', so 0 means no changes!
1280
+            if ($changecount === 0 || ($changecount !== false && $changecount <= $windowSize)) {
1281
+                self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_COMPLETED);
1282
+
1283
+                // we should update the folderstat, but we recheck to see if it changed. If so, it's not updated to force another sync
1284
+                if (self::$backend->HasFolderStats()) {
1285
+                    $newFolderStatAfterExport = self::$backend->GetFolderStat(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
1286
+                    if ($newFolderStat === $newFolderStatAfterExport) {
1287
+                        $this->setFolderStat($spa, $newFolderStat);
1288
+                    }
1289
+                    else {
1290
+                        SLog::Write(LOGLEVEL_DEBUG, "Sync() Folderstat differs after export, force another exporter run.");
1291
+                    }
1292
+                }
1293
+            }
1294
+            else {
1295
+                self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS);
1296
+            }
1297
+        }
1298
+
1299
+        self::$encoder->endTag();
1300
+
1301
+        // Save the sync state for the next time
1302
+        if ($spa->HasNewSyncKey()) {
1303
+            self::$topCollector->AnnounceInformation("Saving state");
1304
+
1305
+            try {
1306
+                if (isset($exporter) && $exporter) {
1307
+                    $state = $exporter->GetState();
1308
+                }
1309
+
1310
+                // nothing exported, but possibly imported - get the importer state
1311
+                elseif ($sc->GetParameter($spa, "state") !== null) {
1312
+                    $state = $sc->GetParameter($spa, "state");
1313
+                }
1314
+
1315
+                // if a new request without state information (hierarchy) save an empty state
1316
+                elseif (!$spa->HasSyncKey()) {
1317
+                    $state = "";
1318
+                }
1319
+            }
1320
+            catch (StatusException $stex) {
1321
+                $status = $stex->getCode();
1322
+            }
1323
+
1324
+            if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
1325
+                self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
1326
+            }
1327
+            else {
1328
+                SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
1329
+            }
1330
+        }
1331
+
1332
+        // save SyncParameters
1333
+        if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) {
1334
+            $sc->SaveCollection($spa);
1335
+        }
1336
+
1337
+        return $status;
1338
+    }
1339
+
1340
+    /**
1341
+     * Loads the states and writes them into the SyncCollection Object and the actiondata failstate.
1342
+     *
1343
+     * @param SyncCollection $sc           SyncCollection object
1344
+     * @param SyncParameters $spa          SyncParameters object
1345
+     * @param array          $actiondata   Actiondata array
1346
+     * @param bool           $loadFailsave (opt) default false - indicates if the failsave states should be loaded
1347
+     *
1348
+     * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS
1349
+     */
1350
+    private function loadStates($sc, $spa, &$actiondata, $loadFailsave = false) {
1351
+        $status = SYNC_STATUS_SUCCESS;
1352
+
1353
+        if ($sc->GetParameter($spa, "state") == null) {
1354
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync->loadStates(): loading states for folder '%s'", $spa->GetFolderId()));
1355
+
1356
+            try {
1357
+                $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
1358
+
1359
+                if ($loadFailsave) {
1360
+                    // if this request was made before, there will be a failstate available
1361
+                    $actiondata["failstate"] = self::$deviceManager->GetStateManager()->GetSyncFailState();
1362
+                }
1363
+
1364
+                // if this is an additional folder the backend has to be setup correctly
1365
+                if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
1366
+                    throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
1367
+                }
1368
+            }
1369
+            catch (StateNotFoundException $snfex) {
1370
+                $status = SYNC_STATUS_INVALIDSYNCKEY;
1371
+                self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
1372
+                $this->saveMultiFolderInfo("exception", "StateNotFoundException");
1373
+            }
1374
+            catch (StatusException $stex) {
1375
+                $status = $stex->getCode();
1376
+                self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
1377
+                $this->saveMultiFolderInfo("exception", "StateNotFoundException");
1378
+            }
1379
+        }
1380
+
1381
+        return $status;
1382
+    }
1383
+
1384
+    /**
1385
+     * Initializes the importer for the SyncParameters folder, loads necessary
1386
+     * states (incl. failsave states) and initializes the conflict detection.
1387
+     *
1388
+     * @param SyncCollection $sc         SyncCollection object
1389
+     * @param SyncParameters $spa        SyncParameters object
1390
+     * @param array          $actiondata Actiondata array
1391
+     *
1392
+     * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS
1393
+     */
1394
+    private function getImporter($sc, $spa, &$actiondata) {
1395
+        SLog::Write(LOGLEVEL_DEBUG, "Sync->getImporter(): initialize importer");
1396
+        $status = SYNC_STATUS_SUCCESS;
1397
+
1398
+        // load the states with failsave data
1399
+        $status = $this->loadStates($sc, $spa, $actiondata, true);
1400
+
1401
+        try {
1402
+            if ($status == SYNC_STATUS_SUCCESS) {
1403
+                // Configure importer with last state
1404
+                $this->importer = self::$backend->GetImporter($spa->GetBackendFolderId());
1405
+
1406
+                // if something goes wrong, ask the mobile to resync the hierarchy
1407
+                if ($this->importer === false) {
1408
+                    throw new StatusException(sprintf("Sync->getImporter(): no importer for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
1409
+                }
1410
+
1411
+                // if there is a valid state obtained after importing changes in a previous loop, we use that state
1412
+                if (isset($actiondata["failstate"], $actiondata["failstate"]["failedsyncstate"])) {
1413
+                    $this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict());
1414
+                }
1415
+                else {
1416
+                    $this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict());
1417
+                }
1418
+
1419
+                // the CPO is also needed by the importer to check if imported changes are inside the sync window - see ZP-258
1420
+                $this->importer->ConfigContentParameters($spa->GetCPO());
1421
+                $this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state"));
1422
+            }
1423
+        }
1424
+        catch (StatusException $stex) {
1425
+            $status = $stex->getCode();
1426
+        }
1427
+
1428
+        return $status;
1429
+    }
1430
+
1431
+    /**
1432
+     * Imports a message.
1433
+     *
1434
+     * @param SyncParameters $spa          SyncParameters object
1435
+     * @param array          $actiondata   Actiondata array
1436
+     * @param int            $todo         WBXML flag indicating how message should be imported.
1437
+     *                                     Valid values: SYNC_ADD, SYNC_MODIFY, SYNC_REMOVE
1438
+     * @param SyncObject     $message      SyncObject message to be imported
1439
+     * @param string         $clientid     Client message identifier
1440
+     * @param string         $serverid     Server message identifier
1441
+     * @param string         $foldertype   On sms sync, this says "SMS", else false
1442
+     * @param int            $messageCount Counter of already imported messages
1443
+     *
1444
+     * @throws StatusException in case the importer is not available
1445
+     *
1446
+     * @return - message related status are returned in the actiondata
1447
+     */
1448
+    private function importMessage($spa, &$actiondata, $todo, $message, $clientid, $serverid, $foldertype, $messageCount) {
1449
+        // the importer needs to be available!
1450
+        if ($this->importer == false) {
1451
+            throw new StatusException("Sync->importMessage(): importer not available", SYNC_STATUS_SERVERERROR);
1452
+        }
1453
+
1454
+        // mark this state as used, e.g. for HeartBeat
1455
+        self::$deviceManager->SetHeartbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter());
1456
+
1457
+        // Detect incoming loop
1458
+        // messages which were created/removed before will not have the same action executed again
1459
+        // if a message is edited we perform this action "again", as the message could have been changed on the mobile in the meantime
1460
+        $ignoreMessage = false;
1461
+        if ($actiondata["failstate"]) {
1462
+            // message was ADDED before, do NOT add it again
1463
+            if ($todo == SYNC_ADD && isset($actiondata["failstate"]["clientids"][$clientid])) {
1464
+                $ignoreMessage = true;
1465
+
1466
+                // make sure no messages are sent back
1467
+                self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0);
1468
+
1469
+                $actiondata["clientids"][$clientid] = $actiondata["failstate"]["clientids"][$clientid];
1470
+                $actiondata["statusids"][$clientid] = $actiondata["failstate"]["statusids"][$clientid];
1471
+
1472
+                SLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Incoming new message '%s' was created on the server before. Replying with known new server id: %s", $clientid, $actiondata["clientids"][$clientid]));
1473
+            }
1474
+
1475
+            // message was REMOVED before, do NOT attempt to remove it again
1476
+            if ($todo == SYNC_REMOVE && isset($actiondata["failstate"]["removeids"][$serverid])) {
1477
+                $ignoreMessage = true;
1478
+
1479
+                // make sure no messages are sent back
1480
+                self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0);
1481
+
1482
+                $actiondata["removeids"][$serverid] = $actiondata["failstate"]["removeids"][$serverid];
1483
+                $actiondata["statusids"][$serverid] = $actiondata["failstate"]["statusids"][$serverid];
1484
+
1485
+                SLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Message '%s' was deleted by the mobile before. Replying with known status: %s", $clientid, $actiondata["statusids"][$serverid]));
1486
+            }
1487
+        }
1488
+
1489
+        if (!$ignoreMessage) {
1490
+            switch ($todo) {
1491
+                case SYNC_MODIFY:
1492
+                    self::$topCollector->AnnounceInformation(sprintf("Saving modified message %d", $messageCount));
1493
+
1494
+                    try {
1495
+                        $actiondata["modifyids"][] = $serverid;
1496
+
1497
+                        // ignore sms messages
1498
+                        if ($foldertype == "SMS" || stripos($serverid, self::GSYNCIGNORESMS) !== false) {
1499
+                            SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1500
+                            // TODO we should update the SMS
1501
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1502
+                        }
1503
+                        // check incoming message without logging WARN messages about errors
1504
+                        elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1505
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1506
+                        }
1507
+                        else {
1508
+                            if (isset($message->read)) {
1509
+                                // Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag.
1510
+                                $this->importer->ImportMessageReadFlag($serverid, $message->read);
1511
+                            }
1512
+                            elseif (!isset($message->flag)) {
1513
+                                $this->importer->ImportMessageChange($serverid, $message);
1514
+                            }
1515
+
1516
+                            // email todoflags - some devices send todos flags together with read flags,
1517
+                            // so they have to be handled separately
1518
+                            if (isset($message->flag)) {
1519
+                                $this->importer->ImportMessageChange($serverid, $message);
1520
+                            }
1521
+
1522
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1523
+                        }
1524
+                    }
1525
+                    catch (StatusException $stex) {
1526
+                        $actiondata["statusids"][$serverid] = $stex->getCode();
1527
+                    }
1528
+                    break;
1529
+
1530
+                case SYNC_ADD:
1531
+                    self::$topCollector->AnnounceInformation(sprintf("Creating new message from mobile %d", $messageCount));
1532
+
1533
+                    try {
1534
+                        // mark the message as new message so SyncObject->Check() can differentiate
1535
+                        $message->flags = SYNC_NEWMESSAGE;
1536
+
1537
+                        // ignore sms messages
1538
+                        if ($foldertype == "SMS") {
1539
+                            SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1540
+                            // TODO we should create the SMS
1541
+                            // return a fake serverid which we can identify later
1542
+                            $actiondata["clientids"][$clientid] = self::GSYNCIGNORESMS . $clientid;
1543
+                            $actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1544
+                        }
1545
+                        // check incoming message without logging WARN messages about errors
1546
+                        elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1547
+                            $actiondata["clientids"][$clientid] = false;
1548
+                            $actiondata["statusids"][$clientid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1549
+                        }
1550
+                        else {
1551
+                            $actiondata["clientids"][$clientid] = false;
1552
+                            $actiondata["clientids"][$clientid] = $this->importer->ImportMessageChange(false, $message);
1553
+                            $actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1554
+                        }
1555
+                    }
1556
+                    catch (StatusException $stex) {
1557
+                        $actiondata["statusids"][$clientid] = $stex->getCode();
1558
+                    }
1559
+                    break;
1560
+
1561
+                case SYNC_REMOVE:
1562
+                    self::$topCollector->AnnounceInformation(sprintf("Deleting message removed on mobile %d", $messageCount));
1563
+
1564
+                    try {
1565
+                        $actiondata["removeids"][] = $serverid;
1566
+                        // ignore sms messages
1567
+                        if ($foldertype == "SMS" || stripos($serverid, self::GSYNCIGNORESMS) !== false) {
1568
+                            SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1569
+                            // TODO we should delete the SMS
1570
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1571
+                        }
1572
+                        else {
1573
+                            // if message deletions are to be moved, move them
1574
+                            if ($spa->GetDeletesAsMoves()) {
1575
+                                $folderid = self::$backend->GetWasteBasket();
1576
+
1577
+                                if ($folderid) {
1578
+                                    $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1579
+                                    $this->importer->ImportMessageMove($serverid, $folderid);
1580
+
1581
+                                    break;
1582
+                                }
1583
+
1584
+                                SLog::Write(LOGLEVEL_WARN, "Message should be moved to WasteBasket, but the Backend did not return a destination ID. Message is hard deleted now!");
1585
+                            }
1586
+
1587
+                            $this->importer->ImportMessageDeletion($serverid);
1588
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1589
+                        }
1590
+                    }
1591
+                    catch (StatusException $stex) {
1592
+                        if ($stex->getCode() != SYNC_MOVEITEMSSTATUS_SUCCESS) {
1593
+                            $actiondata["statusids"][$serverid] = SYNC_STATUS_OBJECTNOTFOUND;
1594
+                        }
1595
+                    }
1596
+                    break;
1597
+            }
1598
+            SLog::Write(LOGLEVEL_DEBUG, "Sync->importMessage(): message imported");
1599
+        }
1600
+    }
1601
+
1602
+    /**
1603
+     * Keeps some interesting information about the sync process of several folders.
1604
+     *
1605
+     * @param mixed $key
1606
+     * @param mixed $value
1607
+     *
1608
+     * @return
1609
+     */
1610
+    private function saveMultiFolderInfo($key, $value) {
1611
+        if ($key == "incoming" || $key == "outgoing" || $key == "queued" || $key == "fetching") {
1612
+            if (!isset($this->multiFolderInfo[$key])) {
1613
+                $this->multiFolderInfo[$key] = 0;
1614
+            }
1615
+            $this->multiFolderInfo[$key] += $value;
1616
+        }
1617
+        if ($key == "exception") {
1618
+            if (!isset($this->multiFolderInfo[$key])) {
1619
+                $this->multiFolderInfo[$key] = [];
1620
+            }
1621
+            $this->multiFolderInfo[$key][] = $value;
1622
+        }
1623
+    }
1624
+
1625
+    /**
1626
+     * Returns a single string with information about the multi folder synchronization.
1627
+     *
1628
+     * @param int $amountOfFolders
1629
+     *
1630
+     * @return string
1631
+     */
1632
+    private function getMultiFolderInfoLine($amountOfFolders) {
1633
+        $s = $amountOfFolders . " folders";
1634
+        if (isset($this->multiFolderInfo["incoming"])) {
1635
+            $s .= ": " . $this->multiFolderInfo["incoming"] . " saved";
1636
+        }
1637
+        if (isset($this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]) && $this->multiFolderInfo["outgoing"] > 0) {
1638
+            $s .= sprintf(": Streamed %d out of %d", $this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]);
1639
+        }
1640
+        elseif (!isset($this->multiFolderInfo["outgoing"]) && !isset($this->multiFolderInfo["queued"])) {
1641
+            $s .= ": no changes";
1642
+        }
1643
+        else {
1644
+            if (isset($this->multiFolderInfo["outgoing"])) {
1645
+                $s .= "/" . $this->multiFolderInfo["outgoing"] . " streamed";
1646
+            }
1647
+            if (isset($this->multiFolderInfo["queued"])) {
1648
+                $s .= "/" . $this->multiFolderInfo["queued"] . " queued";
1649
+            }
1650
+        }
1651
+        if (isset($this->multiFolderInfo["exception"])) {
1652
+            $exceptions = array_count_values($this->multiFolderInfo["exception"]);
1653
+            foreach ($exceptions as $name => $count) {
1654
+                $s .= sprintf("-%s(%d)", $name, $count);
1655
+            }
1656
+        }
1657
+
1658
+        return $s;
1659
+    }
1660
+
1661
+    /**
1662
+     * Sets the new folderstat and calculates & sets an expiration date for the folder stat.
1663
+     *
1664
+     * @param SyncParameters $spa
1665
+     * @param string         $newFolderStat
1666
+     *
1667
+     * @return
1668
+     */
1669
+    private function setFolderStat($spa, $newFolderStat) {
1670
+        $spa->SetFolderStat($newFolderStat);
1671
+        $maxTimeout = 60 * 60 * 24 * 31; // one month
1672
+
1673
+        $interval = Utils::GetFiltertypeInterval($spa->GetFilterType());
1674
+        $timeout = time() + (($interval && $interval < $maxTimeout) ? $interval : $maxTimeout);
1675
+        // randomize timeout in 12h
1676
+        $timeout -= rand(0, 43200);
1677
+        SLog::Write(LOGLEVEL_DEBUG, sprintf("Sync()->setFolderStat() on %s: %s expiring %s", $spa->getFolderId(), $newFolderStat, date('Y-m-d H:i:s', $timeout)));
1678
+        $spa->SetFolderStatTimeout($timeout);
1679
+    }
1680 1680
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
 					if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
207 207
 						$spa->SetDeletesAsMoves(true);
208 208
 						if (($dam = self::$decoder->getElementContent()) !== false) {
209
-							$spa->SetDeletesAsMoves((bool) $dam);
209
+							$spa->SetDeletesAsMoves((bool)$dam);
210 210
 							if (!self::$decoder->getElementEndTag()) {
211 211
 								return false;
212 212
 							}
@@ -249,7 +249,7 @@  discard block
 block discarded – undo
249 249
 					if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
250 250
 						$spa->SetConversationMode(true);
251 251
 						if (($conversationmode = self::$decoder->getElementContent()) !== false) {
252
-							$spa->SetConversationMode((bool) $conversationmode);
252
+							$spa->SetConversationMode((bool)$conversationmode);
253 253
 							if (!self::$decoder->getElementEndTag()) {
254 254
 								return false;
255 255
 							}
@@ -619,7 +619,7 @@  discard block
 block discarded – undo
619 619
 
620 620
 			if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
621 621
 				$sc->SetGlobalWindowSize(self::$decoder->getElementContent());
622
-				SLog::Write(LOGLEVEL_DEBUG, "Sync(): Global WindowSize requested: " . $sc->GetGlobalWindowSize());
622
+				SLog::Write(LOGLEVEL_DEBUG, "Sync(): Global WindowSize requested: ".$sc->GetGlobalWindowSize());
623 623
 				if (!self::$decoder->getElementEndTag()) { // SYNC_WINDOWSIZE
624 624
 					return false;
625 625
 				}
@@ -1197,7 +1197,7 @@  discard block
 block discarded – undo
1197 1197
 		}
1198 1198
 
1199 1199
 		// Stream outgoing changes
1200
-		if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0 && (bool) $exporter) {
1200
+		if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0 && (bool)$exporter) {
1201 1201
 			self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", (($changecount > $windowSize) ? $windowSize : $changecount)));
1202 1202
 
1203 1203
 			// Output message changes per folder
@@ -1249,7 +1249,7 @@  discard block
 block discarded – undo
1249 1249
 
1250 1250
 			// $progress is not an array when exporting the last message
1251 1251
 			// so we get the number to display from the streamimporter if it's available
1252
-			if ((bool) $streamimporter) {
1252
+			if ((bool)$streamimporter) {
1253 1253
 				$n = $streamimporter->GetImportedMessages();
1254 1254
 			}
1255 1255
 
@@ -1266,7 +1266,7 @@  discard block
 block discarded – undo
1266 1266
 				}
1267 1267
 			}
1268 1268
 
1269
-			self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, ($n >= $windowSize) ? " of " . $changecount : ""), $this->singleFolder);
1269
+			self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, ($n >= $windowSize) ? " of ".$changecount : ""), $this->singleFolder);
1270 1270
 			$this->saveMultiFolderInfo("outgoing", $n);
1271 1271
 			$this->saveMultiFolderInfo("queued", $changecount);
1272 1272
 
@@ -1539,7 +1539,7 @@  discard block
 block discarded – undo
1539 1539
 							SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1540 1540
 							// TODO we should create the SMS
1541 1541
 							// return a fake serverid which we can identify later
1542
-							$actiondata["clientids"][$clientid] = self::GSYNCIGNORESMS . $clientid;
1542
+							$actiondata["clientids"][$clientid] = self::GSYNCIGNORESMS.$clientid;
1543 1543
 							$actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1544 1544
 						}
1545 1545
 						// check incoming message without logging WARN messages about errors
@@ -1630,9 +1630,9 @@  discard block
 block discarded – undo
1630 1630
 	 * @return string
1631 1631
 	 */
1632 1632
 	private function getMultiFolderInfoLine($amountOfFolders) {
1633
-		$s = $amountOfFolders . " folders";
1633
+		$s = $amountOfFolders." folders";
1634 1634
 		if (isset($this->multiFolderInfo["incoming"])) {
1635
-			$s .= ": " . $this->multiFolderInfo["incoming"] . " saved";
1635
+			$s .= ": ".$this->multiFolderInfo["incoming"]." saved";
1636 1636
 		}
1637 1637
 		if (isset($this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]) && $this->multiFolderInfo["outgoing"] > 0) {
1638 1638
 			$s .= sprintf(": Streamed %d out of %d", $this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]);
@@ -1642,10 +1642,10 @@  discard block
 block discarded – undo
1642 1642
 		}
1643 1643
 		else {
1644 1644
 			if (isset($this->multiFolderInfo["outgoing"])) {
1645
-				$s .= "/" . $this->multiFolderInfo["outgoing"] . " streamed";
1645
+				$s .= "/".$this->multiFolderInfo["outgoing"]." streamed";
1646 1646
 			}
1647 1647
 			if (isset($this->multiFolderInfo["queued"])) {
1648
-				$s .= "/" . $this->multiFolderInfo["queued"] . " queued";
1648
+				$s .= "/".$this->multiFolderInfo["queued"]." queued";
1649 1649
 			}
1650 1650
 		}
1651 1651
 		if (isset($this->multiFolderInfo["exception"])) {
Please login to merge, or discard this patch.
Braces   +46 added lines, -92 removed lines patch added patch discarded remove patch
@@ -41,8 +41,7 @@  discard block
 block discarded – undo
41 41
 				SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
42 42
 				self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
43 43
 				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
44
-			}
45
-			else {
44
+			} else {
46 45
 				SLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
47 46
 			}
48 47
 		}
@@ -99,8 +98,7 @@  discard block
 block discarded – undo
99 98
 								return false;
100 99
 							}
101 100
 						}
102
-					}
103
-					else {
101
+					} else {
104 102
 						return false;
105 103
 					}
106 104
 
@@ -133,16 +131,14 @@  discard block
 block discarded – undo
133 131
 							$spa->RemoveSyncKey();
134 132
 							$spa->DelFolderStat();
135 133
 							$spa->SetMoveState(false);
136
-						}
137
-						elseif ($synckey !== false) {
134
+						} elseif ($synckey !== false) {
138 135
 							if ($synckey !== $spa->GetSyncKey() && $synckey !== $spa->GetNewSyncKey()) {
139 136
 								SLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Synckey does not match latest saved for this folder or there is a move state, removing folderstat to force Exporter setup");
140 137
 								$spa->DelFolderStat();
141 138
 							}
142 139
 							$spa->SetSyncKey($synckey);
143 140
 						}
144
-					}
145
-					catch (StateInvalidException $stie) {
141
+					} catch (StateInvalidException $stie) {
146 142
 						$spa = new SyncParameters();
147 143
 						$status = SYNC_STATUS_INVALIDSYNCKEY;
148 144
 						self::$topCollector->AnnounceInformation("State invalid - Resync folder", $this->singleFolder);
@@ -163,8 +159,7 @@  discard block
 block discarded – undo
163 159
 						try {
164 160
 							$spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
165 161
 							SLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
166
-						}
167
-						catch (NoHierarchyCacheAvailableException $nhca) {
162
+						} catch (NoHierarchyCacheAvailableException $nhca) {
168 163
 							$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
169 164
 							self::$deviceManager->ForceFullResync();
170 165
 						}
@@ -176,8 +171,7 @@  discard block
 block discarded – undo
176 171
 
177 172
 					if ($spa->HasContentClass()) {
178 173
 						self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), $this->singleFolder);
179
-					}
180
-					else {
174
+					} else {
181 175
 						SLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
182 176
 					}
183 177
 
@@ -480,8 +474,7 @@  discard block
 block discarded – undo
480 474
 								if (!self::$decoder->getElementEndTag()) {
481 475
 									return false;
482 476
 								}
483
-							}
484
-							else {
477
+							} else {
485 478
 								$foldertype = false;
486 479
 							}
487 480
 
@@ -500,8 +493,7 @@  discard block
 block discarded – undo
500 493
 								if (!self::$decoder->getElementEndTag()) { // end clientid
501 494
 									return false;
502 495
 								}
503
-							}
504
-							else {
496
+							} else {
505 497
 								$clientid = false;
506 498
 							}
507 499
 
@@ -516,8 +508,7 @@  discard block
 block discarded – undo
516 508
 								if (!self::$decoder->getElementEndTag()) { // end applicationdata
517 509
 									return false;
518 510
 								}
519
-							}
520
-							else {
511
+							} else {
521 512
 								$message = false;
522 513
 							}
523 514
 
@@ -534,8 +525,7 @@  discard block
 block discarded – undo
534 525
 
535 526
 									if ($status == SYNC_STATUS_SUCCESS) {
536 527
 										$this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
537
-									}
538
-									else {
528
+									} else {
539 529
 										SLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
540 530
 									}
541 531
 									break;
@@ -543,8 +533,7 @@  discard block
 block discarded – undo
543 533
 
544 534
 							if ($actiondata["fetchids"]) {
545 535
 								self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
546
-							}
547
-							else {
536
+							} else {
548 537
 								self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
549 538
 							}
550 539
 
@@ -563,8 +552,7 @@  discard block
 block discarded – undo
563 552
 							try {
564 553
 								// Save the updated state, which is used for the exporter later
565 554
 								$sc->AddParameter($spa, "state", $this->importer->GetState());
566
-							}
567
-							catch (StatusException $stex) {
555
+							} catch (StatusException $stex) {
568 556
 								$status = $stex->getCode();
569 557
 							}
570 558
 						}
@@ -627,8 +615,7 @@  discard block
 block discarded – undo
627 615
 
628 616
 			if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
629 617
 				$partial = true;
630
-			}
631
-			else {
618
+			} else {
632 619
 				$partial = false;
633 620
 			}
634 621
 
@@ -657,13 +644,11 @@  discard block
 block discarded – undo
657 644
 			// Load all collections - do not overwrite existing (received!), load states, check permissions and only load confirmed states!
658 645
 			try {
659 646
 				$sc->LoadAllCollections(false, true, true, true, true);
660
-			}
661
-			catch (StateInvalidException $siex) {
647
+			} catch (StateInvalidException $siex) {
662 648
 				$status = SYNC_STATUS_INVALIDSYNCKEY;
663 649
 				self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
664 650
 				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
665
-			}
666
-			catch (StatusException $stex) {
651
+			} catch (StatusException $stex) {
667 652
 				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
668 653
 				self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
669 654
 				$this->saveMultiFolderInfo("exception", "StatusException");
@@ -686,8 +671,7 @@  discard block
 block discarded – undo
686 671
 			if (!$sc->HasCollections()) {
687 672
 				$status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
688 673
 			}
689
-		}
690
-		elseif (isset($hbinterval)) {
674
+		} elseif (isset($hbinterval)) {
691 675
 			// load the hierarchy data - there are no permissions to verify so we just set it to false
692 676
 			if (!$sc->LoadCollection(false, true, false)) {
693 677
 				$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
@@ -730,12 +714,10 @@  discard block
 block discarded – undo
730 714
 					// always check for changes
731 715
 					SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
732 716
 					$foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
733
-				}
734
-				catch (StatusException $stex) {
717
+				} catch (StatusException $stex) {
735 718
 					if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
736 719
 						$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
737
-					}
738
-					else {
720
+					} else {
739 721
 						$status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
740 722
 						self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
741 723
 						$this->saveMultiFolderInfo("exception", "StatusException");
@@ -752,8 +734,7 @@  discard block
 block discarded – undo
752 734
 					foreach ($sc as $folderid => $spa) {
753 735
 						if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
754 736
 							$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
755
-						}
756
-						else {
737
+						} else {
757 738
 							$sc->SaveCollection($spa);
758 739
 						}
759 740
 					}
@@ -774,8 +755,7 @@  discard block
 block discarded – undo
774 755
 						if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
775 756
 							SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
776 757
 							$status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
777
-						}
778
-						else {
758
+						} else {
779 759
 							SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
780 760
 						}
781 761
 					}
@@ -875,8 +855,7 @@  discard block
 block discarded – undo
875 855
 							if ($exporter === false) {
876 856
 								throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
877 857
 							}
878
-						}
879
-						catch (StatusException $stex) {
858
+						} catch (StatusException $stex) {
880 859
 							$status = $stex->getCode();
881 860
 						}
882 861
 
@@ -891,12 +870,10 @@  discard block
 block discarded – undo
891 870
 
892 871
 								$changecount = $exporter->GetChangeCount();
893 872
 							}
894
-						}
895
-						catch (StatusException $stex) {
873
+						} catch (StatusException $stex) {
896 874
 							if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
897 875
 								$status = SYNC_STATUS_INVALIDSYNCKEY;
898
-							}
899
-							else {
876
+							} else {
900 877
 								$status = $stex->getCode();
901 878
 							}
902 879
 						}
@@ -910,8 +887,7 @@  discard block
 block discarded – undo
910 887
 							if ($changecount > 0) {
911 888
 								self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
912 889
 							}
913
-						}
914
-						elseif ($status != SYNC_STATUS_SUCCESS) {
890
+						} elseif ($status != SYNC_STATUS_SUCCESS) {
915 891
 							self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
916 892
 							$this->saveMultiFolderInfo("exception", "StatusException");
917 893
 						}
@@ -1055,8 +1031,7 @@  discard block
 block discarded – undo
1055 1031
 		self::$encoder->startTag(SYNC_SYNCKEY);
1056 1032
 		if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
1057 1033
 			self::$encoder->content($spa->GetNewSyncKey());
1058
-		}
1059
-		else {
1034
+		} else {
1060 1035
 			self::$encoder->content($spa->GetSyncKey());
1061 1036
 		}
1062 1037
 		self::$encoder->endTag();
@@ -1150,8 +1125,7 @@  discard block
 block discarded – undo
1150 1125
 						SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
1151 1126
 						$fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1152 1127
 					}
1153
-				}
1154
-				catch (StatusException $stex) {
1128
+				} catch (StatusException $stex) {
1155 1129
 					$fetchstatus = $stex->getCode();
1156 1130
 				}
1157 1131
 
@@ -1168,8 +1142,7 @@  discard block
 block discarded – undo
1168 1142
 					self::$encoder->startTag(SYNC_DATA);
1169 1143
 					$data->Encode(self::$encoder);
1170 1144
 					self::$encoder->endTag();
1171
-				}
1172
-				else {
1145
+				} else {
1173 1146
 					SLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
1174 1147
 				}
1175 1148
 				self::$encoder->endTag();
@@ -1215,13 +1188,11 @@  discard block
 block discarded – undo
1215 1188
 					if ($n % 10 == 0) {
1216 1189
 						self::$topCollector->AnnounceInformation(sprintf("Streamed data of %d objects out of %d", $n, (($changecount > $windowSize) ? $windowSize : $changecount)));
1217 1190
 					}
1218
-				}
1219
-				catch (SyncObjectBrokenException $mbe) {
1191
+				} catch (SyncObjectBrokenException $mbe) {
1220 1192
 					$brokenSO = $mbe->GetSyncObject();
1221 1193
 					if (!$brokenSO) {
1222 1194
 						SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
1223
-					}
1224
-					else {
1195
+					} else {
1225 1196
 						if (!isset($brokenSO->id)) {
1226 1197
 							$brokenSO->id = "Unknown ID";
1227 1198
 							SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Caught SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
@@ -1285,13 +1256,11 @@  discard block
 block discarded – undo
1285 1256
 					$newFolderStatAfterExport = self::$backend->GetFolderStat(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
1286 1257
 					if ($newFolderStat === $newFolderStatAfterExport) {
1287 1258
 						$this->setFolderStat($spa, $newFolderStat);
1288
-					}
1289
-					else {
1259
+					} else {
1290 1260
 						SLog::Write(LOGLEVEL_DEBUG, "Sync() Folderstat differs after export, force another exporter run.");
1291 1261
 					}
1292 1262
 				}
1293
-			}
1294
-			else {
1263
+			} else {
1295 1264
 				self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS);
1296 1265
 			}
1297 1266
 		}
@@ -1316,15 +1285,13 @@  discard block
 block discarded – undo
1316 1285
 				elseif (!$spa->HasSyncKey()) {
1317 1286
 					$state = "";
1318 1287
 				}
1319
-			}
1320
-			catch (StatusException $stex) {
1288
+			} catch (StatusException $stex) {
1321 1289
 				$status = $stex->getCode();
1322 1290
 			}
1323 1291
 
1324 1292
 			if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
1325 1293
 				self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
1326
-			}
1327
-			else {
1294
+			} else {
1328 1295
 				SLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
1329 1296
 			}
1330 1297
 		}
@@ -1365,13 +1332,11 @@  discard block
 block discarded – undo
1365 1332
 				if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
1366 1333
 					throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
1367 1334
 				}
1368
-			}
1369
-			catch (StateNotFoundException $snfex) {
1335
+			} catch (StateNotFoundException $snfex) {
1370 1336
 				$status = SYNC_STATUS_INVALIDSYNCKEY;
1371 1337
 				self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
1372 1338
 				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
1373
-			}
1374
-			catch (StatusException $stex) {
1339
+			} catch (StatusException $stex) {
1375 1340
 				$status = $stex->getCode();
1376 1341
 				self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
1377 1342
 				$this->saveMultiFolderInfo("exception", "StateNotFoundException");
@@ -1411,8 +1376,7 @@  discard block
 block discarded – undo
1411 1376
 				// if there is a valid state obtained after importing changes in a previous loop, we use that state
1412 1377
 				if (isset($actiondata["failstate"], $actiondata["failstate"]["failedsyncstate"])) {
1413 1378
 					$this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict());
1414
-				}
1415
-				else {
1379
+				} else {
1416 1380
 					$this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict());
1417 1381
 				}
1418 1382
 
@@ -1420,8 +1384,7 @@  discard block
 block discarded – undo
1420 1384
 				$this->importer->ConfigContentParameters($spa->GetCPO());
1421 1385
 				$this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state"));
1422 1386
 			}
1423
-		}
1424
-		catch (StatusException $stex) {
1387
+		} catch (StatusException $stex) {
1425 1388
 			$status = $stex->getCode();
1426 1389
 		}
1427 1390
 
@@ -1503,13 +1466,11 @@  discard block
 block discarded – undo
1503 1466
 						// check incoming message without logging WARN messages about errors
1504 1467
 						elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1505 1468
 							$actiondata["statusids"][$serverid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1506
-						}
1507
-						else {
1469
+						} else {
1508 1470
 							if (isset($message->read)) {
1509 1471
 								// Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag.
1510 1472
 								$this->importer->ImportMessageReadFlag($serverid, $message->read);
1511
-							}
1512
-							elseif (!isset($message->flag)) {
1473
+							} elseif (!isset($message->flag)) {
1513 1474
 								$this->importer->ImportMessageChange($serverid, $message);
1514 1475
 							}
1515 1476
 
@@ -1521,8 +1482,7 @@  discard block
 block discarded – undo
1521 1482
 
1522 1483
 							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1523 1484
 						}
1524
-					}
1525
-					catch (StatusException $stex) {
1485
+					} catch (StatusException $stex) {
1526 1486
 						$actiondata["statusids"][$serverid] = $stex->getCode();
1527 1487
 					}
1528 1488
 					break;
@@ -1546,14 +1506,12 @@  discard block
 block discarded – undo
1546 1506
 						elseif (!($message instanceof SyncObject) || !$message->Check(true)) {
1547 1507
 							$actiondata["clientids"][$clientid] = false;
1548 1508
 							$actiondata["statusids"][$clientid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
1549
-						}
1550
-						else {
1509
+						} else {
1551 1510
 							$actiondata["clientids"][$clientid] = false;
1552 1511
 							$actiondata["clientids"][$clientid] = $this->importer->ImportMessageChange(false, $message);
1553 1512
 							$actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS;
1554 1513
 						}
1555
-					}
1556
-					catch (StatusException $stex) {
1514
+					} catch (StatusException $stex) {
1557 1515
 						$actiondata["statusids"][$clientid] = $stex->getCode();
1558 1516
 					}
1559 1517
 					break;
@@ -1568,8 +1526,7 @@  discard block
 block discarded – undo
1568 1526
 							SLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message.");
1569 1527
 							// TODO we should delete the SMS
1570 1528
 							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1571
-						}
1572
-						else {
1529
+						} else {
1573 1530
 							// if message deletions are to be moved, move them
1574 1531
 							if ($spa->GetDeletesAsMoves()) {
1575 1532
 								$folderid = self::$backend->GetWasteBasket();
@@ -1587,8 +1544,7 @@  discard block
 block discarded – undo
1587 1544
 							$this->importer->ImportMessageDeletion($serverid);
1588 1545
 							$actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS;
1589 1546
 						}
1590
-					}
1591
-					catch (StatusException $stex) {
1547
+					} catch (StatusException $stex) {
1592 1548
 						if ($stex->getCode() != SYNC_MOVEITEMSSTATUS_SUCCESS) {
1593 1549
 							$actiondata["statusids"][$serverid] = SYNC_STATUS_OBJECTNOTFOUND;
1594 1550
 						}
@@ -1636,11 +1592,9 @@  discard block
 block discarded – undo
1636 1592
 		}
1637 1593
 		if (isset($this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]) && $this->multiFolderInfo["outgoing"] > 0) {
1638 1594
 			$s .= sprintf(": Streamed %d out of %d", $this->multiFolderInfo["outgoing"], $this->multiFolderInfo["queued"]);
1639
-		}
1640
-		elseif (!isset($this->multiFolderInfo["outgoing"]) && !isset($this->multiFolderInfo["queued"])) {
1595
+		} elseif (!isset($this->multiFolderInfo["outgoing"]) && !isset($this->multiFolderInfo["queued"])) {
1641 1596
 			$s .= ": no changes";
1642
-		}
1643
-		else {
1597
+		} else {
1644 1598
 			if (isset($this->multiFolderInfo["outgoing"])) {
1645 1599
 				$s .= "/" . $this->multiFolderInfo["outgoing"] . " streamed";
1646 1600
 			}
Please login to merge, or discard this patch.
lib/log/syslog.php 3 patches
Indentation   +207 added lines, -207 removed lines patch added patch discarded remove patch
@@ -8,211 +8,211 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class Syslog extends Log {
11
-	protected $program_name = '';
12
-	protected $host;
13
-	protected $port;
14
-
15
-	/**
16
-	 * @return string
17
-	 */
18
-	public function GetProgramName() {
19
-		return $this->program_name;
20
-	}
21
-
22
-	/**
23
-	 * @param string $value
24
-	 */
25
-	public function SetProgramName($value) {
26
-		$this->program_name = $value;
27
-	}
28
-
29
-	/**
30
-	 * @return string
31
-	 */
32
-	public function GetHost() {
33
-		return $this->host;
34
-	}
35
-
36
-	/**
37
-	 * @param string $value
38
-	 */
39
-	public function SetHost($value) {
40
-		$this->host = $value;
41
-	}
42
-
43
-	/**
44
-	 * @return int
45
-	 */
46
-	public function GetPort() {
47
-		return $this->port;
48
-	}
49
-
50
-	/**
51
-	 * @param int $value
52
-	 */
53
-	public function SetPort($value) {
54
-		if (is_numeric($value)) {
55
-			$this->port = (int) $value;
56
-		}
57
-	}
58
-
59
-	/**
60
-	 * Constructor.
61
-	 * Sets configured values if no parameters are given.
62
-	 *
63
-	 * @param string $program_name
64
-	 * @param string $host
65
-	 * @param string $port
66
-	 */
67
-	public function __construct($program_name = null, $host = null, $port = null) {
68
-		parent::__construct();
69
-
70
-		if (is_null($program_name)) {
71
-			$program_name = LOG_SYSLOG_PROGRAM;
72
-		}
73
-		if (is_null($host)) {
74
-			$host = LOG_SYSLOG_HOST;
75
-		}
76
-		if (is_null($port)) {
77
-			$port = LOG_SYSLOG_PORT;
78
-		}
79
-
80
-		$this->SetProgramName($program_name);
81
-		$this->SetHost($host);
82
-		$this->SetPort($port);
83
-	}
84
-
85
-	/**
86
-	 * Return the full program name for syslog.
87
-	 * The name can be grommunio-sync/core or grommunio-sync/{backend} where backend is the backend that initiated the log.
88
-	 *
89
-	 * @return string
90
-	 */
91
-	protected function GenerateProgramName() {
92
-		// @TODO Use another mechanism than debug_backtrace to determine to origin of the log
93
-		$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
94
-		// Shift the "syslog.php" entry.
95
-		array_shift($backtrace);
96
-		foreach ($backtrace as $trace) {
97
-			if (!isset($trace['file'])) {
98
-				continue;
99
-			}
100
-			if (strpos($trace['file'], REAL_BASE_PATH . 'backend/') !== false) {
101
-				preg_match('/\/backend\/([a-zA-Z]*)/', $trace['file'], $match);
102
-				if (isset($match[1])) {
103
-					return $this->GetProgramName() . '/' . $match[1];
104
-				}
105
-			}
106
-			elseif (basename($trace['file'], '.php') != 'slog') {
107
-				return $this->GetProgramName() . '/core';
108
-			}
109
-		}
110
-
111
-		return $this->GetProgramName() . '/core';
112
-	}
113
-
114
-	/**
115
-	 * Maps the grommunio-sync loglevel with those of syslog.
116
-	 *
117
-	 * @param int $loglevel
118
-	 *
119
-	 * @return int one of many LOG_* syslog level
120
-	 */
121
-	protected function GetGsyncLogLevelToSyslogLogLevel($loglevel) {
122
-		switch ($loglevel) {
123
-			case LOGLEVEL_FATAL:
124
-				return LOG_ALERT;
125
-
126
-			case LOGLEVEL_ERROR:
127
-				return LOG_ERR;
128
-
129
-			case LOGLEVEL_WARN:
130
-				return LOG_WARNING;
131
-
132
-			case LOGLEVEL_INFO:
133
-				return LOG_INFO;
134
-
135
-			case LOGLEVEL_DEBUG:
136
-				return LOG_DEBUG;
137
-
138
-			case LOGLEVEL_WBXML:
139
-				return LOG_DEBUG;
140
-
141
-			case LOGLEVEL_DEVICEID:
142
-				return LOG_DEBUG;
143
-
144
-			case LOGLEVEL_WBXMLSTACK:
145
-				return LOG_DEBUG;
146
-		}
147
-
148
-		return null;
149
-	}
150
-
151
-	/**
152
-	 * Build the log string for syslog.
153
-	 *
154
-	 * @param int    $loglevel
155
-	 * @param string $message
156
-	 * @param bool   $includeUserDevice puts username and device in the string, default: true
157
-	 *
158
-	 * @return string
159
-	 */
160
-	public function BuildLogString($loglevel, $message, $includeUserDevice = true) {
161
-		$log = $this->GetLogLevelString($loglevel); // Never pad syslog log because syslog log are usually read with a software.
162
-		// when the users differ, we need to log both
163
-		if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
164
-			$log .= ' [' . $this->GetUser() . ']';
165
-		}
166
-		else {
167
-			$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
168
-		}
169
-		if ($loglevel >= LOGLEVEL_DEVICEID) {
170
-			$log .= '[' . $this->GetDevid() . ']';
171
-		}
172
-		$log .= ' ' . $message;
173
-
174
-		return $log;
175
-	}
176
-
177
-	//
178
-	// Implementation of Log
179
-	//
180
-
181
-	/**
182
-	 * Writes a log message to the general log.
183
-	 *
184
-	 * @param int    $loglevel
185
-	 * @param string $message
186
-	 */
187
-	protected function Write($loglevel, $message) {
188
-		if ($this->GetHost() && $this->GetPort()) {
189
-			$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
190
-			$facility = 1; // user level
191
-			$pri = ($facility * 8) + $loglevel; // multiplying the Facility number by 8 + adding the level
192
-			$data = $this->BuildLogString($loglevel, $message);
193
-			if (strlen(trim($data)) > 0) {
194
-				$syslog_message = "<{$pri}>" . date('M d H:i:s ') . '[' . $this->GetProgramName() . ']: ' . $data;
195
-				socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $this->GetHost(), $this->GetPort());
196
-			}
197
-			socket_close($sock);
198
-		}
199
-		else {
200
-			openlog($this->GenerateProgramName(), LOG_PID, LOG_SYSLOG_FACILITY);
201
-			syslog(
202
-				$this->GetGsyncLogLevelToSyslogLogLevel($loglevel),
203
-				$this->BuildLogString($loglevel, $message)
204
-			);
205
-		}
206
-	}
207
-
208
-	/**
209
-	 * This function is used as an event for log implementer.
210
-	 * It happens when the a call to the Log function is finished.
211
-	 *
212
-	 * @param mixed $loglevel
213
-	 * @param mixed $message
214
-	 */
215
-	public function WriteForUser($loglevel, $message) {
216
-		$this->Write(LOGLEVEL_DEBUG, $message); // Always pass the logleveldebug so it uses syslog level LOG_DEBUG
217
-	}
11
+    protected $program_name = '';
12
+    protected $host;
13
+    protected $port;
14
+
15
+    /**
16
+     * @return string
17
+     */
18
+    public function GetProgramName() {
19
+        return $this->program_name;
20
+    }
21
+
22
+    /**
23
+     * @param string $value
24
+     */
25
+    public function SetProgramName($value) {
26
+        $this->program_name = $value;
27
+    }
28
+
29
+    /**
30
+     * @return string
31
+     */
32
+    public function GetHost() {
33
+        return $this->host;
34
+    }
35
+
36
+    /**
37
+     * @param string $value
38
+     */
39
+    public function SetHost($value) {
40
+        $this->host = $value;
41
+    }
42
+
43
+    /**
44
+     * @return int
45
+     */
46
+    public function GetPort() {
47
+        return $this->port;
48
+    }
49
+
50
+    /**
51
+     * @param int $value
52
+     */
53
+    public function SetPort($value) {
54
+        if (is_numeric($value)) {
55
+            $this->port = (int) $value;
56
+        }
57
+    }
58
+
59
+    /**
60
+     * Constructor.
61
+     * Sets configured values if no parameters are given.
62
+     *
63
+     * @param string $program_name
64
+     * @param string $host
65
+     * @param string $port
66
+     */
67
+    public function __construct($program_name = null, $host = null, $port = null) {
68
+        parent::__construct();
69
+
70
+        if (is_null($program_name)) {
71
+            $program_name = LOG_SYSLOG_PROGRAM;
72
+        }
73
+        if (is_null($host)) {
74
+            $host = LOG_SYSLOG_HOST;
75
+        }
76
+        if (is_null($port)) {
77
+            $port = LOG_SYSLOG_PORT;
78
+        }
79
+
80
+        $this->SetProgramName($program_name);
81
+        $this->SetHost($host);
82
+        $this->SetPort($port);
83
+    }
84
+
85
+    /**
86
+     * Return the full program name for syslog.
87
+     * The name can be grommunio-sync/core or grommunio-sync/{backend} where backend is the backend that initiated the log.
88
+     *
89
+     * @return string
90
+     */
91
+    protected function GenerateProgramName() {
92
+        // @TODO Use another mechanism than debug_backtrace to determine to origin of the log
93
+        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
94
+        // Shift the "syslog.php" entry.
95
+        array_shift($backtrace);
96
+        foreach ($backtrace as $trace) {
97
+            if (!isset($trace['file'])) {
98
+                continue;
99
+            }
100
+            if (strpos($trace['file'], REAL_BASE_PATH . 'backend/') !== false) {
101
+                preg_match('/\/backend\/([a-zA-Z]*)/', $trace['file'], $match);
102
+                if (isset($match[1])) {
103
+                    return $this->GetProgramName() . '/' . $match[1];
104
+                }
105
+            }
106
+            elseif (basename($trace['file'], '.php') != 'slog') {
107
+                return $this->GetProgramName() . '/core';
108
+            }
109
+        }
110
+
111
+        return $this->GetProgramName() . '/core';
112
+    }
113
+
114
+    /**
115
+     * Maps the grommunio-sync loglevel with those of syslog.
116
+     *
117
+     * @param int $loglevel
118
+     *
119
+     * @return int one of many LOG_* syslog level
120
+     */
121
+    protected function GetGsyncLogLevelToSyslogLogLevel($loglevel) {
122
+        switch ($loglevel) {
123
+            case LOGLEVEL_FATAL:
124
+                return LOG_ALERT;
125
+
126
+            case LOGLEVEL_ERROR:
127
+                return LOG_ERR;
128
+
129
+            case LOGLEVEL_WARN:
130
+                return LOG_WARNING;
131
+
132
+            case LOGLEVEL_INFO:
133
+                return LOG_INFO;
134
+
135
+            case LOGLEVEL_DEBUG:
136
+                return LOG_DEBUG;
137
+
138
+            case LOGLEVEL_WBXML:
139
+                return LOG_DEBUG;
140
+
141
+            case LOGLEVEL_DEVICEID:
142
+                return LOG_DEBUG;
143
+
144
+            case LOGLEVEL_WBXMLSTACK:
145
+                return LOG_DEBUG;
146
+        }
147
+
148
+        return null;
149
+    }
150
+
151
+    /**
152
+     * Build the log string for syslog.
153
+     *
154
+     * @param int    $loglevel
155
+     * @param string $message
156
+     * @param bool   $includeUserDevice puts username and device in the string, default: true
157
+     *
158
+     * @return string
159
+     */
160
+    public function BuildLogString($loglevel, $message, $includeUserDevice = true) {
161
+        $log = $this->GetLogLevelString($loglevel); // Never pad syslog log because syslog log are usually read with a software.
162
+        // when the users differ, we need to log both
163
+        if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
164
+            $log .= ' [' . $this->GetUser() . ']';
165
+        }
166
+        else {
167
+            $log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
168
+        }
169
+        if ($loglevel >= LOGLEVEL_DEVICEID) {
170
+            $log .= '[' . $this->GetDevid() . ']';
171
+        }
172
+        $log .= ' ' . $message;
173
+
174
+        return $log;
175
+    }
176
+
177
+    //
178
+    // Implementation of Log
179
+    //
180
+
181
+    /**
182
+     * Writes a log message to the general log.
183
+     *
184
+     * @param int    $loglevel
185
+     * @param string $message
186
+     */
187
+    protected function Write($loglevel, $message) {
188
+        if ($this->GetHost() && $this->GetPort()) {
189
+            $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
190
+            $facility = 1; // user level
191
+            $pri = ($facility * 8) + $loglevel; // multiplying the Facility number by 8 + adding the level
192
+            $data = $this->BuildLogString($loglevel, $message);
193
+            if (strlen(trim($data)) > 0) {
194
+                $syslog_message = "<{$pri}>" . date('M d H:i:s ') . '[' . $this->GetProgramName() . ']: ' . $data;
195
+                socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $this->GetHost(), $this->GetPort());
196
+            }
197
+            socket_close($sock);
198
+        }
199
+        else {
200
+            openlog($this->GenerateProgramName(), LOG_PID, LOG_SYSLOG_FACILITY);
201
+            syslog(
202
+                $this->GetGsyncLogLevelToSyslogLogLevel($loglevel),
203
+                $this->BuildLogString($loglevel, $message)
204
+            );
205
+        }
206
+    }
207
+
208
+    /**
209
+     * This function is used as an event for log implementer.
210
+     * It happens when the a call to the Log function is finished.
211
+     *
212
+     * @param mixed $loglevel
213
+     * @param mixed $message
214
+     */
215
+    public function WriteForUser($loglevel, $message) {
216
+        $this->Write(LOGLEVEL_DEBUG, $message); // Always pass the logleveldebug so it uses syslog level LOG_DEBUG
217
+    }
218 218
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -52,7 +52,7 @@  discard block
 block discarded – undo
52 52
 	 */
53 53
 	public function SetPort($value) {
54 54
 		if (is_numeric($value)) {
55
-			$this->port = (int) $value;
55
+			$this->port = (int)$value;
56 56
 		}
57 57
 	}
58 58
 
@@ -97,18 +97,18 @@  discard block
 block discarded – undo
97 97
 			if (!isset($trace['file'])) {
98 98
 				continue;
99 99
 			}
100
-			if (strpos($trace['file'], REAL_BASE_PATH . 'backend/') !== false) {
100
+			if (strpos($trace['file'], REAL_BASE_PATH.'backend/') !== false) {
101 101
 				preg_match('/\/backend\/([a-zA-Z]*)/', $trace['file'], $match);
102 102
 				if (isset($match[1])) {
103
-					return $this->GetProgramName() . '/' . $match[1];
103
+					return $this->GetProgramName().'/'.$match[1];
104 104
 				}
105 105
 			}
106 106
 			elseif (basename($trace['file'], '.php') != 'slog') {
107
-				return $this->GetProgramName() . '/core';
107
+				return $this->GetProgramName().'/core';
108 108
 			}
109 109
 		}
110 110
 
111
-		return $this->GetProgramName() . '/core';
111
+		return $this->GetProgramName().'/core';
112 112
 	}
113 113
 
114 114
 	/**
@@ -161,15 +161,15 @@  discard block
 block discarded – undo
161 161
 		$log = $this->GetLogLevelString($loglevel); // Never pad syslog log because syslog log are usually read with a software.
162 162
 		// when the users differ, we need to log both
163 163
 		if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
164
-			$log .= ' [' . $this->GetUser() . ']';
164
+			$log .= ' ['.$this->GetUser().']';
165 165
 		}
166 166
 		else {
167
-			$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
167
+			$log .= ' ['.$this->GetAuthUser().Request::IMPERSONATE_DELIM.$this->GetUser().']';
168 168
 		}
169 169
 		if ($loglevel >= LOGLEVEL_DEVICEID) {
170
-			$log .= '[' . $this->GetDevid() . ']';
170
+			$log .= '['.$this->GetDevid().']';
171 171
 		}
172
-		$log .= ' ' . $message;
172
+		$log .= ' '.$message;
173 173
 
174 174
 		return $log;
175 175
 	}
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 			$pri = ($facility * 8) + $loglevel; // multiplying the Facility number by 8 + adding the level
192 192
 			$data = $this->BuildLogString($loglevel, $message);
193 193
 			if (strlen(trim($data)) > 0) {
194
-				$syslog_message = "<{$pri}>" . date('M d H:i:s ') . '[' . $this->GetProgramName() . ']: ' . $data;
194
+				$syslog_message = "<{$pri}>".date('M d H:i:s ').'['.$this->GetProgramName().']: '.$data;
195 195
 				socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $this->GetHost(), $this->GetPort());
196 196
 			}
197 197
 			socket_close($sock);
Please login to merge, or discard this patch.
Braces   +3 added lines, -6 removed lines patch added patch discarded remove patch
@@ -102,8 +102,7 @@  discard block
 block discarded – undo
102 102
 				if (isset($match[1])) {
103 103
 					return $this->GetProgramName() . '/' . $match[1];
104 104
 				}
105
-			}
106
-			elseif (basename($trace['file'], '.php') != 'slog') {
105
+			} elseif (basename($trace['file'], '.php') != 'slog') {
107 106
 				return $this->GetProgramName() . '/core';
108 107
 			}
109 108
 		}
@@ -162,8 +161,7 @@  discard block
 block discarded – undo
162 161
 		// when the users differ, we need to log both
163 162
 		if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
164 163
 			$log .= ' [' . $this->GetUser() . ']';
165
-		}
166
-		else {
164
+		} else {
167 165
 			$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
168 166
 		}
169 167
 		if ($loglevel >= LOGLEVEL_DEVICEID) {
@@ -195,8 +193,7 @@  discard block
 block discarded – undo
195 193
 				socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $this->GetHost(), $this->GetPort());
196 194
 			}
197 195
 			socket_close($sock);
198
-		}
199
-		else {
196
+		} else {
200 197
 			openlog($this->GenerateProgramName(), LOG_PID, LOG_SYSLOG_FACILITY);
201 198
 			syslog(
202 199
 				$this->GetGsyncLogLevelToSyslogLogLevel($loglevel),
Please login to merge, or discard this patch.
lib/log/filelog.php 3 patches
Indentation   +100 added lines, -100 removed lines patch added patch discarded remove patch
@@ -8,115 +8,115 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class FileLog extends Log {
11
-	/**
12
-	 * @var bool|string
13
-	 */
14
-	private $log_to_user_file = false;
11
+    /**
12
+     * @var bool|string
13
+     */
14
+    private $log_to_user_file = false;
15 15
 
16
-	/**
17
-	 * Constructor.
18
-	 */
19
-	public function __construct() {
20
-	}
16
+    /**
17
+     * Constructor.
18
+     */
19
+    public function __construct() {
20
+    }
21 21
 
22
-	/**
23
-	 * Get the log user file.
24
-	 *
25
-	 * @return string
26
-	 */
27
-	private function getLogToUserFile() {
28
-		if ($this->log_to_user_file === false) {
29
-			if (in_array(strtolower($this->GetDevid()), ['', 'validate'])) {
30
-				$this->setLogToUserFile(preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '.log');
31
-			}
32
-			else {
33
-				$this->setLogToUserFile(
34
-					preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '-' .
35
-						(($this->GetAuthUser() != $this->GetUser()) ? preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetUser())) . '-' : '') .
36
-						preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetDevid())) .
37
-						'.log'
38
-				);
39
-			}
40
-		}
22
+    /**
23
+     * Get the log user file.
24
+     *
25
+     * @return string
26
+     */
27
+    private function getLogToUserFile() {
28
+        if ($this->log_to_user_file === false) {
29
+            if (in_array(strtolower($this->GetDevid()), ['', 'validate'])) {
30
+                $this->setLogToUserFile(preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '.log');
31
+            }
32
+            else {
33
+                $this->setLogToUserFile(
34
+                    preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '-' .
35
+                        (($this->GetAuthUser() != $this->GetUser()) ? preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetUser())) . '-' : '') .
36
+                        preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetDevid())) .
37
+                        '.log'
38
+                );
39
+            }
40
+        }
41 41
 
42
-		return $this->log_to_user_file;
43
-	}
42
+        return $this->log_to_user_file;
43
+    }
44 44
 
45
-	/**
46
-	 * Set user log-file relative to log directory.
47
-	 *
48
-	 * @param string $value
49
-	 */
50
-	private function setLogToUserFile($value) {
51
-		$this->log_to_user_file = $value;
52
-	}
45
+    /**
46
+     * Set user log-file relative to log directory.
47
+     *
48
+     * @param string $value
49
+     */
50
+    private function setLogToUserFile($value) {
51
+        $this->log_to_user_file = $value;
52
+    }
53 53
 
54
-	/**
55
-	 * Returns the string to be logged.
56
-	 *
57
-	 * @param int    $loglevel
58
-	 * @param string $message
59
-	 * @param bool   $includeUserDevice puts username and device in the string, default: true
60
-	 *
61
-	 * @return string
62
-	 */
63
-	public function BuildLogString($loglevel, $message, $includeUserDevice = true) {
64
-		$log = Utils::GetFormattedTime() . ' [' . str_pad($this->GetPid(), 5, " ", STR_PAD_LEFT) . '] ' . $this->GetLogLevelString($loglevel, $loglevel >= LOGLEVEL_INFO);
54
+    /**
55
+     * Returns the string to be logged.
56
+     *
57
+     * @param int    $loglevel
58
+     * @param string $message
59
+     * @param bool   $includeUserDevice puts username and device in the string, default: true
60
+     *
61
+     * @return string
62
+     */
63
+    public function BuildLogString($loglevel, $message, $includeUserDevice = true) {
64
+        $log = Utils::GetFormattedTime() . ' [' . str_pad($this->GetPid(), 5, " ", STR_PAD_LEFT) . '] ' . $this->GetLogLevelString($loglevel, $loglevel >= LOGLEVEL_INFO);
65 65
 
66
-		if ($includeUserDevice) {
67
-			// when the users differ, we need to log both
68
-			if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
69
-				$log .= ' [' . $this->GetUser() . ']';
70
-			}
71
-			else {
72
-				$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
73
-			}
74
-		}
75
-		if ($includeUserDevice && (LOGLEVEL >= LOGLEVEL_DEVICEID || (LOGUSERLEVEL >= LOGLEVEL_DEVICEID && $this->IsAuthUserInSpecialLogUsers()))) {
76
-			$log .= ' [' . $this->GetDevid() . ']';
77
-		}
78
-		$log .= ' ' . $message;
66
+        if ($includeUserDevice) {
67
+            // when the users differ, we need to log both
68
+            if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
69
+                $log .= ' [' . $this->GetUser() . ']';
70
+            }
71
+            else {
72
+                $log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
73
+            }
74
+        }
75
+        if ($includeUserDevice && (LOGLEVEL >= LOGLEVEL_DEVICEID || (LOGUSERLEVEL >= LOGLEVEL_DEVICEID && $this->IsAuthUserInSpecialLogUsers()))) {
76
+            $log .= ' [' . $this->GetDevid() . ']';
77
+        }
78
+        $log .= ' ' . $message;
79 79
 
80
-		return $log;
81
-	}
80
+        return $log;
81
+    }
82 82
 
83
-	//
84
-	// Implementation of Log
85
-	//
83
+    //
84
+    // Implementation of Log
85
+    //
86 86
 
87
-	/**
88
-	 * Writes a log message to the general log.
89
-	 *
90
-	 * @param int    $loglevel
91
-	 * @param string $message
92
-	 */
93
-	protected function Write($loglevel, $message) {
94
-		$data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
95
-		@file_put_contents(LOGFILE, $data, FILE_APPEND);
96
-	}
87
+    /**
88
+     * Writes a log message to the general log.
89
+     *
90
+     * @param int    $loglevel
91
+     * @param string $message
92
+     */
93
+    protected function Write($loglevel, $message) {
94
+        $data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
95
+        @file_put_contents(LOGFILE, $data, FILE_APPEND);
96
+    }
97 97
 
98
-	/**
99
-	 * Writes a log message to the user specific log.
100
-	 *
101
-	 * @param int    $loglevel
102
-	 * @param string $message
103
-	 */
104
-	public function WriteForUser($loglevel, $message) {
105
-		$data = $this->BuildLogString($loglevel, $message, false) . PHP_EOL;
106
-		@file_put_contents(LOGFILEDIR . $this->getLogToUserFile(), $data, FILE_APPEND);
107
-	}
98
+    /**
99
+     * Writes a log message to the user specific log.
100
+     *
101
+     * @param int    $loglevel
102
+     * @param string $message
103
+     */
104
+    public function WriteForUser($loglevel, $message) {
105
+        $data = $this->BuildLogString($loglevel, $message, false) . PHP_EOL;
106
+        @file_put_contents(LOGFILEDIR . $this->getLogToUserFile(), $data, FILE_APPEND);
107
+    }
108 108
 
109
-	/**
110
-	 * This function is used as an event for log implementer.
111
-	 * It happens when the a call to the Log function is finished.
112
-	 *
113
-	 * @param mixed $loglevel
114
-	 * @param mixed $message
115
-	 */
116
-	protected function afterLog($loglevel, $message) {
117
-		if ($loglevel & (LOGLEVEL_FATAL | LOGLEVEL_ERROR | LOGLEVEL_WARN)) {
118
-			$data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
119
-			@file_put_contents(LOGERRORFILE, $data, FILE_APPEND);
120
-		}
121
-	}
109
+    /**
110
+     * This function is used as an event for log implementer.
111
+     * It happens when the a call to the Log function is finished.
112
+     *
113
+     * @param mixed $loglevel
114
+     * @param mixed $message
115
+     */
116
+    protected function afterLog($loglevel, $message) {
117
+        if ($loglevel & (LOGLEVEL_FATAL | LOGLEVEL_ERROR | LOGLEVEL_WARN)) {
118
+            $data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
119
+            @file_put_contents(LOGERRORFILE, $data, FILE_APPEND);
120
+        }
121
+    }
122 122
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -27,13 +27,13 @@  discard block
 block discarded – undo
27 27
 	private function getLogToUserFile() {
28 28
 		if ($this->log_to_user_file === false) {
29 29
 			if (in_array(strtolower($this->GetDevid()), ['', 'validate'])) {
30
-				$this->setLogToUserFile(preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '.log');
30
+				$this->setLogToUserFile(preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())).'.log');
31 31
 			}
32 32
 			else {
33 33
 				$this->setLogToUserFile(
34
-					preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '-' .
35
-						(($this->GetAuthUser() != $this->GetUser()) ? preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetUser())) . '-' : '') .
36
-						preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetDevid())) .
34
+					preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())).'-'.
35
+						(($this->GetAuthUser() != $this->GetUser()) ? preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetUser())).'-' : '').
36
+						preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetDevid())).
37 37
 						'.log'
38 38
 				);
39 39
 			}
@@ -61,21 +61,21 @@  discard block
 block discarded – undo
61 61
 	 * @return string
62 62
 	 */
63 63
 	public function BuildLogString($loglevel, $message, $includeUserDevice = true) {
64
-		$log = Utils::GetFormattedTime() . ' [' . str_pad($this->GetPid(), 5, " ", STR_PAD_LEFT) . '] ' . $this->GetLogLevelString($loglevel, $loglevel >= LOGLEVEL_INFO);
64
+		$log = Utils::GetFormattedTime().' ['.str_pad($this->GetPid(), 5, " ", STR_PAD_LEFT).'] '.$this->GetLogLevelString($loglevel, $loglevel >= LOGLEVEL_INFO);
65 65
 
66 66
 		if ($includeUserDevice) {
67 67
 			// when the users differ, we need to log both
68 68
 			if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
69
-				$log .= ' [' . $this->GetUser() . ']';
69
+				$log .= ' ['.$this->GetUser().']';
70 70
 			}
71 71
 			else {
72
-				$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
72
+				$log .= ' ['.$this->GetAuthUser().Request::IMPERSONATE_DELIM.$this->GetUser().']';
73 73
 			}
74 74
 		}
75 75
 		if ($includeUserDevice && (LOGLEVEL >= LOGLEVEL_DEVICEID || (LOGUSERLEVEL >= LOGLEVEL_DEVICEID && $this->IsAuthUserInSpecialLogUsers()))) {
76
-			$log .= ' [' . $this->GetDevid() . ']';
76
+			$log .= ' ['.$this->GetDevid().']';
77 77
 		}
78
-		$log .= ' ' . $message;
78
+		$log .= ' '.$message;
79 79
 
80 80
 		return $log;
81 81
 	}
@@ -91,7 +91,7 @@  discard block
 block discarded – undo
91 91
 	 * @param string $message
92 92
 	 */
93 93
 	protected function Write($loglevel, $message) {
94
-		$data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
94
+		$data = $this->BuildLogString($loglevel, $message).PHP_EOL;
95 95
 		@file_put_contents(LOGFILE, $data, FILE_APPEND);
96 96
 	}
97 97
 
@@ -102,8 +102,8 @@  discard block
 block discarded – undo
102 102
 	 * @param string $message
103 103
 	 */
104 104
 	public function WriteForUser($loglevel, $message) {
105
-		$data = $this->BuildLogString($loglevel, $message, false) . PHP_EOL;
106
-		@file_put_contents(LOGFILEDIR . $this->getLogToUserFile(), $data, FILE_APPEND);
105
+		$data = $this->BuildLogString($loglevel, $message, false).PHP_EOL;
106
+		@file_put_contents(LOGFILEDIR.$this->getLogToUserFile(), $data, FILE_APPEND);
107 107
 	}
108 108
 
109 109
 	/**
@@ -114,8 +114,8 @@  discard block
 block discarded – undo
114 114
 	 * @param mixed $message
115 115
 	 */
116 116
 	protected function afterLog($loglevel, $message) {
117
-		if ($loglevel & (LOGLEVEL_FATAL | LOGLEVEL_ERROR | LOGLEVEL_WARN)) {
118
-			$data = $this->BuildLogString($loglevel, $message) . PHP_EOL;
117
+		if ($loglevel & (LOGLEVEL_FATAL|LOGLEVEL_ERROR|LOGLEVEL_WARN)) {
118
+			$data = $this->BuildLogString($loglevel, $message).PHP_EOL;
119 119
 			@file_put_contents(LOGERRORFILE, $data, FILE_APPEND);
120 120
 		}
121 121
 	}
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -28,8 +28,7 @@  discard block
 block discarded – undo
28 28
 		if ($this->log_to_user_file === false) {
29 29
 			if (in_array(strtolower($this->GetDevid()), ['', 'validate'])) {
30 30
 				$this->setLogToUserFile(preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '.log');
31
-			}
32
-			else {
31
+			} else {
33 32
 				$this->setLogToUserFile(
34 33
 					preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetAuthUser())) . '-' .
35 34
 						(($this->GetAuthUser() != $this->GetUser()) ? preg_replace('/[^a-z0-9]/', '_', strtolower($this->GetUser())) . '-' : '') .
@@ -67,8 +66,7 @@  discard block
 block discarded – undo
67 66
 			// when the users differ, we need to log both
68 67
 			if (strcasecmp($this->GetAuthUser(), $this->GetUser()) == 0) {
69 68
 				$log .= ' [' . $this->GetUser() . ']';
70
-			}
71
-			else {
69
+			} else {
72 70
 				$log .= ' [' . $this->GetAuthUser() . Request::IMPERSONATE_DELIM . $this->GetUser() . ']';
73 71
 			}
74 72
 		}
Please login to merge, or discard this patch.
lib/log/log.php 3 patches
Indentation   +314 added lines, -314 removed lines patch added patch discarded remove patch
@@ -8,318 +8,318 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 abstract class Log {
11
-	/**
12
-	 * @var string
13
-	 */
14
-	protected $user = '';
15
-
16
-	/**
17
-	 * @var string
18
-	 */
19
-	protected $authUser = '';
20
-
21
-	/**
22
-	 * @var string
23
-	 */
24
-	protected $devid = '';
25
-
26
-	/**
27
-	 * @var string
28
-	 */
29
-	protected $pid = '';
30
-
31
-	/**
32
-	 * @var array
33
-	 */
34
-	protected $specialLogUsers = [];
35
-
36
-	/**
37
-	 * Only used as a cache value for IsUserInSpecialLogUsers.
38
-	 *
39
-	 * @var array
40
-	 */
41
-	private $isUserInSpecialLogUsers = [];
42
-
43
-	/**
44
-	 * Only used as a cache value for IsAuthUserInSpecialLogUsers function.
45
-	 *
46
-	 * @var bool
47
-	 */
48
-	private $isAuthUserInSpecialLogUsers = false;
49
-
50
-	/**
51
-	 * @var array
52
-	 */
53
-	private $unauthMessageCache = [];
54
-
55
-	/**
56
-	 * Constructor.
57
-	 */
58
-	public function __construct() {
59
-	}
60
-
61
-	/**
62
-	 * Returns the current user.
63
-	 *
64
-	 * @return string
65
-	 */
66
-	public function GetUser() {
67
-		return $this->user;
68
-	}
69
-
70
-	/**
71
-	 * Sets the current user.
72
-	 *
73
-	 * @param string $value
74
-	 */
75
-	public function SetUser($value) {
76
-		$this->user = $value;
77
-	}
78
-
79
-	/**
80
-	 * Returns the current authenticated user.
81
-	 *
82
-	 * @return string
83
-	 */
84
-	public function GetAuthUser() {
85
-		return $this->authUser;
86
-	}
87
-
88
-	/**
89
-	 * Sets the current authenticated user.
90
-	 *
91
-	 * @param string $value
92
-	 */
93
-	public function SetAuthUser($value) {
94
-		$this->isAuthUserInSpecialLogUsers = false;
95
-		$this->authUser = $value;
96
-	}
97
-
98
-	/**
99
-	 * Check that the current authUser ($this->GetAuthUser) is in the special log user array.
100
-	 * This call is equivalent to `$this->IsUserInSpecialLogUsers($this->GetAuthUser())` at the exception that this
101
-	 * call uses cache so there won't be more than one check to the specialLogUser for the AuthUser.
102
-	 *
103
-	 * @return bool
104
-	 */
105
-	public function IsAuthUserInSpecialLogUsers() {
106
-		if ($this->isAuthUserInSpecialLogUsers) {
107
-			return true;
108
-		}
109
-		if ($this->IsUserInSpecialLogUsers($this->GetAuthUser())) {
110
-			$this->isAuthUserInSpecialLogUsers = true;
111
-
112
-			return true;
113
-		}
114
-
115
-		return false;
116
-	}
117
-
118
-	/**
119
-	 * Returns the current device id.
120
-	 *
121
-	 * @return string
122
-	 */
123
-	public function GetDevid() {
124
-		return $this->devid;
125
-	}
126
-
127
-	/**
128
-	 * Sets the current device id.
129
-	 *
130
-	 * @param string $value
131
-	 */
132
-	public function SetDevid($value) {
133
-		$this->devid = $value;
134
-	}
135
-
136
-	/**
137
-	 * Returns the current PID (as string).
138
-	 *
139
-	 * @return string
140
-	 */
141
-	public function GetPid() {
142
-		return $this->pid;
143
-	}
144
-
145
-	/**
146
-	 * Sets the current PID.
147
-	 *
148
-	 * @param string $value
149
-	 */
150
-	public function SetPid($value) {
151
-		$this->pid = $value;
152
-	}
153
-
154
-	/**
155
-	 * Indicates if special log users are known.
156
-	 *
157
-	 * @return bool True if we do have to log some specific user. False otherwise.
158
-	 */
159
-	public function HasSpecialLogUsers() {
160
-		return !empty($this->specialLogUsers) || $this->isAuthUserInSpecialLogUsers;
161
-	}
162
-
163
-	/**
164
-	 * Indicates if the user is in the special log users.
165
-	 *
166
-	 * @param string $user
167
-	 *
168
-	 * @return bool
169
-	 */
170
-	public function IsUserInSpecialLogUsers($user) {
171
-		if (isset($this->isUserInSpecialLogUsers[$user])) {
172
-			return true;
173
-		}
174
-		if ($this->HasSpecialLogUsers() && in_array($user, $this->GetSpecialLogUsers())) {
175
-			$this->isUserInSpecialLogUsers[$user] = true;
176
-
177
-			return true;
178
-		}
179
-
180
-		return false;
181
-	}
182
-
183
-	/**
184
-	 * Returns the current special log users array.
185
-	 *
186
-	 * @return array
187
-	 */
188
-	public function GetSpecialLogUsers() {
189
-		return $this->specialLogUsers;
190
-	}
191
-
192
-	/**
193
-	 * Sets the current special log users array.
194
-	 */
195
-	public function SetSpecialLogUsers(array $value) {
196
-		$this->isUserInSpecialLogUsers = []; // reset cache
197
-		$this->specialLogUsers = $value;
198
-	}
199
-
200
-	/**
201
-	 * If called, the current user should get an extra log-file.
202
-	 *
203
-	 * If called until the user is authenticated (e.g. at the end of IBackend->Logon()) all
204
-	 * messages logged until then will also be logged in the user file.
205
-	 */
206
-	public function SpecialLogUser() {
207
-		$this->isAuthUserInSpecialLogUsers = true;
208
-	}
209
-
210
-	/**
211
-	 * Logs a message with a given log level.
212
-	 *
213
-	 * @param int    $loglevel
214
-	 * @param string $message
215
-	 */
216
-	public function Log($loglevel, $message) {
217
-		if ($loglevel <= LOGLEVEL) {
218
-			$this->Write($loglevel, $message);
219
-		}
220
-		if ($loglevel <= LOGUSERLEVEL) {
221
-			// cache log messages for unauthenticated users
222
-			if (!RequestProcessor::isUserAuthenticated()) {
223
-				$this->unauthMessageCache[] = [$loglevel, $message];
224
-			}
225
-			// user is authenticated now
226
-			elseif ($this->IsAuthUserInSpecialLogUsers()) {
227
-				// something was logged before the user was authenticated and cached write it to the log
228
-				if (!empty($this->unauthMessageCache)) {
229
-					foreach ($this->unauthMessageCache as $authcache) {
230
-						$this->WriteForUser($authcache[0], $authcache[1]);
231
-					}
232
-					$this->unauthMessageCache = [];
233
-				}
234
-				$this->WriteForUser($loglevel, $message);
235
-			}
236
-			else {
237
-				$this->unauthMessageCache[] = [$loglevel, $message];
238
-			}
239
-		}
240
-
241
-		$this->afterLog($loglevel, $message);
242
-	}
243
-
244
-	/**
245
-	 * This function is used as an event for log implementer.
246
-	 * It happens when the SLog static class is finished with the initialization of this instance.
247
-	 */
248
-	public function AfterInitialize() {
249
-	}
250
-
251
-	/**
252
-	 * This function is used as an event for log implementer.
253
-	 * It happens when the a call to the Log function is finished.
254
-	 *
255
-	 * @param mixed $loglevel
256
-	 * @param mixed $message
257
-	 */
258
-	protected function afterLog($loglevel, $message) {
259
-	}
260
-
261
-	/**
262
-	 * Returns the string representation of the given $loglevel.
263
-	 * String can be padded.
264
-	 *
265
-	 * @param int  $loglevel one of the LOGLEVELs
266
-	 * @param bool $pad
267
-	 *
268
-	 * @return string
269
-	 */
270
-	protected function GetLogLevelString($loglevel, $pad = false) {
271
-		if ($pad) {
272
-			$s = " ";
273
-		}
274
-		else {
275
-			$s = "";
276
-		}
277
-
278
-		switch ($loglevel) {
279
-			case LOGLEVEL_OFF:
280
-				return "";
281
-
282
-			case LOGLEVEL_FATAL:
283
-				return "[FATAL]";
284
-
285
-			case LOGLEVEL_ERROR:
286
-				return "[ERROR]";
287
-
288
-			case LOGLEVEL_WARN:
289
-				return "[" . $s . "WARN]";
290
-
291
-			case LOGLEVEL_INFO:
292
-				return "[" . $s . "INFO]";
293
-
294
-			case LOGLEVEL_DEBUG:
295
-				return "[DEBUG]";
296
-
297
-			case LOGLEVEL_WBXML:
298
-				return "[WBXML]";
299
-
300
-			case LOGLEVEL_DEVICEID:
301
-				return "[DEVICEID]";
302
-
303
-			case LOGLEVEL_WBXMLSTACK:
304
-				return "[WBXMLSTACK]";
305
-		}
306
-
307
-		return "";
308
-	}
309
-
310
-	/**
311
-	 * Writes a log message to the general log.
312
-	 *
313
-	 * @param int    $loglevel
314
-	 * @param string $message
315
-	 */
316
-	abstract protected function Write($loglevel, $message);
317
-
318
-	/**
319
-	 * Writes a log message to the user specific log.
320
-	 *
321
-	 * @param int    $loglevel
322
-	 * @param string $message
323
-	 */
324
-	abstract public function WriteForUser($loglevel, $message);
11
+    /**
12
+     * @var string
13
+     */
14
+    protected $user = '';
15
+
16
+    /**
17
+     * @var string
18
+     */
19
+    protected $authUser = '';
20
+
21
+    /**
22
+     * @var string
23
+     */
24
+    protected $devid = '';
25
+
26
+    /**
27
+     * @var string
28
+     */
29
+    protected $pid = '';
30
+
31
+    /**
32
+     * @var array
33
+     */
34
+    protected $specialLogUsers = [];
35
+
36
+    /**
37
+     * Only used as a cache value for IsUserInSpecialLogUsers.
38
+     *
39
+     * @var array
40
+     */
41
+    private $isUserInSpecialLogUsers = [];
42
+
43
+    /**
44
+     * Only used as a cache value for IsAuthUserInSpecialLogUsers function.
45
+     *
46
+     * @var bool
47
+     */
48
+    private $isAuthUserInSpecialLogUsers = false;
49
+
50
+    /**
51
+     * @var array
52
+     */
53
+    private $unauthMessageCache = [];
54
+
55
+    /**
56
+     * Constructor.
57
+     */
58
+    public function __construct() {
59
+    }
60
+
61
+    /**
62
+     * Returns the current user.
63
+     *
64
+     * @return string
65
+     */
66
+    public function GetUser() {
67
+        return $this->user;
68
+    }
69
+
70
+    /**
71
+     * Sets the current user.
72
+     *
73
+     * @param string $value
74
+     */
75
+    public function SetUser($value) {
76
+        $this->user = $value;
77
+    }
78
+
79
+    /**
80
+     * Returns the current authenticated user.
81
+     *
82
+     * @return string
83
+     */
84
+    public function GetAuthUser() {
85
+        return $this->authUser;
86
+    }
87
+
88
+    /**
89
+     * Sets the current authenticated user.
90
+     *
91
+     * @param string $value
92
+     */
93
+    public function SetAuthUser($value) {
94
+        $this->isAuthUserInSpecialLogUsers = false;
95
+        $this->authUser = $value;
96
+    }
97
+
98
+    /**
99
+     * Check that the current authUser ($this->GetAuthUser) is in the special log user array.
100
+     * This call is equivalent to `$this->IsUserInSpecialLogUsers($this->GetAuthUser())` at the exception that this
101
+     * call uses cache so there won't be more than one check to the specialLogUser for the AuthUser.
102
+     *
103
+     * @return bool
104
+     */
105
+    public function IsAuthUserInSpecialLogUsers() {
106
+        if ($this->isAuthUserInSpecialLogUsers) {
107
+            return true;
108
+        }
109
+        if ($this->IsUserInSpecialLogUsers($this->GetAuthUser())) {
110
+            $this->isAuthUserInSpecialLogUsers = true;
111
+
112
+            return true;
113
+        }
114
+
115
+        return false;
116
+    }
117
+
118
+    /**
119
+     * Returns the current device id.
120
+     *
121
+     * @return string
122
+     */
123
+    public function GetDevid() {
124
+        return $this->devid;
125
+    }
126
+
127
+    /**
128
+     * Sets the current device id.
129
+     *
130
+     * @param string $value
131
+     */
132
+    public function SetDevid($value) {
133
+        $this->devid = $value;
134
+    }
135
+
136
+    /**
137
+     * Returns the current PID (as string).
138
+     *
139
+     * @return string
140
+     */
141
+    public function GetPid() {
142
+        return $this->pid;
143
+    }
144
+
145
+    /**
146
+     * Sets the current PID.
147
+     *
148
+     * @param string $value
149
+     */
150
+    public function SetPid($value) {
151
+        $this->pid = $value;
152
+    }
153
+
154
+    /**
155
+     * Indicates if special log users are known.
156
+     *
157
+     * @return bool True if we do have to log some specific user. False otherwise.
158
+     */
159
+    public function HasSpecialLogUsers() {
160
+        return !empty($this->specialLogUsers) || $this->isAuthUserInSpecialLogUsers;
161
+    }
162
+
163
+    /**
164
+     * Indicates if the user is in the special log users.
165
+     *
166
+     * @param string $user
167
+     *
168
+     * @return bool
169
+     */
170
+    public function IsUserInSpecialLogUsers($user) {
171
+        if (isset($this->isUserInSpecialLogUsers[$user])) {
172
+            return true;
173
+        }
174
+        if ($this->HasSpecialLogUsers() && in_array($user, $this->GetSpecialLogUsers())) {
175
+            $this->isUserInSpecialLogUsers[$user] = true;
176
+
177
+            return true;
178
+        }
179
+
180
+        return false;
181
+    }
182
+
183
+    /**
184
+     * Returns the current special log users array.
185
+     *
186
+     * @return array
187
+     */
188
+    public function GetSpecialLogUsers() {
189
+        return $this->specialLogUsers;
190
+    }
191
+
192
+    /**
193
+     * Sets the current special log users array.
194
+     */
195
+    public function SetSpecialLogUsers(array $value) {
196
+        $this->isUserInSpecialLogUsers = []; // reset cache
197
+        $this->specialLogUsers = $value;
198
+    }
199
+
200
+    /**
201
+     * If called, the current user should get an extra log-file.
202
+     *
203
+     * If called until the user is authenticated (e.g. at the end of IBackend->Logon()) all
204
+     * messages logged until then will also be logged in the user file.
205
+     */
206
+    public function SpecialLogUser() {
207
+        $this->isAuthUserInSpecialLogUsers = true;
208
+    }
209
+
210
+    /**
211
+     * Logs a message with a given log level.
212
+     *
213
+     * @param int    $loglevel
214
+     * @param string $message
215
+     */
216
+    public function Log($loglevel, $message) {
217
+        if ($loglevel <= LOGLEVEL) {
218
+            $this->Write($loglevel, $message);
219
+        }
220
+        if ($loglevel <= LOGUSERLEVEL) {
221
+            // cache log messages for unauthenticated users
222
+            if (!RequestProcessor::isUserAuthenticated()) {
223
+                $this->unauthMessageCache[] = [$loglevel, $message];
224
+            }
225
+            // user is authenticated now
226
+            elseif ($this->IsAuthUserInSpecialLogUsers()) {
227
+                // something was logged before the user was authenticated and cached write it to the log
228
+                if (!empty($this->unauthMessageCache)) {
229
+                    foreach ($this->unauthMessageCache as $authcache) {
230
+                        $this->WriteForUser($authcache[0], $authcache[1]);
231
+                    }
232
+                    $this->unauthMessageCache = [];
233
+                }
234
+                $this->WriteForUser($loglevel, $message);
235
+            }
236
+            else {
237
+                $this->unauthMessageCache[] = [$loglevel, $message];
238
+            }
239
+        }
240
+
241
+        $this->afterLog($loglevel, $message);
242
+    }
243
+
244
+    /**
245
+     * This function is used as an event for log implementer.
246
+     * It happens when the SLog static class is finished with the initialization of this instance.
247
+     */
248
+    public function AfterInitialize() {
249
+    }
250
+
251
+    /**
252
+     * This function is used as an event for log implementer.
253
+     * It happens when the a call to the Log function is finished.
254
+     *
255
+     * @param mixed $loglevel
256
+     * @param mixed $message
257
+     */
258
+    protected function afterLog($loglevel, $message) {
259
+    }
260
+
261
+    /**
262
+     * Returns the string representation of the given $loglevel.
263
+     * String can be padded.
264
+     *
265
+     * @param int  $loglevel one of the LOGLEVELs
266
+     * @param bool $pad
267
+     *
268
+     * @return string
269
+     */
270
+    protected function GetLogLevelString($loglevel, $pad = false) {
271
+        if ($pad) {
272
+            $s = " ";
273
+        }
274
+        else {
275
+            $s = "";
276
+        }
277
+
278
+        switch ($loglevel) {
279
+            case LOGLEVEL_OFF:
280
+                return "";
281
+
282
+            case LOGLEVEL_FATAL:
283
+                return "[FATAL]";
284
+
285
+            case LOGLEVEL_ERROR:
286
+                return "[ERROR]";
287
+
288
+            case LOGLEVEL_WARN:
289
+                return "[" . $s . "WARN]";
290
+
291
+            case LOGLEVEL_INFO:
292
+                return "[" . $s . "INFO]";
293
+
294
+            case LOGLEVEL_DEBUG:
295
+                return "[DEBUG]";
296
+
297
+            case LOGLEVEL_WBXML:
298
+                return "[WBXML]";
299
+
300
+            case LOGLEVEL_DEVICEID:
301
+                return "[DEVICEID]";
302
+
303
+            case LOGLEVEL_WBXMLSTACK:
304
+                return "[WBXMLSTACK]";
305
+        }
306
+
307
+        return "";
308
+    }
309
+
310
+    /**
311
+     * Writes a log message to the general log.
312
+     *
313
+     * @param int    $loglevel
314
+     * @param string $message
315
+     */
316
+    abstract protected function Write($loglevel, $message);
317
+
318
+    /**
319
+     * Writes a log message to the user specific log.
320
+     *
321
+     * @param int    $loglevel
322
+     * @param string $message
323
+     */
324
+    abstract public function WriteForUser($loglevel, $message);
325 325
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -286,10 +286,10 @@
 block discarded – undo
286 286
 				return "[ERROR]";
287 287
 
288 288
 			case LOGLEVEL_WARN:
289
-				return "[" . $s . "WARN]";
289
+				return "[".$s."WARN]";
290 290
 
291 291
 			case LOGLEVEL_INFO:
292
-				return "[" . $s . "INFO]";
292
+				return "[".$s."INFO]";
293 293
 
294 294
 			case LOGLEVEL_DEBUG:
295 295
 				return "[DEBUG]";
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -232,8 +232,7 @@  discard block
 block discarded – undo
232 232
 					$this->unauthMessageCache = [];
233 233
 				}
234 234
 				$this->WriteForUser($loglevel, $message);
235
-			}
236
-			else {
235
+			} else {
237 236
 				$this->unauthMessageCache[] = [$loglevel, $message];
238 237
 			}
239 238
 		}
@@ -270,8 +269,7 @@  discard block
 block discarded – undo
270 269
 	protected function GetLogLevelString($loglevel, $pad = false) {
271 270
 		if ($pad) {
272 271
 			$s = " ";
273
-		}
274
-		else {
272
+		} else {
275 273
 			$s = "";
276 274
 		}
277 275
 
Please login to merge, or discard this patch.
lib/syncobjects/syncappointmentexception.php 1 patch
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -10,44 +10,44 @@
 block discarded – undo
10 10
  */
11 11
 
12 12
 class SyncAppointmentException extends SyncAppointment {
13
-	public $deleted;
14
-	public $exceptionstarttime;
13
+    public $deleted;
14
+    public $exceptionstarttime;
15 15
 
16
-	public function __construct() {
17
-		parent::__construct();
16
+    public function __construct() {
17
+        parent::__construct();
18 18
 
19
-		$this->mapping += [
20
-			SYNC_POOMCAL_DELETED => [
21
-				self::STREAMER_VAR => "deleted",
22
-				self::STREAMER_CHECKS => [self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO],
23
-				self::STREAMER_RONOTIFY => true,
24
-			],
25
-			SYNC_POOMCAL_EXCEPTIONSTARTTIME => [
26
-				self::STREAMER_VAR => "exceptionstarttime",
27
-				self::STREAMER_TYPE => self::STREAMER_TYPE_DATE,
28
-				self::STREAMER_CHECKS => [self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE],
29
-				self::STREAMER_RONOTIFY => true,
30
-			],
31
-		];
19
+        $this->mapping += [
20
+            SYNC_POOMCAL_DELETED => [
21
+                self::STREAMER_VAR => "deleted",
22
+                self::STREAMER_CHECKS => [self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO],
23
+                self::STREAMER_RONOTIFY => true,
24
+            ],
25
+            SYNC_POOMCAL_EXCEPTIONSTARTTIME => [
26
+                self::STREAMER_VAR => "exceptionstarttime",
27
+                self::STREAMER_TYPE => self::STREAMER_TYPE_DATE,
28
+                self::STREAMER_CHECKS => [self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE],
29
+                self::STREAMER_RONOTIFY => true,
30
+            ],
31
+        ];
32 32
 
33
-		// some parameters are not required in an exception, others are not allowed to be set in SyncAppointmentExceptions
34
-		$this->mapping[SYNC_POOMCAL_TIMEZONE][self::STREAMER_CHECKS] = [];
35
-		$this->mapping[SYNC_POOMCAL_TIMEZONE][self::STREAMER_RONOTIFY] = true;
36
-		$this->mapping[SYNC_POOMCAL_DTSTAMP][self::STREAMER_CHECKS] = [];
37
-		$this->mapping[SYNC_POOMCAL_STARTTIME][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPLOWER => SYNC_POOMCAL_ENDTIME];
38
-		$this->mapping[SYNC_POOMCAL_STARTTIME][self::STREAMER_RONOTIFY] = true;
39
-		$this->mapping[SYNC_POOMCAL_SUBJECT][self::STREAMER_CHECKS] = [];
40
-		$this->mapping[SYNC_POOMCAL_SUBJECT][self::STREAMER_RONOTIFY] = true;
41
-		$this->mapping[SYNC_POOMCAL_ENDTIME][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMCAL_STARTTIME];
42
-		$this->mapping[SYNC_POOMCAL_ENDTIME][self::STREAMER_RONOTIFY] = true;
43
-		$this->mapping[SYNC_POOMCAL_BUSYSTATUS][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_ONEVALUEOF => [0, 1, 2, 3, 4]];
44
-		$this->mapping[SYNC_POOMCAL_BUSYSTATUS][self::STREAMER_RONOTIFY] = true;
45
-		$this->mapping[SYNC_POOMCAL_REMINDER][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPHIGHER => -1];
46
-		$this->mapping[SYNC_POOMCAL_REMINDER][self::STREAMER_RONOTIFY] = true;
47
-		$this->mapping[SYNC_POOMCAL_EXCEPTIONS][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_NOTALLOWED => true];
33
+        // some parameters are not required in an exception, others are not allowed to be set in SyncAppointmentExceptions
34
+        $this->mapping[SYNC_POOMCAL_TIMEZONE][self::STREAMER_CHECKS] = [];
35
+        $this->mapping[SYNC_POOMCAL_TIMEZONE][self::STREAMER_RONOTIFY] = true;
36
+        $this->mapping[SYNC_POOMCAL_DTSTAMP][self::STREAMER_CHECKS] = [];
37
+        $this->mapping[SYNC_POOMCAL_STARTTIME][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPLOWER => SYNC_POOMCAL_ENDTIME];
38
+        $this->mapping[SYNC_POOMCAL_STARTTIME][self::STREAMER_RONOTIFY] = true;
39
+        $this->mapping[SYNC_POOMCAL_SUBJECT][self::STREAMER_CHECKS] = [];
40
+        $this->mapping[SYNC_POOMCAL_SUBJECT][self::STREAMER_RONOTIFY] = true;
41
+        $this->mapping[SYNC_POOMCAL_ENDTIME][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMCAL_STARTTIME];
42
+        $this->mapping[SYNC_POOMCAL_ENDTIME][self::STREAMER_RONOTIFY] = true;
43
+        $this->mapping[SYNC_POOMCAL_BUSYSTATUS][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_ONEVALUEOF => [0, 1, 2, 3, 4]];
44
+        $this->mapping[SYNC_POOMCAL_BUSYSTATUS][self::STREAMER_RONOTIFY] = true;
45
+        $this->mapping[SYNC_POOMCAL_REMINDER][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_CMPHIGHER => -1];
46
+        $this->mapping[SYNC_POOMCAL_REMINDER][self::STREAMER_RONOTIFY] = true;
47
+        $this->mapping[SYNC_POOMCAL_EXCEPTIONS][self::STREAMER_CHECKS] = [self::STREAMER_CHECK_NOTALLOWED => true];
48 48
 
49
-		// Indicates that this SyncObject supports the private flag and stripping of private data.
50
-		// It behaves as a SyncAppointment.
51
-		$this->supportsPrivateStripping = true;
52
-	}
49
+        // Indicates that this SyncObject supports the private flag and stripping of private data.
50
+        // It behaves as a SyncAppointment.
51
+        $this->supportsPrivateStripping = true;
52
+    }
53 53
 }
Please login to merge, or discard this patch.
lib/syncobjects/syncdeviceinformation.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -10,40 +10,40 @@
 block discarded – undo
10 10
  */
11 11
 
12 12
 class SyncDeviceInformation extends SyncObject {
13
-	public $model;
14
-	public $imei;
15
-	public $friendlyname;
16
-	public $os;
17
-	public $oslanguage;
18
-	public $phonenumber;
19
-	public $useragent; // 12.1 &14.0
20
-	public $mobileoperator; // 14.0
21
-	public $enableoutboundsms; // 14.0
22
-	public $Status;
13
+    public $model;
14
+    public $imei;
15
+    public $friendlyname;
16
+    public $os;
17
+    public $oslanguage;
18
+    public $phonenumber;
19
+    public $useragent; // 12.1 &14.0
20
+    public $mobileoperator; // 14.0
21
+    public $enableoutboundsms; // 14.0
22
+    public $Status;
23 23
 
24
-	public function __construct() {
25
-		$mapping = [
26
-			SYNC_SETTINGS_MODEL => [self::STREAMER_VAR => "model"],
27
-			SYNC_SETTINGS_IMEI => [self::STREAMER_VAR => "imei"],
28
-			SYNC_SETTINGS_FRIENDLYNAME => [self::STREAMER_VAR => "friendlyname"],
29
-			SYNC_SETTINGS_OS => [self::STREAMER_VAR => "os"],
30
-			SYNC_SETTINGS_OSLANGUAGE => [self::STREAMER_VAR => "oslanguage"],
31
-			SYNC_SETTINGS_PHONENUMBER => [self::STREAMER_VAR => "phonenumber"],
32
-			SYNC_SETTINGS_PROP_STATUS => [
33
-				self::STREAMER_VAR => "Status",
34
-				self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE,
35
-			],
36
-		];
24
+    public function __construct() {
25
+        $mapping = [
26
+            SYNC_SETTINGS_MODEL => [self::STREAMER_VAR => "model"],
27
+            SYNC_SETTINGS_IMEI => [self::STREAMER_VAR => "imei"],
28
+            SYNC_SETTINGS_FRIENDLYNAME => [self::STREAMER_VAR => "friendlyname"],
29
+            SYNC_SETTINGS_OS => [self::STREAMER_VAR => "os"],
30
+            SYNC_SETTINGS_OSLANGUAGE => [self::STREAMER_VAR => "oslanguage"],
31
+            SYNC_SETTINGS_PHONENUMBER => [self::STREAMER_VAR => "phonenumber"],
32
+            SYNC_SETTINGS_PROP_STATUS => [
33
+                self::STREAMER_VAR => "Status",
34
+                self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE,
35
+            ],
36
+        ];
37 37
 
38
-		if (Request::GetProtocolVersion() >= 12.1) {
39
-			$mapping[SYNC_SETTINGS_USERAGENT] = [self::STREAMER_VAR => "useragent"];
40
-		}
38
+        if (Request::GetProtocolVersion() >= 12.1) {
39
+            $mapping[SYNC_SETTINGS_USERAGENT] = [self::STREAMER_VAR => "useragent"];
40
+        }
41 41
 
42
-		if (Request::GetProtocolVersion() >= 14.0) {
43
-			$mapping[SYNC_SETTINGS_MOBILEOPERATOR] = [self::STREAMER_VAR => "mobileoperator"];
44
-			$mapping[SYNC_SETTINGS_ENABLEOUTBOUNDSMS] = [self::STREAMER_VAR => "enableoutboundsms"];
45
-		}
42
+        if (Request::GetProtocolVersion() >= 14.0) {
43
+            $mapping[SYNC_SETTINGS_MOBILEOPERATOR] = [self::STREAMER_VAR => "mobileoperator"];
44
+            $mapping[SYNC_SETTINGS_ENABLEOUTBOUNDSMS] = [self::STREAMER_VAR => "enableoutboundsms"];
45
+        }
46 46
 
47
-		parent::__construct($mapping);
48
-	}
47
+        parent::__construct($mapping);
48
+    }
49 49
 }
Please login to merge, or discard this patch.
lib/syncobjects/syncbaseattachment.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -10,25 +10,25 @@
 block discarded – undo
10 10
  */
11 11
 
12 12
 class SyncBaseAttachment extends SyncObject {
13
-	public $displayname;
14
-	public $filereference;
15
-	public $method;
16
-	public $estimatedDataSize;
17
-	public $contentid;
18
-	public $contentlocation;
19
-	public $isinline;
13
+    public $displayname;
14
+    public $filereference;
15
+    public $method;
16
+    public $estimatedDataSize;
17
+    public $contentid;
18
+    public $contentlocation;
19
+    public $isinline;
20 20
 
21
-	public function __construct() {
22
-		$mapping = [
23
-			SYNC_AIRSYNCBASE_DISPLAYNAME => [self::STREAMER_VAR => "displayname"],
24
-			SYNC_AIRSYNCBASE_FILEREFERENCE => [self::STREAMER_VAR => "filereference"],
25
-			SYNC_AIRSYNCBASE_METHOD => [self::STREAMER_VAR => "method"],
26
-			SYNC_AIRSYNCBASE_ESTIMATEDDATASIZE => [self::STREAMER_VAR => "estimatedDataSize"],
27
-			SYNC_AIRSYNCBASE_CONTENTID => [self::STREAMER_VAR => "contentid"],
28
-			SYNC_AIRSYNCBASE_CONTENTLOCATION => [self::STREAMER_VAR => "contentlocation"],
29
-			SYNC_AIRSYNCBASE_ISINLINE => [self::STREAMER_VAR => "isinline"],
30
-		];
21
+    public function __construct() {
22
+        $mapping = [
23
+            SYNC_AIRSYNCBASE_DISPLAYNAME => [self::STREAMER_VAR => "displayname"],
24
+            SYNC_AIRSYNCBASE_FILEREFERENCE => [self::STREAMER_VAR => "filereference"],
25
+            SYNC_AIRSYNCBASE_METHOD => [self::STREAMER_VAR => "method"],
26
+            SYNC_AIRSYNCBASE_ESTIMATEDDATASIZE => [self::STREAMER_VAR => "estimatedDataSize"],
27
+            SYNC_AIRSYNCBASE_CONTENTID => [self::STREAMER_VAR => "contentid"],
28
+            SYNC_AIRSYNCBASE_CONTENTLOCATION => [self::STREAMER_VAR => "contentlocation"],
29
+            SYNC_AIRSYNCBASE_ISINLINE => [self::STREAMER_VAR => "isinline"],
30
+        ];
31 31
 
32
-		parent::__construct($mapping);
33
-	}
32
+        parent::__construct($mapping);
33
+    }
34 34
 }
Please login to merge, or discard this patch.