diff -urN ../z-push-2.6.0.alpha1-210/src/backend/caldav/caldav.php ./src/backend/caldav/caldav.php
--- ../z-push-2.6.0.alpha1-210/src/backend/caldav/caldav.php 2023-03-17 22:44:44.870027000 +0200
+++ ./src/backend/caldav/caldav.php 2023-03-17 22:49:29.305359000 +0200
@@ -125,7 +125,6 @@
$folders = array();
$calendars = $this->_caldav->FindCalendars();
foreach ($calendars as $val) {
- $folder = array();
$fpath = explode("/", $val->url, -1);
if (is_array($fpath)) {
$folderid = array_pop($fpath);
@@ -216,7 +215,7 @@
$msgs = $this->_caldav->GetEventsList($begin, $finish, $path);
}
else {
- $msgs = $this->_caldav->GetTodosList($begin, $finish, false, false, $path);
+ $msgs = $this->_caldav->GetTodosList(null, null, null, null, $path);
}
$messages = array();
@@ -335,11 +334,51 @@
}
/**
- * Move a message is not supported by CalDAV.
+ * Move a message by fetching it, trying to create a copy
+ * into another collection and deleting the original.
* @see BackendDiff::MoveMessage()
*/
public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) {
- return false;
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->MoveMessage('%s','%s','%s')", $folderid, $id, $newfolderid));
+
+ if ($folderid == $newfolderid) {
+ throw new StatusException(sprintf("BackendCalDAV->MoveMessage('%s','%s','%s'): Error, destination folder is source folder. Canceling the move.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST);
+ }
+
+ // get source message
+ $path = $this->_caldav_path . substr($folderid, 1) . "/";
+ $href = $path . $id;
+
+ $messages = $this->_caldav->CalendarMultiget( array( $href ), $path );
+ $data = $messages[$href];
+
+ if (!isset($data) || $data == '') {
+ throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to retrieve source message.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
+ }
+ unset($messages, $href);
+
+ // create copy in destination folder with new id
+ $newId = sprintf("%s-%s.ics", gmdate("Ymd\THis\Z"), hash("md5", microtime()));
+ $path = sprintf("%s%s/%s", $this->_caldav_path, substr($newfolderid, 1), $newId);
+ $etag = "*";
+
+ $etag_new = $this->_caldav->DoPUTRequest($path, $data, $etag);
+ unset($data);
+
+ if ($etag_new != null) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->MoveMessage(): Created message copy '%s' in destination folder.", $newId));
+
+ if($this->DeleteMessage($folderid, $id, $contentParameters)) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->MoveMessage(): Deleted source message '%s', MoveMessage() complete.", $id));
+ return $newId;
+ }
+ else {
+ throw new StatusException(sprintf("BackendCalDAV->MoveMessage('%s','%s','%s'): Error, deletion of source message failed, duplicates may exist.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED);
+ }
+ }
+ else {
+ throw new StatusException(sprintf("BackendCalDAV->MoveMessage('%s','%s','%s'): Error, copy to destination folder failed.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
+ }
}
/**
@@ -484,14 +523,12 @@
if (CALDAV_SUPPORTS_SYNC) {
if (count($response) > 0) {
$changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
}
}
else {
// If the numbers of events are different, we know for sure, there are changes
if (count($response) != count($v)) {
$changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
}
else {
// If the numbers of events are equals, we compare the biggest date
@@ -510,14 +547,11 @@
$changed = true;
}
}
-
- if ($changed) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
- }
}
}
if ($changed) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
$notifications[] = $k;
}
}
@@ -601,9 +635,14 @@
* @param int $truncsize
*/
private function _ParseVEventToSyncObject($event, $message, $truncsize) {
- //Defaults
- $message->busystatus = "2";
+ // get user details
+ $userDetails = ZPush::GetBackend()->GetCurrentUsername();
+
+ // track vevent involvement
+ $userIsAttendee = false;
+ $userIsOrganizer = false;
+
$properties = $event->GetProperties();
foreach ($properties as $property) {
switch ($property->Name()) {
@@ -624,12 +663,21 @@
break;
case "ORGANIZER":
- $org_mail = str_ireplace("MAILTO:", "", $property->Value());
- $message->organizeremail = $org_mail;
- $org_cn = $property->GetParameterValue("CN");
- if ($org_cn) {
- $message->organizername = $org_cn;
+ $organizerEMail = str_ireplace("MAILTO:", "", $property->Value());
+ if ($organizerEMail) {
+ $message->organizeremail = $organizerEMail;
+ if (strcasecmp($organizerEMail, $userDetails['emailaddress']) == 0) {
+ $userIsOrganizer = true;
+ }
}
+
+ $organizerName = $property->GetParameterValue("CN");
+ if ($organizerName) {
+ $message->organizername = $organizerName;
+ if (strcasecmp($organizerName, $userDetails['fullname']) == 0) {
+ $userIsOrganizer = true;
+ }
+ }
break;
case "LOCATION":
@@ -680,6 +728,7 @@
}
break;
+ case "X-MICROSOFT-CDO-BUSYSTATUS":
case "X-MICROSOFT-CDO-INTENDEDSTATUS":
switch ($property->Value()) {
case "FREE":
@@ -714,25 +763,73 @@
case "STATUS":
switch ($property->Value()) {
case "TENTATIVE":
- $message->meetingstatus = "3"; // was 1
- break;
case "CONFIRMED":
- $message->meetingstatus = "1"; // was 3
+ $message->meetingstatus = "1";
break;
case "CANCELLED":
- $message->meetingstatus = "5"; // could also be 7
+ $message->meetingstatus = "5";
break;
}
break;
case "ATTENDEE":
$attendee = new SyncAttendee();
- $att_email = str_ireplace("MAILTO:", "", $property->Value());
- $attendee->email = $att_email;
- $att_cn = $property->GetParameterValue("CN");
- if ($att_cn) {
- $attendee->name = $att_cn;
+
+ $attendeeEMail = str_ireplace("MAILTO:", "", $property->Value());
+ if ($attendeeEMail) {
+ $attendee->email = $attendeeEMail;
+ if (strcasecmp($attendeeEMail, $userDetails['emailaddress']) == 0) {
+ $userIsAttendee = true;
+ }
}
+
+ $attendeeName = $property->GetParameterValue("CN");
+ if ($attendeeName) {
+ $attendee->name = $attendeeName;
+ if (strcasecmp($attendeeName, $userDetails['fullname']) == 0) {
+ $userIsAttendee = true;
+ }
+ }
+
+ if (Request::GetProtocolVersion() >= 12.0) {
+ // attendeestatus: 0 = response unknown, 2 = tentative, 3 = accept, 4 = decline, 5 = not responded
+ $attendeeStatus = $property->GetParameterValue("PARTSTAT");
+ if ($attendeeStatus) {
+ switch ($attendeeStatus) {
+ case "TENTATIVE":
+ $attendee->attendeestatus = "2";
+ break;
+ case "ACCEPTED":
+ $attendee->attendeestatus = "3";
+ break;
+ case "DECLINED":
+ $attendee->attendeestatus = "4";
+ break;
+ case "NEEDS-ACTION":
+ $attendee->attendeestatus = "5";
+ break;
+ }
+ }
+
+ // attendeetype: 1 = required, 2 = optional, 3 = resource
+ $attendeeType = $property->GetParameterValue("ROLE");
+ if ($attendeeType) {
+ switch ($attendeeType) {
+ case "REQ-PARTICIPANT":
+ $attendee->attendeetype = "1";
+ break;
+ case "OPT-PARTICIPANT":
+ $attendee->attendeetype = "2";
+ break;
+ case "NON-PARTICIPANT":
+ $attendee->attendeetype = "3";
+ break;
+ case "CHAIR":
+ break;
+ }
+ }
+ }
+
if (isset($message->attendees) && is_array($message->attendees)) {
$message->attendees[] = $attendee;
}
@@ -746,7 +843,7 @@
$message->asbody = new SyncBaseBody();
// the DESCRIPTION component is specified to be plain text (RFC5545), for HTML use X-ALT-DESC
- $data = str_replace("\n","\r\n", str_replace("\r","",Utils::ConvertHtmlToText($property->Value())));
+ $data = str_replace("\n", "\r\n", str_replace("\r", "", $property->Value()));
// truncate body, if requested
if (strlen($data) > $truncsize) {
@@ -779,22 +876,46 @@
break;
case "CATEGORIES":
- $categories = explode(",", $property->Value());
- $message->categories = $categories;
+ $categories = $this->explodeUnescapedDelimiter(",", $property->Value());
+ if (!isset($message->categories)) {
+ $message->categories = $this->unescape($categories);
+ }
+ else {
+ foreach($categories as $category) {
+ $message->categories[] = $this->unescape($category);
+ }
+ }
break;
case "EXDATE":
- $exception = new SyncAppointmentException();
- $exception->deleted = "1";
- $exception->exceptionstarttime = TimezoneUtil::MakeUTCDate($property->Value());
+ $exceptionDateTimes = explode(",", $property->Value());
if (!isset($message->exceptions)) {
$message->exceptions = array();
}
- $message->exceptions[] = $exception;
+ foreach($exceptionDateTimes as $exceptionDateTime) {
+ $exception = new SyncAppointmentException();
+ $exception->deleted = "1";
+ $exception->exceptionstarttime = TimezoneUtil::MakeUTCDate($exceptionDateTime);
+ $message->exceptions[] = $exception;
+ }
break;
+ case "X-MICROSOFT-DISALLOW-COUNTER":
+ if (Request::GetProtocolVersion() >= 14.0) {
+ switch ($property->Value()) {
+ case "TRUE":
+ $message->disallownewtimeproposal = true;
+ break;
+ case "FALSE":
+ $message->disallownewtimeproposal = false;
+ break;
+ }
+ }
+ break;
+
//We can ignore the following
case "PRIORITY":
+ case "X-MICROSOFT-CDO-IMPORTANCE":
case "SEQUENCE":
case "CREATED":
case "DTSTAMP":
@@ -809,14 +930,33 @@
}
}
- if ($message->meetingstatus > 0) {
- // No organizer was set for the meeting, assume it is the user
+ // default busystatus
+ if (!isset($message->busystatus)) {
+ $message->busystatus = "2";
+ }
+
+ // meeting without attendee is an appointment
+ if (!isset($message->attendees)) {
+ $message->meetingstatus = 0;
+ }
+
+ // meeting with attendee
+ if ($message->meetingstatus > 0 || !isset($message->meetingstatus)) {
+ // set meetingstatus if not already set
+ $message->meetingstatus = isset($message->meetingstatus) ? $message->meetingstatus : 1;
+
+ // check if meeting has an organizer set, otherwise fallback to current user
if (!isset($message->organizeremail)) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): No organizeremail defined, using user details"));
- $userDetails = ZPush::GetBackend()->GetCurrentUsername();
$message->organizeremail = $userDetails['emailaddress'];
$message->organizername = $userDetails['fullname'];
}
+ // check if user is not organizer but attendee to detect received meeting
+ elseif ($userIsAttendee && !$userIsOrganizer) {
+ // apply received flag
+ $message->meetingstatus |= 0x2;
+ }
+
// Ensure the organizer name is set
if (!isset($message->organizername)) {
$message->organizername = Utils::GetLocalPartFromEmail($message->organizeremail);
@@ -833,12 +973,15 @@
$trigger = date_create("@" . TimezoneUtil::MakeUTCDate($property->Value()));
$begin = date_create("@" . $message->starttime);
$interval = date_diff($begin, $trigger);
- $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24;
+ $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + intval($interval->format("%a")) * 60 * 24;
}
elseif (!array_key_exists("VALUE", $parameters) || $parameters["VALUE"] == "DURATION") {
- $val = str_replace("-", "", $property->Value());
- $interval = new DateInterval($val);
- $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24;
+ $val = $property->Value();
+ // only use negative values, because reminder = minutes before calendar item start
+ if ($val[0] == '-') {
+ $interval = new DateInterval(substr($val, 1));
+ $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%d") * 60 * 24;
+ }
}
}
}
@@ -1010,39 +1153,73 @@
$vevent->AddProperty("UID", $id);
$ical->AddComponent($vevent);
if (isset($data->exceptions) && is_array($data->exceptions)) {
- foreach ($data->exceptions as $ex) {
- if (isset($ex->deleted) && $ex->deleted == "1") {
+ foreach ($data->exceptions as $exception) {
+ if (isset($exception->deleted) && $exception->deleted == "1") {
if ($exdate = $vevent->GetPValue("EXDATE")) {
if ($data->alldayevent == 1) {
- $vevent->SetPValue("EXDATE", $exdate.",".$this->_GetDateFromUTC("Ymd", $ex->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
+ $vevent->SetPValue("EXDATE", $exdate.",".$this->_GetDateFromUTC("Ymd", $exception->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
}
else {
- $vevent->SetPValue("EXDATE", $exdate.",".$this->DAVDateTimeInTimezone($ex->exceptionstarttime, $tzid), $tzpar);
+ $vevent->SetPValue("EXDATE", $exdate.",".$this->DAVDateTimeInTimezone($exception->exceptionstarttime, $tzid), $tzpar);
}
}
else {
if ($data->alldayevent == 1) {
- $vevent->AddProperty("EXDATE", $this->_GetDateFromUTC("Ymd", $ex->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
+ $vevent->AddProperty("EXDATE", $this->_GetDateFromUTC("Ymd", $exception->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
}
else {
- $vevent->AddProperty("EXDATE", $this->DAVDateTimeInTimezone($ex->exceptionstarttime, $tzid), $tzpar);
+ $vevent->AddProperty("EXDATE", $this->DAVDateTimeInTimezone($exception->exceptionstarttime, $tzid), $tzpar);
}
}
continue;
}
- $exception = $this->_ParseASEventToVEvent($ex, $id, $tzid, $tzpar);
+ // If an element is not specified in the exception element, the element inherits from the top-level element.
+ if (isset($data->busystatus) && !isset($exception->busystatus)) {
+ $exception->busystatus = $data->busystatus;
+ }
+ if (isset($data->meetingstatus) && !isset($exception->meetingstatus)) {
+ $exception->meetingstatus = $data->meetingstatus;
+ }
+ if (isset($data->dtstamp) && !isset($exception->dtstamp)) {
+ $exception->dtstamp = $data->dtstamp;
+ }
+ if (isset($data->starttime) && !isset($exception->starttime)) {
+ $exception->starttime = $data->starttime;
+ }
+ if (isset($data->endtime) && !isset($exception->endtime)) {
+ $exception->endtime = $data->endtime;
+ }
+ if (isset($data->location) && !isset($exception->location)) {
+ $exception->location = $data->location;
+ }
+ if (isset($data->subject) && !isset($exception->subject)) {
+ $exception->subject = $data->subject;
+ }
+ if (isset($data->sensitivity) && !isset($exception->sensitivity)) {
+ $exception->sensitivity = $data->sensitivity;
+ }
+ if (isset($data->reminder) && !isset($exception->reminder)) {
+ $exception->reminder = $data->reminder;
+ }
+ if (isset($data->attendees) && !isset($exception->attendees)) {
+ $exception->attendees = $data->attendees;
+ }
+
+ $exceptionVEvent = $this->_ParseASEventToVEvent($exception, $id, $tzid, $tzpar);
+
if ($data->alldayevent == 1) {
- $exception->AddProperty("RECURRENCE-ID", $this->_GetDateFromUTC("Ymd", $ex->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
+ $exceptionVEvent->AddProperty("RECURRENCE-ID", $this->_GetDateFromUTC("Ymd", $exception->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
}
else {
- $exception->AddProperty("RECURRENCE-ID", $this->DAVDateTimeInTimezone($ex->exceptionstarttime, $tzid), $tzpar);
+ $exceptionVEvent->AddProperty("RECURRENCE-ID", $this->DAVDateTimeInTimezone($exception->exceptionstarttime, $tzid), $tzpar);
}
- $exception->AddProperty("UID", $id);
- $ical->AddComponent($exception);
+ $exceptionVEvent->AddProperty("UID", $id);
+ $ical->AddComponent($exceptionVEvent);
}
}
}
+
if ($folderid[0] == "T") {
$vtodo = $this->_ParseASTaskToVTodo($data, $id);
$vtodo->AddProperty("UID", $id);
@@ -1112,30 +1289,6 @@
break;
}
}
- if (isset($data->busystatus)) {
- switch ($data->busystatus) {
- case "0": //Free
- $vevent->AddProperty("TRANSP", "TRANSPARENT");
- $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "FREE");
- break;
- case "1": //Tentative
- $vevent->AddProperty("TRANSP", "OPAQUE");
- $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "TENTATIVE");
- break;
- case "2": //Busy
- $vevent->AddProperty("TRANSP", "OPAQUE");
- $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "BUSY");
- break;
- case "3": //Out of office
- $vevent->AddProperty("TRANSP", "TRANSPARENT");
- $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "OOF");
- break;
- case "4": //Working elsewhere (not yet in Android)
- $vevent->AddProperty("TRANSP", "TRANSPARENT");
- $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "WORKINGELSEWHERE");
- break;
- }
- }
if (isset($data->reminder)) {
$valarm = new iCalComponent();
$valarm->SetType("VALARM");
@@ -1151,25 +1304,75 @@
$rtfparser->parse();
$vevent->AddProperty("DESCRIPTION", $rtfparser->out);
}
- if (isset($data->meetingstatus) && $data->meetingstatus > 0) {
- switch ($data->meetingstatus) {
- case "1":
- $vevent->AddProperty("STATUS", "TENTATIVE");
+ if (isset($data->busystatus) && ((isset($data->meetingstatus) && $data->meetingstatus == 0) || !isset($data->meetingstatus))) {
+ switch ($data->busystatus) {
+ case "0": //Free
+ $vevent->AddProperty("TRANSP", "TRANSPARENT");
+ $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "FREE");
+ break;
+ case "1": //Tentative
+ $vevent->AddProperty("TRANSP", "OPAQUE");
$vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "TENTATIVE");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
break;
- case "3":
- $vevent->AddProperty("STATUS", "CONFIRMED");
- $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CONFIRMED");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
+ case "2": //Busy
+ $vevent->AddProperty("TRANSP", "OPAQUE");
+ $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "BUSY");
break;
- case "5":
- case "7":
- $vevent->AddProperty("STATUS", "CANCELLED");
- $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CANCELLED");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE");
+ case "3": //Out of office
+ $vevent->AddProperty("TRANSP", "OPAQUE");
+ $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "OOF");
break;
+ case "4": //Working elsewhere (not yet in Android) but not defined in [MS-OXCICAL]
+ $vevent->AddProperty("TRANSP", "TRANSPARENT");
+ $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "WORKINGELSEWHERE");
+ break;
}
+ }
+ elseif (isset($data->meetingstatus) && $data->meetingstatus > 0) {
+ if (isset($data->busystatus)) {
+ switch ($data->busystatus) {
+ case "0": //Free
+ $vevent->AddProperty("TRANSP", "TRANSPARENT");
+ $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "FREE");
+ break;
+ case "1": //Tentative
+ $vevent->AddProperty("TRANSP", "OPAQUE");
+ $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "TENTATIVE");
+ break;
+ case "2": //Busy
+ $vevent->AddProperty("TRANSP", "OPAQUE");
+ $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "BUSY");
+ break;
+ case "3": //Out of office
+ $vevent->AddProperty("TRANSP", "OPAQUE");
+ $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "OOF");
+ break;
+ case "4": //Working elsewhere (not yet in Android) but not defined in [MS-OXCICAL]
+ $vevent->AddProperty("TRANSP", "TRANSPARENT");
+ $vevent->AddProperty("X-MICROSOFT-CDO-INTENDEDSTATUS", "WORKINGELSEWHERE");
+ break;
+ }
+ }
+ if (isset($data->disallownewtimeproposal) && $data->disallownewtimeproposal == true) {
+ $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE");
+ }
+ else {
+ $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
+ }
+ if ($data->meetingstatus == 1 || $data->meetingstatus == 3) {
+ if (isset($data->busystatus)) {
+ if ($data->busystatus == 1) {
+ $vevent->AddProperty("STATUS", "TENTATIVE");
+ }
+ elseif ($data->busystatus == 2 || $data->busystatus == 3) {
+ $vevent->AddProperty("STATUS", "CONFIRMED");
+ }
+ }
+ }
+ elseif ($data->meetingstatus == 5 || $data->meetingstatus == 7) {
+ $vevent->AddProperty("STATUS", "CANCELLED");
+ $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE");
+ }
if (isset($data->organizeremail) && isset($data->organizername)) {
$vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $data->organizeremail), array("CN" => $data->organizername));
}
@@ -1182,12 +1385,48 @@
$vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $userDetails['emailaddress']), array("CN" => $userDetails['fullname']));
}
if (isset($data->attendees) && is_array($data->attendees)) {
- foreach ($data->attendees as $att) {
- if (isset($att->name)) {
- $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email), array("CN" => $att->name));
+ foreach ($data->attendees as $attendee) {
+ $attendeeParameters = array();
+ if (isset($attendee->name)) {
+ $attendeeParameters += array("CN" => $attendee->name);
}
+ if (isset($attendee->attendeetype)) {
+ switch($attendee->attendeetype) {
+ case "1":
+ $attendeeParameters += array("ROLE" => "REQ-PARTICIPANT");
+ break;
+ case "2":
+ $attendeeParameters += array("ROLE" => "OPT-PARTICIPANT");
+ break;
+ case "3":
+ $attendeeParameters += array("ROLE" => "NON-PARTICIPANT");
+ break;
+ }
+ }
+ if (isset($attendee->attendeestatus)) {
+ switch($attendee->attendeestatus) {
+ case "0":
+ $attendeeParameters += array("PARTSTAT" => "NEEDS-ACTION");
+ break;
+ case "2":
+ $attendeeParameters += array("PARTSTAT" => "TENTATIVE");
+ break;
+ case "3":
+ $attendeeParameters += array("PARTSTAT" => "ACCEPTED");
+ break;
+ case "4":
+ $attendeeParameters += array("PARTSTAT" => "DECLINED");
+ break;
+ case "5":
+ $attendeeParameters += array("PARTSTAT" => "NEEDS-ACTION");
+ break;
+ }
+ }
+ if (!empty($attendeeParameters)) {
+ $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $attendee->email), $attendeeParameters);
+ }
else {
- $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email));
+ $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $attendee->email));
}
}
}
@@ -1202,13 +1441,13 @@
}
}
if (isset($data->categories) && is_array($data->categories)) {
- $vevent->AddProperty("CATEGORIES", implode(",", $data->categories));
+ $vevent->AddProperty("CATEGORIES", implode(",", $this->escape($data->categories)));
}
-// X-MICROSOFT-CDO-APPT-SEQUENCE:0
-// X-MICROSOFT-CDO-OWNERAPPTID:2113393086
-// X-MICROSOFT-CDO-IMPORTANCE:1
-// X-MICROSOFT-CDO-INSTTYPE:0
+ // X-MICROSOFT-CDO-APPT-SEQUENCE:0
+ // X-MICROSOFT-CDO-OWNERAPPTID:2113393086
+ // X-MICROSOFT-CDO-IMPORTANCE:1
+ // X-MICROSOFT-CDO-INSTTYPE:0
return $vevent;
@@ -1389,12 +1628,18 @@
case "PRIORITY":
$priority = $property->Value();
- if ($priority <= 3)
- $message->importance = "0";
- if ($priority <= 6)
+ // MS-ASTASK: 0 = low, 1 = normal (default), 2 = high
+ // RFC5545: 0 = undefined, 1-4 = high, 5 = normal, 6-9 = low
+ // or 0 = undefined, 1-3 = high, 4-6 = normal, 7-9 = low
+ if ($priority == 0 || $priority == 5) {
$message->importance = "1";
- if ($priority > 6)
+ }
+ elseif ($priority > 0 && $priority < 5) {
$message->importance = "2";
+ }
+ elseif ($priority > 5 && $priority <= 9) {
+ $message->importance = "0";
+ }
break;
case "RRULE":
@@ -1419,13 +1664,53 @@
$message->utcstartdate = TimezoneUtil::MakeUTCDate($property->Value());
break;
- case "SUMMARY":
- $message->subject = $property->Value();
+ case "DESCRIPTION":
+ if (Request::GetProtocolVersion() >= 12.0) {
+ $message->asbody = new SyncBaseBody();
+
+ // the DESCRIPTION component is specified to be plain text (RFC5545), for HTML use X-ALT-DESC
+ $data = str_replace("\n", "\r\n", str_replace("\r", "", $property->Value()));
+
+ // truncate body, if requested
+ if (strlen($data) > $truncsize) {
+ $message->asbody->truncated = 1;
+ $data = Utils::Utf8_truncate($data, $truncsize);
+ }
+ else {
+ $message->asbody->truncated = 0;
+ }
+ $message->asbody->data = StringStreamWrapper::Open($data);
+ $message->asbody->estimatedDataSize = strlen($data);
+ unset($data);
+
+ // set body type accordingly
+ $message->asbody->type = SYNC_BODYPREFERENCE_PLAIN;
+ $message->nativebodytype = SYNC_BODYPREFERENCE_PLAIN;
+ }
+ else {
+ $body = $property->Value();
+ // truncate body, if requested
+ if(strlen($body) > $truncsize) {
+ $body = Utils::Utf8_truncate($body, $truncsize);
+ $message->bodytruncated = 1;
+ } else {
+ $message->bodytruncated = 0;
+ }
+ $body = str_replace("\n","\r\n", str_replace("\r","",$body));
+ $message->body = $body;
+ }
break;
case "CATEGORIES":
- $categories = explode(",", $property->Value());
- $message->categories = $categories;
+ $categories = $this->explodeUnescapedDelimiter(",", $property->Value());
+ if (!isset($message->categories)) {
+ $message->categories = $this->unescape($categories);
+ }
+ else {
+ foreach($categories as $category) {
+ $message->categories[] = $this->unescape($category);
+ }
+ }
break;
}
}
@@ -1497,16 +1782,21 @@
$vtodo->AddProperty("DUE", gmdate("Ymd\THis\Z", $data->utcduedate));
}
if (isset($data->importance)) {
- if ($data->importance == "1") {
- $vtodo->AddProperty("PRIORITY", 6);
+ switch ($data->importance) {
+ case "0":
+ $vtodo->AddProperty("PRIORITY", 9);
+ break;
+ case "1":
+ $vtodo->AddProperty("PRIORITY", 5);
+ break;
+ case "2":
+ $vtodo->AddProperty("PRIORITY", 1);
+ break;
}
- elseif ($data->importance == "2") {
- $vtodo->AddProperty("PRIORITY", 9);
- }
- else {
- $vtodo->AddProperty("PRIORITY", 1);
- }
}
+ else {
+ $vtodo->AddProperty("PRIORITY", 0);
+ }
if (isset($data->recurrence)) {
$vtodo->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence, false));
}
@@ -1546,7 +1836,7 @@
$vtodo->AddProperty("DESCRIPTION", $rtfparser->out);
}
if (isset($data->categories) && is_array($data->categories)) {
- $vtodo->AddProperty("CATEGORIES", implode(",", $data->categories));
+ $vtodo->AddProperty("CATEGORIES", implode(",", $this->escape($data->categories)));
}
return $vtodo;
@@ -1838,6 +2128,76 @@
$offset = abs($phpoffset);
$hours = floor($offset / 3600);
return sprintf("$prefix%'.02d%'.02d", $hours, ($offset - ($hours * 3600)) / 60);
+ }
+
+ /**
+ * Escape string according to RFC5545 (3.3.11. TEXT)
+ * @param string|array $data string or array of strings to be escaped
+ * @access private
+ * @return string|array
+ */
+ private function escape($data) {
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ $data[$key] = $this->escape($value);
+ }
+ return $data;
+ }
+
+ $data = str_replace(array('\\', ';', ',', "\n", "\N", "\r"), array('\\\\', '\\;', '\\,', '\\n', '\\n'), $data);
+ return $data;
+ }
+
+ /**
+ * Un-escape string according to RFC5545 (3.3.11. TEXT)
+ * @param string $data string to be un-escaped
+ * @access private
+ * @return string
+ */
+ private function unescape($data) {
+ $data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
+ return $data;
+ }
+
+ /**
+ * Explode string only on unescaped delimiter, this function does not regard quoted parts
+ * @param string $delimiter delimiter
+ * @param string $data string to be exploded
+ * @access private
+ * @return array
+ */
+ private function explodeUnescapedDelimiter($delimiter, $data, $escapeCharacter = '\\') {
+ $length = strlen($data);
+ $escaped = false;
+ $result = array();
+ $temp = '';
+
+ for ($position = 0; $position < $length; $position++) {
+ // use boolean switching to detect escaped delimiters
+ if ($data[$position] == $escapeCharacter) {
+ $escaped = !$escaped;
+ }
+ elseif ($escaped == false && $data[$position] == $delimiter) {
+ // only add non empty strings to result
+ if (strlen($temp) > 0) {
+ $result[] = $temp;
+ }
+ $temp = '';
+ continue;
+ }
+ else {
+ $escaped = false;
+ }
+
+ $temp .= $data[$position];
+ }
+
+ //append last part if available
+ if (strlen($temp) > 0) {
+ $result[] = $temp;
+ }
+
+ return $result;
}
};
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/combined/combined.php ./src/backend/combined/combined.php
--- ../z-push-2.6.0.alpha1-210/src/backend/combined/combined.php 2023-03-17 22:44:44.871492000 +0200
+++ ./src/backend/combined/combined.php 2023-03-17 22:49:29.306420000 +0200
@@ -438,6 +438,7 @@
* @access public
* @return array
*/
+
public function ChangesSink($timeout = 30) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink(%d)", $timeout));
@@ -445,17 +446,25 @@
if ($this->numberChangesSink == 0) {
ZLog::Write(LOGLEVEL_DEBUG, "BackendCombined doesn't include any Sinkable backends");
} else {
- $time_each = $timeout / $this->numberChangesSink;
+ $stopat = time() + $timeout - 1;
+
foreach ($this->backends as $i => $b) {
if ($this->backends[$i]->HasChangesSink()) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink - Calling in '%s' with %d", get_class($b), $time_each));
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink - Calling in '%s'", get_class($b)));
- $notifications_backend = $this->backends[$i]->ChangesSink($time_each);
- //preppend backend delimiter
+ $notifications_backend = $this->backends[$i]->ChangesSink(1);
+ // prepend backend delimiter
for ($c = 0; $c < count($notifications_backend); $c++) {
$notifications_backend[$c] = $i . $this->config['delimiter'] . $notifications_backend[$c];
}
$notifications = array_merge($notifications, $notifications_backend);
+ }
+ }
+
+ // If nothing changed, wait until timeout
+ if (empty($notifications)) {
+ while ($stopat > time()) {
+ sleep(1);
}
}
}
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/imap/imap.php ./src/backend/imap/imap.php
--- ../z-push-2.6.0.alpha1-210/src/backend/imap/imap.php 2023-03-17 22:44:44.876105000 +0200
+++ ./src/backend/imap/imap.php 2023-03-17 22:49:29.311698000 +0200
@@ -251,6 +251,10 @@
$message->headers["cc"] = Utils::CheckAndFixEncodingInHeadersOfSentMail($Mail_RFC822->parseAddressList($message->headers["cc"], null, null, false, null));
}
+ if (isset($message->headers["bcc"])) {
+ $message->headers["bcc"] = Utils::CheckAndFixEncodingInHeadersOfSentMail($Mail_RFC822->parseAddressList($message->headers["bcc"], null, null, false, null));
+ }
+
unset($Mail_RFC822);
if (isset($message->headers["subject"]) && mb_detect_encoding($message->headers["subject"], "UTF-8") != false && preg_match('/[^\x00-\x7F]/', $message->headers["subject"]) == 1) {
@@ -2635,11 +2639,11 @@
if (is_array($toaddr)) {
$recipients = $toaddr;
}
- else {
+ elseif ($toaddr !== "") {
$recipients = array($toaddr);
}
- // Cc and Bcc headers are sent, but we need to make sure that the recipient list contains them
+ // add Bcc and Cc header fields to recipients
foreach (array("CC", "cc", "Cc", "BCC", "Bcc", "bcc") as $key) {
if (!empty($headers[$key])) {
if (is_array($headers[$key])) {
@@ -2647,6 +2651,11 @@
}
else {
$recipients[] = $headers[$key];
+ }
+
+ // remove BCC header field from message
+ if (strcasecmp($key, "BCC") == 0) {
+ unset($headers[$key]);
}
}
}
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/imap/mime_calendar.php ./src/backend/imap/mime_calendar.php
--- ../z-push-2.6.0.alpha1-210/src/backend/imap/mime_calendar.php 2023-03-17 22:44:44.876607000 +0200
+++ ./src/backend/imap/mime_calendar.php 2023-03-17 22:49:29.312270000 +0200
@@ -64,7 +64,7 @@
$caldav->Logoff();
}
else {
- ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): event not found, we will end with zombie events");
+ ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->delete_calendar_dav(): event not found, we may have zombie events");
}
}
else {
@@ -167,6 +167,17 @@
* @param $is_sent_folder boolean
*/
function parse_meeting_calendar($part, &$output, $is_sent_folder) {
+ $connected = false;
+ if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) {
+ $caldav = new BackendCalDAV();
+ if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) {
+ $connected = true;
+ }
+ else {
+ ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->parse_meeting_calendar(): Error connecting with BackendCalDAV");
+ }
+ }
+
$ical = new iCalComponent();
$ical->ParseFrom($part->body);
ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendIMAP->parse_meeting_calendar(): %s", $part->body));
@@ -200,12 +211,17 @@
case "cancel":
$output->messageclass = "IPM.Schedule.Meeting.Canceled";
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Event canceled, removing calendar object");
- delete_calendar_dav($uid);
+
+ // don't delete the recurring event on receiving cancelled exception
+ if (count($ical->GetPropertiesByPath("VEVENT/RECURRENCE-ID")) == 0) {
+ delete_calendar_dav($uid);
+ }
break;
+ case "declinecounter":
+ ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Declining a counter is not implemented.");
case "counter":
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Counter received");
$output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
- $output->meetingrequest->disallownewtimeproposal = 0;
break;
case "reply":
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Reply received");
@@ -248,18 +264,24 @@
}
}
}
- $output->meetingrequest->disallownewtimeproposal = 1;
+ $output->meetingrequest->disallownewtimeproposal = "1";
break;
case "request":
$output->messageclass = "IPM.Schedule.Meeting.Request";
- $output->meetingrequest->disallownewtimeproposal = 0;
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): New request");
// New meeting, we don't create it now, because we need to confirm it first, but if we don't create it we won't see it in the calendar
break;
+ case "add":
+ ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Add method is not implemented.");
+ $output->messageclass = "IPM.Appointment";
+ break;
+ case "publish":
+ ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Publish method is not a meeting request.");
+ $output->messageclass = "IPM.Appointment";
+ break;
default:
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown method <%s>, please report it to the developers", strtolower($part->headers["method"])));
+ ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown method <%s>, please report it to the developers", $method));
$output->messageclass = "IPM.Appointment";
- $output->meetingrequest->disallownewtimeproposal = 0;
break;
}
}
@@ -318,7 +340,7 @@
}
// Get $tz from first timezone
- $props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
+ $props = $ical->GetPropertiesByPath('VTIMEZONE/TZID');
if (count($props) > 0) {
// TimeZones shouldn't have dots
$tzname = str_replace(".", "", $props[0]->Value());
@@ -329,13 +351,87 @@
}
$output->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz));
- // Fixed values
- $output->meetingrequest->instancetype = 0;
+ // guess instancetype by checking for recurrence rules or ids
+ if (count($ical->GetPropertiesByPath('VEVENT/RRULE')) == 1) {
+ $output->meetingrequest->instancetype = 1;
+ }
+ elseif (count($ical->GetPropertiesByPath('VEVENT/RECURRENCE-ID')) == 1) {
+ if ($connected && count($caldav->FindCalendar($uid)) == 1) {
+ $output->meetingrequest->instancetype = 3;
+ }
+ else {
+ $output->meetingrequest->instancetype = 2;
+ }
+ }
+ else {
+ $output->meetingrequest->instancetype = 0;
+ }
+
$output->meetingrequest->responserequested = 1;
- $output->meetingrequest->busystatus = 2;
- // TODO: reminder
- $output->meetingrequest->reminder = "";
+ // get intended busystatus
+ $props = $ical->GetPropertiesByPath('VEVENT/X-MICROSOFT-CDO-INTENDEDSTATUS');
+ if (count($props) == 1) {
+ switch ($props[0]->Value()) {
+ case "FREE":
+ $output->meetingrequest->busystatus = "0";
+ break;
+ case "TENTATIVE":
+ $output->meetingrequest->busystatus = "1";
+ break;
+ case "BUSY":
+ $output->meetingrequest->busystatus = "2";
+ break;
+ case "OOF":
+ $output->meetingrequest->busystatus = "3";
+ break;
+ }
+ }
+ elseif (count($props = $ical->GetPropertiesByPath('VEVENT/TRANSP')) == 1) {
+ switch ($props[0]->Value()) {
+ case "TRANSPARENT":
+ $output->meetingrequest->busystatus = "0";
+ break;
+ case "OPAQUE":
+ $output->meetingrequest->busystatus = "2";
+ break;
+ }
+ }
+ else {
+ $output->meetingrequest->busystatus = 2;
+ }
+
+ // is counter allowed
+ $props = $ical->GetPropertiesByPath('VEVENT/X-MICROSOFT-DISALLOW-COUNTER');
+ if (count($props) > 0) {
+ switch ($props[0]->Value()) {
+ case "TRUE":
+ $output->meetingrequest->disallownewtimeproposal = "1";
+ break;
+ case "FALSE":
+ $output->meetingrequest->disallownewtimeproposal = "0";
+ break;
+ }
+ }
+
+ // use reminder with smallest interval
+ $props = $ical->GetPropertiesByPath('VEVENT/VALARM/TRIGGER');
+ if (count($props) > 0) {
+ foreach ($props as $vAlarmTrigger) {
+ $vAlarmTriggerValue = $vAlarmTrigger->Value();
+ if ($vAlarmTriggerValue[0] == "-") {
+ $reminderSeconds = new DateInterval(substr($vAlarmTriggerValue, 1));
+ $reminderSeconds = $reminderSeconds->format("%s") + $reminderSeconds->format("%i") * 60 + $reminderSeconds->format("%h") * 3600 + $reminderSeconds->format("%d") * 86400;
+ if (!isset($output->meetingrequest->reminderSeconds) || $output->meetingrequest->reminder > $reminderSeconds) {
+ $output->meetingrequest->reminder = $reminderSeconds;
+ }
+ }
+ }
+ }
+
+ if ($connected) {
+ $caldav->Logoff();
+ }
}
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/imap/user_identity.php ./src/backend/imap/user_identity.php
--- ../z-push-2.6.0.alpha1-210/src/backend/imap/user_identity.php 2023-03-17 22:44:44.876885000 +0200
+++ ./src/backend/imap/user_identity.php 2023-03-17 22:49:29.312593000 +0200
@@ -136,23 +136,31 @@
if ($ldap_conn) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Connected to LDAP"));
+
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0);
$ldap_bind = ldap_bind($ldap_conn, IMAP_FROM_LDAP_USER, IMAP_FROM_LDAP_PASSWORD);
if ($ldap_bind) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Authenticated in LDAP"));
+
$filter = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_LDAP_QUERY));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Searching From with filter: %s", $filter));
+
$search = ldap_search($ldap_conn, IMAP_FROM_LDAP_BASE, $filter, unserialize(IMAP_FROM_LDAP_FIELDS));
$items = ldap_get_entries($ldap_conn, $search);
if ($items['count'] > 0) {
$ret_value = $identity;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Found entry in LDAP. Generating From"));
- // We get the first object. It's your responsability to make the query unique
+
+ // We get the first object. It's your responsibility to make the query unique.
foreach (unserialize(IMAP_FROM_LDAP_FIELDS) as $field) {
- $ret_value = str_replace('#'.$field, $items[0][$field][0], $ret_value);
+ if (isset($items[0][$field][0])) {
+ $ret_value = str_replace('#' . $field, $items[0][$field][0], $ret_value);
+ }
}
+ $ret_value = trim($ret_value);
+
if ($encode) {
$ret_value = encodeFrom($ret_value);
}
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/ipcsharedmemory/ipcsharedmemoryprovider.php ./src/backend/ipcsharedmemory/ipcsharedmemoryprovider.php
--- ../z-push-2.6.0.alpha1-210/src/backend/ipcsharedmemory/ipcsharedmemoryprovider.php 2023-03-17 22:44:44.877276000 +0200
+++ ./src/backend/ipcsharedmemory/ipcsharedmemoryprovider.php 2023-03-17 22:49:29.313087000 +0200
@@ -41,8 +41,8 @@
$this->type = $type;
$this->allocate = $allocate;
- if ($this->initSharedMem())
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("%s(): Initialized mutexid %s and memid %s.", $class, $this->mutexid, $this->memid));
+ if ($this->initSharedMem())
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("%s(): Initialized.", $class));
}
/**
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/kopano/mapi/class.meetingrequest.php ./src/backend/kopano/mapi/class.meetingrequest.php
--- ../z-push-2.6.0.alpha1-210/src/backend/kopano/mapi/class.meetingrequest.php 2023-03-17 22:44:44.882818000 +0200
+++ ./src/backend/kopano/mapi/class.meetingrequest.php 2023-03-17 22:49:29.319623000 +0200
@@ -690,9 +690,9 @@
// instead of tentative, and accept the same as per the intended busystatus.
$senderEntryId = isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) ? $messageprops[PR_SENT_REPRESENTING_ENTRYID] : $messageprops[PR_SENDER_ENTRYID];
if(isset($messageprops[PR_RECEIVED_BY_ENTRYID]) && MAPIUtils::CompareEntryIds($senderEntryId, $messageprops[PR_RECEIVED_BY_ENTRYID])) {
- $entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $body, true, $store, $calFolder, $basedate);
+ $entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $store, $calFolder, $body, true, $basedate);
} else {
- $entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $body, $userAction, $store, $calFolder, $basedate);
+ $entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $store, $calFolder, $body, $userAction, $basedate);
}
// if we have first time processed this meeting then set PR_PROCESSED property
@@ -707,13 +707,13 @@
return $entryid;
}
- function accept($tentative, $sendresponse, $move, $proposeNewTimeProps = array(), $body = false, $userAction = false, $store, $calFolder, $basedate = false)
+ function accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $store, $calFolder, $body = false, $userAction = false, $basedate = false)
{
$messageprops = mapi_getprops($this->message);
$isDelegate = isset($messageprops[PR_RCVD_REPRESENTING_NAME]);
if ($sendresponse) {
- $this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
+ $this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $store, $calFolder, $body, $basedate);
}
/**
@@ -875,7 +875,7 @@
// Main recurring item is found, so now update exception
if ($calendarItem) {
- $this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
+ $this->acceptException($calendarItem, $this->message, $basedate, $store, $tentative, $userAction, $move, $isDelegate);
$calendarItemProps = mapi_getprops($calendarItem, array(PR_ENTRYID));
$entryid = $calendarItemProps[PR_ENTRYID];
}
@@ -1141,7 +1141,7 @@
}
if($sendresponse) {
- $this->createResponse(olResponseDeclined, array(), $body, $store, $basedate, $calFolder);
+ $this->createResponse(olResponseDeclined, $store, $calFolder, $body, $basedate);
}
if ($basedate) {
@@ -1856,7 +1856,7 @@
* @param Array $proposeNewTimeProps properties of attendee's proposal
* @param integer $basedate date of occurrence which attendee has responded
*/
- function createResponse($status, $proposeNewTimeProps = array(), $body = false, $store, $basedate = false, $calFolder) {
+ function createResponse($status, $store, $calFolder, $proposeNewTimeProps = array(), $body = false, $basedate = false) {
$messageprops = mapi_getprops($this->message, Array(PR_SENT_REPRESENTING_ENTRYID,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_ADDRTYPE,
@@ -2691,7 +2691,8 @@
* @param resource $store user store
* @param boolean $isDelegate true if delegate is processing this meeting request
*/
- function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move = false, $tentative, $userAction = false, $store, $isDelegate = false)
+
+ function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $store, $tentative, $userAction = false, $move = false, $isDelegate = false)
{
$recurr = new Recurrence($store, $recurringItem);
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/kopano/mapiprovider.php ./src/backend/kopano/mapiprovider.php
--- ../z-push-2.6.0.alpha1-210/src/backend/kopano/mapiprovider.php 2023-03-17 22:44:44.886493000 +0200
+++ ./src/backend/kopano/mapiprovider.php 2023-03-17 22:49:29.323536000 +0200
@@ -2907,7 +2907,14 @@
if (strlen($persistData) == 4 && $persistData == PERSIST_SENTINEL) {
break;
}
- $unpackedData = unpack("vdataSize/velementID/velDataSize", substr($persistData, 2, 6));
+ // incase of empty $persistData: Suppress error 'unpack(): Type v: not enough input, need 2, have 0'
+ $part = substr($persistData, 2, 6);
+ if(strlen($part)==6)
+ $unpackedData = unpack("vdataSize/velementID/velDataSize", $part);
+ else{
+ ZLog::Write(LOGLEVEL_INFO, "MAPIProvider->getSpecialFoldersData(): Not enough data");
+ $unpackedData = array();
+ }
if (isset($unpackedData['dataSize']) && isset($unpackedData['elementID']) && $unpackedData['elementID'] == RSF_ELID_ENTRYID && isset($unpackedData['elDataSize'])) {
$this->specialFoldersData[] = substr($persistData, 8, $unpackedData['elDataSize']);
// Add PersistId and DataElementsSize lenghts to the data size as they're not part of it
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/kopano/replybackimexporter.php ./src/backend/kopano/replybackimexporter.php
--- ../z-push-2.6.0.alpha1-210/src/backend/kopano/replybackimexporter.php 2023-03-17 22:44:44.887001000 +0200
+++ ./src/backend/kopano/replybackimexporter.php 2023-03-17 22:49:29.324623000 +0200
@@ -557,7 +557,7 @@
$data = substr(get_class($oldmessage), 4) . "\r\n";
// get the suppported fields as we need them to determine the ghosted properties
$supportedFields = ZPush::GetDeviceManager()->GetSupportedFields(ZPush::GetDeviceManager()->GetFolderIdForBackendId($folderid));
- $dataarray = $oldmessage->EvaluateAndCompare($message, @constant('READ_ONLY_NOTIFY_YOURDATA'), $supportedFields);
+ $dataarray = $oldmessage->EvaluateAndCompare($message, $supportedFields, @constant('READ_ONLY_NOTIFY_YOURDATA'));
foreach($dataarray as $key => $value) {
$value = str_replace("\r", "", $value);
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/maildir/maildir.php ./src/backend/maildir/maildir.php
--- ../z-push-2.6.0.alpha1-210/src/backend/maildir/maildir.php 2023-03-17 22:44:44.887851000 +0200
+++ ./src/backend/maildir/maildir.php 2023-03-17 22:49:29.325562000 +0200
@@ -279,7 +279,7 @@
return false;
while($entry = readdir($dir)) {
- if($entry{0} == ".")
+ if($entry[0] == ".")
continue;
$message = array();
@@ -383,16 +383,37 @@
}
}
- // convert mime-importance to AS-importance
- if (isset($message->headers["x-priority"])) {
- $mimeImportance = preg_replace("/\D+/", "", $message->headers["x-priority"]);
- if ($mimeImportance > 3)
+ // convert mime-importance to AS-importance using RFC4021, X-Priority or default to "normal" (ZP-320)
+ //AS: 0 - low, 1 - normal, 2 - important
+ if (isset($message->headers["importance"])) {
+ //Importance: high, normal, low
+ $mimeImportance = strtolower($message->headers["importance"]);
+ if ($mimeImportance == "normal") {
+ $output->importance = 1;
+ }
+ elseif ($mimeImportance == "high") {
+ $output->importance = 2;
+ }
+ elseif ($mimeImportance == "low") {
$output->importance = 0;
- if ($mimeImportance == 3)
+ }
+ }
+ elseif (isset($message->headers["x-priority"])) {
+ //X-Priority: 1 - highest, 2 - high, 3 - normal, 4 - low, 5 - lowest
+ $mimeImportance = preg_replace("/\D+/", "", $message->headers["x-priority"]);
+ if ($mimeImportance == 3) {
$output->importance = 1;
- if ($mimeImportance < 3)
+ }
+ elseif ($mimeImportance < 3) {
$output->importance = 2;
+ }
+ elseif ($mimeImportance > 3) {
+ $output->importance = 0;
+ }
}
+ else {
+ $output->importance = 1;
+ }
// Attachments are only searched in the top-level part
$n = 0;
@@ -679,7 +700,7 @@
$newdir = opendir($newdirname);
while($newentry = readdir($newdir)) {
- if($newentry{0} == ".")
+ if($newentry[0] == ".")
continue;
// link/unlink == move. This is the way to move the message according to cr.yp.to
diff -urN ../z-push-2.6.0.alpha1-210/src/backend/stickynote/stickynote.php ./src/backend/stickynote/stickynote.php
--- ../z-push-2.6.0.alpha1-210/src/backend/stickynote/stickynote.php 2023-03-17 22:44:44.889117000 +0200
+++ ./src/backend/stickynote/stickynote.php 2023-03-17 22:49:29.327040000 +0200
@@ -309,115 +309,122 @@
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage('%s','%s')", $folderid, $id));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage(Message '%s')", $message));
- // If we have a null ID then it's a new note; allocate an ordinal for
- // it. Then insert into the database and return the stat pointer for it.
- // If we get an ID then it's an update; perform it and return stat
- // pointer.
- //
- $_contents = stream_get_contents($message->asbody->data, 1024000);
- if (!$id) {
- $this->_result = pg_query($this->_dbconn, "select nextval('ordinal')");
- if (pg_result_status($this->_result) != PGSQL_TUPLES_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot get new sequence number for item')"));
- return false;
- }
- $id = pg_fetch_result($this->_result, 0, 0);
- pg_free_result($this->_result);
+ if(ZPush::GetDeviceManager()->IsKoe() && KOE_CAPABILITY_NOTES && $id && !isset($message->asbody)) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage(): KOE patch item update. Ignoring incoming update."));
+ }
+ else {
+ $_contents = stream_get_contents($message->asbody->data);
- $this->_result = pg_query($this->_dbconn, "Begin");
- if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Transaction start failure!')"));
+ // If we have a null ID then it's a new note; allocate an ordinal for
+ // it. Then insert into the database and return the stat pointer for it.
+ // If we get an ID then it's an update; perform it and return stat
+ // pointer.
+ //
+ $_contents = stream_get_contents($message->asbody->data, 1024000);
+ if (!$id) {
+ $this->_result = pg_query($this->_dbconn, "select nextval('ordinal')");
+ if (pg_result_status($this->_result) != PGSQL_TUPLES_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot get new sequence number for item')"));
+ return false;
+ }
+ $id = pg_fetch_result($this->_result, 0, 0);
pg_free_result($this->_result);
- return false;
- }
- pg_free_result($this->_result);
- $_params = array();
- array_push($_params, $id, $message->subject, $_contents, $this->_user, $this->_domain);
- $this->_result = pg_query_params($this->_dbconn, "insert into note (ordinal, subject, content, login, domain) values ($1, $2, $3, $4, $5)", $_params);
- if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert new item; fail!')"));
+ $this->_result = pg_query($this->_dbconn, "Begin");
+ if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Transaction start failure!')"));
+ pg_free_result($this->_result);
+ return false;
+ }
pg_free_result($this->_result);
- $this->_result = pg_query($this->_dbconn, "Rollback");
+
+ $_params = array();
+ array_push($_params, $id, $message->subject, $_contents, $this->_user, $this->_domain);
+ $this->_result = pg_query_params($this->_dbconn, "insert into note (ordinal, subject, content, login, domain) values ($1, $2, $3, $4, $5)", $_params);
+ if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert new item; fail!')"));
+ pg_free_result($this->_result);
+ $this->_result = pg_query($this->_dbconn, "Rollback");
+ pg_free_result($this->_result);
+ return false;
+ }
+ if (pg_affected_rows($this->_result) == 1) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage('Insert of item %s (subj '%s') succeded')", $id, $message->subject));
+ } else {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Insert of item %s (subj '%s') failed')", $id, $message->subject));
+ pg_free_result($this->_result);
+ $this->_result = pg_query($this->_dbconn, "Rollback");
+ pg_free_result($this->_result);
+ return false;
+ }
+ unset ($_params);
pg_free_result($this->_result);
- return false;
- }
- if (pg_affected_rows($this->_result) == 1) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage('Insert of item %s (subj '%s') succeded')", $id, $message->subject));
- } else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Insert of item %s (subj '%s') failed')", $id, $message->subject));
- pg_free_result($this->_result);
- $this->_result = pg_query($this->_dbconn, "Rollback");
- pg_free_result($this->_result);
- return false;
- }
- unset ($_params);
- pg_free_result($this->_result);
- if ($message->categories) {
- foreach ($message->categories as $_category) {
- $_params = array();
- array_push($_params, $id, $_category);
- $this->_result = pg_query_params($this->_dbconn, "insert into categories (ordinal, tag) values ($1, $2)", $_params);
- if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert category for item; fail!')"));
+ if ($message->categories) {
+ foreach ($message->categories as $_category) {
+ $_params = array();
+ array_push($_params, $id, $_category);
+ $this->_result = pg_query_params($this->_dbconn, "insert into categories (ordinal, tag) values ($1, $2)", $_params);
+ if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert category for item; fail!')"));
+ pg_free_result($this->_result);
+ $this->_result = pg_query($this->_dbconn, "Rollback");
+ pg_free_result($this->_result);
+ return(false);
+ }
pg_free_result($this->_result);
- $this->_result = pg_query($this->_dbconn, "Rollback");
- pg_free_result($this->_result);
- return(false);
}
- pg_free_result($this->_result);
+ unset ($_category);
}
- unset ($_category);
- }
- } else {
- $_params = array();
- array_push($_params, $message->subject, $_contents, $id, $this->_user, $this->_domain);
- $this->_result = pg_query_params($this->_dbconn, "update note set subject=$1, content=$2, modified=now() where ordinal=$3 and login=$4 and domain=$5", $_params);
- if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Update of item %s failed!')", $id));
- }
- if (pg_affected_rows($this->_result) == 1) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage('Update of item %s (subj '%s') succeded')", $id, $message->subject));
} else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Update of item %s (subj '%s') failed (credential mismatch)')", $id, $message->subject));
- }
- pg_free_result($this->_result);
- unset ($_params);
- if ($message->categories) {
$_params = array();
- array_push($_params, $id);
- $this->_result = pg_query_params($this->_dbconn, "delete from categories where ordinal=$1", $_params);
+ array_push($_params, $message->subject, $_contents, $id, $this->_user, $this->_domain);
+ $this->_result = pg_query_params($this->_dbconn, "update note set subject=$1, content=$2, modified=now() where ordinal=$3 and login=$4 and domain=$5", $_params);
if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot clear category for item; fail!')"));
- pg_free_result($this->_result);
- $this->_result = pg_query($this->_dbconn, "Rollback");
- pg_free_result($this->_result);
- return(false);
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Update of item %s failed!')", $id));
}
+ if (pg_affected_rows($this->_result) == 1) {
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendStickyNote->ChangeMessage('Update of item %s (subj '%s') succeded')", $id, $message->subject));
+ } else {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Update of item %s (subj '%s') failed (credential mismatch)')", $id, $message->subject));
+ }
+ pg_free_result($this->_result);
unset ($_params);
- foreach ($message->categories as $_category) {
+ if ($message->categories) {
$_params = array();
- array_push($_params, $id, $_category);
- $this->_result = pg_query_params($this->_dbconn, "insert into categories (ordinal, tag) values ($1, $2)", $_params);
+ array_push($_params, $id);
+ $this->_result = pg_query_params($this->_dbconn, "delete from categories where ordinal=$1", $_params);
if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert category for item; fail!')"));
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot clear category for item; fail!')"));
pg_free_result($this->_result);
$this->_result = pg_query($this->_dbconn, "Rollback");
pg_free_result($this->_result);
return(false);
}
- pg_free_result($this->_result);
+ unset ($_params);
+ foreach ($message->categories as $_category) {
+ $_params = array();
+ array_push($_params, $id, $_category);
+ $this->_result = pg_query_params($this->_dbconn, "insert into categories (ordinal, tag) values ($1, $2)", $_params);
+ if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Cannot insert category for item; fail!')"));
+ pg_free_result($this->_result);
+ $this->_result = pg_query($this->_dbconn, "Rollback");
+ pg_free_result($this->_result);
+ return(false);
+ }
+ pg_free_result($this->_result);
+ }
+ unset ($_category);
}
- unset ($_category);
+ }
+ $this->_result = pg_query($this->_dbconn, "COMMIT");
+ if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Transaction commit FAIL!')"));
+ pg_free_result($this->_result);
+ return false;
}
- }
- $this->_result = pg_query($this->_dbconn, "COMMIT");
- if (pg_result_status($this->_result) != PGSQL_COMMAND_OK) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendStickyNote->ChangeMessage('Transaction commit FAIL!')"));
pg_free_result($this->_result);
- return false;
}
- pg_free_result($this->_result);
return $this->StatMessage($folderid, $id);
}
diff -urN ../z-push-2.6.0.alpha1-210/src/include/iCalendar.php ./src/include/iCalendar.php
--- ../z-push-2.6.0.alpha1-210/src/include/iCalendar.php 2023-03-17 22:44:44.890317000 +0200
+++ ./src/include/iCalendar.php 2023-03-17 22:49:29.328150000 +0200
@@ -104,19 +104,29 @@
}
list($prop, $value) = $split;
- // Unescape ESCAPED-CHAR
- $this->content = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $value);
-
// Split property name and parameters
$parameters = $this->SplitQuoted($prop, ';');
$this->name = array_shift($parameters);
$this->parameters = array();
+
+ // Unescape ESCAPED-CHAR
+ switch( $this->name ) {
+ case 'CATEGORIES':
+ case 'RESOURCES':
+ $this->content = $value;
+ break;
+ default:
+ $this->content = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $value);
+ }
+
+ // Split parameters
foreach ($parameters AS $k => $v) {
$pos = strpos($v, '=');
$name = substr($v, 0, $pos);
$value = substr($v, $pos + 1);
$this->parameters[$name] = preg_replace('/^"(.+)"$/', '$1', $value); // Removes DQUOTE on demand
}
+
ZLog::Write(LOGLEVEL_DEBUG, sprintf("iCalendar->ParseFrom(): found '%s' = '%s' with %d parameters", $this->name, substr($this->content,0,200), count($this->parameters)));
}
@@ -282,7 +292,8 @@
case 'DURATION': case 'FREEBUSY': case 'TZOFFSETFROM': case 'TZOFFSETTO':
case 'TZURL': case 'ATTENDEE': case 'ORGANIZER': case 'RECURRENCE-ID':
case 'URL': case 'EXRULE': case 'SEQUENCE': case 'CREATED':
- case 'RRULE': case 'REPEAT': case 'TRIGGER':
+ case 'RRULE': case 'REPEAT': case 'TRIGGER': case 'CATEGORIES':
+ case 'RESOURCES':
break;
case 'COMPLETED': case 'DTEND':
diff -urN ../z-push-2.6.0.alpha1-210/src/include/mimeDecode.php ./src/include/mimeDecode.php
--- ../z-push-2.6.0.alpha1-210/src/include/mimeDecode.php 2023-03-17 22:44:44.890696000 +0200
+++ ./src/include/mimeDecode.php 2023-03-17 22:49:29.328544000 +0200
@@ -154,7 +154,7 @@
/**
* Flag to determine whether to decode headers
- * (set to UTF8 to iconv convert headers)
+ * (set to UTF8 to convert headers)
* @var mixed
* @access private
*/
@@ -234,20 +234,15 @@
// Called via an object
} else {
- $this->_include_bodies = isset($params['include_bodies']) ?
- $params['include_bodies'] : false;
- $this->_decode_bodies = isset($params['decode_bodies']) ?
- $params['decode_bodies'] : false;
- $this->_decode_headers = isset($params['decode_headers']) ?
- $params['decode_headers'] : false;
- $this->_rfc822_bodies = isset($params['rfc_822bodies']) ?
- $params['rfc_822bodies'] : false;
- $this->_charset = isset($params['charset']) ?
- strtolower($params['charset']) : 'utf-8';
+ $this->_include_bodies = isset($params['include_bodies']) ? $params['include_bodies'] : false;
+ $this->_decode_bodies = isset($params['decode_bodies']) ? $params['decode_bodies'] : false;
+ $this->_decode_headers = isset($params['decode_headers']) ? $params['decode_headers'] : false;
+ $this->_rfc822_bodies = isset($params['rfc_822bodies']) ? $params['rfc_822bodies'] : false;
+ $this->_charset = isset($params['charset']) ? strtolower($params['charset']) : 'utf-8';
if (is_string($this->_decode_headers)) {
- if (!function_exists('iconv')) {
- $this->raiseError('header decode conversion requested, however iconv is missing');
+ if (!function_exists('mb_convert_encoding')) {
+ $this->raiseError('header decode conversion requested, however mbstring is missing');
}
$this->_decode_headers = strtolower($this->_decode_headers);
}
@@ -527,7 +522,7 @@
if (!$got_start) {
// munge headers for mbox style from
if ($value[0] == '>') {
- $value = substring($value, 1); // remove mbox >
+ $value = substr($value, 1); // remove mbox >
}
if (substr($value,0,5) == 'From ') {
$value = 'Return-Path: ' . substr($value, 5);
@@ -825,15 +820,32 @@
break;
}
if (is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
- $conv = @iconv($charset, $this->_decode_headers, $text);
- $text = ($conv === false) ? $text : $conv;
+ if (@mb_check_encoding($text, $charset) == false) {
+ // list of encodings, sorted by priority to assist mb_detect_encoding()
+ $encodingPriority = array('UTF-8', 'SJIS', 'GB18030', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
+ 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-13',
+ 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16', 'WINDOWS-1252', 'WINDOWS-1251', 'EUC-JP', 'EUC-TW',
+ 'KOI8-R', 'BIG-5', 'ISO-2022-KR', 'ISO-2022-JP-MS');
+
+ // only use encodings supported by the system
+ $encodings = array_unique(array_merge($encodingPriority, mb_list_encodings()));
+
+ // detect suitable encoding
+ if (@mb_check_encoding($text, ($encoding = mb_detect_encoding($text, $encodings)))) {
+ ZLog::Write(LOGLEVEL_WARN, sprintf("mimeDecode::_decodeHeader(): invalid encoding in header: using '%s' instead of '%s'", $encoding, $charset));
+ $charset = $encoding;
+ }
+ else {
+ ZLog::Write(LOGLEVEL_WARN, sprintf("mimeDecode::_decodeHeader(): invalid encoding '%s' used in header, no substitution found", $charset));
+ }
+ }
+ $text = @mb_convert_encoding($text, $this->_decode_headers, $charset);
}
$input = str_replace($encoded, $text, $input);
}
- if ($default_charset && is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
- $conv = @iconv($charset, $this->_decode_headers, $input); // TODO: shouldn't this be $default_charset ?
- $input = ($conv === false) ? $input : $conv;
+ if ($default_charset && is_string($this->_decode_headers) && $default_charset != $this->_decode_headers) {
+ $input = mb_convert_encoding($input, $this->_decode_headers, $default_charset);
}
return $input;
@@ -861,9 +873,28 @@
$input = base64_decode($input);
break;
}
+
if ($detectCharset && strtolower($charset) != $this->_charset) {
- $conv = @iconv($charset, $this->_charset, $input);
- $input = ($conv === false) ? $input : $conv;
+ if (@mb_check_encoding($input, $charset) == false) {
+ // list of encodings, sorted by priority to assist mb_detect_encoding()
+ $encodingPriority = array('UTF-8', 'SJIS', 'GB18030', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
+ 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-13',
+ 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16', 'WINDOWS-1252', 'WINDOWS-1251', 'EUC-JP', 'EUC-TW',
+ 'KOI8-R', 'BIG-5', 'ISO-2022-KR', 'ISO-2022-JP-MS');
+
+ // only use encodings supported by the system
+ $encodings = array_unique(array_merge($encodingPriority, mb_list_encodings()));
+
+ // detect suitable encoding
+ if (@mb_check_encoding($input, ($encoding = mb_detect_encoding($input, $encodings)))) {
+ ZLog::Write(LOGLEVEL_WARN, sprintf("mimeDecode::_decodeBody(): invalid encoding in body: using '%s' instead of '%s'", $encoding, $charset));
+ $charset = $encoding;
+ }
+ else {
+ ZLog::Write(LOGLEVEL_WARN, sprintf("mimeDecode::_decodeBody(): invalid encoding '%s' used in body, no substitution found", $charset));
+ }
+ }
+ $input = @mb_convert_encoding($input, $this->_decode_headers, $charset);
}
return $input;
@@ -1175,4 +1206,4 @@
return false;
}
-} // End of class
\ No newline at end of file
+} // End of class
diff -urN ../z-push-2.6.0.alpha1-210/src/include/z_RTF.php ./src/include/z_RTF.php
--- ../z-push-2.6.0.alpha1-210/src/include/z_RTF.php 2023-03-17 22:44:44.891369000 +0200
+++ ./src/include/z_RTF.php 2023-03-17 22:49:29.329228000 +0200
@@ -172,7 +172,7 @@
$c=0;
$end = $off + $len;
for($i=$off;$i < $end;$i++) {
- $c=$this->CRC32_TABLE[($c ^ ord($buf{$i})) & 0xFF] ^ (($c >> 8) & 0x00ffffff);
+ $c=$this->CRC32_TABLE[($c ^ ord($buf[$i])) & 0xFF] ^ (($c >> 8) & 0x00ffffff);
}
return $c;
}
diff -urN ../z-push-2.6.0.alpha1-210/src/include/z_caldav.php ./src/include/z_caldav.php
--- ../z-push-2.6.0.alpha1-210/src/include/z_caldav.php 2023-03-17 22:44:44.891719000 +0200
+++ ./src/include/z_caldav.php 2023-03-17 22:49:29.329601000 +0200
@@ -96,15 +96,15 @@
*/
private $curl = false;
- private $synctoken = array();
+ private $synctoken = array();
/**
- * Constructor, initialises the class
- *
- * @param string $caldav_url The URL for the calendar server
- * @param string $user The name of the user logging in
- * @param string $pass The password for that user
- */
+ * Constructor, initialises the class
+ *
+ * @param string $caldav_url The URL for the calendar server
+ * @param string $user The name of the user logging in
+ * @param string $pass The password for that user
+ */
function __construct( $caldav_url, $user, $pass ) {
$this->url = $caldav_url;
$this->user = $user;
@@ -115,86 +115,91 @@
$parsed_url = parse_url($caldav_url);
if ($parsed_url === false) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCalDAV->caldav_backend(): Couldn't parse URL: %s", $caldav_url));
- return;
+ return;
}
$this->server = $parsed_url['scheme'] . '://' . $parsed_url['host'] . ':' . $parsed_url['port'];
$this->base_url = $parsed_url['path'];
- //ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->caldav_backend(): base_url '%s'", $this->base_url));
- //$this->base_url .= !empty($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
- //$this->base_url .= !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->caldav_backend(): base_url '%s'", $this->base_url));
- if (substr($this->base_url, -1) !== '/') {
+ //$this->base_url .= !empty($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
+ //$this->base_url .= !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
+
+ if (substr($this->base_url, -1) !== '/') {
$this->base_url = $this->base_url . '/';
}
}
+
/**
- * Checks if the CalDAV server is reachable
- *
- * @return boolean
- */
- public function CheckConnection() {
- $result = $this->DoRequest($this->url, 'OPTIONS');
+ * Checks if the CalDAV server is reachable
+ *
+ * @return boolean
+ */
+ public function CheckConnection() {
+ $result = $this->DoRequest($this->url, 'OPTIONS');
- switch ($this->httpResponseCode) {
- case 200:
- case 207:
- case 401:
- $status = true;
- break;
- default:
- $status = false;
- }
+ switch ($this->httpResponseCode) {
+ case 200:
+ case 207:
+ case 401:
+ $status = true;
+ break;
+ default:
+ $status = false;
+ }
+ return $status;
+ }
- return $status;
- }
- /**
- * Disconnect curl connection
- *
- */
- public function Disconnect() {
- if ($this->curl !== false) {
- curl_close($this->curl);
- $this->curl = false;
- }
- }
+ /**
+ * Disconnect curl connection
+ *
+ */
+ public function Disconnect() {
+ if ($this->curl !== false) {
+ curl_close($this->curl);
+ $this->curl = false;
+ }
+ }
/**
- * Adds an If-Match or If-None-Match header
- *
- * @param bool $match to Match or Not to Match, that is the question!
- * @param string $etag The etag to match / not match against.
- */
+ * Adds an If-Match or If-None-Match header
+ *
+ * @param bool $match to Match or Not to Match, that is the question!
+ * @param string $etag The etag to match / not match against.
+ */
function SetMatch( $match, $etag = '*' ) {
$this->headers['match'] = sprintf( "%s-Match: \"%s\"", ($match ? "If" : "If-None"), trim($etag,'"'));
}
+
/**
- * Add a Depth: header. Valid values are 0, 1 or infinity
- *
- * @param int $depth The depth, default to infinity
- */
+ * Add a Depth: header. Valid values are 0, 1 or infinity
+ *
+ * @param int $depth The depth, default to infinity
+ */
function SetDepth( $depth = '0' ) {
$this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
}
+
/**
- * Set the calendar_url we will be using for a while.
- *
- * @param string $url The calendar_url
- */
+ * Set the calendar_url we will be using for a while.
+ *
+ * @param string $url The calendar_url
+ */
function SetCalendar( $url ) {
$this->calendar_url = $url;
}
+
/**
- * Split response into httpResponse and xmlResponse
- *
- * @param string Response from server
- */
+ * Split response into httpResponse and xmlResponse
+ *
+ * @param string Response from server
+ */
function ParseResponse( $response ) {
$pos = strpos($response, 'curl_init();
@@ -289,12 +294,12 @@
/**
- * Send an OPTIONS request to the server
- *
- * @param string $url The URL to make the request to
- *
- * @return array The allowed options
- */
+ * Send an OPTIONS request to the server
+ *
+ * @param string $url The URL to make the request to
+ *
+ * @return array The allowed options
+ */
function DoOptionsRequest( $url = null ) {
$headers = $this->DoRequest($url === null ? $this->url : $url, "OPTIONS");
$options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
@@ -304,48 +309,48 @@
/**
- * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR)
- *
- * @param string $method The method (PROPFIND, REPORT, etc) to use with the request
- * @param string $xml The XML to send along with the request
- * @param string $url The URL to make the request to
- *
- * @return array An array of the allowed methods
- */
+ * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR)
+ *
+ * @param string $method The method (PROPFIND, REPORT, etc) to use with the request
+ * @param string $xml The XML to send along with the request
+ * @param string $url The URL to make the request to
+ *
+ * @return array An array of the allowed methods
+ */
function DoXMLRequest( $request_method, $xml, $url = null ) {
return $this->DoRequest($url, $request_method, $xml, "text/xml");
}
/**
- * Get a single item from the server.
- *
- * @param string $url The URL to GET
- */
+ * Get a single item from the server.
+ *
+ * @param string $url The URL to GET
+ */
function DoGETRequest( $url ) {
return $this->DoRequest($url, "GET");
}
/**
- * Get the HEAD of a single item from the server.
- *
- * @param string $url The URL to HEAD
- */
+ * Get the HEAD of a single item from the server.
+ *
+ * @param string $url The URL to HEAD
+ */
function DoHEADRequest( $url ) {
return $this->DoRequest($url, "HEAD");
}
/**
- * PUT a text/icalendar resource, returning the etag
- *
- * @param string $url The URL to make the request to
- * @param string $icalendar The iCalendar resource to send to the server
- * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource.
- *
- * @return string The content of the response from the server
- */
+ * PUT a text/icalendar resource, returning the etag
+ *
+ * @param string $url The URL to make the request to
+ * @param string $icalendar The iCalendar resource to send to the server
+ * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource.
+ *
+ * @return string The content of the response from the server
+ */
function DoPUTRequest( $url, $icalendar, $etag = null ) {
if ( $etag != null ) {
$this->SetMatch( ($etag != '*'), $etag );
@@ -373,13 +378,13 @@
/**
- * DELETE a text/icalendar resource
- *
- * @param string $url The URL to make the request to
- * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL.
- *
- * @return int The HTTP Result Code for the DELETE
- */
+ * DELETE a text/icalendar resource
+ *
+ * @param string $url The URL to make the request to
+ * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL.
+ *
+ * @return int The HTTP Result Code for the DELETE
+ */
function DoDELETERequest( $url, $etag = null ) {
if ( $etag != null ) {
$this->SetMatch( true, $etag );
@@ -390,10 +395,10 @@
/**
- * Get a single item from the server.
- *
- * @param string $url The URL to PROPFIND on
- */
+ * Get a single item from the server.
+ *
+ * @param string $url The URL to PROPFIND on
+ */
function DoPROPFINDRequest( $url, $props, $depth = 0 ) {
$this->SetDepth($depth);
$xml = new XMLDocument( array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ) );
@@ -408,10 +413,10 @@
/**
- * Get/Set the Principal URL
- *
- * @param $url string The Principal URL to set
- */
+ * Get/Set the Principal URL
+ *
+ * @param $url string The Principal URL to set
+ */
function PrincipalURL( $url = null ) {
if ( isset($url) ) {
$this->principal_url = $url;
@@ -421,10 +426,10 @@
/**
- * Get/Set the calendar-home-set URL
- *
- * @param $url array of string The calendar-home-set URLs to set
- */
+ * Get/Set the calendar-home-set URL
+ *
+ * @param $url array of string The calendar-home-set URLs to set
+ */
function CalendarHomeSet( $urls = null ) {
if ( isset($urls) ) {
if ( !is_array($urls) ) {
@@ -437,10 +442,10 @@
/**
- * Get/Set the calendar-home-set URL
- *
- * @param $urls array of string The calendar URLs to set
- */
+ * Get/Set the calendar-home-set URL
+ *
+ * @param $urls array of string The calendar URLs to set
+ */
function CalendarUrls( $urls = null ) {
if ( isset($urls) ) {
if ( !is_array($urls) ) {
@@ -453,10 +458,10 @@
/**
- * Return the first occurrence of an href inside the named tag.
- *
- * @param string $tagname The tag name to find the href inside of
- */
+ * Return the first occurrence of an href inside the named tag.
+ *
+ * @param string $tagname The tag name to find the href inside of
+ */
function HrefValueInside( $tagname ) {
foreach( $this->xmltags[$tagname] AS $k => $v ) {
$j = $v + 1;
@@ -469,11 +474,11 @@
/**
- * Return the href containing this property. Except only if it's inside a status != 200
- *
- * @param string $tagname The tag name of the property to find the href for
- * @param integer $which Which instance of the tag should we use
- */
+ * Return the href containing this property. Except only if it's inside a status != 200
+ *
+ * @param string $tagname The tag name of the property to find the href for
+ * @param integer $which Which instance of the tag should we use
+ */
function HrefForProp( $tagname, $i = 0 ) {
if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
$j = $this->xmltags[$tagname][$i];
@@ -497,11 +502,11 @@
/**
- * Return the href which has a resourcetype of the specified type
- *
- * @param string $tagname The tag name of the resourcetype to find the href for
- * @param integer $which Which instance of the tag should we use
- */
+ * Return the href which has a resourcetype of the specified type
+ *
+ * @param string $tagname The tag name of the resourcetype to find the href for
+ * @param integer $which Which instance of the tag should we use
+ */
function HrefForResourcetype( $tagname, $i = 0 ) {
if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
$j = $this->xmltags[$tagname][$i];
@@ -518,10 +523,10 @@
/**
- * Return the ... of a propstat where the status is OK
- *
- * @param string $nodenum The node number in the xmlnodes which is the href
- */
+ * Return the ... of a propstat where the status is OK
+ *
+ * @param string $nodenum The node number in the xmlnodes which is the href
+ */
function GetOKProps( $nodenum ) {
$props = null;
$level = $this->xmlnodes[$nodenum]['level'];
@@ -549,10 +554,10 @@
/**
- * Attack the given URL in an attempt to find a principal URL
- *
- * @param string $url The URL to find the principal-URL from
- */
+ * Attack the given URL in an attempt to find a principal URL
+ *
+ * @param string $url The URL to find the principal-URL from
+ */
function FindPrincipal( $url=null ) {
$xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL', 'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
@@ -571,10 +576,10 @@
/**
- * Attack the given URL in an attempt to find a principal URL
- *
- * @param string $url The URL to find the calendar-home-set from
- */
+ * Attack the given URL in an attempt to find a principal URL
+ *
+ * @param string $url The URL to find the calendar-home-set from
+ */
function FindCalendarHome( $recursed=false ) {
if ( !isset($this->principal_url) ) {
$this->FindPrincipal();
@@ -607,8 +612,8 @@
/**
- * Find the calendars, from the calendar_home_set
- */
+ * Find the calendars, from the calendar_home_set
+ */
function FindCalendars( $recursed=false ) {
if ( !isset($this->calendar_home_set[0]) ) {
$this->FindCalendarHome();
@@ -654,8 +659,8 @@
/**
- * Find the calendars, from the calendar_home_set
- */
+ * Find the calendars, from the calendar_home_set
+ */
function GetCalendarDetails( $url = null ) {
if ( isset($url) ) {
$this->SetCalendar($url);
@@ -687,8 +692,8 @@
/**
- * Get all etags for a calendar
- */
+ * Get all etags for a calendar
+ */
function GetCollectionETags( $url = null ) {
if ( isset($url) ) {
$this->SetCalendar($url);
@@ -711,8 +716,8 @@
/**
- * Get a bunch of events for a calendar with a calendar-multiget report
- */
+ * Get a bunch of events for a calendar with a calendar-multiget report
+ */
function CalendarMultiget( $event_hrefs, $url = null ) {
if ( isset($url) ) {
$this->SetCalendar($url);
@@ -752,20 +757,20 @@
/**
- * Given XML for a calendar query, return an array of the events (/todos) in the
- * response. Each event in the array will have a 'href', 'etag' and an optional '$response_type'
- * part (depending on the flag '$include_data'), where the 'href' is relative to the calendar and
- * the '$response_type' contains the
- * definition of the calendar data in iCalendar format.
- *
- * @param string $filter XML fragment which is the element of a calendar-query
- * @param string $url The URL of the calendar, or empty/null to use the 'current' calendar_url
- * @param boolean $include_data Flags whether to request the content-data (the icalendar data) in the response
- *
- * @return array An array of the relative URLs, etags, and events from the server. Each element of the array will
- * be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
- * etag (which only varies when the data changes) and the calendar data in iCalendar format.
- */
+ * Given XML for a calendar query, return an array of the events (/todos) in the
+ * response. Each event in the array will have a 'href', 'etag' and an optional '$response_type'
+ * part (depending on the flag '$include_data'), where the 'href' is relative to the calendar and
+ * the '$response_type' contains the
+ * definition of the calendar data in iCalendar format.
+ *
+ * @param string $filter XML fragment which is the element of a calendar-query
+ * @param string $url The URL of the calendar, or empty/null to use the 'current' calendar_url
+ * @param boolean $include_data Flags whether to request the content-data (the icalendar data) in the response
+ *
+ * @return array An array of the relative URLs, etags, and events from the server. Each element of the array will
+ * be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+ * etag (which only varies when the data changes) and the calendar data in iCalendar format.
+ */
function DoCalendarQuery( $filter, $url = null, $include_data = true ) {
if ( !empty($url) ) {
$this->SetCalendar($url);
@@ -820,25 +825,25 @@
}
/**
- * Get a list of events in a range from $start to $finish. The dates should be in the
- * format yyyymmddThhmmssZ and should be in GMT. The events are returned as an
- * array of event parameter arrays. Unlike the original GetEvents, each event array will only contain the 'href' and 'etag'
- * parts, where the 'href' is relative to the calendar.
- *
- * @param timestamp $start The start time for the period
- * @param timestamp $finish The finish time for the period
- * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''.
- *
- * @return array An array of the relative URLs and etags as [['href'], ['etag']]
- *
- * This function has been modified from the original GetEvents function; the function has been renamed to prevent regression errors
- */
+ * Get a list of events in a range from $start to $finish. The dates should be in the
+ * format yyyymmddThhmmssZ and should be in GMT. The events are returned as an
+ * array of event parameter arrays. Unlike the original GetEvents, each event array will only contain the 'href' and 'etag'
+ * parts, where the 'href' is relative to the calendar.
+ *
+ * @param timestamp $start The start time for the period
+ * @param timestamp $finish The finish time for the period
+ * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''.
+ *
+ * @return array An array of the relative URLs and etags as [['href'], ['etag']]
+ *
+ * This function has been modified from the original GetEvents function; the function has been renamed to prevent regression errors
+ */
function GetEventsList( $start = null, $finish = null, $relative_url = null ) {
- $filter = "";
- if ( isset($start) && isset($finish) ) {
+
+ if ( isset($start, $finish) ) {
$range = "";
} else {
- $range = '';
+ $range = "";
}
$filter = <<
-EOTIME;
- } else {
- $time_range = "";
- }
+ $range_filter = "";
+ if(isset($start)) {
+ $range_filter .= ' start="' . $start . '"';
+ }
+ if(isset($finish)) {
+ $range_filter .= ' end="' . $finish . '"';
+ }
+ if($range_filter !== "") {
+ $range_filter = "";
+ }
// Warning! May contain traces of double negatives...
- $neg_cancelled = ( $cancelled === true ? "no" : "yes" );
- $neg_completed = ( $cancelled === true ? "no" : "yes" );
+ $completed_filter = "";
+ if(isset($completed)) {
+ $completed_filter = '
-
-
- COMPLETED
-
-
- CANCELLED
- $time_range
-
+
+ $completed_filter
+ $cancelled_filter
+ $range_filter
+
EOFILTER;
@@ -907,14 +933,14 @@
/**
- * Get the calendar entry by UID
- *
- * @param uid
- * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''.
- * @param string $component_type The component type inside the VCALENDAR. Default 'VEVENT'.
- *
- * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery()
- */
+ * Get the calendar entry by UID
+ *
+ * @param uid
+ * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''.
+ * @param string $component_type The component type inside the VCALENDAR. Default 'VEVENT'.
+ *
+ * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery()
+ */
function GetEntryByUid( $uid, $relative_url = null, $component_type = 'VEVENT' ) {
$filter = "";
if ( $uid ) {
@@ -936,37 +962,38 @@
/**
- * Get the calendar entry by HREF
- *
- * @param string $href The href from a call to GetEvents or GetTodos etc.
- *
- * @return string The iCalendar of the calendar entry
- */
+ * Get the calendar entry by HREF
+ *
+ * @param string $href The href from a call to GetEvents or GetTodos etc.
+ *
+ * @return string The iCalendar of the calendar entry
+ */
function GetEntryByHref( $href ) {
$href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
return $this->DoGETRequest( $href );
}
- /**
- * Do a Sync operation. This is the fastest way to detect changes.
- *
- * @param string $url URL for the calendar
- * @param boolean $initial It's the first synchronization
- * @param boolean $support_dav_sync The CalDAV server supports sync-collection
- *
- * @return array of responses
- */
- public function GetSync($relative_url = null, $initial = true, $support_dav_sync = false) {
- if (!empty($relative_url)) {
- $this->SetCalendar($relative_url);
- }
- $hasToken = !$initial && isset($this->synctoken[$this->calendar_url]);
- if ($support_dav_sync) {
- $token = ($hasToken ? $this->synctoken[$this->calendar_url] : "");
+ /**
+ * Do a Sync operation. This is the fastest way to detect changes.
+ *
+ * @param string $url URL for the calendar
+ * @param boolean $initial It's the first synchronization
+ * @param boolean $support_dav_sync The CalDAV server supports sync-collection
+ *
+ * @return array of responses
+ */
+ public function GetSync($relative_url = null, $initial = true, $support_dav_sync = false) {
+ if (!empty($relative_url)) {
+ $this->SetCalendar($relative_url);
+ }
+ $hasToken = !$initial && isset($this->synctoken[$this->calendar_url]);
+ if ($support_dav_sync) {
+ $token = ($hasToken ? $this->synctoken[$this->calendar_url] : "");
ZLog::Write(LOGLEVEL_WBXML, sprintf("CalDAVClient->GetSync called for %s'%s' with token '%s'", ($initial ? "initial sync of " : ""), $this->calendar_url, $token));
- $body = <<
$token
@@ -977,9 +1004,9 @@
EOXML;
- }
- else {
- $body = <<
@@ -991,50 +1018,49 @@
EOXML;
- }
+ }
- $this->SetDepth(1);
- $this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml");
+ $this->SetDepth(1);
+ $this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml");
- $report = array();
- foreach ($this->xmlnodes as $k => $v) {
- switch ($v['tag']) {
- case 'DAV::response':
- if ($v['type'] == 'open') {
- $response = array();
- }
- elseif ($v['type'] == 'close') {
- $report[] = $response;
- ZLog::Write(LOGLEVEL_WBXML, sprintf("CalDAVClient->GetSync return value includes response: %s",implode("|",$response)));
- }
- break;
- case 'DAV::href':
- $response['href'] = basename( rawurldecode($v['value']) );
- break;
- case 'DAV::getlastmodified':
- if (isset($v['value'])) {
- $response['getlastmodified'] = $v['value'];
- }
- else {
- $response['getlastmodified'] = '';
- }
- break;
- case 'DAV::getetag':
- $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
- break;
- case 'DAV::sync-token':
- $this->synctoken[$this->calendar_url] = $v['value'];
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("CalDAVClient->GetSync: Response from '%s' includes sync token: '%s'", $this->calendar_url, $this->synctoken[$this->calendar_url]));
- break;
- }
- }
+ $report = array();
+ foreach ($this->xmlnodes as $k => $v) {
+ switch ($v['tag']) {
+ case 'DAV::response':
+ if ($v['type'] == 'open') {
+ $response = array();
+ }
+ elseif ($v['type'] == 'close') {
+ $report[] = $response;
+ ZLog::Write(LOGLEVEL_WBXML, sprintf("CalDAVClient->GetSync return value includes response: %s",implode("|",$response)));
+ }
+ break;
+ case 'DAV::href':
+ $response['href'] = basename( rawurldecode($v['value']) );
+ break;
+ case 'DAV::getlastmodified':
+ if (isset($v['value'])) {
+ $response['getlastmodified'] = $v['value'];
+ }
+ else {
+ $response['getlastmodified'] = '';
+ }
+ break;
+ case 'DAV::getetag':
+ $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
+ break;
+ case 'DAV::sync-token':
+ $this->synctoken[$this->calendar_url] = $v['value'];
+ ZLog::Write(LOGLEVEL_DEBUG, sprintf("CalDAVClient->GetSync: Response from '%s' includes sync token: '%s'", $this->calendar_url, $this->synctoken[$this->calendar_url]));
+ break;
+ }
+ }
- // Report sync-token support on initial sync
- if ($initial && $support_dav_sync && !isset($this->synctoken[$this->calendar_url])) {
- ZLog::Write(LOGLEVEL_WARN, 'CalDAVClient->GetSync(): no DAV::sync-token received; did you set CALDAV_SUPPORTS_SYNC correctly?');
- }
+ // Report sync-token support on initial sync
+ if ($initial && $support_dav_sync && !isset($this->synctoken[$this->calendar_url])) {
+ ZLog::Write(LOGLEVEL_WARN, 'CalDAVClient->GetSync(): no DAV::sync-token received; did you set CALDAV_SUPPORTS_SYNC correctly?');
+ }
- return $report;
- }
-
+ return $report;
+ }
};
diff -urN ../z-push-2.6.0.alpha1-210/src/lib/core/streamer.php ./src/lib/core/streamer.php
--- ../z-push-2.6.0.alpha1-210/src/lib/core/streamer.php 2023-03-17 22:44:44.895287000 +0200
+++ ./src/lib/core/streamer.php 2023-03-17 22:49:29.333286000 +0200
@@ -457,6 +457,10 @@
* @return string
*/
private function formatDate($ts, $type) {
+ if ('' === $ts) {
+ $ts = null;
+ }
+
if($type == self::STREAMER_TYPE_DATE)
return gmstrftime("%Y%m%dT%H%M%SZ", $ts);
else if($type == self::STREAMER_TYPE_DATE_DASHES)
diff -urN ../z-push-2.6.0.alpha1-210/src/lib/syncobjects/syncobject.php ./src/lib/syncobjects/syncobject.php
--- ../z-push-2.6.0.alpha1-210/src/lib/syncobjects/syncobject.php 2023-03-17 22:44:44.907054000 +0200
+++ ./src/lib/syncobjects/syncobject.php 2023-03-17 22:49:29.347503000 +0200
@@ -170,7 +170,7 @@
* @access public
* @return array with one property per line, key being the property instance variable name
*/
- public function EvaluateAndCompare($odo, $odoName = "", $supportedFields, $keyprefix = "", $recCount = 0) {
+ public function EvaluateAndCompare($odo, $supportedFields, $odoName = "", $keyprefix = "", $recCount = 0) {
if ($odo === false)
return false;
diff -urN ../z-push-2.6.0.alpha1-210/src/lib/utils/timezoneutil.php ./src/lib/utils/timezoneutil.php
--- ../z-push-2.6.0.alpha1-210/src/lib/utils/timezoneutil.php 2023-03-17 22:44:44.909419000 +0200
+++ ./src/lib/utils/timezoneutil.php 2023-03-17 22:49:29.349867000 +0200
@@ -1329,6 +1329,9 @@
//20110930 (Append T000000Z to the date, so it starts at midnight)
$date = date_create_from_format('Ymd\THis\Z', $value . "T000000Z", $tz);
}
+ if (!$date) {
+ ZLog::Write(LOGLEVEL_ERROR, sprintf("TimezoneUtil::MakeUTCDate(): failed to convert '%s' to date", $value));
+ }
return date_timestamp_get($date);
}
diff -urN ../z-push-2.6.0.alpha1-210/src/lib/wbxml/wbxmlencoder.php ./src/lib/wbxml/wbxmlencoder.php
--- ../z-push-2.6.0.alpha1-210/src/lib/wbxml/wbxmlencoder.php 2023-03-17 22:44:44.911398000 +0200
+++ ./src/lib/wbxml/wbxmlencoder.php 2023-03-17 22:49:29.351432000 +0200
@@ -169,6 +169,7 @@
*/
public function contentStream($stream, $asBase64 = false, $opaque = false) {
// Do not append filters to opaque data as it might contain null char
+ $stream = $this->stringToStream($stream);
if (!$asBase64 && !$opaque) {
stream_filter_register('replacenullchar', 'ReplaceNullcharFilter');
$rnc_filter = stream_filter_append($stream, 'replacenullchar');
@@ -551,4 +552,24 @@
}
ZLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: ". $data, false);
}
+
+ /**
+ * converts string to stream
+ *
+ * @param string $string
+ *
+ * @access private
+ * @return
+ */
+ private function stringToStream($string) {
+ if (!is_string($string)) {
+ return $string;
+ }
+
+ $stream = fopen('php://memory', 'r+');
+ fwrite($stream, $string);
+ rewind($stream);
+ return $stream;
+ }
+
}
diff -urN ../z-push-2.6.0.alpha1-210/src/z-push-top.php ./src/z-push-top.php
--- ../z-push-2.6.0.alpha1-210/src/z-push-top.php 2023-03-17 22:44:44.913830000 +0200
+++ ./src/z-push-top.php 2023-03-17 22:49:29.353889000 +0200
@@ -104,6 +104,7 @@
private $activeHosts = array();
private $activeUsers = array();
private $activeDevices = array();
+ private $activeBackend;
/**
* Constructor
@@ -127,6 +128,9 @@
$this->scrSize = array('width' => 80, 'height' => 24);
$this->pingInterval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 12;
+ // Identify the backend that will be loaded by z-push
+ $this->activeBackend = get_class(ZPush::GetBackend());
+
// get a TopCollector
$this->topCollector = new TopCollector();
}
@@ -304,8 +308,8 @@
$this->scrPrintAt($lc,0, "\033[1mZ-Push top live statistics\033[0m\t\t\t\t\t". @strftime("%d/%m/%Y %T")."\n"); $lc++;
$this->scrPrintAt($lc,0, sprintf("Open connections: %d\t\t\t\tUsers:\t %d\tZ-Push: %s ",count($this->activeConn),count($this->activeUsers), $this->getVersion())); $lc++;
- $this->scrPrintAt($lc,0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices),phpversion("mapi"))); $lc++;
- $this->scrPrintAt($lc,0, sprintf(" Hosts:\t %d", count($this->activeHosts))); $lc++;
+ $this->scrPrintAt($lc,0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices), phpversion("mapi"))); $lc++;
+ $this->scrPrintAt($lc,0, sprintf(" Hosts:\t %d\tBackend: %s", count($this->activeHosts), $this->$activeBackend)); $lc++;
$lc++;
$this->scrPrintAt($lc,0, "\033[4m". $this->getLine(array('pid'=>'PID', 'ip'=>'IP', 'user'=>'USER', 'command'=>'COMMAND', 'time'=>'TIME', 'devagent'=>'AGENT', 'devid'=>'DEVID', 'addinfo'=>'Additional Information')). str_repeat(" ",20)."\033[0m"); $lc++;