@@ -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 | } |