diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/BirthdayCalendarGenerator.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/BirthdayCalendarGenerator.php index 49793b0d4..11ae8be14 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/BirthdayCalendarGenerator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/BirthdayCalendarGenerator.php @@ -95,7 +95,7 @@ class BirthdayCalendarGenerator // We've seen clients (ez-vcard) putting "BDAY:" properties // without a value into vCards. If we come across those, we'll // skip them. - if (empty($object->BDAY->getValue())) { + if ('' === $object->BDAY->getValue()) { continue; } @@ -116,7 +116,7 @@ class BirthdayCalendarGenerator // Skip if we can't parse the BDAY value. try { $dateParts = DateTimeParser::parseVCardDateTime($object->BDAY->getValue()); - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { continue; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Cli.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Cli.php index 69af6f470..386cf2c73 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Cli.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Cli.php @@ -92,7 +92,7 @@ class Cli // @codeCoverageIgnoreEnd try { - list($options, $positional) = $this->parseArguments($argv); + [$options, $positional] = $this->parseArguments($argv); if (isset($options['q'])) { $this->quiet = true; @@ -110,27 +110,10 @@ class Cli return 0; case 'format': - switch ($value) { - // jcard/jcal documents - case 'jcard': - case 'jcal': - // specific document versions - case 'vcard21': - case 'vcard30': - case 'vcard40': - case 'icalendar20': - // specific formats - case 'json': - case 'mimedir': - // icalendar/vcard - case 'icalendar': - case 'vcard': - $this->format = $value; - break; - - default: - throw new \InvalidArgumentException('Unknown format: '.$value); - } + $this->format = match ($value) { + 'jcard', 'jcal', 'vcard21', 'vcard30', 'vcard40', 'icalendar20', 'json', 'mimedir', 'icalendar', 'vcard' => $value, + default => throw new \InvalidArgumentException('Unknown format: '.$value), + }; break; case 'pretty': $this->pretty = true; @@ -139,28 +122,11 @@ class Cli $this->forgiving = true; break; case 'inputformat': - switch ($value) { - // json formats - case 'jcard': - case 'jcal': - case 'json': - $this->inputFormat = 'json'; - break; - - // mimedir formats - case 'mimedir': - case 'icalendar': - case 'vcard': - case 'vcard21': - case 'vcard30': - case 'vcard40': - case 'icalendar20': - $this->inputFormat = 'mimedir'; - break; - - default: - throw new \InvalidArgumentException('Unknown format: '.$value); - } + $this->inputFormat = match ($value) { + 'jcard', 'jcal', 'json' => 'json', + 'mimedir', 'icalendar', 'vcard', 'vcard21', 'vcard30', 'vcard40', 'icalendar20' => 'mimedir', + default => throw new \InvalidArgumentException('Unknown format: '.$value), + }; break; default: throw new \InvalidArgumentException('Unknown option: '.$name); @@ -181,7 +147,7 @@ class Cli throw new \InvalidArgumentException('Too many arguments'); } - if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) { + if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'], true)) { throw new \InvalidArgumentException('Unknown command: '.$positional[0]); } } catch (\InvalidArgumentException $e) { @@ -201,14 +167,14 @@ class Cli } if (null === $this->inputFormat) { - if ('.json' === substr($this->inputPath, -5)) { + if (str_ends_with($this->inputPath, '.json')) { $this->inputFormat = 'json'; } else { $this->inputFormat = 'mimedir'; } } if (null === $this->format) { - if ('.json' === substr($this->outputPath, -5)) { + if (str_ends_with($this->outputPath, '.json')) { $this->format = 'json'; } else { $this->format = 'mimedir'; @@ -219,12 +185,13 @@ class Cli try { while ($input = $this->readInput()) { + // @phpstan-ignore method.dynamicName $returnCode = $this->$command($input); if (0 !== $returnCode) { $realCode = $returnCode; } } - } catch (EofException $e) { + } catch (EofException) { // end of file } catch (\Exception $e) { $this->log('Error: '.$e->getMessage(), 'red'); @@ -408,7 +375,7 @@ HELP } fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions)); } else { - fwrite($this->stdout, $vObj->serialize()); + fwrite($this->stdout, (string) $vObj->serialize()); } return 0; @@ -538,7 +505,7 @@ HELP $this->cWrite('red', ':'); if ($property instanceof Property\Binary) { - $this->cWrite('default', 'embedded binary stripped. ('.strlen($property->getValue()).' bytes)'); + $this->cWrite('default', 'embedded binary stripped. ('.strlen((string) $property->getValue()).' bytes)'); } else { $parts = $property->getParts(); $first1 = true; @@ -593,17 +560,17 @@ HELP $v = $argv[$ii]; - if ('--' === substr($v, 0, 2)) { + if (str_starts_with((string) $v, '--')) { // This is a long-form option. - $optionName = substr($v, 2); + $optionName = substr((string) $v, 2); $optionValue = true; if (strpos($optionName, '=')) { - list($optionName, $optionValue) = explode('=', $optionName); + [$optionName, $optionValue] = explode('=', $optionName); } $options[$optionName] = $optionValue; - } elseif ('-' === substr($v, 0, 1) && strlen($v) > 1) { + } elseif (str_starts_with((string) $v, '-') && strlen((string) $v) > 1) { // This is a short-form option. - foreach (str_split(substr($v, 1)) as $option) { + foreach (str_split(substr((string) $v, 1)) as $option) { $options[$option] = true; } } else { @@ -628,6 +595,9 @@ HELP if (!$this->parser) { if ('-' !== $this->inputPath) { $this->stdin = fopen($this->inputPath, 'r'); + if (false === $this->stdin) { + throw new InvalidDataException('Cannot open "'.$this->inputPath); + } } if ('mimedir' === $this->inputFormat) { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component.php index 95bde3d40..0849c5a8e 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component.php @@ -140,7 +140,7 @@ class Component extends Node // If there's no dot in the name, it's an exact property name, // we can just wipe out all those properties. // - if (false === strpos($item, '.')) { + if (!str_contains($item, '.')) { unset($this->children[strtoupper($item)]); return; @@ -210,8 +210,8 @@ class Component extends Node { $group = null; $name = strtoupper($name); - if (false !== strpos($name, '.')) { - list($group, $name) = explode('.', $name, 2); + if (str_contains($name, '.')) { + [$group, $name] = explode('.', $name, 2); } if ('' === $name) { $name = null; @@ -222,16 +222,14 @@ class Component extends Node if (is_null($group)) { return $result; - } else { - // If we have a group filter as well, we need to narrow it down - // more. - return array_filter( - $result, - function ($child) use ($group) { - return $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group; - } - ); } + + // If we have a group filter as well, we need to narrow it down + // more. + return array_filter( + $result, + fn ($child) => $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group + ); } // If we got to this point, it means there was no 'name' specified for @@ -368,7 +366,7 @@ class Component extends Node $writer->startElement(strtolower($this->name)); - if (!empty($properties)) { + if ([] !== $properties) { $writer->startElement('properties'); foreach ($properties as $property) { @@ -378,7 +376,7 @@ class Component extends Node $writer->endElement(); } - if (!empty($components)) { + if ([] !== $components) { $writer->startElement('components'); foreach ($components as $component) { @@ -422,13 +420,12 @@ class Component extends Node $matches = $this->select($name); if (0 === count($matches)) { return null; - } else { - $firstMatch = current($matches); - /* @var $firstMatch Property */ - $firstMatch->setIterator(new ElementList(array_values($matches))); - - return $firstMatch; } + $firstMatch = current($matches); + /* @var $firstMatch Property */ + $firstMatch->setIterator(new ElementList(array_values($matches))); + + return $firstMatch; } /** @@ -542,7 +539,7 @@ class Component extends Node $messages = []; foreach ($this->children() as $child) { - $name = strtoupper($child->name); + $name = strtoupper((string) $child->name); if (!isset($propertyCounters[$name])) { $propertyCounters[$name] = 1; } else { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php index abe61768e..47474dfbf 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php @@ -32,9 +32,9 @@ class VAlarm extends VObject\Component public function getEffectiveTriggerTime(): \DateTimeImmutable { $trigger = $this->TRIGGER; - if (!isset($trigger['VALUE']) || ($trigger['VALUE'] && 'DURATION' === strtoupper($trigger['VALUE']))) { + if (!isset($trigger['VALUE']) || ($trigger['VALUE'] && 'DURATION' === strtoupper((string) $trigger['VALUE']))) { $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); - $related = (isset($trigger['RELATED']) && 'END' == strtoupper($trigger['RELATED'])) ? 'END' : 'START'; + $related = (isset($trigger['RELATED']) && 'END' === strtoupper($trigger['RELATED'])) ? 'END' : 'START'; /** @var VEvent|VTodo $parentComponent */ $parentComponent = $this->parent; @@ -96,9 +96,9 @@ class VAlarm extends VObject\Component } return false; - } else { - return $start <= $effectiveTrigger && $end > $effectiveTrigger; } + + return $start <= $effectiveTrigger && $end > $effectiveTrigger; } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAvailability.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAvailability.php index ff6e9bd65..a4db47642 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAvailability.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAvailability.php @@ -33,7 +33,7 @@ class VAvailability extends VObject\Component */ public function isInTimeRange(\DateTimeInterface $start, \DateTimeInterface $end): bool { - list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd(); + [$effectiveStart, $effectiveEnd] = $this->getEffectiveStartEnd(); return (is_null($effectiveStart) || $start < $effectiveEnd) diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCalendar.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCalendar.php index 7b6e66e88..879fcee29 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCalendar.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCalendar.php @@ -145,6 +145,10 @@ class VCalendar extends VObject\Document // Additions from draft-daboo-calendar-availability-05 'BUSYTYPE' => Property\Text::class, + + // RFC 7986 properties + 'NAME' => Property\FlatText::class, + 'COLOR' => Property\FlatText::class, ]; /** @@ -164,7 +168,7 @@ class VCalendar extends VObject\Document * * @param string|null $componentName filter by component name * - * @return VObject\Component[] + * @return Component[] */ public function getBaseComponents(?string $componentName = null): array { @@ -327,7 +331,7 @@ class VCalendar extends VObject\Document foreach ($recurringEvents as $events) { try { $it = new EventIterator($events, null, $timeZone); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. @@ -379,6 +383,18 @@ class VCalendar extends VObject\Document 'CALSCALE' => '?', 'METHOD' => '?', + + // RFC 7986 calendar properties + 'UID' => '?', + 'LAST-MODIFIED' => '?', + 'URL' => '?', + 'REFRESH-INTERVAL' => '?', + 'SOURCE' => '?', + 'COLOR' => '?', + 'NAME' => '*', + 'DESCRIPTION' => '*', + 'CATEGORIES' => '*', + 'IMAGE' => '*', ]; } @@ -424,7 +440,7 @@ class VCalendar extends VObject\Document if ($child instanceof Component) { ++$componentsFound; - if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) { + if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'], true)) { continue; } $componentTypes[] = $child->name; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php index 9ec4c88cc..62223aaeb 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php @@ -132,6 +132,13 @@ class VCard extends VObject\Document 'ORG-DIRECTORY' => VObject\Property\FlatText::class, 'X-CRYPTO' => VObject\Property\XCrypto::class, + + // rfc9554 properties + 'CREATED' => VObject\Property\VCard\TimeStamp::class, + 'GRAMGENDER' => VObject\Property\FlatText::class, + 'LANGUAGE' => VObject\Property\VCard\LanguageTag::class, + 'PRONOUNS' => VObject\Property\FlatText::class, + 'SOCIALPROFILE' => VObject\Property\Uri::class, ]; /** @@ -326,6 +333,11 @@ class VCard extends VObject\Document 'CALURI' => '*', 'CATEGORIES' => '*', 'CLIENTPIDMAP' => '*', + 'CREATED' => '?', + 'GRAMGENDER' => '*', + 'LANGUAGE' => '?', + 'PRONOUNS' => '*', + 'SOCIALPROFILE' => '*', 'EMAIL' => '*', 'FBURL' => '*', 'IMPP' => '*', @@ -411,6 +423,34 @@ class VCard extends VObject\Document return null; } + /** + * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL). + * + * This function will return null if the exact property list does not exist. + * + * For example to get the property of `TEL;TYPE=HOME,CELL` + * you would call `getByTypes('TEL', ['HOME', 'CELL'])` + * + * @param string[] $types + * + * @return \ArrayAccess|array|null + */ + public function getByTypes(string $propertyName, array $types) + { + $types = array_map(strtolower(...), $types); + foreach ($this->select($propertyName) as $field) { + if (isset($field['TYPE'])) { + $parts = array_map(strtolower(...), $field['TYPE']->getParts()); + + if (!array_diff($types, $parts) && !array_diff($parts, $types)) { + return $field; + } + } + } + + return null; + } + /** * This method should return a list of default property values. */ @@ -453,11 +493,9 @@ class VCard extends VObject\Document $propertiesByGroup = []; foreach ($this->children() as $property) { - $group = $property->group; + $group = $property->group ?? ''; - if (!isset($propertiesByGroup[$group])) { - $propertiesByGroup[$group] = []; - } + $propertiesByGroup[$group] ??= []; $propertiesByGroup[$group][] = $property; } @@ -465,9 +503,9 @@ class VCard extends VObject\Document $writer->startElement(strtolower($this->name)); foreach ($propertiesByGroup as $group => $properties) { - if (!empty($group)) { + if ('' !== $group) { $writer->startElement('group'); - $writer->writeAttribute('name', strtolower($group)); + $writer->writeAttribute('name', strtolower((string) $group)); } foreach ($properties as $property) { @@ -487,7 +525,7 @@ class VCard extends VObject\Document } } - if (!empty($group)) { + if ('' !== $group) { $writer->endElement(); } } @@ -503,7 +541,7 @@ class VCard extends VObject\Document $className = parent::getClassNameForPropertyName($propertyName); // In vCard 4, BINARY no longer exists, and we need URI instead. - if (VObject\Property\Binary::class == $className && self::VCARD40 === $this->getDocumentType()) { + if (VObject\Property\Binary::class === $className && self::VCARD40 === $this->getDocumentType()) { return VObject\Property\Uri::class; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php index e29e56322..b1a762690 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php @@ -41,10 +41,10 @@ class VEvent extends VObject\Component */ public function isInTimeRange(\DateTimeInterface $start, \DateTimeInterface $end): bool { - if ($this->RRULE) { + if ($this->RRULE || $this->RDATE) { try { $it = new EventIterator($this, null, $start->getTimezone()); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // If we've caught this exception, there are no instances // for the event that fall into the specified time-range. return false; @@ -117,6 +117,7 @@ class VEvent extends VObject\Component 'DTSTART' => $hasMethod ? '?' : '1', 'CLASS' => '?', 'CREATED' => '?', + 'COLOR' => '?', 'DESCRIPTION' => '?', 'GEO' => '?', 'LAST-MODIFIED' => '?', @@ -137,7 +138,9 @@ class VEvent extends VObject\Component 'ATTENDEE' => '*', 'CATEGORIES' => '*', 'COMMENT' => '*', + 'CONFERENCE' => '*', 'CONTACT' => '*', + 'IMAGE' => '*', 'EXDATE' => '*', 'REQUEST-STATUS' => '*', 'RELATED-TO' => '*', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VFreeBusy.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VFreeBusy.php index a9891c92c..623652ff9 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VFreeBusy.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VFreeBusy.php @@ -41,7 +41,7 @@ class VFreeBusy extends VObject\Component // Every period is formatted as [start]/[end]. The start is an // absolute UTC time, the end may be an absolute UTC time, or // duration (relative) value. - list($busyStart, $busyEnd) = explode('/', $period); + [$busyStart, $busyEnd] = explode('/', $period); $busyStart = VObject\DateTimeParser::parse($busyStart); $busyEnd = VObject\DateTimeParser::parse($busyEnd); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VJournal.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VJournal.php index 30e40adf7..79c14dece 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VJournal.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VJournal.php @@ -62,6 +62,7 @@ class VJournal extends VObject\Component 'CLASS' => '?', 'CREATED' => '?', + 'COLOR' => '?', 'DTSTART' => '?', 'LAST-MODIFIED' => '?', 'ORGANIZER' => '?', @@ -78,6 +79,7 @@ class VJournal extends VObject\Component 'CATEGORIES' => '*', 'COMMENT' => '*', 'CONTACT' => '*', + 'IMAGE' => '*', 'DESCRIPTION' => '*', 'EXDATE' => '*', 'RELATED-TO' => '*', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VTodo.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VTodo.php index cab672ce5..e0ed15de0 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VTodo.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VTodo.php @@ -54,9 +54,9 @@ class VTodo extends VObject\Component return ($start < $due || $start <= $dtstart) && ($end > $dtstart || $end >= $due); - } else { - return $start <= $dtstart && $end > $dtstart; } + + return $start <= $dtstart && $end > $dtstart; } if ($due) { return $start < $due && $end >= $due; @@ -98,6 +98,7 @@ class VTodo extends VObject\Component 'CLASS' => '?', 'COMPLETED' => '?', 'CREATED' => '?', + 'COLOR' => '?', 'DESCRIPTION' => '?', 'DTSTART' => '?', 'GEO' => '?', @@ -120,7 +121,9 @@ class VTodo extends VObject\Component 'ATTENDEE' => '*', 'CATEGORIES' => '*', 'COMMENT' => '*', + 'CONFERENCE' => '*', 'CONTACT' => '*', + 'IMAGE' => '*', 'EXDATE' => '*', 'REQUEST-STATUS' => '*', 'RELATED-TO' => '*', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/DateTimeParser.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/DateTimeParser.php index e8777b7fe..fad973a8c 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/DateTimeParser.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/DateTimeParser.php @@ -42,7 +42,7 @@ class DateTimeParser try { $date = new \DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3].' '.$matches[4].':'.$matches[5].':'.$matches[6], $tz); - } catch (\Exception $e) { + } catch (\Exception) { throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: '.$dt); } @@ -63,13 +63,11 @@ class DateTimeParser throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date); } - if (is_null($tz)) { - $tz = new \DateTimeZone('UTC'); - } + $tz ??= new \DateTimeZone('UTC'); try { $date = new \DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3], $tz); - } catch (\Exception $e) { + } catch (\Exception) { throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date); } @@ -205,9 +203,9 @@ class DateTimeParser return self::parseDuration($date); } elseif (8 === strlen($date)) { return self::parseDate($date, $referenceTz); - } else { - return self::parseDateTime($date, $referenceTz); } + + return self::parseDateTime($date, $referenceTz); } /** @@ -329,7 +327,7 @@ class DateTimeParser $result = []; foreach ($parts as $part) { - if (empty($matches[$part])) { + if (!array_key_exists($part, $matches) || '' === $matches[$part]) { $result[$part] = null; } elseif ('-' === $matches[$part] || '--' === $matches[$part]) { $result[$part] = null; @@ -424,7 +422,7 @@ class DateTimeParser $result = []; foreach ($parts as $part) { - if (empty($matches[$part])) { + if (!array_key_exists($part, $matches) || '' === $matches[$part]) { $result[$part] = null; } elseif ('-' === $matches[$part]) { $result[$part] = null; @@ -541,7 +539,7 @@ class DateTimeParser $parts['year0'] = &$parts['year']; foreach ($parts as $part => &$value) { - if (!empty($matches[$part])) { + if (array_key_exists($part, $matches) && '' !== $matches[$part]) { $value = $matches[$part]; } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Document.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Document.php index 02eaf13c7..8319b49be 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Document.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Document.php @@ -118,10 +118,10 @@ abstract class Document extends Component public function create(string $name) { if (isset(static::$componentMap[strtoupper($name)])) { - return call_user_func_array([$this, 'createComponent'], func_get_args()); - } else { - return call_user_func_array([$this, 'createProperty'], func_get_args()); + return $this->createComponent(...func_get_args()); } + + return $this->createProperty(...func_get_args()); } /** @@ -146,9 +146,7 @@ abstract class Document extends Component if (isset(static::$componentMap[$name])) { $class = static::$componentMap[$name]; } - if (is_null($children)) { - $children = []; - } + $children ??= []; return new $class($this, $name, $children, $defaults); } @@ -180,26 +178,29 @@ abstract class Document extends Component $class = null; + // If a VALUE parameter is supplied, we have to use that + // According to https://datatracker.ietf.org/doc/html/rfc5545#section-3.2.20 + // If the property's value is the default value type, then this + // parameter need not be specified. However, if the property's + // default value type is overridden by some other allowable value + // type, then this parameter MUST be specified. + if (!$valueType) { + $valueType = $parameters['VALUE'] ?? null; + } + if ($valueType) { // The valueType argument comes first to figure out the correct // class. $class = $this->getClassNameForPropertyValue($valueType); } - if (is_null($class)) { - // If a VALUE parameter is supplied, we should use that. - if (isset($parameters['VALUE'])) { - $class = $this->getClassNameForPropertyValue($parameters['VALUE']); - if (is_null($class)) { - throw new InvalidDataException('Unsupported VALUE parameter for '.$name.' property. You supplied "'.$parameters['VALUE'].'"'); - } - } else { - $class = $this->getClassNameForPropertyName($name); - } - } - if (is_null($parameters)) { - $parameters = []; - } + // If the value parameter is not set or set to something we do not recognize + // we do not attempt to interpret or parse the datass value as specified in + // https://datatracker.ietf.org/doc/html/rfc5545#section-3.2.20 + // So when we so far did not get a class-name, we use the default for the property + $class ??= $this->getClassNameForPropertyName($name); + + $parameters ??= []; return new $class($this, $name, $value, $parameters, $group, $lineIndex, $lineString); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyData.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyData.php index 405a5886d..6cdf2b937 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyData.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyData.php @@ -11,25 +11,19 @@ namespace Sabre\VObject; */ class FreeBusyData { - /** - * Start timestamp. - */ - protected int $start; - - /** - * End timestamp. - */ - protected int $end; - /** * A list of free-busy times. */ protected array $data; - public function __construct(int $start, int $end) + public function __construct(/** + * Start timestamp. + */ + protected int $start, /** + * End timestamp. + */ + protected int $end) { - $this->start = $start; - $this->end = $end; $this->data = []; $this->data[] = [ diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyGenerator.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyGenerator.php index 424826034..9eec5c8d7 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyGenerator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/FreeBusyGenerator.php @@ -79,9 +79,7 @@ class FreeBusyGenerator if ($objects) { $this->setObjects($objects); } - if (is_null($timeZone)) { - $timeZone = new \DateTimeZone('UTC'); - } + $timeZone ??= new \DateTimeZone('UTC'); $this->setTimeZone($timeZone); } @@ -217,7 +215,7 @@ class FreeBusyGenerator $new = []; foreach ($old as $vavail) { - list($compStart, $compEnd) = $vavail->getEffectiveStartEnd(); + [$compStart, $compEnd] = $vavail->getEffectiveStartEnd(); // We don't care about date-times that are earlier or later than the // start and end of the freebusy report, so this gets normalized @@ -237,7 +235,7 @@ class FreeBusyGenerator // Going through our existing list of components to see if there's // a higher priority component that already fully covers this one. foreach ($new as $higherVavail) { - list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd(); + [$higherStart, $higherEnd] = $higherVavail->getEffectiveStartEnd(); if ( (is_null($higherStart) || $higherStart < $compStart) && (is_null($higherEnd) || $higherEnd > $compEnd) @@ -259,7 +257,7 @@ class FreeBusyGenerator // priority components to override the lower ones. foreach (array_reverse($new) as $vavail) { $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE'; - list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd(); + [$vavailStart, $vavailEnd] = $vavail->getEffectiveStartEnd(); // Making the component size no larger than the requested free-busy // report range. @@ -281,7 +279,7 @@ class FreeBusyGenerator // Looping over the AVAILABLE components. if (isset($vavail->AVAILABLE)) { foreach ($vavail->AVAILABLE as $available) { - list($availStart, $availEnd) = $available->getEffectiveStartEnd(); + [$availStart, $availEnd] = $available->getEffectiveStartEnd(); $fbData->add( $availStart->getTimeStamp(), $availEnd->getTimeStamp(), @@ -362,7 +360,7 @@ class FreeBusyGenerator if ($component->RRULE) { try { $iterator = new EventIterator($object, (string) $component->UID, $this->timeZone); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. @@ -437,12 +435,12 @@ class FreeBusyGenerator continue; } - $values = explode(',', $freebusy); + $values = explode(',', (string) $freebusy); foreach ($values as $value) { - list($startTime, $endTime) = explode('/', $value); + [$startTime, $endTime] = explode('/', $value); $startTime = DateTimeParser::parseDateTime($startTime); - if ('P' === substr($endTime, 0, 1) || '-P' === substr($endTime, 0, 2)) { + if (str_starts_with($endTime, 'P') || str_starts_with($endTime, '-P')) { $duration = DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime = $endTime->add($duration); @@ -507,7 +505,7 @@ class FreeBusyGenerator $vfreebusy->add($dtstamp); foreach ($fbData->getData() as $busyTime) { - $busyType = strtoupper($busyTime['type']); + $busyType = strtoupper((string) $busyTime['type']); // Ignoring all the FREE parts, because those are already assumed. if ('FREE' === $busyType) { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Broker.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Broker.php index 76ee0c71e..19de8b16b 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Broker.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Broker.php @@ -228,7 +228,7 @@ class Broker $eventInfo = $oldEventInfo; - if (in_array($eventInfo['organizer'], $userHref)) { + if (in_array($eventInfo['organizer'], $userHref, true)) { // This is an organizer deleting the event. $eventInfo['attendees'] = []; // Increasing the sequence, but only if the organizer deleted @@ -237,7 +237,7 @@ class Broker } else { // This is an attendee deleting the event. foreach ($eventInfo['attendees'] as $key => $attendee) { - if (in_array($attendee['href'], $userHref)) { + if (in_array($attendee['href'], $userHref, true)) { $eventInfo['attendees'][$key]['instances'] = ['master' => ['id' => 'master', 'partstat' => 'DECLINED'], ]; } @@ -246,16 +246,29 @@ class Broker $baseCalendar = $oldCalendar; } - if (in_array($eventInfo['organizer'], $userHref)) { + // Check if the user is the organizer + if (in_array($eventInfo['organizer'], $userHref, true)) { return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo); - } elseif ($oldCalendar) { - // We need to figure out if the user is an attendee, but we're only - // doing so if there's an oldCalendar, because we only want to - // process updates, not creation of new events. - foreach ($eventInfo['attendees'] as $attendee) { - if (in_array($attendee['href'], $userHref)) { + } + + // Check if the user is an attendee + foreach ($eventInfo['attendees'] as $attendee) { + if (in_array($attendee['href'], $userHref, true)) { + // If this is a event update, we always generate a reply + if ($oldCalendar) { return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); } + + // If this is a new event, we only generate a reply if the participation status is set + foreach ($attendee['instances'] as $instance) { + if (isset($instance['partstat']) && 'NEEDS-ACTION' !== $instance['partstat']) { + // Attendee has responded (ACCEPTED/DECLINED/TENTATIVE) - generate REPLY + return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); + } + } + + // User is attendee but no response to process + break; } } @@ -341,7 +354,7 @@ class Broker $instances[$recurId] = $attendee['PARTSTAT']->getValue(); if (isset($vevent->{'REQUEST-STATUS'})) { $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue(); - list($requestStatus) = explode(';', $requestStatus); + [$requestStatus] = explode(';', $requestStatus); } } @@ -479,7 +492,6 @@ class Broker } $messages = []; - foreach ($attendees as $attendee) { // An organizer can also be an attendee. We should not generate any // messages for those. @@ -554,7 +566,7 @@ class Broker $message->significantChange = 'REQUEST' === $attendee['forceSend'] - || count($oldAttendeeInstances) != count($newAttendeeInstances) + || count($oldAttendeeInstances) !== count($newAttendeeInstances) || count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0 || $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash']; @@ -564,9 +576,9 @@ class Broker // We need to find a list of events that the attendee // is not a part of to add to the list of exceptions. $exceptions = []; - foreach ($eventInfo['instances'] as $instanceId => $vevent) { - if (!isset($attendee['newInstances'][$instanceId])) { - $exceptions[] = $instanceId; + foreach ($eventInfo['instances'] as $eventInstanceId => $vevent) { + if (!isset($attendee['newInstances'][$eventInstanceId])) { + $exceptions[] = $eventInstanceId; } } @@ -580,6 +592,9 @@ class Broker )); } else { $currentEvent->EXDATE = $exceptions; + if ($currentEvent->DTSTART['TZID']) { + $currentEvent->EXDATE['TZID'] = clone $currentEvent->DTSTART['TZID']; + } } } @@ -588,15 +603,13 @@ class Broker unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']); unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']); - foreach ($currentEvent->ATTENDEE as $attendee) { - unset($attendee['SCHEDULE-FORCE-SEND']); - unset($attendee['SCHEDULE-STATUS']); + foreach ($currentEvent->ATTENDEE as $currentEventAttendee) { + unset($currentEventAttendee['SCHEDULE-FORCE-SEND']); + unset($currentEventAttendee['SCHEDULE-STATUS']); // We're adding PARTSTAT=NEEDS-ACTION to ensure that // iOS shows an "Inbox Item" - if (!isset($attendee['PARTSTAT'])) { - $attendee['PARTSTAT'] = 'NEEDS-ACTION'; - } + $currentEventAttendee['PARTSTAT'] ??= 'NEEDS-ACTION'; } } @@ -633,7 +646,12 @@ class Broker return []; } - $oldInstances = !empty($oldEventInfo['attendees'][$attendee]['instances']) ? + $oldInstances = + is_array($oldEventInfo['attendees']) + && array_key_exists($attendee, $oldEventInfo['attendees']) + && is_array($oldEventInfo['attendees'][$attendee]) + && array_key_exists('instances', $oldEventInfo['attendees'][$attendee]) + && is_array($oldEventInfo['attendees'][$attendee]['instances']) ? $oldEventInfo['attendees'][$attendee]['instances'] : []; @@ -664,7 +682,7 @@ class Broker // We only need to do that though, if the master event is not declined. if (isset($instances['master']) && 'DECLINED' !== $instances['master']['newstatus']) { foreach ($eventInfo['exdate'] as $exDate) { - if (!in_array($exDate, $oldEventInfo['exdate'])) { + if (!in_array($exDate, $oldEventInfo['exdate'] ?? [], true)) { if (isset($instances[$exDate])) { $instances[$exDate]['newstatus'] = 'DECLINED'; } else { @@ -707,7 +725,7 @@ class Broker $hasReply = false; foreach ($instances as $instance) { - if ($instance['oldstatus'] == $instance['newstatus'] && 'REPLY' !== $eventInfo['organizerForceSend']) { + if ($instance['oldstatus'] === $instance['newstatus'] && 'REPLY' !== $eventInfo['organizerForceSend']) { // Skip continue; } @@ -739,7 +757,7 @@ class Broker // EXDATE $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); // Treat is as a DATE field - if (strlen($instance['id']) <= 8) { + if (strlen((string) $instance['id']) <= 8) { $event->add('DTSTART', $dt, ['VALUE' => 'DATE']); } else { $event->add('DTSTART', $dt); @@ -751,7 +769,7 @@ class Broker if ('master' !== $instance['id']) { $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); // Treat is as a DATE field - if (strlen($instance['id']) <= 8) { + if (strlen((string) $instance['id']) <= 8) { $event->add('RECURRENCE-ID', $dt, ['VALUE' => 'DATE']); } else { $event->add('RECURRENCE-ID', $dt); @@ -774,9 +792,9 @@ class Broker $message->message = $icalMsg; return [$message]; - } else { - return []; } + + return []; } /** @@ -843,7 +861,7 @@ class Broker $organizer = $vevent->ORGANIZER->getNormalizedValue(); $organizerName = $vevent->ORGANIZER['CN'] ?? null; } else { - if (strtoupper($organizer) !== strtoupper($vevent->ORGANIZER->getNormalizedValue())) { + if (strtoupper($organizer) !== strtoupper((string) $vevent->ORGANIZER->getNormalizedValue())) { throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.'); } } @@ -869,7 +887,7 @@ class Broker foreach ($vevent->select('RRULE') as $rr) { foreach ($rr->getParts() as $key => $val) { // ignore default values (https://github.com/sabre-io/vobject/issues/126) - if ('INTERVAL' === $key && 1 == $val) { + if ('INTERVAL' === $key && '1' === (string) $val) { continue; } if (is_array($val)) { @@ -881,7 +899,7 @@ class Broker sort($rrule); } if (isset($vevent->STATUS)) { - $status = strtoupper($vevent->STATUS->getValue()); + $status = strtoupper((string) $vevent->STATUS->getValue()); } $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; @@ -892,11 +910,14 @@ class Broker $timezone = $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimeZone(); } } + + $instances[$recurId] = $vevent; + if (isset($vevent->ATTENDEE)) { foreach ($vevent->ATTENDEE as $attendee) { if ($this->scheduleAgentServerRules && isset($attendee['SCHEDULE-AGENT']) - && 'CLIENT' === strtoupper($attendee['SCHEDULE-AGENT']->getValue()) + && 'CLIENT' === strtoupper((string) $attendee['SCHEDULE-AGENT']->getValue()) ) { continue; } @@ -930,10 +951,10 @@ class Broker ]; } } - $instances[$recurId] = $vevent; } foreach ($this->significantChangeProperties as $prop) { + // @phpstan-ignore property.dynamicName if (isset($vevent->$prop)) { $propertyValues = $vevent->select($prop); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Message.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Message.php index 9aa93996a..505e07f25 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Message.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/ITip/Message.php @@ -32,12 +32,12 @@ class Message * Contains the ITip method, which is something like REQUEST, REPLY or * CANCEL. */ - public ?string $method; + public ?string $method = null; /** * The current sequence number for the event. */ - public ?int $sequence; + public ?int $sequence = null; /** * The senders' email address. @@ -52,7 +52,7 @@ class Message * The name of the sender. This is often populated from a CN parameter from * either the ORGANIZER or ATTENDEE, depending on the message. */ - public ?string $senderName; + public ?string $senderName = null; /** * The recipient's email address. @@ -63,7 +63,7 @@ class Message * The name of the recipient. This is usually populated with the CN * parameter from the ATTENDEE or ORGANIZER property, if it's available. */ - public ?string $recipientName; + public ?string $recipientName = null; /** * After the message has been delivered, this should contain a string such @@ -107,10 +107,9 @@ class Message { if (!$this->scheduleStatus) { return false; - } else { - list($scheduleStatus) = explode(';', $this->scheduleStatus); - - return $scheduleStatus; } + [$scheduleStatus] = explode(';', $this->scheduleStatus); + + return $scheduleStatus; } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php index 0dbe7cfad..47889a2ab 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php @@ -42,7 +42,7 @@ abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable, \Js /** * Reference to the parent object, if this is not the top object. */ - public ?Node $parent; + public ?Node $parent = null; /** * Iterator override. @@ -52,7 +52,7 @@ abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable, \Js /** * The root document. */ - protected ?Component $root; + protected ?Component $root = null; /** * Serializes the node into a mimedir format. diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/PHPUnitAssertions.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/PHPUnitAssertions.php index 1976c3afd..aad5438a5 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/PHPUnitAssertions.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/PHPUnitAssertions.php @@ -41,7 +41,7 @@ trait PHPUnitAssertions $input = Reader::read($input); } if (!$input instanceof Component) { - $this->fail('Input must be a string, stream or VObject component'); + self::fail('Input must be a string, stream or VObject component'); } unset($input->PRODID); if ($input instanceof Component\VCalendar && 'GREGORIAN' === (string) $input->CALSCALE) { @@ -51,23 +51,28 @@ trait PHPUnitAssertions return $input; }; - $expected = $getObj($expected)->serialize(); - $actual = $getObj($actual)->serialize(); + /** + * @var string $expectedSerialized + */ + $expectedSerialized = $getObj($expected)->serialize(); + $actualSerialized = $getObj($actual)->serialize(); // Finding wildcards in expected. - preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expected, $matches, PREG_SET_ORDER); + $result = preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expectedSerialized, $matches, PREG_SET_ORDER); + + self::assertNotFalse($result); foreach ($matches as $match) { - $actual = preg_replace( + $actualSerialized = preg_replace( '|^'.preg_quote($match[1], '|').':(.*)\r$|m', $match[1].':**ANY**'."\r", - $actual + (string) $actualSerialized ); } - $this->assertEquals( - $expected, - $actual, + self::assertEquals( + $expectedSerialized, + $actualSerialized, $message ); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parameter.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parameter.php index c5217f1dc..c97e76ea9 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parameter.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parameter.php @@ -16,7 +16,7 @@ use Sabre\Xml; * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -class Parameter extends Node +class Parameter extends Node implements \Stringable { /** * Parameter name. @@ -74,83 +74,12 @@ class Parameter extends Node */ public static function guessParameterNameByValue(string $value): string { - switch (strtoupper($value)) { - // Encodings - case '7-BIT': - case 'QUOTED-PRINTABLE': - case 'BASE64': - $name = 'ENCODING'; - break; - - // Common types - case 'WORK': - case 'HOME': - case 'PREF': - // Delivery Label Type - case 'DOM': - case 'INTL': - case 'POSTAL': - case 'PARCEL': - // Telephone types - case 'VOICE': - case 'FAX': - case 'MSG': - case 'CELL': - case 'PAGER': - case 'BBS': - case 'MODEM': - case 'CAR': - case 'ISDN': - case 'VIDEO': - // EMAIL types (lol) - case 'AOL': - case 'APPLELINK': - case 'ATTMAIL': - case 'CIS': - case 'EWORLD': - case 'INTERNET': - case 'IBMMAIL': - case 'MCIMAIL': - case 'POWERSHARE': - case 'PRODIGY': - case 'TLX': - case 'X400': - // Photo / Logo format types - case 'GIF': - case 'CGM': - case 'WMF': - case 'BMP': - case 'DIB': - case 'PICT': - case 'TIFF': - case 'PDF': - case 'PS': - case 'JPEG': - case 'MPEG': - case 'MPEG2': - case 'AVI': - case 'QTIME': - // Sound Digital Audio Type - case 'WAVE': - case 'PCM': - case 'AIFF': - // Key types - case 'X509': - case 'PGP': - $name = 'TYPE'; - break; - - // Value types - case 'INLINE': - case 'URL': - case 'CONTENT-ID': - case 'CID': - $name = 'VALUE'; - break; - - default: - $name = ''; - } + $name = match (strtoupper($value)) { + '7-BIT', 'QUOTED-PRINTABLE', 'BASE64' => 'ENCODING', + 'WORK', 'HOME', 'PREF', 'DOM', 'INTL', 'POSTAL', 'PARCEL', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO', 'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD', 'INTERNET', 'IBMMAIL', 'MCIMAIL', 'POWERSHARE', 'PRODIGY', 'TLX', 'X400', 'GIF', 'CGM', 'WMF', 'BMP', 'DIB', 'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME', 'WAVE', 'PCM', 'AIFF', 'X509', 'PGP' => 'TYPE', + 'INLINE', 'URL', 'CONTENT-ID', 'CID' => 'VALUE', + default => '', + }; return $name; } @@ -177,9 +106,9 @@ class Parameter extends Node { if (is_array($this->value)) { return implode(',', $this->value); - } else { - return $this->value; } + + return $this->value; } /** @@ -201,9 +130,9 @@ class Parameter extends Node return $this->value; } elseif (is_null($this->value)) { return []; - } else { - return [$this->value]; } + + return [$this->value]; } /** @@ -234,7 +163,8 @@ class Parameter extends Node { return in_array( strtolower($value), - array_map('strtolower', (array) $this->value) + array_map(strtolower(...), (array) $this->value), + true ); } @@ -278,20 +208,19 @@ class Parameter extends Node // added + as well. if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) { return $out.$item; - } else { - // Enclosing in double-quotes, and using RFC6868 for encoding any - // special characters - $out .= '"'.strtr( - $item, - [ - '^' => '^^', - "\n" => '^n', - '"' => '^\'', - ] - ).'"'; - - return $out; } + // Enclosing in double-quotes, and using RFC6868 for encoding any + // special characters + $out .= '"'.strtr( + $item, + [ + '^' => '^^', + "\n" => '^n', + '"' => '^\'', + ] + ).'"'; + + return $out; } ); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Json.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Json.php index 9729afe86..e54a61c7e 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Json.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Json.php @@ -27,12 +27,12 @@ class Json extends Parser /** * The input data. */ - protected ?array $input; + protected ?array $input = null; /** * Root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * This method starts the parsing process. @@ -61,16 +61,11 @@ class Json extends Parser $this->options = $options; } - switch ($this->input[0]) { - case 'vcalendar': - $this->root = new VCalendar([], false); - break; - case 'vcard': - $this->root = new VCard([], false); - break; - default: - throw new ParseException('The root component must either be a vcalendar, or a vcard'); - } + $this->root = match ($this->input[0]) { + 'vcalendar' => new VCalendar([], false), + 'vcard' => new VCard([], false), + default => throw new ParseException('The root component must either be a vcalendar, or a vcard'), + }; foreach ($this->input[1] as $prop) { $this->root->add($this->parseProperty($prop)); } @@ -97,17 +92,13 @@ class Json extends Parser $self = $this; $properties = array_map( - function ($jProp) use ($self) { - return $self->parseProperty($jProp); - }, + $self->parseProperty(...), $jComp[1] ); if (isset($jComp[2])) { $components = array_map( - function ($jComp) use ($self) { - return $self->parseComponent($jComp); - }, + $self->parseComponent(...), $jComp[2] ); } else { @@ -134,7 +125,7 @@ class Json extends Parser $valueType, ) = $jProp; - $propertyName = strtoupper($propertyName); + $propertyName = strtoupper((string) $propertyName); // This is the default class we would be using if we didn't know the // value type. We're using this value later in this function. @@ -144,7 +135,7 @@ class Json extends Parser $value = array_slice($jProp, 3); - $valueType = strtoupper($valueType); + $valueType = strtoupper((string) $valueType); if (isset($parameters['group'])) { $propertyName = $parameters['group'].'.'.$propertyName; @@ -169,7 +160,7 @@ class Json extends Parser // If the value type we received (e.g.: TEXT) was not the default value // type for the given property (e.g.: BDAY), we need to add a VALUE= // parameter. - if ($defaultPropertyClass !== get_class($prop)) { + if ($defaultPropertyClass !== $prop::class) { $prop['VALUE'] = $valueType; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/MimeDir.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/MimeDir.php index be5d87baf..1263ced8e 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/MimeDir.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/MimeDir.php @@ -26,6 +26,11 @@ use Sabre\VObject\Property; */ class MimeDir extends Parser { + public const TOKEN_PROPNAME = 1; + public const TOKEN_PROPVALUE = 2; + public const TOKEN_PARAMNAME = 3; + public const TOKEN_PARAMVALUE = 4; + /** * The input stream. * @@ -36,7 +41,7 @@ class MimeDir extends Parser /** * Root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * By default, all input will be assumed to be UTF-8. @@ -106,7 +111,7 @@ class MimeDir extends Parser */ public function setCharset(string $charset): void { - if (!in_array($charset, self::$SUPPORTED_CHARSETS)) { + if (!in_array($charset, self::$SUPPORTED_CHARSETS, true)) { throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: '.implode(', ', self::$SUPPORTED_CHARSETS).')'); } $this->charset = $charset; @@ -150,23 +155,18 @@ class MimeDir extends Parser // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF). // It's 0xEF 0xBB 0xBF in UTF-8 hex. - if (3 <= strlen($line) + if (3 <= strlen((string) $line) && 0xEF === ord($line[0]) && 0xBB === ord($line[1]) && 0xBF === ord($line[2])) { - $line = \substr($line, 3); + $line = \substr((string) $line, 3); } - switch (strtoupper($line)) { - case 'BEGIN:VCALENDAR': - $class = VCalendar::$componentMap['VCALENDAR']; - break; - case 'BEGIN:VCARD': - $class = VCard::$componentMap['VCARD']; - break; - default: - throw new ParseException('This parser only supports VCARD and VCALENDAR files'); - } + $class = match (strtoupper((string) $line)) { + 'BEGIN:VCALENDAR' => VCalendar::$componentMap['VCALENDAR'], + 'BEGIN:VCARD' => VCard::$componentMap['VCARD'], + default => throw new ParseException('This parser only supports VCARD and VCALENDAR files'), + }; $this->root = new $class([], false); @@ -174,10 +174,10 @@ class MimeDir extends Parser // Reading until we hit END: try { $line = $this->readLine(); - } catch (EofException $oEx) { + } catch (EofException) { $line = 'END:'.$this->root->name; } - if ('END:' === strtoupper(\substr($line, 0, 4))) { + if ('END:' === strtoupper(\substr((string) $line, 0, 4))) { break; } $result = $this->parseLine($line); @@ -186,7 +186,7 @@ class MimeDir extends Parser } } - $name = strtoupper(\substr($line, 4)); + $name = strtoupper(\substr((string) $line, 4)); if ($name !== $this->root->name) { throw new ParseException('Invalid MimeDir file. expected: "END:'.$this->root->name.'" got: "END:'.$name.'"'); } @@ -215,7 +215,7 @@ class MimeDir extends Parser while (true) { // Reading until we hit END: $line = $this->readLine(); - if ('END:' === strtoupper(\substr($line, 0, 4))) { + if ('END:' === strtoupper(\substr((string) $line, 0, 4))) { break; } $result = $this->parseLine($line); @@ -224,22 +224,21 @@ class MimeDir extends Parser } } - $name = strtoupper(\substr($line, 4)); + $name = strtoupper(\substr((string) $line, 4)); if ($name !== $component->name) { throw new ParseException('Invalid MimeDir file. expected: "END:'.$component->name.'" got: "END:'.$name.'"'); } return $component; - } else { - // Property reader - $property = $this->readProperty($line); - if (!$property) { - // Ignored line - return false; - } - - return $property; } + // Property reader + $property = $this->readProperty($line); + if (!$property) { + // Ignored line + return false; + } + + return $property; } /** @@ -360,6 +359,12 @@ class MimeDir extends Parser 'value' => null, ]; + /* + * Keep track on the last token we parsed in order to do + * better error checking + */ + $lastToken = null; + $lastParam = null; /* @@ -385,10 +390,16 @@ class MimeDir extends Parser // option is set to ignore invalid lines, we ignore this line // This can happen when servers provide faulty data as iCloud // frequently does with X-APPLE-STRUCTURED-LOCATION + $lastToken = self::TOKEN_PARAMVALUE; continue; } throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.' did not follow iCalendar/vCard conventions'); } + + if ('=' === $match[0][0] && self::TOKEN_PARAMNAME !== $lastToken) { + throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.': Missing parameter name for parameter value "'.$match['paramValue'].'"'); + } + if (\is_null($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = $value; } elseif (is_array($property['parameters'][$lastParam])) { @@ -402,21 +413,23 @@ class MimeDir extends Parser $value, ]; } + $lastToken = self::TOKEN_PARAMVALUE; continue; } if (isset($match['paramName'])) { $lastParam = strtoupper($match['paramName']); - if (!isset($property['parameters'][$lastParam])) { - $property['parameters'][$lastParam] = null; - } + $property['parameters'][$lastParam] ??= null; + $lastToken = self::TOKEN_PARAMNAME; continue; } if (isset($match['propValue'])) { $property['value'] = $match['propValue']; + $lastToken = self::TOKEN_PROPVALUE; continue; } - if (isset($match['name']) && $match['name']) { + if (isset($match['name']) && 0 < strlen($match['name'])) { $property['name'] = strtoupper($match['name']); + $lastToken = self::TOKEN_PROPNAME; continue; } @@ -425,10 +438,8 @@ class MimeDir extends Parser // @codeCoverageIgnoreEnd } - if (\is_null($property['value'])) { - $property['value'] = ''; - } - if (!$property['name']) { + $property['value'] ??= ''; + if (!isset($property['name']) || 0 === strlen($property['name'])) { if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { return false; } @@ -671,7 +682,7 @@ class MimeDir extends Parser // missing a whitespace. So if 'forgiving' is turned on, we will take // those as well. if ($this->options & self::OPTION_FORGIVING) { - while ('=' === \substr($value, -1) && $this->lineBuffer) { + while (str_ends_with($value, '=') && $this->lineBuffer) { // Reading the line $this->readLine(); // Grabbing the raw form diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Parser.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Parser.php index 29921d182..c5126445a 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Parser.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/Parser.php @@ -30,11 +30,6 @@ abstract class Parser */ public const OPTION_IGNORE_INVALID_LINES = 2; - /** - * Bitmask of parser options. - */ - protected int $options; - /** * Creates the parser. * @@ -42,12 +37,11 @@ abstract class Parser * * @param int $options any parser options (OPTION constants) */ - public function __construct($input = null, int $options = 0) + public function __construct($input = null, protected int $options = 0) { if (!is_null($input)) { $this->setInput($input); } - $this->options = $options; } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/XML.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/XML.php index 43e42e6d5..46abd8020 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/XML.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Parser/XML.php @@ -28,17 +28,17 @@ class XML extends Parser /** * The input data. */ - protected ?array $input; + protected ?array $input = null; /** * A pointer/reference to the input. */ - private ?array $pointer; + private ?array $pointer = null; /** * Document, root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * Creates the parser. @@ -112,7 +112,7 @@ class XML extends Parser */ protected function parseVCalendarComponents(Component $parentComponent): void { - foreach ($this->pointer['value'] ?: [] as $children) { + foreach ($this->pointer['value'] ?? [] as $children) { switch (static::getTagName($children['name'])) { case 'properties': $this->pointer = &$children['value']; @@ -145,8 +145,8 @@ class XML extends Parser */ protected function parseProperties(Component $parentComponent, string $propertyNamePrefix = ''): void { - foreach ($this->pointer ?: [] as $xmlProperty) { - list($namespace, $tagName) = SabreXml\Service::parseClarkNotation($xmlProperty['name']); + foreach ($this->pointer ?? [] as $xmlProperty) { + [$namespace, $tagName] = SabreXml\Service::parseClarkNotation($xmlProperty['name']); $propertyName = $tagName; $propertyValue = []; @@ -322,7 +322,7 @@ class XML extends Parser */ protected function parseComponent(Component $parentComponent): void { - $components = $this->pointer['value'] ?: []; + $components = $this->pointer['value'] ?? []; foreach ($components as $component) { $componentName = static::getTagName($component['name']); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property.php index f4d0bdc0b..7432e5064 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property.php @@ -14,27 +14,8 @@ use Sabre\Xml; * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -abstract class Property extends Node +abstract class Property extends Node implements \Stringable { - /** - * The root document. - */ - public ?Component $root; - - /** - * Property name. - * - * This will contain a string such as DTSTART, SUMMARY, FN. - */ - public ?string $name; - - /** - * Property group. - * - * This is only used in vcards - */ - public ?string $group; - /** * List of parameters. */ @@ -75,13 +56,13 @@ abstract class Property extends Node * @param array $parameters List of parameters * @param string|null $group The vcard property group */ - public function __construct(Component $root, ?string $name, $value = null, array $parameters = [], ?string $group = null, ?int $lineIndex = null, ?string $lineString = null) + public function __construct(public ?Component $root, /** + * Property name. + * + * This will contain a string such as DTSTART, SUMMARY, FN. + */ + public ?string $name, $value = null, array $parameters = [], public ?string $group = null, ?int $lineIndex = null, ?string $lineString = null) { - $this->name = $name; - $this->group = $group; - - $this->root = $root; - foreach ($parameters as $k => $v) { $this->add($k, $v); } @@ -123,7 +104,7 @@ abstract class Property extends Node public function getValue() { if (is_array($this->value)) { - if (0 == count($this->value)) { + if (0 === count($this->value)) { return null; } elseif (1 === count($this->value)) { return $this->value[0]; @@ -155,9 +136,9 @@ abstract class Property extends Node return []; } elseif (is_array($this->value)) { return $this->value; - } else { - return [$this->value]; } + + return [$this->value]; } /** @@ -242,7 +223,7 @@ abstract class Property extends Node ); // remove single space after last CRLF - return \substr($str, 0, -1); + return \substr((string) $str, 0, -1); } /** @@ -282,7 +263,7 @@ abstract class Property extends Node if ('VALUE' === $parameter->name) { continue; } - $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize(); + $parameters[strtolower((string) $parameter->name)] = $parameter->jsonSerialize(); } // In jCard, we need to encode the property-group as a separate 'group' // parameter. @@ -292,7 +273,7 @@ abstract class Property extends Node return array_merge( [ - strtolower($this->name), + strtolower((string) $this->name), (object) $parameters, strtolower($this->getValueType()), ], @@ -329,13 +310,13 @@ abstract class Property extends Node $parameters[] = $parameter; } - $writer->startElement(strtolower($this->name)); + $writer->startElement(strtolower((string) $this->name)); - if (!empty($parameters)) { + if ([] !== $parameters) { $writer->startElement('parameters'); foreach ($parameters as $parameter) { - $writer->startElement(strtolower($parameter->name)); + $writer->startElement(strtolower((string) $parameter->name)); $writer->write($parameter); $writer->endElement(); } @@ -520,7 +501,7 @@ abstract class Property extends Node } // Checking if the property name does not contain any invalid bytes. - if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { + if (!preg_match('/^([A-Z0-9-]+)$/', (string) $this->name)) { $warnings[] = [ 'level' => $options & self::REPAIR ? 1 : 3, 'message' => 'The property name: '.$this->name.' contains invalid characters. Only A-Z, 0-9 and - are allowed', @@ -572,7 +553,7 @@ abstract class Property extends Node } break; } - if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding)) { + if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding, true)) { $warnings[] = [ 'level' => 3, 'message' => 'ENCODING='.strtoupper($encoding).' is not valid for this document type.', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Binary.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Binary.php index 25cda8d25..9d68f415e 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Binary.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Binary.php @@ -54,7 +54,7 @@ class Binary extends Property */ public function setRawMimeDirValue(string $val): void { - $this->value = base64_decode($val); + $this->value = base64_decode($val, true); } /** @@ -62,7 +62,7 @@ class Binary extends Property */ public function getRawMimeDirValue(): string { - return base64_encode($this->value); + return base64_encode((string) $this->value); } /** @@ -83,7 +83,7 @@ class Binary extends Property */ public function getJsonValue(): array { - return [base64_encode($this->getValue())]; + return [base64_encode((string) $this->getValue())]; } /** @@ -93,7 +93,7 @@ class Binary extends Property */ public function setJsonValue(array $value): void { - $value = array_map('base64_decode', $value); + $value = array_map(base64_decode(...), $value); parent::setJsonValue($value); } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Boolean.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Boolean.php index cdc3408aa..f33fbafdc 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Boolean.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Boolean.php @@ -56,9 +56,7 @@ class Boolean extends Property public function setXmlValue(array $value): void { $value = array_map( - function ($value) { - return 'true' === $value; - }, + fn ($value) => 'true' === $value, $value ); parent::setXmlValue($value); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/FloatValue.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/FloatValue.php index 7a36e0976..32b02a972 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/FloatValue.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/FloatValue.php @@ -67,7 +67,7 @@ class FloatValue extends Property */ public function getJsonValue(): array { - $val = array_map('floatval', $this->getParts()); + $val = array_map(floatval(...), $this->getParts()); // Special-casing the GEO property. // @@ -86,7 +86,7 @@ class FloatValue extends Property */ public function setXmlValue(array $value): void { - $value = array_map('floatval', $value); + $value = array_map(floatval(...), $value); parent::setXmlValue($value); } @@ -101,7 +101,7 @@ class FloatValue extends Property // See: // http://tools.ietf.org/html/rfc6321#section-3.4.1.2 if ('GEO' === $this->name) { - $value = array_map('floatval', $this->getParts()); + $value = array_map(floatval(...), $this->getParts()); $writer->writeElement('latitude', $value[0]); $writer->writeElement('longitude', $value[1]); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/CalAddress.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/CalAddress.php index 01969e87d..a2b13eee7 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/CalAddress.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/CalAddress.php @@ -43,10 +43,10 @@ class CalAddress extends Text public function getNormalizedValue(): string { $input = $this->getValue(); - if (!strpos($input, ':')) { + if (!strpos((string) $input, ':')) { return $input; } - list($schema, $everythingElse) = explode(':', $input, 2); + [$schema, $everythingElse] = explode(':', (string) $input, 2); $schema = strtolower($schema); if ('mailto' === $schema) { $everythingElse = strtolower($everythingElse); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/DateTime.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/DateTime.php index 631a73b08..cd92475f3 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/DateTime.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/DateTime.php @@ -111,7 +111,7 @@ class DateTime extends Property !$this->hasTime() || ( !isset($this['TZID']) - && false === strpos($this->getValue(), 'Z') + && !str_contains((string) $this->getValue(), 'Z') ); } @@ -205,7 +205,7 @@ class DateTime extends Property } if (is_null($tz)) { $tz = $d->getTimeZone(); - $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00'], true); if (!$isUtc) { $this->offsetSet('TZID', $tz->getName()); } @@ -257,15 +257,15 @@ class DateTime extends Property $isFloating = $this->isFloating(); $tz = $dts[0]->getTimeZone(); - $isUtc = !$isFloating && in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + $isUtc = !$isFloating && in_array($tz->getName(), ['UTC', 'GMT', 'Z'], true); return array_map( function (\DateTimeInterface $dt) use ($hasTime, $isUtc) { if ($hasTime) { return $dt->format('Y-m-d\\TH:i:s').($isUtc ? 'Z' : ''); - } else { - return $dt->format('Y-m-d'); } + + return $dt->format('Y-m-d'); }, $dts ); @@ -286,9 +286,7 @@ class DateTime extends Property // those. $this->setValue( array_map( - function ($item) { - return strtr($item, [':' => '', '-' => '']); - }, + fn ($item) => strtr($item, [':' => '', '-' => '']), $value ) ); @@ -306,7 +304,7 @@ class DateTime extends Property public function offsetSet($offset, $value): void { parent::offsetSet($offset, $value); - if ('VALUE' !== strtoupper($offset)) { + if ('VALUE' !== strtoupper((string) $offset)) { return; } @@ -347,7 +345,7 @@ class DateTime extends Property DateTimeParser::parseDateTime($value); break; } - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { $messages[] = [ 'level' => 3, 'message' => 'The supplied value ('.$value.') is not a correct '.$valueType, diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Period.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Period.php index 7632cb4d1..da9d972b1 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Period.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Period.php @@ -64,14 +64,17 @@ class Period extends Property public function setJsonValue(array $value): void { $value = array_map( - function ($item) { - return strtr(implode('/', $item), [':' => '', '-' => '']); - }, + fn ($item) => strtr(implode('/', $item), [':' => '', '-' => '']), $value ); parent::setJsonValue($value); } + public function appendUtc(string $strDate) + { + return !str_ends_with($strDate, 'Z') ? '' : 'Z'; + } + /** * Returns the value, in the format it should be encoded for json. * @@ -83,21 +86,21 @@ class Period extends Property { $return = []; foreach ($this->getParts() as $item) { - list($start, $end) = explode('/', $item, 2); + [$start, $end] = explode('/', (string) $item, 2); - $start = DateTimeParser::parseDateTime($start); + $startDt = DateTimeParser::parseDateTime($start)->format('Y-m-d\\TH:i:s').$this->appendUtc($start); // This is a duration value. if ('P' === $end[0]) { $return[] = [ - $start->format('Y-m-d\\TH:i:s'), + $startDt, $end, ]; } else { - $end = DateTimeParser::parseDateTime($end); + $endDt = DateTimeParser::parseDateTime($end)->format('Y-m-d\\TH:i:s').$this->appendUtc($end); $return[] = [ - $start->format('Y-m-d\\TH:i:s'), - $end->format('Y-m-d\\TH:i:s'), + $startDt, + $endDt, ]; } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Recur.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Recur.php index 10081673e..f3d45158a 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Recur.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/ICalendar/Recur.php @@ -29,14 +29,14 @@ class Recur extends Property /** * Reference to the parent object, if this is not the top object. */ - public ?Node $parent; + public ?Node $parent = null; /** * Updates the current value. * * This may be either a single, or multiple strings in an array. * - * @param string|array $value + * @param string|array|object $value */ public function setValue($value): void { @@ -52,17 +52,17 @@ class Recur extends Property $v = strtoupper($v); // The value had multiple sub-values - if (false !== strpos($v, ',')) { + if (str_contains($v, ',')) { $v = explode(',', $v); } - if (0 === strcmp($k, 'until')) { + if (0 === strcmp((string) $k, 'until')) { $v = strtr($v, [':' => '', '-' => '']); } } elseif (is_array($v)) { - $v = array_map('strtoupper', $v); + $v = array_map(strtoupper(...), $v); } - $newVal[strtoupper($k)] = $v; + $newVal[strtoupper((string) $k)] = $v; } $this->value = $newVal; } elseif (is_string($value)) { @@ -151,13 +151,13 @@ class Recur extends Property { $values = []; foreach ($this->getParts() as $k => $v) { - if (0 === strcmp($k, 'UNTIL')) { + if (0 === strcmp((string) $k, 'UNTIL')) { $date = new DateTime($this->root, null, $v); - $values[strtolower($k)] = $date->getJsonValue()[0]; - } elseif (0 === strcmp($k, 'COUNT')) { - $values[strtolower($k)] = intval($v); + $values[strtolower((string) $k)] = $date->getJsonValue()[0]; + } elseif (0 === strcmp((string) $k, 'COUNT')) { + $values[strtolower((string) $k)] = intval($v); } else { - $values[strtolower($k)] = $v; + $values[strtolower((string) $k)] = $v; } } @@ -186,7 +186,7 @@ class Recur extends Property $newValue = []; foreach (explode(';', $value) as $part) { // Skipping empty parts. - if (empty($part)) { + if ('' === $part) { continue; } @@ -196,10 +196,10 @@ class Recur extends Property throw new InvalidDataException('The supplied iCalendar RRULE part is incorrect: '.$part); } - list($partName, $partValue) = $parts; + [$partName, $partValue] = $parts; // The value itself had multiple values.. - if (false !== strpos($partValue, ',')) { + if (str_contains($partValue, ',')) { $partValue = explode(',', $partValue); } $newValue[$partName] = $partValue; @@ -243,7 +243,7 @@ class Recur extends Property if ($repair) { unset($values[$key]); } - } elseif ('BYMONTH' == $key) { + } elseif ('BYMONTH' === $key) { $byMonth = (array) $value; foreach ($byMonth as $i => $v) { if (!is_numeric($v) || (int) $v < 1 || (int) $v > 12) { @@ -262,13 +262,13 @@ class Recur extends Property } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } - } elseif ('BYWEEKNO' == $key) { + } elseif ('BYWEEKNO' === $key) { $byWeekNo = (array) $value; foreach ($byWeekNo as $i => $v) { - if (!is_numeric($v) || (int) $v < -53 || 0 == (int) $v || (int) $v > 53) { + if (!is_numeric($v) || (int) $v < -53 || 0 === (int) $v || (int) $v > 53) { $warnings[] = [ 'level' => $repair ? 1 : 3, 'message' => 'BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', @@ -284,13 +284,13 @@ class Recur extends Property } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } - } elseif ('BYYEARDAY' == $key) { + } elseif ('BYYEARDAY' === $key) { $byYearDay = (array) $value; foreach ($byYearDay as $i => $v) { - if (!is_numeric($v) || (int) $v < -366 || 0 == (int) $v || (int) $v > 366) { + if (!is_numeric($v) || (int) $v < -366 || 0 === (int) $v || (int) $v > 366) { $warnings[] = [ 'level' => $repair ? 1 : 3, 'message' => 'BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', @@ -306,7 +306,7 @@ class Recur extends Property } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/IntegerValue.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/IntegerValue.php index f1ae55d17..1cd7273cc 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/IntegerValue.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/IntegerValue.php @@ -62,7 +62,7 @@ class IntegerValue extends Property */ public function setXmlValue(array $value): void { - $value = array_map('intval', $value); + $value = array_map(intval(...), $value); parent::setXmlValue($value); } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Text.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Text.php index c59029059..e585026fc 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Text.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Text.php @@ -71,7 +71,7 @@ class Text extends Property // 2. structured value properties // // The former is always separated by a comma, the latter by semicolon. - if (in_array($name, $this->structuredValues)) { + if (in_array($name, $this->structuredValues, true)) { $this->delimiter = ';'; } @@ -155,7 +155,7 @@ class Text extends Property // Structured text values should always be returned as a single // array-item. Multi-value text should be returned as multiple items in // the top-array. - if (in_array($this->name, $this->structuredValues)) { + if (in_array($this->name, $this->structuredValues, true)) { return [$this->getParts()]; } @@ -213,7 +213,7 @@ class Text extends Property // If the resulting value contains a \n, we must encode it as // quoted-printable. - if (false !== \strpos($val, "\n")) { + if (str_contains((string) $val, "\n")) { $str .= ';ENCODING=QUOTED-PRINTABLE:'; $lastLine = $str; $out = ''; @@ -222,13 +222,13 @@ class Text extends Property // encode newlines for us. Specifically, the \r\n sequence must in // vcards be encoded as =0D=OA and we must insert soft-newlines // every 75 bytes. - for ($ii = 0; $ii < \strlen($val); ++$ii) { + for ($ii = 0; $ii < \strlen((string) $val); ++$ii) { $ord = \ord($val[$ii]); // These characters are encoded as themselves. if ($ord >= 32 && $ord <= 126) { $lastLine .= $val[$ii]; } else { - $lastLine .= '='.\strtoupper(\bin2hex($val[$ii])); + $lastLine .= '='.\strtoupper(\bin2hex((string) $val[$ii])); } if (\strlen($lastLine) >= 75) { // Soft line break @@ -241,22 +241,21 @@ class Text extends Property } return $out; - } else { - $str .= ':'.$val; + } + $str .= ':'.$val; - $str = \preg_replace( - '/( + $str = \preg_replace( + '/( (?:^.)? # 1 additional byte in first line because of missing single space (see next line) .{1,74} # max 75 bytes per line (1 byte is used for a single space added after every CRLF) (?![\x80-\xbf]) # prevent splitting multibyte characters )/x', - "$1\r\n ", - $str - ); + "$1\r\n ", + $str + ); - // remove single space after last CRLF - return \substr($str, 0, -1); - } + // remove single space after last CRLF + return \substr((string) $str, 0, -1); } /** @@ -271,7 +270,7 @@ class Text extends Property foreach ($items as $i => $item) { $writer->writeElement( $item, - !empty($values[$i]) ? $values[$i] : null + ('' !== $values[$i] && [] !== $values[$i]) ? $values[$i] : null ); } }; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Time.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Time.php index ed314fc0d..9c4016c64 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Time.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Time.php @@ -104,7 +104,7 @@ class Time extends Text $timeStr .= 'Z'; } else { $timeStr .= - preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']); + preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', (string) $parts['timezone']); } } @@ -118,9 +118,7 @@ class Time extends Text public function setXmlValue(array $value): void { $value = array_map( - function ($value) { - return str_replace(':', '', $value); - }, + fn ($value) => str_replace(':', '', $value), $value ); parent::setXmlValue($value); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Uri.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Uri.php index 228b7090f..dce7bf4bb 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Uri.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/Uri.php @@ -38,7 +38,7 @@ class Uri extends Text public function parameters(): array { $parameters = parent::parameters(); - if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) { + if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'], true)) { // If we are encoding a URI value, and this URI value has no // VALUE=URI parameter, we add it anyway. // @@ -74,14 +74,10 @@ class Uri extends Text $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $newVal = ''; foreach ($matches as $match) { - switch ($match) { - case '\:': - $newVal .= ':'; - break; - default: - $newVal .= $match; - break; - } + match ($match) { + '\:' => $newVal .= ':', + default => $newVal .= $match, + }; } $this->value = $newVal; } else { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/UtcOffset.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/UtcOffset.php index 09aab0151..52d81f1f6 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/UtcOffset.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/UtcOffset.php @@ -42,9 +42,7 @@ class UtcOffset extends Text public function setJsonValue(array $value): void { $value = array_map( - function ($value) { - return str_replace(':', '', $value); - }, + fn ($value) => str_replace(':', '', $value), $value ); parent::setJsonValue($value); @@ -58,10 +56,8 @@ class UtcOffset extends Text public function getJsonValue(): array { return array_map( - function ($value) { - return substr($value, 0, -2).':'. - substr($value, -2); - }, + fn ($value) => substr((string) $value, 0, -2).':'. + substr((string) $value, -2), parent::getJsonValue() ); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/VCard/DateAndOrTime.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/VCard/DateAndOrTime.php index e87bf1b3a..9a7693961 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/VCard/DateAndOrTime.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Property/VCard/DateAndOrTime.php @@ -77,7 +77,7 @@ class DateAndOrTime extends Property public function setDateTime(\DateTimeInterface $dt): void { $tz = $dt->getTimeZone(); - $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z'], true); if ($isUtc) { $value = $dt->format('Ymd\\THis\\Z'); @@ -232,14 +232,10 @@ class DateAndOrTime extends Property $value = ''; // $d = defined - $d = function ($part) use ($parts): bool { - return !is_null($parts[$part]); - }; + $d = (fn ($part): bool => !is_null($parts[$part])); // $r = read - $r = function ($part) use ($parts) { - return $parts[$part]; - }; + $r = (fn ($part) => $parts[$part]); // From the Relax NG Schema. // @@ -341,7 +337,7 @@ class DateAndOrTime extends Property try { DateTimeParser::parseVCardDateTime($value); - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { $messages[] = [ 'level' => 3, 'message' => 'The supplied value ('.$value.') is not a correct DATE-AND-OR-TIME property', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/EventIterator.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/EventIterator.php index 2c93bcf4e..5db409350 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/EventIterator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/EventIterator.php @@ -91,9 +91,7 @@ class EventIterator implements \Iterator */ public function __construct($input, ?string $uid = null, ?\DateTimeZone $timeZone = null) { - if (is_null($timeZone)) { - $timeZone = new \DateTimeZone('UTC'); - } + $timeZone ??= new \DateTimeZone('UTC'); $this->timeZone = $timeZone; if (is_array($input)) { @@ -164,8 +162,12 @@ class EventIterator implements \Iterator } if (isset($this->masterEvent->RDATE)) { + $rdateValues = []; + foreach ($this->masterEvent->RDATE as $rdate) { + $rdateValues = array_merge($rdateValues, $rdate->getParts()); + } $this->recurIterator = new RDateIterator( - $this->masterEvent->RDATE->getParts(), + $rdateValues, $this->startDate ); } elseif (isset($this->masterEvent->RRULE)) { @@ -228,11 +230,10 @@ class EventIterator implements \Iterator } if ($this->currentOverriddenEvent && $this->currentOverriddenEvent->DTEND) { return $this->currentOverriddenEvent->DTEND->getDateTime($this->timeZone); - } else { - $end = clone $this->currentDate; - - return $end->modify('+'.$this->eventDuration.' seconds'); } + $end = clone $this->currentDate; + + return $end->modify('+'.$this->eventDuration.' seconds'); } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RDateIterator.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RDateIterator.php index 4ad688f9d..b9bd0beff 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RDateIterator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RDateIterator.php @@ -26,9 +26,13 @@ class RDateIterator implements \Iterator * * @param string|array $rrule */ - public function __construct($rrule, \DateTimeInterface $start) + public function __construct($rrule, /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + */ + protected \DateTimeInterface $startDate) { - $this->startDate = $start; $this->parseRDate($rrule); $this->currentDate = clone $this->startDate; } @@ -117,13 +121,6 @@ class RDateIterator implements \Iterator } } - /** - * The reference start date/time for the rrule. - * - * All calculations are based on this initial date. - */ - protected \DateTimeInterface $startDate; - /** * The date of the current iteration. You can get this by calling * ->current(). diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RRuleIterator.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RRuleIterator.php index ff276a697..6f1f28ae3 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RRuleIterator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Recur/RRuleIterator.php @@ -37,9 +37,13 @@ class RRuleIterator implements \Iterator * * @throws InvalidDataException */ - public function __construct($rrule, \DateTimeInterface $start) + public function __construct($rrule, /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + */ + protected \DateTimeInterface $startDate) { - $this->startDate = $start; $this->parseRRule($rrule); $this->currentDate = clone $this->startDate; } @@ -146,13 +150,6 @@ class RRuleIterator implements \Iterator } } - /** - * The reference start date/time for the rrule. - * - * All calculations are based on this initial date. - */ - protected \DateTimeInterface $startDate; - /** * The date of the current iteration. You can get this by calling * ->current(). @@ -357,23 +354,23 @@ class RRuleIterator implements \Iterator } $recurrenceHours = []; - if (!empty($this->byHour)) { + if ($this->byHour) { $recurrenceHours = $this->getHours(); } $recurrenceDays = []; - if (!empty($this->byDay)) { + if ($this->byDay) { $recurrenceDays = $this->getDays(); } $recurrenceMonths = []; - if (!empty($this->byMonth)) { + if ($this->byMonth) { $recurrenceMonths = $this->getMonths(); } do { if ($this->byHour) { - if ('23' == $this->currentDate->format('G')) { + if ('23' === $this->currentDate->format('G')) { // to obey the interval rule $this->currentDate = $this->currentDate->modify('+'.($this->interval - 1).' days'); } @@ -398,9 +395,9 @@ class RRuleIterator implements \Iterator return; } } while ( - ($this->byDay && !in_array($currentDay, $recurrenceDays)) - || ($this->byHour && !in_array($currentHour, $recurrenceHours)) - || ($this->byMonth && !in_array($currentMonth, $recurrenceMonths)) + ($this->byDay && !in_array((int) $currentDay, $recurrenceDays, true)) + || ($this->byHour && !in_array($currentHour, $recurrenceHours, true)) + || ($this->byMonth && !in_array($currentMonth, $recurrenceMonths, true)) ); } @@ -442,18 +439,18 @@ class RRuleIterator implements \Iterator $currentHour = (int) $this->currentDate->format('G'); // We need to roll over to the next week - if ($currentDay === $firstDay && (!$this->byHour || '0' == $currentHour)) { + if ($currentDay === $firstDay && (!$this->byHour || 0 === $currentHour)) { $this->currentDate = $this->currentDate->modify('+'.($this->interval - 1).' weeks'); // We need to go to the first day of this week, but only if we // are not already on this first day of this week. - if ($this->currentDate->format('w') != $firstDay) { + if ((int) $this->currentDate->format('w') !== $firstDay) { $this->currentDate = $this->currentDate->modify('last '.$this->dayNames[$this->dayMap[$this->weekStart]]); } } // We have a match - } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours))); + } while (($this->byDay && !in_array($currentDay, $recurrenceDays, true)) || ($this->byHour && !in_array((string) $currentHour, $recurrenceHours, true))); } /** @@ -476,14 +473,14 @@ class RRuleIterator implements \Iterator ++$increase; $tempDate = clone $this->currentDate; $tempDate = $tempDate->modify('+ '.($this->interval * $increase).' months '.$this->startTime()); - } while ($tempDate->format('j') != $currentDayOfMonth); + } while ($tempDate->format('j') !== $currentDayOfMonth); $this->currentDate = $tempDate; } return; } - $occurrence = -1; + $selectedOccurrence = -1; while (true) { $occurrences = $this->getMonthlyOccurrences(); @@ -491,6 +488,7 @@ class RRuleIterator implements \Iterator // The first occurrence that's higher than the current // day of the month wins. if ($occurrence > $currentDayOfMonth) { + $selectedOccurrence = $occurrence; break 2; } } @@ -533,7 +531,7 @@ class RRuleIterator implements \Iterator $this->currentDate = $this->currentDate->setDate( (int) $this->currentDate->format('Y'), (int) $this->currentDate->format('n'), - (int) $occurrence + (int) $selectedOccurrence )->modify($this->startTime()); } @@ -547,9 +545,9 @@ class RRuleIterator implements \Iterator $currentDayOfMonth = $this->currentDate->format('j'); // No sub-rules, so we just advance by year - if (empty($this->byMonth)) { + if (!$this->byMonth) { // Unless it was a leap day! - if (2 == $currentMonth && 29 == $currentDayOfMonth) { + if (2 === (int) $currentMonth && 29 === (int) $currentDayOfMonth) { $counter = 0; do { ++$counter; @@ -563,7 +561,7 @@ class RRuleIterator implements \Iterator // functions instead. $nextDate = clone $this->currentDate; $nextDate = $nextDate->modify('+ '.($this->interval * $counter).' years'); - } while (2 != $nextDate->format('n')); + } while (2 !== (int) $nextDate->format('n')); $this->currentDate = $nextDate; @@ -634,7 +632,7 @@ class RRuleIterator implements \Iterator $date = $date->sub(new \DateInterval('P'.abs($byYearDay + 1).'D')); } - if ($date > $this->currentDate && in_array($date->format('N'), $dayOffsets)) { + if ($date > $this->currentDate && in_array((int) $date->format('N'), $dayOffsets, true)) { $checkDates[] = $date; } } @@ -665,7 +663,7 @@ class RRuleIterator implements \Iterator // If we got a byDay or getMonthDay filter, we must first expand // further. if ($this->byDay || $this->byMonthDay) { - $occurrence = -1; + $selectedOccurrence = -1; while (true) { $occurrences = $this->getMonthlyOccurrences(); @@ -677,7 +675,8 @@ class RRuleIterator implements \Iterator if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { // only consider byMonth matches, // otherwise, we don't follow RRule correctly - if (in_array($currentMonth, $this->byMonth)) { + if (in_array((string) $currentMonth, $this->byMonth, true)) { + $selectedOccurrence = $occurrence; break 2; } } @@ -693,12 +692,12 @@ class RRuleIterator implements \Iterator $currentYear += $this->interval; $currentMonth = 1; } - } while (!in_array($currentMonth, $this->byMonth)); + } while (!in_array((string) $currentMonth, $this->byMonth, true)); $this->currentDate = $this->currentDate->setDate( (int) $currentYear, - (int) $currentMonth, - (int) $currentDayOfMonth + $currentMonth, + $currentDayOfMonth ); // To prevent running this forever (better: until we hit the max date of DateTimeImmutable) we simply @@ -714,28 +713,25 @@ class RRuleIterator implements \Iterator $this->currentDate = $this->currentDate->setDate( (int) $currentYear, (int) $currentMonth, - (int) $occurrence - )->modify($this->startTime()); - - return; - } else { - // These are the 'byMonth' rules, if there are no byDay or - // byMonthDay sub-rules. - do { - ++$currentMonth; - if ($currentMonth > 12) { - $currentYear += $this->interval; - $currentMonth = 1; - } - } while (!in_array($currentMonth, $this->byMonth)); - $this->currentDate = $this->currentDate->setDate( - (int) $currentYear, - (int) $currentMonth, - (int) $currentDayOfMonth + (int) $selectedOccurrence )->modify($this->startTime()); return; } + // These are the 'byMonth' rules, if there are no byDay or + // byMonthDay sub-rules. + do { + ++$currentMonth; + if ($currentMonth > 12) { + $currentYear += $this->interval; + $currentMonth = 1; + } + } while (!in_array((string) $currentMonth, $this->byMonth, true)); + $this->currentDate = $this->currentDate->setDate( + (int) $currentYear, + $currentMonth, + (int) $currentDayOfMonth + )->modify($this->startTime()); } /* }}} */ @@ -755,13 +751,14 @@ class RRuleIterator implements \Iterator } foreach ($rrule as $key => $value) { - $key = strtoupper($key); + $key = strtoupper((string) $key); switch ($key) { case 'FREQ': - $value = strtolower($value); + $value = strtolower((string) $value); if (!in_array( $value, - ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'] + ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'], + true )) { throw new InvalidDataException('Unknown value for FREQ='.strtoupper($value)); } @@ -809,7 +806,7 @@ class RRuleIterator implements \Iterator case 'BYDAY': $value = (array) $value; foreach ($value as $part) { - if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) { + if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $#xi', (string) $part)) { throw new InvalidDataException('Invalid part in BYDAY clause: '.$part); } } @@ -823,7 +820,7 @@ class RRuleIterator implements \Iterator case 'BYYEARDAY': $this->byYearDay = (array) $value; foreach ($this->byYearDay as $byYearDay) { - if (!is_numeric($byYearDay) || (int) $byYearDay < -366 || 0 == (int) $byYearDay || (int) $byYearDay > 366) { + if (!is_numeric($byYearDay) || (int) $byYearDay < -366 || 0 === (int) $byYearDay || (int) $byYearDay > 366) { throw new InvalidDataException('BYYEARDAY in RRULE must have value(s) from 1 to 366, or -366 to -1!'); } } @@ -832,7 +829,7 @@ class RRuleIterator implements \Iterator case 'BYWEEKNO': $this->byWeekNo = (array) $value; foreach ($this->byWeekNo as $byWeekNo) { - if (!is_numeric($byWeekNo) || (int) $byWeekNo < -53 || 0 == (int) $byWeekNo || (int) $byWeekNo > 53) { + if (!is_numeric($byWeekNo) || (int) $byWeekNo < -53 || 0 === (int) $byWeekNo || (int) $byWeekNo > 53) { throw new InvalidDataException('BYWEEKNO in RRULE must have value(s) from 1 to 53, or -53 to -1!'); } } @@ -852,7 +849,7 @@ class RRuleIterator implements \Iterator break; case 'WKST': - $this->weekStart = strtoupper($value); + $this->weekStart = strtoupper((string) $value); break; default: @@ -892,7 +889,7 @@ class RRuleIterator implements \Iterator // that point and add it to the results. if ($this->byDay) { foreach ($this->byDay as $day) { - $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]]; + $dayName = $this->dayNames[$this->dayMap[substr((string) $day, -2)]]; // Dayname will be something like 'wednesday'. Now we need to find // all wednesdays in this month. @@ -913,8 +910,8 @@ class RRuleIterator implements \Iterator // So now we have 'all wednesdays' for month. It is however // possible that the user only really wanted the 1st, 2nd or last // wednesday. - if (strlen($day) > 2) { - $offset = (int) substr($day, 0, -2); + if (strlen((string) $day) > 2) { + $offset = (int) substr((string) $day, 0, -2); if ($offset > 0) { // It is possible that the day does not exist, such as a @@ -949,7 +946,7 @@ class RRuleIterator implements \Iterator $byMonthDayResults[] = $monthDay; } else { // Negative values - $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; + $byMonthDayResults[] = (int) $startDate->format('t') + 1 + $monthDay; } } } @@ -1018,7 +1015,7 @@ class RRuleIterator implements \Iterator // The day may be preceded with a positive (+n) or // negative (-n) integer. However, this does not make // sense in 'weekly' so we ignore it here. - $recurrenceDays[] = $this->dayMap[substr($byDay, -2)]; + $recurrenceDays[] = $this->dayMap[substr((string) $byDay, -2)]; } return $recurrenceDays; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/ICalendar.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/ICalendar.php index 79f0bd443..edbba68cc 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/ICalendar.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/ICalendar.php @@ -96,8 +96,8 @@ class ICalendar implements SplitterInterface } return $object; - } else { - return null; } + + return null; } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/VCard.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/VCard.php index 40775a335..a802dbe75 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/VCard.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Splitter/VCard.php @@ -22,13 +22,6 @@ use Sabre\VObject\Parser\MimeDir; */ class VCard implements SplitterInterface { - /** - * File handle. - * - * @var resource - */ - protected $input; - /** * Persistent parser. */ @@ -42,10 +35,12 @@ class VCard implements SplitterInterface * @param resource $input * @param int $options parser options, see the OPTIONS constants */ - public function __construct($input, int $options = 0) + public function __construct(/** + * File handle. + */ + protected $input, int $options = 0) { - $this->input = $input; - $this->parser = new MimeDir($input, $options); + $this->parser = new MimeDir($this->input, $options); } /** @@ -64,7 +59,7 @@ class VCard implements SplitterInterface if (!$object instanceof Component\VCard) { throw new VObject\ParseException('The supplied input contained non-VCARD data.'); } - } catch (VObject\EofException $e) { + } catch (VObject\EofException) { return null; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimeZoneUtil.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimeZoneUtil.php index 2cd458578..19333e86a 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimeZoneUtil.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimeZoneUtil.php @@ -41,9 +41,7 @@ class TimeZoneUtil private static function getInstance(): self { - if (null === self::$instance) { - self::$instance = new self(); - } + self::$instance ??= new self(); return self::$instance; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromOffset.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromOffset.php index d50cb969c..418fd6ba9 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromOffset.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromOffset.php @@ -24,6 +24,24 @@ class FindFromOffset implements TimezoneFinder // @codeCoverageIgnoreEnd } + if (0 === strncasecmp($tzid, 'UTC', 3)) { + $offset = substr($tzid, 3); + if (6 === strlen($offset) && ':' === $offset[3]) { + $offset = substr($offset, 0, 3).substr($offset, 4); + } + + if (5 !== strlen($offset) + || ('+' !== $offset[0] && '-' !== $offset[0]) + || 4 !== strspn(substr($offset, 1), '0123456789') + || (int) substr($offset, 1, 2) > 23 + || (int) substr($offset, 3, 2) > 59 + ) { + return null; + } + + return new \DateTimeZone($offset); + } + return null; } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneIdentifier.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneIdentifier.php index ba84d45eb..5e56b82f1 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneIdentifier.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneIdentifier.php @@ -40,13 +40,13 @@ class FindFromTimezoneIdentifier implements TimezoneFinder try { if ( - in_array($tzid, $tzIdentifiers) + in_array($tzid, $tzIdentifiers, true) || preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches) - || in_array($tzid, $this->getIdentifiersBC()) + || in_array($tzid, $this->getIdentifiersBC(), true) ) { return new \DateTimeZone($tzid); } - } catch (\Exception $e) { + } catch (\Exception) { } return null; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneMap.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneMap.php index 83466b28e..5beb5450d 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneMap.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/FindFromTimezoneMap.php @@ -18,11 +18,18 @@ class FindFromTimezoneMap implements TimezoneFinder '/^\((UTC|GMT)(\+|\-)[\d]{2}\.[\d]{2}\) (.*)/', ]; + /** + * @throws void + */ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZone { // Next, we check if the tzid is somewhere in our tzid map. if ($this->hasTzInMap($tzid)) { - return new \DateTimeZone($this->getTzFromMap($tzid)); + try { + return new \DateTimeZone($this->getTzFromMap($tzid)); + } catch (\Exception) { + return null; + } } // Some Microsoft products prefix the offset first, so let's strip that off @@ -34,7 +41,11 @@ class FindFromTimezoneMap implements TimezoneFinder } $tzidAlternate = $matches[3]; if ($this->hasTzInMap($tzidAlternate)) { - return new \DateTimeZone($this->getTzFromMap($tzidAlternate)); + try { + return new \DateTimeZone($this->getTzFromMap($tzidAlternate)); + } catch (\Exception) { + return null; + } } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromLicEntry.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromLicEntry.php index 486b33796..7212117f7 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromLicEntry.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromLicEntry.php @@ -23,7 +23,7 @@ class GuessFromLicEntry implements TimezoneGuesser // Libical generators may specify strings like // "SystemV/EST5EDT". For those we must remove the // SystemV part. - if ('SystemV/' === substr($lic, 0, 8)) { + if (str_starts_with($lic, 'SystemV/')) { $lic = substr($lic, 8); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromMsTzId.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromMsTzId.php index 392333cb0..82060bd27 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromMsTzId.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/TimezoneGuesser/GuessFromMsTzId.php @@ -105,7 +105,7 @@ class GuessFromMsTzId implements TimezoneGuesser $cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue(); // 2 can mean both Europe/Lisbon and Europe/Sarajevo. - if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) { + if (2 === $cdoId && str_contains((string) $vtimezone->TZID, 'Sarajevo')) { return new \DateTimeZone('Europe/Sarajevo'); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/VCardConverter.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/VCardConverter.php index ff8fe3f81..39969d558 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/VCardConverter.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/VCardConverter.php @@ -38,10 +38,10 @@ class VCardConverter return clone $input; } - if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40])) { + if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40], true)) { throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data'); } - if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40])) { + if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40], true)) { throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version'); } @@ -69,7 +69,7 @@ class VCardConverter protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, int $targetVersion): void { // Skipping these, those are automatically added. - if (in_array($property->name, ['VERSION', 'PRODID'])) { + if (in_array($property->name, ['VERSION', 'PRODID'], true)) { return; } @@ -93,7 +93,7 @@ class VCardConverter ); if (Document::VCARD30 === $targetVersion) { - if ($property instanceof Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'])) { + if ($property instanceof Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'], true)) { /** @var Uri $newProperty */ $newProperty = $this->convertUriToBinary($output, $newProperty); } elseif ($property instanceof Property\VCard\DateAndOrTime) { @@ -111,7 +111,7 @@ class VCardConverter $newProperty['X-APPLE-OMIT-YEAR'] = '1604'; } - if ('ANNIVERSARY' == $newProperty->name) { + if ('ANNIVERSARY' === $newProperty->name) { // Microsoft non-standard anniversary $newProperty->name = 'X-ANNIVERSARY'; @@ -127,7 +127,7 @@ class VCardConverter $output->add('ITEM'.$x.'.X-ABLABEL', '_$!!$_'); } } elseif ('KIND' === $property->name) { - switch (strtolower($property->getValue())) { + switch (strtolower((string) $property->getValue())) { case 'org': // vCard 3.0 does not have an equivalent to KIND:ORG, // but apple has an extension that means the same @@ -149,7 +149,7 @@ class VCardConverter } } elseif (Document::VCARD40 === $targetVersion) { // These properties were removed in vCard 4.0 - if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'])) { + if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'], true)) { return; } @@ -171,12 +171,12 @@ class VCardConverter } switch ($property->name) { case 'X-ABSHOWAS': - if ('COMPANY' === strtoupper($property->getValue())) { + if ('COMPANY' === strtoupper((string) $property->getValue())) { $newProperty = $output->createProperty('KIND', 'ORG'); } break; case 'X-ADDRESSBOOKSERVER-KIND': - if ('GROUP' === strtoupper($property->getValue())) { + if ('GROUP' === strtoupper((string) $property->getValue())) { $newProperty = $output->createProperty('KIND', 'GROUP'); } break; @@ -274,10 +274,11 @@ class VCardConverter $newTypes = []; foreach ($parameters['TYPE']->getParts() as $typePart) { if (in_array( - strtoupper($typePart), - ['JPEG', 'PNG', 'GIF'] + strtoupper((string) $typePart), + ['JPEG', 'PNG', 'GIF'], + true )) { - $mimeType = 'image/'.strtolower($typePart); + $mimeType = 'image/'.strtolower((string) $typePart); } else { $newTypes[] = $typePart; } @@ -292,7 +293,7 @@ class VCardConverter } } - $newProperty->setValue('data:'.$mimeType.';base64,'.base64_encode($value)); + $newProperty->setValue('data:'.$mimeType.';base64,'.base64_encode((string) $value)); return $newProperty; } @@ -313,7 +314,7 @@ class VCardConverter $value = $newProperty->getValue(); // Only converting data: uris - if ('data:' !== substr($value, 0, 5)) { + if (!str_starts_with((string) $value, 'data:')) { return $newProperty; } @@ -325,12 +326,12 @@ class VCardConverter 'BINARY' ); - $mimeType = substr($value, 5, strpos($value, ',') - 5); + $mimeType = substr((string) $value, 5, strpos((string) $value, ',') - 5); if (strpos($mimeType, ';')) { $mimeType = substr($mimeType, 0, strpos($mimeType, ';')); - $newProperty->setValue(base64_decode(substr($value, strpos($value, ',') + 1))); + $newProperty->setValue(base64_decode(substr((string) $value, strpos((string) $value, ',') + 1), true)); } else { - $newProperty->setValue(substr($value, strpos($value, ',') + 1)); + $newProperty->setValue(substr((string) $value, strpos((string) $value, ',') + 1)); } unset($value); @@ -367,7 +368,7 @@ class VCardConverter // that's now PREF=1. case 'TYPE': foreach ($param->getParts() as $paramPart) { - if ('PREF' === strtoupper($paramPart)) { + if ('PREF' === strtoupper((string) $paramPart)) { $newProperty->add('PREF', '1'); } else { $newProperty->add($param->name, $paramPart); @@ -402,7 +403,7 @@ class VCardConverter case 'ENCODING': // This value only existed in vCard 2.1, and should be // removed for anything else. - if ('QUOTED-PRINTABLE' !== strtoupper($param->getValue())) { + if ('QUOTED-PRINTABLE' !== strtoupper((string) $param->getValue())) { $newProperty->add($param->name, $param->getParts()); } break; @@ -413,7 +414,7 @@ class VCardConverter * Any other PREF numbers we'll drop. */ case 'PREF': - if ('1' == $param->getValue()) { + if ('1' === (string) $param->getValue()) { $newProperty->add('TYPE', 'PREF'); } break; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Version.php index 893f272d3..e851a6507 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Version.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Version.php @@ -14,5 +14,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.5.2'; + public const VERSION = '5.0.0'; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/exchangezones.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/exchangezones.php index 89bddc27c..f42944b3b 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/exchangezones.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/exchangezones.php @@ -17,6 +17,7 @@ return [ 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon', 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London', 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin', + 'Amsterdam, Berlin, Bern, Rom, Stockholm, Wien' => 'Europe/Berlin', 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague', 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris', 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris', @@ -39,12 +40,12 @@ return [ 'Kabul' => 'Asia/Kabul', 'Ekaterinburg' => 'Asia/Yekaterinburg', 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi', - 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta', + 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Kolkata', 'Kathmandu, Nepal' => 'Asia/Kathmandu', 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty', 'Astana, Dhaka' => 'Asia/Dhaka', 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo', - 'Rangoon' => 'Asia/Rangoon', + 'Rangoon' => 'Asia/Yangon', 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok', 'Krasnoyarsk' => 'Asia/Krasnoyarsk', 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai', @@ -71,7 +72,7 @@ return [ 'Mid-Atlantic' => 'America/Noronha', 'Brasilia' => 'America/Sao_Paulo', // Best guess 'Buenos Aires' => 'America/Argentina/Buenos_Aires', - 'Greenland' => 'America/Godthab', + 'Greenland' => 'America/Nuuk', 'Newfoundland' => 'America/St_Johns', 'Atlantic Time (Canada)' => 'America/Halifax', 'Caracas, La Paz' => 'America/Caracas', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/lotuszones.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/lotuszones.php index 9115ac743..45e76f7d6 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/lotuszones.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/lotuszones.php @@ -34,7 +34,7 @@ return [ 'Newfoundland' => 'America/St_Johns', 'Argentina' => 'America/Argentina/Buenos_Aires', 'E. South America' => 'America/Belem', - 'Greenland' => 'America/Godthab', + 'Greenland' => 'America/Nuuk', 'Montevideo' => 'America/Montevideo', 'SA Eastern' => 'America/Belem', // 'Mid-Atlantic' => 'Etc/GMT-2', // conflict with windows timezones. @@ -71,12 +71,12 @@ return [ 'Ekaterinburg' => 'Asia/Yekaterinburg', 'Pakistan' => 'Asia/Karachi', 'West Asia' => 'Asia/Tashkent', - 'India' => 'Asia/Calcutta', + 'India' => 'Asia/Kolkata', 'Sri Lanka' => 'Asia/Colombo', 'Nepal' => 'Asia/Kathmandu', 'Central Asia' => 'Asia/Dhaka', 'N. Central Asia' => 'Asia/Almaty', - 'Myanmar' => 'Asia/Rangoon', + 'Myanmar' => 'Asia/Yangon', 'North Asia' => 'Asia/Krasnoyarsk', 'SE Asia' => 'Asia/Bangkok', 'China' => 'Asia/Shanghai', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/windowszones.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/windowszones.php index 335007983..2dd9120e3 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/windowszones.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/timezonedata/windowszones.php @@ -20,7 +20,7 @@ return [ 'Arab Standard Time' => 'Asia/Riyadh', 'Arabian Standard Time' => 'Asia/Dubai', 'Arabic Standard Time' => 'Asia/Baghdad', - 'Argentina Standard Time' => 'America/Buenos_Aires', + 'Argentina Standard Time' => 'America/Argentina/Buenos_Aires', 'Astrakhan Standard Time' => 'Europe/Astrakhan', 'Atlantic Standard Time' => 'America/Halifax', 'Aus Central W. Standard Time' => 'Australia/Eucla', @@ -55,16 +55,16 @@ return [ 'Eastern Standard Time (Mexico)' => 'America/Cancun', 'Egypt Standard Time' => 'Africa/Cairo', 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', - 'FLE Standard Time' => 'Europe/Kiev', + 'FLE Standard Time' => 'Europe/Kyiv', 'Fiji Standard Time' => 'Pacific/Fiji', 'GMT Standard Time' => 'Europe/London', 'GTB Standard Time' => 'Europe/Bucharest', 'Georgian Standard Time' => 'Asia/Tbilisi', - 'Greenland Standard Time' => 'America/Godthab', + 'Greenland Standard Time' => 'America/Nuuk', 'Greenwich Standard Time' => 'Atlantic/Reykjavik', 'Haiti Standard Time' => 'America/Port-au-Prince', 'Hawaiian Standard Time' => 'Pacific/Honolulu', - 'India Standard Time' => 'Asia/Calcutta', + 'India Standard Time' => 'Asia/Kolkata', 'Iran Standard Time' => 'Asia/Tehran', 'Israel Standard Time' => 'Asia/Jerusalem', 'Jordan Standard Time' => 'Asia/Amman', @@ -82,10 +82,10 @@ return [ 'Morocco Standard Time' => 'Africa/Casablanca', 'Mountain Standard Time' => 'America/Denver', 'Mountain Standard Time (Mexico)' => 'America/Chihuahua', - 'Myanmar Standard Time' => 'Asia/Rangoon', + 'Myanmar Standard Time' => 'Asia/Yangon', 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', 'Namibia Standard Time' => 'Africa/Windhoek', - 'Nepal Standard Time' => 'Asia/Katmandu', + 'Nepal Standard Time' => 'Asia/Kathmandu', 'New Zealand Standard Time' => 'Pacific/Auckland', 'Newfoundland Standard Time' => 'America/St_Johns', 'Norfolk Standard Time' => 'Pacific/Norfolk', @@ -127,7 +127,7 @@ return [ 'Transbaikal Standard Time' => 'Asia/Chita', 'Turkey Standard Time' => 'Europe/Istanbul', 'Turks And Caicos Standard Time' => 'America/Grand_Turk', - 'US Eastern Standard Time' => 'America/Indianapolis', + 'US Eastern Standard Time' => 'America/Indiana/Indianapolis', 'US Mountain Standard Time' => 'America/Phoenix', 'UTC' => 'Etc/GMT', 'UTC+12' => 'Etc/GMT-12', diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Deserializer/functions.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Deserializer/functions.php index 949aa720e..cb5b7ca56 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Deserializer/functions.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Deserializer/functions.php @@ -212,11 +212,13 @@ function valueObject(Reader $reader, string $className, string $namespace): obje $reader->read(); do { - if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI == $namespace) { + if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI === $namespace) { if (property_exists($valueObject, $reader->localName)) { if (is_array($defaultProperties[$reader->localName])) { + // @phpstan-ignore property.dynamicName $valueObject->{$reader->localName}[] = $reader->parseCurrentElement()['value']; } else { + // @phpstan-ignore property.dynamicName $valueObject->{$reader->localName} = $reader->parseCurrentElement()['value']; } } else { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Base.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Base.php index 7c597fa8c..10fe97838 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Base.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Base.php @@ -19,19 +19,13 @@ use Sabre\Xml; */ class Base implements Xml\Element { - /** - * @var mixed PHP value to serialize - */ - protected $value; - /** * Constructor. * * @param mixed $value PHP value to serialize */ - public function __construct($value = null) + public function __construct(protected $value = null) { - $this->value = $value; } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Cdata.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Cdata.php index 8e16afbae..54e3879ee 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Cdata.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Cdata.php @@ -21,17 +21,15 @@ use Sabre\Xml; */ class Cdata implements Xml\XmlSerializable { - /** - * CDATA element value. - */ - protected string $value; - /** * Constructor. */ - public function __construct(string $value) - { - $this->value = $value; + public function __construct( + /** + * CDATA element value. + */ + protected string $value, + ) { } /** @@ -52,6 +50,6 @@ class Cdata implements Xml\XmlSerializable */ public function xmlSerialize(Xml\Writer $writer): void { - $writer->writeCData($this->value); + $writer->writeCdata($this->value); } } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Elements.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Elements.php index b62a2f3ea..44d296bd3 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Elements.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Elements.php @@ -37,21 +37,17 @@ use Sabre\Xml\Serializer; */ class Elements implements Xml\Element { - /** - * Value to serialize. - * - * @var array - */ - protected array $value; - /** * Constructor. * * @param array $value */ - public function __construct(array $value = []) - { - $this->value = $value; + public function __construct( + /** + * Value to serialize. + */ + protected array $value = [], + ) { } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/KeyValue.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/KeyValue.php index 54c31eb26..862c181c3 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/KeyValue.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/KeyValue.php @@ -37,21 +37,17 @@ use Sabre\Xml\Deserializer; */ class KeyValue implements Xml\Element { - /** - * Value to serialize. - * - * @var array - */ - protected array $value; - /** * Constructor. * * @param array $value */ - public function __construct(array $value = []) - { - $this->value = $value; + public function __construct( + /** + * Value to serialize. + */ + protected array $value = [], + ) { } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Uri.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Uri.php index e54d706a9..583aff618 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Uri.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/Uri.php @@ -26,17 +26,15 @@ use function Sabre\Uri\resolve; */ class Uri implements Xml\Element { - /** - * Uri element value. - */ - protected string $value; - /** * Constructor. */ - public function __construct(string $value) - { - $this->value = $value; + public function __construct( + /** + * Uri element value. + */ + protected string $value, + ) { } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/XmlFragment.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/XmlFragment.php index 94f745b4c..908fe5b22 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/XmlFragment.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Element/XmlFragment.php @@ -24,17 +24,15 @@ use Sabre\Xml\Writer; */ class XmlFragment implements Element { - /** - * The inner XML value. - */ - protected string $xml; - /** * Constructor. */ - public function __construct(string $xml) - { - $this->xml = $xml; + public function __construct( + /** + * The inner XML value. + */ + protected string $xml, + ) { } /** @@ -72,7 +70,7 @@ class XmlFragment implements Element {$this->getXml()} XML; - $reader->xml($xml); + $reader->XML($xml); while ($reader->read()) { if ($reader->depth < 1) { diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/LibXMLException.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/LibXMLException.php index 5c642a3bf..0f760c849 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/LibXMLException.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/LibXMLException.php @@ -17,13 +17,6 @@ use LibXMLError; */ class LibXMLException extends ParseException { - /** - * The error list. - * - * @var \LibXMLError[] - */ - protected array $errors; - /** * Creates the exception. * @@ -31,10 +24,12 @@ class LibXMLException extends ParseException * * @param \LibXMLError[] $errors */ - public function __construct(array $errors, int $code = 0, ?\Throwable $previousException = null) + public function __construct(/** + * The error list. + */ + protected array $errors, int $code = 0, ?\Throwable $previousException = null) { - $this->errors = $errors; - parent::__construct($errors[0]->message.' on line '.$errors[0]->line.', column '.$errors[0]->column, $code, $previousException); + parent::__construct($this->errors[0]->message.' on line '.$this->errors[0]->line.', column '.$this->errors[0]->column, $code, $previousException); } /** diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Reader.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Reader.php index a85bd058c..8d01f710a 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Reader.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Reader.php @@ -33,7 +33,7 @@ class Reader extends \XMLReader */ public function getClark(): ?string { - if (!$this->localName) { + if ('' === $this->localName) { return null; } @@ -67,7 +67,7 @@ class Reader extends \XMLReader if (!$this->read()) { $errors = libxml_get_errors(); libxml_clear_errors(); - if ($errors) { + if (count($errors) > 0) { throw new LibXMLException($errors); } } @@ -77,7 +77,7 @@ class Reader extends \XMLReader // last line of defense in case errors did occur above $errors = libxml_get_errors(); libxml_clear_errors(); - if ($errors) { + if (count($errors) > 0) { throw new LibXMLException($errors); } } finally { @@ -153,7 +153,7 @@ class Reader extends \XMLReader if (!$this->read()) { $errors = libxml_get_errors(); libxml_clear_errors(); - if ($errors) { + if (count($errors) > 0) { throw new LibXMLException($errors); } throw new ParseException('This should never happen (famous last words)'); @@ -165,7 +165,7 @@ class Reader extends \XMLReader if (!$this->isValid()) { $errors = libxml_get_errors(); - if ($errors) { + if (count($errors) > 0) { libxml_clear_errors(); throw new LibXMLException($errors); } @@ -199,7 +199,7 @@ class Reader extends \XMLReader } } - return $elements ?: $text; + return count($elements) > 0 ? $elements : $text; } /** @@ -210,8 +210,8 @@ class Reader extends \XMLReader $result = ''; $previousDepth = $this->depth; - while ($this->read() && $this->depth != $previousDepth) { - if (in_array($this->nodeType, [\XMLReader::TEXT, \XMLReader::CDATA, \XMLReader::WHITESPACE])) { + while ($this->read() && $this->depth !== $previousDepth) { + if (in_array($this->nodeType, [\XMLReader::TEXT, \XMLReader::CDATA, \XMLReader::WHITESPACE], true)) { $result .= $this->value; } } @@ -266,7 +266,7 @@ class Reader extends \XMLReader $attributes = []; while ($this->moveToNextAttribute()) { - if ($this->namespaceURI) { + if ('' !== $this->namespaceURI) { // Ignoring 'xmlns', it doesn't make any sense. if ('http://www.w3.org/2000/xmlns/' === $this->namespaceURI) { continue; @@ -290,10 +290,10 @@ class Reader extends \XMLReader public function getDeserializerForElementName(string $name): callable { if (!array_key_exists($name, $this->elementMap)) { - if ('{}' == substr($name, 0, 2) && array_key_exists(substr($name, 2), $this->elementMap)) { + if (str_starts_with($name, '{}') && array_key_exists(substr($name, 2), $this->elementMap)) { $name = substr($name, 2); } else { - return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize']; + return Element\Base::xmlDeserialize(...); } } @@ -302,8 +302,8 @@ class Reader extends \XMLReader return $deserializer; } - if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) { - return [$deserializer, 'xmlDeserialize']; + if (is_subclass_of($deserializer, XmlDeserializable::class)) { + return fn (Reader $reader) => $deserializer::xmlDeserialize($reader); } throw new \LogicException('Could not use this type as a deserializer: '.get_debug_type($deserializer).' for element: '.$name); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Serializer/functions.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Serializer/functions.php index 4e0856a0c..66685bf43 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Serializer/functions.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Serializer/functions.php @@ -123,7 +123,7 @@ function valueObject(Writer $writer, object $valueObject, string $namespace): vo * * You can even mix the two array syntaxes. * - * @param string|int|float|bool|array|object $value + * @param string|int|float|bool|array|object|null $value */ function standardSerializer(Writer $writer, $value): void { @@ -133,9 +133,9 @@ function standardSerializer(Writer $writer, $value): void } elseif ($value instanceof XmlSerializable) { // XmlSerializable classes or Element classes. $value->xmlSerialize($writer); - } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { + } elseif (is_object($value) && isset($writer->classMap[$value::class])) { // It's an object which class appears in the classmap. - $writer->classMap[get_class($value)]($writer, $value); + $writer->classMap[$value::class]($writer, $value); } elseif (is_callable($value)) { // A callback $value($writer); @@ -144,8 +144,8 @@ function standardSerializer(Writer $writer, $value): void // describes a 'name' and optionally 'attributes' and 'value'. $name = $value['name']; - $attributes = isset($value['attributes']) ? $value['attributes'] : []; - $value = isset($value['value']) ? $value['value'] : null; + $attributes = $value['attributes'] ?? []; + $value = $value['value'] ?? null; $writer->startElement($name); $writer->writeAttributes($attributes); @@ -157,7 +157,7 @@ function standardSerializer(Writer $writer, $value): void // This item has a numeric index. We just loop through the // array and throw it back in the writer. standardSerializer($writer, $item); - } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { + } elseif (is_array($item) && isset($item['attributes'])) { // The key is used for a name, but $item has 'attributes' and // possibly 'value' $writer->startElement($name); @@ -176,7 +176,7 @@ function standardSerializer(Writer $writer, $value): void } } } elseif (is_object($value)) { - throw new \InvalidArgumentException('The writer cannot serialize objects of class: '.get_class($value)); + throw new \InvalidArgumentException('The writer cannot serialize objects of class: '.$value::class); } elseif (!is_null($value)) { throw new \InvalidArgumentException('The writer cannot serialize values of type: '.get_debug_type($value)); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Service.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Service.php index 6e225bdfd..1f1b3585b 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Service.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Service.php @@ -123,7 +123,7 @@ class Service } // If input is empty, then it's safe to throw an exception - if (empty($input)) { + if ('' === $input) { throw new ParseException('The input element to parse is empty. Do not attempt to parse'); } @@ -174,7 +174,7 @@ class Service } // If input is empty, then it's safe to throw an exception - if (empty($input)) { + if ('' === $input) { throw new ParseException('The input element to parse is empty. Do not attempt to parse'); } @@ -254,14 +254,12 @@ class Service */ public function mapValueObject(string $elementName, string $className): void { - list($namespace) = self::parseClarkNotation($elementName); + [$namespace] = self::parseClarkNotation($elementName); require_once __DIR__ . '/Deserializer/functions.php'; - $this->elementMap[$elementName] = function (Reader $reader) use ($className, $namespace) { - return \Sabre\Xml\Deserializer\valueObject($reader, $className, $namespace); - }; + $this->elementMap[$elementName] = (fn (Reader $reader) => \Sabre\Xml\Deserializer\valueObject($reader, $className, $namespace)); require_once __DIR__ . '/Serializer/functions.php'; - $this->classMap[$className] = function (Writer $writer, $valueObject) use ($namespace) { + $this->classMap[$className] = function (Writer $writer, $valueObject) use ($namespace): void { \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace); }; $this->valueObjectMap[$className] = $elementName; @@ -280,12 +278,12 @@ class Service */ public function writeValueObject(object $object, ?string $contextUri = null): string { - if (!isset($this->valueObjectMap[get_class($object)])) { - throw new \InvalidArgumentException('"'.get_class($object).'" is not a registered value object class. Register your class with mapValueObject.'); + if (!isset($this->valueObjectMap[$object::class])) { + throw new \InvalidArgumentException('"'.$object::class.'" is not a registered value object class. Register your class with mapValueObject.'); } return $this->write( - $this->valueObjectMap[get_class($object)], + $this->valueObjectMap[$object::class], $object, $contextUri ); @@ -306,7 +304,7 @@ class Service static $cache = []; if (!isset($cache[$str])) { - if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { + if (1 !== preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { throw new \InvalidArgumentException('\''.$str.'\' is not a valid clark-notation formatted string'); } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Version.php index 340393169..88f4128a5 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Version.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Version.php @@ -16,5 +16,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.0.6'; + public const VERSION = '4.1.0'; } diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Writer.php b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Writer.php index d2fb04ccd..ed08eb216 100644 --- a/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Writer.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/Xml/Writer.php @@ -123,11 +123,11 @@ class Writer extends \XMLWriter public function startElement($name): bool { if ('{' === $name[0]) { - list($namespace, $localName) = + [$namespace, $localName] = Service::parseClarkNotation($name); if (array_key_exists($namespace, $this->namespaceMap)) { - $result = $this->startElementNS( + $result = $this->startElementNs( '' === $this->namespaceMap[$namespace] ? null : $this->namespaceMap[$namespace], $localName, null @@ -139,10 +139,8 @@ class Writer extends \XMLWriter $result = $this->startElement($localName); $this->writeAttribute('xmlns', ''); } else { - if (!isset($this->adhocNamespaces[$namespace])) { - $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); - } - $result = $this->startElementNS($this->adhocNamespaces[$namespace], $localName, $namespace); + $this->adhocNamespaces[$namespace] ??= 'x'.(count($this->adhocNamespaces) + 1); + $result = $this->startElementNs($this->adhocNamespaces[$namespace], $localName, $namespace); } } } else { @@ -151,7 +149,7 @@ class Writer extends \XMLWriter if (!$this->namespacesWritten) { foreach ($this->namespaceMap as $namespace => $prefix) { - $this->writeAttribute($prefix ? 'xmlns:'.$prefix : 'xmlns', $namespace); + $this->writeAttribute(is_string($prefix) && '' !== $prefix ? 'xmlns:'.$prefix : 'xmlns', $namespace); } $this->namespacesWritten = true; } @@ -248,11 +246,9 @@ class Writer extends \XMLWriter } // We don't know the namespace, we must add it in-line - if (!isset($this->adhocNamespaces[$namespace])) { - $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); - } + $this->adhocNamespaces[$namespace] ??= 'x'.(count($this->adhocNamespaces) + 1); - return $this->writeAttributeNS( + return $this->writeAttributeNs( $this->adhocNamespaces[$namespace], $localName, $namespace, diff --git a/snappymail/v/0.0.0/app/localization/ca/LICENSE-RainLoop b/snappymail/v/0.0.0/app/localization/ca/LICENSE-RainLoop new file mode 100644 index 000000000..1a6999c1a --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/LICENSE-RainLoop @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 RainLoopTeam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/snappymail/v/0.0.0/app/localization/ca/README.md b/snappymail/v/0.0.0/app/localization/ca/README.md new file mode 100644 index 000000000..17336a3c9 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/README.md @@ -0,0 +1,17 @@ +# Catalan translation + +Catalan (`ca`, Català) translation for SnappyMail. A complete linguistic pass +was applied on 2026-10-16; see [review notes](REVIEW.md). Further corrections +from Catalan-speaking users are welcome. + +Existing translations were adapted from RainLoop's official Catalan resources: +https://github.com/RainLoop/rainloop-webmail/tree/9bf8fa5b63d7e6871ee533126a7e1b1c85ee45b5/rainloop/v/0.0.0/app/localization + +SnappyMail-specific strings were translated for this contribution. Preserve the +original MIT copyright and permission notice in `LICENSE-RainLoop` when reusing +the adapted RainLoop texts. The translation includes user, admin and static +messages, plus the relative-time fallback resource. + +Native language selector on a test login page (no authenticated session or mail): + +![Català in the native selector](selector.png) diff --git a/snappymail/v/0.0.0/app/localization/ca/REVIEW.md b/snappymail/v/0.0.0/app/localization/ca/REVIEW.md new file mode 100644 index 000000000..ffa6bcdc5 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/REVIEW.md @@ -0,0 +1,43 @@ +# Catalan linguistic review — 2026-10-16 + +Reviewed the full user, administration, static and relative-time resources +against their English originals: 567 user entries, 140 administration entries +and 7 static entries. This pass changes 210 entries (161 user, +46 administration, 3 static), including typography. + +## Editorial criteria + +- Use the Softcatalà distinction between short imperative commands in buttons + (for example, *Desa*, *Xifra*) and the *vós* form when addressing users in + explanations, questions or instructions (*Introduïu*, *Seleccioneu*, *Voleu*). +- Prefer consistent terms such as *desar*, *xifrar/desxifrar*, *conversa*, + *text sense format* and *emmagatzematge*. Preserve proper names such as Sieve. +- Correct semantic errors, not only spelling: `STOP_LABEL` must stop further + processing, `LABEL_UNREAD` marks messages as unread, and the domain allow-list + describes permitted **users**, not domains. +- Distinguish read/display receipts from delivery receipts. Preserve all message + variables, including `%READ-RECEIPT%`. +- Remove inherited RainLoop product references from SnappyMail interface text. +- Preserve JSON keys, technical flags, placeholders and required HTML structure. + Use consistent apostrophes and quotation marks in displayed Catalan text. + +## Representative corrections + +| Before | After | Reason | +| --- | --- | --- | +| No aturis el processament de les regles | Atura el processament de les regles següents | The previous wording reversed the action. | +| No llegeixis els missatges seleccionats | Marca els missatges seleccionats com a no llegits | Describes changing the unread flag. | +| Net | Text sense format | Correct meaning of the plain-text editor option. | +| scripts de sedàs | scripts Sieve | Sieve is a protocol name. | +| Emmagatzeatge | Emmagatzematge | Spelling correction. | +| Llista de dominis… | Llista d’usuaris del domini… | The allow-list contains users. | +| El RainLoop està actualitzat. | SnappyMail està actualitzat. | Correct product name. | + +## References + +- [Softcatalà linguistic style](https://www.softcatala.org/guia-estil-de-softcatala/aspectes-linguistics/) +- [TERMCAT criteria for software localization](https://arxiu.termcat.cat/criteris/localitzacio-productes-informatics.pdf) + +This is a linguistic review of this contribution, not an endorsement by either +organization. Further review and corrections from Catalan-speaking maintainers +remain welcome. diff --git a/snappymail/v/0.0.0/app/localization/ca/admin.json b/snappymail/v/0.0.0/app/localization/ca/admin.json new file mode 100644 index 000000000..ef5639570 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/admin.json @@ -0,0 +1,182 @@ +{ + "GLOBAL": { + "ADD": "Afegeix", + "CLOSE": "Tanca", + "SAVE": "Desa", + "TEST": "Prova", + "UPDATE": "Actualitza", + "USERNAME": "Nom d’usuari", + "PASSWORD": "Contrasenya", + "CANCEL": "Cancel·la", + "SEARCH": "Cerca", + "YES": "Sí", + "NO": "No" + }, + "LOGIN": { + "LABEL_LOGIN": "Nom d’usuari", + "LABEL_TOTP": "Codi TOTP", + "BUTTON_LOGIN": "Accedeix al tauler d’administració" + }, + "TOP_PANEL": { + "LABEL_ADMIN_PANEL": "Tauler d’administració" + }, + "SETTINGS_LABELS": { + "GENERAL": "General", + "LOGIN": "Inici de sessió", + "BRANDING": "Imatge de marca", + "CONTACTS": "Contactes", + "DOMAINS": "Dominis", + "SECURITY": "Seguretat", + "PACKAGES": "Extensions", + "CONFIG": "Configuració", + "ABOUT": "Quant a" + }, + "TAB_GENERAL": { + "LEGEND_INTERFACE": "Interfície", + "LABEL_LANGUAGE": "Llengua", + "LABEL_LANGUAGE_ADMIN": "Llengua (administració)", + "LABEL_THEME": "Tema", + "LABEL_ALLOW_LANGUAGES_ON_SETTINGS": "Permet la selecció de llengua a la pantalla de configuració", + "LABEL_ALLOW_THEMES_ON_SETTINGS": "Permet la selecció del tema a la pantalla de configuració", + "LABEL_ALLOW_BACKGROUND_ON_SETTINGS": "Permet la selecció del fons a la pantalla de configuració", + "LABEL_SHOW_THUMBNAILS": "Mostra les miniatures (adjunts)", + "LEGEND_MAIN": "Principal", + "LABEL_ATTACHMENT_SIZE_LIMIT": "Límit de mida dels adjunts", + "LABEL_ALLOW_ADDITIONAL_ACCOUNTS": "Permet comptes addicionals", + "LABEL_ALLOW_IDENTITIES": "Permet identitats múltiples", + "ALERT_DATA_ACCESS": "La carpeta de dades és accessible des de l’exterior. Configureu el servidor web per impedir-hi l’accés extern. Més informació:", + "ALERT_WARNING": "Avís!", + "HTML_ALERT_WEAK_PASSWORD": "Esteu utilitzant la contrasenya d’administració predeterminada.\n
\nPer motius de seguretat,\ncanvieu-la\nara per una altra.\n" + }, + "TAB_LOGIN": { + "LEGEND_LOGIN_SCREEN": "Pantalla d’inici de sessió", + "LABEL_DEFAULT_DOMAIN": "Domini predeterminat", + "LABEL_DETERMINE_USER_DOMAIN": "Intenta determinar el domini de l’usuari", + "LABEL_ALLOW_LANGUAGES_ON_LOGIN": "Permet triar la llengua a la pantalla d’inici de sessió", + "LABEL_DETERMINE_USER_LANGUAGE": "Intenta determinar la llengua de l’usuari" + }, + "TAB_BRANDING": { + "LEGEND_BRANDING": "Imatge de marca", + "LABEL_PAGE_TITLE": "Títol de la pàgina", + "LABEL_LOADING_DESCRIPTION": "Text durant la càrrega", + "LABEL_FAVICON_URL": "URL de la icona de favorits" + }, + "TAB_CONTACTS": { + "LEGEND_CONTACTS": "Contactes", + "LEGEND_STORAGE": "Emmagatzematge (PDO)", + "LABEL_ENABLE_CONTACTS": "Activa els contactes", + "LABEL_ALLOW_SYNC": "Permet la sincronització dels contactes (amb el servidor extern CardDAV)", + "LABEL_STORAGE_TYPE": "Tipus", + "LABEL_STORAGE_DSN": "DSN (nom de la font de dades)", + "LABEL_STORAGE_USER": "Usuari", + "LABEL_STORAGE_PASSWORD": "Contrasenya", + "SQLITE_GLOBAL": "Utilitza una base de dades global en lloc d’una per compte", + "ALERT_NOTICE": "Atenció!", + "SUGGESTIONS_LIMIT": "Límit de suggeriments", + "HTML_ALERT_DO_NOT_USE_THIS_DATABASE": "No useu aquest tipus de base de dades amb un gran nombre d’usuaris actius.", + "HTML_ALERT_DOES_NOT_SUPPORTED": "El sistema no admet els contactes.\n
\nHeu d’instal·lar o activar l’extensió PDO (SQLite / MySQL / PostgreSQL) al servidor.\n" + }, + "TAB_DOMAINS": { + "LEGEND_DOMAINS": "Dominis", + "BUTTON_ADD_DOMAIN": "Afegeix un domini", + "BUTTON_ADD_ALIAS": "Afegeix un àlies", + "DELETE_ARE_YOU_SURE": "Voleu continuar?", + "HTML_DOMAINS_HELPER": "Llista de dominis als quals el correu web pot accedir.\n
\nFeu clic al nom d’un domini per configurar-lo.\n" + }, + "TAB_SECURITY": { + "LEGEND_SECURITY": "Seguretat", + "LABEL_USE_IMAGE_PROXY": "Utilitza un servidor intermediari local per a les imatges externes", + "LABEL_AUTO_VERIFY": "Verifica les signatures automàticament", + "LABEL_SHOW_PHP_INFO": "Mostra la informació de PHP", + "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credencials d’accés al tauler d’administració", + "LABEL_CURRENT_PASSWORD": "Contrasenya actual", + "LABEL_NEW_PASSWORD": "Contrasenya nova", + "LABEL_REPEAT_PASSWORD": "Repetiu la contrasenya", + "LABEL_REQUIRE_VERIFICATION": "Exigeix la verificació del certificat SSL utilitzat (IMAP / SMTP)", + "CRYPTO": "Criptografia", + "LABEL_ALLOW_SELF_SIGNED": "Permet els certificats autosignats" + }, + "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Activa les extensions", + "LEGEND_AVAILABLE_FOR_UPDATE": "Actualitzacions disponibles", + "LEGEND_AVAILABLE_FOR_INSTALLATION": "Extensions disponibles per instal·lar", + "LEGEND_INSTALLED_PACKAGES": "Paquets instal·lats", + "ALERT_CANNOT_ACCESS_REPOSITORY": "Ara no es pot accedir al repositori." + }, + "TAB_ABOUT": { + "LEGEND_ABOUT": "Quant a", + "LABEL_TAG_HINT": "Client de correu electrònic senzill, modern i ràpid basat en la web", + "LABEL_ALL_RIGHTS_RESERVED": "Tots els drets reservats.", + "HINT_READ_CHANGE_LOG": "Llegiu el registre de canvis abans d’actualitzar.", + "HINT_IS_UP_TO_DATE": "SnappyMail està actualitzat.", + "HTML_NEW_VERSION": "Hi ha disponible una versió nova: %VERSION%.", + "LABEL_UPDATING": "S’està actualitzant…", + "LABEL_CHECKING": "S’està comprovant si hi ha actualitzacions…", + "BUTTON_RELEASES": "Versions publicades" + }, + "POPUPS_DOMAIN_ALIAS": { + "TITLE_ADD_DOMAIN_ALIAS": "Afegeix un àlies", + "LABEL_ALIAS": "Àlies", + "LABEL_DOMAIN": "Domini" + }, + "POPUPS_DOMAIN": { + "TITLE_ADD_DOMAIN": "Afegeix un domini", + "TITLE_ADD_DOMAIN_WITH_NAME": "Afegeix el domini «%NAME%»", + "TITLE_EDIT_DOMAIN": "Edita el domini «%NAME%»", + "LABEL_NAME": "Nom", + "NAME_HELPER": "S’admet el comodí *", + "LABEL_SERVER": "Servidor", + "LABEL_PORT": "Port", + "LABEL_SECURE": "Seguretat", + "TIMEOUT": "Temps d’espera", + "DISABLE_CAPABILITIES": "Desactiva capacitats", + "LIMITS": "Límits", + "FOLDERS": "Carpetes", + "MESSAGES": "Missatges", + "SECURE_OPTION_NONE": "Cap", + "LABEL_ALLOW_SIEVE_SCRIPTS": "Permet scripts Sieve", + "LABEL_USE_SHORT_LOGIN": "Utilitza el nom d’usuari sense el domini", + "LABEL_LOWERCASE_LOGIN": "Nom d’usuari en minúscules, sense distingir majúscules i minúscules", + "LABEL_USE_AUTH": "Utilitza l’autenticació", + "LABEL_SET_SENDER": "Utilitza el nom d’usuari com a remitent", + "LABEL_AUTH_PLAIN_LINE": "Força l’autenticació amb una sola ordre AUTH PLAIN", + "LABEL_USE_PHP_MAIL": "Utilitza la funció mail() de PHP", + "BUTTON_WHITE_LIST": "Usuaris permesos", + "NEW_DOMAIN_DESC": "Aquesta configuració de domini us permetrà treballar amb les adreces de correu de %NAME%.\n", + "WHITE_LIST_ALERT": "Llista d’usuaris del domini que poden accedir al correu web.\nSepareu-los amb espais.\n", + "VALID_SPAM_HEADERS": "Capçaleres vàlides de correu brossa", + "VALID_VIRUS_HEADERS": "Capçaleres vàlides de virus" + }, + "POPUPS_PLUGIN": { + "TITLE_PLUGIN": "Extensió", + "DESC_NOTHING_TO_CONFIGURE": "No hi ha res per configurar." + }, + "POPUPS_ASK": { + "DESC_WANT_CLOSE_THIS_WINDOW": "Segur que voleu tancar aquesta finestra?" + }, + "POPUPS_LANGUAGES": { + "TITLE_LANGUAGES": "Trieu la llengua" + }, + "HINTS": { + "BETA": "beta", + "WARNING": "Avís!" + }, + "ERRORS": { + "DOMAIN_ALREADY_EXISTS": "El domini ja existeix.", + "UNKNOWN_ERROR": "Error desconegut" + }, + "NOTIFICATIONS": { + "RequestError": "La sol·licitud ha fallat", + "RequestAborted": "S’ha cancel·lat la sol·licitud", + "RequestTimeout": "S’ha esgotat el temps d’espera de la sol·licitud", + "InvalidToken": "Testimoni no vàlid", + "AuthError": "Ha fallat l’autenticació", + "DomainAlreadyExists": "El domini ja existeix.", + "CantSavePluginSettings": "No es pot desar la configuració.", + "CantInstallPackage": "No s’ha pogut instal·lar l’extensió", + "CantDeletePackage": "No s’ha pogut eliminar l’extensió", + "InvalidPluginPackage": "Paquet d’extensió no vàlid", + "UnsupportedPluginPackage": "Paquet d’extensió no compatible", + "UnknownError": "Error desconegut" + } +} diff --git a/snappymail/v/0.0.0/app/localization/ca/relativetimeformat.js b/snappymail/v/0.0.0/app/localization/ca/relativetimeformat.js new file mode 100644 index 000000000..a981c01f9 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/relativetimeformat.js @@ -0,0 +1,75 @@ +{ + "long": { + "year": { + "past": { + "one": "fa {0} any", + "other": "fa {0} anys" + }, + "future": { + "one": "d’aquí a {0} any", + "other": "d’aquí a {0} anys" + } + }, + "month": { + "past": { + "one": "fa {0} mes", + "other": "fa {0} mesos" + }, + "future": { + "one": "d’aquí a {0} mes", + "other": "d’aquí a {0} mesos" + } + }, + "week": { + "past": { + "one": "fa {0} setmana", + "other": "fa {0} setmanes" + }, + "future": { + "one": "d’aquí a {0} setmana", + "other": "d’aquí a {0} setmanes" + } + }, + "day": { + "past": { + "one": "fa {0} dia", + "other": "fa {0} dies" + }, + "future": { + "one": "d’aquí a {0} dia", + "other": "d’aquí a {0} dies" + } + }, + "hour": { + "past": { + "one": "fa {0} hora", + "other": "fa {0} hores" + }, + "future": { + "one": "d’aquí a {0} hora", + "other": "d’aquí a {0} hores" + } + }, + "minute": { + "past": { + "one": "fa {0} minut", + "other": "fa {0} minuts" + }, + "future": { + "one": "d’aquí a {0} minut", + "other": "d’aquí a {0} minuts" + } + }, + "second": { + "past": { + "one": "fa {0} segon", + "other": "fa {0} segons" + }, + "future": { + "one": "d’aquí a {0} segon", + "other": "d’aquí a {0} segons" + } + } + }, + plural: n => n === 1 ? "one" : "other" +} diff --git a/snappymail/v/0.0.0/app/localization/ca/selector.png b/snappymail/v/0.0.0/app/localization/ca/selector.png new file mode 100644 index 000000000..01bb1b564 Binary files /dev/null and b/snappymail/v/0.0.0/app/localization/ca/selector.png differ diff --git a/snappymail/v/0.0.0/app/localization/ca/static.json b/snappymail/v/0.0.0/app/localization/ca/static.json new file mode 100644 index 000000000..348cacc0e --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/static.json @@ -0,0 +1,9 @@ +{ + "BACK_LINK": "Torna a carregar", + "NO_SCRIPT_TITLE": "Aquesta aplicació necessita JavaScript.", + "NO_SCRIPT_DESC": "El navegador no té activat JavaScript.\nActiveu-lo a la configuració del navegador i torneu-ho a provar.", + "NO_COOKIE_TITLE": "Aquesta aplicació necessita galetes.", + "NO_COOKIE_DESC": "El navegador no té activades les galetes.\nActiveu-les a la configuració del navegador i torneu-ho a provar.", + "BAD_BROWSER_TITLE": "El navegador està obsolet.", + "BAD_BROWSER_DESC": "Per utilitzar totes les funcions de l’aplicació,\nbaixeu i instal·leu un navegador actualitzat." +} diff --git a/snappymail/v/0.0.0/app/localization/ca/user.json b/snappymail/v/0.0.0/app/localization/ca/user.json new file mode 100644 index 000000000..2c803dc84 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/ca/user.json @@ -0,0 +1,647 @@ +{ + "LANG_DIR": "ltr", + "GLOBAL": { + "TO_ARCHIVE": "Arxiva", + "ARE_YOU_SURE": "Voleu continuar?", + "BACK": "Enrere", + "BCC": "C/o", + "CANCEL": "Cancel·la", + "CC": "A/c", + "CLEAR": "Neteja", + "CLOSE": "Tanca", + "CONTACTS": "Contactes", + "DELETE": "Suprimeix", + "UNDELETE": "Restaura", + "DONE": "Fet", + "EMAIL": "Adreça de correu", + "FROM": "De", + "LOADING": "S’està carregant…", + "LOGOUT": "Tanca la sessió", + "MORE": "Més", + "MOVE_TO": "Mou a", + "COPY_TO": "Copia a", + "NAME": "Nom", + "NONE": "Cap", + "TO_SPAM": "Mou els missatges al correu brossa", + "NOT_SPAM": "No és correu brossa", + "USERNAME": "Nom d’usuari", + "PASSWORD": "Contrasenya", + "REMEMBER": "Recorda", + "REPLY_TO": "Respon a", + "SAVE": "Desa", + "SAVE_CHANGES": "Voleu desar els canvis?", + "SEARCH": "Cerca", + "SPAM": "Correu brossa", + "SUBJECT": "Assumpte", + "TEST": "Prova", + "TO": "Per a", + "SERVER_MESSAGE": "Missatge del servidor", + "YES": "Sí", + "NO": "No", + "INACTIVE": "Inactiu", + "DEFAULT": "predeterminat", + "HELP": "Ajuda", + "TEXT": "Text", + "ATTACHMENTS": "Adjunts" + }, + "LOGIN": { + "LABEL_LOGIN": "Nom d’usuari", + "LABEL_SIGN_ME": "Recorda’m", + "BUTTON_SIGN_IN": "Inicia la sessió" + }, + "TOP_TOOLBAR": { + "BUTTON_ADD_ACCOUNT": "Afegeix un compte" + }, + "SEARCH": { + "TITLE_ADV": "Cerca avançada", + "TEXT": "Text", + "HAS_ATTACHMENT": "Té fitxers adjunts", + "FLAGGED": "Destacats", + "UNSEEN": "No llegits", + "DATE": "Data", + "DATE_ALL": "Qualsevol data", + "BEFORE_3_DAYS": "De fa més de 3 dies", + "BEFORE_7_DAYS": "De fa més d’una setmana", + "BEFORE_MONTH": "De fa més d’un mes", + "BEFORE_3_MONTHS": "De fa més de 3 mesos", + "BEFORE_6_MONTHS": "De fa més de 6 mesos", + "BEFORE_YEAR": "De fa més d’un any", + "SINCE_3_DAYS": "Antiguitat de fins a 3 dies", + "SINCE_7_DAYS": "Antiguitat de fins a 1 setmana", + "SINCE_MONTH": "Antiguitat de fins a 1 mes", + "SINCE_3_MONTHS": "Antiguitat de fins a 3 mesos", + "SINCE_6_MONTHS": "Antiguitat de fins a 6 mesos", + "SINCE_YEAR": "Antiguitat de fins a 1 any", + "SUBFOLDERS": "Subcarpetes", + "SUBFOLDERS_NONE": "Cap", + "SUBFOLDERS_SUBTREE": "Totes", + "SUBFOLDERS_SUBTREE_ONE": "Un nivell", + "REPLIED": "Respostos", + "KEYWORD": "Etiqueta" + }, + "FOLDER_LIST": { + "BUTTON_COMPOSE": "Escriu", + "BUTTON_NEW_MESSAGE": "Missatge nou", + "INBOX_NAME": "Bústia d’entrada", + "SENT_NAME": "Enviats", + "DRAFTS_NAME": "Esborranys", + "TRASH_NAME": "Paperera", + "ARCHIVE_NAME": "Arxiu" + }, + "IMAP": { + "ACL_RIGHTS": "Permisos", + "ACL_A": "Administra", + "ACL_E": "Elimina definitivament", + "ACL_I": "Insereix missatges", + "ACL_K": "Crea subcarpetes", + "ACL_L": "Mostra els missatges", + "ACL_P": "Envia correu", + "ACL_R": "Selecciona una carpeta", + "ACL_S": "Etiqueta \\SEEN", + "ACL_T": "Etiqueta \\DELETED", + "ACL_W": "Altres etiquetes", + "ACL_X": "Elimina o canvia el nom de la carpeta" + }, + "MESSAGE_LIST": { + "BUTTON_RELOAD": "Torna a carregar la llista de missatges", + "BUTTON_EMPTY_FOLDER": "Buida la carpeta", + "BUTTON_MULTY_FORWARD": "Reenvia com a fitxers adjunts", + "BUTTON_DELETE_WITHOUT_MOVE": "Suprimeix definitivament", + "MENU_SET_SEEN": "Marca com a llegit", + "MENU_SET_ALL_SEEN": "Marca’ls tots com a llegits", + "MENU_UNSET_SEEN": "Marca com a no llegit", + "MENU_SET_FLAG": "Destaca", + "MENU_UNSET_FLAG": "Deixa de destacar", + "MENU_SELECT_ALL": "Selecciona-ho tot", + "MENU_SELECT_INVERT": "Inverteix la selecció", + "MENU_SELECT_UNSEEN": "No llegits", + "MENU_SELECT_SEEN": "Llegits", + "MENU_SELECT_FLAGGED": "Destacats", + "MENU_SELECT_UNFLAGGED": "Sense destacar", + "DOWNLOAD_ALL_ATTACHMENTS": "Baixa tots els fitxers adjunts", + "EMPTY_LIST": "No hi ha missatges.", + "EMPTY_SEARCH_LIST": "Cap missatge coincideix amb la cerca.", + "SEARCH_RESULT_FOR": "Resultats de la cerca de «%SEARCH%»", + "BACK_TO_MESSAGE_LIST": "Torna a la llista de missatges", + "PUT_MESSAGE_HERE": "Deixeu anar el missatge aquí per veure’l a la llista.", + "TODAY_AT": "avui a les %TIME%", + "YESTERDAY_AT": "ahir a les %TIME%", + "NEW_MESSAGE_NOTIFICATION": "Teniu %COUNT% missatges nous.", + "QUOTA_SIZE": "S’utilitzen %SIZE% (%PROC%%) de %LIMIT%", + "SORT": "Ordena", + "NO_SORT": "El servidor de correu no permet ordenar els missatges", + "SORT_ARRIVAL_ASC": "Data de recepció ascendent", + "SORT_ARRIVAL_DESC": "Data de recepció descendent", + "SORT_DATE_ASC": "Data ascendent", + "SORT_DATE_DESC": "Data descendent", + "SORT_SIZE_ASC": "Mida ascendent", + "SORT_SIZE_DESC": "Mida descendent", + "SORT_SUBJECT_ASC": "Assumpte A–Z", + "SORT_SUBJECT_DESC": "Assumpte Z–A", + "SORT_FROM_ASC": "Remitent A–Z", + "SORT_FROM_DESC": "Remitent Z–A" + }, + "MESSAGE": { + "BUTTON_EDIT": "Edita", + "BUTTON_UNSUBSCRIBE": "Cancel·la la subscripció a la llista", + "BUTTON_REPLY": "Respon", + "BUTTON_REPLY_ALL": "Respon a tothom", + "BUTTON_FORWARD": "Reenvia", + "BUTTON_FORWARD_AS_ATTACHMENT": "Reenvia com a fitxer adjunt", + "BUTTON_EDIT_AS_NEW": "Edita com a missatge nou", + "BUTTON_SHOW_IMAGES": "Mostra les imatges externes", + "BUTTON_NOTIFY_READ_RECEIPT": "El remitent ha demanat rebre una notificació quan llegiu aquest missatge.", + "BUTTON_IN_NEW_WINDOW": "Mostra en una finestra separada", + "BUTTON_THREAD_LIST": "Llista de missatges de la conversa", + "BUTTON_THREAD_PREV": "Anterior", + "BUTTON_THREAD_NEXT": "Següent", + "BUTTON_THREAD_MORE": "Més missatges", + "MENU_HEADERS": "Mostra les capçaleres del missatge", + "MENU_VIEW_ORIGINAL": "Mostra el codi font", + "HTML_VIEW": "Mostra el missatge en HTML", + "PLAIN_VIEW": "Mostra el missatge en text sense format", + "SWAP_COLORS": "Intercanvia el color de fons predeterminat", + "MENU_DOWNLOAD_ORIGINAL": "Baixa com a fitxer .eml", + "MENU_FILTER_SIMILAR": "Filtra missatges semblants", + "MENU_PRINT": "Imprimeix", + "EMPTY_SUBJECT_TEXT": "(Sense assumpte)", + "LABEL_DATE": "Data", + "LABEL_FROM_SHORT": "de", + "LABEL_TO_SHORT": "per a", + "PRINT_LABEL_ATTACHMENTS": "Adjunts", + "MESSAGE_VIEW_DESC": "Seleccioneu qualsevol missatge de la llista per veure’l aquí.", + "MESSAGE_VIEW_MOVE_DESC": "Feu clic al nom de la carpeta del tauler de l’esquerra per seleccionar la destinació.", + "LINK_DOWNLOAD_AS_ZIP": "Baixa en format ZIP", + "SPAM_SCORE": "Puntuació de correu brossa", + "HAS_VIRUS_WARNING": "AVÍS: s’ha detectat un virus", + "TAGS": "Etiquetes", + "NEW_TAG": "Etiqueta nova", + "ENABLE_TRACKING_LINKS": "Activa els enllaços de seguiment", + "TRACKING_ENABLED": "Ara es farà el seguiment dels enllaços quan hi feu clic." + }, + "MESSAGE_TAGS": { + "$important": "Important", + "$todo": "Per fer", + "$label1": "Important", + "$label2": "Feina", + "$label3": "Personal", + "$label4": "Per fer", + "$label5": "Més endavant" + }, + "READ_RECEIPT": { + "SUBJECT": "Confirmació de lectura (missatge mostrat) - %SUBJECT%", + "BODY": "Aquesta és una confirmació de lectura del missatge que vau enviar a %READ-RECEIPT%.\n\nNota: aquesta confirmació només indica que el missatge s’ha mostrat a l’ordinador del destinatari.\nNo garanteix que el destinatari hagi llegit o entès el contingut del missatge.\n" + }, + "SUGGESTIONS": { + "SEARCHING_DESC": "S’està cercant…" + }, + "CONTACTS": { + "TAB_CONTACT": "Contacte", + "TAB_LOCATIONS": "Ubicacions", + "TAB_BUSINESS": "Feina", + "LABEL_ORGANIZATION": "Organització", + "LABEL_DEPARTMENT": "Departament", + "LABEL_TITLE": "Càrrec", + "TAB_CRYPTO": "Criptografia", + "ASK": "Pregunta", + "NEVER": "Mai", + "ALWAYS": "Sempre", + "ALWAYS_IF_POSSIBLE": "Sempre que sigui possible", + "BUTTON_ADD_CONTACT": "Afegeix un contacte", + "BUTTON_CREATE_CONTACT": "Crea", + "BUTTON_IMPORT": "Importa (csv, vcf, vCard)", + "BUTTON_EXPORT_VCARD": "Exporta (vcf, vCard)", + "BUTTON_EXPORT_CSV": "Exporta (csv)", + "ERROR_IMPORT_FILE": "Error d’importació (format de fitxer no vàlid)", + "EMPTY_LIST": "Aquí no hi ha cap contacte", + "EMPTY_SEARCH": "No s’ha trobat cap contacte", + "CLEAR_SEARCH": "Neteja la cerca", + "CONTACT_VIEW_DESC": "Seleccioneu un contacte de la llista per veure’l aquí.", + "LABEL_BIRTHDAY": "Data de naixement", + "DISPLAY_NAME": "Nom per mostrar", + "LAST_NAME": "Cognoms", + "FIRST_NAME": "Nom", + "MIDDLE_NAME": "Segon nom", + "NAME_PREFIX": "Prefix del nom", + "NAME_SUFFIX": "Sufix del nom", + "NICK_NAME": "Àlies", + "LABEL_READ_ONLY": "Només de lectura", + "ADD_MENU_LABEL": "Afegeix", + "ADD_MENU_NICKNAME": "Àlies", + "ADD_MENU_NOTE": "Nota", + "ADD_MENU_PHONE": "Telèfon", + "ADD_MENU_URL": "URL", + "ADD_MENU_ADDRESS": "Adreça", + "ADD_MENU_BIRTHDAY": "Data de naixement", + "ADD_MENU_TAGS": "Etiquetes", + "BUTTON_SHARE_ALL": "Tothom", + "BUTTON_SYNC": "Sincronització (CardDAV)", + "SEND_TO_ALL_CONTACT_EMAILS": "Envia a totes les adreces del contacte" + }, + "COMPOSE": { + "LINK_SHOW_INPUTS": "Mostra tots els camps", + "BUTTON_SEND": "Envia", + "BUTTON_MINIMIZE": "Minimitza", + "SAVED_TIME": "Desat a les %TIME%", + "SAVED_ERROR_ON_SEND": "El missatge s’ha enviat, però no s’ha desat a la carpeta de missatges enviats.", + "DISCARD_UNSAVED_DATA": "Voleu descartar les dades no desades?", + "ATTACH_FILES": "Adjunta fitxers", + "ATTACH_DROP_FILES_DESC": "Deixeu anar fitxers aquí", + "REPLY_MESSAGE_TITLE": "%DATETIME%, %EMAIL% va escriure", + "FORWARD_MESSAGE_TOP_TITLE": "-------- Missatge reenviat -------", + "FORWARD_MESSAGE_TOP_SENT": "Enviat", + "EMPTY_TO_ERROR_DESC": "Indiqueu almenys un destinatari.", + "NO_ATTACHMENTS_HERE_DESC": "No hi ha cap fitxer adjunt.", + "ATTACHMENTS_ERROR_DESC": "No s’han pogut pujar tots els fitxers adjunts.", + "ATTACHMENTS_UPLOAD_ERROR_DESC": "Encara s’estan pujant fitxers adjunts.", + "ALLOW_SPELLCHECK": "Activa el corrector ortogràfic del navegador", + "ALLOW_DRAFT_AUTOSAVE": "Desa l’esborrany automàticament", + "BUTTON_REQUEST_READ_RECEIPT": "Demana una confirmació de lectura", + "BUTTON_MARK_AS_IMPORTANT": "Marca com a important", + "BUTTON_REQUEST_DSN": "Demana una confirmació de lliurament", + "BUTTON_REQUIRE_TLS": "Exigeix un lliurament segur", + "ERROR_EMPTY_BODY": "El cos del missatge és buit" + }, + "POPUPS_ASK": { + "EXIT_ARE_YOU_SURE": "Segur que voleu sortir?", + "DESC_WANT_CLOSE_THIS_WINDOW": "Segur que voleu tancar aquesta finestra?", + "DESC_WANT_DELETE_MESSAGES": "Segur que voleu suprimir els missatges?" + }, + "POPUPS_LANGUAGES": { + "TITLE_LANGUAGES": "Trieu la llengua" + }, + "POPUPS_ADD_ACCOUNT": { + "TITLE_ADD_ACCOUNT": "Afegeix un compte", + "BUTTON_ADD_ACCOUNT": "Afegeix-lo", + "TITLE_UPDATE_ACCOUNT": "Actualitza el compte" + }, + "POPUPS_IDENTITY": { + "TITLE_ADD_IDENTITY": "Afegeix una identitat", + "TITLE_UPDATE_IDENTITY": "Actualitza la identitat", + "LABEL": "Etiqueta", + "BUTTON_ADD_IDENTITY": "Afegeix-la", + "SIGNATURE": "Signatura", + "LABEL_SIGNATURE_INSERT_BEFORE": "Insereix aquesta signatura abans del text citat en les respostes." + }, + "POPUPS_CREATE_FOLDER": { + "TITLE_CREATE_FOLDER": "Crea una carpeta", + "LABEL_PARENT": "Carpeta superior", + "BUTTON_CREATE": "Crea-la", + "TITLE_CREATING_PROCESS": "S’està creant la carpeta…" + }, + "POPUPS_CLEAR_FOLDER": { + "TITLE_CLEAR_FOLDER": "Voleu suprimir tots els missatges de la carpeta?", + "DANGER_DESC_WARNING": "Avís", + "DANGER_DESC_HTML_1": "Aquesta acció suprimirà definitivament tots els missatges de la carpeta %FOLDER%.", + "DANGER_DESC_HTML_2": "Un cop iniciat, el procés no es pot aturar ni cancel·lar.", + "TITLE_CLEARING_PROCESS": "S’està buidant la carpeta…" + }, + "CRYPTO": { + "SIGN": "Signa", + "ENCRYPT": "Xifra", + "DECRYPT": "Desxifra", + "VERIFY": "Verifica", + "GOOD_SIGNATURE": "Signatura vàlida de %USER%", + "ERROR": "Error de %TYPE%: %ERROR%", + "CREATE_SELF_SIGNED": "Crea un certificat autosignat", + "VALID_UNTIL": "Vàlid fins al", + "PRIVATE_KEY": "Clau privada", + "CHANGE_PASS": "Canvia la contrasenya", + "CURRENT_PASS": "Contrasenya actual", + "NEW_PASS": "Contrasenya nova", + "ASK_CRYPTKEY_PASS": "La contrasenya d’accés ha canviat. Introduïu la contrasenya anterior per desxifrar les dades." + }, + "OPENPGP": { + "POPUP_IMPORT_TITLE": "Importa una clau OpenPGP", + "POPUP_IMPORT_BUTTON": "Importa-la", + "POPUP_VIEW_TITLE": "Mostra una clau OpenPGP", + "POPUP_VIEW_BUTTON": "Selecciona", + "POPUP_GENERATE_TITLE": "Genera un parell de claus OpenPGP", + "POPUP_GENERATE_BUTTON": "Genera", + "LABEL_KEY_TYPE": "Tipus", + "SIGNED_MESSAGE": "Missatge signat amb OpenPGP", + "ENCRYPTED_MESSAGE": "Missatge xifrat amb OpenPGP", + "STORE_IN_GNUPG": "Desa al servidor amb GnuPG", + "STORE_PUBLIC_KEY_IN_GNUPG": "Desa la clau pública al servidor amb GnuPG", + "STORE_PRIVATE_KEY_IN_GNUPG": "Desa la clau privada al servidor amb GnuPG", + "BACKUP_ON_SERVER": "Desa al servidor amb xifratge", + "BACKUP_PUBLIC_KEY_ON_SERVER": "Desa una còpia de la clau pública al servidor", + "BACKUP_PRIVATE_KEY_ON_SERVER": "Desa una còpia de la clau privada al servidor" + }, + "SMIME": { + "POPUP_IMPORT_TITLE": "Importa un certificat S/MIME", + "CERTIFICATE": "Certificat", + "CERTIFICATES": "Certificats S/MIME", + "SIGNED_MESSAGE": "Missatge signat amb S/MIME", + "ENCRYPTED_MESSAGE": "Missatge xifrat amb S/MIME", + "PRIVATE_KEY_OF": "Clau privada S/MIME de %EMAIL%" + }, + "POPUPS_FILTER": { + "TITLE_CREATE_FILTER": "Crea un filtre", + "TITLE_EDIT_FILTER": "Edita el filtre", + "LEGEND_CONDITIONS": "Condicions", + "LEGEND_ACTIONS": "Accions", + "BUTTON_ADD_CONDITION": "Afegeix-hi una condició", + "SELECT_ACTION_MOVE_TO": "Mou a", + "SELECT_ACTION_FORWARD_TO": "Reenvia a", + "SELECT_ACTION_REJECT": "Rebutja", + "SELECT_ACTION_VACATION_MESSAGE": "Missatge de vacances", + "SELECT_ACTION_DISCARD": "Descarta-ho", + "SELECT_FIELD_RECIPIENTS": "Destinataris (Per a o A/c)", + "SELECT_FIELD_HEADER": "Capçalera", + "SELECT_FIELD_BODY": "Cos", + "SELECT_FIELD_SIZE": "Mida", + "SELECT_TYPE_CONTAINS": "Conté", + "SELECT_TYPE_NOT_CONTAINS": "No conté", + "SELECT_TYPE_MATCHES": "Coincideix amb (s’admeten * i ?)", + "SELECT_TYPE_NOT_MATCHES": "No coincideix amb (s’admeten * i ?)", + "SELECT_TYPE_REGEXP": "Expressió regular", + "SELECT_TYPE_NOT_REGEXP": "No coincideix amb l’expressió regular", + "SELECT_TYPE_EQUAL_TO": "Igual a", + "SELECT_TYPE_NOT_EQUAL_TO": "No igual a", + "SELECT_TYPE_OVER": "Més gran que", + "SELECT_TYPE_UNDER": "Més petit que", + "SELECT_TYPE_TEXT": "Text", + "SELECT_TYPE_RAW": "Sense processar", + "SELECT_MATCH_ANY": "Coincideix amb QUALSEVOL de les regles següents", + "SELECT_MATCH_ALL": "Coincideix amb TOTES les regles següents", + "MARK_AS_READ_LABEL": "Marca com a llegit", + "REPLY_INTERVAL_LABEL": "Interval de resposta (dies)", + "KEEP_LABEL": "Mantén-ho", + "STOP_LABEL": "Atura el processament de les regles següents", + "VACATION_SUBJECT_LABEL": "Assumpte (opcional)", + "VACATION_MESSAGE_LABEL": "Missatge", + "VACATION_RECIPIENTS_LABEL": "Destinataris (separats per comes)", + "REJECT_MESSAGE_LABEL": "Missatge de rebuig", + "ALL_INCOMING_MESSAGES_DESC": "Tots els missatges d’entrada" + }, + "POPUPS_SIEVE_SCRIPT": { + "TITLE_CREATE": "Crea un script", + "TITLE_EDIT": "Edita l’script", + "BUTTON_ADD_FILTER": "Afegeix-hi un filtre", + "BUTTON_RAW_SCRIPT": "Utilitza un script d’usuari personalitzat", + "CAPABILITY_LABEL": "Capacitats", + "CHANGES_NEED_TO_BE_SAVED_DESC": "Cal desar aquests canvis al servidor." + }, + "POPUPS_SYSTEM_FOLDERS": { + "TITLE_SYSTEM_FOLDERS": "Seleccioneu carpetes del sistema", + "SELECT_CHOOSE_ONE": "Trieu-ne una", + "SELECT_UNUSE_NAME": "No l’utilitzis", + "NOTIFICATION_SENT": "No heu seleccionat la carpeta del sistema «Enviats», on es desen els missatges enviats.\nSi no els voleu desar, seleccioneu l’opció «No l’utilitzis».\n", + "NOTIFICATION_DRAFTS": "No heu seleccionat la carpeta del sistema «Esborranys», on es desen els missatges mentre els redacteu.", + "NOTIFICATION_SPAM": "No heu seleccionat la carpeta del sistema «Correu brossa», on es mouen els missatges marcats com a correu brossa.\nSi els voleu suprimir definitivament, seleccioneu l’opció «No l’utilitzis».\n", + "NOTIFICATION_TRASH": "No heu seleccionat la carpeta del sistema «Paperera», on es mouen els missatges suprimits.\nSi els voleu suprimir definitivament, seleccioneu l’opció «No l’utilitzis».\n", + "NOTIFICATION_ARCHIVE": "No heu seleccionat la carpeta del sistema «Arxiu», on es desen els missatges arxivats." + }, + "TITLES": { + "MAILBOX": "Bústia de correu", + "SETTINGS": "Configuració" + }, + "UPLOAD": { + "ERROR_FILE_IS_TOO_BIG": "El fitxer és massa gran", + "ERROR_FILE_PARTIALLY_UPLOADED": "El fitxer només s’ha carregat parcialment a causa d’un error desconegut", + "ERROR_NO_FILE_UPLOADED": "No s’ha carregat cap fitxer", + "ERROR_MISSING_TEMP_FOLDER": "Manca el fitxer temporal", + "ERROR_ON_SAVING_FILE": "S’ha produït un error en desar el fitxer.", + "ERROR_FILE_TYPE": "Tipus de fitxer no vàlid", + "ERROR_UNKNOWN": "S’ha produït un error desconegut en pujar el fitxer." + }, + "EDITOR": { + "PLAIN": "HTML <-> TEXT", + "SOURCE": "HTML <-> CODI FONT", + "FONTFAMILY": "Tipus de lletra", + "FONTSIZE": "Mida de la lletra", + "DIR": "Direcció del text", + "DIR_LTR": "D’esquerra a dreta", + "DIR_RTL": "De dreta a esquerra", + "DIR_AUTO": "Automàtica", + "TEXTCOLOR": "Color del text", + "BACKGROUNDCOLOR": "Color de fons", + "BOLD": "Negreta", + "ITALIC": "Cursiva", + "UNDERLINE": "Subratllat", + "STRIKE": "Ratllat", + "SUB": "Subíndex", + "SUP": "Superíndex", + "OL": "Llista numerada", + "UL": "Llista amb pics", + "QUOTE": "Citació", + "INDENTDECREASE": "Disminueix el sagnat", + "INDENTINCREASE": "Augmenta el sagnat", + "LINK": "Enllaç", + "IMAGEURL": "URL de la imatge", + "IMAGEUPLOAD": "Selecciona una imatge", + "UNDO": "Desfés", + "REDO": "Refés", + "REMOVESTYLE": "Elimina el format" + }, + "SETTINGS_LABELS": { + "GENERAL": "General", + "CONTACTS": "Contactes", + "FOLDERS": "Carpetes", + "ACCOUNTS": "Comptes", + "FILTERS": "Filtres", + "SECURITY": "Seguretat", + "THEMES": "Temes" + }, + "SETTINGS_FILTERS": { + "SIMPLE": "Simple", + "ADVANCED": "Avançat", + "BUTTON_ADD_SCRIPT": "Afegeix un script", + "SUBNAME_MOVE_TO": "Mou a «%FOLDER%»", + "SUBNAME_FORWARD_TO": "Reenvia a «%EMAIL%»", + "SUBNAME_REJECT": "Rebutja", + "SUBNAME_VACATION_MESSAGE": "Missatge de vacances", + "SUBNAME_DISCARD": "Descarta-ho" + }, + "SETTINGS_SECURITY": { + "LEGEND_SECURITY": "Seguretat", + "LABEL_AUTOLOGOUT": "Tancament automàtic de sessió", + "FORGET_KEY_PASS": "Oblida la contrasenya de la clau privada", + "NEVER": "Mai" + }, + "SETTINGS_GENERAL": { + "LANGUAGE": "Llengua", + "HOUR_FORMAT": "Format de l’hora", + "IDENTITY": "Identitat", + "LAYOUT": "Disposició", + "LAYOUT_NO_SPLIT": "Sense divisió", + "LAYOUT_VERTICAL_SPLIT": "Divisió vertical", + "LAYOUT_HORIZONTAL_SPLIT": "Divisió horitzontal", + "SHOW_NEXT_MESSAGE": "Mostra el missatge següent en moure o eliminar el missatge actual", + "EDITOR": "Editor de text predeterminat", + "EDITOR_HTML": "HTML", + "EDITOR_PLAIN": "Text sense format", + "VIEW_OPTIONS": "Opcions de visualització", + "USE_PREVIEW_PANE": "Utilitza la subfinestra de previsualització", + "HIDE_DELETED_MESSAGES": "Amaga els missatges marcats per eliminar", + "USE_CHECKBOXES_IN_LIST": "Mostra les caselles a la llista", + "USE_THREADS": "Agrupa els missatges en converses", + "THREAD_ALGORITHM": "Algorisme d’agrupació de converses", + "REPLY_SAME_FOLDER": "Desa les respostes a la carpeta del missatge al qual es respon", + "PREFER_HTML": "Prefereix HTML al text sense format", + "PREFER_HTML_INFO": "De vegades els missatges inclouen tots dos formats. Aquesta opció permet triar si es mostra la part HTML o la part de text sense format.", + "REMOVE_COLORS": "Elimina els colors de fons i del text del cos del missatge", + "ALLOW_STYLES": "Permet CSS amb