/** * Recursively decodes the WBXML from input stream. This means that if this * message contains complex types (like Appointment.Recuurence for example) * the sub-objects are auto-instantiated and decoded as well. Places the * decoded objects in the local properties array. * * @param Horde_ActiveSync_Wbxml_Decoder The stream decoder * * @throws Horde_ActiveSync_Exception */ public function decodeStream(Horde_ActiveSync_Wbxml_Decoder &$decoder) { while (1) { $entity = $decoder->getElement(); if ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { if (!($entity[Horde_ActiveSync_Wbxml::EN_FLAGS] & Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT)) { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (!isset($map[self::KEY_TYPE])) { $this->{$map[self::KEY_ATTRIBUTE]} = ''; } elseif ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) { $this->{$map[self::KEY_ATTRIBUTE]} = ''; } continue; } // Found start tag if (!isset($this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]])) { $this->_logger->err(sprintf('Tag %s unexpected in type XML type %s.', $entity[Horde_ActiveSync_Wbxml::EN_TAG], get_class($this))); throw new Horde_ActiveSync_Exception('Unexpected tag'); } else { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (isset($map[self::KEY_VALUES])) { // Handle arrays of attribute values while (1) { // If we can have multiple types of objects in this // container, or we are parsing a NO_CONTAINER, // check that we are not at the end tag of the // or we have a valid start tag for the NO_CONTAINER // object. If not, break out of loop. if (is_array($map[self::KEY_VALUES])) { $token = $decoder->peek(); if ($token[Horde_ActiveSync_Wbxml_Decoder::EN_TYPE] == Horde_ActiveSync_Wbxml_Decoder::EN_TYPE_ENDTAG) { break; } } elseif (!(isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER) && !$decoder->getElementStartTag($map[self::KEY_VALUES])) { break; } // We know we have some valid value, parse out what // it is. Either an array of (possibly varied) // objects, a single object, or simple value. if (is_array($map[self::KEY_VALUES])) { $token = $decoder->getToken(); if (($idx = array_search($token[Horde_ActiveSync_Wbxml_Decoder::EN_TAG], $map[self::KEY_VALUES])) !== false) { $class = $map[self::KEY_TYPE][$idx]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->commandType = $this->commandType; $decoded->decodeStream($decoder); } else { throw new Horde_ActiveSync_Exception('Error in message map configuration'); } } elseif (isset($map[self::KEY_TYPE])) { $class = $map[self::KEY_TYPE]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->commandType = $this->commandType; $decoded->decodeStream($decoder); } else { $decoded = $decoder->getElementContent(); } // Assign the parsed value to the mapped attribute. if (!isset($this->{$map[self::KEY_ATTRIBUTE]})) { $this->{$map[self::KEY_ATTRIBUTE]} = array($decoded); } else { $this->{$map[self::KEY_ATTRIBUTE]}[] = $decoded; } // Get the end tag of this attribute node. if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } // For NO_CONTAINER attributes, need some magic to // make sure we break out properly. if (isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER) { $e = $decoder->peek(); // Go back to the initial while if another block // of a non-container element is found. if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { continue 2; } // Break on end tag because no other container // elements block end is reached. if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG || empty($e)) { break; } } } // Do not get container end tag for an array without a container if (!(isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER) && !$decoder->getElementEndTag()) { return false; } } else { // Handle a simple attribute value if (isset($map[self::KEY_TYPE])) { if (in_array($map[self::KEY_TYPE], array(self::TYPE_DATE, self::TYPE_DATE_DASHES, self::TYPE_DATE_LOCAL))) { $decoded = $this->_parseDate($decoder->getElementContent()); } elseif ($map[self::KEY_TYPE] == self::TYPE_HEX) { $decoded = self::_hex2bin($decoder->getElementContent()); } else { // Complex type, decode recursively $class = $map[self::KEY_TYPE]; $subdecoder = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $subdecoder->commandType = $this->commandType; $subdecoder->decodeStream($decoder); $decoded = $subdecoder; } } else { // Simple type, just get content $decoded = $decoder->getElementContent(); if ($decoded === false) { $decoded = ''; $this->_logger->notice(sprintf('Unable to get expected content for %s: Setting to an empty string.', $entity[Horde_ActiveSync_Wbxml::EN_TAG])); } } if (!$decoder->getElementEndTag()) { $this->_logger->err(sprintf('Unable to get end tag for %s.', $entity[Horde_ActiveSync_Wbxml::EN_TAG])); throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } $this->{$map[self::KEY_ATTRIBUTE]} = $decoded; } } } elseif ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { $decoder->_ungetElement($entity); break; } else { $this->_logger->err('Unexpected content in type'); break; } } if (!$this->_validateDecodedValues()) { throw new Horde_ActiveSync_Exception(sprintf('Invalid values detected in %s.', get_class($this))); } }