Index: /contrib/z-push/wbxml.php
===================================================================
--- /contrib/z-push/wbxml.php	(revision 4898)
+++ /contrib/z-push/wbxml.php	(revision 4898)
@@ -0,0 +1,727 @@
+<?php
+/***********************************************
+* File      :   wbxml.php
+* Project   :   Z-Push
+* Descr     :   WBXML mapping file
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+include_once('debug.php');
+
+define('WBXML_DEBUG', false);
+
+define('WBXML_SWITCH_PAGE',     0x00);
+define('WBXML_END',             0x01);
+define('WBXML_ENTITY',          0x02);
+define('WBXML_STR_I',           0x03);
+define('WBXML_LITERAL',         0x04);
+define('WBXML_EXT_I_0',         0x40);
+define('WBXML_EXT_I_1',         0x41);
+define('WBXML_EXT_I_2',         0x42);
+define('WBXML_PI',              0x43);
+define('WBXML_LITERAL_C',       0x44);
+define('WBXML_EXT_T_0',         0x80);
+define('WBXML_EXT_T_1',         0x81);
+define('WBXML_EXT_T_2',         0x82);
+define('WBXML_STR_T',           0x83);
+define('WBXML_LITERAL_A',       0x84);
+define('WBXML_EXT_0',           0xC0);
+define('WBXML_EXT_1',           0xC1);
+define('WBXML_EXT_2',           0xC2);
+define('WBXML_OPAQUE',          0xC3);
+define('WBXML_LITERAL_AC',      0xC4);
+
+define('EN_TYPE',               1);
+define('EN_TAG',                2);
+define('EN_CONTENT',            3);
+define('EN_FLAGS',              4);
+define('EN_ATTRIBUTES',         5);
+
+define('EN_TYPE_STARTTAG',      1);
+define('EN_TYPE_ENDTAG',        2);
+define('EN_TYPE_CONTENT',       3);
+
+define('EN_FLAGS_CONTENT',      1);
+define('EN_FLAGS_ATTRIBUTES',   2);
+
+class WBXMLDecoder {
+    var $dtd;
+    var $in;
+
+    var $version;
+    var $publicid;
+    var $publicstringid;
+    var $charsetid;
+    var $stringtable;
+
+    var $tagcp = 0;
+    var $attrcp = 0;
+
+    var $ungetbuffer;
+
+    var $logStack = array();
+
+    function WBXMLDecoder($input, $dtd) {
+        $this->in = $input;
+        $this->dtd = $dtd;
+
+        $this->version = $this->getByte();
+        $this->publicid = $this->getMBUInt();
+        if($this->publicid == 0) {
+            $this->publicstringid = $this->getMBUInt();
+        }
+
+        $this->charsetid = $this->getMBUInt();
+        $this->stringtable = $this->getStringTable();
+
+    }
+
+    // Returns either start, content or end, and auto-concatenates successive content
+    function getElement()
+    {
+        $element = $this->getToken();
+
+        switch($element[EN_TYPE]) {
+            case EN_TYPE_STARTTAG:
+                return $element;
+            case EN_TYPE_ENDTAG:
+                return $element;
+            case EN_TYPE_CONTENT:
+                while(1) {
+                    $next = $this->getToken();
+                    if($next == false)
+                        return false;
+                    else if($next[EN_TYPE] == EN_CONTENT) {
+                        $element[EN_CONTENT] .= $next[EN_CONTENT];
+                    } else {
+                        $this->ungetElement($next);
+                        break;
+                    }
+                }
+                return $element;
+        }
+
+        return false;
+    }
+
+    function peek()
+    {
+        $element = $this->getElement();
+
+        $this->ungetElement($element);
+
+        return $element;
+    }
+
+    function getElementStartTag($tag)
+    {
+        $element = $this->getToken();
+
+        if($element[EN_TYPE] == EN_TYPE_STARTTAG && $element[EN_TAG] == $tag)
+            return $element;
+        else {
+            debug("Unmatched tag $tag:");
+            debug(print_r($element,true));
+            $this->ungetElement($element);
+        }
+
+        return false;
+    }
+
+    function getElementEndTag()
+    {
+        $element = $this->getToken();
+
+        if($element[EN_TYPE] == EN_TYPE_ENDTAG)
+            return $element;
+        else {
+            debug("Unmatched end tag:");
+            debug(print_r($element,true));
+            $bt = debug_backtrace();
+            $c = count($bt);
+            debugLog(print_r($bt,true));
+            debug("From " . $bt[$c-2]["file"] . ":" . $bt[$c-2]["line"]);
+            $this->ungetElement($element);
+        }
+
+        return false;
+    }
+
+    function getElementContent()
+    {
+        $element = $this->getToken();
+
+        if($element[EN_TYPE] == EN_TYPE_CONTENT) {
+            return $element[EN_CONTENT];
+        } else {
+            debug("Unmatched content:");
+            debug(print_r($element, true));
+            $this->ungetElement($element);
+        }
+
+        return false;
+    }
+
+    // ---------------------- Private functions ------------------------
+
+    function getToken() {
+        // See if there's something in the ungetBuffer
+        if($this->ungetbuffer) {
+            $element = $this->ungetbuffer;
+            $this->ungetbuffer = false;
+            return $element;
+        }
+
+        $el = $this->_getToken();
+
+        $this->logToken($el);
+
+        return $el;
+    }
+
+    function logToken($el) {
+        if(!WBXML_DEBUG)
+            return;
+        $spaces = str_repeat(" ", count($this->logStack));
+
+        switch($el[EN_TYPE]) {
+            case EN_TYPE_STARTTAG:
+                if($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
+                    debugLog("I " . $spaces . " <". $el[EN_TAG] . ">");
+                    array_push($this->logStack, $el[EN_TAG]);
+                } else
+                    debugLog("I " . $spaces . " <" . $el[EN_TAG] . "/>");
+
+                break;
+            case EN_TYPE_ENDTAG:
+                $tag = array_pop($this->logStack);
+                debugLog("I " . $spaces . "</" . $tag . ">");
+                break;
+            case EN_TYPE_CONTENT:
+                debugLog("I " . $spaces . " " . $el[EN_CONTENT]);
+                break;
+        }
+    }
+
+    // Returns either a start tag, content or end tag
+    function _getToken() {
+
+        // Get the data from the input stream
+        $element = array();
+
+        while(1) {
+            $byte = $this->getByte();
+
+            if(!isset($byte))
+                break;
+
+            switch($byte) {
+                case WBXML_SWITCH_PAGE:
+                    $this->tagcp = $this->getByte();
+                    continue;
+
+                case WBXML_END:
+                    $element[EN_TYPE] = EN_TYPE_ENDTAG;
+                    return $element;
+
+                case WBXML_ENTITY:
+                    $entity = $this->getMBUInt();
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
+                    $element[EN_CONTENT] = $this->entityToCharset($entity);
+                    return $element;
+
+                case WBXML_STR_I:
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
+                    $element[EN_CONTENT] = $this->getTermStr();
+                    return $element;
+
+                case WBXML_LITERAL:
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
+                    $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
+                    $element[EN_FLAGS] = 0;
+                    return $element;
+
+                case WBXML_EXT_I_0:
+                case WBXML_EXT_I_1:
+                case WBXML_EXT_I_2:
+                    $this->getTermStr();
+                    // Ignore extensions
+                    continue;
+
+                case WBXML_PI:
+                    // Ignore PI
+                    $this->getAttributes();
+                    continue;
+
+                case WBXML_LITERAL_C:
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
+                    $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
+                    $element[EN_FLAGS] = EN_FLAGS_CONTENT;
+                    return $element;
+
+                case WBXML_EXT_T_0:
+                case WBXML_EXT_T_1:
+                case WBXML_EXT_T_2:
+                    $this->getMBUInt();
+                    // Ingore extensions;
+                    continue;
+
+                case WBXML_STR_T:
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
+                    $element[EN_CONTENT] = $this->getStringTableEntry($this->getMBUInt());
+                    return $element;
+
+                case WBXML_LITERAL_A:
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
+                    $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
+                    $element[EN_ATTRIBUTES] = $this->getAttributes();
+                    $element[EN_FLAGS] = EN_FLAGS_ATTRIBUTES;
+                    return $element;
+                case WBXML_EXT_0:
+                case WBXML_EXT_1:
+                case WBXML_EXT_2:
+                    continue;
+
+                case WBXML_OPAQUE:
+                    $length = $this->getMBUInt();
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
+                    $element[EN_CONTENT] = $this->getOpaque($length);
+                    return $element;
+
+                case WBXML_LITERAL_AC:
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
+                    $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
+                    $element[EN_ATTRIBUTES] = $this->getAttributes();
+                    $element[EN_FLAGS] = EN_FLAGS_ATTRIBUTES | EN_FLAGS_CONTENT;
+                    return $element;
+
+                default:
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
+                    $element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3f);
+                    $element[EN_FLAGS] = ($byte & 0x80 ? EN_FLAGS_ATTRIBUTES : 0) | ($byte & 0x40 ? EN_FLAGS_CONTENT : 0);
+                    if($byte & 0x80)
+                        $element[EN_ATTRIBUTES] = $this->getAttributes();
+                    return $element;
+            }
+        }
+    }
+
+    function ungetElement($element) {
+        if($this->ungetbuffer)
+            debugLog("Double unget!");
+
+        $this->ungetbuffer = $element;
+    }
+
+    function getAttributes() {
+        $attributes = array();
+        $attr = "";
+
+        while(1) {
+            $byte = $this->getByte();
+
+            if(count($byte) == 0)
+                break;
+
+            switch($byte) {
+                case WBXML_SWITCH_PAGE:
+                    $this->attrcp = $this->getByte();
+                    break;
+
+                case WBXML_END:
+                    if($attr != "")
+                        $attributes += $this->splitAttribute($attr);
+
+                    return $attributes;
+
+                case WBXML_ENTITY:
+                    $entity = $this->getMBUInt();
+                    $attr .= $this->entityToCharset($entity);
+                    return $element;
+
+                case WBXML_STR_I:
+                    $attr .= $this->getTermStr();
+                    return $element;
+
+                case WBXML_LITERAL:
+                    if($attr != "")
+                        $attributes += $this->splitAttribute($attr);
+
+                    $attr = $this->getStringTableEntry($this->getMBUInt());
+                    return $element;
+
+                case WBXML_EXT_I_0:
+                case WBXML_EXT_I_1:
+                case WBXML_EXT_I_2:
+                    $this->getTermStr();
+                    continue;
+
+                case WBXML_PI:
+                case WBXML_LITERAL_C:
+                    // Invalid
+                    return false;
+
+                case WBXML_EXT_T_0:
+                case WBXML_EXT_T_1:
+                case WBXML_EXT_T_2:
+                    $this->getMBUInt();
+                    continue;
+
+                case WBXML_STR_T:
+                    $attr .= $this->getStringTableEntry($this->getMBUInt());
+                    return $element;
+
+                case WBXML_LITERAL_A:
+                    return false;
+
+                case WBXML_EXT_0:
+                case WBXML_EXT_1:
+                case WBXML_EXT_2:
+                    continue;
+
+                case WBXML_OPAQUE:
+                    $length = $this->getMBUInt();
+                    $attr .= $this->getOpaque($length);
+                    return $element;
+
+                case WBXML_LITERAL_AC:
+                    return false;
+
+                default:
+                    if($byte < 128) {
+                        if($attr != "") {
+                            $attributes += $this->splitAttribute($attr);
+                            $attr = "";
+                        }
+                    }
+
+                    $attr .= $this->getMapping($this->attrcp, $byte);
+                    break;
+            }
+        }
+
+    }
+
+    function splitAttribute($attr) {
+        $attributes = array();
+
+        $pos = strpos($attr,chr(61)); // equals sign
+
+        if($pos)
+            $attributes[substr($attr, 0, $pos)] = substr($attr, $pos+1);
+        else
+            $attributes[$attr] = null;
+
+        return $attributes;
+    }
+
+    function getTermStr() {
+        $str = "";
+        while(1) {
+            $in = $this->getByte();
+
+            if($in == 0)
+                break;
+            else
+                $str .= chr($in);
+        }
+
+        return $str;
+    }
+
+    function getOpaque($len) {
+        return fread($this->in, $len);
+    }
+
+    function getByte() {
+        $ch = fread($this->in, 1);
+        if(strlen($ch) > 0)
+            return ord($ch);
+        else
+            return;
+    }
+
+    function getMBUInt() {
+        $uint = 0;
+
+        while(1) {
+          $byte = $this->getByte();
+
+          $uint |= $byte & 0x7f;
+
+          if($byte & 0x80)
+              $uint = $uint << 7;
+          else
+              break;
+        }
+
+        return $uint;
+    }
+
+    function getStringTable() {
+        $stringtable = "";
+
+        $length = $this->getMBUInt();
+        if($length > 0)
+            $stringtable = fread($this->in, $length);
+
+        return $stringtable;
+    }
+
+    function getMapping($cp, $id) {
+        if(!isset($this->dtd["codes"][$cp]) || !isset($this->dtd["codes"][$cp][$id]))
+            return false;
+        else {
+            if(isset($this->dtd["namespaces"][$cp])) {
+                return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id];
+            } else
+                return $this->dtd["codes"][$cp][$id];
+        }
+    }
+}
+
+class WBXMLEncoder {
+    var $_dtd;
+    var $_out;
+
+    var $_tagcp;
+    var $_attrcp;
+
+    var $logStack = array();
+
+    // We use a delayed output mechanism in which we only output a tag when it actually has something
+    // in it. This can cause entire XML trees to disappear if they don't have output data in them; Ie
+    // calling 'startTag' 10 times, and then 'endTag' will cause 0 bytes of output apart from the header.
+
+    // Only when content() is called do we output the current stack of tags
+
+    var $_stack;
+
+    function WBXMLEncoder($output, $dtd) {
+        $this->_out = $output;
+
+        $this->_tagcp = 0;
+        $this->_attrcp = 0;
+
+        // reverse-map the DTD
+        foreach($dtd["namespaces"] as $nsid => $nsname) {
+            $this->_dtd["namespaces"][$nsname] = $nsid;
+        }
+
+        foreach($dtd["codes"] as $cp => $value) {
+            $this->_dtd["codes"][$cp] = array();
+            foreach($dtd["codes"][$cp] as $tagid => $tagname) {
+                $this->_dtd["codes"][$cp][$tagname] = $tagid;
+            }
+        }
+        $this->_stack = array();
+    }
+
+    function startWBXML() {
+        header("Content-Type: application/vnd.ms-sync.wbxml");
+
+        $this->outByte(0x03); // WBXML 1.3
+        $this->outMBUInt(0x01); // Public ID 1
+        $this->outMBUInt(106); // UTF-8
+        $this->outMBUInt(0x00); // string table length (0)
+    }
+
+    function startTag($tag, $attributes = false, $nocontent = false) {
+        $stackelem = array();
+
+        if(!$nocontent) {
+            $stackelem['tag'] = $tag;
+            $stackelem['attributes'] = $attributes;
+            $stackelem['nocontent'] = $nocontent;
+            $stackelem['sent'] = false;
+
+            array_push($this->_stack, $stackelem);
+
+            // If 'nocontent' is specified, then apparently the user wants to force
+            // output of an empty tag, and we therefore output the stack here
+        } else {
+            $this->_outputStack();
+            $this->_startTag($tag, $attributes, $nocontent);
+        }
+    }
+
+    function endTag() {
+        $stackelem = array_pop($this->_stack);
+
+        // Only output end tags for items that have had a start tag sent
+        if($stackelem['sent']) {
+            $this->_endTag();
+        }
+    }
+
+    function content($content) {
+        // We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
+        // cannot send \0 characters within the XML content anywhere.
+        $content = str_replace("\0","",$content);
+
+        if("x" . $content == "x")
+            return;
+        $this->_outputStack();
+        $this->_content($content);
+    }
+
+    // Output any tags on the stack that haven't been output yet
+    function _outputStack() {
+        for($i=0;$i<count($this->_stack);$i++) {
+            if(!$this->_stack[$i]['sent']) {
+                $this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['attributes'], $this->_stack[$i]['nocontent']);
+                $this->_stack[$i]['sent'] = true;
+            }
+        }
+    }
+
+    // Outputs an actual start tag
+    function _startTag($tag, $attributes = false, $nocontent = false) {
+        $this->logStartTag($tag, $attributes, $nocontent);
+
+        $mapping = $this->getMapping($tag);
+
+        if(!$mapping)
+            return false;
+
+        if($this->_tagcp != $mapping["cp"]) {
+            $this->outSwitchPage($mapping["cp"]);
+            $this->_tagcp = $mapping["cp"];
+        }
+
+        $code = $mapping["code"];
+        if(isset($attributes) && is_array($attributes) && count($attributes) > 0) {
+            $code |= 0x80;
+        }
+
+        if(!isset($nocontent) || !$nocontent)
+            $code |= 0x40;
+
+        $this->outByte($code);
+
+        if($code & 0x80)
+            $this->outAttributes($attributes);
+    }
+
+    // Outputs actual data
+    function _content($content) {
+        $this->logContent($content);
+        $this->outByte(WBXML_STR_I);
+        $this->outTermStr($content);
+    }
+
+    // Outputs an actual end tag
+    function _endTag() {
+        $this->logEndTag();
+        $this->outByte(WBXML_END);
+    }
+
+    // --------------------------- Private
+
+    function outByte($byte) {
+        fwrite($this->_out, chr($byte));
+    }
+
+    function outMBUInt($uint) {
+        while(1) {
+            $byte = $uint & 0x7f;
+            $uint = $uint >> 7;
+            if($uint == 0) {
+                $this->outByte($byte);
+                break;
+            } else {
+                $this->outByte($byte | 0x80);
+            }
+        }
+    }
+
+    function outTermStr($content) {
+        fwrite($this->_out, $content);
+        fwrite($this->_out, chr(0));
+    }
+
+    function outAttributes() {
+        // We don't actually support this, because to do so, we would have
+        // to build a string table before sending the data (but we can't
+        // because we're streaming), so we'll just send an END, which just
+        // terminates the attribute list with 0 attributes.
+        $this->outByte(WBXML_END);
+    }
+
+    function outSwitchPage($page) {
+        $this->outByte(WBXML_SWITCH_PAGE);
+        $this->outByte($page);
+    }
+
+    function getMapping($tag) {
+        $mapping = array();
+
+        $split = $this->splitTag($tag);
+
+        if(isset($split["ns"])) {
+            $cp = $this->_dtd["namespaces"][$split["ns"]];
+        } else {
+            $cp = 0;
+        }
+
+        $code = $this->_dtd["codes"][$cp][$split["tag"]];
+
+        $mapping["cp"] = $cp;
+        $mapping["code"] = $code;
+
+        return $mapping;
+    }
+
+    function splitTag($fulltag) {
+        $ns = false;
+        $pos = strpos($fulltag, chr(58)); // chr(58) == ':'
+
+        if($pos) {
+            $ns = substr($fulltag, 0, $pos);
+            $tag = substr($fulltag, $pos+1);
+        } else {
+            $tag = $fulltag;
+        }
+
+        $ret = array();
+        if($ns)
+            $ret["ns"] = $ns;
+        $ret["tag"] = $tag;
+
+        return $ret;
+    }
+
+    function logStartTag($tag, $attr, $nocontent) {
+        if(!WBXML_DEBUG)
+            return;
+
+        $spaces = str_repeat(" ", count($this->logStack));
+        if($nocontent)
+            debugLog("O " . $spaces . " <$tag/>");
+        else {
+            array_push($this->logStack, $tag);
+            debugLog("O " . $spaces . " <$tag>");
+        }
+    }
+
+    function logEndTag() {
+        if(!WBXML_DEBUG)
+            return;
+
+        $spaces = str_repeat(" ", count($this->logStack));
+        $tag = array_pop($this->logStack);
+        debugLog("O " . $spaces . "</$tag>");
+    }
+
+    function logContent($content) {
+        if(!WBXML_DEBUG)
+            return;
+
+        $spaces = str_repeat(" ", count($this->logStack));
+        debugLog("O " . $spaces . $content);
+    }
+}
Index: /contrib/z-push/LICENSE
===================================================================
--- /contrib/z-push/LICENSE	(revision 4898)
+++ /contrib/z-push/LICENSE	(revision 4898)
@@ -0,0 +1,347 @@
+NOTE: According to sec. 8 of the GNU General Public License (GPL), 
+Version 2, the distribution of the Program in or to the 
+United States of America is excluded from the scope of this license. 
+The Program is licensed under GPL Version 2 only.
+ 
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
+
Index: /contrib/z-push/include/mimeDecode.php
===================================================================
--- /contrib/z-push/include/mimeDecode.php	(revision 4613)
+++ /contrib/z-push/include/mimeDecode.php	(revision 4898)
@@ -102,818 +102,810 @@
 class Mail_mimeDecode
 {
-    /**
-     * The raw email to decode
-     *
-     * @var    string
-     * @access private
-     */
-    var $_input;
-
-    /**
-     * The header part of the input
-     *
-     * @var    string
-     * @access private
-     */
-    var $_header;
-
-    /**
-     * The body part of the input
-     *
-     * @var    string
-     * @access private
-     */
-    var $_body;
-
-    /**
-     * If an error occurs, this is used to store the message
-     *
-     * @var    string
-     * @access private
-     */
-    var $_error;
-
-    /**
-     * Flag to determine whether to include bodies in the
-     * returned object.
-     *
-     * @var    boolean
-     * @access private
-     */
-    var $_include_bodies;
-
-    /**
-     * Flag to determine whether to decode bodies
-     *
-     * @var    boolean
-     * @access private
-     */
-    var $_decode_bodies;
-
-    /**
-     * Flag to determine whether to decode headers
-     *
-     * @var    boolean
-     * @access private
-     */
-    var $_decode_headers;
-
-    /**
-     * Flag to determine whether to include attached messages
-     * as body in the returned object. Depends on $_include_bodies
-     *
-     * @var    boolean
-     * @access private
-     */
-    var $_rfc822_bodies;
-
-    /**
-     * Constructor.
-     *
-     * Sets up the object, initialise the variables, and splits and
-     * stores the header and body of the input.
-     *
-     * @param string The input to decode
-     * @access public
-     */
-    function Mail_mimeDecode($input, $deprecated_linefeed = '')
-    {
-        list($header, $body)   = $this->_splitBodyHeader($input);
-
-        $this->_input          = $input;
-        $this->_header         = $header;
-        $this->_body           = $body;
-        $this->_decode_bodies  = false;
-        $this->_include_bodies = true;
-        $this->_rfc822_bodies  = false;
-    }
-
-    /**
-     * Begins the decoding process. If called statically
-     * it will create an object and call the decode() method
-     * of it.
-     *
-     * @param array An array of various parameters that determine
-     *              various things:
-     *              include_bodies - Whether to include the body in the returned
-     *                               object.
-     *              decode_bodies  - Whether to decode the bodies
-     *                               of the parts. (Transfer encoding)
-     *              decode_headers - Whether to decode headers
-     *              input          - If called statically, this will be treated
-     *                               as the input
-     *              charset        - convert all data to this charset
-     * @return object Decoded results
-     * @access public
-     */
-    function decode($params = null)
-    {
-        // determine if this method has been called statically
-        $isStatic = !(isset($this) && get_class($this) == __CLASS__);
-
-        // Have we been called statically?
-        // If so, create an object and pass details to that.
-        if ($isStatic AND isset($params['input'])) {
-
-            $obj = new Mail_mimeDecode($params['input']);
-            $structure = $obj->decode($params);
-
-        // Called statically but no input
-        } elseif ($isStatic) {
-            return $this->raiseError('Called statically and no input given');
-
-        // Called via an object
-        } else {
-            $this->_include_bodies = isset($params['include_bodies']) ?
-                                 $params['include_bodies'] : false;
-            $this->_decode_bodies  = isset($params['decode_bodies']) ?
-                                 $params['decode_bodies']  : false;
-            $this->_decode_headers = isset($params['decode_headers']) ?
-                                 $params['decode_headers'] : false;
-            $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ?
-                                 $params['rfc_822bodies']  : false;
-            $this->_charset = isset($params['charset']) ?
-                                 strtolower($params['charset']) : 'utf-8';
-
-            $structure = $this->_decode($this->_header, $this->_body);
-            if ($structure === false) {
-                $structure = $this->raiseError($this->_error);
-            }
-        }
-
-        return $structure;
-    }
-
-    /**
-     * Performs the decoding. Decodes the body string passed to it
-     * If it finds certain content-types it will call itself in a
-     * recursive fashion
-     *
-     * @param string Header section
-     * @param string Body section
-     * @return object Results of decoding process
-     * @access private
-     */
-    function _decode($headers, $body, $default_ctype = 'text/plain')
-    {
-        $return = new stdClass;
-        $return->headers = array();
-        $headers = $this->_parseHeaders($headers);
-
-        foreach ($headers as $value) {
-            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
-                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);
-                $return->headers[strtolower($value['name'])][] = $value['value'];
-
-            } elseif (isset($return->headers[strtolower($value['name'])])) {
-                $return->headers[strtolower($value['name'])][] = $value['value'];
-
-            } else {
-                $return->headers[strtolower($value['name'])] = $value['value'];
-            }
-        }
-
-        reset($headers);
-        while (list($key, $value) = each($headers)) {
-            $headers[$key]['name'] = strtolower($headers[$key]['name']);
-            switch ($headers[$key]['name']) {
-
-                case 'content-type':
-                    $content_type = $this->_parseHeaderValue($headers[$key]['value']);
-
-                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
-                        $return->ctype_primary   = $regs[1];
-                        $return->ctype_secondary = $regs[2];
-                    }
-
-                    if (isset($content_type['other'])) {
-                        while (list($p_name, $p_value) = each($content_type['other'])) {
-                            $return->ctype_parameters[$p_name] = $p_value;
-                        }
-                    }
-                    break;
-
-                case 'content-disposition':
-                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
-                    $return->disposition   = $content_disposition['value'];
-                    if (isset($content_disposition['other'])) {
-                        while (list($p_name, $p_value) = each($content_disposition['other'])) {
-                            $return->d_parameters[$p_name] = $p_value;
-                        }
-                    }
-                    break;
-
-                case 'content-transfer-encoding':
-                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
-                    break;
-            }
-        }
-
-        if (isset($content_type)) {
-            switch (strtolower($content_type['value'])) {
-                case 'text/plain':
-                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
-                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
-                    break;
-
-                case 'text/html':
-                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
-                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
-                    break;
-
-                case 'multipart/parallel':
-                case 'multipart/appledouble': // Appledouble mail
-                case 'multipart/report': // RFC1892
-                case 'multipart/signed': // PGP
-                case 'multipart/digest':
-                case 'multipart/alternative':
-                case 'multipart/related':
-                case 'multipart/mixed':
-                    if(!isset($content_type['other']['boundary'])){
-                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
-                        return false;
-                    }
-
-                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
-
-                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
-                    for ($i = 0; $i < count($parts); $i++) {
-                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
-                        $part = $this->_decode($part_header, $part_body, $default_ctype);
-                        if($part === false)
-                            $part = $this->raiseError($this->_error);
-                        $return->parts[] = $part;
-                    }
-                    break;
-
-                case 'message/rfc822':
-                    if ($this->_rfc822_bodies) {
-                        $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
-                        $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
-                        $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body);
-                    }
-
-                    $obj = new Mail_mimeDecode($body);
-                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
-                                                          'decode_bodies'  => $this->_decode_bodies,
-                                                          'decode_headers' => $this->_decode_headers));
-                    unset($obj);
-                    break;
-
-                default:
-                    if(!isset($content_transfer_encoding['value']))
-                        $content_transfer_encoding['value'] = '7bit';
-                    // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted
-                    $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) )? $return->ctype_parameters['charset']: '';
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null;
-                    break;
-            }
-
-        } else {
-            $ctype = explode('/', $default_ctype);
-            $return->ctype_primary   = $ctype[0];
-            $return->ctype_secondary = $ctype[1];
-            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
-        }
-
-        return $return;
-    }
-
-    /**
-     * Given the output of the above function, this will return an
-     * array of references to the parts, indexed by mime number.
-     *
-     * @param  object $structure   The structure to go through
-     * @param  string $mime_number Internal use only.
-     * @return array               Mime numbers
-     */
-    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
-    {
-        $return = array();
-        if (!empty($structure->parts)) {
-            if ($mime_number != '') {
-                $structure->mime_id = $prepend . $mime_number;
-                $return[$prepend . $mime_number] = &$structure;
-            }
-            for ($i = 0; $i < count($structure->parts); $i++) {
-
-
-                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
-                    $prepend      = $prepend . $mime_number . '.';
-                    $_mime_number = '';
-                } else {
-                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
-                }
-
-                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
-                foreach ($arr as $key => $val) {
-                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
-                }
-            }
-        } else {
-            if ($mime_number == '') {
-                $mime_number = '1';
-            }
-            $structure->mime_id = $prepend . $mime_number;
-            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
-        }
-
-        return $return;
-    }
-
-    /**
-     * Given a string containing a header and body
-     * section, this function will split them (at the first
-     * blank line) and return them.
-     *
-     * @param string Input to split apart
-     * @return array Contains header and body section
-     * @access private
-     */
-    function _splitBodyHeader($input)
-    {
-        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
-            return array($match[1], $match[2]);
-        }
-        $this->_error = 'Could not split header and body';
-        return false;
-    }
-
-    /**
-     * Parse headers given in $input and return
-     * as assoc array.
-     *
-     * @param string Headers to parse
-     * @return array Contains parsed headers
-     * @access private
-     */
-    function _parseHeaders($input)
-    {
-
-        if ($input !== '') {
-            // Unfold the input
-            $input   = preg_replace("/\r?\n/", "\r\n", $input);
-            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input);
-            $headers = explode("\r\n", trim($input));
-
-            foreach ($headers as $value) {
-                $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
-                $hdr_value = substr($value, $pos+1);
-                if($hdr_value[0] == ' ')
-                    $hdr_value = substr($hdr_value, 1);
-
-                $return[] = array(
-                                  'name'  => $hdr_name,
-                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
-                                 );
-            }
-        } else {
-            $return = array();
-        }
-
-        return $return;
-    }
-
-    /**
-     * Function to parse a header value,
-     * extract first part, and any secondary
-     * parts (after ;) This function is not as
-     * robust as it could be. Eg. header comments
-     * in the wrong place will probably break it.
-     *
-     * @param string Header value to parse
-     * @return array Contains parsed result
-     * @access private
-     */
-    function _parseHeaderValue($input)
-    {
-
-        if (($pos = strpos($input, ';')) !== false) {
-
-            $return['value'] = trim(substr($input, 0, $pos));
-            $input = trim(substr($input, $pos+1));
-
-            if (strlen($input) > 0) {
-
-                // This splits on a semi-colon, if there's no preceeding backslash
-                // Now works with quoted values; had to glue the \; breaks in PHP
-                // the regex is already bordering on incomprehensible
-                //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
-                // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex
-                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/';
-                preg_match_all($splitRegex, $input, $matches);
-
-                $parameters = array();
-                for ($i=0; $i<count($matches[0]); $i++) {
-                    $param = $matches[0][$i];
-                    while (substr($param, -2) == '\;') {
-                        $param .= $matches[0][++$i];
-                    }
-                    $parameters[] = $param;
-                }
-
-                for ($i = 0; $i < count($parameters); $i++) {
-                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
-                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
-                    if (!empty($param_value[0]) && $param_value[0] == '"') {
-                        $param_value = substr($param_value, 1, -1);
-                    }
-                    $return['other'][$param_name] = $param_value;
-                    $return['other'][strtolower($param_name)] = $param_value;
-                }
-            }
-        } else {
-            $return['value'] = trim($input);
-        }
-
-        return $return;
-    }
-
-    /**
-     * This function splits the input based
-     * on the given boundary
-     *
-     * @param string Input to parse
-     * @return array Contains array of resulting mime parts
-     * @access private
-     */
-    function _boundarySplit($input, $boundary)
-    {
-        $parts = array();
-
-        $bs_possible = substr($boundary, 2, -2);
-        $bs_check = '\"' . $bs_possible . '\"';
-
-        if ($boundary == $bs_check) {
-            $boundary = $bs_possible;
-        }
-
-        $tmp = explode('--' . $boundary, $input);
-
-        for ($i = 1; $i < count($tmp) - 1; $i++) {
-            $parts[] = $tmp[$i];
-        }
-
-        return $parts;
-    }
-
-    /**
-     * Given a header, this function will decode it
-     * according to RFC2047. Probably not *exactly*
-     * conformant, but it does pass all the given
-     * examples (in RFC2047).
-     *
-     * @param string Input header value to decode
-     * @return string Decoded header value
-     * @access private
-     */
-    function _decodeHeader($input)
-    {
-        // Remove white space between encoded-words
-        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
-
-        // For each encoded-word...
-        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
-
-            $encoded  = $matches[1];
-            $charset  = $matches[2];
-            $encoding = $matches[3];
-            $text     = $matches[4];
-
-            switch (strtolower($encoding)) {
-                case 'b':
-                    $text = base64_decode($text);
-                    break;
-
-                case 'q':
-                    $text = str_replace('_', ' ', $text);
-                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
-                    foreach($matches[1] as $value)
-                        $text = str_replace('='.$value, chr(hexdec($value)), $text);
-                    break;
-            }
-
-            $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input);
-        }
-
-        return $input;
-    }
-
-    /**
-     * Given a body string and an encoding type,
-     * this function will decode and return it.
-     *
-     * @param  string Input body to decode
-     * @param  string Encoding type to use.
-     * @return string Decoded body
-     * @access private
-     */
-    function _decodeBody($input, $encoding = '7bit', $charset = '')
-    {
-        switch (strtolower($encoding)) {
-            case '7bit':
-                return $this->_fromCharset($charset, $input);;
-                break;
-
-            case '8bit':
-                return $this->_fromCharset($charset, $input);
-                break;
-
-            case 'quoted-printable':
-                return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input));
-                break;
-
-            case 'base64':
-                return $this->_fromCharset($charset, base64_decode($input));
-                break;
-
-            default:
-                return $input;
-        }
-    }
-
-    /**
-     * Given a quoted-printable string, this
-     * function will decode and return it.
-     *
-     * @param  string Input body to decode
-     * @return string Decoded body
-     * @access private
-     */
-    function _quotedPrintableDecode($input)
-    {
-        // Remove soft line breaks
-        $input = preg_replace("/=\r?\n/", '', $input);
-
-        // Replace encoded characters
-        $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
-
-        return $input;
-    }
-
-    /**
-     * Checks the input for uuencoded files and returns
-     * an array of them. Can be called statically, eg:
-     *
-     * $files =& Mail_mimeDecode::uudecode($some_text);
-     *
-     * It will check for the begin 666 ... end syntax
-     * however and won't just blindly decode whatever you
-     * pass it.
-     *
-     * @param  string Input body to look for attahcments in
-     * @return array  Decoded bodies, filenames and permissions
-     * @access public
-     * @author Unknown
-     */
-    function &uudecode($input)
-    {
-        // Find all uuencoded sections
-        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
-
-        for ($j = 0; $j < count($matches[3]); $j++) {
-
-            $str      = $matches[3][$j];
-            $filename = $matches[2][$j];
-            $fileperm = $matches[1][$j];
-
-            $file = '';
-            $str = preg_split("/\r?\n/", trim($str));
-            $strlen = count($str);
-
-            for ($i = 0; $i < $strlen; $i++) {
-                $pos = 1;
-                $d = 0;
-                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
-
-                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
-                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
-
-                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077));
-
-                    $pos += 4;
-                    $d += 3;
-                }
-
-                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
-
-                    $pos += 3;
-                    $d += 2;
-                }
-
-                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                }
-            }
-            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
-        }
-
-        return $files;
-    }
-
-    /**
-     * getSendArray() returns the arguments required for Mail::send()
-     * used to build the arguments for a mail::send() call
-     *
-     * Usage:
-     * $mailtext = Full email (for example generated by a template)
-     * $decoder = new Mail_mimeDecode($mailtext);
-     * $parts =  $decoder->getSendArray();
-     * if (!PEAR::isError($parts) {
-     *     list($recipents,$headers,$body) = $parts;
-     *     $mail = Mail::factory('smtp');
-     *     $mail->send($recipents,$headers,$body);
-     * } else {
-     *     echo $parts->message;
-     * }
-     * @return mixed   array of recipeint, headers,body or Pear_Error
-     * @access public
-     * @author Alan Knowles <alan@akbkhome.com>
-     */
-    function getSendArray()
-    {
-        // prevent warning if this is not set
-        $this->_decode_headers = FALSE;
-        $headerlist =$this->_parseHeaders($this->_header);
-        $to = "";
-        if (!$headerlist) {
-            return $this->raiseError("Message did not contain headers");
-        }
-        foreach($headerlist as $item) {
-            $header[$item['name']] = $item['value'];
-            switch (strtolower($item['name'])) {
-                case "to":
-                case "cc":
-                case "bcc":
-                    $to .= ",".$item['value'];
-                default:
-                   break;
-            }
-        }
-        if ($to == "") {
-            return $this->raiseError("Message did not contain any recipents");
-        }
-        $to = substr($to,1);
-        return array($to,$header,$this->_body);
-    }
-
-    /**
-     * Returns a xml copy of the output of
-     * Mail_mimeDecode::decode. Pass the output in as the
-     * argument. This function can be called statically. Eg:
-     *
-     * $output = $obj->decode();
-     * $xml    = Mail_mimeDecode::getXML($output);
-     *
-     * The DTD used for this should have been in the package. Or
-     * alternatively you can get it from cvs, or here:
-     * http://www.phpguru.org/xmail/xmail.dtd.
-     *
-     * @param  object Input to convert to xml. This should be the
-     *                output of the Mail_mimeDecode::decode function
-     * @return string XML version of input
-     * @access public
-     */
-    function getXML($input)
-    {
-        $crlf    =  "\r\n";
-        $output  = '<?xml version=\'1.0\'?>' . $crlf .
+	/**
+	 * The raw email to decode
+	 *
+	 * @var    string
+	 * @access private
+	 */
+	var $_input;
+
+	/**
+	 * The header part of the input
+	 *
+	 * @var    string
+	 * @access private
+	 */
+	var $_header;
+
+	/**
+	 * The body part of the input
+	 *
+	 * @var    string
+	 * @access private
+	 */
+	var $_body;
+
+	/**
+	 * If an error occurs, this is used to store the message
+	 *
+	 * @var    string
+	 * @access private
+	 */
+	var $_error;
+
+	/**
+	 * Flag to determine whether to include bodies in the
+	 * returned object.
+	 *
+	 * @var    boolean
+	 * @access private
+	 */
+	var $_include_bodies;
+
+	/**
+	 * Flag to determine whether to decode bodies
+	 *
+	 * @var    boolean
+	 * @access private
+	 */
+	var $_decode_bodies;
+
+	/**
+	 * Flag to determine whether to decode headers
+	 *
+	 * @var    boolean
+	 * @access private
+	 */
+	var $_decode_headers;
+
+	/**
+	 * Flag to determine whether to include attached messages
+	 * as body in the returned object. Depends on $_include_bodies
+	 *
+	 * @var    boolean
+	 * @access private
+	 */
+	var $_rfc822_bodies;
+
+	/**
+	 * Constructor.
+	 *
+	 * Sets up the object, initialise the variables, and splits and
+	 * stores the header and body of the input.
+	 *
+	 * @param string The input to decode
+	 * @access public
+	 */
+	function Mail_mimeDecode($input, $deprecated_linefeed = '')
+	{
+		list($header, $body)   = $this->_splitBodyHeader($input);
+
+		$this->_input          = $input;
+		$this->_header         = $header;
+		$this->_body           = $body;
+		$this->_decode_bodies  = false;
+		$this->_include_bodies = true;
+		$this->_rfc822_bodies  = false;
+	}
+
+	/**
+	 * Begins the decoding process. If called statically
+	 * it will create an object and call the decode() method
+	 * of it.
+	 *
+	 * @param array An array of various parameters that determine
+	 *              various things:
+	 *              include_bodies - Whether to include the body in the returned
+	 *                               object.
+	 *              decode_bodies  - Whether to decode the bodies
+	 *                               of the parts. (Transfer encoding)
+	 *              decode_headers - Whether to decode headers
+	 *              input          - If called statically, this will be treated
+	 *                               as the input
+	 *              charset        - convert all data to this charset
+	 * @return object Decoded results
+	 * @access public
+	 */
+	function decode($params = null)
+	{
+		// determine if this method has been called statically
+		$isStatic = !(isset($this) && get_class($this) == __CLASS__);
+
+		// Have we been called statically?
+		// If so, create an object and pass details to that.
+		if ($isStatic AND isset($params['input'])) {
+
+			$obj = new Mail_mimeDecode($params['input']);
+			$structure = $obj->decode($params);
+
+			// Called statically but no input
+		} elseif ($isStatic) {
+			return $this->raiseError('Called statically and no input given');
+
+			// Called via an object
+		} else {
+			$this->_include_bodies = isset($params['include_bodies']) ?
+				$params['include_bodies'] : false;
+			$this->_decode_bodies  = isset($params['decode_bodies']) ?
+				$params['decode_bodies']  : false;
+			$this->_decode_headers = isset($params['decode_headers']) ?
+				$params['decode_headers'] : false;
+			$this->_rfc822_bodies  = isset($params['rfc_822bodies']) ?
+				$params['rfc_822bodies']  : false;
+			$this->_charset = isset($params['charset']) ?
+				strtolower($params['charset']) : 'utf-8';
+			 
+			$structure = $this->_decode($this->_header, $this->_body);
+			if ($structure === false) {
+				$structure = $this->raiseError($this->_error);
+			}
+		}
+
+		return $structure;
+	}
+
+	/**
+	 * Performs the decoding. Decodes the body string passed to it
+	 * If it finds certain content-types it will call itself in a
+	 * recursive fashion
+	 *
+	 * @param string Header section
+	 * @param string Body section
+	 * @return object Results of decoding process
+	 * @access private
+	 */
+	function _decode($headers, $body, $default_ctype = 'text/plain')
+	{
+		$return = new stdClass;
+		$return->headers = array();
+		$headers = $this->_parseHeaders($headers);
+
+		foreach ($headers as $value) {
+			if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
+				$return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);
+				$return->headers[strtolower($value['name'])][] = $value['value'];
+
+			} elseif (isset($return->headers[strtolower($value['name'])])) {
+				$return->headers[strtolower($value['name'])][] = $value['value'];
+
+			} else {
+				$return->headers[strtolower($value['name'])] = $value['value'];
+			}
+		}
+
+		reset($headers);
+		while (list($key, $value) = each($headers)) {
+			$headers[$key]['name'] = strtolower($headers[$key]['name']);
+			switch ($headers[$key]['name']) {
+
+				case 'content-type':
+					$content_type = $this->_parseHeaderValue($headers[$key]['value']);
+
+					if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
+						$return->ctype_primary   = $regs[1];
+						$return->ctype_secondary = $regs[2];
+					}
+
+					if (isset($content_type['other'])) {
+						while (list($p_name, $p_value) = each($content_type['other'])) {
+							$return->ctype_parameters[$p_name] = $p_value;
+						}
+					}
+					break;
+
+				case 'content-disposition':
+					$content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
+					$return->disposition   = $content_disposition['value'];
+					if (isset($content_disposition['other'])) {
+						while (list($p_name, $p_value) = each($content_disposition['other'])) {
+							$return->d_parameters[$p_name] = $p_value;
+						}
+					}
+					break;
+
+				case 'content-transfer-encoding':
+					$content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
+					break;
+			}
+		}
+
+		if (isset($content_type)) {
+			switch (strtolower($content_type['value'])) {
+				case 'text/plain':
+					$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+					$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
+					$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
+					break;
+
+				case 'text/html':
+					$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+					$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
+					$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
+					break;
+
+				case 'multipart/parallel':
+				case 'multipart/appledouble': // Appledouble mail
+				case 'multipart/report': // RFC1892
+				case 'multipart/signed': // PGP
+				case 'multipart/digest':
+				case 'multipart/alternative':
+				case 'multipart/related':
+				case 'multipart/mixed':
+					if(!isset($content_type['other']['boundary'])){
+						$this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
+						return false;
+					}
+
+					$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
+
+					$parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
+					for ($i = 0; $i < count($parts); $i++) {
+						list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
+						$part = $this->_decode($part_header, $part_body, $default_ctype);
+						if($part === false)
+						$part = $this->raiseError($this->_error);
+						$return->parts[] = $part;
+					}
+					break;
+
+				case 'message/rfc822':
+					if ($this->_rfc822_bodies) {
+						$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+						$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
+						$return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body);
+					}
+
+					$obj = new Mail_mimeDecode($body);
+					$return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
+						'decode_bodies'  => $this->_decode_bodies,
+						'decode_headers' => $this->_decode_headers));
+					unset($obj);
+					break;
+
+				default:
+					if(!isset($content_transfer_encoding['value']))
+						$content_transfer_encoding['value'] = '7bit';
+					
+					// if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted
+					$charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) ) ? $return->ctype_parameters['charset']: '';
+					$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null;
+					break;
+			}
+
+		} else {
+			$ctype = explode('/', $default_ctype);
+			$return->ctype_primary   = $ctype[0];
+			$return->ctype_secondary = $ctype[1];
+			$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Given the output of the above function, this will return an
+	 * array of references to the parts, indexed by mime number.
+	 *
+	 * @param  object $structure   The structure to go through
+	 * @param  string $mime_number Internal use only.
+	 * @return array               Mime numbers
+	 */
+	function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
+	{
+		$return = array();
+		if (!empty($structure->parts)) {
+			if ($mime_number != '') {
+				$structure->mime_id = $prepend . $mime_number;
+				$return[$prepend . $mime_number] = &$structure;
+			}
+			for ($i = 0; $i < count($structure->parts); $i++) {
+				if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
+					$prepend      = $prepend . $mime_number . '.';
+					$_mime_number = '';
+				} else {
+					$_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
+				}
+
+				$arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
+				foreach ($arr as $key => $val) {
+					$no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
+				}
+			}
+		} else {
+			if ($mime_number == '') {
+				$mime_number = '1';
+			}
+			$structure->mime_id = $prepend . $mime_number;
+			$no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Given a string containing a header and body
+	 * section, this function will split them (at the first
+	 * blank line) and return them.
+	 *
+	 * @param string Input to split apart
+	 * @return array Contains header and body section
+	 * @access private
+	 */
+	function _splitBodyHeader($input)
+	{
+		if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
+			return array($match[1], $match[2]);
+		}
+		$this->_error = 'Could not split header and body';
+		return false;
+	}
+
+	/**
+	 * Parse headers given in $input and return
+	 * as assoc array.
+	 *
+	 * @param string Headers to parse
+	 * @return array Contains parsed headers
+	 * @access private
+	 */
+	function _parseHeaders($input)
+	{
+
+		if ($input !== '') {
+			// Unfold the input
+			$input   = preg_replace("/\r?\n/", "\r\n", $input);
+			$input   = preg_replace("/\r\n(\t| )+/", ' ', $input);
+			$headers = explode("\r\n", trim($input));
+
+			foreach ($headers as $value) {
+				$hdr_name = substr($value, 0, $pos = strpos($value, ':'));
+				$hdr_value = substr($value, $pos+1);
+				if($hdr_value[0] == ' ')
+					$hdr_value = substr($hdr_value, 1);
+
+				$return[] = array(
+					'name'  => $hdr_name,
+					'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
+				);
+			}
+		} else {
+			$return = array();
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Function to parse a header value,
+	 * extract first part, and any secondary
+	 * parts (after ;) This function is not as
+	 * robust as it could be. Eg. header comments
+	 * in the wrong place will probably break it.
+	 *
+	 * @param string Header value to parse
+	 * @return array Contains parsed result
+	 * @access private
+	 */
+	function _parseHeaderValue($input)
+	{
+		if (($pos = strpos($input, ';')) !== false) {
+			$return['value'] = trim(substr($input, 0, $pos));
+			$input = trim(substr($input, $pos+1));
+
+			if (strlen($input) > 0) {
+				// This splits on a semi-colon, if there's no preceeding backslash
+				// Now works with quoted values; had to glue the \; breaks in PHP
+				// the regex is already bordering on incomprehensible
+				//$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
+				// simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex
+				$splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/';
+				preg_match_all($splitRegex, $input, $matches);
+
+				$parameters = array();
+				for ($i=0; $i<count($matches[0]); $i++) {
+					$param = $matches[0][$i];
+					while (substr($param, -2) == '\;') {
+						$param .= $matches[0][++$i];
+					}
+					$parameters[] = $param;
+				}
+
+				for ($i = 0; $i < count($parameters); $i++) {
+					$param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
+					$param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
+					if (!empty($param_value[0]) && $param_value[0] == '"') {
+						$param_value = substr($param_value, 1, -1);
+					}
+					$return['other'][$param_name] = $param_value;
+					$return['other'][strtolower($param_name)] = $param_value;
+				}
+			}
+		} else {
+			$return['value'] = trim($input);
+		}
+
+		return $return;
+	}
+
+	/**
+	 * This function splits the input based
+	 * on the given boundary
+	 *
+	 * @param string Input to parse
+	 * @return array Contains array of resulting mime parts
+	 * @access private
+	 */
+	function _boundarySplit($input, $boundary)
+	{
+		$parts = array();
+
+		$bs_possible = substr($boundary, 2, -2);
+		$bs_check = '\"' . $bs_possible . '\"';
+
+		if ($boundary == $bs_check) {
+			$boundary = $bs_possible;
+		}
+
+		$tmp = explode('--' . $boundary, $input);
+
+		for ($i = 1; $i < count($tmp) - 1; $i++) {
+			$parts[] = $tmp[$i];
+		}
+
+		return $parts;
+	}
+
+	/**
+	 * Given a header, this function will decode it
+	 * according to RFC2047. Probably not *exactly*
+	 * conformant, but it does pass all the given
+	 * examples (in RFC2047).
+	 *
+	 * @param string Input header value to decode
+	 * @return string Decoded header value
+	 * @access private
+	 */
+	function _decodeHeader($input)
+	{
+		// Remove white space between encoded-words
+		$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
+
+		// For each encoded-word...
+		while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
+			$encoded  = $matches[1];
+			$charset  = $matches[2];
+			$encoding = $matches[3];
+			$text     = $matches[4];
+
+			switch (strtolower($encoding)) {
+				case 'b':
+					$text = base64_decode($text);
+					break;
+
+				case 'q':
+					$text = str_replace('_', ' ', $text);
+					preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
+					foreach($matches[1] as $value)
+					$text = str_replace('='.$value, chr(hexdec($value)), $text);
+					break;
+			}
+
+			$input = str_replace($encoded, $this->_fromCharset($charset, $text), $input);
+		}
+
+		return $input;
+	}
+
+	/**
+	 * Given a body string and an encoding type,
+	 * this function will decode and return it.
+	 *
+	 * @param  string Input body to decode
+	 * @param  string Encoding type to use.
+	 * @return string Decoded body
+	 * @access private
+	 */
+	function _decodeBody($input, $encoding = '7bit', $charset = '')
+	{
+		switch (strtolower($encoding)) {
+			case '7bit':
+				return $this->_fromCharset($charset, $input);;
+				break;
+
+			case '8bit':
+				return $this->_fromCharset($charset, $input);
+				break;
+
+			case 'quoted-printable':
+				return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input));
+				break;
+
+			case 'base64':
+				return $this->_fromCharset($charset, base64_decode($input));
+				break;
+
+			default:
+				return $input;
+		}
+	}
+
+	/**
+	 * Given a quoted-printable string, this
+	 * function will decode and return it.
+	 *
+	 * @param  string Input body to decode
+	 * @return string Decoded body
+	 * @access private
+	 */
+	function _quotedPrintableDecode($input)
+	{
+		// Remove soft line breaks
+		$input = preg_replace("/=\r?\n/", '', $input);
+
+		// Replace encoded characters
+		$input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
+
+		return $input;
+	}
+
+	/**
+	 * Checks the input for uuencoded files and returns
+	 * an array of them. Can be called statically, eg:
+	 *
+	 * $files =& Mail_mimeDecode::uudecode($some_text);
+	 *
+	 * It will check for the begin 666 ... end syntax
+	 * however and won't just blindly decode whatever you
+	 * pass it.
+	 *
+	 * @param  string Input body to look for attahcments in
+	 * @return array  Decoded bodies, filenames and permissions
+	 * @access public
+	 * @author Unknown
+	 */
+	function &uudecode($input)
+	{
+		// Find all uuencoded sections
+		preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
+
+		for ($j = 0; $j < count($matches[3]); $j++) {
+			$str      = $matches[3][$j];
+			$filename = $matches[2][$j];
+			$fileperm = $matches[1][$j];
+
+			$file = '';
+			$str = preg_split("/\r?\n/", trim($str));
+			$strlen = count($str);
+
+			for ($i = 0; $i < $strlen; $i++) {
+				$pos = 1;
+				$d = 0;
+				$len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
+
+				while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
+					$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+					$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+					$c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+					$c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
+					$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+					$file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+					$file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077));
+
+					$pos += 4;
+					$d += 3;
+				}
+
+				if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
+					$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+					$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+					$c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+					$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+					$file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+					$pos += 3;
+					$d += 2;
+				}
+
+				if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
+					$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+					$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+					$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+				}
+			}
+			$files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
+		}
+
+		return $files;
+	}
+
+	/**
+	 * getSendArray() returns the arguments required for Mail::send()
+	 * used to build the arguments for a mail::send() call
+	 *
+	 * Usage:
+	 * $mailtext = Full email (for example generated by a template)
+	 * $decoder = new Mail_mimeDecode($mailtext);
+	 * $parts =  $decoder->getSendArray();
+	 * if (!PEAR::isError($parts) {
+	 *     list($recipents,$headers,$body) = $parts;
+	 *     $mail = Mail::factory('smtp');
+	 *     $mail->send($recipents,$headers,$body);
+	 * } else {
+	 *     echo $parts->message;
+	 * }
+	 * @return mixed   array of recipeint, headers,body or Pear_Error
+	 * @access public
+	 * @author Alan Knowles <alan@akbkhome.com>
+	 */
+	function getSendArray()
+	{
+		// prevent warning if this is not set
+		$this->_decode_headers = FALSE;
+		$headerlist =$this->_parseHeaders($this->_header);
+		$to = "";
+		if (!$headerlist) {
+			return $this->raiseError("Message did not contain headers");
+		}
+		foreach($headerlist as $item) {
+			$header[$item['name']] = $item['value'];
+			switch (strtolower($item['name'])) {
+				case "to":
+				case "cc":
+				case "bcc":
+					$to .= ",".$item['value'];
+				default:
+					break;
+			}
+		}
+		if ($to == "") {
+			return $this->raiseError("Message did not contain any recipents");
+		}
+		$to = substr($to,1);
+		return array($to,$header,$this->_body);
+	}
+
+	/**
+	 * Returns a xml copy of the output of
+	 * Mail_mimeDecode::decode. Pass the output in as the
+	 * argument. This function can be called statically. Eg:
+	 *
+	 * $output = $obj->decode();
+	 * $xml    = Mail_mimeDecode::getXML($output);
+	 *
+	 * The DTD used for this should have been in the package. Or
+	 * alternatively you can get it from cvs, or here:
+	 * http://www.phpguru.org/xmail/xmail.dtd.
+	 *
+	 * @param  object Input to convert to xml. This should be the
+	 *                output of the Mail_mimeDecode::decode function
+	 * @return string XML version of input
+	 * @access public
+	 */
+	function getXML($input)
+	{
+		$crlf    =  "\r\n";
+		$output  = '<?xml version=\'1.0\'?>' . $crlf .
                    '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
                    '<email>' . $crlf .
-                   Mail_mimeDecode::_getXML($input) .
+		Mail_mimeDecode::_getXML($input) .
                    '</email>';
 
-        return $output;
-    }
-
-    /**
-     * Function that does the actual conversion to xml. Does a single
-     * mimepart at a time.
-     *
-     * @param  object  Input to convert to xml. This is a mimepart object.
-     *                 It may or may not contain subparts.
-     * @param  integer Number of tabs to indent
-     * @return string  XML version of input
-     * @access private
-     */
-    function _getXML($input, $indent = 1)
-    {
-        $htab    =  "\t";
-        $crlf    =  "\r\n";
-        $output  =  '';
-        $headers = @(array)$input->headers;
-
-        foreach ($headers as $hdr_name => $hdr_value) {
-
-            // Multiple headers with this name
-            if (is_array($headers[$hdr_name])) {
-                for ($i = 0; $i < count($hdr_value); $i++) {
-                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
-                }
-
-            // Only one header of this sort
-            } else {
-                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
-            }
-        }
-
-        if (!empty($input->parts)) {
-            for ($i = 0; $i < count($input->parts); $i++) {
-                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
-                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
-                           str_repeat($htab, $indent) . '</mimepart>' . $crlf;
-            }
-        } elseif (isset($input->body)) {
-            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
-                       $input->body . ']]></body>' . $crlf;
-        }
-
-        return $output;
-    }
-
-    /**
-     * Helper function to _getXML(). Returns xml of a header.
-     *
-     * @param  string  Name of header
-     * @param  string  Value of header
-     * @param  integer Number of tabs to indent
-     * @return string  XML version of input
-     * @access private
-     */
-    function _getXML_helper($hdr_name, $hdr_value, $indent)
-    {
-        $htab   = "\t";
-        $crlf   = "\r\n";
-        $return = '';
-
-        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
-        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
-
-        // Sort out any parameters
-        if (!empty($new_hdr_value['other'])) {
-            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
-                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
-            }
-
-            $params = implode('', $params);
-        } else {
-            $params = '';
-        }
-
-        $return = str_repeat($htab, $indent) . '<header>' . $crlf .
-                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
-                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
-                  $params .
-                  str_repeat($htab, $indent) . '</header>' . $crlf;
-
-        return $return;
-    }
-
-    /**
-     * Z-Push helper to decode text
-     *
-     * @param  string  current charset of input
-     * @param  string  input
-     * @return string  XML version of input
-     * @access private
-     */
-    function _fromCharset($charset, $input) {
-        if($charset == '' || (strtolower($charset) == $this->_charset))
-            return $input;
-
-        return @iconv($charset, $this->_charset. "//TRANSLIT", $input);
-    }
-
-    /**
-     * Z-Push helper for error logging
-     * removing PEAR dependency
-     *
-     * @param  string  debug message
-     * @return boolean always false as there was an error
-     * @access private
-     */
-    function raiseError($message) {
-        debugLog("mimeDecode error: ". $message);
-        return false;
-    }
+		return $output;
+	}
+
+	/**
+	 * Function that does the actual conversion to xml. Does a single
+	 * mimepart at a time.
+	 *
+	 * @param  object  Input to convert to xml. This is a mimepart object.
+	 *                 It may or may not contain subparts.
+	 * @param  integer Number of tabs to indent
+	 * @return string  XML version of input
+	 * @access private
+	 */
+	function _getXML($input, $indent = 1)
+	{
+		$htab    =  "\t";
+		$crlf    =  "\r\n";
+		$output  =  '';
+		$headers = @(array)$input->headers;
+
+		foreach ($headers as $hdr_name => $hdr_value) {
+			// Multiple headers with this name
+			if (is_array($headers[$hdr_name])) {
+				for ($i = 0; $i < count($hdr_value); $i++) {
+					$output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
+				}
+
+				// Only one header of this sort
+			} else {
+				$output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
+			}
+		}
+
+		if (!empty($input->parts)) {
+			for ($i = 0; $i < count($input->parts); $i++) {
+				$output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
+				Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
+				str_repeat($htab, $indent) . '</mimepart>' . $crlf;
+			}
+		} elseif (isset($input->body)) {
+			$output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
+			$input->body . ']]></body>' . $crlf;
+		}
+
+		return $output;
+	}
+
+	/**
+	 * Helper function to _getXML(). Returns xml of a header.
+	 *
+	 * @param  string  Name of header
+	 * @param  string  Value of header
+	 * @param  integer Number of tabs to indent
+	 * @return string  XML version of input
+	 * @access private
+	 */
+	function _getXML_helper($hdr_name, $hdr_value, $indent)
+	{
+		$htab   = "\t";
+		$crlf   = "\r\n";
+		$return = '';
+
+		$new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
+		$new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
+
+		// Sort out any parameters
+		if (!empty($new_hdr_value['other'])) {
+			foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
+				$params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
+				str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
+				str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
+				str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
+			}
+
+			$params = implode('', $params);
+		} else {
+			$params = '';
+		}
+
+		$return = str_repeat($htab, $indent) . '<header>' . $crlf .
+		str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
+		str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
+		$params .
+		str_repeat($htab, $indent) . '</header>' . $crlf;
+
+		return $return;
+	}
+
+	/**
+	 * Z-Push helper to decode text
+	 *
+	 * @param  string  current charset of input
+	 * @param  string  input
+	 * @return string  XML version of input
+	 * @access private
+	 */
+	function _fromCharset($charset, $input) {
+		if($charset == '' || (strtolower($charset) == $this->_charset))
+			return $input;
+
+		return @iconv($charset, $this->_charset. "//TRANSLIT", $input);
+	}
+
+	/**
+	 * Z-Push helper for error logging
+	 * removing PEAR dependency
+	 *
+	 * @param  string  debug message
+	 * @return boolean always false as there was an error
+	 * @access private
+	 */
+	function raiseError($message) {
+		debugLog("mimeDecode error: ". $message);
+		return false;
+	}
 } // End of class
Index: /contrib/z-push/include/z_RFC822.php
===================================================================
--- /contrib/z-push/include/z_RFC822.php	(revision 4898)
+++ /contrib/z-push/include/z_RFC822.php	(revision 4898)
@@ -0,0 +1,925 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2001-2002, Richard Heyes                                |
+// | All rights reserved.                                                  |
+// |                                                                       |
+// | Redistribution and use in source and binary forms, with or without    |
+// | modification, are permitted provided that the following conditions    |
+// | are met:                                                              |
+// |                                                                       |
+// | o Redistributions of source code must retain the above copyright      |
+// |   notice, this list of conditions and the following disclaimer.       |
+// | o Redistributions in binary form must reproduce the above copyright   |
+// |   notice, this list of conditions and the following disclaimer in the |
+// |   documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote      |
+// |   products derived from this software without specific prior written  |
+// |   permission.                                                         |
+// |                                                                       |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
+// |                                                                       |
+// +-----------------------------------------------------------------------+
+// | Authors: Richard Heyes <richard@phpguru.org>                          |
+// |          Chuck Hagenbuch <chuck@horde.org>                            |
+// +-----------------------------------------------------------------------+
+
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * What is it?
+ *
+ * This class will take an address string, and parse it into it's consituent
+ * parts, be that either addresses, groups, or combinations. Nested groups
+ * are not supported. The structure it returns is pretty straight forward,
+ * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
+ * print_r() to view the structure.
+ *
+ * How do I use it?
+ *
+ * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
+ * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
+ * print_r($structure);
+ *
+ * @author  Richard Heyes <richard@phpguru.org>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @version $Revision: 1.23 $
+ * @license BSD
+ * @package Mail
+ */
+class Mail_RFC822 {
+
+    /**
+     * The address being parsed by the RFC822 object.
+     * @var string $address
+     */
+    var $address = '';
+
+    /**
+     * The default domain to use for unqualified addresses.
+     * @var string $default_domain
+     */
+    var $default_domain = 'localhost';
+
+    /**
+     * Should we return a nested array showing groups, or flatten everything?
+     * @var boolean $nestGroups
+     */
+    var $nestGroups = true;
+
+    /**
+     * Whether or not to validate atoms for non-ascii characters.
+     * @var boolean $validate
+     */
+    var $validate = true;
+
+    /**
+     * The array of raw addresses built up as we parse.
+     * @var array $addresses
+     */
+    var $addresses = array();
+
+    /**
+     * The final array of parsed address information that we build up.
+     * @var array $structure
+     */
+    var $structure = array();
+
+    /**
+     * The current error message, if any.
+     * @var string $error
+     */
+    var $error = null;
+
+    /**
+     * An internal counter/pointer.
+     * @var integer $index
+     */
+    var $index = null;
+
+    /**
+     * The number of groups that have been found in the address list.
+     * @var integer $num_groups
+     * @access public
+     */
+    var $num_groups = 0;
+
+    /**
+     * A variable so that we can tell whether or not we're inside a
+     * Mail_RFC822 object.
+     * @var boolean $mailRFC822
+     */
+    var $mailRFC822 = true;
+
+    /**
+    * A limit after which processing stops
+    * @var int $limit
+    */
+    var $limit = null;
+
+    /**
+     * Sets up the object. The address must either be set here or when
+     * calling parseAddressList(). One or the other.
+     *
+     * @access public
+     * @param string  $address         The address(es) to validate.
+     * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+     *
+     * @return object Mail_RFC822 A new Mail_RFC822 object.
+     */
+    function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
+    {
+        if (isset($address))        $this->address        = $address;
+        if (isset($default_domain)) $this->default_domain = $default_domain;
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
+        if (isset($validate))       $this->validate       = $validate;
+        if (isset($limit))          $this->limit          = $limit;
+    }
+
+    /**
+     * Starts the whole process. The address must either be set here
+     * or when creating the object. One or the other.
+     *
+     * @access public
+     * @param string  $address         The address(es) to validate.
+     * @param string  $default_domain  Default domain/host etc.
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+     *
+     * @return array A structured array of addresses.
+     */
+    function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
+    {
+        if (!isset($this) || !isset($this->mailRFC822)) {
+            $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
+            return $obj->parseAddressList();
+        }
+
+        if (isset($address))        $this->address        = $address;
+        if (isset($default_domain)) $this->default_domain = $default_domain;
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
+        if (isset($validate))       $this->validate       = $validate;
+        if (isset($limit))          $this->limit          = $limit;
+
+        $this->structure  = array();
+        $this->addresses  = array();
+        $this->error      = null;
+        $this->index      = null;
+
+        // Unfold any long lines in $this->address.
+        $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
+        $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
+
+        while ($this->address = $this->_splitAddresses($this->address));
+        if ($this->address === false || isset($this->error)) {
+            require_once 'PEAR.php';
+            return PEAR::raiseError($this->error);
+        }
+
+        // Validate each address individually.  If we encounter an invalid
+        // address, stop iterating and return an error immediately.
+        foreach ($this->addresses as $address) {
+            $valid = $this->_validateAddress($address);
+
+            if ($valid === false || isset($this->error)) {
+                require_once 'PEAR.php';
+                return PEAR::raiseError($this->error);
+            }
+
+            if (!$this->nestGroups) {
+                $this->structure = array_merge($this->structure, $valid);
+            } else {
+                $this->structure[] = $valid;
+            }
+        }
+
+        return $this->structure;
+    }
+
+    /**
+     * Splits an address into separate addresses.
+     *
+     * @access private
+     * @param string $address The addresses to split.
+     * @return boolean Success or failure.
+     */
+    function _splitAddresses($address)
+    {
+        if (!empty($this->limit) && count($this->addresses) == $this->limit) {
+            return '';
+        }
+
+        if ($this->_isGroup($address) && !isset($this->error)) {
+            $split_char = ';';
+            $is_group   = true;
+        } elseif (!isset($this->error)) {
+            $split_char = ',';
+            $is_group   = false;
+        } elseif (isset($this->error)) {
+            return false;
+        }
+
+        // Split the string based on the above ten or so lines.
+        $parts  = explode($split_char, $address);
+        $string = $this->_splitCheck($parts, $split_char);
+
+        // If a group...
+        if ($is_group) {
+            // If $string does not contain a colon outside of
+            // brackets/quotes etc then something's fubar.
+
+            // First check there's a colon at all:
+            if (strpos($string, ':') === false) {
+                $this->error = 'Invalid address: ' . $string;
+                return false;
+            }
+
+            // Now check it's outside of brackets/quotes:
+            if (!$this->_splitCheck(explode(':', $string), ':')) {
+                return false;
+            }
+
+            // We must have a group at this point, so increase the counter:
+            $this->num_groups++;
+        }
+
+        // $string now contains the first full address/group.
+        // Add to the addresses array.
+        $this->addresses[] = array(
+                                   'address' => trim($string),
+                                   'group'   => $is_group
+                                   );
+
+        // Remove the now stored address from the initial line, the +1
+        // is to account for the explode character.
+        $address = trim(substr($address, strlen($string) + 1));
+
+        // If the next char is a comma and this was a group, then
+        // there are more addresses, otherwise, if there are any more
+        // chars, then there is another address.
+        if ($is_group && substr($address, 0, 1) == ','){
+            $address = trim(substr($address, 1));
+            return $address;
+
+        } elseif (strlen($address) > 0) {
+            return $address;
+
+        } else {
+            return '';
+        }
+
+        // If you got here then something's off
+        return false;
+    }
+
+    /**
+     * Checks for a group at the start of the string.
+     *
+     * @access private
+     * @param string $address The address to check.
+     * @return boolean Whether or not there is a group at the start of the string.
+     */
+    function _isGroup($address)
+    {
+        // First comma not in quotes, angles or escaped:
+        $parts  = explode(',', $address);
+        $string = $this->_splitCheck($parts, ',');
+
+        // Now we have the first address, we can reliably check for a
+        // group by searching for a colon that's not escaped or in
+        // quotes or angle brackets.
+        if (count($parts = explode(':', $string)) > 1) {
+            $string2 = $this->_splitCheck($parts, ':');
+            return ($string2 !== $string);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * A common function that will check an exploded string.
+     *
+     * @access private
+     * @param array $parts The exloded string.
+     * @param string $char  The char that was exploded on.
+     * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
+     */
+    function _splitCheck($parts, $char)
+    {
+        $string = $parts[0];
+
+        for ($i = 0; $i < count($parts); $i++) {
+            if ($this->_hasUnclosedQuotes($string)
+                || $this->_hasUnclosedBrackets($string, '<>')
+                || $this->_hasUnclosedBrackets($string, '[]')
+                || $this->_hasUnclosedBrackets($string, '()')
+                || substr($string, -1) == '\\') {
+                if (isset($parts[$i + 1])) {
+                    $string = $string . $char . $parts[$i + 1];
+                } else {
+                    $this->error = 'Invalid address spec. Unclosed bracket or quotes';
+                    return false;
+                }
+            } else {
+                $this->index = $i;
+                break;
+            }
+        }
+
+        return $string;
+    }
+
+    /**
+     * Checks if a string has an unclosed quotes or not.
+     *
+     * @access private
+     * @param string $string The string to check.
+     * @return boolean True if there are unclosed quotes inside the string, false otherwise.
+     */
+    function _hasUnclosedQuotes($string)
+    {
+        $string     = explode('"', $string);
+        $string_cnt = count($string);
+
+        for ($i = 0; $i < (count($string) - 1); $i++)
+            if (substr($string[$i], -1) == '\\')
+                $string_cnt--;
+
+        return ($string_cnt % 2 === 0);
+    }
+
+    /**
+     * Checks if a string has an unclosed brackets or not. IMPORTANT:
+     * This function handles both angle brackets and square brackets;
+     *
+     * @access private
+     * @param string $string The string to check.
+     * @param string $chars  The characters to check for.
+     * @return boolean True if there are unclosed brackets inside the string, false otherwise.
+     */
+    function _hasUnclosedBrackets($string, $chars)
+    {
+        $num_angle_start = substr_count($string, $chars[0]);
+        $num_angle_end   = substr_count($string, $chars[1]);
+
+        $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
+        $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
+
+        if ($num_angle_start < $num_angle_end) {
+            $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
+            return false;
+        } else {
+            return ($num_angle_start > $num_angle_end);
+        }
+    }
+
+    /**
+     * Sub function that is used only by hasUnclosedBrackets().
+     *
+     * @access private
+     * @param string $string The string to check.
+     * @param integer &$num    The number of occurences.
+     * @param string $char   The character to count.
+     * @return integer The number of occurences of $char in $string, adjusted for backslashes.
+     */
+    function _hasUnclosedBracketsSub($string, &$num, $char)
+    {
+        $parts = explode($char, $string);
+        for ($i = 0; $i < count($parts); $i++){
+            if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
+                $num--;
+            if (isset($parts[$i + 1]))
+                $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
+        }
+
+        return $num;
+    }
+
+    /**
+     * Function to begin checking the address.
+     *
+     * @access private
+     * @param string $address The address to validate.
+     * @return mixed False on failure, or a structured array of address information on success.
+     */
+    function _validateAddress($address)
+    {
+        $is_group = false;
+        $addresses = array();
+
+        if ($address['group']) {
+            $is_group = true;
+
+            // Get the group part of the name
+            $parts     = explode(':', $address['address']);
+            $groupname = $this->_splitCheck($parts, ':');
+            $structure = array();
+
+            // And validate the group part of the name.
+            if (!$this->_validatePhrase($groupname)){
+                $this->error = 'Group name did not validate.';
+                return false;
+            } else {
+                // Don't include groups if we are not nesting
+                // them. This avoids returning invalid addresses.
+                if ($this->nestGroups) {
+                    $structure = new stdClass;
+                    $structure->groupname = $groupname;
+                }
+            }
+
+            $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
+        }
+
+        // If a group then split on comma and put into an array.
+        // Otherwise, Just put the whole address in an array.
+        if ($is_group) {
+            while (strlen($address['address']) > 0) {
+                $parts       = explode(',', $address['address']);
+                $addresses[] = $this->_splitCheck($parts, ',');
+                $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
+            }
+        } else {
+            $addresses[] = $address['address'];
+        }
+
+        // Check that $addresses is set, if address like this:
+        // Groupname:;
+        // Then errors were appearing.
+        if (!count($addresses)){
+            $this->error = 'Empty group.';
+            return false;
+        }
+
+        // Trim the whitespace from all of the address strings.
+        array_map('trim', $addresses);
+
+        // Validate each mailbox.
+        // Format could be one of: name <geezer@domain.com>
+        //                         geezer@domain.com
+        //                         geezer
+        // ... or any other format valid by RFC 822.
+        for ($i = 0; $i < count($addresses); $i++) {
+            if (!$this->validateMailbox($addresses[$i])) {
+                if (empty($this->error)) {
+                    $this->error = 'Validation failed for: ' . $addresses[$i];
+                }
+                return false;
+            }
+        }
+
+        // Nested format
+        if ($this->nestGroups) {
+            if ($is_group) {
+                $structure->addresses = $addresses;
+            } else {
+                $structure = $addresses[0];
+            }
+
+        // Flat format
+        } else {
+            if ($is_group) {
+                $structure = array_merge($structure, $addresses);
+            } else {
+                $structure = $addresses;
+            }
+        }
+
+        return $structure;
+    }
+
+    /**
+     * Function to validate a phrase.
+     *
+     * @access private
+     * @param string $phrase The phrase to check.
+     * @return boolean Success or failure.
+     */
+    function _validatePhrase($phrase)
+    {
+        // Splits on one or more Tab or space.
+        $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
+        $phrase_parts = array();
+        while (count($parts) > 0){
+            $phrase_parts[] = $this->_splitCheck($parts, ' ');
+            for ($i = 0; $i < $this->index + 1; $i++)
+                array_shift($parts);
+        }
+
+        foreach ($phrase_parts as $part) {
+            // If quoted string:
+            if (substr($part, 0, 1) == '"') {
+                if (!$this->_validateQuotedString($part)) {
+                    return false;
+                }
+                continue;
+            }
+
+            // Otherwise it's an atom:
+            if (!$this->_validateAtom($part)) return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Function to validate an atom which from rfc822 is:
+     * atom = 1*<any CHAR except specials, SPACE and CTLs>
+     *
+     * If validation ($this->validate) has been turned off, then
+     * validateAtom() doesn't actually check anything. This is so that you
+     * can split a list of addresses up before encoding personal names
+     * (umlauts, etc.), for example.
+     *
+     * @access private
+     * @param string $atom The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateAtom($atom)
+    {
+        if (!$this->validate) {
+            // Validation has been turned off; assume the atom is okay.
+            return true;
+        }
+        // Check for any char from ASCII 0 - ASCII 127
+        if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
+            return false;
+        }
+
+        // Check for specials:
+        if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
+            return false;
+        }
+
+        // Check for control characters (ASCII 0-31):
+        if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Function to validate quoted string, which is:
+     * quoted-string = <"> *(qtext/quoted-pair) <">
+     *
+     * @access private
+     * @param string $qstring The string to check
+     * @return boolean Success or failure.
+     */
+    function _validateQuotedString($qstring)
+    {
+        // Leading and trailing "
+        $qstring = substr($qstring, 1, -1);
+
+        // Perform check, removing quoted characters first.
+        return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
+    }
+
+    /**
+     * Function to validate a mailbox, which is:
+     * mailbox =   addr-spec         ; simple address
+     *           / phrase route-addr ; name and route-addr
+     *
+     * @access public
+     * @param string &$mailbox The string to check.
+     * @return boolean Success or failure.
+     */
+    function validateMailbox(&$mailbox)
+    {
+        // A couple of defaults.
+        $phrase  = '';
+        $comment = '';
+        $comments = array();
+
+        // Catch any RFC822 comments and store them separately.
+        $_mailbox = $mailbox;
+        while (strlen(trim($_mailbox)) > 0) {
+            $parts = explode('(', $_mailbox);
+            $before_comment = $this->_splitCheck($parts, '(');
+            if ($before_comment != $_mailbox) {
+                // First char should be a (.
+                $comment    = substr(str_replace($before_comment, '', $_mailbox), 1);
+                $parts      = explode(')', $comment);
+                $comment    = $this->_splitCheck($parts, ')');
+                $comments[] = $comment;
+
+                // +1 is for the trailing )
+                $_mailbox   = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);
+            } else {
+                break;
+            }
+        }
+
+        foreach ($comments as $comment) {
+            $mailbox = str_replace("($comment)", '', $mailbox);
+        }
+
+        $mailbox = trim($mailbox);
+
+        // Check for name + route-addr
+        if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
+            $parts  = explode('<', $mailbox);
+            $name   = $this->_splitCheck($parts, '<');
+
+            $phrase     = trim($name);
+            $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
+
+            //z-push fix for umlauts and other special chars
+            if (substr($phrase, 0, 1) != '"' && substr($phrase, -1) != '"') {
+                $phrase = '"'.$phrase.'"';
+            }
+
+            if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
+
+                return false;
+            }
+
+        // Only got addr-spec
+        } else {
+            // First snip angle brackets if present.
+            if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
+                $addr_spec = substr($mailbox, 1, -1);
+            } else {
+                $addr_spec = $mailbox;
+            }
+
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        }
+
+        // Construct the object that will be returned.
+        $mbox = new stdClass();
+
+        // Add the phrase (even if empty) and comments
+        $mbox->personal = $phrase;
+        $mbox->comment  = isset($comments) ? $comments : array();
+
+        if (isset($route_addr)) {
+            $mbox->mailbox = $route_addr['local_part'];
+            $mbox->host    = $route_addr['domain'];
+            $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
+        } else {
+            $mbox->mailbox = $addr_spec['local_part'];
+            $mbox->host    = $addr_spec['domain'];
+        }
+
+        $mailbox = $mbox;
+        return true;
+    }
+
+    /**
+     * This function validates a route-addr which is:
+     * route-addr = "<" [route] addr-spec ">"
+     *
+     * Angle brackets have already been removed at the point of
+     * getting to this function.
+     *
+     * @access private
+     * @param string $route_addr The string to check.
+     * @return mixed False on failure, or an array containing validated address/route information on success.
+     */
+    function _validateRouteAddr($route_addr)
+    {
+        // Check for colon.
+        if (strpos($route_addr, ':') !== false) {
+            $parts = explode(':', $route_addr);
+            $route = $this->_splitCheck($parts, ':');
+        } else {
+            $route = $route_addr;
+        }
+
+        // If $route is same as $route_addr then the colon was in
+        // quotes or brackets or, of course, non existent.
+        if ($route === $route_addr){
+            unset($route);
+            $addr_spec = $route_addr;
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        } else {
+            // Validate route part.
+            if (($route = $this->_validateRoute($route)) === false) {
+                return false;
+            }
+
+            $addr_spec = substr($route_addr, strlen($route . ':'));
+
+            // Validate addr-spec part.
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        }
+
+        if (isset($route)) {
+            $return['adl'] = $route;
+        } else {
+            $return['adl'] = '';
+        }
+
+        $return = array_merge($return, $addr_spec);
+        return $return;
+    }
+
+    /**
+     * Function to validate a route, which is:
+     * route = 1#("@" domain) ":"
+     *
+     * @access private
+     * @param string $route The string to check.
+     * @return mixed False on failure, or the validated $route on success.
+     */
+    function _validateRoute($route)
+    {
+        // Split on comma.
+        $domains = explode(',', trim($route));
+
+        foreach ($domains as $domain) {
+            $domain = str_replace('@', '', trim($domain));
+            if (!$this->_validateDomain($domain)) return false;
+        }
+
+        return $route;
+    }
+
+    /**
+     * Function to validate a domain, though this is not quite what
+     * you expect of a strict internet domain.
+     *
+     * domain = sub-domain *("." sub-domain)
+     *
+     * @access private
+     * @param string $domain The string to check.
+     * @return mixed False on failure, or the validated domain on success.
+     */
+    function _validateDomain($domain)
+    {
+        // Note the different use of $subdomains and $sub_domains
+        $subdomains = explode('.', $domain);
+
+        while (count($subdomains) > 0) {
+            $sub_domains[] = $this->_splitCheck($subdomains, '.');
+            for ($i = 0; $i < $this->index + 1; $i++)
+                array_shift($subdomains);
+        }
+
+        foreach ($sub_domains as $sub_domain) {
+            if (!$this->_validateSubdomain(trim($sub_domain)))
+                return false;
+        }
+
+        // Managed to get here, so return input.
+        return $domain;
+    }
+
+    /**
+     * Function to validate a subdomain:
+     *   subdomain = domain-ref / domain-literal
+     *
+     * @access private
+     * @param string $subdomain The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateSubdomain($subdomain)
+    {
+        if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
+            if (!$this->_validateDliteral($arr[1])) return false;
+        } else {
+            if (!$this->_validateAtom($subdomain)) return false;
+        }
+
+        // Got here, so return successful.
+        return true;
+    }
+
+    /**
+     * Function to validate a domain literal:
+     *   domain-literal =  "[" *(dtext / quoted-pair) "]"
+     *
+     * @access private
+     * @param string $dliteral The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateDliteral($dliteral)
+    {
+        return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
+    }
+
+    /**
+     * Function to validate an addr-spec.
+     *
+     * addr-spec = local-part "@" domain
+     *
+     * @access private
+     * @param string $addr_spec The string to check.
+     * @return mixed False on failure, or the validated addr-spec on success.
+     */
+    function _validateAddrSpec($addr_spec)
+    {
+        $addr_spec = trim($addr_spec);
+
+        // Split on @ sign if there is one.
+        if (strpos($addr_spec, '@') !== false) {
+            $parts      = explode('@', $addr_spec);
+            $local_part = $this->_splitCheck($parts, '@');
+            $domain     = substr($addr_spec, strlen($local_part . '@'));
+
+        // No @ sign so assume the default domain.
+        } else {
+            $local_part = $addr_spec;
+            $domain     = $this->default_domain;
+        }
+
+        if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
+        if (($domain     = $this->_validateDomain($domain)) === false) return false;
+
+        // Got here so return successful.
+        return array('local_part' => $local_part, 'domain' => $domain);
+    }
+
+    /**
+     * Function to validate the local part of an address:
+     *   local-part = word *("." word)
+     *
+     * @access private
+     * @param string $local_part
+     * @return mixed False on failure, or the validated local part on success.
+     */
+    function _validateLocalPart($local_part)
+    {
+        $parts = explode('.', $local_part);
+        $words = array();
+
+        // Split the local_part into words.
+        while (count($parts) > 0){
+            $words[] = $this->_splitCheck($parts, '.');
+            for ($i = 0; $i < $this->index + 1; $i++) {
+                array_shift($parts);
+            }
+        }
+
+        // Validate each word.
+        foreach ($words as $word) {
+            // If this word contains an unquoted space, it is invalid. (6.2.4)
+            if (strpos($word, ' ') && $word[0] !== '"')
+            {
+                return false;
+            }
+
+            if ($this->_validatePhrase(trim($word)) === false) return false;
+        }
+
+        // Managed to get here, so return the input.
+        return $local_part;
+    }
+
+    /**
+     * Returns an approximate count of how many addresses are in the
+     * given string. This is APPROXIMATE as it only splits based on a
+     * comma which has no preceding backslash. Could be useful as
+     * large amounts of addresses will end up producing *large*
+     * structures when used with parseAddressList().
+     *
+     * @param  string $data Addresses to count
+     * @return int          Approximate count
+     */
+    function approximateCount($data)
+    {
+        return count(preg_split('/(?<!\\\\),/', $data));
+    }
+
+    /**
+     * This is a email validating function separate to the rest of the
+     * class. It simply validates whether an email is of the common
+     * internet form: <user>@<domain>. This can be sufficient for most
+     * people. Optional stricter mode can be utilised which restricts
+     * mailbox characters allowed to alphanumeric, full stop, hyphen
+     * and underscore.
+     *
+     * @param  string  $data   Address to check
+     * @param  boolean $strict Optional stricter mode
+     * @return mixed           False if it fails, an indexed array
+     *                         username/domain if it matches
+     */
+    function isValidInetAddress($data, $strict = false)
+    {
+        $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
+        if (preg_match($regex, trim($data), $matches)) {
+            return array($matches[1], $matches[2]);
+        } else {
+            return false;
+        }
+    }
+
+}
Index: /contrib/z-push/include/utils.php
===================================================================
--- /contrib/z-push/include/utils.php	(revision 4898)
+++ /contrib/z-push/include/utils.php	(revision 4898)
@@ -0,0 +1,234 @@
+<?php
+
+/***********************************************
+* File      :   utils.php
+* Project   :   Z-Push
+* Descr     :
+*
+* Created   :   03.04.2008
+*
+*  Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+// saves information about folder data for a specific device
+function _saveFolderData($devid, $folders) {
+    if (!is_array($folders) || empty ($folders))
+        return false;
+
+    $unique_folders = array ();
+
+    foreach ($folders as $folder) {
+        if (!isset($folder->type))
+            continue;
+
+        // don't save folder-ids for emails
+        if ($folder->type == SYNC_FOLDER_TYPE_INBOX)
+            continue;
+
+        // no folder from that type    or the default folder
+        if (!array_key_exists($folder->type, $unique_folders) || $folder->parentid == 0) {
+            $unique_folders[$folder->type] = $folder->serverid;
+        }
+    }
+
+    // Treo does initial sync for calendar and contacts too, so we need to fake
+    // these folders if they are not supported by the backend
+    if (!array_key_exists(SYNC_FOLDER_TYPE_APPOINTMENT, $unique_folders))
+        $unique_folders[SYNC_FOLDER_TYPE_APPOINTMENT] = SYNC_FOLDER_TYPE_DUMMY;
+    if (!array_key_exists(SYNC_FOLDER_TYPE_CONTACT, $unique_folders))
+        $unique_folders[SYNC_FOLDER_TYPE_CONTACT] = SYNC_FOLDER_TYPE_DUMMY;
+
+    if (!file_put_contents(BASE_PATH.STATE_DIR."/compat-$devid", serialize($unique_folders))) {
+        debugLog("_saveFolderData: Data could not be saved!");
+    }
+}
+
+// returns information about folder data for a specific device
+function _getFolderID($devid, $class) {
+    $filename = BASE_PATH.STATE_DIR."/compat-$devid";
+
+    if (file_exists($filename)) {
+        $arr = unserialize(file_get_contents($filename));
+
+        if ($class == "Calendar")
+            return $arr[SYNC_FOLDER_TYPE_APPOINTMENT];
+        if ($class == "Contacts")
+            return $arr[SYNC_FOLDER_TYPE_CONTACT];
+
+    }
+
+    return false;
+}
+
+/**
+ * Function which converts a hex entryid to a binary entryid.
+ * @param string @data the hexadecimal string
+ */
+function hex2bin($data) {
+    return pack("H*", $data);
+}
+
+function utf8_to_windows1252($string, $option = "")
+{
+    if (function_exists("iconv")){
+        return @iconv("UTF-8", "Windows-1252" . $option, $string);
+    }else{
+        return utf8_decode($string); // no euro support here
+    }
+}
+
+function windows1252_to_utf8($string, $option = "")
+{
+    if (function_exists("iconv")){
+        return @iconv("Windows-1252", "UTF-8" . $option, $string);
+    }else{
+        return utf8_encode($string); // no euro support here
+    }
+}
+
+function w2u($string) { return windows1252_to_utf8($string); }
+function u2w($string) { return utf8_to_windows1252($string); }
+
+function w2ui($string) { return windows1252_to_utf8($string, "//TRANSLIT"); }
+function u2wi($string) { return utf8_to_windows1252($string, "//TRANSLIT"); }
+
+/**
+ * Truncate an UTF-8 encoded sting correctly
+ *
+ * If it's not possible to truncate properly, an empty string is returned
+ *
+ * @param string $string - the string
+ * @param string $length - position where string should be cut
+ * @return string truncated string
+ */
+function utf8_truncate($string, $length) {
+    if (strlen($string) <= $length)
+        return $string;
+
+    while($length >= 0) {
+        if ((ord($string[$length]) < 0x80) || (ord($string[$length]) >= 0xC0))
+            return substr($string, 0, $length);
+
+        $length--;
+    }
+    return "";
+}
+
+
+/**
+ * Build an address string from the components
+ *
+ * @param string $street - the street
+ * @param string $zip - the zip code
+ * @param string $city - the city
+ * @param string $state - the state
+ * @param string $country - the country
+ * @return string the address string or null
+ */
+function buildAddressString($street, $zip, $city, $state, $country) {
+    $out = "";
+
+    if (isset($country) && $street != "") $out = $country;
+
+    $zcs = "";
+    if (isset($zip) && $zip != "") $zcs = $zip;
+    if (isset($city) && $city != "") $zcs .= (($zcs)?" ":"") . $city;
+    if (isset($state) && $state != "") $zcs .= (($zcs)?" ":"") . $state;
+    if ($zcs) $out = $zcs . "\r\n" . $out;
+
+    if (isset($street) && $street != "") $out = $street . (($out)?"\r\n\r\n". $out: "") ;
+
+    return ($out)?$out:null;
+}
+
+/**
+ * Checks if the PHP-MAPI extension is available and in a requested version
+ *
+ * @param string $version - the version to be checked ("6.30.10-18495", parts or build number)
+ * @return boolean installed version is superior to the checked strin
+ */
+function checkMapiExtVersion($version = "") {
+    // compare build number if requested
+    if (preg_match('/^\d+$/',$version) && strlen > 3) {
+        $vs = preg_split('/-/', phpversion("mapi"));
+        return ($version <= $vs[1]);
+    }
+
+    if (extension_loaded("mapi")){
+        if (version_compare(phpversion("mapi"), $version) == -1){
+            return false;
+        }
+    }
+    else
+        return false;
+
+    return true;
+}
+
+/**
+ * Parses and returns an ecoded vCal-Uid from an
+ * OL compatible GlobalObjectID
+ *
+ * @param string $olUid - an OL compatible GlobalObjectID
+ * @return string the vCal-Uid if available in the olUid, else the original olUid as HEX
+ */
+function getICalUidFromOLUid($olUid){
+    //check if "vCal-Uid" is somewhere in outlookid case-insensitive
+    $icalUid = stristr($olUid, "vCal-Uid");
+    if ($icalUid !== false) {
+        //get the length of the ical id - go back 4 position from where "vCal-Uid" was found
+        $begin = unpack("V", substr($olUid, strlen($icalUid) * (-1) - 4, 4));
+        //remove "vCal-Uid" and packed "1" and use the ical id length
+        return substr($icalUid, 12, ($begin[1] - 13));
+    }
+    return strtoupper(bin2hex($olUid));
+}
+
+/**
+ * Checks the given UID if it is an OL compatible GlobalObjectID
+ * If not, the given UID is encoded inside the GlobalObjectID
+ *
+ * @param string $icalUid - an appointment uid as HEX
+ * @return string an OL compatible GlobalObjectID
+ *
+ */
+function getOLUidFromICalUid($icalUid) {
+    if (strlen($icalUid) <= 64) {
+        $len = 13 + strlen($icalUid);
+        $OLUid = pack("V", $len);
+        $OLUid .= "vCal-Uid";
+        $OLUid .= pack("V", 1);
+        $OLUid .= $icalUid;
+        return hex2bin("040000008200E00074C5B7101A82E0080000000000000000000000000000000000000000". bin2hex($OLUid). "00");
+    }
+    else
+       return hex2bin($icalUid);
+}
+
+/**
+ * Extracts the basedate of the GlobalObjectID and the RecurStartTime
+ *
+ * @param string $goid - OL compatible GlobalObjectID
+ * @param long $recurStartTime - RecurStartTime
+ * @return long basedate
+ *
+ */
+function extractBaseDate($goid, $recurStartTime) {
+    $hexbase = substr(bin2hex($goid), 32, 8);
+    $day = hexdec(substr($hexbase, 6, 2));
+    $month = hexdec(substr($hexbase, 4, 2));
+    $year = hexdec(substr($hexbase, 0, 4));
+
+    if ($day && $month && $year) {
+        $h = $recurStartTime >> 12;
+        $m = ($recurStartTime - $h * 4096) >> 6;
+        $s = $recurStartTime - $h * 4096 - $m * 64;
+
+        return gmmktime($h, $m, $s, $month, $day, $year);
+    }
+    else
+        return false;
+}
+?>
Index: /contrib/z-push/include/z_ical.php
===================================================================
--- /contrib/z-push/include/z_ical.php	(revision 4898)
+++ /contrib/z-push/include/z_ical.php	(revision 4898)
@@ -0,0 +1,171 @@
+<?php
+/***********************************************
+* File      :   z_ical.php
+* Project   :   Z-Push
+* Descr     :
+*
+* Created   :   01.12.2008
+*
+* Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+class ZPush_ical{
+    function ZPush_ical($store){
+        $this->_store = $store;
+    }
+
+    /*
+    * Function reads calendar part and puts mapi properties into an array.
+    */
+    function extractProps($ical, &$mapiprops) {
+        //mapping between partstat in ical and MAPI Meeting Response classes as well as icons
+        $aClassMap = array(
+            "ACCEPTED"          => array("class" => "IPM.Schedule.Meeting.Resp.Pos", "icon" => 0x405),
+            "DECLINED"          => array("class" => "IPM.Schedule.Meeting.Resp.Neg", "icon" => 0x406),
+            "TENTATIVE"         => array("class" => "IPM.Schedule.Meeting.Resp.Tent", "icon" => 0x407),
+            "NEEDS-ACTION"      => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //iphone
+            "REQ-PARTICIPANT"   => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //nokia
+        );
+
+        $aical = array_map("rtrim", preg_split("/[\n]/", $ical));
+        $elemcount = count($aical);
+        $i=0;
+        $nextline = $aical[0];
+        //last element is empty
+        while ($i < $elemcount - 1) {
+            $line = $nextline;
+            //if a line starts with a space or a tab it belongs to the previous line
+            $nextline = $aical[$i+1];
+            if (strlen($nextline) == 0) {
+                $i++;
+                continue;
+            }
+
+            while ($nextline{0} == " " || $nextline{0} == "\t") {
+                $line .= substr($nextline, 1);
+                $nextline = $aical[++$i + 1];
+            }
+            switch (strtoupper($line)) {
+                case "BEGIN:VCALENDAR":
+                case "BEGIN:VEVENT":
+                case "END:VEVENT":
+                case "END:VCALENDAR":
+                    break;
+                default:
+                    unset ($field, $data, $prop_pos, $property);
+                    if (ereg ("([^:]+):(.*)", $line, $line)){
+                        $field = $line[1];
+                        $data = $line[2];
+                        $property = $field;
+                        $prop_pos = strpos($property,';');
+                        if ($prop_pos !== false) $property = substr($property, 0, $prop_pos);
+                        $property = strtoupper($property);
+
+                        switch ($property) {
+                            case 'DTSTART':
+                                $data = $this->getTimestampFromStreamerDate($data);
+                                $namedStartTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820d");
+                                $mapiprops[$namedStartTime] = $data;
+                                $namedCommonStart = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8516");
+                                $mapiprops[$namedCommonStart] = $data;
+                                $clipStart = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x8235");
+                                $mapiprops[$clipStart] = $data;
+                                $mapiprops[PR_START_DATE] = $data;
+                                break;
+
+                            case 'DTEND':
+                                $data = $this->getTimestampFromStreamerDate($data);
+                                $namedEndTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820e");
+                                $mapiprops[$namedEndTime] = $data;
+                                $namedCommonEnd = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8517");
+                                $mapiprops[$namedCommonEnd] = $data;
+                                $clipEnd = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x8236");
+                                $mapiprops[$clipEnd] = $data;
+                                $mapiprops[PR_END_DATE] = $data;
+                                break;
+
+                            case 'UID':
+                                $goid = GetPropIDFromString($this->_store, "PT_BINARY:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x3");
+                                $goid2 = GetPropIDFromString($this->_store, "PT_BINARY:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x23");
+                                $mapiprops[$goid] = $mapiprops[$goid2] = hex2bin($data);
+                                break;
+
+                            case 'ATTENDEE':
+                                $fields = explode(";", $field);
+                                foreach ($fields as $field) {
+                                    $prop_pos     = strpos($field, '=');
+                                    if ($prop_pos !== false) {
+                                        switch (substr($field, 0, $prop_pos)) {
+                                            case 'PARTSTAT'    : $partstat = substr($field, $prop_pos+1); break;
+                                            case 'CN'        : $cn = substr($field, $prop_pos+1); break;
+                                            case 'ROLE'        : $role = substr($field, $prop_pos+1); break;
+                                            case 'RSVP'        : $rsvp = substr($field, $prop_pos+1); break;
+                                        }
+                                    }
+                                }
+                                if (isset($partstat) && isset($aClassMap[$partstat]) &&
+
+                                   (!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) {
+                                    $mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$partstat]['class'];
+                                    $mapiprops[PR_ICON_INDEX] = $aClassMap[$partstat]['icon'];
+                                }
+                                // START ADDED dw2412 to support meeting requests on HTC Android Mail App
+                                elseif (isset($role) && isset($aClassMap[$role]) &&
+                                   (!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) {
+                                    $mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$role]['class'];
+                                    $mapiprops[PR_ICON_INDEX] = $aClassMap[$role]['icon'];
+                                }
+                                // END ADDED dw2412 to support meeting requests on HTC Android Mail App
+                                if (!isset($cn)) $cn = ""; 
+                                $data         = str_replace ("MAILTO:", "", $data);
+                                $attendee[] = array ('name' => stripslashes($cn), 'email' => stripslashes($data));
+                                break;
+
+                            case 'ORGANIZER':
+                                $field          = str_replace("ORGANIZER;CN=", "", $field);
+                                $data          = str_replace ("MAILTO:", "", $data);
+                                $organizer[] = array ('name' => stripslashes($field), 'email' => stripslashes($data));
+                                break;
+
+                            case 'LOCATION':
+                                $data = str_replace("\\n", "<br />", $data);
+                                $data = str_replace("\\t", "&nbsp;", $data);
+                                $data = str_replace("\\r", "<br />", $data);
+                                $data = stripslashes($data);
+                                $namedLocation = GetPropIDFromString($this->_store, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208");
+                                $mapiprops[$namedLocation] = $data;
+                                $tneflocation = GetPropIDFromString($this->_store, "PT_STRING8:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x2");
+                                $mapiprops[$tneflocation] = $data;
+                                break;
+                        }
+                    }
+                    break;
+            }
+            $i++;
+
+        }
+        $useTNEF = GetPropIDFromString($this->_store, "PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8582");
+        $mapiprops[$useTNEF] = true;
+    }
+
+    /*
+     * Converts an YYYYMMDDTHHMMSSZ kind of string into an unixtimestamp
+     *
+     * @param string $data
+     * @return long
+     */
+    function getTimestampFromStreamerDate ($data) {
+        $data = str_replace('Z', '', $data);
+        $data = str_replace('T', '', $data);
+
+        preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs);
+        if ($regs[1] < 1970) {
+            $regs[1] = '1971';
+        }
+        return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+    }
+}
+
+?>
Index: /contrib/z-push/include/z_tnef.php
===================================================================
--- /contrib/z-push/include/z_tnef.php	(revision 4898)
+++ /contrib/z-push/include/z_tnef.php	(revision 4898)
@@ -0,0 +1,668 @@
+<?php
+/***********************************************
+* File      :   z_tnef.php
+* Project   :   Z-Push
+* Descr     :
+*
+* Created   :   21.06.2008
+*
+* Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************
+
+ * This is tnef implementation for z-push. It is based on
+ * Zarafa's tnef implementation.
+ *
+ * The ZPush_tnef class does only simple reading of a tnef stream.
+ * Most importantly, we currently only support properties within
+ * the message itself, and do not support recipient tables and
+ * attachment properties within the TNEF data.
+ *
+ * The ZPush_tnef class will accept TNEF streams with data about
+ * recipients and attachments, but the information will be ignored.
+ *
+ * Created on 21.06.2008 by Manfred
+ *
+ * For more information on tnef refer to:
+ * http://msdn.microsoft.com/en-us/library/ms530652(EXCHG.10).aspx
+ * http://msdn.microsoft.com/en-us/library/cc425498(EXCHG.80).aspx
+ *
+ * The mapping between Microsoft Mail IPM classes and those used in
+ * MAPI see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx
+ */
+
+define('TNEF_SIGNATURE',      0x223e9f78);
+define('TNEF_LVL_MESSAGE',    0x01);
+define('TNEF_LVL_ATTACHMENT', 0x02);
+
+
+
+define('ZP_DWORD', 32);
+define('ZP_WORD', 16);
+define('ZP_BYTE', 8);
+
+define('ZP_GUID_SIZE', 256);
+
+class ZPush_tnef{
+
+    //we need a store in order to get the namedpropers
+    function ZPush_tnef($store) {
+        $this->_store = $store;
+    }
+    /*
+    * Function reads tnef stream and puts mapi properties into an array.
+    */
+    function extractProps($tnefstream, &$mapiprops) {
+        $hresult = NOERROR;
+        $signature = 0; //tnef signature - 32 Bit
+        $key = 0; //a nonzero 16-bit unsigned integer
+
+        $type = 0; // 32-bit value
+        $size = 0; // 32-bit value
+        $checksum = 0; //16-bit value
+        $component = 0; //8-bit value - either TNEF_LVL_MESSAGE or TNEF_LVL_ATTACHMENT
+        $buffer = "";
+
+        //mapping between Microsoft Mail IPM classes and those in MAPI
+        $aClassMap = array(
+            "IPM.Microsoft Schedule.MtgReq"        => "IPM.Schedule.Meeting.Request",
+            "IPM.Microsoft Schedule.MtgRespP"    => "IPM.Schedule.Meeting.Resp.Pos",
+            "IPM.Microsoft Schedule.MtgRespN"    => "IPM.Schedule.Meeting.Resp.Neg",
+            "IPM.Microsoft Schedule.MtgRespA"    => "IPM.Schedule.Meeting.Resp.Tent",
+            "IPM.Microsoft Schedule.MtgCncl"    => "IPM.Schedule.Meeting.Canceled",
+            "IPM.Microsoft Mail.Non-Delivery"    => "Report.IPM.Note.NDR",
+            "IPM.Microsoft Mail.Read Receipt"    => "Report.IPM.Note.IPNRN",
+            "IPM.Microsoft Mail.Note"            => "IPM.Note",
+            "IPM.Microsoft Mail.Note"            => "IPM",
+        );
+        //read signature
+        $hresult = $this->_readFromTnefStream($tnefstream, ZP_DWORD, $signature);
+        if ($hresult !== NOERROR) {
+            debugLog("TNEF STREAM:".bin2hex($tnefstream));
+            debugLog("There was an error reading tnef signature");
+            return $hresult;
+        }
+
+        //check signature
+        if ($signature != TNEF_SIGNATURE) {
+            debugLog("Corrupt signature.");
+            return MAPI_E_CORRUPT_DATA;
+        }
+
+        //read key
+        $hresult = $this->_readFromTnefStream($tnefstream, ZP_WORD, $key);
+        if ($hresult !== NOERROR) {
+            debugLog("There was an error reading tnef key.");
+            return $hresult;
+        }
+
+        // File is made of blocks, with each a type and size. Component and Key are ignored.
+        while(1) {
+            //the stream is empty. exit
+            if (strlen($tnefstream) == 0) return NOERROR;
+
+            //read component - it is either TNEF_LVL_MESSAGE or TNEF_LVL_ATTACHMENT
+            $hresult = $this->_readFromTnefStream($tnefstream, ZP_BYTE, $component);
+            if ($hresult !== NOERROR) {
+                $hresult = NOERROR; //EOF -> no error
+                return $hresult;
+                break;
+            }
+
+            //read type
+            $hresult = $this->_readFromTnefStream($tnefstream, ZP_DWORD, $type);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property type");
+                return $hresult;
+            }
+
+            //read size
+            $hresult = $this->_readFromTnefStream($tnefstream, ZP_DWORD, $size);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property size");
+                return $hresult;
+            }
+
+            if ($size == 0) {
+                // do not allocate 0 size data block
+                debugLog("Size is 0. Corrupt data.");
+                return MAPI_E_CORRUPT_DATA;
+            }
+
+            //read buffer
+            $hresult = $this->_readBuffer($tnefstream, $size, $buffer);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property buffer");
+                return $hresult;
+            }
+
+            //read checksum
+            $hresult = $this->_readFromTnefStream($tnefstream, ZP_WORD, $checksum);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property checksum.");
+                return $hresult;
+            }
+
+            // Loop through all the blocks of the TNEF data. We are only interested
+            // in the properties block for now (0x00069003)
+            switch ($type) {
+                case 0x00069003:
+                    $hresult = $this->_readMapiProps($buffer, $size, $mapiprops);
+                    if ($hresult !== NOERROR) {
+                        debugLog("There was an error reading mapi properties' part.");
+                        return $hresult;
+                    }
+                    break;
+                case 0x00078008: // PR_MESSAGE_CLASS
+                    $msMailClass = trim($buffer);
+                    if (array_key_exists($msMailClass, $aClassMap)) {
+                        $messageClass = $aClassMap[$msMailClass];
+                    }
+                    else {
+                        $messageClass = $msMailClass;
+                    }
+                    $mapiprops[PR_MESSAGE_CLASS] = $messageClass;
+                    break;
+                case 0x00050008: // PR_OWNER_APPT_ID
+                    $mapiprops[PR_OWNER_APPT_ID] = $buffer;
+                    break;
+                case 0x00040009: // PR_RESPONSE_REQUESTED
+                    $mapiprops[PR_RESPONSE_REQUESTED] = $buffer;
+                    break;
+
+                // --- TNEF attachemnts ---
+                case 0x00069002:
+                    break;
+                case 0x00018010:        // PR_ATTACH_FILENAME
+                    break;
+                case 0x00068011:        // PR_ATTACH_RENDERING, extra icon information
+                    break;
+                case 0x0006800f:        // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
+                    break;
+                case 0x00069005:        // Attachment property stream
+                    break;
+                default:
+                    // Ignore this block
+                    break;
+            }
+
+        }
+        return NOERROR;
+
+    }
+
+    /*
+    * Reads a given number of bits from stream and converts them from little indian in a "normal" order. The function
+    * also cuts the tnef stream after reading.
+    */
+    function _readFromTnefStream(&$tnefstream, $bits, &$element) {
+        $bytes = $bits / 8;
+
+        $part = substr($tnefstream, 0, $bytes);
+        $packs = array();
+
+        switch ($bits) {
+            case ZP_DWORD:
+                $packs = unpack("V", $part);
+                break;
+            case ZP_WORD:
+                $packs = unpack("v", $part);
+                break;
+            case ZP_BYTE:
+                $packs[1] = ord($part[0]);
+                break;
+            default:
+                $packs = array();
+                break;
+        }
+
+        if (empty($packs) || !isset($packs[1])) return MAPI_E_CORRUPT_DATA;
+
+        $tnefstream = substr($tnefstream, $bytes);
+        $element = $packs[1];
+        return NOERROR;
+    }
+
+    /*
+    * Reads a given number of bits from stream and puts them into $element. The function
+    * also cuts the tnef stream after reading.
+    */
+    function _readBuffer(&$tnefstream, $bytes, &$element) {
+
+        $element = substr($tnefstream, 0, $bytes);
+        $tnefstream = substr($tnefstream, $bytes);
+        return NOERROR;
+
+    }
+
+    /*
+    * Reads mapi props from buffer into an anrray.
+    */
+    function _readMapiProps (&$buffer, $size, &$mapiprops) {
+        $nrprops = 0;
+        //get number of mapi properties
+        $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $nrprops);
+        if ($hresult !== NOERROR) {
+                debugLog("There was an error getting the number of mapi properties in stream.");
+                return $hresult;
+        }
+
+        $size -= 4;
+
+//debugLog("nrprops:$nrprops");
+        //loop through all the properties and add them to our internal list
+        while($nrprops) {
+//debugLog("\tPROP:$nrprops");
+            $hresult = $this->_readSingleMapiProp($buffer, $size, $read, $mapiprops);
+            if ($hresult !== NOERROR) {
+                    debugLog("There was an error reading a mapi property.");
+                    debugLog("result: " . sprintf("%x", $hresult));
+
+                    return $hresult;
+            }
+            $nrprops--;
+        }
+        return NOERROR;
+    }
+
+    /*
+    * Reads a single mapi prop.
+    */
+    function _readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) {
+        $propTag = 0;
+        $len = 0;
+        $origSize = $size;
+        $isNamedId = 0;
+        $namedProp = 0;
+        $count = 0;
+        $mvProp = 0;
+        $guid = 0;
+
+        if($size < 8) {
+            return MAPI_E_NOT_FOUND;
+        }
+
+        $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $propTag);
+        if ($hresult !== NOERROR) {
+            debugLog("There was an error reading a mapi property tag from the stream.");
+            return $hresult;
+        }
+        $size -= 4;
+//debugLog("mapi prop type:".dechex(mapi_prop_type($propTag)));
+//debugLog("mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag)));
+        if (mapi_prop_id($propTag) >= 0x8000) {
+        // Named property, first read GUID, then name/id
+            if($size < 24) {
+                debugLog("Corrupt guid size for named property:".dechex($propTag));
+                return MAPI_E_CORRUPT_DATA;
+            }
+            //strip GUID & name/id
+            $hresult = $this->_readBuffer($buffer, 16, $guid);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property buffer");
+                return $hresult;
+            }
+
+            $size -= 16;
+             //it is not used and is here only for eventual debugging
+            $readableGuid = unpack("VV/v2v/n4n", $guid);
+            $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}",$readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'],$readableGuid['n1'],$readableGuid['n2'],$readableGuid['n3'],$readableGuid['n4']);
+//debugLog("guid:$readableGuid");
+            $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $isNamedId);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading stream property checksum.");
+                return $hresult;
+            }
+            $size -= 4;
+
+            if($isNamedId != 0) {
+                // A string name follows
+                //read length of the property
+                $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len);
+                if ($hresult !== NOERROR) {
+                    debugLog("There was an error reading mapi property's length");
+                    return $hresult;
+                }
+                $size -= 4;
+                if ($size < $len) {
+                    return MAPI_E_CORRUPT_DATA;
+                }
+                //read the name of the property, eg Keywords
+                $hresult = $this->_readBuffer($buffer, $len, $namedProp);
+                if ($hresult !== NOERROR) {
+                    debugLog("There was an error reading stream property buffer");
+                    return $hresult;
+                }
+
+                $size -= $len;
+
+                //Re-align
+                $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
+                $size -= $len & 3 ? 4 - ($len & 3) : 0;
+            }
+            else {
+                $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $namedProp);
+                if ($hresult !== NOERROR) {
+                    debugLog("There was an error reading mapi property's length");
+                    return $hresult;
+                }
+//debugLog("named: 0x".sprintf("%04x", $namedProp));
+                $size -= 4;
+            }
+
+            if ($this->_store !== false) {
+                $named = mapi_getidsfromnames($this->_store, array($namedProp), array(makeguid($readableGuid)));
+
+                $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0]));
+            }
+            else {
+                debugLog("Store not available. It is impossible to get named properties");
+            }
+        }
+//debugLog("mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag)));
+        if($propTag & MV_FLAG) {
+            if($size < 4) {
+                return MAPI_E_CORRUPT_DATA;
+            }
+            //read the number of properties
+            $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $count);
+            if ($hresult !== NOERROR) {
+                debugLog("There was an error reading number of properties for:".dechex($propTag));
+                return $hresult;
+            }
+            $size -= 4;
+        }
+        else {
+            $count = 1;
+        }
+
+        for ($mvProp = 0; $mvProp < $count; $mvProp++) {
+            switch(mapi_prop_type($propTag) & ~MV_FLAG ) {
+                case PT_I2:
+                case PT_LONG:
+                    $hresult = $this->_readBuffer($buffer, 4, $value);
+                    if ($hresult !== NOERROR) {
+                        debugLog("There was an error reading stream property buffer");
+                        return $hresult;
+                    }
+                    $value = unpack("V", $value);
+                    $value = intval($value[1], 16);
+
+                    if($propTag & MV_FLAG) {
+                        $mapiprops[$propTag][] = $value;
+                    }
+                    else {
+                        $mapiprops[$propTag] = $value;
+                    }
+                    $size -= 4;
+//debugLog("int or long propvalue:".$value);
+                    break;
+
+                case PT_R4:
+                    if($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag][]);
+
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    $size -= 4;
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+                case PT_BOOLEAN:
+                    $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    $size -= 4;
+                    //reported by dw2412
+                    //cast to integer as it evaluates to 1 or 0 because
+                    //a non empty string evaluates to true :(
+                    $mapiprops[$propTag] = (integer) bin2hex($mapiprops[$propTag]{0});
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+
+                case PT_SYSTIME:
+                    if($size < 8) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    if($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag][]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    //we have to convert the filetime to an unixtime timestamp
+                    $filetime = unpack("V2v", $mapiprops[$propTag]);
+                    //php on 64-bit systems converts unsigned values differently than on 32 bit systems
+                    //we need this "fix" in order to get the same values on both types of systems
+                    $filetime['v2'] = substr(sprintf("%08x",$filetime['v2']), -8);
+                    $filetime['v1'] = substr(sprintf("%08x",$filetime['v1']), -8);
+
+                    $filetime = hexdec($filetime['v2'].$filetime['v1']);
+                    $filetime = ($filetime - 116444736000000000) / 10000000;
+                    $mapiprops[$propTag] = $filetime;
+                    // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough
+                    if ($propTag == PR_START_DATE) {
+                        $namedStartTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820d");
+                        $mapiprops[$namedStartTime] = $filetime;
+                        $namedCommonStart = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8516");
+                        $mapiprops[$namedCommonStart] = $filetime;
+                    }
+                    if ($propTag == PR_END_DATE) {
+                        $namedEndTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820e");
+                        $mapiprops[$namedEndTime] = $filetime;
+                        $namedCommonEnd = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8517");
+                        $mapiprops[$namedCommonEnd] = $filetime;
+                    }
+                    $size -= 8;
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+                case PT_DOUBLE:
+                case PT_CURRENCY:
+                case PT_I8:
+                case PT_APPTIME:
+                    if($size < 8) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    if($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag][]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    $size -= 8;
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+                case PT_STRING8:
+                    if($size < 8) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    // Skip next 4 bytes, it's always '1' (ULONG)
+                    $buffer = substr($buffer, 4);
+                    $size -= 4;
+
+                    //read length of the property
+                    $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len);
+                    if ($hresult !== NOERROR) {
+                        debugLog("There was an error reading mapi property's length");
+                        return $hresult;
+                    }
+                    $size -= 4;
+                    if ($size < $len) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+
+                    if ($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    //location fix. it looks like tnef uses this value for location
+                    if (mapi_prop_id($propTag) == 0x8342) {
+                        $namedLocation = GetPropIDFromString($this->_store, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208");
+                        $mapiprops[$namedLocation] = $mapiprops[$propTag];
+                        unset($mapiprops[$propTag]);
+                    }
+
+                    $size -= $len;
+
+                    //Re-align
+                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
+                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+                case PT_UNICODE:
+                    if($size < 8) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    // Skip next 4 bytes, it's always '1' (ULONG)
+                    $buffer = substr($buffer, 4);
+                    $size -= 4;
+
+                    //read length of the property
+                    $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len);
+                    if ($hresult !== NOERROR) {
+                        debugLog("There was an error reading mapi property's length");
+                        return $hresult;
+                    }
+                    $size -= 4;
+                    if ($size < $len) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8
+                    $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag));
+
+                    if ($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+
+                    //location fix. it looks like tnef uses this value for location
+                    if (mapi_prop_id($propTag) == 0x8342) {
+                        $namedLocation = GetPropIDFromString($this->_store, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208");
+                        $mapiprops[$namedLocation] = $mapiprops[$propTag];
+                        unset($mapiprops[$propTag]);
+                        $mapiprops[$namedLocation] = iconv("UCS-2","windows-1252", $mapiprops[$namedLocation]);
+                    }
+
+                    //convert from unicode to windows encoding
+                    if (isset($mapiprops[$propTag])) $mapiprops[$propTag] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
+                    $size -= $len;
+
+                    //Re-align
+                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
+                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
+//debugLog("propvalue:".$mapiprops[$propTag]);
+                    break;
+
+                case PT_OBJECT:        // PST sends PT_OBJECT data. Treat as PT_BINARY
+                case PT_BINARY:
+                    if($size < ZP_BYTE) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+                    // Skip next 4 bytes, it's always '1' (ULONG)
+                    $buffer = substr($buffer, 4);
+                    $size -= 4;
+
+                    //read length of the property
+                    $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len);
+                    if ($hresult !== NOERROR) {
+                        debugLog("There was an error reading mapi property's length");
+                        return $hresult;
+                    }
+                    $size -= 4;
+
+                    if (mapi_prop_type($propTag) == PT_OBJECT) {
+                        // TODO: IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ]
+                        $buffer = substr($buffer, 16);
+                        $size -= 16;
+                        $len -= 16;
+                    }
+
+                    if ($size < $len) {
+                        return MAPI_E_CORRUPT_DATA;
+                    }
+
+                    if ($propTag & MV_FLAG) {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+                    else {
+                        $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]);
+                        if ($hresult !== NOERROR) {
+                            debugLog("There was an error reading stream property buffer");
+                            return $hresult;
+                        }
+                    }
+
+                    $size -= $len;
+
+                    //Re-align
+                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
+                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
+//debugLog("propvalue:".bin2hex($mapiprops[$propTag]));
+                    break;
+
+                default:
+                    return MAPI_E_INVALID_PARAMETER;
+                    break;
+            }
+        }
+        return NOERROR;
+    }
+}
+?>
Index: /contrib/z-push/memimporter.php
===================================================================
--- /contrib/z-push/memimporter.php	(revision 4898)
+++ /contrib/z-push/memimporter.php	(revision 4898)
@@ -0,0 +1,74 @@
+<?php
+/***********************************************
+* File      :   memimporter.php
+* Project   :   Z-Push
+* Descr     :   Classes that collect changes
+*
+* Created   :   01.10.2007
+*
+* © Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+class ImportContentsChangesMem extends ImportContentsChanges {
+    function ImportContentsChangesMem() {
+    }
+    
+    function ImportMessageChange($message) { return true; }
+
+    function ImportMessageDeletion($message) { return true; }
+    
+    function ImportMessageReadFlag($message) { return true; }
+
+    function ImportMessageMove($message) { return true; }
+};
+
+// This simply collects all changes so that they can be retrieved later, for
+// statistics gathering for example
+class ImportHierarchyChangesMem extends ImportHierarchyChanges {
+    var $changed;
+    var $deleted;
+    var $count;
+    var $foldercache;
+    
+    function ImportHierarchyChangesMem($foldercache) {
+    	$this->foldercache = $foldercache;
+        $this->changed = array();
+        $this->deleted = array();
+        $this->count = 0;
+        
+        return true;
+    }
+    
+    function ImportFolderChange($folder) {
+    	// The HierarchyExporter exports all kinds of changes.
+    	// Frequently these changes are not relevant for the mobiles, 
+    	// as something changes but the relevant displayname and parentid 
+    	// stay the same. These changes will be dropped and not sent
+    	if (array_key_exists($folder->serverid, $this->foldercache) &&
+    	    $this->foldercache[$folder->serverid]->displayname == $folder->displayname &&
+            $this->foldercache[$folder->serverid]->parentid == $folder->parentid &&
+            $this->foldercache[$folder->serverid]->type == $folder->type
+           ) {
+            debugLog("Change for folder '".$folder->displayname."' will not be sent as modification is not relevant");  	
+            return true;
+    	}
+   	
+        array_push($this->changed, $folder);
+        $this->count++;
+        // temporarily add/update the folder to the cache so changes are not sent twice
+        $this->foldercache[$folder->serverid] = $folder;
+        return true;
+    }
+
+    function ImportFolderDeletion($id) {
+        array_push($this->deleted, $id);
+        
+        $this->count++;
+        
+        return true;
+    }
+};
+
+?>
Index: /contrib/z-push/backend/BackendCalendarExpresso.php
===================================================================
--- /contrib/z-push/backend/BackendCalendarExpresso.php	(revision 4219)
+++ /contrib/z-push/backend/BackendCalendarExpresso.php	(revision 4898)
@@ -7,5 +7,5 @@
  * Created   :   06.12.2010 - emerson-faria.nobre@serpro.gov.br
  *
- * ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+ * Ã¯Â¿Âœ Zarafa Deutschland GmbH, www.zarafaserver.de
  * This file is distributed under GPL v2.
  * Consult LICENSE file for details
@@ -65,12 +65,12 @@
 			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
 			while ($row = pg_fetch_row($result)) {
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> Lê item do BD (cal_id: '.$row[0].', title: '.$row[4].', last_update: '.date("d/m/Y G:i:s",substr($row[1], 0, strlen($row[1])-3)).', last_update_timestamp: '.$row[1].', datetime: '.date("d/m/Y G:i:s",$row[2]).', datetime_timestamp: '.$row[2].', cal_type: '.$row[3].')');
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> LÃª item do BD (cal_id: '.$row[0].', title: '.$row[4].', last_update: '.date("d/m/Y G:i:s",substr($row[1], 0, strlen($row[1])-3)).', last_update_timestamp: '.$row[1].', datetime: '.date("d/m/Y G:i:s",$row[2]).', datetime_timestamp: '.$row[2].', cal_type: '.$row[3].')');
 				$result_recur = pg_query($this->db,"select recur_enddate from phpgw_cal_repeats where cal_id = " . $row[0] . ";");
 				if ($result_recur == FALSE) throw new Exception(pg_last_error($this->db));
 				if ($row[3] == 'M') {
 					if ($recur_enddate = pg_fetch_result($result_recur, 0, 0)) {
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> Evento '.$row[0].' é recorrente: recur_enddate : '.date("d/m/Y G:i:s",$recur_enddate).', recur_enddate_timestamp: '.$recur_enddate);
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> Evento '.$row[0].' Ã© recorrente: recur_enddate : '.date("d/m/Y G:i:s",$recur_enddate).', recur_enddate_timestamp: '.$recur_enddate);
 						if ($recur_enddate < $row[2]) {
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> ERRO: Esse evento não será adicionado na lista porque o campo phpgw_cal_repeats.recur_enddate é menor que o campo phpgw_cal.datetime. Corrija o valor de um dos campos');
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> ERRO: Esse evento nÃ£o serÃ¡ adicionado na lista porque o campo phpgw_cal_repeats.recur_enddate Ã© menor que o campo phpgw_cal.datetime. Corrija o valor de um dos campos');
 							continue; // Nao sincroniza este evento, porque a data final da recorrencia eh menor que a data inicial do evento. Eh o BD do Expresso que esta inconsistente.
 						}
@@ -91,5 +91,5 @@
 			if ($result_invalid_ref == FALSE) throw new Exception(pg_last_error($this->db));
 			while ($row_invalid_ref = pg_fetch_row($result_invalid_ref)) {
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> ERRO: Evento com campo reference inválido (cal_id: '.$row_invalid_ref[0].', title: '.$row_invalid_ref[4].', reference: '.$row_invalid_ref[5].', last_update: '.date("d/m/Y G:i:s",substr($row_invalid_ref[1], 0, strlen($row[1])-3)).', last_update_timestamp: '.$row_invalid_ref[1].', datetime: '.date("d/m/Y G:i:s",$row_invalid_ref[2]).', datetime_timestamp: '.$row_invalid_ref[2].', cal_type: '.$row_invalid_ref[3].')');
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> ERRO: Evento com campo reference invÃ¡lido (cal_id: '.$row_invalid_ref[0].', title: '.$row_invalid_ref[4].', reference: '.$row_invalid_ref[5].', last_update: '.date("d/m/Y G:i:s",substr($row_invalid_ref[1], 0, strlen($row[1])-3)).', last_update_timestamp: '.$row_invalid_ref[1].', datetime: '.date("d/m/Y G:i:s",$row_invalid_ref[2]).', datetime_timestamp: '.$row_invalid_ref[2].', cal_type: '.$row_invalid_ref[3].')');
 			}
 			// Evento fora da faixa filtrada
@@ -107,5 +107,5 @@
 			debugLog("exception -> " . $e->getMessage() . " - ARQUIVO: " . $e->getFile() . " - LINHA: " . $e->getLine());
 		}
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> Fim normal da função');
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageList-> Fim normal da funÃ§Ã£o');
 		return $messages;
 	}
@@ -147,25 +147,25 @@
 		if($folderid != "calendar") return false;
 		try {
-		 $result = pg_query($this->db,"BEGIN;");
-		 if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-		 $result_calendar = pg_query($this->db, "select last_update from phpgw_cal where cal_id = " . $id . ";");
-		 if ($result_calendar == FALSE) throw new Exception(pg_last_error($this->db));
-		 while ($row_calendar = pg_fetch_row($result_calendar)) {
-		 	if(isset($row_calendar[0])) {
-		 		$message = array();
-		 		$message["mod"] = substr($row_calendar[0], 0, strlen($row_calendar[0])-3);
-		 		$message["id"] = $id;
-		 		$message["flags"] = 1;
-		 		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::StatMessage-> mod: '.$message["mod"].', id: '.$message["id"]);
-		 		return $message;
-		 	}
-		 }
-		 $result = pg_query($this->db,"COMMIT;");
-		 if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result_calendar = pg_query($this->db, "select last_update from phpgw_cal where cal_id = " . $id . ";");
+			if ($result_calendar == FALSE) throw new Exception(pg_last_error($this->db));
+		while ($row_calendar = pg_fetch_row($result_calendar)) {
+			if(isset($row_calendar[0])) {
+				$message = array();
+				$message["mod"] = substr($row_calendar[0], 0, strlen($row_calendar[0])-3);
+				$message["id"] = $id;
+				$message["flags"] = 1;
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::StatMessage-> mod: '.$message["mod"].', id: '.$message["id"]);
+				return $message;
+			}
+		}
+		$result = pg_query($this->db,"COMMIT;");
+		if ($result == FALSE) throw new Exception(pg_last_error($this->db));
 		} catch (Exception $e) {
 			pg_query($this->db,"ROLLBACK;");
 			debugLog("exception -> " . $e->getMessage() . " - ARQUIVO: " . $e->getFile() . " - LINHA: " . $e->getLine());
 		}
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::StatMessage: ERRO: Não encontrou valor para o campo last_update para esse evento.');
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::StatMessage: ERRO: NÃ£o encontrou valor para o campo last_update para esse evento.');
 		return false;
 	}
@@ -186,5 +186,5 @@
 			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
 			if ($row = pg_fetch_row($result)) {
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Lê evento do DB para enviar para o celular (cal_id: '.$row[0].', title:'.$row[1].', location: '.$row[2].', mdatetime: '.$row[3].', datetime: '.$row[4].', edatetime: '.$row[5].', reference: '.$row[7].', isPublic: '.$row[8].')');
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> LÃª evento do DB para enviar para o celular (cal_id: '.$row[0].', title:'.$row[1].', location: '.$row[2].', mdatetime: '.$row[3].', datetime: '.$row[4].', edatetime: '.$row[5].', reference: '.$row[7].', isPublic: '.$row[8].')');
 				if ($row[7] != 0) $has_parent_event = true;
 				else $has_parent_event = false;
@@ -236,10 +236,10 @@
 					}
 				}
-				$message->alldayevent = 0; // (0 - Não(default), 1- Sim)
+				$message->alldayevent = 0; // (0 - NÃ£o(default), 1- Sim)
 				$message->busystatus = 2; // 2 - Ocupado
 				//TODO: Tratar $message - timezone
-				//$tz = $this->_getGMTTZ();
+				// $tz = $this->_getGMTTZ();
 				$tz = array("bias" => 180, "stdbias" => 0, "dstbias" => -60, "dstendyear" => 0, "dstendmonth" => 2, "dstendday" => 0, "dstendweek" => 2, "dstendhour" => 2, "dstendminute" => 0, "dstendsecond" => 0, "dstendmillis" => 0,
-		                                      "dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
+						                                      "dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
 				$message->timezone = base64_encode($this->_getSyncBlobFromTZ($tz));
 				//TODO: Se necessario, tratar $message - meetingstatus
@@ -265,5 +265,5 @@
 				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
 				if ($row = pg_fetch_row($result)){
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Lê recorrência do evento (recur_type: '.$row[0].', recur_use_end: '.$row[1].', recur_enddate: '.$row[2].', recur_interval: '.$row[3].', recur_data: '.$row[4].', recur_exception: '.$row[5].')');
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> LÃª recorrÃªncia do evento (recur_type: '.$row[0].', recur_use_end: '.$row[1].', recur_enddate: '.$row[2].', recur_interval: '.$row[3].', recur_data: '.$row[4].', recur_exception: '.$row[5].')');
 					$recur = new SyncRecurrence();
 					// Converte os tipos do Expresso para os tipos do Protocolo ActiveSync
@@ -311,5 +311,5 @@
 					}
 					$message->recurrence = $recur;
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - Lê recorrência do evento.');
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - LÃª recorrÃªncia do evento.');
 				}
 
@@ -318,5 +318,5 @@
 				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
 				while ($row_recur_exception = pg_fetch_row($result)) {
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO: Lê evento único da recorrência com id: '.$row_recur_exception[0]);
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO: LÃª evento Ãºnico da recorrÃªncia com id: '.$row_recur_exception[0]);
 					$recur_exception = $this->GetMessageDAO($row_recur_exception[0]);
 					if ($recur_exception instanceof SyncAppointment) {
@@ -324,5 +324,5 @@
 						array_push($message->exceptions, $recur_exception);
 					}
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - Lê evento único da recorrência com id: '.$row_recur_exception[0]);
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - LÃª evento Ãºnico da recorrÃªncia com id: '.$row_recur_exception[0]);
 				}
 
@@ -331,5 +331,5 @@
 					$array_timestamps_recur_deleted = explode(",",$row[5]);
 					foreach ($array_timestamps_recur_deleted as $timestamp_recur_deleted) {
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO: Timestamps dos eventos únicos da recorrência do tipo deletados: '.$timestamp_recur_deleted);
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO: Timestamps dos eventos Ãºnicos da recorrÃªncia do tipo deletados: '.$timestamp_recur_deleted);
 						$recur_deleted = new SyncAppointment();
 						$recur_deleted->deleted = '1';
@@ -340,5 +340,4 @@
 				}
 				//TODO: Tratar Participantes do Evento
-
 			}
 			$result = pg_query($this->db,"COMMIT;");
@@ -349,5 +348,5 @@
 			return;
 		}
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - Lê evento do DB para enviar para o celular: '.$id);
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::GetMessageDAO-> Fim do Bloco - LÃª evento do DB para enviar para o celular: '.$id);
 		return $message;
 	}
@@ -389,5 +388,5 @@
 			return false;
 		}
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog("CalendarExpresso::DeleteMessage-> Fim normal da Função.");
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog("CalendarExpresso::DeleteMessage-> Fim normal da FunÃ§Ã£o.");
 		return true;
 	}
@@ -399,5 +398,5 @@
 	function ChangeMessageDAO($id, $message) {
 		debugLog('CalendarExpresso::ChangeMessageDAO('.$id.', ..)');
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> O evento do celular será inserido/atualizado no BD com o ID: '.$id);
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> O evento do celular serÃ¡ inserido/atualizado no BD com o ID: '.$id);
 		try {
 			$result = pg_query($this->db,"BEGIN;");
@@ -543,5 +542,5 @@
 			//TODO: Implementar para os tipos 3 e 6. O Expresso ainda nao suporta esses tipos :-(
 			if (isset($message->recurrence) and ($message->recurrence->type == 0 or $message->recurrence->type == 1 or $message->recurrence->type == 2 or $message->recurrence->type == 5)) {
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> O Evento é Recorrente.');
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> O Evento Ã© Recorrente.');
 				switch ($message->recurrence->type) {
 					case 0: //repeticao diaria
@@ -562,13 +561,13 @@
 						break;
 				}
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência-> recur_type: '.$arrayRecur["recur_type"]);
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia-> recur_type: '.$arrayRecur["recur_type"]);
 				if (isset($message->recurrence->interval)) {
 					$arrayRecur["recur_interval"] = $message->recurrence->interval;
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência-> recur_interval: '.$arrayRecur["recur_interval"]);
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia-> recur_interval: '.$arrayRecur["recur_interval"]);
 				}
 				if ($message->recurrence->type == 1 or $message->recurrence->type == 3 or $message->recurrence->type == 6) {
 					if (isset($message->recurrence->dayofweek)) {
 						$arrayRecur["recur_data"] = $message->recurrence->dayofweek;
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência-> recur_data: '.$arrayRecur["recur_data"]);
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia-> recur_data: '.$arrayRecur["recur_data"]);
 						/*$day = $message->recurrence->dayofweek;
 						 $day_of_week_counter = 0;
@@ -615,5 +614,5 @@
 					$arrayRecur["recur_enddate"] = 1893283200; // Se nao tem data de termino seta para 30/12/2029
 				}
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência-> recur_enddate: '.$arrayRecur["recur_enddate"]);
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia-> recur_enddate: '.$arrayRecur["recur_enddate"]);
 
 				// Trata excecoes da recorrencia
@@ -621,5 +620,5 @@
 					// Tem Excecoes de recorrencia do tipo Edicao
 					// Verifica se ja existe o registro do evento de excecao de recorrencia na tabela phpgw_cal
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento tem exceção de recorrência');
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento tem exceÃ§Ã£o de recorrÃªncia');
 					$recur_exception_changed_ids = "";
 					$recur_exception_deleted_starttimes = "";
@@ -643,5 +642,5 @@
 									$except_description_from_DB = $row[2];
 									$found_except_cal_id = true;
-									if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Achou o evento na tabela phpgw_cal, vai ATUALIZAR a exceção da recorrência nessa tabela');
+									if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Achou o evento na tabela phpgw_cal, vai ATUALIZAR a exceÃ§Ã£o da recorrÃªncia nessa tabela');
 								}
 							}
@@ -653,5 +652,5 @@
 									if(isset($row[0])) {
 										$found_except_cal_user = true;
-										if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Achou o evento na tabela phpgw_cal_user, vai ATUALIZAR a exceção da recorrência nessa tabela');
+										if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Achou o evento na tabela phpgw_cal_user, vai ATUALIZAR a exceÃ§Ã£o da recorrÃªncia nessa tabela');
 									}
 								}
@@ -661,5 +660,5 @@
 							if(isset($message->uid)) {
 								$arrayExceptCal["uid"] = $this->truncateString(utf8_decode($message->uid),255);
-								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> uid: '.$arrayExceptCal["uid"]);
+								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> uid: '.$arrayExceptCal["uid"]);
 							}
 							if(isset($recur_exception->subject)) {
@@ -668,5 +667,5 @@
 								$arrayExceptCal["title"] = $this->truncateString(utf8_decode($message->subject),299);
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> title: '.$arrayExceptCal["title"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> title: '.$arrayExceptCal["title"]);
 							if(isset($recur_exception->location)) {
 								$arrayExceptCal["location"] = $this->truncateString(utf8_decode($recur_exception->location),255);
@@ -674,5 +673,5 @@
 								$arrayExceptCal["location"] = $this->truncateString(utf8_decode($message->location),255);
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> location: '.$arrayExceptCal["location"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> location: '.$arrayExceptCal["location"]);
 							if(isset($recur_exception->dtstamp)) {
 								$arrayExceptCal["mdatetime"] = $recur_exception->dtstamp;
@@ -680,5 +679,5 @@
 								$arrayExceptCal["mdatetime"] = $message->dtstamp;
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> mdatetime: '.$arrayExceptCal["mdatetime"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> mdatetime: '.$arrayExceptCal["mdatetime"]);
 							if(isset($recur_exception->starttime)) {
 								$arrayExceptCal["datetime"] = $recur_exception->starttime;
@@ -686,5 +685,5 @@
 								$arrayExceptCal["datetime"] = $message->starttime;
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> datetime: '.$arrayExceptCal["datetime"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> datetime: '.$arrayExceptCal["datetime"]);
 							if(isset($recur_exception->endtime)) {
 								$arrayExceptCal["edatetime"] = $recur_exception->endtime;
@@ -692,5 +691,5 @@
 								$arrayExceptCal["edatetime"] = $message->endtime;
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> edatetime: '.$arrayExceptCal["edatetime"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> edatetime: '.$arrayExceptCal["edatetime"]);
 							if(isset($recur_exception->sensitivity) and $recur_exception->sensitivity == 0) {
 								$arrayExceptCal["is_public"] = 1; // 1 - Normal
@@ -700,5 +699,5 @@
 								$arrayExceptCal["is_public"] = 1; // 1 - Normal
 							}else $arrayExceptCal["is_public"] = 0; // 0 - Privado
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> is_public: '.$arrayExceptCal["is_public"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> is_public: '.$arrayExceptCal["is_public"]);
 							if (isset($recur_exception->body)) {
 								$arrayExceptCal["description"] = utf8_decode($recur_exception->body);
@@ -738,5 +737,5 @@
 								}
 							}
-							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Exceção De Recorrência-> category: '.$arrayExceptCal["category"]);
+							if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> ExceÃ§Ã£o De RecorrÃªncia-> category: '.$arrayExceptCal["category"]);
 							//TODO: Converter eventos que sao alldayevent para iniciar as 0:00hs e terminar as 23:59hs. Isso porque o Expresso nao suporta alldayevents
 							//		 if(isset($message->alldayevent)) {
@@ -752,9 +751,9 @@
 								$result = pg_insert($this->db, 'phpgw_cal', $arrayExceptCal);
 								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de Exceção da Recorrência INSERIDO na tabela phpgw_cal com cal_id: '.$arrayExceptCal["cal_id"]);
+								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de ExceÃ§Ã£o da RecorrÃªncia INSERIDO na tabela phpgw_cal com cal_id: '.$arrayExceptCal["cal_id"]);
 							} else {
 								$result = pg_update($this->db, 'phpgw_cal', $arrayExceptCal, array('cal_id' => $except_cal_id));
 								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de Exceção da Recorrência ATUALIZADO na tabela phpgw_cal');
+								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de ExceÃ§Ã£o da RecorrÃªncia ATUALIZADO na tabela phpgw_cal');
 							}
 
@@ -768,9 +767,9 @@
 								$result = pg_insert($this->db, 'phpgw_cal_user', $arrayExceptCalUser);
 								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de Exceção da Recorrência INSERIDO na tabela phpgw_cal_user com cal_id: '.$arrayExceptCalUser["cal_id"]);
+								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de ExceÃ§Ã£o da RecorrÃªncia INSERIDO na tabela phpgw_cal_user com cal_id: '.$arrayExceptCalUser["cal_id"]);
 							} else {
 								$result = pg_update($this->db, 'phpgw_cal_user', $arrayExceptCalUser, array('cal_id' => $except_cal_id, 'cal_login' => $this->_uidnumber));
 								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de Exceção da Recorrência ATUALIZADO na tabela phpgw_cal_user');
+								if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento de ExceÃ§Ã£o da RecorrÃªncia ATUALIZADO na tabela phpgw_cal_user');
 							}
 							if ($recur_exception_changed_ids == "") {
@@ -790,8 +789,8 @@
 						//Tem Excecoes de recorrencia do tipo Exclusao
 						$arrayRecur["recur_exception"] = $recur_exception_deleted_starttimes;
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento tem exceção de recorrência do tipo exclusão com os seguintes starttimes: '.$recur_exception_deleted_starttimes);
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Evento tem exceÃ§Ã£o de recorrÃªncia do tipo exclusÃ£o com os seguintes starttimes: '.$recur_exception_deleted_starttimes);
 					} else {
 						$arrayRecur["recur_exception"] = "";
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Não tem exceção de recorrência do tipo exclusão para esse evento');
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> NÃ£o tem exceÃ§Ã£o de recorrÃªncia do tipo exclusÃ£o para esse evento');
 					}
 					if ($recur_exception_changed_ids == "") {
@@ -801,5 +800,5 @@
 						$result = pg_query($this->db, "delete from phpgw_cal where reference = " . $cal_id . ";");
 						if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Não tem exceção de recorrência, então deleta possíveis eventos de exceção de recorrência criados anteriormente para esse evento.');
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> NÃ£o tem exceÃ§Ã£o de recorrÃªncia, entÃ£o deleta possÃ­veis eventos de exceÃ§Ã£o de recorrÃªncia criados anteriormente para esse evento.');
 					} else {
 						//Deleta as excecoes da recorrencia, exceto as inseridas/atualizadas acima
@@ -808,5 +807,5 @@
 						$result = pg_query($this->db, "delete from phpgw_cal where reference = " . $cal_id . " and cal_id not in (" . $recur_exception_changed_ids . ");");
 						if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Deleta as exçeções da recorrência, exceto as inseridas/atualizadas pelo celular com os seguintes cal_id:'.$recur_exception_changed_ids);
+						if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Deleta as exÃ§eÃ§Ãµes da recorrÃªncia, exceto as inseridas/atualizadas pelo celular com os seguintes cal_id:'.$recur_exception_changed_ids);
 					}
 				} else {
@@ -817,5 +816,5 @@
 					$result = pg_query($this->db, "delete from phpgw_cal where reference = " . $cal_id . ";");
 					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Não tem exceção de recorrência, então deleta possíveis eventos de exceção de recorrência criados anteriormente para esse evento..');
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> NÃ£o tem exceÃ§Ã£o de recorrÃªncia, entÃ£o deleta possÃ­veis eventos de exceÃ§Ã£o de recorrÃªncia criados anteriormente para esse evento..');
 				}
 				if (!$found_cal_repeats){
@@ -823,9 +822,9 @@
 					$result = pg_insert($this->db, 'phpgw_cal_repeats', $arrayRecur);
 					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência INSERIDA na tabela phpgw_cal_repeats com cal_id: '.$arrayRecur["cal_id"]);
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia INSERIDA na tabela phpgw_cal_repeats com cal_id: '.$arrayRecur["cal_id"]);
 				} else {
 					$result = pg_update($this->db, 'phpgw_cal_repeats', $arrayRecur, array('cal_id' => $cal_id));
 					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Recorrência ATUALIZADA na tabela phpgw_cal_repeats');
+					if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> RecorrÃªncia ATUALIZADA na tabela phpgw_cal_repeats');
 				}
 			} elseif ($found_cal_id) {
@@ -837,5 +836,5 @@
 				$result = pg_query($this->db, "delete from phpgw_cal_repeats where cal_id = " . $cal_id . ";");
 				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
-				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Não tem recorrência, então deleta possíveis eventos de recorrência criados anteriormente para esse evento');
+				if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> NÃ£o tem recorrÃªncia, entÃ£o deleta possÃ­veis eventos de recorrÃªncia criados anteriormente para esse evento');
 			}
 			$result = pg_query($this->db,"COMMIT;");
@@ -847,5 +846,5 @@
 			return false;
 		}
-		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Fim normal da Função');
+		if (TRACE_UID !== false and (TRACE_TYPE == 'CALENDAR' or TRACE_TYPE == 'ALL')) traceLog('CalendarExpresso::ChangeMessageDAO-> Fim normal da FunÃ§Ã£o');
 		return $id;
 	}
@@ -917,5 +916,6 @@
 
 	function  removeAccents($data){
-		$data = strtr($data,"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÜÚÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùüúþÿ","aaaaaaaceeeeiiii noooooxouuutbaaaaaaaceeeeiiii nooooo/ouuuty");
+		$data = strtr($data,"ÃÃÃÃÃÃ
+ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ Ã¡Ã¢Ã£Ã€Ã¥ÃŠÃ§ÃšÃ©ÃªÃ«Ã¬Ã­Ã®Ã¯Ã°Ã±Ã²Ã³ÃŽÃµÃ¶Ã·ÃžÃ¹ÃŒÃºÃŸÃ¿","aaaaaaaceeeeiiii noooooxouuutbaaaaaaaceeeeiiii nooooo/ouuuty");
 		return $data;
 	}
@@ -944,7 +944,7 @@
 	function _getGMTTZ() {
 		//$tz = array("bias" => 0, "stdbias" => 0, "dstbias" => 0, "dstendyear" => 0, "dstendmonth" => 2, "dstendday" => 0, "dstendweek" => 2, "dstendhour" => 2, "dstendminute" => 0, "dstendsecond" => 0, "dstendmillis" => 0,
-		//                              "dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
-		$tz = array("bias" => 120, "stdbias" => 0, "dstbias" => -60, "dstendyear" => 0, "dstendmonth" => 2, "dstendday" => 0, "dstendweek" => 2, "dstendhour" => 2, "dstendminute" => 0, "dstendsecond" => 0, "dstendmillis" => 0,
-		                                      "dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
+		//"dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
+		$tz = array("bias" => 120, "stdbias" => 0, "dstbias" => -60, "dstendyear" => 0, "dstendmonth" => 2, "dstendday" => 0, "dstendweek" => 2, "dstendhour" => 2, "dstendminute" => 0, "dstendsecond" => 0, "dstendmillis" => 0, "dststartyear" => 0, "dststartmonth" =>10, "dststartday" =>0, "dststartweek" => 3, "dststarthour" => 2, "dststartminute" => 0, "dststartsecond" => 0, "dststartmillis" => 0);
+		
 		return $tz;
 	}
Index: /contrib/z-push/backend/imap.php
===================================================================
--- /contrib/z-push/backend/imap.php	(revision 4619)
+++ /contrib/z-push/backend/imap.php	(revision 4898)
@@ -9,5 +9,5 @@
  * Created   :   10.10.2007
  *
- * ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+ * Zarafa Deutschland GmbH, www.zarafaserver.de
  * This file is distributed under GPL v2.
  * Consult LICENSE file for details
@@ -91,349 +91,349 @@
 	 * the new message as any other new message in a folder.
 	 */
-    function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
-        debugLog("IMAP-SendMail: for: $forward   reply: $reply   parent: $parent  RFC822:  \n". $rfc822 );
-
-        $mobj = new Mail_mimeDecode($rfc822);
-        $message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'));
-        
-        $Mail_RFC822 = new Mail_RFC822();
-        $toaddr = $ccaddr = $bccaddr = "";
-        if(isset($message->headers["to"]))
-            $toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"]));
-        if(isset($message->headers["cc"]))
-            $ccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["cc"]));
-        if(isset($message->headers["bcc"]))
-            $bccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["bcc"]));
-
-        // save some headers when forwarding mails (content type & transfer-encoding)
-        $headers = "";
-        $forward_h_ct = "";
-        $forward_h_cte = "";
-        $envelopefrom = "";
-
-        $use_orgbody = false;
-        
-        // clean up the transmitted headers
-        // remove default headers because we are using imap_mail
-        $changedfrom = false;
-        $returnPathSet = false;
-        $body_base64 = false;
-        $org_charset = "";
-        $org_boundary = false;
-        $multipartmixed = false;
-        foreach($message->headers as $k => $v) {
-            if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc")
-                continue;
-
-            if ($k == "content-type") {
-                // if the message is a multipart message, then we should use the sent body
-                if (preg_match("/multipart/i", $v)) {
-                    $use_orgbody = true;
-                    $org_boundary = $message->ctype_parameters["boundary"];
-                }
-
-                // save the original content-type header for the body part when forwarding
-                if ($forward && !$use_orgbody) {
-                    $forward_h_ct = $v;
-                    continue;
-                }
-
-                // set charset always to utf-8
-                $org_charset = $v;
-                $v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v);
-            }
-
-            if ($k == "content-transfer-encoding") {
-                // if the content was base64 encoded, encode the body again when sending
-                if (trim($v) == "base64") $body_base64 = true;
-
-                // save the original encoding header for the body part when forwarding
-                if ($forward) {
-                    $forward_h_cte = $v;
-                    continue;
-                }
-            }
-
-            // check if "from"-header is set, do nothing if it's set
-            // else set it to IMAP_DEFAULTFROM
-            if ($k == "from") {
-                if (trim($v)) {
-                    $changedfrom = true;
-                } elseif (! trim($v) && IMAP_DEFAULTFROM) {
-                    $changedfrom = true;
-                    if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
-                    else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
-                    else $v = $this->_username . IMAP_DEFAULTFROM;
-                    $envelopefrom = "-f$v";
-                }
-            }
-
-            // check if "Return-Path"-header is set
-            if ($k == "return-path") {
-                $returnPathSet = true;
-                if (! trim($v) && IMAP_DEFAULTFROM) {
-                    if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
-                    else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
-                    else $v = $this->_username . IMAP_DEFAULTFROM;
-                }
-            }
-
-            // all other headers stay
-            if ($headers) $headers .= "\n";
-            $headers .= ucfirst($k) . ": ". $v;
-        }
-
-        // set "From" header if not set on the device
-        if(IMAP_DEFAULTFROM && !$changedfrom){
-            if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
-            else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
-            else $v = $this->_username . IMAP_DEFAULTFROM;
-            if ($headers) $headers .= "\n";
-            $headers .= 'From: '.$v;
-            $envelopefrom = "-f$v";
-        }
-
-        // set "Return-Path" header if not set on the device
-        if(IMAP_DEFAULTFROM && !$returnPathSet){
-            if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
-            else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
-            else $v = $this->_username . IMAP_DEFAULTFROM;
-            if ($headers) $headers .= "\n";
-            $headers .= 'Return-Path: '.$v;
-        }
-
-        // if this is a multipart message with a boundary, we must use the original body
-        if ($use_orgbody) {
-            list(,$body) = $mobj->_splitBodyHeader($rfc822); 
-            $repl_body = $this->getBody($message); 
-            if ($message->parts[0]->headers["content-transfer-encoding"] == "base64") $multipart_text_cte_base64 = true;
-            else $multipart_text_cte_base64 = false;
-        }
-        else
-            $body = $this->getBody($message);
-
-        // reply
-        if ($reply && $parent) {
-            $this->imap_reopenFolder($parent);
-            // receive entire mail (header + body) to decode body correctly
-            $origmail = @imap_fetchheader($this->_mbox, $reply, FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID);
-
-            $mobj2 = new Mail_mimeDecode($origmail);
-            // receive only body
-            $body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')));
-            // unset mimedecoder & origmail - free memory
-            unset($mobj2);
-            unset($origmail);
-        }
-
-        // encode the body to base64 if it was sent originally in base64 by the pda
-        // contrib - chunk base64 encoded body
-        if ($body_base64 && !$forward) $body = chunk_split(base64_encode($body));
-
-
-        // forward
-        if ($forward && $parent) {
-            $this->imap_reopenFolder($parent);
-            // receive entire mail (header + body)
-            $origmail = @imap_fetchheader($this->_mbox, $forward, FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID);
-
-            //if (!defined('IMAP_INLINE_FORWARD') || IMAP_INLINE_FORWARD === false) {
-            if (defined('IMAP_INLINE_FORWARD') && IMAP_INLINE_FORWARD === false) {
-                // contrib - chunk base64 encoded body
-                if ($body_base64) $body = chunk_split(base64_encode($body));
-                //use original boundary if it's set
-                $boundary = ($org_boundary) ? $org_boundary : false;
-                // build a new mime message, forward entire old mail as file
-                list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte,$boundary);
-                // add boundary headers
-                $headers .= "\n" . $aheader;
-
-            }
-            else {
-                $mobj2 = new Mail_mimeDecode($origmail);
-                $mess2 = $mobj2->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'));
-
-                if (!$use_orgbody)
-                    $nbody = $body;
-                else
-                    $nbody = $repl_body;
-
-                $nbody .= "\r\n\r\n";
-                $nbody .= "-----Original Message-----\r\n";
-                if(isset($mess2->headers['from']))
-                    $nbody .= "From: " . $mess2->headers['from'] . "\r\n";
-                if(isset($mess2->headers['to']) && strlen($mess2->headers['to']) > 0)
-                    $nbody .= "To: " . $mess2->headers['to'] . "\r\n";
-                if(isset($mess2->headers['cc']) && strlen($mess2->headers['cc']) > 0)
-                    $nbody .= "Cc: " . $mess2->headers['cc'] . "\r\n";
-                if(isset($mess2->headers['date']))
-                    $nbody .= "Sent: " . $mess2->headers['date'] . "\r\n";
-                if(isset($mess2->headers['subject']))
-                    $nbody .= "Subject: " . $mess2->headers['subject'] . "\r\n";
-                $nbody .= "\r\n";
-                $nbody .= $this->getBody($mess2);
-
-                if ($body_base64) {
-                    // contrib - chunk base64 encoded body
-                    $nbody = chunk_split(base64_encode($nbody));
-                    if ($use_orgbody)
-                    // contrib - chunk base64 encoded body
-                        $repl_body = chunk_split(base64_encode($repl_body));
-                }
-
-                if ($use_orgbody) {
-                    debugLog("-------------------");
-                    debugLog("old:\n'$repl_body'\nnew:\n'$nbody'\nund der body:\n'$body'");
-                    //$body is quoted-printable encoded while $repl_body and $nbody are plain text,
-                    //so we need to decode $body in order replace to take place
-                    if (!$multipart_text_cte_base64) {
-                       $body = str_replace($repl_body, $nbody, quoted_printable_decode($body));
-                    } else {
-                       $body = str_replace(base64_encode($repl_body), base64_encode($nbody), $body);                    }
-                }
-                else
-                    $body = $nbody;
-
-
-                if(isset($mess2->parts)) {
-                    $attached = false;
-
-                    if ($org_boundary) {
-                        $att_boundary = $org_boundary;
-                        // cut end boundary from body
-                        $body = substr($body, 0, strrpos($body, "--$att_boundary--"));
-                    }
-                    else {
-                        $att_boundary = strtoupper(md5(uniqid(time())));
-                        // add boundary headers
-                        $headers .= "\n" . "Content-Type: multipart/mixed; boundary=$att_boundary";
-                        $multipartmixed = true;
-                    }
-
-                    foreach($mess2->parts as $part) {
-                        if(isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) {
-
-                            if(isset($part->d_parameters['filename']))
-                                $attname = $part->d_parameters['filename'];
-                            else if(isset($part->ctype_parameters['name']))
-                                $attname = $part->ctype_parameters['name'];
-                            else if(isset($part->headers['content-description']))
-                                $attname = $part->headers['content-description'];
-                            else $attname = "unknown attachment";
-
-                            // ignore html content
-                            if ($part->ctype_primary == "text" && $part->ctype_secondary == "html") {
-                                continue;
-                            }
-                            //
-                            if ($use_orgbody || $attached) {
-                                $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary);
-                            }
-                            // first attachment
-                            else {
-                                $encmail = $body;
-                                $attached = true;
-                                $body = $this->enc_multipart($att_boundary, $body, $forward_h_ct, $forward_h_cte);
-                                $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary);
-                            }
-                        }
-                    }
-                    if ($multipartmixed) {
-                        //this happens if a multipart/alternative message is forwarded
-                        //then it's a multipart/mixed message which consists of:
-                        //1. text/plain part which was written on the mobile
-                        //2. multipart/alternative part which is the original message
-                        //$body = "This is a message with multiple parts in MIME format.\n--".
-                        //        $att_boundary.
-                        //        "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n".
-                        //        (($body_base64) ? chunk_split(base64_encode($message->body)) : rtrim($message->body)).
-                        //        "\n--".$att_boundary.
-                        //        "\nContent-Type: {$mess2->headers['content-type']}\n\n".
-                        //        @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID)."\n\n";
-                        $body = "\n--".
-                                $att_boundary.
-                                "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n".
-                                $body;
-                    }
-
-                    $body .= "--$att_boundary--\n\n";
-                }
-
-                unset($mobj2);
-            }
-
-            // unset origmail - free memory
-            unset($origmail);
-
-        }
-
-        // remove carriage-returns from body
-        $body = str_replace("\r\n", "\n", $body);
-
-        if (!$multipartmixed) {
-            if (!empty($forward_h_ct)) $headers .= "\nContent-Type: $forward_h_ct";
-            if (!empty($forward_h_cte)) $headers .= "\nContent-Transfer-Encoding: $forward_h_cte";
-        }
-        //advanced debugging
-        debugLog("IMAP-SendMail: parsed message: ". print_r($message,1));
-        debugLog("IMAP-SendMail: headers: $headers");
-        debugLog("IMAP-SendMail: subject: {$message->headers["subject"]}");
-        debugLog("IMAP-SendMail: body: $body");
-
-        if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) {
-            $send =  @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr);
-        }
-        else {
-            if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr";
-            if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr";
-            $send =  @mail ( $toaddr, $message->headers["subject"], $body, $headers, $envelopefrom );
-        }
-
-        // email sent?
-        if (!$send) {
-            debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error());
-        }
-
-        // add message to the sent folder
-        // build complete headers
-        $headers .= "\nTo: $toaddr";
-        $headers .= "\nSubject: " . $message->headers["subject"];
-
-        if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) {
-            if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr";
-            if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr";
-        }
-        debugLog("IMAP-SendMail: complete headers: $headers");
-
-        $asf = false;
-        if ($this->_sentID) {
-            $asf = $this->addSentMessage($this->_sentID, $headers, $body);
-        }
-        else if (IMAP_SENTFOLDER) {
-            $asf = $this->addSentMessage(IMAP_SENTFOLDER, $headers, $body);
-            debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".IMAP_SENTFOLDER."': ". (($asf)?"success":"failed"));
-        }
-        // No Sent folder set, try defaults
-        else {
-            debugLog("IMAP-SendMail: No Sent mailbox set");
-            if($this->addSentMessage("INBOX.Sent", $headers, $body)) {
-                debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX.Sent'");
-                $asf = true;
-            }
-            else if ($this->addSentMessage("Sent", $headers, $body)) {
-                debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'");
-                $asf = true;
-            }
-            else if ($this->addSentMessage("Sent Items", $headers, $body)) {
-                debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'");
-                $asf = true;
-            }
-        }
-
-        // unset mimedecoder - free memory
-        unset($mobj);
-        return ($send && $asf);
-    }
-	
+	function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
+		debugLog("IMAP-SendMail: for: $forward   reply: $reply   parent: $parent  RFC822:  \n". $rfc822 );
+
+		$mobj = new Mail_mimeDecode($rfc822);
+		$message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'));
+
+		$Mail_RFC822 = new Mail_RFC822();
+		$toaddr = $ccaddr = $bccaddr = "";
+		if(isset($message->headers["to"]))
+			$toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"]));
+		if(isset($message->headers["cc"]))
+			$ccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["cc"]));
+		if(isset($message->headers["bcc"]))
+			$bccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["bcc"]));
+
+		// save some headers when forwarding mails (content type & transfer-encoding)
+		$headers = "";
+		$forward_h_ct = "";
+		$forward_h_cte = "";
+		$envelopefrom = "";
+
+		$use_orgbody = false;
+
+		// clean up the transmitted headers
+		// remove default headers because we are using imap_mail
+		$changedfrom = false;
+		$returnPathSet = false;
+		$body_base64 = false;
+		$org_charset = "";
+		$org_boundary = false;
+		$multipartmixed = false;
+		
+		foreach($message->headers as $k => $v) {
+			if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc")
+				continue;
+
+			if ($k == "content-type") {
+				// if the message is a multipart message, then we should use the sent body
+				if (preg_match("/multipart/i", $v)) {
+					$use_orgbody = true;
+					$org_boundary = $message->ctype_parameters["boundary"];
+				}
+
+				// save the original content-type header for the body part when forwarding
+				if ($forward && !$use_orgbody) {
+					$forward_h_ct = $v;
+					continue;
+				}
+
+				// set charset always to utf-8
+				$org_charset = $v;
+				$v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v);
+			}
+
+			if ($k == "content-transfer-encoding") {
+				// if the content was base64 encoded, encode the body again when sending
+				if (trim($v) == "base64") $body_base64 = true;
+
+				// save the original encoding header for the body part when forwarding
+				if ($forward) {
+					$forward_h_cte = $v;
+					continue;
+				}
+			}
+
+			// check if "from"-header is set, do nothing if it's set
+			// else set it to IMAP_DEFAULTFROM
+			if ($k == "from") {
+				if (trim($v)) {
+					$changedfrom = true;
+				} elseif (! trim($v) && IMAP_DEFAULTFROM) {
+					$changedfrom = true;
+					if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
+					else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
+					else $v = $this->_username . IMAP_DEFAULTFROM;
+					$envelopefrom = "-f$v";
+				}
+			}
+
+			// check if "Return-Path"-header is set
+			if ($k == "return-path") {
+				$returnPathSet = true;
+				if (! trim($v) && IMAP_DEFAULTFROM) {
+					if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
+					else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
+					else $v = $this->_username . IMAP_DEFAULTFROM;
+				}
+			}
+
+			// all other headers stay
+			if ($headers) $headers .= "\n";
+			$headers .= ucfirst($k) . ": ". $v;
+		}
+
+		// set "From" header if not set on the device
+		if(IMAP_DEFAULTFROM && !$changedfrom){
+			if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
+			else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
+			else $v = $this->_username . IMAP_DEFAULTFROM;
+			if ($headers) $headers .= "\n";
+			$headers .= 'From: '.$v;
+			$envelopefrom = "-f$v";
+		}
+
+		// set "Return-Path" header if not set on the device
+		if(IMAP_DEFAULTFROM && !$returnPathSet){
+			if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username;
+			else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain;
+			else $v = $this->_username . IMAP_DEFAULTFROM;
+			if ($headers) $headers .= "\n";
+			$headers .= 'Return-Path: '.$v;
+		}
+
+		// if this is a multipart message with a boundary, we must use the original body
+		if ($use_orgbody) {
+			list(,$body) = $mobj->_splitBodyHeader($rfc822);
+			$repl_body = $this->getBody($message); 
+			if ($message->parts[0]->headers["content-transfer-encoding"] == "base64") $multipart_text_cte_base64 = true;
+			else $multipart_text_cte_base64 = false;
+		}
+		else
+			$body = $this->getBody($message);
+
+		// reply
+		if ($reply && $parent) {
+			$this->imap_reopenFolder($parent);
+			// receive entire mail (header + body) to decode body correctly
+			$origmail = @imap_fetchheader($this->_mbox, $reply, FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID);
+
+			$mobj2 = new Mail_mimeDecode($origmail);
+			// receive only body
+			$body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')));
+			// unset mimedecoder & origmail - free memory
+			unset($mobj2);
+			unset($origmail);
+		}
+
+		// encode the body to base64 if it was sent originally in base64 by the pda
+		// contrib - chunk base64 encoded body
+		if ($body_base64 && !$forward) $body = chunk_split(base64_encode($body));
+
+
+		// forward
+		if ($forward && $parent) {
+			$this->imap_reopenFolder($parent);
+			// receive entire mail (header + body)
+			$origmail = @imap_fetchheader($this->_mbox, $forward, FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID);
+
+				if (defined('IMAP_INLINE_FORWARD') && IMAP_INLINE_FORWARD === false) {
+					// contrib - chunk base64 encoded body
+					if ($body_base64) $body = chunk_split(base64_encode($body));
+					//use original boundary if it's set
+					$boundary = ($org_boundary) ? $org_boundary : false;
+					// build a new mime message, forward entire old mail as file
+					list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte,$boundary);
+					// add boundary headers
+					$headers .= "\n" . $aheader;
+				} 
+				else {
+					$mobj2 = new Mail_mimeDecode($origmail);
+					$mess2 = $mobj2->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'));
+
+					if (!$use_orgbody)
+						$nbody = $body;
+					else
+						$nbody = $repl_body;
+
+					$nbody .= "\r\n\r\n";
+					$nbody .= "-----Original Message-----\r\n";
+					if(isset($mess2->headers['from']))
+						$nbody .= "From: " . $mess2->headers['from'] . "\r\n";
+					if(isset($mess2->headers['to']) && strlen($mess2->headers['to']) > 0)
+						$nbody .= "To: " . $mess2->headers['to'] . "\r\n";
+					if(isset($mess2->headers['cc']) && strlen($mess2->headers['cc']) > 0)
+						$nbody .= "Cc: " . $mess2->headers['cc'] . "\r\n";
+					if(isset($mess2->headers['date']))
+						$nbody .= "Sent: " . $mess2->headers['date'] . "\r\n";
+					if(isset($mess2->headers['subject']))
+						$nbody .= "Subject: " . $mess2->headers['subject'] . "\r\n";
+					
+					$nbody .= "\r\n";
+					$nbody .= $this->getBody($mess2);
+
+					if ($body_base64) {
+						// contrib - chunk base64 encoded body
+						$nbody = chunk_split(base64_encode($nbody));
+						if ($use_orgbody)
+							// contrib - chunk base64 encoded body
+							$repl_body = chunk_split(base64_encode($repl_body));
+					}
+
+					if ($use_orgbody) {
+						debugLog("-------------------");
+						debugLog("old:\n'$repl_body'\nnew:\n'$nbody'\nund der body:\n'$body'");
+						//$body is quoted-printable encoded while $repl_body and $nbody are plain text,
+						//so we need to decode $body in order replace to take place
+						if (!$multipart_text_cte_base64) {
+							$body = str_replace($repl_body, $nbody, quoted_printable_decode($body));
+						} else {
+							$body = str_replace(base64_encode($repl_body), base64_encode($nbody), $body);                    
+						}
+					}
+					else
+						$body = $nbody;
+
+
+					if(isset($mess2->parts)) {
+						$attached = false;
+
+						if ($org_boundary) {
+							$att_boundary = $org_boundary;
+							// cut end boundary from body
+							$body = substr($body, 0, strrpos($body, "--$att_boundary--"));
+						}
+						else {
+							$att_boundary = strtoupper(md5(uniqid(time())));
+							// add boundary headers
+							$headers .= "\n" . "Content-Type: multipart/mixed; boundary=$att_boundary";
+							$multipartmixed = true;
+						}
+
+						foreach($mess2->parts as $part) {
+							if(isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) {
+
+							if(isset($part->d_parameters['filename']))
+								$attname = $part->d_parameters['filename'];
+							else if(isset($part->ctype_parameters['name']))
+								$attname = $part->ctype_parameters['name'];
+							else if(isset($part->headers['content-description']))
+								$attname = $part->headers['content-description'];
+							else $attname = "unknown attachment";
+
+							// ignore html content
+							if ($part->ctype_primary == "text" && $part->ctype_secondary == "html") {
+								continue;
+							}
+							//
+							if ($use_orgbody || $attached) {
+								$body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary);
+							}
+							// first attachment
+							else {
+								$encmail = $body;
+								$attached = true;
+								$body = $this->enc_multipart($att_boundary, $body, $forward_h_ct, $forward_h_cte);
+								$body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary);
+							}
+						}
+					}
+					if ($multipartmixed) {
+						//this happens if a multipart/alternative message is forwarded
+						//then it's a multipart/mixed message which consists of:
+						//1. text/plain part which was written on the mobile
+						//2. multipart/alternative part which is the original message
+						//$body = "This is a message with multiple parts in MIME format.\n--".
+						//        $att_boundary.
+						//        "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n".
+						//        (($body_base64) ? chunk_split(base64_encode($message->body)) : rtrim($message->body)).
+						//        "\n--".$att_boundary.
+						//        "\nContent-Type: {$mess2->headers['content-type']}\n\n".
+						//        @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID)."\n\n";
+						$body = "\n--".
+							$att_boundary.
+							"\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n".
+							$body;
+					}
+
+					$body .= "--$att_boundary--\n\n";
+				}
+
+ 				unset($mobj2);
+			}
+
+			// unset origmail - free memory
+			unset($origmail);
+
+		}
+
+		// remove carriage-returns from body
+		$body = str_replace("\r\n", "\n", $body);
+
+		if (!$multipartmixed) {
+			if (!empty($forward_h_ct)) $headers .= "\nContent-Type: $forward_h_ct";
+			if (!empty($forward_h_cte)) $headers .= "\nContent-Transfer-Encoding: $forward_h_cte";
+		}
+		//advanced debugging
+		debugLog("IMAP-SendMail: parsed message: ". print_r($message,1));
+		debugLog("IMAP-SendMail: headers: $headers");
+		debugLog("IMAP-SendMail: subject: {$message->headers["subject"]}");
+		debugLog("IMAP-SendMail: body: $body");
+
+		if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) {
+			$send =  @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr);
+		}
+		else {
+			if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr";
+			if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr";
+			$send =  @mail ( $toaddr, $message->headers["subject"], $body, $headers, $envelopefrom );
+		}
+
+		// email sent?
+		if (!$send) {
+			debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error());
+		}
+
+		// add message to the sent folder
+		// build complete headers
+		$headers .= "\nTo: $toaddr";
+		$headers .= "\nSubject: " . $message->headers["subject"];
+
+		if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) {
+			if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr";
+			if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr";
+		}
+		debugLog("IMAP-SendMail: complete headers: $headers");
+
+		$asf = false;
+		if ($this->_sentID) {
+			$asf = $this->addSentMessage($this->_sentID, $headers, $body);
+		}
+		else if(defined("IMAP_SENTFOLDER")) {
+			$asf = $this->addSentMessage(IMAP_SENTFOLDER, $headers, $body);
+			debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".IMAP_SENTFOLDER."': ". (($asf)?"success":"failed"));
+		}
+		// No Sent folder set, try defaults
+		else {
+			debugLog("IMAP-SendMail: No Sent mailbox set");
+			if($this->addSentMessage("INBOX.Sent", $headers, $body)) {
+				debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX.Sent'");
+				$asf = true;
+			}
+			else if ($this->addSentMessage("Sent", $headers, $body)) {
+				debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'");
+				$asf = true;
+			}
+			else if ($this->addSentMessage("Sent Items", $headers, $body)) {
+				debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'");
+				$asf = true;
+			}
+		}
+
+		// unset mimedecoder - free memory
+		unset($mobj);
+		return ($send && $asf);
+	}
 
 	/* Should return a wastebasket folder if there is one. This is used when deleting
@@ -468,5 +468,5 @@
 			if ($search !== false)
 			$sequence = implode(",", $search);
-			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A função @imap_search leu as seguintes SEQUÊNCIAS do servidor IMAP com base no filtro de data: '.$sequence);
+			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A funÃ§Ã£o @imap_search leu as seguintes SEQUÃNCIAS do servidor IMAP com base no filtro de data: '.$sequence);
 		}
 
@@ -479,19 +479,19 @@
 				$date = "";
 				$vars = get_object_vars($overview);
-				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A função @imap_fetch_overview leu mais detalhes da mensagem: '. print_r($overview,1));
+				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A funÃ§Ã£o @imap_fetch_overview leu mais detalhes da mensagem: '. print_r($overview,1));
 				if (array_key_exists( "date", $vars)) {
 					// message is out of range for cutoffdate, ignore it
 					if(strtotime(preg_replace("/\(.*\)/", "", $overview->date)) < $cutoffdate) { // emerson-faria.nobre@serpro.gov.br - 07/feb/2011
-						if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> O overview->date: '.$overview->date.' (overview->date_timestamp: '.strtotime(preg_replace("/\(.*\)/", "", $overview->date)).') é menor que o $cutoffdate: '.date("d/m/Y G:i:s",$cutoffdate).' ($cutoffdate_timestamp: '.$cutoffdate.') ou a função "strtotime" gerou um ERRO porque não conseguiu retornar um valor para o overview->date_timestamp. A mensagem será descartada da sincronização.');
+						if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> O overview->date: '.$overview->date.' (overview->date_timestamp: '.strtotime(preg_replace("/\(.*\)/", "", $overview->date)).') Ã© menor que o $cutoffdate: '.date("d/m/Y G:i:s",$cutoffdate).' ($cutoffdate_timestamp: '.$cutoffdate.') ou a funÃ§Ã£o "strtotime" gerou um ERRO porque nÃ£o conseguiu retornar um valor para o overview->date_timestamp. A mensagem serÃ¡ descartada da sincronizaÃ§Ã£o.');
 						continue;
 					}
 					//if(strtotime($overview->date) < $cutoffdate) continue;
 					$date = $overview->date;
-				} else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo date não existe no overview da mensagem.');
+				} else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo date nÃ£o existe no overview da mensagem.');
 
 				// cut of deleted messages
 				if (array_key_exists( "deleted", $vars) && $overview->deleted) {
-					if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A mensagem está com o flag deleted ativo e será descartada da sincronização');
-					continue;
+					if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A mensagem estÃ¡ com o flag deleted ativo e serÃ¡ descartada da sincronizaÃ§Ã£o');
+				continue;
 				}
 
@@ -506,7 +506,7 @@
 					$message["flags"] = 1;
 					array_push($messages, $message);
-				} else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo uid não existe no overview da mensagem.');
-			}
-		}
+				} else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo uid nÃ£o existe no overview da mensagem.');
+				}
+			}
 		return $messages;
 	}
@@ -523,5 +523,5 @@
 			$list = array_reverse($list);
 			foreach ($list as $val) {
-				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> Pasta lida usando a função @imap_getmailboxes: '.print_r($val,1));
+				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> Pasta lida usando a funÃ§Ã£o @imap_getmailboxes: '.print_r($val,1));
 				$box = array();
 
@@ -542,5 +542,5 @@
 					$box["parent"] = "0";
 				}
-				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> Parâmetros da pasta após decodificação: '.print_r($box,1));
+				if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> ParÃ¢metros da pasta apÃ³s decodificaÃ§Ã£o: '.print_r($box,1));
 				$folders[]=$box;
 			}
@@ -699,5 +699,5 @@
 		if (isset($message->parts[$part]->body))
 		print $message->parts[$part]->body;
-		
+
 		// unset mimedecoder & mail
 		unset($mobj);
@@ -729,5 +729,5 @@
 			$vars = get_object_vars($overview[0]);
 
-			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL') and (! array_key_exists( "uid", $vars))) debugLog('IMAP::StatMessage-> ERRO: A mensagem será desconsiderada porque não tem o campo "uid"');
+			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL') and (! array_key_exists( "uid", $vars))) debugLog('IMAP::StatMessage-> ERRO: A mensagem serÃ¡ desconsiderada porque nÃ£o tem o campo "uid"');
 			// without uid it's not a valid message
 			if (! array_key_exists( "uid", $vars)) return false;
@@ -768,5 +768,5 @@
 			$mail = @imap_fetchheader($this->_mbox, $id, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $id, FT_PEEK | FT_UID);
 
-			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog("IMAP-GetMessage: Mensagem lida do servidor IMAP através da função @imap_fetchheader: ". print_r($mail,1));
+			if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog("IMAP-GetMessage: Mensagem lida do servidor IMAP atravÃ©s da funÃ§Ã£o @imap_fetchheader: ". print_r($mail,1));
 
 			$mobj = new Mail_mimeDecode($mail);
@@ -999,8 +999,8 @@
 			// Remove the HTML tags using the 'html2text' - emerson-faria.nobre@serpro.gov.br
 			// The 'html2text' (http://www.mbayer.de/html2text) must be installed in Z-Push server.
-
+				
 			// Advanced debug
 			// debugLog("IMAP-getBody: subject: " . $message->headers["subject"]);
-			$body = utf8_encode($body);
+			$body = utf8_encode($body);	
 			libxml_use_internal_errors(true);
 			try {
@@ -1054,5 +1054,5 @@
 
 		if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) {
-		foreach($message->parts as $part) {
+			foreach($message->parts as $part) {
 				if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment"))  {
 					$this->getBodyRecursive($part, $subtype, $body);
@@ -1086,43 +1086,44 @@
 
 	// build a multipart email, embedding body and one file (for attachments)
-   function mail_attach($filenm,$filesize,$file_cont,$body, $body_ct, $body_cte, $boundary = false) {
-        if (!$boundary) $boundary = strtoupper(md5(uniqid(time())));
-
-        //remove the ending boundary because we will add it at the end
-        $body = str_replace("--$boundary--", "", $body);
-
-        $mail_header = "Content-Type: multipart/mixed; boundary=$boundary\n";
-
-        // build main body with the sumitted type & encoding from the pda
-        $mail_body  = $this->enc_multipart($boundary, $body, $body_ct, $body_cte);
-        $mail_body .= $this->enc_attach_file($boundary, $filenm, $filesize, $file_cont);
-
-        $mail_body .= "--$boundary--\n\n";
-        return array($mail_header, $mail_body);
-    }
-
-    function enc_multipart($boundary, $body, $body_ct, $body_cte) {
+	function mail_attach($filenm,$filesize,$file_cont,$body, $body_ct, $body_cte, $boundary = false) {
+		if (!$boundary) $boundary = strtoupper(md5(uniqid(time())));
+
+		//remove the ending boundary because we will add it at the end
+		$body = str_replace("--$boundary--", "", $body);
+
+		$mail_header = "Content-Type: multipart/mixed; boundary=$boundary\n";
+
+		// build main body with the sumitted type & encoding from the pda
+		$mail_body  = $this->enc_multipart($boundary, $body, $body_ct, $body_cte);
+		$mail_body .= $this->enc_attach_file($boundary, $filenm, $filesize, $file_cont);
+
+		$mail_body .= "--$boundary--\n\n";
+		
+		return array($mail_header, $mail_body);
+	}
+
+	function enc_multipart($boundary, $body, $body_ct, $body_cte) {
 //        $mail_body = "This is a multi-part message in MIME format\n\n";   
 //        $mail_body .= "--$boundary\n";
 //        $mail_body .= "Content-Type: $body_ct\n";
 //        $mail_body .= "Content-Transfer-Encoding: $body_cte\n\n";
-        $mail_body = "$body\n\n";
-
-        return $mail_body;
-    }
+	$mail_body = "$body\n\n";
+
+	return $mail_body;
+	}
     
-    function enc_attach_file($boundary, $filenm, $filesize, $file_cont, $content_type = "") {
-        if (!$content_type) $content_type = "text/plain";
-        $mail_body = "--$boundary\n";
-        $mail_body .= "Content-Type: $content_type; name=\"$filenm\"\n";
-        $mail_body .= "Content-Transfer-Encoding: base64\n";
-        $mail_body .= "Content-Disposition: attachment; filename=\"$filenm\"\n";
-        $mail_body .= "Content-Description: $filenm\n\n";
-        //contrib - chunk base64 encoded attachments
-        $mail_body .= chunk_split(base64_encode($file_cont)) . "\n\n";
-
-        return $mail_body;
-    }
-	
+	function enc_attach_file($boundary, $filenm, $filesize, $file_cont, $content_type = "") {
+		if (!$content_type) $content_type = "text/plain";
+		$mail_body = "--$boundary\n";
+		$mail_body .= "Content-Type: $content_type; name=\"$filenm\"\n";
+		$mail_body .= "Content-Transfer-Encoding: base64\n";
+		$mail_body .= "Content-Disposition: attachment; filename=\"$filenm\"\n";
+		$mail_body .= "Content-Description: $filenm\n\n";
+		//contrib - chunk base64 encoded attachments
+		$mail_body .= chunk_split(base64_encode($file_cont)) . "\n\n";
+
+		return $mail_body;
+	}
+
 	// adds a message as seen to a specified folder (used for saving sent mails)
 	function addSentMessage($folderid, $header, $body) {
Index: /contrib/z-push/backend/backend.php
===================================================================
--- /contrib/z-push/backend/backend.php	(revision 4898)
+++ /contrib/z-push/backend/backend.php	(revision 4898)
@@ -0,0 +1,81 @@
+<?php
+/***********************************************
+* File      :   backend.php
+* Project   :   Z-Push
+* Descr     :   This is what C++ people
+*               (and PHP5) would call an
+*               abstract class. All backend
+*               modules should adhere to this
+*               specification. All communication
+*               with this module is done via
+*               the Sync* object types, the
+*               backend module itself is
+*               responsible for converting any
+*               necessary types and formats.
+*
+*               If you wish to implement a new
+*               backend, all you need to do is
+*               to subclass the following class,
+*               and place the subclassed file in
+*               the backend/ directory. You can
+*               then use your backend by
+*               specifying it in the config.php file
+*
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+define('EXPORT_HIERARCHY', 1);
+define('EXPORT_CONTENTS', 2);
+
+define('BACKEND_DISCARD_DATA', 1);
+
+class ImportContentsChanges {
+    function ImportMessageChange($message) {}
+
+    function ImportMessageDeletion($message) {}
+
+    function ImportMessageReadStateChange($message) {}
+
+    function ImportMessageMove($message) {}
+};
+
+class ImportHierarchyChanges {
+    function ImportFolderChange($folder) {}
+
+    function ImportFolderDeletion($folder) {}
+};
+
+class ExportChanges {
+    // Exports (returns) changes since '$synckey' as an array of Sync* objects. $flags
+    // can be EXPORT_HIERARCHY or EXPORT_CONTENTS. $restrict contains the restriction on which
+    // messages should be filtered. Synckey is updated via reference (!)
+    function ExportChanges($importer, $folderid, $restrict, $syncstate, $flags) {}
+};
+
+class Backend {
+    var $hierarchyimporter;
+    var $contentsimporter;
+    var $exporter;
+
+    // Returns TRUE if the logon succeeded, FALSE if not
+    function Logon($username, $domain, $password) {}
+
+    // called before closing connection
+    function Logoff() {}
+
+    // Returns an array of SyncFolder types for the entire folder hierarchy
+    // on the server (the array itself is flat, but refers to parents via the 'parent'
+    // property)
+    function GetHierarchy() {}
+
+    // Called when a message has to be sent and the message needs to be saved to the 'sent items'
+    // folder
+    function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {}
+};
+
+?>
Index: /contrib/z-push/proto.php
===================================================================
--- /contrib/z-push/proto.php	(revision 4898)
+++ /contrib/z-push/proto.php	(revision 4898)
@@ -0,0 +1,432 @@
+<?php
+/***********************************************
+* File      :   proto.php
+* Project   :   Z-Push
+* Descr     :   WBXML entities that can be parsed
+*               directly (as a stream) from WBXML.
+*               They are automatically decoded
+*               according to $mapping,
+*               and the Sync WBXML mappings.
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+include_once("streamer.php");
+
+class SyncFolder extends Streamer {
+    var $serverid;
+    var $parentid;
+    var $displayname;
+    var $type;
+
+    function SyncFolder() {
+        $mapping = array (
+                                SYNC_FOLDERHIERARCHY_SERVERENTRYID => array (STREAMER_VAR => "serverid"),
+                                SYNC_FOLDERHIERARCHY_PARENTID => array (STREAMER_VAR => "parentid"),
+                                SYNC_FOLDERHIERARCHY_DISPLAYNAME => array (STREAMER_VAR => "displayname"),
+                                SYNC_FOLDERHIERARCHY_TYPE => array (STREAMER_VAR => "type")
+                              );
+
+        parent::Streamer($mapping);
+    }
+};
+
+class SyncAttachment extends Streamer {
+    var $attmethod;
+    var $attsize;
+    var $displayname;
+    var $attname;
+    var $attoid;
+    var $attremoved;
+
+    function SyncAttachment() {
+        $mapping = array(
+                                SYNC_POOMMAIL_ATTMETHOD => array (STREAMER_VAR => "attmethod"),
+                                SYNC_POOMMAIL_ATTSIZE => array (STREAMER_VAR => "attsize"),
+                                SYNC_POOMMAIL_DISPLAYNAME => array (STREAMER_VAR => "displayname"),
+                                SYNC_POOMMAIL_ATTNAME => array (STREAMER_VAR => "attname"),
+                                SYNC_POOMMAIL_ATTOID => array (STREAMER_VAR => "attoid"),
+                                SYNC_POOMMAIL_ATTREMOVED => array (STREAMER_VAR => "attremoved"),
+                        );
+
+        parent::Streamer($mapping);
+    }
+};
+
+class SyncMeetingRequest extends Streamer {
+    function SyncMeetingRequest() {
+        $mapping = array (
+                                SYNC_POOMMAIL_ALLDAYEVENT => array (STREAMER_VAR => "alldayevent"),
+                                SYNC_POOMMAIL_STARTTIME => array (STREAMER_VAR => "starttime", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                                SYNC_POOMMAIL_DTSTAMP => array (STREAMER_VAR => "dtstamp", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                                SYNC_POOMMAIL_ENDTIME => array (STREAMER_VAR => "endtime", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                                SYNC_POOMMAIL_INSTANCETYPE => array (STREAMER_VAR => "instancetype"),
+                                SYNC_POOMMAIL_LOCATION => array (STREAMER_VAR => "location"),
+                                SYNC_POOMMAIL_ORGANIZER => array (STREAMER_VAR => "organizer"),
+                                SYNC_POOMMAIL_RECURRENCEID => array (STREAMER_VAR => "recurrenceid", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                                SYNC_POOMMAIL_REMINDER => array (STREAMER_VAR => "reminder"),
+                                SYNC_POOMMAIL_RESPONSEREQUESTED => array (STREAMER_VAR => "responserequested"),
+                                SYNC_POOMMAIL_RECURRENCES => array (STREAMER_VAR => "recurrences", STREAMER_TYPE => "SyncMeetingRequestRecurrence", STREAMER_ARRAY => SYNC_POOMMAIL_RECURRENCE),
+                                SYNC_POOMMAIL_SENSITIVITY => array (STREAMER_VAR => "sensitivity"),
+                                SYNC_POOMMAIL_BUSYSTATUS => array (STREAMER_VAR => "busystatus"),
+                                SYNC_POOMMAIL_TIMEZONE => array (STREAMER_VAR => "timezone"),
+                                SYNC_POOMMAIL_GLOBALOBJID => array (STREAMER_VAR => "globalobjid"),
+                              );
+
+        parent::Streamer($mapping);
+    }
+
+};
+
+class SyncMail extends Streamer {
+    var $body;
+    var $bodysize;
+    var $bodytruncated;
+    var $datereceived;
+    var $displayto;
+    var $importance;
+    var $messageclass;
+    var $subject;
+    var $read;
+    var $to;
+    var $cc;
+    var $from;
+    var $reply_to;
+
+    var $attachments = array();
+
+    function SyncMail() {
+        global $protocolversion;
+
+        $mapping = array (
+                                SYNC_POOMMAIL_TO => array (STREAMER_VAR => "to"),
+                                SYNC_POOMMAIL_CC => array (STREAMER_VAR => "cc"),
+                                SYNC_POOMMAIL_FROM => array (STREAMER_VAR => "from"),
+                                SYNC_POOMMAIL_SUBJECT => array (STREAMER_VAR => "subject"),
+                                SYNC_POOMMAIL_THREADTOPIC => array (STREAMER_VAR => "threadtopic"),
+                                SYNC_POOMMAIL_DATERECEIVED => array (STREAMER_VAR => "datereceived", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES ),
+                                SYNC_POOMMAIL_DISPLAYTO =>  array (STREAMER_VAR => "displayto"),
+                                SYNC_POOMMAIL_IMPORTANCE => array (STREAMER_VAR => "importance"),
+                                SYNC_POOMMAIL_READ => array (STREAMER_VAR => "read"),
+                                SYNC_POOMMAIL_ATTACHMENTS => array (STREAMER_VAR => "attachments", STREAMER_TYPE => "SyncAttachment", STREAMER_ARRAY => SYNC_POOMMAIL_ATTACHMENT ),
+                                SYNC_POOMMAIL_MIMETRUNCATED => array ( STREAMER_VAR => "mimetruncated" ),//
+                                SYNC_POOMMAIL_MIMEDATA => array ( STREAMER_VAR => "mimedata", STREAMER_TYPE => STREAMER_TYPE_MAPI_STREAM),//
+                                SYNC_POOMMAIL_MIMESIZE => array ( STREAMER_VAR => "mimesize" ),//
+                                SYNC_POOMMAIL_BODYTRUNCATED => array (STREAMER_VAR => "bodytruncated"),
+                                SYNC_POOMMAIL_BODYSIZE => array (STREAMER_VAR => "bodysize"),
+                                SYNC_POOMMAIL_BODY => array (STREAMER_VAR => "body"),
+                                SYNC_POOMMAIL_MESSAGECLASS => array (STREAMER_VAR => "messageclass"),
+                                SYNC_POOMMAIL_MEETINGREQUEST => array (STREAMER_VAR => "meetingrequest", STREAMER_TYPE => "SyncMeetingRequest"),
+                                SYNC_POOMMAIL_REPLY_TO => array (STREAMER_VAR => "reply_to"),
+                              );
+
+        if(isset($protocolversion) && $protocolversion >= 2.5) {
+            $mapping += array(
+                                SYNC_POOMMAIL_INTERNETCPID => array (STREAMER_VAR => "internetcpid"),
+                              );
+        }
+
+        parent::Streamer($mapping);
+    }
+};
+
+class SyncContact extends Streamer {
+    var $anniversary;
+    var $assistantname;
+    var $assistnamephonenumber;
+    var $birthday;
+    var $body;
+    var $bodysize;
+    var $bodytruncated;
+    var $business2phonenumber;
+    var $businesscity;
+    var $businesscountry;
+    var $businesspostalcode;
+    var $businessstate;
+    var $businessstreet;
+    var $businessfaxnumber;
+    var $businessphonenumber;
+    var $carphonenumber;
+    var $categories;
+    var $children;
+    var $companyname;
+    var $department;
+    var $email1address;
+    var $email2address;
+    var $email3address;
+    var $fileas;
+    var $firstname;
+    var $home2phonenumber;
+    var $homecity;
+    var $homecountry;
+    var $homepostalcode;
+    var $homestate;
+    var $homestreet;
+    var $homefaxnumber;
+    var $homephonenumber;
+    var $jobtitle;
+    var $lastname;
+    var $middlename;
+    var $mobilephonenumber;
+    var $officelocation;
+    var $othercity;
+    var $othercountry;
+    var $otherpostalcode;
+    var $otherstate;
+    var $otherstreet;
+    var $pagernumber;
+    var $radiophonenumber;
+    var $spouse;
+    var $suffix;
+    var $title;
+    var $webpage;
+    var $yomicompanyname;
+    var $yomifirstname;
+    var $yomilastname;
+    var $rtf;
+    var $picture;
+    var $nickname;
+
+    function SyncContact() {
+        global $protocolversion;
+
+        $mapping = array (
+            SYNC_POOMCONTACTS_ANNIVERSARY => array (STREAMER_VAR => "anniversary", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES  ),
+            SYNC_POOMCONTACTS_ASSISTANTNAME => array (STREAMER_VAR => "assistantname"),
+            SYNC_POOMCONTACTS_ASSISTNAMEPHONENUMBER => array (STREAMER_VAR => "assistnamephonenumber"),
+            SYNC_POOMCONTACTS_BIRTHDAY => array (STREAMER_VAR => "birthday", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES  ),
+            SYNC_POOMCONTACTS_BODY => array (STREAMER_VAR => "body"),
+            SYNC_POOMCONTACTS_BODYSIZE => array (STREAMER_VAR => "bodysize"),
+            SYNC_POOMCONTACTS_BODYTRUNCATED => array (STREAMER_VAR => "bodytruncated"),
+            SYNC_POOMCONTACTS_BUSINESS2PHONENUMBER => array (STREAMER_VAR => "business2phonenumber"),
+            SYNC_POOMCONTACTS_BUSINESSCITY => array (STREAMER_VAR => "businesscity"),
+            SYNC_POOMCONTACTS_BUSINESSCOUNTRY => array (STREAMER_VAR => "businesscountry"),
+            SYNC_POOMCONTACTS_BUSINESSPOSTALCODE => array (STREAMER_VAR => "businesspostalcode"),
+            SYNC_POOMCONTACTS_BUSINESSSTATE => array (STREAMER_VAR => "businessstate"),
+            SYNC_POOMCONTACTS_BUSINESSSTREET => array (STREAMER_VAR => "businessstreet"),
+            SYNC_POOMCONTACTS_BUSINESSFAXNUMBER => array (STREAMER_VAR => "businessfaxnumber"),
+            SYNC_POOMCONTACTS_BUSINESSPHONENUMBER => array (STREAMER_VAR => "businessphonenumber"),
+            SYNC_POOMCONTACTS_CARPHONENUMBER => array (STREAMER_VAR => "carphonenumber"),
+            SYNC_POOMCONTACTS_CHILDREN => array (STREAMER_VAR => "children", STREAMER_ARRAY => SYNC_POOMCONTACTS_CHILD ),
+            SYNC_POOMCONTACTS_COMPANYNAME => array (STREAMER_VAR => "companyname"),
+            SYNC_POOMCONTACTS_DEPARTMENT => array (STREAMER_VAR => "department"),
+            SYNC_POOMCONTACTS_EMAIL1ADDRESS => array (STREAMER_VAR => "email1address"),
+            SYNC_POOMCONTACTS_EMAIL2ADDRESS => array (STREAMER_VAR => "email2address"),
+            SYNC_POOMCONTACTS_EMAIL3ADDRESS => array (STREAMER_VAR => "email3address"),
+            SYNC_POOMCONTACTS_FILEAS => array (STREAMER_VAR => "fileas"),
+            SYNC_POOMCONTACTS_FIRSTNAME => array (STREAMER_VAR => "firstname"),
+            SYNC_POOMCONTACTS_HOME2PHONENUMBER => array (STREAMER_VAR => "home2phonenumber"),
+            SYNC_POOMCONTACTS_HOMECITY => array (STREAMER_VAR => "homecity"),
+            SYNC_POOMCONTACTS_HOMECOUNTRY => array (STREAMER_VAR => "homecountry"),
+            SYNC_POOMCONTACTS_HOMEPOSTALCODE => array (STREAMER_VAR => "homepostalcode"),
+            SYNC_POOMCONTACTS_HOMESTATE => array (STREAMER_VAR => "homestate"),
+            SYNC_POOMCONTACTS_HOMESTREET => array (STREAMER_VAR => "homestreet"),
+            SYNC_POOMCONTACTS_HOMEFAXNUMBER => array (STREAMER_VAR => "homefaxnumber"),
+            SYNC_POOMCONTACTS_HOMEPHONENUMBER => array (STREAMER_VAR => "homephonenumber"),
+            SYNC_POOMCONTACTS_JOBTITLE => array (STREAMER_VAR => "jobtitle"),
+            SYNC_POOMCONTACTS_LASTNAME => array (STREAMER_VAR => "lastname"),
+            SYNC_POOMCONTACTS_MIDDLENAME => array (STREAMER_VAR => "middlename"),
+            SYNC_POOMCONTACTS_MOBILEPHONENUMBER => array (STREAMER_VAR => "mobilephonenumber"),
+            SYNC_POOMCONTACTS_OFFICELOCATION => array (STREAMER_VAR => "officelocation"),
+            SYNC_POOMCONTACTS_OTHERCITY => array (STREAMER_VAR => "othercity"),
+            SYNC_POOMCONTACTS_OTHERCOUNTRY => array (STREAMER_VAR => "othercountry"),
+            SYNC_POOMCONTACTS_OTHERPOSTALCODE => array (STREAMER_VAR => "otherpostalcode"),
+            SYNC_POOMCONTACTS_OTHERSTATE => array (STREAMER_VAR => "otherstate"),
+            SYNC_POOMCONTACTS_OTHERSTREET => array (STREAMER_VAR => "otherstreet"),
+            SYNC_POOMCONTACTS_PAGERNUMBER => array (STREAMER_VAR => "pagernumber"),
+            SYNC_POOMCONTACTS_RADIOPHONENUMBER => array (STREAMER_VAR => "radiophonenumber"),
+            SYNC_POOMCONTACTS_SPOUSE => array (STREAMER_VAR => "spouse"),
+            SYNC_POOMCONTACTS_SUFFIX => array (STREAMER_VAR => "suffix"),
+            SYNC_POOMCONTACTS_TITLE => array (STREAMER_VAR => "title"),
+            SYNC_POOMCONTACTS_WEBPAGE => array (STREAMER_VAR => "webpage"),
+            SYNC_POOMCONTACTS_YOMICOMPANYNAME => array (STREAMER_VAR => "yomicompanyname"),
+            SYNC_POOMCONTACTS_YOMIFIRSTNAME => array (STREAMER_VAR => "yomifirstname"),
+            SYNC_POOMCONTACTS_YOMILASTNAME => array (STREAMER_VAR => "yomilastname"),
+            SYNC_POOMCONTACTS_RTF => array (STREAMER_VAR => "rtf"),
+            SYNC_POOMCONTACTS_PICTURE => array (STREAMER_VAR => "picture"),
+            SYNC_POOMCONTACTS_CATEGORIES => array (STREAMER_VAR => "categories", STREAMER_ARRAY => SYNC_POOMCONTACTS_CATEGORY ),
+    );
+
+        if(isset($protocolversion) && $protocolversion >= 2.5) {
+            $mapping += array(
+                SYNC_POOMCONTACTS2_CUSTOMERID => array (STREAMER_VAR => "customerid"),
+                SYNC_POOMCONTACTS2_GOVERNMENTID => array (STREAMER_VAR => "governmentid"),
+                SYNC_POOMCONTACTS2_IMADDRESS => array (STREAMER_VAR => "imaddress"),
+                SYNC_POOMCONTACTS2_IMADDRESS2 => array (STREAMER_VAR => "imaddress2"),
+                SYNC_POOMCONTACTS2_IMADDRESS3 => array (STREAMER_VAR => "imaddress3"),
+                SYNC_POOMCONTACTS2_MANAGERNAME => array (STREAMER_VAR => "managername"),
+                SYNC_POOMCONTACTS2_COMPANYMAINPHONE => array (STREAMER_VAR => "companymainphone"),
+                SYNC_POOMCONTACTS2_ACCOUNTNAME => array (STREAMER_VAR => "accountname"),
+                SYNC_POOMCONTACTS2_NICKNAME => array (STREAMER_VAR => "nickname"),
+                SYNC_POOMCONTACTS2_MMS => array (STREAMER_VAR => "mms"),
+            );
+        }
+
+        parent::Streamer($mapping);
+    }
+}
+
+class SyncAttendee extends Streamer {
+    function SyncAttendee() {
+        $mapping = array(
+                      SYNC_POOMCAL_EMAIL => array (STREAMER_VAR => "email"),
+                      SYNC_POOMCAL_NAME => array (STREAMER_VAR => "name" )
+                  );
+
+        parent::Streamer($mapping);
+    }
+}
+
+class SyncAppointment extends Streamer {
+    function SyncAppointment() {
+        $mapping = array(
+                      SYNC_POOMCAL_TIMEZONE => array (STREAMER_VAR => "timezone"),
+                      SYNC_POOMCAL_DTSTAMP => array (STREAMER_VAR => "dtstamp", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMCAL_STARTTIME => array (STREAMER_VAR => "starttime", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMCAL_SUBJECT => array (STREAMER_VAR => "subject"),
+                      SYNC_POOMCAL_UID => array (STREAMER_VAR => "uid"),
+                      SYNC_POOMCAL_ORGANIZERNAME => array (STREAMER_VAR => "organizername"),
+                      SYNC_POOMCAL_ORGANIZEREMAIL => array (STREAMER_VAR => "organizeremail"),
+                      SYNC_POOMCAL_LOCATION => array (STREAMER_VAR => "location"),
+                      SYNC_POOMCAL_ENDTIME => array (STREAMER_VAR => "endtime", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMCAL_RECURRENCE => array (STREAMER_VAR => "recurrence", STREAMER_TYPE => "SyncRecurrence"),
+                      SYNC_POOMCAL_SENSITIVITY => array (STREAMER_VAR => "sensitivity"),
+                      SYNC_POOMCAL_BUSYSTATUS => array (STREAMER_VAR => "busystatus"),
+                      SYNC_POOMCAL_ALLDAYEVENT => array (STREAMER_VAR => "alldayevent"),
+                      SYNC_POOMCAL_REMINDER => array (STREAMER_VAR => "reminder"),
+                      SYNC_POOMCAL_RTF => array (STREAMER_VAR => "rtf"),
+                      SYNC_POOMCAL_MEETINGSTATUS => array (STREAMER_VAR => "meetingstatus"),
+                      SYNC_POOMCAL_ATTENDEES => array (STREAMER_VAR => "attendees", STREAMER_TYPE => "SyncAttendee", STREAMER_ARRAY => SYNC_POOMCAL_ATTENDEE),
+                      SYNC_POOMCAL_BODY => array (STREAMER_VAR => "body"),
+                      SYNC_POOMCAL_BODYTRUNCATED => array (STREAMER_VAR => "bodytruncated"),
+                      SYNC_POOMCAL_EXCEPTIONS => array (STREAMER_VAR => "exceptions", STREAMER_TYPE => "SyncAppointment", STREAMER_ARRAY => SYNC_POOMCAL_EXCEPTION),
+                      SYNC_POOMCAL_DELETED => array (STREAMER_VAR => "deleted"),
+                      SYNC_POOMCAL_EXCEPTIONSTARTTIME => array (STREAMER_VAR => "exceptionstarttime", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMCAL_CATEGORIES => array (STREAMER_VAR => "categories", STREAMER_ARRAY => SYNC_POOMCAL_CATEGORY),
+
+        );
+
+        parent::Streamer($mapping);
+    }
+}
+
+class SyncRecurrence extends Streamer {
+    var $type;
+    var $until;
+    var $occurrences;
+    var $interval;
+    var $dayofweek;
+    var $dayofmonth;
+    var $weekofmonth;
+    var $monthofyear;
+
+    function SyncRecurrence() {
+        $mapping = array (
+                      SYNC_POOMCAL_TYPE => array (STREAMER_VAR => "type"),
+                      SYNC_POOMCAL_UNTIL => array (STREAMER_VAR => "until", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMCAL_OCCURRENCES => array (STREAMER_VAR => "occurrences"),
+                      SYNC_POOMCAL_INTERVAL => array (STREAMER_VAR => "interval"),
+                      SYNC_POOMCAL_DAYOFWEEK => array (STREAMER_VAR => "dayofweek"),
+                      SYNC_POOMCAL_DAYOFMONTH => array (STREAMER_VAR => "dayofmonth"),
+                      SYNC_POOMCAL_WEEKOFMONTH => array (STREAMER_VAR => "weekofmonth"),
+                      SYNC_POOMCAL_MONTHOFYEAR => array (STREAMER_VAR => "monthofyear")
+        );
+
+        parent::Streamer($mapping);
+    }
+}
+
+// Exactly the same as SyncRecurrence, but then with SYNC_POOMMAIL_*
+class SyncMeetingRequestRecurrence extends Streamer {
+    var $type;
+    var $until;
+    var $occurrences;
+    var $interval;
+    var $dayofweek;
+    var $dayofmonth;
+    var $weekofmonth;
+    var $monthofyear;
+
+    function SyncMeetingRequestRecurrence() {
+        $mapping = array (
+                      SYNC_POOMMAIL_TYPE => array (STREAMER_VAR => "type"),
+                      SYNC_POOMMAIL_UNTIL => array (STREAMER_VAR => "until", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMMAIL_OCCURRENCES => array (STREAMER_VAR => "occurrences"),
+                      SYNC_POOMMAIL_INTERVAL => array (STREAMER_VAR => "interval"),
+                      SYNC_POOMMAIL_DAYOFWEEK => array (STREAMER_VAR => "dayofweek"),
+                      SYNC_POOMMAIL_DAYOFMONTH => array (STREAMER_VAR => "dayofmonth"),
+                      SYNC_POOMMAIL_WEEKOFMONTH => array (STREAMER_VAR => "weekofmonth"),
+                      SYNC_POOMMAIL_MONTHOFYEAR => array (STREAMER_VAR => "monthofyear")
+        );
+
+        parent::Streamer($mapping);
+    }
+}
+
+// Exactly the same as SyncRecurrence, but then with SYNC_POOMTASKS_*
+class SyncTaskRecurrence extends Streamer {
+    function SyncTaskRecurrence() {
+        $mapping = array (
+                      SYNC_POOMTASKS_TYPE => array (STREAMER_VAR => "type"),
+                      SYNC_POOMTASKS_UNTIL => array (STREAMER_VAR => "until", STREAMER_TYPE => STREAMER_TYPE_DATE),
+                      SYNC_POOMTASKS_OCCURRENCES => array (STREAMER_VAR => "occurrences"),
+                      SYNC_POOMTASKS_INTERVAL => array (STREAMER_VAR => "interval"),
+                      SYNC_POOMTASKS_DAYOFWEEK => array (STREAMER_VAR => "dayofweek"),
+                      SYNC_POOMTASKS_DAYOFMONTH => array (STREAMER_VAR => "dayofmonth"),
+                      SYNC_POOMTASKS_WEEKOFMONTH => array (STREAMER_VAR => "weekofmonth"),
+                      SYNC_POOMTASKS_MONTHOFYEAR => array (STREAMER_VAR => "monthofyear"),
+        );
+
+        parent::Streamer($mapping);
+    }
+}
+
+class SyncTask extends Streamer {
+    var $body;
+    var $categories = array();
+    var $complete;
+    var $datecompleted;
+    var $duedate;
+    var $utcduedate;
+    var $importance;
+    var $recurrence;
+    var $regenerate;
+    var $deadoccur;
+    var $reminderset;
+    var $remindertime;
+    var $sensitivity;
+    var $startdate;
+    var $utcstartdate;
+    var $subject;
+    var $rtf;
+
+    function SyncTask() {
+        $mapping = array (
+                      SYNC_POOMTASKS_BODY => array (STREAMER_VAR => "body"),
+                      SYNC_POOMTASKS_COMPLETE => array (STREAMER_VAR => "complete"),
+                      SYNC_POOMTASKS_DATECOMPLETED => array (STREAMER_VAR => "datecompleted", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_DUEDATE => array (STREAMER_VAR => "duedate", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_UTCDUEDATE => array (STREAMER_VAR => "utcduedate", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_IMPORTANCE => array (STREAMER_VAR => "importance"),
+                      SYNC_POOMTASKS_RECURRENCE => array (STREAMER_VAR => "recurrence", STREAMER_TYPE => "SyncTaskRecurrence"),
+                      SYNC_POOMTASKS_REGENERATE => array (STREAMER_VAR => "regenerate"),
+                      SYNC_POOMTASKS_DEADOCCUR => array (STREAMER_VAR => "deadoccur"),
+                      SYNC_POOMTASKS_REMINDERSET => array (STREAMER_VAR => "reminderset"),
+                      SYNC_POOMTASKS_REMINDERTIME => array (STREAMER_VAR => "remindertime", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_SENSITIVITY => array (STREAMER_VAR => "sensitivity"),
+                      SYNC_POOMTASKS_STARTDATE => array (STREAMER_VAR => "startdate", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_UTCSTARTDATE => array (STREAMER_VAR => "utcstartdate", STREAMER_TYPE => STREAMER_TYPE_DATE_DASHES),
+                      SYNC_POOMTASKS_SUBJECT => array (STREAMER_VAR => "subject"),
+                      SYNC_POOMTASKS_RTF => array (STREAMER_VAR => "rtf"),
+                      SYNC_POOMTASKS_CATEGORIES => array (STREAMER_VAR => "categories", STREAMER_ARRAY => SYNC_POOMTASKS_CATEGORY),
+        );
+
+        parent::Streamer($mapping);
+    }
+}
+
+?>
Index: /contrib/z-push/zpushdtd.php
===================================================================
--- /contrib/z-push/zpushdtd.php	(revision 4898)
+++ /contrib/z-push/zpushdtd.php	(revision 4898)
@@ -0,0 +1,407 @@
+<?php
+/***********************************************
+* File      :   zpushdtd.php
+* Project   :   Z-Push
+* Descr     :   dtd definition file
+*
+* Created   :   01.10.2007
+*
+*  Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+$zpushdtd = array(
+                "codes" => array (
+                    0 => array (
+                        0x05 => "Synchronize",
+                        0x06 => "Replies",
+                        0x07 => "Add",
+                        0x08 => "Modify",
+                        0x09 => "Remove",
+                        0x0a => "Fetch",
+                        0x0b => "SyncKey",
+                        0x0c => "ClientEntryId",
+                        0x0d => "ServerEntryId",
+                        0x0e => "Status",
+                        0x0f => "Folder",
+                        0x10 => "FolderType",
+                        0x11 => "Version",
+                        0x12 => "FolderId",
+                        0x13 => "GetChanges",
+                        0x14 => "MoreAvailable",
+                        0x15 => "MaxItems",
+                        0x16 => "Perform",
+                        0x17 => "Options",
+                        0x18 => "FilterType",
+                        0x19 => "Truncation",
+                        0x1a => "RtfTruncation",
+                        0x1b => "Conflict",
+                        0x1c => "Folders",
+                        0x1d => "Data",
+                        0x1e => "DeletesAsMoves",
+                        0x1f => "NotifyGUID",
+                        0x20 => "Supported",
+                        0x21 => "SoftDelete",
+                        0x22 => "MIMESupport",
+                        0x23 => "MIMETruncation",
+                    ), 1 => array (
+                        0x05 => "Anniversary",
+                        0x06 => "AssistantName",
+                        0x07 => "AssistnamePhoneNumber",
+                        0x08 => "Birthday",
+                        0x09 => "Body",
+                        0x0a => "BodySize",
+                        0x0b => "BodyTruncated",
+                        0x0c => "Business2PhoneNumber",
+                        0x0d => "BusinessCity",
+                        0x0e => "BusinessCountry",
+                        0x0f => "BusinessPostalCode",
+                        0x10 => "BusinessState",
+                        0x11 => "BusinessStreet",
+                        0x12 => "BusinessFaxNumber",
+                        0x13 => "BusinessPhoneNumber",
+                        0x14 => "CarPhoneNumber",
+                        0x15 => "Categories",
+                        0x16 => "Category",
+                        0x17 => "Children",
+                        0x18 => "Child",
+                        0x19 => "CompanyName",
+                        0x1a => "Department",
+                        0x1b => "Email1Address",
+                        0x1c => "Email2Address",
+                        0x1d => "Email3Address",
+                        0x1e => "FileAs",
+                        0x1f => "FirstName",
+                        0x20 => "Home2PhoneNumber",
+                        0x21 => "HomeCity",
+                        0x22 => "HomeCountry",
+                        0x23 => "HomePostalCode",
+                        0x24 => "HomeState",
+                        0x25 => "HomeStreet",
+                        0x26 => "HomeFaxNumber",
+                        0x27 => "HomePhoneNumber",
+                        0x28 => "JobTitle",
+                        0x29 => "LastName",
+                        0x2a => "MiddleName",
+                        0x2b => "MobilePhoneNumber",
+                        0x2c => "OfficeLocation",
+                        0x2d => "OtherCity",
+                        0x2e => "OtherCountry",
+                        0x2f => "OtherPostalCode",
+                        0x30 => "OtherState",
+                        0x31 => "OtherStreet",
+                        0x32 => "PagerNumber",
+                        0x33 => "RadioPhoneNumber",
+                        0x34 => "Spouse",
+                        0x35 => "Suffix",
+                        0x36 => "Title",
+                        0x37 => "WebPage",
+                        0x38 => "YomiCompanyName",
+                        0x39 => "YomiFirstName",
+                        0x3a => "YomiLastName",
+                        0x3b => "Rtf",
+                        0x3c => "Picture",
+                    ), 2 => array (
+                        0x05 => "Attachment",
+                        0x06 => "Attachments",
+                        0x07 => "AttName",
+                        0x08 => "AttSize",
+                        0x09 => "AttOid",
+                        0x0a => "AttMethod",
+                        0x0b => "AttRemoved",
+                        0x0c => "Body",
+                        0x0d => "BodySize",
+                        0x0e => "BodyTruncated",
+                        0x0f => "DateReceived",
+                        0x10 => "DisplayName",
+                        0x11 => "DisplayTo",
+                        0x12 => "Importance",
+                        0x13 => "MessageClass",
+                        0x14 => "Subject",
+                        0x15 => "Read",
+                        0x16 => "To",
+                        0x17 => "Cc",
+                        0x18 => "From",
+                        0x19 => "Reply-To",
+                        0x1a => "AllDayEvent",
+                        0x1b => "Categories",
+                        0x1c => "Category",
+                        0x1d => "DtStamp",
+                        0x1e => "EndTime",
+                        0x1f => "InstanceType",
+                        0x20 => "BusyStatus",
+                        0x21 => "Location",
+                        0x22 => "MeetingRequest",
+                        0x23 => "Organizer",
+                        0x24 => "RecurrenceId",
+                        0x25 => "Reminder",
+                        0x26 => "ResponseRequested",
+                        0x27 => "Recurrences",
+                        0x28 => "Recurrence",
+                        0x29 => "Type",
+                        0x2a => "Until",
+                        0x2b => "Occurrences",
+                        0x2c => "Interval",
+                        0x2d => "DayOfWeek",
+                        0x2e => "DayOfMonth",
+                        0x2f => "WeekOfMonth",
+                        0x30 => "MonthOfYear",
+                        0x31 => "StartTime",
+                        0x32 => "Sensitivity",
+                        0x33 => "TimeZone",
+                        0x34 => "GlobalObjId",
+                        0x35 => "ThreadTopic",
+                        0x36 => "MIMEData",
+                        0x37 => "MIMETruncated",
+                        0x38 => "MIMESize",
+                        0x39 => "InternetCPID",
+                    ), 3 => array (
+                        0x05 => "Notify",
+                        0x06 => "Notification",
+                        0x07 => "Version",
+                        0x08 => "Lifetime",
+                        0x09 => "DeviceInfo",
+                        0x0a => "Enable",
+                        0x0b => "Folder",
+                        0x0c => "ServerEntryId",
+                        0x0d => "DeviceAddress",
+                        0x0e => "ValidCarrierProfiles",
+                        0x0f => "CarrierProfile",
+                        0x10 => "Status",
+                        0x11 => "Replies",
+//                        0x05 => "Version='1.1'",
+                        0x12 => "Devices",
+                        0x13 => "Device",
+                        0x14 => "Id",
+                        0x15 => "Expiry",
+                        0x16 => "NotifyGUID",
+                    ), 4 => array (
+                        0x05 => "Timezone",
+                        0x06 => "AllDayEvent",
+                        0x07 => "Attendees",
+                        0x08 => "Attendee",
+                        0x09 => "Email",
+                        0x0a => "Name",
+                        0x0b => "Body",
+                        0x0c => "BodyTruncated",
+                        0x0d => "BusyStatus",
+                        0x0e => "Categories",
+                        0x0f => "Category",
+                        0x10 => "Rtf",
+                        0x11 => "DtStamp",
+                        0x12 => "EndTime",
+                        0x13 => "Exception",
+                        0x14 => "Exceptions",
+                        0x15 => "Deleted",
+                        0x16 => "ExceptionStartTime",
+                        0x17 => "Location",
+                        0x18 => "MeetingStatus",
+                        0x19 => "OrganizerEmail",
+                        0x1a => "OrganizerName",
+                        0x1b => "Recurrence",
+                        0x1c => "Type",
+                        0x1d => "Until",
+                        0x1e => "Occurrences",
+                        0x1f => "Interval",
+                        0x20 => "DayOfWeek",
+                        0x21 => "DayOfMonth",
+                        0x22 => "WeekOfMonth",
+                        0x23 => "MonthOfYear",
+                        0x24 => "Reminder",
+                        0x25 => "Sensitivity",
+                        0x26 => "Subject",
+                        0x27 => "StartTime",
+                        0x28 => "UID",
+                    ), 5 => array (
+                        0x05 => "Moves",
+                        0x06 => "Move",
+                        0x07 => "SrcMsgId",
+                        0x08 => "SrcFldId",
+                        0x09 => "DstFldId",
+                        0x0a => "Response",
+                        0x0b => "Status",
+                        0x0c => "DstMsgId",
+                    ), 6 => array (
+                        0x05 => "GetItemEstimate",
+                        0x06 => "Version",
+                        0x07 => "Folders",
+                        0x08 => "Folder",
+                        0x09 => "FolderType",
+                        0x0a => "FolderId",
+                        0x0b => "DateTime",
+                        0x0c => "Estimate",
+                        0x0d => "Response",
+                        0x0e => "Status",
+                    ), 7 => array (
+                        0x05 => "Folders",
+                        0x06 => "Folder",
+                        0x07 => "DisplayName",
+                        0x08 => "ServerEntryId",
+                        0x09 => "ParentId",
+                        0x0a => "Type",
+                        0x0b => "Response",
+                        0x0c => "Status",
+                        0x0d => "ContentClass",
+                        0x0e => "Changes",
+                        0x0f => "Add",
+                        0x10 => "Remove",
+                        0x11 => "Update",
+                        0x12 => "SyncKey",
+                        0x13 => "FolderCreate",
+                        0x14 => "FolderDelete",
+                        0x15 => "FolderUpdate",
+                        0x16 => "FolderSync",
+                        0x17 => "Count",
+                        0x18 => "Version",
+                    ), 8 => array (
+                        0x05 => "CalendarId",
+                        0x06 => "FolderId",
+                        0x07 => "MeetingResponse",
+                        0x08 => "RequestId",
+                        0x09 => "Request",
+                        0x0a => "Result",
+                        0x0b => "Status",
+                        0x0c => "UserResponse",
+                        0x0d => "Version",
+                    ), 9 => array (
+                        0x05 => "Body",
+                        0x06 => "BodySize",
+                        0x07 => "BodyTruncated",
+                        0x08 => "Categories",
+                        0x09 => "Category",
+                        0x0a => "Complete",
+                        0x0b => "DateCompleted",
+                        0x0c => "DueDate",
+                        0x0d => "UtcDueDate",
+                        0x0e => "Importance",
+                        0x0f => "Recurrence",
+                        0x10 => "Type",
+                        0x11 => "Start",
+                        0x12 => "Until",
+                        0x13 => "Occurrences",
+                        0x14 => "Interval",
+                        0x16 => "DayOfWeek",
+                        0x15 => "DayOfMonth",
+                        0x17 => "WeekOfMonth",
+                        0x18 => "MonthOfYear",
+                        0x19 => "Regenerate",
+                        0x1a => "DeadOccur",
+                        0x1b => "ReminderSet",
+                        0x1c => "ReminderTime",
+                        0x1d => "Sensitivity",
+                        0x1e => "StartDate",
+                        0x1f => "UtcStartDate",
+                        0x20 => "Subject",
+                        0x21 => "Rtf",
+                    ), 0xa => array (
+                        0x05 => "ResolveRecipients",
+                        0x06 => "Response",
+                        0x07 => "Status",
+                        0x08 => "Type",
+                        0x09 => "Recipient",
+                        0x0a => "DisplayName",
+                        0x0b => "EmailAddress",
+                        0x0c => "Certificates",
+                        0x0d => "Certificate",
+                        0x0e => "MiniCertificate",
+                        0x0f => "Options",
+                        0x10 => "To",
+                        0x11 => "CertificateRetrieval",
+                        0x12 => "RecipientCount",
+                        0x13 => "MaxCertificates",
+                        0x14 => "MaxAmbiguousRecipients",
+                        0x15 => "CertificateCount",
+                    ), 0xb => array (
+                        0x05 => "ValidateCert",
+                        0x06 => "Certificates",
+                        0x07 => "Certificate",
+                        0x08 => "CertificateChain",
+                        0x09 => "CheckCRL",
+                        0x0a => "Status",
+                    ), 0xc => array (
+                        0x05 => "CustomerId",
+                        0x06 => "GovernmentId",
+                        0x07 => "IMAddress",
+                        0x08 => "IMAddress2",
+                        0x09 => "IMAddress3",
+                        0x0a => "ManagerName",
+                        0x0b => "CompanyMainPhone",
+                        0x0c => "AccountName",
+                        0x0d => "NickName",
+                        0x0e => "MMS",
+                    ), 0xd => array (
+                        0x05 => "Ping",
+                        0x07 => "Status",
+                        0x08 => "LifeTime",
+                        0x09 => "Folders",
+                        0x0a => "Folder",
+                        0x0b => "ServerEntryId",
+                        0x0c => "FolderType",
+                    ), 0xe => array (
+                        0x05 => "Provision",
+                        0x06 => "Policies",
+                        0x07 => "Policy",
+                        0x08 => "PolicyType",
+                        0x09 => "PolicyKey",
+                        0x0A => "Data",
+                        0x0B => "Status",
+                        0x0C => "RemoteWipe",
+                        0x0D => "EASProvisionDoc",
+                        ),
+                    0xf => array(
+                        0x05 => "Search",
+                        0x07 => "Store",
+                        0x08 => "Name",
+                        0x09 => "Query",
+                        0x0A => "Options",
+                        0x0B => "Range",
+                        0x0C => "Status",
+                        0x0D => "Response",
+                        0x0E => "Result",
+                        0x0F => "Properties",
+                        0x10 => "Total",
+                        0x11 => "EqualTo",
+                        0x12 => "Value",
+                        0x13 => "And",
+                        0x14 => "Or",
+                        0x15 => "FreeText",
+                        0x17 => "DeepTraversal",
+                        0x18 => "LongId",
+                        0x19 => "RebuildResults",
+                        0x1A => "LessThan",
+                        0x1B => "GreaterThan",
+                        0x1C => "Schema",
+                        0x1D => "Supported",
+                    ), 0x10 => array(
+                        0x05 => "DisplayName",
+                        0x06 => "Phone",
+                        0x07 => "Office",
+                        0x08 => "Title",
+                        0x09 => "Company",
+                        0x0A => "Alias",
+                        0x0B => "FirstName",
+                        0x0C => "LastName",
+                        0x0D => "HomePhone",
+                        0x0E => "MobilePhone",
+                        0x0F => "EmailAddress",
+                    )
+              ), "namespaces" => array(
+                  1 => "POOMCONTACTS",
+                  2 => "POOMMAIL",
+                  3 => "AirNotify",
+                  4 => "POOMCAL",
+                  5 => "Move",
+                  6 => "GetItemEstimate",
+                  7 => "FolderHierarchy",
+                  8 => "MeetingResponse",
+                  9 => "POOMTASKS",
+                  0xA => "ResolveRecipients",
+                  0xB => "ValidateCerts",
+                  0xC => "POOMCONTACTS2",
+                  0xD => "Ping",
+                  0xE => "Provision",//
+                  0xF => "Search",//
+                  0x10 => "GAL",
+              )
+          );
+?>
Index: /contrib/z-push/statemachine.php
===================================================================
--- /contrib/z-push/statemachine.php	(revision 4898)
+++ /contrib/z-push/statemachine.php	(revision 4898)
@@ -0,0 +1,116 @@
+<?php
+/***********************************************
+* File      :   statemachine.php
+* Project   :   Z-Push
+* Descr     :   This class handles state requests;
+*               Each differential mechanism can
+*               store its own state information,
+*               which is stored through the
+*               state machine. SyncKey's are
+*               of the  form {UUID}N, in which
+*               UUID is allocated during the
+*               first sync, and N is incremented
+*               for each request to 'getNewSyncKey'.
+*               A sync state is simple an opaque
+*               string value that can differ
+*               for each backend used - normally
+*               a list of items as the backend has
+*               sent them to the PIM. The backend
+*               can then use this backend
+*               information to compute the increments
+*               with current data.
+*
+*               Old sync states are not deleted
+*               until a sync state is requested.
+*               At that moment, the PIM is
+*               apparently requesting an update
+*               since sync key X, so any sync
+*               states before X are already on
+*               the PIM, and can therefore be
+*               removed. This algorithm is
+*               automatically enforced by the
+*               StateMachine class.
+*
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+
+class StateMachine {
+    // Gets the sync state for a specified sync key. Requesting a sync key also implies
+    // that previous sync states for this sync key series are no longer needed, and the
+    // state machine will tidy up these files.
+    function getSyncState($synckey) {
+        // No sync state for sync key '0'
+        if($synckey == "0" || $synckey == "s0")
+            return "";
+
+        // Check if synckey is allowed
+        if(!preg_match('/^s{0,1}\{([0-9A-Za-z-]+)\}([0-9]+)$/', $synckey, $matches)) {
+            return false;
+        }
+
+        // Remember synckey GUID and ID
+        $guid = $matches[1];
+        $n = $matches[2];
+
+        // Cleanup all older syncstates
+        $dir = opendir(BASE_PATH . STATE_DIR);
+        if(!$dir)
+            return false;
+
+        while($entry = readdir($dir)) {
+            if(preg_match('/^s{0,1}\{([0-9A-Za-z-]+)\}([0-9]+)$/', $entry, $matches)) {
+                if($matches[1] == $guid && $matches[2] < $n) {
+                    unlink(BASE_PATH . STATE_DIR . "/$entry");
+                }
+            }
+        }
+
+        // Read current sync state
+        $filename = BASE_PATH . STATE_DIR . "/$synckey";
+
+        if(file_exists($filename))
+            return file_get_contents(BASE_PATH . STATE_DIR . "/$synckey");
+        else return false;
+    }
+
+    // Gets the new sync key for a specified sync key. You must save the new sync state
+    // under this sync key when done sync'ing (by calling setSyncState);
+    function getNewSyncKey($synckey) {
+        if(!isset($synckey) || $synckey == "0") {
+            return "{" . $this->uuid() . "}" . "1";
+        } else {
+            if(preg_match('/^s{0,1}\{([a-fA-F0-9-]+)\}([0-9]+)$/', $synckey, $matches)) {
+                $n = $matches[2];
+                $n++;
+                return "{" . $matches[1] . "}" . $n;
+            } else return false;
+        }
+    }
+
+    // Writes the sync state to a new synckey
+    function setSyncState($synckey, $syncstate) {
+        // Check if synckey is allowed
+        if(!preg_match('/^s{0,1}\{[0-9A-Za-z-]+\}[0-9]+$/', $synckey)) {
+            return false;
+        }
+
+        return file_put_contents(BASE_PATH . STATE_DIR . "/$synckey", $syncstate);
+    }
+
+    function uuid()
+    {
+        return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+                    mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+                    mt_rand( 0, 0x0fff ) | 0x4000,
+                    mt_rand( 0, 0x3fff ) | 0x8000,
+                    mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) );
+    }
+};
+
+?>
Index: /contrib/z-push/index.php
===================================================================
--- /contrib/z-push/index.php	(revision 4898)
+++ /contrib/z-push/index.php	(revision 4898)
@@ -0,0 +1,224 @@
+<?php
+/***********************************************
+* File      :   index.php
+* Project   :   Z-Push
+* Descr     :   This is the entry point
+*               through which all requests
+*               are called.
+*
+* Created   :   01.10.2007
+*
+*  Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+ob_start(false, 1048576);
+
+include_once('zpushdefs.php');
+include_once("config.php");
+include_once("proto.php");
+include_once("request.php");
+include_once("debug.php");
+include_once("compat.php");
+include_once("version.php");
+
+// Attempt to set maximum execution time
+ini_set('max_execution_time', SCRIPT_TIMEOUT);
+set_time_limit(SCRIPT_TIMEOUT);
+
+debugLog("Start");
+debugLog("Z-Push version: $zpush_version");
+debugLog("Client IP: ". $_SERVER['REMOTE_ADDR']);
+
+$input = fopen("php://input", "r");
+$output = fopen("php://output", "w+");
+
+// The script must always be called with authorisation info
+if(!isset($_SERVER['PHP_AUTH_PW'])) {
+    header("WWW-Authenticate: Basic realm=\"ZPush\"");
+    header("HTTP/1.1 401 Unauthorized");
+    print("Access denied. Please send authorisation information");
+    debugLog("Access denied: no password sent.");
+	debugLog("end");
+	debugLog("--------");
+    return;
+}
+
+// split username & domain if received as one
+$pos = strrpos($_SERVER['PHP_AUTH_USER'], '\\');
+if($pos === false){
+    $auth_user = $_SERVER['PHP_AUTH_USER'];
+    $auth_domain = '';
+}else{
+    $auth_domain = substr($_SERVER['PHP_AUTH_USER'],0,$pos);
+    $auth_user = substr($_SERVER['PHP_AUTH_USER'],$pos+1);
+}
+$auth_pw = $_SERVER['PHP_AUTH_PW'];
+
+$cmd = $user = $devid = $devtype = "";
+
+// Parse the standard GET parameters
+if(isset($_GET["Cmd"]))
+    $cmd = $_GET["Cmd"];
+if(isset($_GET["User"]))
+    $user = $_GET["User"];
+if(isset($_GET["DeviceId"]))
+    $devid = $_GET["DeviceId"];
+if(isset($_GET["DeviceType"]))
+    $devtype = $_GET["DeviceType"];
+
+// The GET parameters are required
+if($_SERVER["REQUEST_METHOD"] == "POST") {
+    if(!isset($user) || !isset($devid) || !isset($devtype)) {
+        print("Your device requested the Z-Push URL without the required GET parameters");
+        return;
+    }
+}
+
+// Get the request headers so we can see the versions
+$requestheaders = apache_request_headers();
+if (isset($requestheaders["Ms-Asprotocolversion"])) $requestheaders["MS-ASProtocolVersion"] = $requestheaders["Ms-Asprotocolversion"];
+if(isset($requestheaders["MS-ASProtocolVersion"])) {
+    global $protocolversion;
+
+    $protocolversion = $requestheaders["MS-ASProtocolVersion"];
+    debugLog("Client supports version " . $protocolversion);
+} else {
+    global $protocolversion;
+
+    $protocolversion = "1.0";
+}
+
+if (isset($requestheaders["X-Ms-Policykey"])) $requestheaders["X-MS-PolicyKey"] = $requestheaders["X-Ms-Policykey"];
+if (isset($requestheaders["X-MS-PolicyKey"])) {
+    global $policykey;
+    $policykey = $requestheaders["X-MS-PolicyKey"];
+
+} else {
+    global $policykey;
+    $policykey = 0;
+}
+
+//get user agent
+if (isset($requestheaders["User-Agent"])) {
+    global $useragent;
+    $useragent = $requestheaders["User-Agent"];
+} else {
+    global $useragent;
+    $useragent = "unknown";
+}
+
+// Load our backend driver
+$backend_dir = opendir(BASE_PATH . "/backend");
+while($entry = readdir($backend_dir)) {
+    if(substr($entry,0,1) == "." || substr($entry,-3) != "php")
+        continue;
+
+    if (!function_exists("mapi_logon") && ($entry == "ics.php"))
+        continue;
+
+    include_once(BASE_PATH . "/backend/" . $entry);
+}
+
+// Initialize our backend
+$backend = new $BACKEND_PROVIDER();
+
+if($backend->Logon($auth_user, $auth_domain, $auth_pw) == false) {
+    header("HTTP/1.1 401 Unauthorized");
+    header("WWW-Authenticate: Basic realm=\"ZPush\"");
+    print("Access denied. Username or password incorrect.");
+    debugLog("Access denied: backend logon failed.");
+    debugLog("end");
+    debugLog("--------");
+    return;
+}
+
+// $user is usually the same as the PHP_AUTH_USER. This allows you to sync the 'john' account if you
+// have sufficient privileges as user 'joe'.
+if($backend->Setup($user, $devid, $protocolversion) == false) {
+    header("HTTP/1.1 401 Unauthorized");
+    header("WWW-Authenticate: Basic realm=\"ZPush\"");
+    print("Access denied or user '$user' unknown.");
+    debugLog("Access denied: backend setup failed.");
+    debugLog("end");
+    debugLog("--------");
+    return;
+}
+
+// check policy header 
+if (PROVISIONING === true && $_SERVER["REQUEST_METHOD"] != 'OPTIONS' && $cmd != 'Ping' && $cmd != 'Provision' && 
+    $backend->CheckPolicy($policykey, $devid) != SYNC_PROVISION_STATUS_SUCCESS &&
+    (LOOSE_PROVISIONING === false ||
+    (LOOSE_PROVISIONING === true && isset($requestheaders["X-MS-PolicyKey"])))) {    	
+    	
+    header("HTTP/1.1 449 Retry after sending a PROVISION command");
+    header("MS-Server-ActiveSync: 6.5.7638.1");
+    header("MS-ASProtocolVersions: 1.0,2.0,2.1,2.5");
+    header("MS-ASProtocolCommands: Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Provision,ResolveRecipients,ValidateCert,Search,Ping");
+    header("Cache-Control: private");
+    debugLog("POST cmd $cmd denied: Retry after sending a PROVISION command");
+    debugLog("end");
+    debugLog("--------");
+    return;
+}
+
+// Do the actual request
+switch($_SERVER["REQUEST_METHOD"]) {
+    case 'OPTIONS':
+        header("MS-Server-ActiveSync: 6.5.7638.1");
+        header("MS-ASProtocolVersions: 1.0,2.0,2.1,2.5");
+        header("MS-ASProtocolCommands: Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,ResolveRecipients,ValidateCert,Provision,Search,Ping");
+        debugLog("Options request");
+        break;
+    case 'POST':
+        header("MS-Server-ActiveSync: 6.5.7638.1");
+        debugLog("POST cmd: $cmd");
+        // Do the actual request
+        if(!HandleRequest($backend, $cmd, $devid, $protocolversion)) {
+            // Request failed. Try to output some kind of error information. We can only do this if
+            // output had not started yet. If it has started already, we can't show the user the error, and
+            // the device will give its own (useless) error message.
+            if(!headers_sent()) {
+                header("Content-type: text/html");
+                print("<BODY>\n");
+                print("<h3>Error</h3><p>\n");
+                print("There was a problem processing the <i>$cmd</i> command from your PDA.\n");
+                print("<p>Here is the debug output:<p><pre>\n");
+                print(getDebugInfo());
+                print("</pre>\n");
+                print("</BODY>\n");
+            }
+        }
+        break;
+    case 'GET':
+        header("Content-type: text/html");
+        print("<BODY>\n");
+        print("<h3>GET not supported</h3><p>\n");
+        print("This is the z-push location and can only be accessed by Microsoft ActiveSync-capable devices.");
+        print("</BODY>\n");
+        break;
+}
+
+
+$len = ob_get_length();
+$data = ob_get_contents();
+
+ob_end_clean();
+
+// Unfortunately, even though zpush can stream the data to the client
+// with a chunked encoding, using chunked encoding also breaks the progress bar
+// on the PDA. So we de-chunk here and just output a content-length header and
+// send it as a 'normal' packet. If the output packet exceeds 1MB (see ob_start)
+// then it will be sent as a chunked packet anyway because PHP will have to flush
+// the buffer.
+
+header("Content-Length: $len");
+print $data;
+
+// destruct backend after all data is on the stream
+$backend->Logoff();
+
+debugLog("end");
+debugLog("--------");
+?>
Index: /contrib/z-push/zpushdefs.php
===================================================================
--- /contrib/z-push/zpushdefs.php	(revision 4898)
+++ /contrib/z-push/zpushdefs.php	(revision 4898)
@@ -0,0 +1,452 @@
+<?php
+/***********************************************
+* File      :   zpushdefs.php
+* Project   :   Z-Push
+* Descr     :   Constants' definition file
+*
+* Created   :   01.10.2007
+*
+*  Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+define("SYNC_SYNCHRONIZE","Synchronize");
+define("SYNC_REPLIES","Replies");
+define("SYNC_ADD","Add");
+define("SYNC_MODIFY","Modify");
+define("SYNC_REMOVE","Remove");
+define("SYNC_FETCH","Fetch");
+define("SYNC_SYNCKEY","SyncKey");
+define("SYNC_CLIENTENTRYID","ClientEntryId");
+define("SYNC_SERVERENTRYID","ServerEntryId");
+define("SYNC_STATUS","Status");
+define("SYNC_FOLDER","Folder");
+define("SYNC_FOLDERTYPE","FolderType");
+define("SYNC_VERSION","Version");
+define("SYNC_FOLDERID","FolderId");
+define("SYNC_GETCHANGES","GetChanges");
+define("SYNC_MOREAVAILABLE","MoreAvailable");
+define("SYNC_MAXITEMS","MaxItems");
+define("SYNC_PERFORM","Perform");
+define("SYNC_OPTIONS","Options");
+define("SYNC_FILTERTYPE","FilterType");
+define("SYNC_TRUNCATION","Truncation");
+define("SYNC_RTFTRUNCATION","RtfTruncation");
+define("SYNC_CONFLICT","Conflict");
+define("SYNC_FOLDERS","Folders");
+define("SYNC_DATA","Data");
+define("SYNC_DELETESASMOVES","DeletesAsMoves");
+define("SYNC_NOTIFYGUID","NotifyGUID");
+define("SYNC_SUPPORTED","Supported");
+define("SYNC_SOFTDELETE","SoftDelete");
+define("SYNC_MIMESUPPORT","MIMESupport");
+define("SYNC_MIMETRUNCATION","MIMETruncation");
+define("SYNC_NEWMESSAGE","NewMessage");
+
+// POOMCONTACTS
+define("SYNC_POOMCONTACTS_ANNIVERSARY","POOMCONTACTS:Anniversary");
+define("SYNC_POOMCONTACTS_ASSISTANTNAME","POOMCONTACTS:AssistantName");
+define("SYNC_POOMCONTACTS_ASSISTNAMEPHONENUMBER","POOMCONTACTS:AssistnamePhoneNumber");
+define("SYNC_POOMCONTACTS_BIRTHDAY","POOMCONTACTS:Birthday");
+define("SYNC_POOMCONTACTS_BODY","POOMCONTACTS:Body");
+define("SYNC_POOMCONTACTS_BODYSIZE","POOMCONTACTS:BodySize");
+define("SYNC_POOMCONTACTS_BODYTRUNCATED","POOMCONTACTS:BodyTruncated");
+define("SYNC_POOMCONTACTS_BUSINESS2PHONENUMBER","POOMCONTACTS:Business2PhoneNumber");
+define("SYNC_POOMCONTACTS_BUSINESSCITY","POOMCONTACTS:BusinessCity");
+define("SYNC_POOMCONTACTS_BUSINESSCOUNTRY","POOMCONTACTS:BusinessCountry");
+define("SYNC_POOMCONTACTS_BUSINESSPOSTALCODE","POOMCONTACTS:BusinessPostalCode");
+define("SYNC_POOMCONTACTS_BUSINESSSTATE","POOMCONTACTS:BusinessState");
+define("SYNC_POOMCONTACTS_BUSINESSSTREET","POOMCONTACTS:BusinessStreet");
+define("SYNC_POOMCONTACTS_BUSINESSFAXNUMBER","POOMCONTACTS:BusinessFaxNumber");
+define("SYNC_POOMCONTACTS_BUSINESSPHONENUMBER","POOMCONTACTS:BusinessPhoneNumber");
+define("SYNC_POOMCONTACTS_CARPHONENUMBER","POOMCONTACTS:CarPhoneNumber");
+define("SYNC_POOMCONTACTS_CATEGORIES","POOMCONTACTS:Categories");
+define("SYNC_POOMCONTACTS_CATEGORY","POOMCONTACTS:Category");
+define("SYNC_POOMCONTACTS_CHILDREN","POOMCONTACTS:Children");
+define("SYNC_POOMCONTACTS_CHILD","POOMCONTACTS:Child");
+define("SYNC_POOMCONTACTS_COMPANYNAME","POOMCONTACTS:CompanyName");
+define("SYNC_POOMCONTACTS_DEPARTMENT","POOMCONTACTS:Department");
+define("SYNC_POOMCONTACTS_EMAIL1ADDRESS","POOMCONTACTS:Email1Address");
+define("SYNC_POOMCONTACTS_EMAIL2ADDRESS","POOMCONTACTS:Email2Address");
+define("SYNC_POOMCONTACTS_EMAIL3ADDRESS","POOMCONTACTS:Email3Address");
+define("SYNC_POOMCONTACTS_FILEAS","POOMCONTACTS:FileAs");
+define("SYNC_POOMCONTACTS_FIRSTNAME","POOMCONTACTS:FirstName");
+define("SYNC_POOMCONTACTS_HOME2PHONENUMBER","POOMCONTACTS:Home2PhoneNumber");
+define("SYNC_POOMCONTACTS_HOMECITY","POOMCONTACTS:HomeCity");
+define("SYNC_POOMCONTACTS_HOMECOUNTRY","POOMCONTACTS:HomeCountry");
+define("SYNC_POOMCONTACTS_HOMEPOSTALCODE","POOMCONTACTS:HomePostalCode");
+define("SYNC_POOMCONTACTS_HOMESTATE","POOMCONTACTS:HomeState");
+define("SYNC_POOMCONTACTS_HOMESTREET","POOMCONTACTS:HomeStreet");
+define("SYNC_POOMCONTACTS_HOMEFAXNUMBER","POOMCONTACTS:HomeFaxNumber");
+define("SYNC_POOMCONTACTS_HOMEPHONENUMBER","POOMCONTACTS:HomePhoneNumber");
+define("SYNC_POOMCONTACTS_JOBTITLE","POOMCONTACTS:JobTitle");
+define("SYNC_POOMCONTACTS_LASTNAME","POOMCONTACTS:LastName");
+define("SYNC_POOMCONTACTS_MIDDLENAME","POOMCONTACTS:MiddleName");
+define("SYNC_POOMCONTACTS_MOBILEPHONENUMBER","POOMCONTACTS:MobilePhoneNumber");
+define("SYNC_POOMCONTACTS_OFFICELOCATION","POOMCONTACTS:OfficeLocation");
+define("SYNC_POOMCONTACTS_OTHERCITY","POOMCONTACTS:OtherCity");
+define("SYNC_POOMCONTACTS_OTHERCOUNTRY","POOMCONTACTS:OtherCountry");
+define("SYNC_POOMCONTACTS_OTHERPOSTALCODE","POOMCONTACTS:OtherPostalCode");
+define("SYNC_POOMCONTACTS_OTHERSTATE","POOMCONTACTS:OtherState");
+define("SYNC_POOMCONTACTS_OTHERSTREET","POOMCONTACTS:OtherStreet");
+define("SYNC_POOMCONTACTS_PAGERNUMBER","POOMCONTACTS:PagerNumber");
+define("SYNC_POOMCONTACTS_RADIOPHONENUMBER","POOMCONTACTS:RadioPhoneNumber");
+define("SYNC_POOMCONTACTS_SPOUSE","POOMCONTACTS:Spouse");
+define("SYNC_POOMCONTACTS_SUFFIX","POOMCONTACTS:Suffix");
+define("SYNC_POOMCONTACTS_TITLE","POOMCONTACTS:Title");
+define("SYNC_POOMCONTACTS_WEBPAGE","POOMCONTACTS:WebPage");
+define("SYNC_POOMCONTACTS_YOMICOMPANYNAME","POOMCONTACTS:YomiCompanyName");
+define("SYNC_POOMCONTACTS_YOMIFIRSTNAME","POOMCONTACTS:YomiFirstName");
+define("SYNC_POOMCONTACTS_YOMILASTNAME","POOMCONTACTS:YomiLastName");
+define("SYNC_POOMCONTACTS_RTF","POOMCONTACTS:Rtf");
+define("SYNC_POOMCONTACTS_PICTURE","POOMCONTACTS:Picture");
+
+// POOMMAIL
+define("SYNC_POOMMAIL_ATTACHMENT","POOMMAIL:Attachment");
+define("SYNC_POOMMAIL_ATTACHMENTS","POOMMAIL:Attachments");
+define("SYNC_POOMMAIL_ATTNAME","POOMMAIL:AttName");
+define("SYNC_POOMMAIL_ATTSIZE","POOMMAIL:AttSize");
+define("SYNC_POOMMAIL_ATTOID","POOMMAIL:AttOid");
+define("SYNC_POOMMAIL_ATTMETHOD","POOMMAIL:AttMethod");
+define("SYNC_POOMMAIL_ATTREMOVED","POOMMAIL:AttRemoved");
+define("SYNC_POOMMAIL_BODY","POOMMAIL:Body");
+define("SYNC_POOMMAIL_BODYSIZE","POOMMAIL:BodySize");
+define("SYNC_POOMMAIL_BODYTRUNCATED","POOMMAIL:BodyTruncated");
+define("SYNC_POOMMAIL_DATERECEIVED","POOMMAIL:DateReceived");
+define("SYNC_POOMMAIL_DISPLAYNAME","POOMMAIL:DisplayName");
+define("SYNC_POOMMAIL_DISPLAYTO","POOMMAIL:DisplayTo");
+define("SYNC_POOMMAIL_IMPORTANCE","POOMMAIL:Importance");
+define("SYNC_POOMMAIL_MESSAGECLASS","POOMMAIL:MessageClass");
+define("SYNC_POOMMAIL_SUBJECT","POOMMAIL:Subject");
+define("SYNC_POOMMAIL_READ","POOMMAIL:Read");
+define("SYNC_POOMMAIL_TO","POOMMAIL:To");
+define("SYNC_POOMMAIL_CC","POOMMAIL:Cc");
+define("SYNC_POOMMAIL_FROM","POOMMAIL:From");
+define("SYNC_POOMMAIL_REPLY_TO","POOMMAIL:Reply-To");
+define("SYNC_POOMMAIL_ALLDAYEVENT","POOMMAIL:AllDayEvent");
+define("SYNC_POOMMAIL_CATEGORIES","POOMMAIL:Categories");
+define("SYNC_POOMMAIL_CATEGORY","POOMMAIL:Category");
+define("SYNC_POOMMAIL_DTSTAMP","POOMMAIL:DtStamp");
+define("SYNC_POOMMAIL_ENDTIME","POOMMAIL:EndTime");
+define("SYNC_POOMMAIL_INSTANCETYPE","POOMMAIL:InstanceType");
+define("SYNC_POOMMAIL_BUSYSTATUS","POOMMAIL:BusyStatus");
+define("SYNC_POOMMAIL_LOCATION","POOMMAIL:Location");
+define("SYNC_POOMMAIL_MEETINGREQUEST","POOMMAIL:MeetingRequest");
+define("SYNC_POOMMAIL_ORGANIZER","POOMMAIL:Organizer");
+define("SYNC_POOMMAIL_RECURRENCEID","POOMMAIL:RecurrenceId");
+define("SYNC_POOMMAIL_REMINDER","POOMMAIL:Reminder");
+define("SYNC_POOMMAIL_RESPONSEREQUESTED","POOMMAIL:ResponseRequested");
+define("SYNC_POOMMAIL_RECURRENCES","POOMMAIL:Recurrences");
+define("SYNC_POOMMAIL_RECURRENCE","POOMMAIL:Recurrence");
+define("SYNC_POOMMAIL_TYPE","POOMMAIL:Type");
+define("SYNC_POOMMAIL_UNTIL","POOMMAIL:Until");
+define("SYNC_POOMMAIL_OCCURRENCES","POOMMAIL:Occurrences");
+define("SYNC_POOMMAIL_INTERVAL","POOMMAIL:Interval");
+define("SYNC_POOMMAIL_DAYOFWEEK","POOMMAIL:DayOfWeek");
+define("SYNC_POOMMAIL_DAYOFMONTH","POOMMAIL:DayOfMonth");
+define("SYNC_POOMMAIL_WEEKOFMONTH","POOMMAIL:WeekOfMonth");
+define("SYNC_POOMMAIL_MONTHOFYEAR","POOMMAIL:MonthOfYear");
+define("SYNC_POOMMAIL_STARTTIME","POOMMAIL:StartTime");
+define("SYNC_POOMMAIL_SENSITIVITY","POOMMAIL:Sensitivity");
+define("SYNC_POOMMAIL_TIMEZONE","POOMMAIL:TimeZone");
+define("SYNC_POOMMAIL_GLOBALOBJID","POOMMAIL:GlobalObjId");
+define("SYNC_POOMMAIL_THREADTOPIC","POOMMAIL:ThreadTopic");
+define("SYNC_POOMMAIL_MIMEDATA","POOMMAIL:MIMEData");
+define("SYNC_POOMMAIL_MIMETRUNCATED","POOMMAIL:MIMETruncated");
+define("SYNC_POOMMAIL_MIMESIZE","POOMMAIL:MIMESize");
+define("SYNC_POOMMAIL_INTERNETCPID","POOMMAIL:InternetCPID");
+
+// AIRNOTIFY
+define("SYNC_AIRNOTIFY_NOTIFY","AirNotify:Notify");
+define("SYNC_AIRNOTIFY_NOTIFICATION","AirNotify:Notification");
+define("SYNC_AIRNOTIFY_VERSION","AirNotify:Version");
+define("SYNC_AIRNOTIFY_LIFETIME","AirNotify:Lifetime");
+define("SYNC_AIRNOTIFY_DEVICEINFO","AirNotify:DeviceInfo");
+define("SYNC_AIRNOTIFY_ENABLE","AirNotify:Enable");
+define("SYNC_AIRNOTIFY_FOLDER","AirNotify:Folder");
+define("SYNC_AIRNOTIFY_SERVERENTRYID","AirNotify:ServerEntryId");
+define("SYNC_AIRNOTIFY_DEVICEADDRESS","AirNotify:DeviceAddress");
+define("SYNC_AIRNOTIFY_VALIDCARRIERPROFILES","AirNotify:ValidCarrierProfiles");
+define("SYNC_AIRNOTIFY_CARRIERPROFILE","AirNotify:CarrierProfile");
+define("SYNC_AIRNOTIFY_STATUS","AirNotify:Status");
+define("SYNC_AIRNOTIFY_REPLIES","AirNotify:Replies");
+define("SYNC_AIRNOTIFY_VERSION='1.1'","AirNotify:Version='1.1'");
+define("SYNC_AIRNOTIFY_DEVICES","AirNotify:Devices");
+define("SYNC_AIRNOTIFY_DEVICE","AirNotify:Device");
+define("SYNC_AIRNOTIFY_ID","AirNotify:Id");
+define("SYNC_AIRNOTIFY_EXPIRY","AirNotify:Expiry");
+define("SYNC_AIRNOTIFY_NOTIFYGUID","AirNotify:NotifyGUID");
+
+// POOMCAL
+define("SYNC_POOMCAL_TIMEZONE","POOMCAL:Timezone");
+define("SYNC_POOMCAL_ALLDAYEVENT","POOMCAL:AllDayEvent");
+define("SYNC_POOMCAL_ATTENDEES","POOMCAL:Attendees");
+define("SYNC_POOMCAL_ATTENDEE","POOMCAL:Attendee");
+define("SYNC_POOMCAL_EMAIL","POOMCAL:Email");
+define("SYNC_POOMCAL_NAME","POOMCAL:Name");
+define("SYNC_POOMCAL_BODY","POOMCAL:Body");
+define("SYNC_POOMCAL_BODYTRUNCATED","POOMCAL:BodyTruncated");
+define("SYNC_POOMCAL_BUSYSTATUS","POOMCAL:BusyStatus");
+define("SYNC_POOMCAL_CATEGORIES","POOMCAL:Categories");
+define("SYNC_POOMCAL_CATEGORY","POOMCAL:Category");
+define("SYNC_POOMCAL_RTF","POOMCAL:Rtf");
+define("SYNC_POOMCAL_DTSTAMP","POOMCAL:DtStamp");
+define("SYNC_POOMCAL_ENDTIME","POOMCAL:EndTime");
+define("SYNC_POOMCAL_EXCEPTION","POOMCAL:Exception");
+define("SYNC_POOMCAL_EXCEPTIONS","POOMCAL:Exceptions");
+define("SYNC_POOMCAL_DELETED","POOMCAL:Deleted");
+define("SYNC_POOMCAL_EXCEPTIONSTARTTIME","POOMCAL:ExceptionStartTime");
+define("SYNC_POOMCAL_LOCATION","POOMCAL:Location");
+define("SYNC_POOMCAL_MEETINGSTATUS","POOMCAL:MeetingStatus");
+define("SYNC_POOMCAL_ORGANIZEREMAIL","POOMCAL:OrganizerEmail");
+define("SYNC_POOMCAL_ORGANIZERNAME","POOMCAL:OrganizerName");
+define("SYNC_POOMCAL_RECURRENCE","POOMCAL:Recurrence");
+define("SYNC_POOMCAL_TYPE","POOMCAL:Type");
+define("SYNC_POOMCAL_UNTIL","POOMCAL:Until");
+define("SYNC_POOMCAL_OCCURRENCES","POOMCAL:Occurrences");
+define("SYNC_POOMCAL_INTERVAL","POOMCAL:Interval");
+define("SYNC_POOMCAL_DAYOFWEEK","POOMCAL:DayOfWeek");
+define("SYNC_POOMCAL_DAYOFMONTH","POOMCAL:DayOfMonth");
+define("SYNC_POOMCAL_WEEKOFMONTH","POOMCAL:WeekOfMonth");
+define("SYNC_POOMCAL_MONTHOFYEAR","POOMCAL:MonthOfYear");
+define("SYNC_POOMCAL_REMINDER","POOMCAL:Reminder");
+define("SYNC_POOMCAL_SENSITIVITY","POOMCAL:Sensitivity");
+define("SYNC_POOMCAL_SUBJECT","POOMCAL:Subject");
+define("SYNC_POOMCAL_STARTTIME","POOMCAL:StartTime");
+define("SYNC_POOMCAL_UID","POOMCAL:UID");
+
+// Move
+define("SYNC_MOVE_MOVES","Move:Moves");
+define("SYNC_MOVE_MOVE","Move:Move");
+define("SYNC_MOVE_SRCMSGID","Move:SrcMsgId");
+define("SYNC_MOVE_SRCFLDID","Move:SrcFldId");
+define("SYNC_MOVE_DSTFLDID","Move:DstFldId");
+define("SYNC_MOVE_RESPONSE","Move:Response");
+define("SYNC_MOVE_STATUS","Move:Status");
+define("SYNC_MOVE_DSTMSGID","Move:DstMsgId");
+
+// GetItemEstimate
+define("SYNC_GETITEMESTIMATE_GETITEMESTIMATE","GetItemEstimate:GetItemEstimate");
+define("SYNC_GETITEMESTIMATE_VERSION","GetItemEstimate:Version");
+define("SYNC_GETITEMESTIMATE_FOLDERS","GetItemEstimate:Folders");
+define("SYNC_GETITEMESTIMATE_FOLDER","GetItemEstimate:Folder");
+define("SYNC_GETITEMESTIMATE_FOLDERTYPE","GetItemEstimate:FolderType");
+define("SYNC_GETITEMESTIMATE_FOLDERID","GetItemEstimate:FolderId");
+define("SYNC_GETITEMESTIMATE_DATETIME","GetItemEstimate:DateTime");
+define("SYNC_GETITEMESTIMATE_ESTIMATE","GetItemEstimate:Estimate");
+define("SYNC_GETITEMESTIMATE_RESPONSE","GetItemEstimate:Response");
+define("SYNC_GETITEMESTIMATE_STATUS","GetItemEstimate:Status");
+
+// FolderHierarchy
+define("SYNC_FOLDERHIERARCHY_FOLDERS","FolderHierarchy:Folders");
+define("SYNC_FOLDERHIERARCHY_FOLDER","FolderHierarchy:Folder");
+define("SYNC_FOLDERHIERARCHY_DISPLAYNAME","FolderHierarchy:DisplayName");
+define("SYNC_FOLDERHIERARCHY_SERVERENTRYID","FolderHierarchy:ServerEntryId");
+define("SYNC_FOLDERHIERARCHY_PARENTID","FolderHierarchy:ParentId");
+define("SYNC_FOLDERHIERARCHY_TYPE","FolderHierarchy:Type");
+define("SYNC_FOLDERHIERARCHY_RESPONSE","FolderHierarchy:Response");
+define("SYNC_FOLDERHIERARCHY_STATUS","FolderHierarchy:Status");
+define("SYNC_FOLDERHIERARCHY_CONTENTCLASS","FolderHierarchy:ContentClass");
+define("SYNC_FOLDERHIERARCHY_CHANGES","FolderHierarchy:Changes");
+define("SYNC_FOLDERHIERARCHY_ADD","FolderHierarchy:Add");
+define("SYNC_FOLDERHIERARCHY_REMOVE","FolderHierarchy:Remove");
+define("SYNC_FOLDERHIERARCHY_UPDATE","FolderHierarchy:Update");
+define("SYNC_FOLDERHIERARCHY_SYNCKEY","FolderHierarchy:SyncKey");
+define("SYNC_FOLDERHIERARCHY_FOLDERCREATE","FolderHierarchy:FolderCreate");
+define("SYNC_FOLDERHIERARCHY_FOLDERDELETE","FolderHierarchy:FolderDelete");
+define("SYNC_FOLDERHIERARCHY_FOLDERUPDATE","FolderHierarchy:FolderUpdate");
+define("SYNC_FOLDERHIERARCHY_FOLDERSYNC","FolderHierarchy:FolderSync");
+define("SYNC_FOLDERHIERARCHY_COUNT","FolderHierarchy:Count");
+define("SYNC_FOLDERHIERARCHY_VERSION","FolderHierarchy:Version");
+
+// MeetingResponse
+define("SYNC_MEETINGRESPONSE_CALENDARID","MeetingResponse:CalendarId");
+define("SYNC_MEETINGRESPONSE_FOLDERID","MeetingResponse:FolderId");
+define("SYNC_MEETINGRESPONSE_MEETINGRESPONSE","MeetingResponse:MeetingResponse");
+define("SYNC_MEETINGRESPONSE_REQUESTID","MeetingResponse:RequestId");
+define("SYNC_MEETINGRESPONSE_REQUEST","MeetingResponse:Request");
+define("SYNC_MEETINGRESPONSE_RESULT","MeetingResponse:Result");
+define("SYNC_MEETINGRESPONSE_STATUS","MeetingResponse:Status");
+define("SYNC_MEETINGRESPONSE_USERRESPONSE","MeetingResponse:UserResponse");
+define("SYNC_MEETINGRESPONSE_VERSION","MeetingResponse:Version");
+
+// POOMTASKS
+define("SYNC_POOMTASKS_BODY","POOMTASKS:Body");
+define("SYNC_POOMTASKS_BODYSIZE","POOMTASKS:BodySize");
+define("SYNC_POOMTASKS_BODYTRUNCATED","POOMTASKS:BodyTruncated");
+define("SYNC_POOMTASKS_CATEGORIES","POOMTASKS:Categories");
+define("SYNC_POOMTASKS_CATEGORY","POOMTASKS:Category");
+define("SYNC_POOMTASKS_COMPLETE","POOMTASKS:Complete");
+define("SYNC_POOMTASKS_DATECOMPLETED","POOMTASKS:DateCompleted");
+define("SYNC_POOMTASKS_DUEDATE","POOMTASKS:DueDate");
+define("SYNC_POOMTASKS_UTCDUEDATE","POOMTASKS:UtcDueDate");
+define("SYNC_POOMTASKS_IMPORTANCE","POOMTASKS:Importance");
+define("SYNC_POOMTASKS_RECURRENCE","POOMTASKS:Recurrence");
+define("SYNC_POOMTASKS_TYPE","POOMTASKS:Type");
+define("SYNC_POOMTASKS_START","POOMTASKS:Start");
+define("SYNC_POOMTASKS_UNTIL","POOMTASKS:Until");
+define("SYNC_POOMTASKS_OCCURRENCES","POOMTASKS:Occurrences");
+define("SYNC_POOMTASKS_INTERVAL","POOMTASKS:Interval");
+define("SYNC_POOMTASKS_DAYOFWEEK","POOMTASKS:DayOfWeek");
+define("SYNC_POOMTASKS_DAYOFMONTH","POOMTASKS:DayOfMonth");
+define("SYNC_POOMTASKS_WEEKOFMONTH","POOMTASKS:WeekOfMonth");
+define("SYNC_POOMTASKS_MONTHOFYEAR","POOMTASKS:MonthOfYear");
+define("SYNC_POOMTASKS_REGENERATE","POOMTASKS:Regenerate");
+define("SYNC_POOMTASKS_DEADOCCUR","POOMTASKS:DeadOccur");
+define("SYNC_POOMTASKS_REMINDERSET","POOMTASKS:ReminderSet");
+define("SYNC_POOMTASKS_REMINDERTIME","POOMTASKS:ReminderTime");
+define("SYNC_POOMTASKS_SENSITIVITY","POOMTASKS:Sensitivity");
+define("SYNC_POOMTASKS_STARTDATE","POOMTASKS:StartDate");
+define("SYNC_POOMTASKS_UTCSTARTDATE","POOMTASKS:UtcStartDate");
+define("SYNC_POOMTASKS_SUBJECT","POOMTASKS:Subject");
+define("SYNC_POOMTASKS_RTF","POOMTASKS:Rtf");
+
+// ResolveRecipients
+define("SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS","ResolveRecipients:ResolveRecipients");
+define("SYNC_RESOLVERECIPIENTS_RESPONSE","ResolveRecipients:Response");
+define("SYNC_RESOLVERECIPIENTS_STATUS","ResolveRecipients:Status");
+define("SYNC_RESOLVERECIPIENTS_TYPE","ResolveRecipients:Type");
+define("SYNC_RESOLVERECIPIENTS_RECIPIENT","ResolveRecipients:Recipient");
+define("SYNC_RESOLVERECIPIENTS_DISPLAYNAME","ResolveRecipients:DisplayName");
+define("SYNC_RESOLVERECIPIENTS_EMAILADDRESS","ResolveRecipients:EmailAddress");
+define("SYNC_RESOLVERECIPIENTS_CERTIFICATES","ResolveRecipients:Certificates");
+define("SYNC_RESOLVERECIPIENTS_CERTIFICATE","ResolveRecipients:Certificate");
+define("SYNC_RESOLVERECIPIENTS_MINICERTIFICATE","ResolveRecipients:MiniCertificate");
+define("SYNC_RESOLVERECIPIENTS_OPTIONS","ResolveRecipients:Options");
+define("SYNC_RESOLVERECIPIENTS_TO","ResolveRecipients:To");
+define("SYNC_RESOLVERECIPIENTS_CERTIFICATERETRIEVAL","ResolveRecipients:CertificateRetrieval");
+define("SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT","ResolveRecipients:RecipientCount");
+define("SYNC_RESOLVERECIPIENTS_MAXCERTIFICATES","ResolveRecipients:MaxCertificates");
+define("SYNC_RESOLVERECIPIENTS_MAXAMBIGUOUSRECIPIENTS","ResolveRecipients:MaxAmbiguousRecipients");
+define("SYNC_RESOLVERECIPIENTS_CERTIFICATECOUNT","ResolveRecipients:CertificateCount");
+
+// ValidateCert
+define("SYNC_VALIDATECERT_VALIDATECERT","ValidateCert:ValidateCert");
+define("SYNC_VALIDATECERT_CERTIFICATES","ValidateCert:Certificates");
+define("SYNC_VALIDATECERT_CERTIFICATE","ValidateCert:Certificate");
+define("SYNC_VALIDATECERT_CERTIFICATECHAIN","ValidateCert:CertificateChain");
+define("SYNC_VALIDATECERT_CHECKCRL","ValidateCert:CheckCRL");
+define("SYNC_VALIDATECERT_STATUS","ValidateCert:Status");
+
+// POOMCONTACTS2
+define("SYNC_POOMCONTACTS2_CUSTOMERID","POOMCONTACTS2:CustomerId");
+define("SYNC_POOMCONTACTS2_GOVERNMENTID","POOMCONTACTS2:GovernmentId");
+define("SYNC_POOMCONTACTS2_IMADDRESS","POOMCONTACTS2:IMAddress");
+define("SYNC_POOMCONTACTS2_IMADDRESS2","POOMCONTACTS2:IMAddress2");
+define("SYNC_POOMCONTACTS2_IMADDRESS3","POOMCONTACTS2:IMAddress3");
+define("SYNC_POOMCONTACTS2_MANAGERNAME","POOMCONTACTS2:ManagerName");
+define("SYNC_POOMCONTACTS2_COMPANYMAINPHONE","POOMCONTACTS2:CompanyMainPhone");
+define("SYNC_POOMCONTACTS2_ACCOUNTNAME","POOMCONTACTS2:AccountName");
+define("SYNC_POOMCONTACTS2_NICKNAME","POOMCONTACTS2:NickName");
+define("SYNC_POOMCONTACTS2_MMS","POOMCONTACTS2:MMS");
+
+// Ping
+define("SYNC_PING_PING","Ping:Ping");
+define("SYNC_PING_STATUS","Ping:Status");
+define("SYNC_PING_LIFETIME", "Ping:LifeTime");
+define("SYNC_PING_FOLDERS", "Ping:Folders");
+define("SYNC_PING_FOLDER", "Ping:Folder");
+define("SYNC_PING_SERVERENTRYID", "Ping:ServerEntryId");
+define("SYNC_PING_FOLDERTYPE", "Ping:FolderType");
+
+//Provision
+define("SYNC_PROVISION_PROVISION", "Provision:Provision");
+define("SYNC_PROVISION_POLICIES", "Provision:Policies");
+define("SYNC_PROVISION_POLICY", "Provision:Policy");
+define("SYNC_PROVISION_POLICYTYPE", "Provision:PolicyType");
+define("SYNC_PROVISION_POLICYKEY", "Provision:PolicyKey");
+define("SYNC_PROVISION_DATA", "Provision:Data");
+define("SYNC_PROVISION_STATUS", "Provision:Status");
+define("SYNC_PROVISION_REMOTEWIPE", "Provision:RemoteWipe");
+define("SYNC_PROVISION_EASPROVISIONDOC", "Provision:EASProvisionDoc");
+
+//Search
+define("SYNC_SEARCH_SEARCH", "Search:Search");
+define("SYNC_SEARCH_STORE", "Search:Store");
+define("SYNC_SEARCH_NAME", "Search:Name");
+define("SYNC_SEARCH_QUERY", "Search:Query");
+define("SYNC_SEARCH_OPTIONS", "Search:Options");
+define("SYNC_SEARCH_RANGE", "Search:Range");
+define("SYNC_SEARCH_STATUS", "Search:Status");
+define("SYNC_SEARCH_RESPONSE", "Search:Response");
+define("SYNC_SEARCH_RESULT", "Search:Result");
+define("SYNC_SEARCH_PROPERTIES", "Search:Properties");
+define("SYNC_SEARCH_TOTAL", "Search:Total");
+define("SYNC_SEARCH_EQUALTO", "Search:EqualTo");
+define("SYNC_SEARCH_VALUE", "Search:Value");
+define("SYNC_SEARCH_AND", "Search:And");
+define("SYNC_SEARCH_OR", "Search:Or");
+define("SYNC_SEARCH_FREETEXT", "Search:FreeText");
+define("SYNC_SEARCH_DEEPTRAVERSAL", "Search:DeepTraversal");
+define("SYNC_SEARCH_LONGID", "Search:LongId");
+define("SYNC_SEARCH_REBUILDRESULTS", "Search:RebuildResults");
+define("SYNC_SEARCH_LESSTHAN", "Search:LessThan");
+define("SYNC_SEARCH_GREATERTHAN", "Search:GreaterThan");
+define("SYNC_SEARCH_SCHEMA", "Search:Schema");
+define("SYNC_SEARCH_SUPPORTED", "Search:Supported");
+
+//GAL
+define("SYNC_GAL_DISPLAYNAME", "GAL:DisplayName");
+define("SYNC_GAL_PHONE", "GAL:Phone");
+define("SYNC_GAL_OFFICE", "GAL:Office");
+define("SYNC_GAL_TITLE", "GAL:Title");
+define("SYNC_GAL_COMPANY", "GAL:Company");
+define("SYNC_GAL_ALIAS", "GAL:Alias");
+define("SYNC_GAL_FIRSTNAME", "GAL:FirstName");
+define("SYNC_GAL_LASTNAME", "GAL:LastName");
+define("SYNC_GAL_HOMEPHONE", "GAL:HomePhone");
+define("SYNC_GAL_MOBILEPHONE", "GAL:MobilePhone");
+define("SYNC_GAL_EMAILADDRESS", "GAL:EmailAddress");
+
+// Other constants
+define("SYNC_FOLDER_TYPE_OTHER", 1);
+define("SYNC_FOLDER_TYPE_INBOX", 2);
+define("SYNC_FOLDER_TYPE_DRAFTS", 3);
+define("SYNC_FOLDER_TYPE_WASTEBASKET", 4);
+define("SYNC_FOLDER_TYPE_SENTMAIL", 5);
+define("SYNC_FOLDER_TYPE_OUTBOX", 6);
+define("SYNC_FOLDER_TYPE_TASK", 7);
+define("SYNC_FOLDER_TYPE_APPOINTMENT", 8);
+define("SYNC_FOLDER_TYPE_CONTACT", 9);
+define("SYNC_FOLDER_TYPE_NOTE", 10);
+define("SYNC_FOLDER_TYPE_JOURNAL", 11);
+define("SYNC_FOLDER_TYPE_USER_MAIL", 12);
+define("SYNC_FOLDER_TYPE_USER_APPOINTMENT", 13);
+define("SYNC_FOLDER_TYPE_USER_CONTACT", 14);
+define("SYNC_FOLDER_TYPE_USER_TASK", 15);
+define("SYNC_FOLDER_TYPE_USER_JOURNAL", 16);
+define("SYNC_FOLDER_TYPE_USER_NOTE", 17);
+define("SYNC_FOLDER_TYPE_UNKNOWN", 18);
+define("SYNC_FOLDER_TYPE_RECIPIENT_CACHE", 19);
+define("SYNC_FOLDER_TYPE_DUMMY", "__dummy.Folder.Id__");
+
+define("SYNC_CONFLICT_OVERWRITE_SERVER", 0);
+define("SYNC_CONFLICT_OVERWRITE_PIM", 1);
+
+define("SYNC_FILTERTYPE_ALL", 0);
+define("SYNC_FILTERTYPE_1DAY", 1);
+define("SYNC_FILTERTYPE_3DAYS", 2);
+define("SYNC_FILTERTYPE_1WEEK", 3);
+define("SYNC_FILTERTYPE_2WEEKS", 4);
+define("SYNC_FILTERTYPE_1MONTH", 5);
+define("SYNC_FILTERTYPE_3MONTHS", 6);
+define("SYNC_FILTERTYPE_6MONTHS", 7);
+
+define("SYNC_TRUNCATION_HEADERS", 0);
+define("SYNC_TRUNCATION_512B", 1);
+define("SYNC_TRUNCATION_1K", 2);
+define("SYNC_TRUNCATION_5K", 4);
+define("SYNC_TRUNCATION_SEVEN", 7);
+define("SYNC_TRUNCATION_ALL", 9);
+
+define("SYNC_PROVISION_STATUS_SUCCESS", 1);
+define("SYNC_PROVISION_STATUS_PROTERROR", 2);
+define("SYNC_PROVISION_STATUS_SERVERERROR", 3);
+define("SYNC_PROVISION_STATUS_DEVEXTMANAGED", 4);
+define("SYNC_PROVISION_STATUS_POLKEYMISM", 5);
+
+define("SYNC_PROVISION_RWSTATUS_NA", 0);
+define("SYNC_PROVISION_RWSTATUS_OK", 1);
+define("SYNC_PROVISION_RWSTATUS_PENDING", 2);
+define("SYNC_PROVISION_RWSTATUS_WIPED", 3);
+?>
Index: /contrib/z-push/streamimporter.php
===================================================================
--- /contrib/z-push/streamimporter.php	(revision 4898)
+++ /contrib/z-push/streamimporter.php	(revision 4898)
@@ -0,0 +1,101 @@
+<?php
+/***********************************************
+* File      :   streamimporter.php
+* Project   :   Z-Push
+* Descr     :   Stream import classes
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+// We don't support caching changes for messages
+class ImportContentsChangesStream {
+    var $_encoder;
+    var $_type;
+    var $_seenObjects;
+
+    function ImportContentsChangesStream(&$encoder, $type) {
+        $this->_encoder = &$encoder;
+        $this->_type = $type;
+        $this->_seenObjects = array();
+    }
+
+    function ImportMessageChange($id, $message) {
+        if(strtolower(get_class($message)) != $this->_type)
+            return true; // ignore other types
+
+        // prevent sending the same object twice in one request
+        if (in_array($id, $this->_seenObjects)) {
+        	debugLog("Object $id discarded! Object already sent in this request.");
+        	return true;
+        }
+
+        $this->_seenObjects[] = $id;
+
+        if ($message->flags === false || $message->flags === SYNC_NEWMESSAGE)
+            $this->_encoder->startTag(SYNC_ADD);
+        else
+            $this->_encoder->startTag(SYNC_MODIFY);
+
+        $this->_encoder->startTag(SYNC_SERVERENTRYID);
+        $this->_encoder->content($id);
+        $this->_encoder->endTag();
+        $this->_encoder->startTag(SYNC_DATA);
+        $message->encode($this->_encoder);
+        $this->_encoder->endTag();
+        $this->_encoder->endTag();
+
+        return true;
+    }
+
+    function ImportMessageDeletion($id) {
+        $this->_encoder->startTag(SYNC_REMOVE);
+        $this->_encoder->startTag(SYNC_SERVERENTRYID);
+        $this->_encoder->content($id);
+        $this->_encoder->endTag();
+        $this->_encoder->endTag();
+
+        return true;
+    }
+
+    function ImportMessageReadFlag($id, $flags) {
+        if($this->_type != "syncmail")
+            return true;
+        $this->_encoder->startTag(SYNC_MODIFY);
+            $this->_encoder->startTag(SYNC_SERVERENTRYID);
+                $this->_encoder->content($id);
+            $this->_encoder->endTag();
+            $this->_encoder->startTag(SYNC_DATA);
+                $this->_encoder->startTag(SYNC_POOMMAIL_READ);
+                    $this->_encoder->content($flags);
+                $this->_encoder->endTag();
+            $this->_encoder->endTag();
+        $this->_encoder->endTag();
+
+        return true;
+    }
+
+    function ImportMessageMove($message) {
+        return true;
+    }
+};
+
+class ImportHierarchyChangesStream {
+
+    function ImportHierarchyChangesStream() {
+        return true;
+    }
+
+    function ImportFolderChange($folder) {
+        return true;
+    }
+
+    function ImportFolderDeletion($folder) {
+        return true;
+    }
+};
+
+?>
Index: /contrib/z-push/INSTALL
===================================================================
--- /contrib/z-push/INSTALL	(revision 4898)
+++ /contrib/z-push/INSTALL	(revision 4898)
@@ -0,0 +1,122 @@
+Installing Z-Push
+======================
+
+Requirements
+------------
+
+Z-Push runs on either PHP4 or PHP5. 
+Version prior to 1.2.2 require the 'Mail' package from PEAR.
+Newer versions don't have this dependency anymore.
+
+Additional informations can be found in the Zarafa Administrator Manual:
+http://doc.zarafa.com/trunk/Administrator_Manual/en-US/html/index.html
+
+
+How to install
+--------------
+
+To Install Z-Push, simply untar the z-push tar to your webroot,
+e.g. with
+
+tar -xzvf z-push-<version>.tar.gz -C /var/www
+
+Now, edit the config.php file in the directory to reflect your local system.
+For MAPI use with Zarafa, you needn't change any settings and should work
+as-is.
+
+Please also set your timezone in the config.php file.
+
+Make sure that the 'state' directory is writeable for your webserver
+process, so either change the owner of the 'state' directory to the UID of
+your apache process, or make the directory world writeable:
+
+chmod 777 /var/www/z-push/state
+
+Now, you must configure Apache to redirect the URL
+'Microsoft-Server-ActiveSync' to the index.php file in the Z-Push
+directory. This can be done by adding the line
+
+Alias /Microsoft-Server-ActiveSync /var/www/z-push/index.php
+
+to your httpd.conf file. Make sure that you are adding the line to the
+correct part of your Apache configuration, taking care of virtual hosts and
+other Apache configurations.
+
+*WARNING* You CANNOT simply rename the z-push directory to
+Microsoft-Server-ActiveSync. This will cause Apache to send redirects to the
+PDA, which will definitely break your PDA synchronisation.
+
+Lastly, make sure that PHP has the following settings:
+
+php_flag magic_quotes_gpc off
+php_flag register_globals off
+php_flag magic_quotes_runtime off
+php_flag short_open_tag on
+
+You can set this in the httpd.conf, in php.ini or in an .htaccess file in
+the root of z-push. If you don't set this up correctly, you will not be
+able to login correctly via z-push.
+
+After doing this, you should be able to synchronize from your PDA.
+
+
+Update to newer Z-Push versions
+-------------------------------
+
+Upgrading to a newer Z-Push version follows the same path as the initial 
+installation.
+
+When upgrading to a new minor version e.g. from Z-Push 1.4 to Z-Push 1.4.1, 
+the existing Z-Push directory can be overwritten when extracting the archive.
+When installing a new major version it is recommended to extract the tarball 
+to another directory and to copy the state from the existing installation.
+
+IMPORTANT: It is crucial to always keep the data of the state directory in 
+order to ensure data consistency on already synchronized mobiles.
+
+Without the state information mobile devices, which already have an ActiveSync 
+profile, will receive duplicate items or the synchronization will break completely.
+
+Please also observe the published release notes of the new Z-Push version.
+For some releases it is necessary to e.g. resynchronize the mobile.
+
+
+Setting up your PocketPC
+------------------------
+
+This is simply a case of adding an 'exchange server' to your activesync
+server list, specifying the IP address of the Z-Push apache server,
+disabling SSL, unless you have already setup SSL on your Apache server,
+setting the correct username & password (the domain is ignored, you can
+simply specify 'domain' or some other random string), and then going through
+the standard activesync settings.
+
+Once you have done this, you should be able to synchronise your PocketPC
+simply by clicking the 'Sync' button in ActiveSync on your PocketPC.
+
+Troubleshooting
+---------------
+
+Most problems will be caused by incorrect Apache settings. To test whether
+your Apache setup is working correctly, you can simply type the Z-Push URL
+in your browser, to see if apache is correctly redirecting your request to
+z-push. You can simply use:
+
+http://<serverip>/Microsoft-Server-ActiveSync
+
+If correctly configured, you should see a username/password request, and
+when you specify a valid username & password, you should see a string like
+
+"Your device requested the Z-Push URL without the required GET parameters"
+
+If not, then check your PHP and Apache settings.
+
+If you have other synchronisation problems, you can create the file
+'debug.txt' in the root directory of z-push, which should also be
+world-writable:
+
+touch /var/www/z-push/debug.txt
+chmod 777 /var/www/z-push/debug.txt
+
+The debug.txt file will then collect debug information from your
+synchronisation.
Index: /contrib/z-push/streamer.php
===================================================================
--- /contrib/z-push/streamer.php	(revision 4898)
+++ /contrib/z-push/streamer.php	(revision 4898)
@@ -0,0 +1,231 @@
+<?php
+/***********************************************
+* File      :   streamer.php
+* Project   :   Z-Push
+* Descr     :   This file handles streaming of
+*                WBXML objects. It must be
+*                subclassed so the internals of
+*                the object can be specified via
+*                $mapping. Basically we set/read
+*                the object variables of the
+*                subclass according to the mappings
+*
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+include_once("zpushdtd.php");
+
+define('STREAMER_VAR', 1);
+define('STREAMER_ARRAY', 2);
+define('STREAMER_TYPE', 3);
+
+define('STREAMER_TYPE_DATE', 1);
+define('STREAMER_TYPE_HEX', 2);
+define('STREAMER_TYPE_DATE_DASHES', 3);
+define('STREAMER_TYPE_MAPI_STREAM', 4);
+
+
+class Streamer {
+    var $_mapping;
+
+    var $content;
+    var $attributes;
+    var $flags;
+
+    function Streamer($mapping) {
+        $this->_mapping = $mapping;
+        $this->flags = false;
+    }
+
+    // Decodes the WBXML from $input until we reach the same depth level of WBXML. This
+    // means that if there are multiple objects at this level, then only the first is decoded
+    // SubOjects are auto-instantiated and decoded using the same functionality
+    function decode(&$decoder) {
+        while(1) {
+            $entity = $decoder->getElement();
+
+            if($entity[EN_TYPE] == EN_TYPE_STARTTAG) {
+                if(! ($entity[EN_FLAGS] & EN_FLAGS_CONTENT)) {
+                    $map = $this->_mapping[$entity[EN_TAG]];
+                    if(!isset($map[STREAMER_TYPE])) {
+                        $this->$map[STREAMER_VAR] = "";
+                    } else if ($map[STREAMER_TYPE] == STREAMER_TYPE_DATE || $map[STREAMER_TYPE] == STREAMER_TYPE_DATE_DASHES ) {
+                        $this->$map[STREAMER_VAR] = "";
+                    }
+                    continue;
+                }
+                // Found a start tag
+                if(!isset($this->_mapping[$entity[EN_TAG]])) {
+                    // This tag shouldn't be here, abort
+                    debug("Tag " . $entity[EN_TAG] . " unexpected in type XML type " . get_class($this));
+                    return false;
+                } else {
+                    $map = $this->_mapping[$entity[EN_TAG]];
+
+                    // Handle an array
+                    if(isset($map[STREAMER_ARRAY])) {
+                        while(1) {
+                            if(!$decoder->getElementStartTag($map[STREAMER_ARRAY]))
+                                break;
+                            if(isset($map[STREAMER_TYPE])) {
+                                $decoded = new $map[STREAMER_TYPE];
+                                $decoded->decode($decoder);
+                            } else {
+                                $decoded = $decoder->getElementContent();
+                            }
+
+                            if(!isset($this->$map[STREAMER_VAR]))
+                                $this->$map[STREAMER_VAR] = array($decoded);
+                            else
+                                array_push($this->$map[STREAMER_VAR], $decoded);
+
+                            if(!$decoder->getElementEndTag())
+                                return false;
+                        }
+                        if(!$decoder->getElementEndTag())
+                            return false;
+                    } else { // Handle single value
+                        if(isset($map[STREAMER_TYPE])) {
+                            // Complex type, decode recursively
+                            if($map[STREAMER_TYPE] == STREAMER_TYPE_DATE || $map[STREAMER_TYPE] == STREAMER_TYPE_DATE_DASHES) {
+                                $decoded = $this->parseDate($decoder->getElementContent());
+                                if(!$decoder->getElementEndTag())
+                                    return false;
+                            } else if($map[STREAMER_TYPE] == STREAMER_TYPE_HEX) {
+                                $decoded = hex2bin($decoder->getElementContent());
+                                if(!$decoder->getElementEndTag())
+                                    return false;
+                            } else {
+                                $subdecoder = new $map[STREAMER_TYPE]();
+                                if($subdecoder->decode($decoder) === false)
+                                    return false;
+
+                                $decoded = $subdecoder;
+
+                                if(!$decoder->getElementEndTag()) {
+                                    debug("No end tag for " . $entity[EN_TAG]);
+                                    return false;
+                                }
+                            }
+                        } else {
+                            // Simple type, just get content
+                            $decoded = $decoder->getElementContent();
+
+                            if($decoded === false) {
+                                debug("Unable to get content for " . $entity[EN_TAG]);
+                                return false;
+                            }
+
+                            if(!$decoder->getElementEndTag()) {
+                                debug("Unable to get end tag for " . $entity[EN_TAG]);
+                                return false;
+                            }
+                        }
+                        // $decoded now contains data object (or string)
+                        $this->$map[STREAMER_VAR] = $decoded;
+                    }
+                }
+            }
+            else if($entity[EN_TYPE] == EN_TYPE_ENDTAG) {
+                $decoder->ungetElement($entity);
+                break;
+            }
+            else {
+                debug("Unexpected content in type");
+                break;
+            }
+        }
+    }
+
+    // Encodes this object and any subobjects - output is ordered according to mapping
+    function encode(&$encoder) {
+        $attributes = isset($this->attributes) ? $this->attributes : array();
+
+        foreach($this->_mapping as $tag => $map) {
+            if(isset($this->$map[STREAMER_VAR])) {
+                // Variable is available
+                if(is_object($this->$map[STREAMER_VAR])) {
+                    // Subobjects can do their own encoding
+                    $encoder->startTag($tag);
+                    $this->$map[STREAMER_VAR]->encode($encoder);
+                    $encoder->endTag();
+                } else if(isset($map[STREAMER_ARRAY])) {
+                    // Array of objects
+                    $encoder->startTag($tag); // Outputs array container (eg Attachments)
+                    foreach ($this->$map[STREAMER_VAR] as $element) {
+                        if(is_object($element)) {
+                            $encoder->startTag($map[STREAMER_ARRAY]); // Outputs object container (eg Attachment)
+                            $element->encode($encoder);
+                            $encoder->endTag();
+                        } else {
+                            if(strlen($element) == 0)
+                                  // Do not output empty items. Not sure if we should output an empty tag with $encoder->startTag($map[STREAMER_ARRAY], false, true);
+                                  ;
+                            else {
+                                $encoder->startTag($map[STREAMER_ARRAY]);
+                                $encoder->content($element);
+                                $encoder->endTag();
+                            }
+                        }
+                    }
+                    $encoder->endTag();
+                } else {
+                    // Simple type
+                    if(strlen($this->$map[STREAMER_VAR]) == 0) {
+                          // Do not output empty items. See above: $encoder->startTag($tag, false, true);
+                        continue;
+                    } else
+                        $encoder->startTag($tag);
+
+                    if(isset($map[STREAMER_TYPE]) && ($map[STREAMER_TYPE] == STREAMER_TYPE_DATE || $map[STREAMER_TYPE] == STREAMER_TYPE_DATE_DASHES)) {
+                        if($this->$map[STREAMER_VAR] != 0) // don't output 1-1-1970
+                            $encoder->content($this->formatDate($this->$map[STREAMER_VAR], $map[STREAMER_TYPE]));
+                    } else if(isset($map[STREAMER_TYPE]) && $map[STREAMER_TYPE] == STREAMER_TYPE_HEX) {
+                        $encoder->content(strtoupper(bin2hex($this->$map[STREAMER_VAR])));
+                    } else if(isset($map[STREAMER_TYPE]) && $map[STREAMER_TYPE] == STREAMER_TYPE_MAPI_STREAM) {
+                        $encoder->content($this->$map[STREAMER_VAR]);
+                    } else {
+                        $encoder->content($this->$map[STREAMER_VAR]);
+                    }
+                    $encoder->endTag();
+                }
+            }
+        }
+        // Output our own content
+        if(isset($this->content))
+            $encoder->content($this->content);
+
+    }
+
+
+
+    // Oh yeah. This is beautiful. Exchange outputs date fields differently in calendar items
+    // and emails. We could just always send one or the other, but unfortunately nokia's 'Mail for
+    // exchange' depends on this quirk. So we have to send a different date type depending on where
+    // it's used. Sigh.
+    function formatDate($ts, $type) {
+        if($type == STREAMER_TYPE_DATE)
+            return gmstrftime("%Y%m%dT%H%M%SZ", $ts);
+        else if($type == STREAMER_TYPE_DATE_DASHES)
+            return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts);
+    }
+
+    function parseDate($ts) {
+        if(preg_match("/(\d{4})[^0-9]*(\d{2})[^0-9]*(\d{2})T(\d{2})[^0-9]*(\d{2})[^0-9]*(\d{2})(.\d+)?Z/", $ts, $matches)) {
+            if ($matches[1] >= 2038){
+                $matches[1] = 2038;
+                $matches[2] = 1;
+                $matches[3] = 18;
+                $matches[4] = $matches[5] = $matches[6] = 0;
+            }
+            return gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
+        }
+        return 0;
+    }
+};
+
+?>
Index: /contrib/z-push/compat.php
===================================================================
--- /contrib/z-push/compat.php	(revision 4898)
+++ /contrib/z-push/compat.php	(revision 4898)
@@ -0,0 +1,27 @@
+<?php
+/***********************************************
+* File      :   compat.php
+* Project   :   Z-Push
+* Descr     :   Help function for files
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+if (!function_exists("file_put_contents")) {
+    function file_put_contents($n,$d) {
+        $f=@fopen($n,"w");
+        if (!$f) {
+            return false;
+        } else {
+            fwrite($f,$d);
+            fclose($f);
+            return true;
+        }
+    }
+}
+
+?>
Index: /contrib/z-push/version.php
===================================================================
--- /contrib/z-push/version.php	(revision 4898)
+++ /contrib/z-push/version.php	(revision 4898)
@@ -0,0 +1,15 @@
+<?php
+/***********************************************
+* File      :   version.php
+* Project   :   Z-Push
+* Descr     :   version number
+*
+* Created   :   18.04.2008
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+$zpush_version = "1.4.3 486";
+?>
Index: /contrib/z-push/request.php
===================================================================
--- /contrib/z-push/request.php	(revision 4898)
+++ /contrib/z-push/request.php	(revision 4898)
@@ -0,0 +1,1710 @@
+<?php
+/***********************************************
+* File      :   request.php
+* Project   :   Z-Push
+* Descr     :   This file contains the actual
+*               request handling routines.
+*               The request handlers are optimised
+*               so that as little as possible
+*               data is kept-in-memory, and all
+*               output data is directly streamed
+*               to the client, while also streaming
+*               input data from the client.
+*
+* Created   :   01.10.2007
+*
+*  Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+include_once("proto.php");
+include_once("wbxml.php");
+include_once("statemachine.php");
+include_once("backend/backend.php");
+include_once("memimporter.php");
+include_once("streamimporter.php");
+include_once("zpushdtd.php");
+include_once("zpushdefs.php");
+include_once("include/utils.php");
+
+function GetObjectClassFromFolderClass($folderclass)
+{
+    $classes = array ( "Email" => "syncmail", "Contacts" => "synccontact", "Calendar" => "syncappointment", "Tasks" => "synctask" );
+
+    return $classes[$folderclass];
+}
+
+function HandleMoveItems($backend, $protocolversion) {
+    global $zpushdtd;
+    global $input, $output;
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_MOVE_MOVES))
+        return false;
+
+    $moves = array();
+    while($decoder->getElementStartTag(SYNC_MOVE_MOVE)) {
+        $move = array();
+        if($decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) {
+            $move["srcmsgid"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                break;
+        }
+        if($decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) {
+            $move["srcfldid"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                break;
+        }
+        if($decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) {
+            $move["dstfldid"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                break;
+        }
+        array_push($moves, $move);
+
+        if(!$decoder->getElementEndTag())
+            return false;
+    }
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    $encoder->StartWBXML();
+
+    $encoder->startTag(SYNC_MOVE_MOVES);
+
+    foreach($moves as $move) {
+        $encoder->startTag(SYNC_MOVE_RESPONSE);
+        $encoder->startTag(SYNC_MOVE_SRCMSGID);
+        $encoder->content($move["srcmsgid"]);
+        $encoder->endTag();
+
+        $importer = $backend->GetContentsImporter($move["srcfldid"]);
+        $result = $importer->ImportMessageMove($move["srcmsgid"], $move["dstfldid"]);
+        // We discard the importer state for now.
+
+        $encoder->startTag(SYNC_MOVE_STATUS);
+        $encoder->content($result ? 3 : 1);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_MOVE_DSTMSGID);
+        $encoder->content(is_string($result)?$result:$move["srcmsgid"]);
+        $encoder->endTag();
+        $encoder->endTag();
+    }
+
+    $encoder->endTag();
+    return true;
+}
+
+function HandleNotify($backend, $protocolversion) {
+    global $zpushdtd;
+    global $input, $output;
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_AIRNOTIFY_NOTIFY))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_AIRNOTIFY_DEVICEINFO))
+        return false;
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    $encoder->StartWBXML();
+
+    $encoder->startTag(SYNC_AIRNOTIFY_NOTIFY);
+    {
+        $encoder->startTag(SYNC_AIRNOTIFY_STATUS);
+        $encoder->content(1);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_AIRNOTIFY_VALIDCARRIERPROFILES);
+        $encoder->endTag();
+    }
+
+    $encoder->endTag();
+
+    return true;
+
+}
+
+// Handle GetHierarchy method - simply returns current hierarchy of all folders
+function HandleGetHierarchy($backend, $protocolversion, $devid) {
+    global $zpushdtd;
+    global $output;
+
+    // Input is ignored, no data is sent by the PIM
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    $folders = $backend->GetHierarchy();
+
+    if(!$folders)
+        return false;
+
+    // save folder-ids for fourther syncing
+    _saveFolderData($devid, $folders);
+
+    $encoder->StartWBXML();
+    $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERS);
+
+    foreach ($folders as $folder) {
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDER);
+        $folder->encode($encoder);
+        $encoder->endTag();
+    }
+
+    $encoder->endTag();
+    return true;
+}
+
+// Handles a 'FolderSync' method - receives folder updates, and sends reply with
+// folder changes on the server
+function HandleFolderSync($backend, $protocolversion) {
+    global $zpushdtd;
+    global $input, $output;
+
+    // Maps serverid -> clientid for items that are received from the PIM
+    $map = array();
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    // Parse input
+
+    if(!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY))
+        return false;
+
+    $synckey = $decoder->getElementContent();
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    // First, get the syncstate that is associated with this synckey
+    $statemachine = new StateMachine();
+
+    // The state machine will discard any sync states before this one, as they are no
+    // longer required
+    $syncstate = $statemachine->getSyncState($synckey);
+
+    // additional information about already seen folders
+    $sfolderstate = $statemachine->getSyncState("s".$synckey);
+    
+    if (!$sfolderstate) {
+        $foldercache = array();
+        if ($sfolderstate === false)
+            debugLog("Error: FolderChacheState for state 's". $synckey ."' not found. Reinitializing...");
+    }
+    else {
+    	$foldercache = unserialize($sfolderstate);
+
+    	// transform old seenfolder array
+    	if (array_key_exists("0", $foldercache)) {
+    		$tmp = array();
+    		foreach($foldercache as $s) $tmp[$s] = new SyncFolder();
+    		$foldercache = $tmp;
+    	}
+    }
+        
+    // We will be saving the sync state under 'newsynckey'
+    $newsynckey = $statemachine->getNewSyncKey($synckey);
+    $changes = false;
+    
+    if($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
+        // Ignore <Count> if present
+        if($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
+            $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        // Process the changes (either <Add>, <Modify>, or <Remove>)
+        $element = $decoder->getElement();
+
+        if($element[EN_TYPE] != EN_TYPE_STARTTAG)
+            return false;
+
+        while(1) {
+            $folder = new SyncFolder();
+            if(!$folder->decode($decoder))
+                break;
+
+            // Configure importer with last state
+            $importer = $backend->GetHierarchyImporter();
+            $importer->Config($syncstate);
+
+
+            switch($element[EN_TAG]) {
+                case SYNC_ADD:
+                case SYNC_MODIFY:
+                    $serverid = $importer->ImportFolderChange($folder);
+                    // add folder to the serverflags
+                    $foldercache[$serverid] = $folder;
+                    $changes = true;
+                    break;
+                case SYNC_REMOVE:
+                    $serverid = $importer->ImportFolderDeletion($folder);
+                    $changes = true;
+                    // remove folder from the folderchache
+                    if (array_key_exists($serverid, $foldercache)) 
+                        unset($foldercache[$serverid]);
+                    break;
+            }
+
+            if($serverid)
+                $map[$serverid] = $folder->clientid;
+        }
+
+        if(!$decoder->getElementEndTag())
+            return false;
+    }
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    // We have processed incoming foldersync requests, now send the PIM
+    // our changes
+
+    // The MemImporter caches all imports in-memory, so we can send a change count
+    // before sending the actual data. As the amount of data done in this operation
+    // is rather low, this is not memory problem. Note that this is not done when
+    // sync'ing messages - we let the exporter write directly to WBXML.
+    $importer = new ImportHierarchyChangesMem($foldercache);
+
+    // Request changes from backend, they will be sent to the MemImporter passed as the first
+    // argument, which stores them in $importer. Returns the new sync state for this exporter.
+    $exporter = $backend->GetExporter();
+
+    $exporter->Config($importer, false, false, $syncstate, 0, 0);
+
+    while(is_array($exporter->Synchronize()));
+
+    // Output our WBXML reply now
+    $encoder->StartWBXML();
+
+    $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
+    {
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
+        $encoder->content(1);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
+        // only send new synckey if changes were processed or there are outgoing changes
+        $encoder->content((($changes || $importer->count > 0)?$newsynckey:$synckey));
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
+        {
+            $encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
+            $encoder->content($importer->count);
+            $encoder->endTag();
+
+            if(count($importer->changed) > 0) {
+                foreach($importer->changed as $folder) {
+                	// send a modify flag if the folder is already known on the device
+                	if (isset($folder->serverid) && array_key_exists($folder->serverid, $foldercache) !== false)
+                        $encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
+                	else 
+                        $encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
+                    $foldercache[$folder->serverid] = $folder;
+                    
+                    $folder->encode($encoder);
+                    $encoder->endTag();
+                }
+            }
+
+            if(count($importer->deleted) > 0) {
+                foreach($importer->deleted as $folder) {
+                    $encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
+                        $encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
+                            $encoder->content($folder);
+                        $encoder->endTag();
+                    $encoder->endTag();
+
+                    // remove folder from the folderchache
+                    if (array_key_exists($folder, $foldercache)) 
+                        unset($foldercache[$folder]);
+                }
+            }
+        }
+        $encoder->endTag();
+    }
+    $encoder->endTag();
+
+    // Save the sync state for the next time
+    $syncstate = $exporter->GetState();
+    $statemachine->setSyncState($newsynckey, $syncstate);
+    $statemachine->setSyncState("s".$newsynckey, serialize($foldercache));
+
+
+    return true;
+}
+
+function HandleSync($backend, $protocolversion, $devid) {
+    global $zpushdtd;
+    global $input, $output;
+
+    // Contains all containers requested
+    $collections = array();
+
+    // Init WBXML decoder
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+
+    // Init state machine
+    $statemachine = new StateMachine();
+
+    // Start decode
+    if(!$decoder->getElementStartTag(SYNC_SYNCHRONIZE))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_FOLDERS))
+        return false;
+
+    while($decoder->getElementStartTag(SYNC_FOLDER))
+    {
+        $collection = array();
+        $collection["truncation"] = SYNC_TRUNCATION_ALL;
+        $collection["clientids"] = array();
+        $collection["fetchids"] = array();
+
+        if(!$decoder->getElementStartTag(SYNC_FOLDERTYPE))
+            return false;
+
+        $collection["class"] = $decoder->getElementContent();
+        debugLog("Sync folder:{$collection["class"]}");
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        if(!$decoder->getElementStartTag(SYNC_SYNCKEY))
+            return false;
+
+        $collection["synckey"] = $decoder->getElementContent();
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        if($decoder->getElementStartTag(SYNC_FOLDERID)) {
+            $collection["collectionid"] = $decoder->getElementContent();
+
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if($decoder->getElementStartTag(SYNC_SUPPORTED)) {
+            while(1) {
+                $el = $decoder->getElement();
+                if($el[EN_TYPE] == EN_TYPE_ENDTAG)
+                    break;
+            }
+        }
+
+        if($decoder->getElementStartTag(SYNC_DELETESASMOVES))
+            $collection["deletesasmoves"] = true;
+
+        if($decoder->getElementStartTag(SYNC_GETCHANGES))
+            $collection["getchanges"] = true;
+
+        if($decoder->getElementStartTag(SYNC_MAXITEMS)) {
+            $collection["maxitems"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if($decoder->getElementStartTag(SYNC_OPTIONS)) {
+            while(1) {
+                if($decoder->getElementStartTag(SYNC_FILTERTYPE)) {
+                    $collection["filtertype"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+                if($decoder->getElementStartTag(SYNC_TRUNCATION)) {
+                    $collection["truncation"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+                if($decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
+                    $collection["rtftruncation"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+
+                if($decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
+                    $collection["mimesupport"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+
+                if($decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
+                    $collection["mimetruncation"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+
+                if($decoder->getElementStartTag(SYNC_CONFLICT)) {
+                    $collection["conflict"] = $decoder->getElementContent();
+                    if(!$decoder->getElementEndTag())
+                        return false;
+                }
+                $e = $decoder->peek();
+                if($e[EN_TYPE] == EN_TYPE_ENDTAG) {
+                    $decoder->getElementEndTag();
+                    break;
+                }
+            }
+        }
+
+        // compatibility mode - get folderid from the state directory
+        if (!isset($collection["collectionid"])) {
+            $collection["collectionid"] = _getFolderID($devid, $collection["class"]);
+        }
+
+        // compatibility mode - set default conflict behavior if no conflict resolution algorithm is set (OVERWRITE_PIM)
+        if (!isset($collection["conflict"])) {
+            $collection["conflict"] = 1;
+        }
+
+        //compatibility mode - set maxitems if the client doesn't send it as it breaks some devices
+        if (!isset($collection["maxitems"])) {
+            $collection["maxitems"] = 100;
+        }
+
+        // Get our sync state for this collection
+        $collection["syncstate"] = $statemachine->getSyncState($collection["synckey"]);
+        if($decoder->getElementStartTag(SYNC_PERFORM)) {
+
+            // Configure importer with last state
+            $importer = $backend->GetContentsImporter($collection["collectionid"]);
+            $importer->Config($collection["syncstate"], $collection["conflict"]);
+
+            $nchanges = 0;
+            while(1) {
+                $element = $decoder->getElement(); // MODIFY or REMOVE or ADD or FETCH
+
+                if($element[EN_TYPE] != EN_TYPE_STARTTAG) {
+                    $decoder->ungetElement($element);
+                    break;
+                }
+
+                $nchanges++;
+
+                if($decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
+                    $serverid = $decoder->getElementContent();
+
+                    if(!$decoder->getElementEndTag()) // end serverid
+                        return false;
+                } else {
+                    $serverid = false;
+                }
+
+                if($decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
+                    $clientid = $decoder->getElementContent();
+
+                    if(!$decoder->getElementEndTag()) // end clientid
+                        return false;
+                } else {
+                    $clientid = false;
+                }
+
+                // Get application data if available
+                if($decoder->getElementStartTag(SYNC_DATA)) {
+                    switch($collection["class"]) {
+                        case "Email":
+                            $appdata = new SyncMail();
+                            $appdata->decode($decoder);
+                            break;
+                        case "Contacts":
+                            $appdata = new SyncContact($protocolversion);
+                            $appdata->decode($decoder);
+                            break;
+                        case "Calendar":
+                            $appdata = new SyncAppointment();
+                            $appdata->decode($decoder);
+                            break;
+                        case "Tasks":
+                            $appdata = new SyncTask();
+                            $appdata->decode($decoder);
+                            break;
+                    }
+                    if(!$decoder->getElementEndTag()) // end applicationdata
+                        return false;
+
+                }
+
+                switch($element[EN_TAG]) {
+                    case SYNC_MODIFY:
+                        if(isset($appdata)) {
+                            if(isset($appdata->read)) // Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag.
+                                $importer->ImportMessageReadFlag($serverid, $appdata->read);
+                            else
+                                $importer->ImportMessageChange($serverid, $appdata);
+                            $collection["importedchanges"] = true;
+                        }
+                        break;
+                    case SYNC_ADD:
+                        if(isset($appdata)) {
+                            $id = $importer->ImportMessageChange(false, $appdata);
+
+                            if($clientid && $id) {
+                                $collection["clientids"][$clientid] = $id;
+                                $collection["importedchanges"] = true;
+                            }
+                        }
+                        break;
+                    case SYNC_REMOVE:
+                        if(isset($collection["deletesasmoves"])) {
+                            $folderid = $backend->GetWasteBasket();
+
+                            if($folderid) {
+                                $importer->ImportMessageMove($serverid, $folderid);
+                                $collection["importedchanges"] = true;
+                                break;
+                            }
+                        }
+
+                        $importer->ImportMessageDeletion($serverid);
+                        $collection["importedchanges"] = true;
+                        break;
+                    case SYNC_FETCH:
+                        array_push($collection["fetchids"], $serverid);
+                        break;
+                }
+
+                if(!$decoder->getElementEndTag()) // end change/delete/move
+                    return false;
+            }
+
+            debugLog("Processed $nchanges incoming changes");
+
+            // Save the updated state, which is used for the exporter later
+            $collection["syncstate"] = $importer->getState();
+
+
+            if(!$decoder->getElementEndTag()) // end commands
+                return false;
+        }
+
+        if(!$decoder->getElementEndTag()) // end collection
+            return false;
+
+        array_push($collections, $collection);
+    }
+
+    if(!$decoder->getElementEndTag()) // end collections
+        return false;
+
+    if(!$decoder->getElementEndTag()) // end sync
+        return false;
+
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+    $encoder->startWBXML();
+
+    $encoder->startTag(SYNC_SYNCHRONIZE);
+    {
+        $encoder->startTag(SYNC_FOLDERS);
+        {
+            foreach($collections as $collection) {
+            	// initialize exporter to get changecount
+            	$changecount = 0;
+            	if(isset($collection["getchanges"])) {
+                    // Use the state from the importer, as changes may have already happened
+                    $exporter = $backend->GetExporter($collection["collectionid"]);
+
+                    $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : false;
+                    $exporter->Config($importer, $collection["class"], $filtertype, $collection["syncstate"], 0, $collection["truncation"]);
+
+                    $changecount = $exporter->GetChangeCount();
+            	}
+            	
+                // Get a new sync key to output to the client if any changes have been requested or will be send
+                if (isset($collection["importedchanges"]) || $changecount > 0 || $collection["synckey"] == "0")
+                    $collection["newsynckey"] = $statemachine->getNewSyncKey($collection["synckey"]);
+
+                $encoder->startTag(SYNC_FOLDER);
+
+                $encoder->startTag(SYNC_FOLDERTYPE);
+                $encoder->content($collection["class"]);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_SYNCKEY);
+
+                if(isset($collection["newsynckey"]))
+                    $encoder->content($collection["newsynckey"]);
+                else
+                    $encoder->content($collection["synckey"]);
+
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_FOLDERID);
+                $encoder->content($collection["collectionid"]);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_STATUS);
+                $encoder->content(1);
+                $encoder->endTag();
+
+                //check the mimesupport because we need it for advanced emails
+                $mimesupport = isset($collection['mimesupport']) ? $collection['mimesupport'] : 0;
+
+                // Output server IDs for new items we received from the PDA
+                if(isset($collection["clientids"]) || count($collection["fetchids"]) > 0) {
+                    $encoder->startTag(SYNC_REPLIES);
+                    foreach($collection["clientids"] as $clientid => $serverid) {
+                        $encoder->startTag(SYNC_ADD);
+                        $encoder->startTag(SYNC_CLIENTENTRYID);
+                        $encoder->content($clientid);
+                        $encoder->endTag();
+                        $encoder->startTag(SYNC_SERVERENTRYID);
+                        $encoder->content($serverid);
+                        $encoder->endTag();
+                        $encoder->startTag(SYNC_STATUS);
+                        $encoder->content(1);
+                        $encoder->endTag();
+                        $encoder->endTag();
+                    }
+                    foreach($collection["fetchids"] as $id) {
+                        $data = $backend->Fetch($collection["collectionid"], $id, $mimesupport);
+                        if($data !== false) {
+                            $encoder->startTag(SYNC_FETCH);
+                            $encoder->startTag(SYNC_SERVERENTRYID);
+                            $encoder->content($id);
+                            $encoder->endTag();
+                            $encoder->startTag(SYNC_STATUS);
+                            $encoder->content(1);
+                            $encoder->endTag();
+                            $encoder->startTag(SYNC_DATA);
+                            $data->encode($encoder);
+                            $encoder->endTag();
+                            $encoder->endTag();
+                        } else {
+                            debugLog("unable to fetch $id");
+                        }
+                    }
+                    $encoder->endTag();
+                }
+
+                if(isset($collection["getchanges"])) {
+                    // exporter already intialized
+
+                    if($changecount > $collection["maxitems"]) {
+                        $encoder->startTag(SYNC_MOREAVAILABLE, false, true);
+                    }
+
+                    // Output message changes per folder
+                    $encoder->startTag(SYNC_PERFORM);
+
+                    // Stream the changes to the PDA
+                    $importer = new ImportContentsChangesStream($encoder, GetObjectClassFromFolderClass($collection["class"]));
+
+                    $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : 0;
+
+                    $n = 0;
+                    while(1) {
+                        $progress = $exporter->Synchronize();
+                        if(!is_array($progress))
+                            break;
+                        $n++;
+
+                        if($n >= $collection["maxitems"]) {
+                        	debugLog("Exported maxItems of messages: ". $collection["maxitems"] . " - more available");
+                            break;
+                        }
+
+                    }
+                    $encoder->endTag();
+                }
+
+                $encoder->endTag();
+
+                // Save the sync state for the next time
+                if(isset($collection["newsynckey"])) {
+                    if (isset($exporter) && $exporter)
+                        $state = $exporter->GetState();
+
+                    // nothing exported, but possible imported
+                    else if (isset($importer) && $importer)
+                        $state = $importer->GetState();
+
+                    // if a new request without state information (hierarchy) save an empty state
+                    else if ($collection["synckey"] == "0")
+                        $state = "";
+
+                    if (isset($state)) $statemachine->setSyncState($collection["newsynckey"], $state);
+                    else debugLog("error saving " . $collection["newsynckey"] . " - no state information available");
+                }
+            }
+        }
+        $encoder->endTag();
+    }
+    $encoder->endTag();
+
+    return true;
+}
+
+function HandleGetItemEstimate($backend, $protocolversion, $devid) {
+    global $zpushdtd;
+    global $input, $output;
+
+    $collections = array();
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS))
+        return false;
+
+    while($decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) {
+        $collection = array();
+
+        if(!$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE))
+            return false;
+
+        $class = $decoder->getElementContent();
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        if($decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
+            $collectionid = $decoder->getElementContent();
+
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if(!$decoder->getElementStartTag(SYNC_FILTERTYPE))
+            return false;
+
+        $filtertype = $decoder->getElementContent();
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        if(!$decoder->getElementStartTag(SYNC_SYNCKEY))
+            return false;
+
+        $synckey = $decoder->getElementContent();
+
+        if(!$decoder->getElementEndTag())
+            return false;
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        // compatibility mode - get folderid from the state directory
+        if (!isset($collectionid)) {
+            $collectionid = _getFolderID($devid, $class);
+        }
+
+        $collection = array();
+        $collection["synckey"] = $synckey;
+        $collection["class"] = $class;
+        $collection["filtertype"] = $filtertype;
+        $collection["collectionid"] = $collectionid;
+
+        array_push($collections, $collection);
+    }
+
+    $encoder->startWBXML();
+
+    $encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
+    {
+        foreach($collections as $collection) {
+            $encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
+            {
+                $encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
+                $encoder->content(1);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
+                {
+                    $encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
+                    $encoder->content($collection["class"]);
+                    $encoder->endTag();
+
+                    $encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
+                    $encoder->content($collection["collectionid"]);
+                    $encoder->endTag();
+
+                    $encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
+
+                    $importer = new ImportContentsChangesMem();
+
+                    $statemachine = new StateMachine();
+                    $syncstate = $statemachine->getSyncState($collection["synckey"]);
+
+                    $exporter = $backend->GetExporter($collection["collectionid"]);
+                    $exporter->Config($importer, $collection["class"], $collection["filtertype"], $syncstate, 0, 0);
+
+                    $encoder->content($exporter->GetChangeCount());
+
+                    $encoder->endTag();
+                }
+                $encoder->endTag();
+            }
+            $encoder->endTag();
+        }
+    }
+    $encoder->endTag();
+
+    return true;
+}
+
+function HandleGetAttachment($backend, $protocolversion) {
+    $attname = $_GET["AttachmentName"];
+
+    if(!isset($attname))
+        return false;
+
+    header("Content-Type: application/octet-stream");
+
+    $backend->GetAttachmentData($attname);
+
+    return true;
+}
+
+function HandlePing($backend, $devid) {
+    global $zpushdtd, $input, $output;
+    global $user, $auth_pw;
+    $timeout = 5;
+
+    debugLog("Ping received");
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    $collections = array();
+    $lifetime = 0;
+
+    // Get previous defaults if they exist
+    $file = BASE_PATH . STATE_DIR . "/" . $devid;
+    if (file_exists($file)) {
+        $ping = unserialize(file_get_contents($file));
+        $collections = $ping["collections"];
+        $lifetime = $ping["lifetime"];
+    }
+
+    if($decoder->getElementStartTag(SYNC_PING_PING)) {
+        debugLog("Ping init");
+        if($decoder->getElementStartTag(SYNC_PING_LIFETIME)) {
+            $lifetime = $decoder->getElementContent();
+            $decoder->getElementEndTag();
+        }
+
+        if($decoder->getElementStartTag(SYNC_PING_FOLDERS)) {
+            // avoid ping init if not necessary
+            $saved_collections = $collections;
+
+            $collections = array();
+
+            while($decoder->getElementStartTag(SYNC_PING_FOLDER)) {
+                $collection = array();
+
+                if($decoder->getElementStartTag(SYNC_PING_SERVERENTRYID)) {
+                    $collection["serverid"] = $decoder->getElementContent();
+                    $decoder->getElementEndTag();
+                }
+                if($decoder->getElementStartTag(SYNC_PING_FOLDERTYPE)) {
+                    $collection["class"] = $decoder->getElementContent();
+                    $decoder->getElementEndTag();
+                }
+
+                $decoder->getElementEndTag();
+
+                // initialize empty state
+                $collection["state"] = "";
+
+                // try to find old state in saved states
+                foreach ($saved_collections as $saved_col) {
+                    if ($saved_col["serverid"] == $collection["serverid"] && $saved_col["class"] == $collection["class"]) {
+                        $collection["state"] = $saved_col["state"];
+                        debugLog("reusing saved state for ". $collection["class"]);
+                        break;
+                    }
+                }
+
+                if ($collection["state"] == "")
+                    debugLog("empty state for ". $collection["class"]);
+
+                // Create start state for this collection
+                $exporter = $backend->GetExporter($collection["serverid"]);
+                $importer = false;
+                $exporter->Config($importer, false, false, $collection["state"], BACKEND_DISCARD_DATA, 0);
+                while(is_array($exporter->Synchronize()));
+                $collection["state"] = $exporter->GetState();
+                array_push($collections, $collection);
+            }
+
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if(!$decoder->getElementEndTag())
+            return false;
+    }
+
+    $changes = array();
+    $dataavailable = false;
+
+    debugLog("Waiting for changes... (lifetime $lifetime)");
+    // Wait for something to happen
+    for($n=0;$n<$lifetime / $timeout; $n++ ) {
+        //check the remote wipe status
+        if (PROVISIONING === true) {
+	        $rwstatus = $backend->getDeviceRWStatus($user, $auth_pw, $devid);
+	        if ($rwstatus == SYNC_PROVISION_RWSTATUS_PENDING || $rwstatus == SYNC_PROVISION_RWSTATUS_WIPED) {
+	            //return 7 because it forces folder sync
+	            $pingstatus = 7;
+	            break;
+	        }
+        }
+
+        if(count($collections) == 0) {
+            $error = 1;
+            break;
+        }
+
+        for($i=0;$i<count($collections);$i++) {
+            $collection = $collections[$i];
+
+            $exporter = $backend->GetExporter($collection["serverid"]);
+            $state = $collection["state"];
+            $importer = false;
+            $ret = $exporter->Config($importer, false, false, $state, BACKEND_DISCARD_DATA, 0);
+
+            // stop ping if exporter can not be configured (e.g. after Zarafa-server restart)
+            if ($ret === false ) {
+                // force "ping" to stop
+                $n = $lifetime / $timeout;
+                debugLog("Ping error: Exporter can not be configured. Waiting 30 seconds before ping is retried.");
+                sleep(30);
+                break;
+            }
+
+            $changecount = $exporter->GetChangeCount();
+
+            if($changecount > 0) {
+                $dataavailable = true;
+                $changes[$collection["serverid"]] = $changecount;
+            }
+
+            // Discard any data
+            while(is_array($exporter->Synchronize()));
+
+            // Record state for next Ping
+            $collections[$i]["state"] = $exporter->GetState();
+        }
+
+        if($dataavailable) {
+            debugLog("Found change");
+            break;
+        }
+
+        sleep($timeout);
+    }
+
+    $encoder->StartWBXML();
+
+    $encoder->startTag(SYNC_PING_PING);
+    {
+        $encoder->startTag(SYNC_PING_STATUS);
+        if(isset($error))
+            $encoder->content(3);
+        elseif (isset($pingstatus))
+            $encoder->content($pingstatus);
+        else
+            $encoder->content(count($changes) > 0 ? 2 : 1);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_PING_FOLDERS);
+        foreach($collections as $collection) {
+            if(isset($changes[$collection["serverid"]])) {
+                $encoder->startTag(SYNC_PING_FOLDER);
+                $encoder->content($collection["serverid"]);
+                $encoder->endTag();
+            }
+        }
+        $encoder->endTag();
+    }
+    $encoder->endTag();
+
+    // Save the ping request state for this device
+    file_put_contents(BASE_PATH . "/" . STATE_DIR . "/" . $devid, serialize(array("lifetime" => $lifetime, "collections" => $collections)));
+
+    return true;
+}
+
+function HandleSendMail($backend, $protocolversion) {
+    // All that happens here is that we receive an rfc822 message on stdin
+    // and just forward it to the backend. We provide no output except for
+    // an OK http reply
+
+    global $input;
+
+    $rfc822 = readStream($input);
+
+    return $backend->SendMail($rfc822);
+}
+
+function HandleSmartForward($backend, $protocolversion) {
+    global $input;
+    // SmartForward is a normal 'send' except that you should attach the
+    // original message which is specified in the URL
+
+    $rfc822 = readStream($input);
+
+    if(isset($_GET["ItemId"]))
+        $orig = $_GET["ItemId"];
+    else
+        $orig = false;
+
+    if(isset($_GET["CollectionId"]))
+        $parent = $_GET["CollectionId"];
+    else
+        $parent = false;
+
+    return $backend->SendMail($rfc822, $orig, false, $parent);
+}
+
+function HandleSmartReply($backend, $protocolversion) {
+    global $input;
+    // Smart reply should add the original message to the end of the message body
+
+    $rfc822 = readStream($input);
+
+    if(isset($_GET["ItemId"]))
+        $orig = $_GET["ItemId"];
+    else
+        $orig = false;
+
+    if(isset($_GET["CollectionId"]))
+        $parent = $_GET["CollectionId"];
+    else
+        $parent = false;
+
+    return $backend->SendMail($rfc822, false, $orig, $parent);
+}
+
+function HandleFolderCreate($backend, $protocolversion) {
+    global $zpushdtd;
+    global $input, $output;
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    $el = $decoder->getElement();
+
+    if($el[EN_TYPE] != EN_TYPE_STARTTAG)
+        return false;
+
+    $create = $update = $delete = false;
+
+    if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERCREATE)
+        $create = true;
+    else if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERUPDATE)
+        $update = true;
+    else if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERDELETE)
+        $delete = true;
+
+    if(!$create && !$update && !$delete)
+        return false;
+
+    // SyncKey
+    if(!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY))
+        return false;
+    $synckey = $decoder->getElementContent();
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    // ServerID
+    $serverid = false;
+    if($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID)) {
+        $serverid = $decoder->getElementContent();
+        if(!$decoder->getElementEndTag())
+            return false;
+    }
+
+    // when creating or updating more information is necessary
+    if (!$delete) {
+	    // Parent
+	    $parentid = false;
+	    if($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_PARENTID)) {
+	        $parentid = $decoder->getElementContent();
+	        if(!$decoder->getElementEndTag())
+	            return false;
+	    }
+
+	    // Displayname
+	    if(!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_DISPLAYNAME))
+	        return false;
+	    $displayname = $decoder->getElementContent();
+	    if(!$decoder->getElementEndTag())
+	        return false;
+
+	    // Type
+	    $type = false;
+	    if($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_TYPE)) {
+	        $type = $decoder->getElementContent();
+	        if(!$decoder->getElementEndTag())
+	            return false;
+	    }
+    }
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    // Get state of hierarchy
+    $statemachine = new StateMachine();
+    $syncstate = $statemachine->getSyncState($synckey);
+    $newsynckey = $statemachine->getNewSyncKey($synckey);
+
+    // additional information about already seen folders
+    $seenfolders = unserialize($statemachine->getSyncState("s".$synckey));
+    if (!$seenfolders) $seenfolders = array();
+
+    // Configure importer with last state
+    $importer = $backend->GetHierarchyImporter();
+    $importer->Config($syncstate);
+
+    if (!$delete) {
+	    // Send change
+	    $serverid = $importer->ImportFolderChange($serverid, $parentid, $displayname, $type);
+    }
+    else {
+    	// delete folder
+    	$deletedstat = $importer->ImportFolderDeletion($serverid, 0);
+    }
+
+    $encoder->startWBXML();
+    if ($create) {
+    	// add folder id to the seen folders
+        $seenfolders[] = $serverid;
+
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERCREATE);
+        {
+            {
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
+                $encoder->content(1);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
+                $encoder->content($newsynckey);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
+                $encoder->content($serverid);
+                $encoder->endTag();
+            }
+            $encoder->endTag();
+        }
+        $encoder->endTag();
+    }
+
+    elseif ($update) {
+
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERUPDATE);
+        {
+            {
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
+                $encoder->content(1);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
+                $encoder->content($newsynckey);
+                $encoder->endTag();
+            }
+            $encoder->endTag();
+        }
+    }
+    elseif ($delete) {
+
+        $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERDELETE);
+        {
+            {
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
+                $encoder->content($deletedstat);
+                $encoder->endTag();
+
+                $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
+                $encoder->content($newsynckey);
+                $encoder->endTag();
+            }
+            $encoder->endTag();
+        }
+
+        // remove folder from the folderflags array
+        if (($sid = array_search($serverid, $seenfolders)) !== false) {
+            unset($seenfolders[$sid]);
+            $seenfolders = array_values($seenfolders);
+            debugLog("deleted from seenfolders: ". $serverid);
+        }
+    }
+
+    $encoder->endTag();
+    // Save the sync state for the next time
+    $statemachine->setSyncState($newsynckey, $importer->GetState());
+    $statemachine->setSyncState("s".$newsynckey, serialize($seenfolders));
+
+    return true;
+}
+
+// Handle meetingresponse method
+function HandleMeetingResponse($backend, $protocolversion) {
+    global $zpushdtd;
+    global $output, $input;
+
+    $requests = Array();
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE))
+        return false;
+
+    while($decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUEST)) {
+        $req = Array();
+
+        if($decoder->getElementStartTag(SYNC_MEETINGRESPONSE_USERRESPONSE)) {
+            $req["response"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if($decoder->getElementStartTag(SYNC_MEETINGRESPONSE_FOLDERID)) {
+            $req["folderid"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if($decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUESTID)) {
+            $req["requestid"] = $decoder->getElementContent();
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        array_push($requests, $req);
+    }
+
+    if(!$decoder->getElementEndTag())
+        return false;
+
+
+    // Start output, simply the error code, plus the ID of the calendar item that was generated by the
+    // accept of the meeting response
+
+    $encoder->StartWBXML();
+
+    $encoder->startTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE);
+
+    foreach($requests as $req) {
+        $calendarid = "";
+        $ok = $backend->MeetingResponse($req["requestid"], $req["folderid"], $req["response"], $calendarid);
+        $encoder->startTag(SYNC_MEETINGRESPONSE_RESULT);
+            $encoder->startTag(SYNC_MEETINGRESPONSE_REQUESTID);
+                $encoder->content($req["requestid"]);
+            $encoder->endTag();
+
+            $encoder->startTag(SYNC_MEETINGRESPONSE_STATUS);
+                $encoder->content($ok ? 1 : 2);
+            $encoder->endTag();
+
+            if($ok) {
+                $encoder->startTag(SYNC_MEETINGRESPONSE_CALENDARID);
+                    $encoder->content($calendarid);
+                $encoder->endTag();
+            }
+
+        $encoder->endTag();
+    }
+
+    $encoder->endTag();
+
+    return true;
+}
+
+
+function HandleFolderUpdate($backend, $protocolversion) {
+    return HandleFolderCreate($backend, $protocolversion);
+}
+
+function HandleFolderDelete($backend, $protocolversion) {
+    return HandleFolderCreate($backend, $protocolversion);
+}
+
+function HandleProvision($backend, $devid, $protocolversion) {
+    global $user, $auth_pw, $policykey;
+
+    global $zpushdtd, $policies;
+    global $output, $input;
+
+    $status = SYNC_PROVISION_STATUS_SUCCESS;
+
+    $phase2 = true;
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_PROVISION_PROVISION))
+        return false;
+
+    //handle android remote wipe.
+    if ($decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) {
+        if(!$decoder->getElementStartTag(SYNC_PROVISION_STATUS))
+            return false;
+
+        $status = $decoder->getElementContent();
+
+        if(!$decoder->getElementEndTag())
+            return false;
+
+        if(!$decoder->getElementEndTag())
+            return false;
+    }
+
+    else {
+
+        if(!$decoder->getElementStartTag(SYNC_PROVISION_POLICIES))
+            return false;
+
+        if(!$decoder->getElementStartTag(SYNC_PROVISION_POLICY))
+            return false;
+
+        if(!$decoder->getElementStartTag(SYNC_PROVISION_POLICYTYPE))
+            return false;
+
+        $policytype = $decoder->getElementContent();
+        if ($policytype != 'MS-WAP-Provisioning-XML') {
+            $status = SYNC_PROVISION_STATUS_SERVERERROR;
+        }
+        if(!$decoder->getElementEndTag()) //policytype
+            return false;
+
+        if ($decoder->getElementStartTag(SYNC_PROVISION_POLICYKEY)) {
+            $devpolicykey = $decoder->getElementContent();
+
+            if(!$decoder->getElementEndTag())
+                return false;
+
+            if(!$decoder->getElementStartTag(SYNC_PROVISION_STATUS))
+                return false;
+
+            $status = $decoder->getElementContent();
+            //do status handling
+            $status = SYNC_PROVISION_STATUS_SUCCESS;
+
+            if(!$decoder->getElementEndTag())
+                return false;
+
+            $phase2 = false;
+        }
+
+        if(!$decoder->getElementEndTag()) //policy
+            return false;
+
+        if(!$decoder->getElementEndTag()) //policies
+            return false;
+
+        if ($decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) {
+            if(!$decoder->getElementStartTag(SYNC_PROVISION_STATUS))
+                return false;
+
+            $status = $decoder->getElementContent();
+
+            if(!$decoder->getElementEndTag())
+                return false;
+
+            if(!$decoder->getElementEndTag())
+                return false;
+        }
+    }
+    if(!$decoder->getElementEndTag()) //provision
+        return false;
+
+    $encoder->StartWBXML();
+
+    //set the new final policy key in the backend
+    if (!$phase2) {
+        $policykey = $backend->generatePolicyKey();
+        $backend->setPolicyKey($policykey, $devid);
+    }
+
+    $encoder->startTag(SYNC_PROVISION_PROVISION);
+    {
+        $encoder->startTag(SYNC_PROVISION_STATUS);
+            $encoder->content($status);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_PROVISION_POLICIES);
+            $encoder->startTag(SYNC_PROVISION_POLICY);
+
+            $encoder->startTag(SYNC_PROVISION_POLICYTYPE);
+                   $encoder->content($policytype);
+            $encoder->endTag();
+
+            $encoder->startTag(SYNC_PROVISION_STATUS);
+                $encoder->content($status);
+            $encoder->endTag();
+
+            $encoder->startTag(SYNC_PROVISION_POLICYKEY);
+                   $encoder->content($policykey);
+            $encoder->endTag();
+
+            if ($phase2) {
+                $encoder->startTag(SYNC_PROVISION_DATA);
+                if ($policytype == 'MS-WAP-Provisioning-XML') {
+                    $encoder->content('<wap-provisioningdoc><characteristic type="SecurityPolicy"><parm name="4131" value="1"/><parm name="4133" value="1"/></characteristic></wap-provisioningdoc>');
+                }
+                else {
+                    debugLog("Wrong policy type");
+                    return false;
+                }
+
+                $encoder->endTag();//data
+            }
+            $encoder->endTag();//policy
+        $encoder->endTag(); //policies
+    }
+    $rwstatus = $backend->getDeviceRWStatus($user, $auth_pw, $devid);
+
+
+    //wipe data if status is pending or wiped
+    if ($rwstatus == SYNC_PROVISION_RWSTATUS_PENDING || $rwstatus == SYNC_PROVISION_RWSTATUS_WIPED) {
+        $encoder->startTag(SYNC_PROVISION_REMOTEWIPE, false, true);
+        $backend->setDeviceRWStatus($user, $auth_pw, $devid, SYNC_PROVISION_RWSTATUS_WIPED);
+        //$rwstatus = SYNC_PROVISION_RWSTATUS_WIPED;
+    }
+
+    $encoder->endTag();//provision
+
+    return true;
+}
+
+
+function HandleSearch($backend, $devid, $protocolversion) {
+    global $zpushdtd;
+    global $input, $output;
+
+    $searchrange = '0';
+
+    $decoder = new WBXMLDecoder($input, $zpushdtd);
+    $encoder = new WBXMLEncoder($output, $zpushdtd);
+
+    if(!$decoder->getElementStartTag(SYNC_SEARCH_SEARCH))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_SEARCH_STORE))
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_SEARCH_NAME))
+        return false;
+    $searchname = $decoder->getElementContent();
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    if(!$decoder->getElementStartTag(SYNC_SEARCH_QUERY))
+        return false;
+    $searchquery = $decoder->getElementContent();
+    if(!$decoder->getElementEndTag())
+        return false;
+
+    if($decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) {
+        while(1) {
+            if($decoder->getElementStartTag(SYNC_SEARCH_RANGE)) {
+                $searchrange = $decoder->getElementContent();
+                if(!$decoder->getElementEndTag())
+                    return false;
+                }
+                $e = $decoder->peek();
+                if($e[EN_TYPE] == EN_TYPE_ENDTAG) {
+                    $decoder->getElementEndTag();
+                    break;
+                }
+            }
+        //if(!$decoder->getElementEndTag())
+            //return false;
+    }
+    if(!$decoder->getElementEndTag()) //store
+        return false;
+
+    if(!$decoder->getElementEndTag()) //search
+        return false;
+
+
+    if (strtoupper($searchname) != "GAL") {
+        debugLog("Searchtype $searchname is not supported");
+        return false;
+    }
+    //get search results from backend
+    $rows = $backend->getSearchResults($searchquery, $searchrange);
+
+    $encoder->startWBXML();
+
+    $encoder->startTag(SYNC_SEARCH_SEARCH);
+
+        $encoder->startTag(SYNC_SEARCH_STATUS);
+        $encoder->content(1);
+        $encoder->endTag();
+
+        $encoder->startTag(SYNC_SEARCH_RESPONSE);
+            $encoder->startTag(SYNC_SEARCH_STORE);
+
+                $encoder->startTag(SYNC_SEARCH_STATUS);
+                $encoder->content(1);
+                $encoder->endTag();
+
+                if (is_array($rows) && !empty($rows)) {
+                    $searchrange = $rows['range'];
+                    unset($rows['range']);
+                    foreach ($rows as $u) {
+                        $encoder->startTag(SYNC_SEARCH_RESULT);
+                            $encoder->startTag(SYNC_SEARCH_PROPERTIES);
+
+                                $encoder->startTag(SYNC_GAL_DISPLAYNAME);
+                                $encoder->content($u["fullname"]);
+                                $encoder->endTag();
+
+                                $encoder->startTag(SYNC_GAL_PHONE);
+                                $encoder->content($u["businessphone"]);
+                                $encoder->endTag();
+
+                                $encoder->startTag(SYNC_GAL_ALIAS);
+                                $encoder->content($u["username"]);
+                                $encoder->endTag();
+
+                                //it's not possible not get first and last name of an user
+                                //from the gab and user functions, so we just set fullname
+                                //to lastname and leave firstname empty because nokia needs
+                                //first and lastname in order to display the search result
+                                $encoder->startTag(SYNC_GAL_FIRSTNAME);
+                                $encoder->content("");
+                                $encoder->endTag();
+
+                                $encoder->startTag(SYNC_GAL_LASTNAME);
+                                $encoder->content($u["fullname"]);
+                                $encoder->endTag();
+
+                                $encoder->startTag(SYNC_GAL_EMAILADDRESS);
+                                $encoder->content($u["emailaddress"]);
+                                $encoder->endTag();
+
+                            $encoder->endTag();//result
+                        $encoder->endTag();//properties
+                    }
+                    $encoder->startTag(SYNC_SEARCH_RANGE);
+                    $encoder->content($searchrange);
+                    $encoder->endTag();
+
+                    $encoder->startTag(SYNC_SEARCH_TOTAL);
+                    $encoder->content(count($rows));
+                    $encoder->endTag();
+                }
+
+            $encoder->endTag();//store
+        $encoder->endTag();//response
+    $encoder->endTag();//search
+
+
+    return true;
+}
+
+function HandleRequest($backend, $cmd, $devid, $protocolversion) {
+    switch($cmd) {
+        case 'Sync':
+            $status = HandleSync($backend, $protocolversion, $devid);
+            break;
+        case 'SendMail':
+            $status = HandleSendMail($backend, $protocolversion);
+            break;
+        case 'SmartForward':
+            $status = HandleSmartForward($backend, $protocolversion);
+            break;
+        case 'SmartReply':
+            $status = HandleSmartReply($backend, $protocolversion);
+            break;
+        case 'GetAttachment':
+            $status = HandleGetAttachment($backend, $protocolversion);
+            break;
+        case 'GetHierarchy':
+            $status = HandleGetHierarchy($backend, $protocolversion, $devid);
+            break;
+        case 'CreateCollection':
+            $status = HandleCreateCollection($backend, $protocolversion);
+            break;
+        case 'DeleteCollection':
+            $status = HandleDeleteCollection($backend, $protocolversion);
+            break;
+        case 'MoveCollection':
+            $status = HandleMoveCollection($backend, $protocolversion);
+            break;
+        case 'FolderSync':
+            $status = HandleFolderSync($backend, $protocolversion);
+            break;
+        case 'FolderCreate':
+            $status = HandleFolderCreate($backend, $protocolversion);
+            break;
+        case 'FolderDelete':
+            $status = HandleFolderDelete($backend, $protocolversion);
+            break;
+        case 'FolderUpdate':
+            $status = HandleFolderUpdate($backend, $protocolversion);
+            break;
+        case 'MoveItems':
+            $status = HandleMoveItems($backend, $protocolversion);
+            break;
+        case 'GetItemEstimate':
+            $status = HandleGetItemEstimate($backend, $protocolversion, $devid);
+            break;
+        case 'MeetingResponse':
+            $status = HandleMeetingResponse($backend, $protocolversion);
+            break;
+        case 'Notify': // Used for sms-based notifications (pushmail)
+            $status = HandleNotify($backend, $protocolversion);
+            break;
+        case 'Ping': // Used for http-based notifications (pushmail)
+            $status = HandlePing($backend, $devid, $protocolversion);
+            break;
+        case 'Provision':
+            $status = (PROVISIONING === true) ? HandleProvision($backend, $devid, $protocolversion) : false;
+            break;
+        case 'Search':
+            $status = HandleSearch($backend, $devid, $protocolversion);
+            break;
+
+        default:
+            debugLog("unknown command - not implemented");
+            $status = false;
+            break;
+    }
+
+    return $status;
+}
+
+function readStream(&$input) {
+    $s = "";
+
+    while(1) {
+        $data = fread($input, 4096);
+        if(strlen($data) == 0)
+            break;
+        $s .= $data;
+    }
+
+    return $s;
+}
+
+?>
