@@ -8,127 +8,127 @@ |
||
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 | } |
@@ -24,8 +24,7 @@ discard block |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 | } |
@@ -8,103 +8,103 @@ |
||
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 | } |
@@ -81,8 +81,7 @@ |
||
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 |
@@ -8,1673 +8,1673 @@ |
||
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 | } |
@@ -206,7 +206,7 @@ discard block |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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"])) { |
@@ -41,8 +41,7 @@ discard block |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 | } |
@@ -8,211 +8,211 @@ |
||
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 | } |
@@ -52,7 +52,7 @@ discard block |
||
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 |
||
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 |
||
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 |
||
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); |
@@ -102,8 +102,7 @@ discard block |
||
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 |
||
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 |
||
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), |
@@ -8,115 +8,115 @@ |
||
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 | } |
@@ -27,13 +27,13 @@ discard block |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 | } |
@@ -28,8 +28,7 @@ discard block |
||
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 |
||
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 | } |
@@ -8,318 +8,318 @@ |
||
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 | } |
@@ -286,10 +286,10 @@ |
||
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]"; |
@@ -232,8 +232,7 @@ discard block |
||
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 |
||
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 |
@@ -10,44 +10,44 @@ |
||
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 | } |
@@ -10,40 +10,40 @@ |
||
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 | } |
@@ -10,25 +10,25 @@ |
||
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 | } |