Index: contrib/z-push/include/mimeDecode.php
===================================================================
--- contrib/z-push/include/mimeDecode.php	(revision 3637)
+++ contrib/z-push/include/mimeDecode.php	(revision 3637)
@@ -0,0 +1,919 @@
+<?php
+/**
+ * The Mail_mimeDecode class is used to decode mail/mime messages
+ *
+ * This class will parse a raw mime email and return
+ * the structure. Returned structure is similar to
+ * that returned by imap_fetchstructure().
+ *
+ *  +----------------------------- IMPORTANT ------------------------------+
+ *  | Usage of this class compared to native php extensions such as        |
+ *  | mailparse or imap, is slow and may be feature deficient. If available|
+ *  | you are STRONGLY recommended to use the php extensions.              |
+ *  +----------------------------------------------------------------------+
+ *
+ * Compatible with PHP versions 4 and 5
+ *
+ * LICENSE: This LICENSE is in the BSD license style.
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the authors, nor the names of its contributors 
+ *   may 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.
+ *
+ * @category   Mail
+ * @package    Mail_Mime
+ * @author     Richard Heyes  <richard@phpguru.org>
+ * @author     George Schlossnagle <george@omniti.com>
+ * @author     Cipriano Groenendal <cipri@php.net>
+ * @author     Sean Coates <sean@php.net>
+ * @copyright  2003-2006 PEAR <pear-group@php.net>
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    CVS: $Id: mimeDecode.php 288500 2009-09-21 05:32:32Z alan_k $
+ * @link       http://pear.php.net/package/Mail_mime
+ */
+
+
+/**
+ * Z-Push changes
+ *
+ * removed PEAR dependency by implementing own raiseError()
+ * removed deprecated referencing: $obj = &new Mail_mimeDecode($body); in _decode()
+ * implemented automated decoding of strings from mail charset
+ * 
+ * Reference implementation used: 
+ * http://download.pear.php.net/package/Mail_mimeDecode-1.5.1.tgz
+ * 
+ */
+
+/**
+ * require PEAR
+ *
+ * This package depends on PEAR to raise errors.
+ */
+//require_once 'PEAR.php';
+
+/**
+ * The Mail_mimeDecode class is used to decode mail/mime messages
+ *
+ * This class will parse a raw mime email and return the structure.
+ * Returned structure is similar to that returned by imap_fetchstructure().
+ *
+ *  +----------------------------- IMPORTANT ------------------------------+
+ *  | Usage of this class compared to native php extensions such as        |
+ *  | mailparse or imap, is slow and may be feature deficient. If available|
+ *  | you are STRONGLY recommended to use the php extensions.              |
+ *  +----------------------------------------------------------------------+
+ *
+ * @category   Mail
+ * @package    Mail_Mime
+ * @author     Richard Heyes  <richard@phpguru.org>
+ * @author     George Schlossnagle <george@omniti.com>
+ * @author     Cipriano Groenendal <cipri@php.net>
+ * @author     Sean Coates <sean@php.net>
+ * @copyright  2003-2006 PEAR <pear-group@php.net>
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/Mail_mime
+ */
+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']) ?
+                                 $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
+                        $charset = isset($return->ctype_parameters['charset']) ? $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) .
+                   '</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 == '')
+            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/dbconnect.php
===================================================================
--- contrib/z-push/include/dbconnect.php	(revision 3637)
+++ contrib/z-push/include/dbconnect.php	(revision 3637)
@@ -0,0 +1,15 @@
+<?php
+    //global $db; // global variable that can be used with any script
+    function dbconnect()
+    {
+        // set up database connection details
+        $dbhost = "10.200.112.132"; // replace with your database hostname or ip
+        $dbuser = "postgres"; // replace with your database username
+        $dbpass = "senha123"; // replace with your database password
+        $dbname = "expresso"; // replace with your database name
+
+        // connect to database
+        $db = pg_pconnect("host=" . $dbhost . " port=5432 dbname=" . $dbname . " user=" . $dbuser . " password=" . $dbpass);
+        return $db;
+    }// end connect()
+?>
Index: contrib/z-push/include/z_RTF.php
===================================================================
--- contrib/z-push/include/z_RTF.php	(revision 3637)
+++ contrib/z-push/include/z_RTF.php	(revision 3637)
@@ -0,0 +1,707 @@
+<?php
+/*
+    This class contains code from rtfclass.php that was written by Markus Fischer and placed by him under
+    GPLv2 License.
+
+    =======================================NOTES FROM ORIGINAL AUTHOR====================================
+                Rich Text Format - Parsing Class
+                ================================
+
+                (c) 2000 Markus Fischer
+                <mfischer@josefine.ben.tuwien.ac.at>
+                   http://josefine.ben.tuwien.ac.at/~mfischer/
+
+                Latest versions of this class can always be found at
+                        http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/rtfclass.phps
+                Testing suite is available at
+                        http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/
+
+                License: GPLv2
+
+                Specification:
+                        http://msdn.microsoft.com/library/default.asp?URL=/library/specs/rtfspec.htm
+
+                General Notes:
+                ==============
+                Unknown or unspupported control symbols are silently gnored
+
+                Group stacking is still not supported :(
+                        group stack logic implemented; however not really used yet
+    =====================================================================================================
+
+    It was modified by me (Andreas Brodowski) to allow compressed RTF being uncompressed by code I ported from
+    Java to PHP and adapted according the needs of Z-Push.
+   
+    Currently it is being used to detect empty RTF Streams from Nokia Phones in MfE Clients
+   
+    It needs to be used by other backend writers that needs to have notes in calendar, appointment or tasks
+    objects to be written to their databases since devices send them usually in RTF Format... With Zarafa
+    you can write them directly to DB and Zarafa is doing the conversion job. Other Groupware systems usually
+    don't have this possibility...
+     
+*/
+
+
+class rtf {
+    var $LZRTF_HDR_DATA = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
+    var $LZRTF_HDR_LEN = 207;
+    var $CRC32_TABLE = array(     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
+                                  0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
+                                  0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
+                                  0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
+                                  0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
+                                  0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
+                                  0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
+                                  0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
+                                  0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
+                                  0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
+                                  0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
+                                  0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
+                                  0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
+                                  0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
+                                  0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
+                                  0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
+                                  0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
+                                  0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
+                                  0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
+                                  0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
+                                  0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
+                                  0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
+                                  0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
+                                  0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
+                                  0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
+                                  0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
+                                  0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
+                                  0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
+                                  0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
+                                  0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
+                                  0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
+                                  0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
+                               );
+   
+    var $rtf;                   // rtf core stream
+    var $rtf_len;               // length in characters of the stream (get performace due avoiding calling strlen everytime)
+    var $err = array();         // array of error message, no entities on no error
+
+    var $wantXML;               // convert to XML
+    var $wantHTML;              // convert to HTML
+    var $wantASCII;             // convert to HTML
+
+                                // the only variable which should be accessed from the outside
+    var $out;                   // output data stream (depends on which $wantXXXXX is set to true
+    var $outstyles;             // htmlified styles (generated after parsing if wantHTML
+    var $styles;                // if wantHTML, stylesheet definitions are put in here
+
+                                // internal parser variables --------------------------------
+                                // control word variables
+    var $cword;                 // holds the current (or last) control word, depending on $cw
+    var $cw;                    // are we currently parsing a control word ?
+    var $cfirst;                // could this be the first character ? so watch out for control symbols
+
+    var $flags = array();       // parser flags
+
+    var $queue;                 // every character which is no sepcial char, not belongs to a control word/symbol; is generally considered being 'plain'
+
+    var $stack = array();       // group stack
+
+    /* keywords which don't follw the specification (used by Word '97 - 2000) */
+    // not yet used
+    var $control_exception = array(
+        "clFitText",
+        "clftsWidth(-?[0-9]+)?",
+        "clNoWrap(-?[0-9]+)?",
+        "clwWidth(-?[0-9]+)?",
+        "tdfrmtxtBottom(-?[0-9]+)?",
+        "tdfrmtxtLeft(-?[0-9]+)?",
+        "tdfrmtxtRight(-?[0-9]+)?",
+        "tdfrmtxtTop(-?[0-9]+)?",
+        "trftsWidthA(-?[0-9]+)?",
+        "trftsWidthB(-?[0-9]+)?",
+        "trftsWidth(-?[0-9]+)?",
+        "trwWithA(-?[0-9]+)?",
+        "trwWithB(-?[0-9]+)?",
+        "trwWith(-?[0-9]+)?",
+        "spectspecifygen(-?[0-9]+)?",
+    );
+
+    var $charset_table = array(
+        "0"     =>      "ANSI",
+        "1"     =>      "Default",
+        "2"     =>      "Symbol",
+        "77"    =>      "Mac",
+        "128"   =>      "Shift Jis",
+        "129"   =>      "Hangul",
+        "130"   =>      "Johab",
+        "134"   =>      "GB2312",
+        "136"   =>      "Big5",
+        "161"   =>      "Greek",
+        "162"   =>      "Turkish",
+        "163"   =>      "Vietnamese",
+        "177"   =>      "Hebrew",
+        "178"   =>      "Arabic",
+        "179"   =>      "Arabic Traditional",
+        "180"   =>      "Arabic user",
+        "181"   =>      "Hebrew user",
+        "186"   =>      "Baltic",
+        "204"   =>      "Russian",
+        "222"   =>      "Thai",
+        "238"   =>      "Eastern European",
+        "255"   =>      "PC 437",
+        "255"   =>      "OEM",
+    );
+
+    /* note: the only conversion table used */
+    var $fontmodifier_table = array(
+        "bold"          => "b",
+        "italic"        => "i",
+        "underlined"    => "u",
+        "strikethru"    => "strike",
+    );
+
+       
+    function rtf() {
+        $this->rtf_len = 0;
+        $this->rtf = '';
+
+        $this->out = '';
+    }
+   
+    // loadrtf - load the raw rtf data to be converted by this class
+    // data = the raw rtf
+    function loadrtf($data) {
+        if (($this->rtf = $this->uncompress($data))) {
+            $this->rtf_len = strlen($this->rtf);
+        };
+        if($this->rtf_len == 0) {
+            debugLog("No data in stream found");
+            return false;
+        };
+        return true;
+    }
+   
+    function output($typ) {
+        switch($typ) {
+            case "ascii": $this->wantASCII = true; break;
+            case "xml": $this->wantXML = true; break;
+            case "html": $this->wantHTML = true; break;
+            default: break;
+        }
+    }
+
+    // uncompress - uncompress compressed rtf data
+    // src = the compressed raw rtf in LZRTF format
+    function uncompress($src) {
+        $header = unpack("LcSize/LuSize/Lmagic/Lcrc32",substr($src,0,16));
+        $in = 16;
+        if ($header['cSize'] != strlen($src)-4) {
+            debugLog("Stream too short");
+            return false;
+        }
+   
+        if ($header['crc32'] != $this->LZRTFCalcCRC32($src,16,(($header['cSize']+4))-16)) {
+            debugLog("CRC MISMATCH");
+            return false;
+        }
+   
+        if ($header['magic'] == 0x414c454d) {                   // uncompressed RTF - return as is.
+            $dest = substr($src,$in,$header['uSize']);
+        } else if ($header['magic'] == 0x75465a4c) {            // compressed RTF - uncompress.
+            $dst = $this->LZRTF_HDR_DATA;
+            $out = $this->LZRTF_HDR_LEN;
+            $oblen = $this->LZRTF_HDR_LEN + $header['uSize'];
+            $flagCount = 0;
+            $flags = 0;
+            while ($out<$oblen) {
+                $flags = ($flagCount++ % 8 == 0) ? ord($src{$in++}) : $flags >> 1;
+                if (($flags & 1) == 1) {
+                    $offset = ord($src{$in++});
+                    $length = ord($src{$in++});
+                    $offset = ($offset << 4) | ($length >> 4);
+                    $length = ($length & 0xF) + 2;
+                    $offset = (int)($out / 4096) * 4096 + $offset;
+                    if ($offset >= $out) $offset -= 4096;
+                    $end = $offset + $length;
+                    while ($offset < $end) {
+                        $dst{$out++} = $dst{$offset++};
+                    };
+                } else {
+                    $dst{$out++} = $src{$in++};
+                }
+            }
+            $src = $dst;
+            $dest = substr($src,$this->LZRTF_HDR_LEN,$header['uSize']);
+        } else {                                                // unknown magic - returfn false (please report if this ever happens)
+            debugLog("Unknown Magic");
+            return false;
+        }
+   
+        return $dest;
+    }
+
+    // LZRTFCalcCRC32 - calculates the CRC32 of the LZRTF data part
+    // buf = the whole rtf data part
+    // off = start point of crc calculation
+    // len = length of data to calculate CRC for
+    // function is necessary since in RTF there is no XOR 0xffffffff being done (said to be 0x00 unsafe CRC32 calculation
+    function LZRTFCalcCRC32($buf, $off, $len) {
+        $c=0;
+        $end = $off + $len;
+        for($i=$off;$i < $end;$i++) {
+            $c=$this->CRC32_TABLE[($c ^ ord($buf{$i})) & 0xFF] ^ (($c >> 8) & 0x00ffffff);
+        }
+        return $c;                                                                                                                              
+    }
+
+    function parserInit() { /* Default values according to the specs */
+        $this->flags = array(
+            "fontsize"          => 24,
+            "beginparagraph"    => true,
+        );
+    }
+   
+    function parseControl($control, $parameter) {
+        switch ($control) {    
+            case "fonttbl":             // font table definition start
+                $this->flags["fonttbl"] = true; // signal fonttable control words they are allowed to behave as expected
+                break;
+            case "f":                   // define or set font
+                if($this->flags["fonttbl"]) {   // if its set, the fonttable definition is written to; else its read from
+                    $this->flags["fonttbl_current_write"] = $parameter;
+                } else {
+                    $this->flags["fonttbl_current_read"] = $parameter;
+                }
+                break;
+            case "fcharset":            // this is for preparing flushQueue; it then moves the Queue to $this->fonttable .. instead to formatted output
+                $this->flags["fonttbl_want_fcharset"] = $parameter;
+                break;
+            case "fs":                  // sets the current fontsize; is used by stylesheets (which are therefore generated on the fly
+                $this->flags["fontsize"] = $parameter;
+                break;
+               
+            case "qc":                  // handle center alignment
+                $this->flags["alignment"] = "center";
+                break;
+            case "qr":                  // handle right alignment
+                $this->flags["alignment"] = "right";
+                break;
+           
+            case "pard":                // reset paragraph settings (only alignment)
+                $this->flags["alignment"] = "";
+                break;
+            case "par":                 // define new paragraph (for now, thats a simple break in html) begin new line
+                $this->flags["beginparagraph"] = true;
+                if($this->wantHTML) {
+                    $this->out .= "</div>";
+                }
+                if($this->wantASCII) {
+                    $this->out .= "\n";
+                }
+                break;
+            case "bnone":               // bold
+                $parameter = "0";
+            case "b":
+                // haven'y yet figured out WHY I need a (string)-cast here ... hm
+                if((string)$parameter == "0")
+                    $this->flags["bold"] = false;
+                else
+                    $this->flags["bold"] = true;
+                break;
+            case "ulnone":              // underlined
+                $parameter = "0";
+            case "ul":
+                if((string)$parameter == "0")
+                    $this->flags["underlined"] = false;
+                else
+                    $this->flags["underlined"] = true;
+                break;
+            case "inone":               // italic
+                $parameter = "0";
+            case "i":
+                if((string)$parameter == "0")
+                    $this->flags["italic"] = false;
+                else
+                    $this->flags["italic"] = true;
+                break;
+            case "strikenone":          // strikethru
+                $parameter = "0";
+            case "strike":
+                if((string)$parameter == "0")
+                    $this->flags["strikethru"] = false;
+                else
+                    $this->flags["strikethru"] = true;
+                break;
+            case "plain":               // reset all font modifiers and fontsize to 12
+                $this->flags["bold"] = false;
+                $this->flags["italic"] = false;
+                $this->flags["underlined"] = false;
+                $this->flags["strikethru"] = false;
+                $this->flags["fontsize"] = 12;
+
+                $this->flags["subscription"] = false;
+                $this->flags["superscription"] = false;
+                break;
+            case "subnone":             // subscription
+                $parameter = "0";
+            case "sub":
+                if((string)$parameter == "0")
+                    $this->flags["subscription"] = false;
+                else
+                    $this->flags["subscription"] = true;
+                break;
+            case "supernone":           // superscription
+                $parameter = "0";
+            case "super":
+                if((string)$parameter == "0")
+                    $this->flags["superscription"] = false;
+                else
+                    $this->flags["superscription"] = true;
+                break;
+        }
+    }
+
+    /*
+        Dispatch the control word to the output stream
+    */
+   
+    function flushControl() {
+        if(ereg("^([A-Za-z]+)(-?[0-9]*) ?$", $this->cword, $match)) {
+            $this->parseControl($match[1], $match[2]);
+            if($this->wantXML) {
+                $this->out.="<control word=\"".$match[1]."\"";
+                if(strlen($match[2]) > 0)
+                    $this->out.=" param=\"".$match[2]."\"";
+                $this->out.="/>";
+            }
+        }
+    }
+
+    /*
+        If output stream supports comments, dispatch it
+    */
+       
+    function flushComment($comment) {
+        if($this->wantXML || $this->wantHTML) {
+            $this->out.="<!-- ".$comment." -->";
+        }
+    }
+
+    /*
+        Dispatch start/end of logical rtf groups (not every output type needs it; merely debugging purpose)
+    */
+
+    function flushGroup($state) {
+        if($state == "open") { /* push onto the stack */
+            array_push($this->stack, $this->flags);
+               
+            if($this->wantXML)
+                $this->out.="<group>";
+        }
+        if($state == "close") { /* pop from the stack */
+            $this->last_flags = $this->flags;
+            $this->flags = array_pop($this->stack);
+
+            $this->flags["fonttbl_current_write"] = ""; // on group close, no more fontdefinition will be written to this id
+                                                        // this is not really the right way to do it !
+                                                        // of course a '}' not necessarily donates a fonttable end; a fonttable
+                                                        // group at least *can* contain sub-groups
+                                                        // therefore an stacked approach is heavily needed
+            $this->flags["fonttbl"] = false;            // no matter what you do, if a group closes, its fonttbl definition is closed too
+
+            if($this->wantXML)
+                $this->out.="</group>";
+        }
+    }
+
+    function flushHead() {
+        if($this->wantXML)
+            $this->out.="<rtf>";
+    }
+
+    function flushBottom() {
+        if($this->wantXML)
+            $this->out.="</rtf>";
+    }
+
+    function checkHtmlSpanContent($command) {
+        reset($this->fontmodifier_table);
+        while(list($rtf, $html) = each($this->fontmodifier_table)) {
+            if($this->flags[$rtf] == true) {
+                if($command == "start")
+                    $this->out .= "<".$html.">";
+                else
+                    $this->out .= "</".$html.">";
+            }
+        }
+    }
+               
+    /*
+        flush text in queue
+    */
+    function flushQueue() {
+        if(strlen($this->queue)) {
+            // processing logic
+            if (isset($this->flags["fonttbl_want_fcharset"]) &&
+                ereg("^[0-9]+$", $this->flags["fonttbl_want_fcharset"])) {
+                $this->fonttable[$this->flags["fonttbl_want_fcharset"]]["charset"] = $this->queue;
+                $this->flags["fonttbl_want_fcharset"] = "";
+                $this->queue = "";
+            }
+       
+            // output logic
+            if (strlen($this->queue)) {
+                /*
+                    Everything which passes this is (or, at leat, *should*) be only outputted plaintext
+                    Thats why we can safely add the css-stylesheet when using wantHTML
+                */
+                if($this->wantXML)
+                    $this->out.= "<plain>".$this->queue."</plain>";
+                else if($this->wantHTML) {
+                    // only output html if a valid (for now, just numeric;) fonttable is given
+                    if(ereg("^[0-9]+$", $this->flags["fonttbl_current_read"])) {
+                        if($this->flags["beginparagraph"] == true) {
+                            $this->flags["beginparagraph"] = false;
+                            $this->out .= "<div align=\"";
+                            switch($this->flags["alignment"]) {
+                                case "right":
+                                    $this->out .= "right";
+                                    break;
+                                case "center":
+                                    $this->out .= "center";
+                                    break;
+                                case "left":
+                                default:
+                                    $this->out .= "left";
+                            }
+                            $this->out .= "\">";
+                        }
+                       
+                        /* define new style for that span */
+                        $this->styles["f".$this->flags["fonttbl_current_read"]."s".$this->flags["fontsize"]] = "font-family:".$this->fonttable[$this->flags["fonttbl_current_read"]]["charset"]." font-size:".$this->flags["fontsize"].";";
+                        /* write span start */
+                        $this->out .= "<span class=\"f".$this->flags["fonttbl_current_read"]."s".$this->flags["fontsize"]."\">";
+
+                        /* check if the span content has a modifier */
+                        $this->checkHtmlSpanContent("start");
+                        /* write span content */
+                        $this->out .= $this->queue;
+                        /* close modifiers */
+                        $this->checkHtmlSpanContent("stop");
+                        /* close span */
+                        "</span>";
+                    }
+                }
+                $this->queue = "";
+            }
+        }
+    }
+
+    /*
+        handle special charactes like \'ef
+    */
+   
+    function flushSpecial($special) {
+        if(strlen($special) == 2) {
+            if($this->wantASCII)
+                $this->out .= chr(hexdec('0x'.$special));
+            else if($this->wantXML)
+                $this->out .= "<special value=\"".$special."\"/>";
+            else if($this->wantHTML){
+                $this->out .= "<special value=\"".$special."\"/>";
+                switch($special) {
+                    case "c1": $this->out .= "&Aacute;"; break;
+                    case "e1": $this->out .= "&aacute;"; break;
+                    case "c0": $this->out .= "&Agrave;"; break;
+                    case "e0": $this->out .= "&agrave;"; break;
+                    case "c9": $this->out .= "&Eacute;"; break;
+                    case "e9": $this->out .= "&eacute;"; break;
+                    case "c8": $this->out .= "&Egrave;"; break;
+                    case "e8": $this->out .= "&egrave;"; break;
+                    case "cd": $this->out .= "&Iacute;"; break;
+                    case "ed": $this->out .= "&iacute;"; break;
+                    case "cc": $this->out .= "&Igrave;"; break;
+                    case "ec": $this->out .= "&igrave;"; break;
+                    case "d3": $this->out .= "&Oacute;"; break;
+                    case "f3": $this->out .= "&oacute;"; break;
+                    case "d2": $this->out .= "&Ograve;"; break;
+                    case "f2": $this->out .= "&ograve;"; break;
+                    case "da": $this->out .= "&Uacute;"; break;
+                    case "fa": $this->out .= "&uacute;"; break;
+                    case "d9": $this->out .= "&Ugrave;"; break;
+                    case "f9": $this->out .= "&ugrave;"; break;
+                    case "80": $this->out .= "&#8364;"; break;
+                    case "d1": $this->out .= "&Ntilde;"; break;
+                    case "f1": $this->out .= "&ntilde;"; break;
+                    case "c7": $this->out .= "&Ccedil;"; break;
+                    case "e7": $this->out .= "&ccedil;"; break;
+                    case "dc": $this->out .= "&Uuml;"; break;
+                    case "fc": $this->out .= "&uuml;"; break;
+                    case "bf": $this->out .= "&#191;"; break;
+                    case "a1": $this->out .= "&#161;"; break;
+                    case "b7": $this->out .= "&middot;"; break;
+                    case "a9": $this->out .= "&copy;"; break;
+                    case "ae": $this->out .= "&reg;"; break;
+                    case "ba": $this->out .= "&ordm;"; break;
+                    case "aa": $this->out .= "&ordf;"; break;
+                    case "b2": $this->out .= "&sup2;"; break;
+                    case "b3": $this->out .= "&sup3;"; break;
+                }
+            }
+        }
+    }
+
+    /*
+        Output errors at end
+    */
+    function flushErrors() {
+        if(count($this->err) > 0) {
+            if($this->wantXML) {
+                $this->out .= "<errors>";
+                while(list($num,$value) = each($this->err)) {
+                    $this->out .= "<message>".$value."</message>";
+                }
+                $this->out .= "</errors>";
+            }
+        }
+    }
+
+    function makeStyles() {
+        $this->outstyles = "<style type=\"text/css\"><!--\n";
+        reset($this->styles);
+        while(list($stylename, $styleattrib) = each($this->styles)) {
+            $this->outstyles .= ".".$stylename." { ".$styleattrib." }\n";
+        }
+        $this->outstyles .= "--></style>\n";
+    }
+
+    function parse() {
+
+        $this->parserInit();
+
+        $i = 0;
+        $this->cw= false;       // flag if control word is currently parsed
+        $this->cfirst = false;  // first control character ?
+        $this->cword = "";      // last or current control word (depends on $this->cw
+
+        $this->queue = "";      // plain text data found during parsing
+
+        $this->flushHead();
+
+        while($i < $this->rtf_len) {
+            switch($this->rtf[$i]) {
+                case "{":
+                    if($this->cw) {
+                        $this->flushControl();
+                        $this->cw = false;
+                        $this->cfirst = false;
+                    } else
+                        $this->flushQueue();
+
+                    $this->flushGroup("open");
+                    break;
+                case "}":
+                    if($this->cw) {
+                        $this->flushControl();
+                        $this->cw = false;
+                        $this->cfirst = false;
+                    } else
+                        $this->flushQueue();
+       
+                    $this->flushGroup("close");
+                    break;
+                case "\\":     
+                    if($this->cfirst) { // catches '\\'
+                        $this->queue .= "\\"; // replaced single quotes
+                        $this->cfirst = false;
+                        $this->cw = false;
+                        break;
+                    }
+                    if($this->cw) {
+                        $this->flushControl();
+                    } else
+                        $this->flushQueue();
+                    $this->cw = true;
+                    $this->cfirst = true;
+                    $this->cword = "";
+                    break;
+                default:
+                    if((ord($this->rtf[$i]) == 10) || (ord($this->rtf[$i]) == 13)) break; // eat line breaks
+                    if($this->cw) {     // active control word ?
+                        /*
+                            Watch the RE: there's an optional space at the end which IS part of
+                            the control word (but actually its ignored by flushControl)
+                        */
+                        if(ereg("^[a-zA-Z0-9-]?$", $this->rtf[$i])) { // continue parsing
+                            $this->cword .= $this->rtf[$i];
+                            $this->cfirst = false;
+                        } else {
+                            /*
+                                Control word could be a 'control symbol', like \~ or \* etc.
+                            */
+                            $specialmatch = false;
+                            if($this->cfirst) {
+                                if($this->rtf[$i] == '\'') { // expect to get some special chars
+                                    $this->flushQueue();
+                                    $this->flushSpecial($this->rtf[$i+1].$this->rtf[$i+2]);
+                                    $i+=2;
+                                    $specialmatch = true;
+                                    $this->cw = false;
+                                    $this->cfirst = false;
+                                    $this->cword = "";
+                                } else
+                                    if(ereg("^[{}\*]$", $this->rtf[$i])) {
+                                        $this->flushComment("control symbols not yet handled");
+                                        $specialmatch = true;
+                                    }
+                                $this->cfirst = false;
+                            } else {
+                                if($this->rtf[$i] == ' ') {     // space delimtes control words, so just discard it and flush the controlword
+                                    $this->cw = false;
+                                    $this->flushControl();
+                                    break;
+                                }
+                            }
+                            if(!$specialmatch) {
+                                $this->flushControl();
+                                $this->cw = false;
+                                $this->cfirst = false;
+                                /*
+                                    The current character is a delimeter, but is NOT
+                                    part of the control word so we hop one step back
+                                    in the stream and process it again
+                                */
+                                $i--;
+                            }
+                        }
+                    } else {
+                        // < and > need translation before putting into queue when XML or HTML is wanted
+                        if(($this->wantHTML) || ($this->wantXML)) {
+                            switch($this->rtf[$i]) {
+                                case "<":
+                                    $this->queue .= "&lt;";
+                                    break;
+                                case ">":
+                                    $this->queue .= "&gt;";
+                                    break;
+                                default:
+                                    $this->queue .= $this->rtf[$i];
+                                    break;
+                            }
+                        } else
+                            $this->queue .= $this->rtf[$i];
+                    }
+               
+            }
+            $i++;
+        }
+        $this->flushQueue();
+        $this->flushErrors();
+        $this->flushBottom();
+
+        if($this->wantHTML) {
+            $this->makeStyles();
+        }
+    }
+}
+
+
+?>
Index: contrib/z-push/backend/BackendProxy.php
===================================================================
--- contrib/z-push/backend/BackendProxy.php	(revision 3637)
+++ contrib/z-push/backend/BackendProxy.php	(revision 3637)
@@ -0,0 +1,162 @@
+<?php
+/***********************************************
+ * File      :   BackendProxy.php
+ * Project   :   Z-Push
+ * Descr     :   It route calls to three Backends thru one interface.
+ *
+ * Created   :   04.10.2010 - emerson-faria.nobre@serpro.gov.br
+ *
+ * ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+ * This file is distributed under GPL v2.
+ * Consult LICENSE file for details
+ ************************************************/
+include_once('diffbackend.php');
+
+class BackendProxy extends BackendDiff {
+	var $BackendEmail;
+	var $BackendContacts;
+	var $BackendCalendar;
+
+	function __construct()
+	{
+		global $BACKEND_EMAIL, $BACKEND_CONTACTS, $BACKEND_CALENDAR;
+		if (isset($BACKEND_EMAIL)) $this->BackendEmail = new $BACKEND_EMAIL();
+		if (isset($BACKEND_CONTACTS)) $this->BackendContacts = new $BACKEND_CONTACTS();
+		if (isset($BACKEND_CALENDAR)) $this->BackendCalendar = new $BACKEND_CALENDAR();
+	}
+
+	function Logon($username, $domain, $password) {
+		global $BACKEND_EMAIL;
+		if (isset($BACKEND_EMAIL)) return $this->BackendEmail->Logon($username, $domain, $password);
+		else return true;
+	}
+
+	function Logoff() {
+		global $BACKEND_EMAIL;
+		if (isset($BACKEND_EMAIL)) return $this->BackendEmail->Logoff();
+		else return true;
+	}
+
+	function Setup($user, $devid, $protocolversion) {
+		debugLog('BackendProxy::Setup('.$user.','.$devid.','.$protocolversion.')');
+		global $BACKEND_EMAIL, $BACKEND_CONTACTS, $BACKEND_CALENDAR;
+		if (isset($BACKEND_EMAIL)) $this->BackendEmail->Setup($user, $devid, $protocolversion);
+		if (isset($BACKEND_CONTACTS)) $this->BackendContacts->Setup($user, $devid, $protocolversion);
+		if (isset($BACKEND_CALENDAR)) $this->BackendCalendar->Setup($user, $devid, $protocolversion);
+		return true;
+	}
+
+	function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
+		global $BACKEND_EMAIL;
+		if (isset($BACKEND_EMAIL)) return $this->BackendEmail->SendMail($rfc822, $forward, $reply, $parent);
+		else return false;
+	}
+
+	function GetWasteBasket() {
+		if (isset($BACKEND_EMAIL)) return $this->BackendEmail->GetWasteBasket();
+		else return false;
+	}
+
+	function GetMessageList($folderid, $cutoffdate) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->GetMessageList($folderid, $cutoffdate);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->GetMessageList($folderid, $cutoffdate);
+		} else return $this->BackendEmail->GetMessageList($folderid, $cutoffdate);
+	}
+
+	function GetFolderList() {
+		global $BACKEND_EMAIL, $BACKEND_CONTACTS, $BACKEND_CALENDAR;
+		$folders = Array();
+		if (isset($BACKEND_EMAIL)) $folders += $this->BackendEmail->GetFolderList();
+		if (isset($BACKEND_CALENDAR)) array_push($folders, $this->BackendCalendar->GetFolderList());
+		if (isset($BACKEND_CONTACTS)) array_push($folders, $this->BackendContacts->GetFolderList());
+		return $folders;
+	}
+
+	function GetFolder($id) {
+		if ($id == 'root') {
+			return $this->BackendContacts->GetFolder($id);
+		} else if ($id == 'calendar') {
+			return $this->BackendCalendar->GetFolder($id);
+		} else return $this->BackendEmail->GetFolder($id);
+	}
+
+	function StatFolder($id) {
+		if ($id == 'root') {
+			return $this->BackendContacts->StatFolder($id);
+		} else if ($id == 'calendar') {
+			return $this->BackendCalendar->StatFolder($id);
+		} else return $this->BackendEmail->StatFolder($id);
+	}
+
+	function GetAttachmentData($attname) {
+		list($folderid, $id, $part) = explode(":", $attname);
+		if ($folderid == 'root') {
+			return $this->BackendContacts->GetAttachmentData($attname);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->GetAttachmentData($attname);
+		} else return $this->BackendEmail->GetAttachmentData($attname);
+	}
+
+	function StatMessage($folderid, $id) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->StatMessage($folderid, $id);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->StatMessage($folderid, $id);
+		} else return $this->BackendEmail->StatMessage($folderid, $id);
+	}
+
+	function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->GetMessage($folderid, $id, $truncsize, $mimesupport = 0);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->GetMessage($folderid, $id, $truncsize, $mimesupport = 0);
+		} else return $this->BackendEmail->GetMessage($folderid, $id, $truncsize, $mimesupport = 0);
+	}
+
+	function DeleteMessage($folderid, $id) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->DeleteMessage($folderid, $id);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->DeleteMessage($folderid, $id);
+		} else return $this->BackendEmail->DeleteMessage($folderid, $id);
+	}
+
+	function SetReadFlag($folderid, $id, $flags) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->SetReadFlag($folderid, $id, $flags);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->SetReadFlag($folderid, $id, $flags);
+		} else return $this->BackendEmail->SetReadFlag($folderid, $id, $flags);
+	}
+
+	function ChangeMessage($folderid, $id, $message) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->ChangeMessage($folderid, $id, $message);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->ChangeMessage($folderid, $id, $message);
+		} else return $this->BackendEmail->ChangeMessage($folderid, $id, $message);
+	}
+
+	function MoveMessage($folderid, $id, $newfolderid) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->MoveMessage($folderid, $id, $newfolderid);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->MoveMessage($folderid, $id, $newfolderid);
+		} else return $this->BackendEmail->MoveMessage($folderid, $id, $newfolderid);
+	}
+	function AlterPing() {
+		//TODO:  Verificar como deve ser tratado esse método
+		//if (isset($BACKEND_EMAIL)) return true;
+		return true;
+	}
+	function AlterPingChanges($folderid, &$syncstate) {
+		if ($folderid == 'root') {
+			return $this->BackendContacts->AlterPingChanges($folderid, &$syncstate);
+		} else if ($folderid == 'calendar') {
+			return $this->BackendCalendar->AlterPingChanges($folderid, &$syncstate);
+		} else return $this->BackendEmail->AlterPingChanges($folderid, &$syncstate);
+	}
+};
+?>
Index: contrib/z-push/backend/BackendCalendarExpresso.php
===================================================================
--- contrib/z-push/backend/BackendCalendarExpresso.php	(revision 3637)
+++ contrib/z-push/backend/BackendCalendarExpresso.php	(revision 3637)
@@ -0,0 +1,919 @@
+<?php
+/***********************************************
+ * File      :   BackendCalendarExpresso.php
+ * Project   :   Z-Push
+ * Descr     :   This Calendar Backend is for Expresso Groupware.
+ *
+ * Created   :   06.12.2010 - emerson-faria.nobre@serpro.gov.br
+ *
+ * ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+ * This file is distributed under GPL v2.
+ * Consult LICENSE file for details
+ ************************************************/
+include_once('diffbackend.php');
+include_once('include/z_RTF.php');
+include_once "include/dbconnect.php";
+
+class BackendCalendarExpresso extends BackendDiff {
+	var $_user;
+	var $_devid;
+	var $_protocolversion;
+	var $db;
+
+	function __construct()
+	{
+		$this->db = dbconnect();
+
+		if (!$this->db) {
+			echo "A DB connect error occured.\n";
+			exit;
+		}
+	}
+
+	function Setup($user, $devid, $protocolversion) {
+		$this->_user = $user;
+		$this->_devid = $devid;
+		$this->_protocolversion = $protocolversion;
+		//TODO: Autenticar Usuario no LDAP
+
+		return true;
+	}
+
+	function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
+		return false;
+	}
+
+	function GetWasteBasket() {
+		return false;
+	}
+
+	function GetMessageList($folderid, $cutoffdate) {
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+
+		debugLog('CalendarExpresso::GetMessageList('.$id_owner.')');
+		if ($folderid != "calendar") return false;
+
+		$messages = array();
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			if ($cutoffdate == 0) $result = pg_query($this->db, "select c.cal_id, c.last_update, c.datetime, c.cal_type from phpgw_cal c join phpgw_cal_user u on (c.cal_id = u.cal_id) where u.cal_login ='" . $id_owner . "'and c.reference = 0;");
+			else $result = pg_query($this->db, "select c.cal_id, c.last_update, c.datetime, c.cal_type from phpgw_cal c join phpgw_cal_user u on (c.cal_id = u.cal_id) where u.cal_login ='" . $id_owner . "'and c.reference = 0 and c.edatetime > " . $cutoffdate . ";");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row = pg_fetch_row($result)) {
+				$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 ($recur_enddate < $row[2]) 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.
+					}
+				}
+				$LatestUpdate = $row[1];
+				$message = array();
+				$message["id"] = $row[0];
+				$message["mod"] = substr($LatestUpdate, 0, strlen($LatestUpdate)-3);
+				$message["flags"] = 1; // always 'read'
+				$messages[] = $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());
+		}
+		return $messages;
+	}
+
+	function GetFolderList() {
+		debugLog('CalendarExpresso::GetFolderList()');
+		$calendars = array();
+		$folder = $this->StatFolder("calendar");
+		$calendars[] = $folder;
+		//return $calendars;
+		return $folder;
+	}
+
+	function GetFolder($id) {
+		debugLog('CalendarExpresso::GetFolder('.$id.')');
+		if($id == "calendar") {
+			$folder = new SyncFolder();
+			$folder->serverid = $id;
+			$folder->parentid = "0";
+			$folder->displayname = "Calendario";
+			$folder->type = SYNC_FOLDER_TYPE_APPOINTMENT;
+			return $folder;
+		} else return false;
+	}
+
+	function StatFolder($id) {
+		debugLog('CalendarExpresso::StatFolder('.$id.')');
+		$folder = $this->GetFolder($id);
+		$stat = array();
+		$stat["id"] = $id;
+		$stat["parent"] = $folder->parentid;
+		$stat["mod"] = $folder->displayname;
+		return $stat;
+	}
+
+	function GetAttachmentData($attname) {
+		return false;
+	}
+
+	function StatMessage($folderid, $id) {
+		debugLog('CalendarExpresso::StatMessage('.$folderid.', '.$id.')');
+		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;
+		 		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());
+		}
+		return false;
+	}
+
+	function GetMessageDAO($id) {
+		debugLog('CalendarExpresso::GetMessageDAO('.$id.', ..)');
+		if(trim($id == "")) return false;
+		$message = new SyncAppointment();
+		try {
+			$has_parent_event = false;
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			//Evento
+			$result = pg_query($this->db,"select config_value from phpgw_config WHERE config_name = 'hostname' AND config_app = 'phpgwapi';");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$uid = "-@" . pg_fetch_result($result, 0, 0) . "-" . $id;
+			$result = pg_query($this->db, "select cal_id, title, location, mdatetime, datetime, edatetime, description, reference, is_public, category from phpgw_cal where cal_id = " . $id . ";");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			if ($row = pg_fetch_row($result)) {
+				if ($row[7] != 0) $has_parent_event = true;
+				else $has_parent_event = false;
+				if (!$has_parent_event) {
+					$message->fileas = $id;
+					$message->uid = utf8_encode($uid);
+					if(isset($row[3])) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$message->dtstamp = $row[3]; // phpgw_cal.mdatetime
+						date_default_timezone_set($tz);
+					}
+					if(isset($row[4])) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$message->starttime =  $row[4]; // phpgw_cal.datetime
+						date_default_timezone_set($tz);
+					}
+					if(isset($row[5])) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$message->endtime = $row[5]; // phpgw_cal.edatetime
+						date_default_timezone_set($tz);
+					}
+				}
+				if ($has_parent_event) {
+					$message->exceptionstarttime = $row[4];
+					$message->deleted = 0;
+				}
+				if(isset($row[1])) {
+					$message->subject = utf8_encode($row[1]); // phpgw_cal.title
+				}
+				if(isset($row[2])) {
+					$message->location = utf8_encode($row[2]); // phpgw_cal.location
+				}
+				if(isset($row[6]) and $row[6] != "") {
+					$message->body = utf8_encode(str_replace('\\n', chr(13) . chr(10), $this->escape($row[6]))); // phpgw_cal.description
+					$message->bodysize = strlen($message->body);
+					$message->bodytruncated = 0;
+				}
+				if (isset($row[8]) and $row[8] == 1) $message->sensitivity = 0; // 0 - Normal
+				else $message->sensitivity = 2; // 2 - Privado
+				if (isset($row[9]) and $row[9] != "") {
+					$result = pg_query($this->db,"select cat_name from phpgw_categories WHERE cat_id in(" . $row[9] . ");");
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					while ($cat_name = pg_fetch_row($result)) {
+						if (!isset($message->categories)) $message->categories = array();
+						array_push($message->categories, utf8_encode($cat_name[0]));
+					}
+				}
+				$message->alldayevent = 0; // (0 - Não(default), 1- Sim)
+				$message->busystatus = 2; // 2 - Ocupado
+				//TODO: Tratar $message - timezone
+				//$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);
+				$message->timezone = base64_encode($this->_getSyncBlobFromTZ($tz));
+				//TODO: Se necessario, tratar $message - meetingstatus
+				//TODO: Se necessario, tratar AppointmentReplyTime e ResponseType
+			}
+
+			//Alarme do Evento - O alarme nao sera sincronizado por enquanto.
+			/* Esse exemplo sincroniza o alarme mais distante do evento com o dispositivo movel
+			* $result = pg_query("select data from phpgw_async where id like 'cal:" . $id . ":%' and data like '%enabled%' order by next desc;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			if ($data = pg_fetch_result($result, 0, 0)) {
+			$array_data = unserialize($data);
+			$offset = $array_data["offset"];
+			if (isset($array_data["offset"])) {
+			if ($offset >= 60) $message->reminder = intval($offset / 60);
+			else $message->reminder = 0;
+			}
+			}*/
+			if (!$has_parent_event) {
+				//Recorrencia do evento
+				//TODO: Implementar recorrencia mensal por dia e anual por dia, recur - type 3 e 6 respectivamente. O Expresso ainda nao suporta isso. :(
+				$result = pg_query($this->db,"select recur_type, recur_use_end, recur_enddate, recur_interval, recur_data, recur_exception from phpgw_cal_repeats where cal_id = " . $id . "and recur_type in (1,2,3,5);");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				if ($row = pg_fetch_row($result)){
+					$recur = new SyncRecurrence();
+					// Converte os tipos do Expresso para os tipos do Protocolo ActiveSync
+					switch ($row[0]) {
+						case 1: //repeticao diaria
+							$recur->type = 0;
+							break;
+						case 2: //repeticao semanal
+							$recur->type = 1;
+							break;
+						case 3: //repeticao mensal por data
+							$recur->type = 2;
+							break;
+						case 5: //repeticao anual por data
+							$recur->type = 5;
+							break;
+					}
+					if (isset($row[2])) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$recur->until = $row[2];
+						date_default_timezone_set($tz);
+					}
+					if (isset($row[3]) and $row[3] != 0) $recur->interval = $row[3];
+					else $recur->interval = 1;
+					//TODO: Se o Expresso estiver utilizando o campo phpgw_cal_repeats.recur_use_end, implementar.
+					if ($recur->type == 1 or $recur->type == 3 or $recur->type == 6) {
+						if (isset($row[4])) $recur->dayofweek = $row[4];
+					}
+					if ($recur->type == 2 or $recur->type == 5) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$array_startdatetime	= getdate($message->starttime);
+						$recur->dayofmonth = $array_startdatetime['mday'];
+						date_default_timezone_set($tz);
+
+					}
+					if ($recur->type == 5 or $recur->type == 6) {
+						$tz = date_default_timezone_get();
+						date_default_timezone_set('UTC');
+						$array_startdatetime	= getdate($message->starttime);
+						$recur->monthofyear = $array_startdatetime['mon'];
+						date_default_timezone_set($tz);
+
+					}
+					$message->recurrence = $recur;
+				}
+
+				//Eventos Unicos da Recorrencia Editados
+				$result = pg_query($this->db, "select cal_id from phpgw_cal where reference = " . $id . ";");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				while ($row_recur_exception = pg_fetch_row($result)) {
+					$recur_exception = $this->GetMessageDAO($row_recur_exception[0]);
+					if ($recur_exception instanceof SyncAppointment) {
+						if (!isset($message->exceptions)) $message->exceptions = array();
+						array_push($message->exceptions, $recur_exception);
+					}
+				}
+
+				//Eventos Unicos da Recorrencia Deletados
+				if (isset($row[5]) and $row[5] != "") {
+					$array_timestamps_recur_deleted = explode(",",$row[5]);
+					foreach ($array_timestamps_recur_deleted as $timestamp_recur_deleted) {
+						$recur_deleted = new SyncAppointment();
+						$recur_deleted->deleted = '1';
+						$recur_deleted->exceptionstarttime = (integer)$timestamp_recur_deleted;
+						if (!isset($message->exceptions)) $message->exceptions = array();
+						array_push($message->exceptions, $recur_deleted);
+					}
+				}
+				//TODO: Tratar Participantes do Evento
+			}
+			$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());
+			return;
+		}
+		return $message;
+	}
+
+	function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) {
+		debugLog('CalendarExpresso::GetMessage('.$folderid.', '.$id.', ..)');
+		if($folderid != "calendar")	return;
+		if(trim($id == "")) return;
+		$message = $this->GetMessageDAO($id);
+		return $message;
+
+		//$message->?????  //phpgw_cal.priority (1 - baixa, 2 - normal, 3 - alta) //Quando for criar um registro o valor será 2.
+		//$message->?????  //phpgw_cal.groups (Não vou atribuir nenhum valor)
+		//$message->?????  //phpgw_cal.cal_type (quando for criar um novo registro vou inserir R para Recursivo e E para não recursivo). Quando Alterar um registro entre de Recursivo para não Recursivo e vice-versa vou fazer o mesmo da inclusão. Quando alterar entre dois tipos não recursivos, não faço nada.
+		//$message->?????  //phpgw_cal.ex_participants
+		//$message->?????  //phpgw_cal_user.cal_status
+		//$message->?????  //phpgw_cal_user.cal_type
+		//$message->?????  //phpgw_cal_repeats.recur_interval
+		//$message->?????  //phpgw_cal_repeats.recur_data
+		//$message->?????  //phpgw_cal_repeats.recur_exception
+
+		//$message->timezone  //phpgw?????
+		//$message->organizername //phpgw?????
+		//$message->organizeremail //phpgw????
+		//$message->attendees // phpgw????  (SyncAttendee array)
+
+		//$syncAttendee = new SyncAttendee();
+		//$syncAttendee->email  //phpgw????
+		//$syncAttendee->name   //phpgw????
+	}
+
+	function DeleteMessage($folderid, $id) {
+		debugLog('CalendarExpresso::DeleteMessage('.$folderid.', '.$id.', ..)');
+		if (!isset($id)) return false;
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+		$result = pg_query($this->db, "select owner from phpgw_cal where cal_id = " . $id . ";");
+		if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+		if ($id_owner != pg_fetch_result($result, 0, 0)) return false;
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_delete($this->db, "phpgw_cal_user", array('cal_id' => $id));
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_delete($this->db, "phpgw_cal", array('reference' => $id));
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_delete($this->db, "phpgw_cal_repeats", array('cal_id' => $id));
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_query("delete from phpgw_async where id like 'cal:" . $id . "%';");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_delete($this->db, "phpgw_cal_extra", array('cal_id' => $id));
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_delete($this->db, "phpgw_cal", array('cal_id' => $id));
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$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());
+			return false;
+		}
+		return true;
+	}
+
+	function SetReadFlag($folderid, $id, $flags) {
+		return false;
+	}
+
+	function ChangeMessageDAO($id, $message) {
+		debugLog('CalendarExpresso::ChangeMessageDAO('.$id.', ..)');
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$found_cal_id = false;
+			$found_cal_user = false;
+			$found_cal_repeats = false;
+			if ($id != false) {
+				$result = pg_query($this->db, "select cal_id, cal_type, owner from phpgw_cal where cal_id = " . $id . ";");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				// tenta localizar cal_id para fazer Update
+				while ($row = pg_fetch_row($result)) {
+					if(isset($row[0])) {
+						if ($id_owner != $row[2]) return false; // Nao atualiza evento de outro proprietario
+						$cal_id = $row[0];
+						$cal_type_from_DB = $row[1];
+						$found_cal_id = true;
+					}
+				}
+				// Verifica se ja existe o registro do evento na tabela phpgw_cal_user
+				$result = pg_query($this->db, "select cal_id, cal_login from phpgw_cal_user where cal_id = " . $cal_id . " and cal_login = '" . $id_owner . "';");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				if ($row = pg_fetch_row($result)) {
+					if(isset($row[0])) {
+						$found_cal_user = true;
+					}
+				}
+				// Verifica se ja existe o registro de recorrencia do evento na tabela phpgw_cal_repeats
+				$result = pg_query($this->db, "select cal_id from phpgw_cal_repeats where cal_id = " . $cal_id . ";");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				if ($row = pg_fetch_row($result)) {
+					if(isset($row[0])) {
+						$found_cal_repeats = true;
+					}
+				}
+			}
+
+			//Insere/Atualiza tabela phpgw_cal
+			if(isset($message->uid)) {
+				$arrayCal["uid"] = $this->truncateString(utf8_decode($message->uid),255);
+			}
+			if(isset($message->subject)) {
+				$arrayCal["title"] = $this->truncateString(utf8_decode($message->subject),80);
+			}
+			if(isset($message->location)) {
+				$arrayCal["location"] = $this->truncateString(utf8_decode($message->location),255);
+			}
+			if(isset($message->dtstamp)) {
+				$arrayCal["mdatetime"] = $message->dtstamp;
+			}
+			if(isset($message->starttime)) {
+				$arrayCal["datetime"] = $message->starttime;
+			}
+			if(isset($message->endtime)) {
+				$arrayCal["edatetime"] = $message->endtime;
+			}
+			if(isset($message->sensitivity) and $message->sensitivity == 0) {
+				$arrayCal["is_public"] = 1; // 1 - Normal
+			} else $arrayCal["is_public"] = 0; // 0 - Privado
+
+			if(isset($message->rtf)) {
+				$rtf_to_ascii = new rtf();
+				$rtf_to_ascii->output("ascii");
+				$result_loadrtf = $rtf_to_ascii->loadrtf(base64_decode($message->rtf));
+				if ($result_loadrtf == true) $rtf_to_ascii->parse();
+				$arrayCal["description"] = $rtf_to_ascii->out;
+//			} else {
+//				$arrayCal["description"] = '';
+			}
+			$arrayCal["category"] = '';
+			if (isset($message->categories)) {
+				foreach ($message->categories as $category) {
+					$cat_id = $this->addCategory($category);
+					if ($cat_id != false) {
+						if ($arrayCal["category"] == '') {
+							$arrayCal["category"] = $cat_id;
+						} else {
+							$arrayCal["category"] .= ',' . $cat_id;
+						}
+					}
+				}
+			}
+			$tz_CEL = $this->_getTZFromSyncBlob(base64_decode($message->timezone));
+			//TODO: Atribuir valores para ex_participants
+			$arrayCal["ex_participants"] = '';
+			//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)) {
+			//		 	$arrayCal["alldayevent"] = $this->truncateString(utf8_decode($message->alldayevent),30);
+			//		 }
+			if (!$found_cal_id){
+				$result = pg_query("select nextval('seq_phpgw_cal');");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$cal_id = pg_fetch_result($result, 0, 0);
+				$arrayCal["cal_id"] = $cal_id;
+				$arrayCal["owner"] = $id_owner;
+				if (isset($message->recurrence)) $arrayCal["cal_type"] = 'M';
+				else $arrayCal["cal_type"] = 'E';
+				$result = pg_insert($this->db, 'phpgw_cal', $arrayCal);
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			} else {
+				if (isset($cal_type_from_DB)) {
+					if (isset($message->recurrence)) $arrayCal["cal_type"] = 'M';
+					else if ($cal_type_from_DB == 'M') $arrayCal["cal_type"] = 'E';
+				}
+				$result = pg_update($this->db, 'phpgw_cal', $arrayCal, array('cal_id' => $cal_id));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Insere/Atualiza tabela phpgw_cal_user
+			$arrayCalUser["cal_type"] = 'u';
+			if (!$found_cal_user){
+				$arrayCalUser["cal_id"] = $cal_id;
+				$arrayCalUser["cal_login"] = $id_owner;
+				//TODO: Tratar os outros valores de cal_status
+				$arrayCalUser["cal_status"] = 'A';
+				$result = pg_insert($this->db, 'phpgw_cal_user', $arrayCalUser);
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			} else {
+				$result = pg_update($this->db, 'phpgw_cal_user', $arrayCalUser, array('cal_id' => $cal_id, 'cal_login' => $id_owner));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+			if (!$id) {
+				$id = $cal_id;
+			}
+
+			//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)) {
+				switch ($message->recurrence->type) {
+					case 0: //repeticao diaria
+						$arrayRecur["recur_type"] = 1;
+						$type_name = "day";
+						break;
+					case 1: //repeticao semanal
+						$arrayRecur["recur_type"] = 2;
+						$type_name = "week";
+						break;
+					case 2: //repeticao mensal por data
+						$arrayRecur["recur_type"] = 3;
+						$type_name = "month";
+						break;
+					case 5: //repeticao anual por data
+						$arrayRecur["recur_type"] = 5;
+						$type_name = "year";
+						break;
+				}
+				if (isset($message->recurrence->interval)) {
+					$arrayRecur["recur_interval"] = $message->recurrence->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;
+						/*$day = $message->recurrence->dayofweek;
+						 $day_of_week_counter = 0;
+						 if (($day & 1) > 0) $day_of_week_counter++;
+						 if (($day & 2) > 0) $day_of_week_counter++;
+						 if (($day & 4) > 0) $day_of_week_counter++;
+						 if (($day & 8) > 0) $day_of_week_counter++;
+						 if (($day & 16) > 0) $day_of_week_counter++;
+						 if (($day & 32) > 0) $day_of_week_counter++;
+						 if (($day & 64) > 0) $day_of_week_counter++;*/
+					}
+				}
+				//TODO: Converter ocurrences para until.
+				/*if (!isset($message->recurrence->until) and (isset($message->recurrence->occurrences))) {
+				$tz = date_default_timezone_get();
+				date_default_timezone_set('UTC');
+				$message->recurrence->until = strtotime(date("Y-m-d H:i:s", $message->starttime) . " +" . $message->recurrence->occurrences * $message->recurrence->interval . " " . $type_name);
+				unset($message->recurrence->occurrences);
+				date_default_timezone_set($tz);
+				}*/
+				if (isset($message->recurrence->until)){
+					$defaultTZ = new DateTimeZone(date_default_timezone_get());
+					$gmtTZ = new DateTimeZone('GMT');
+					$gmtDate = new DateTime("now", $gmtTZ);
+					$GMT_EX = $defaultTZ->getOffset($gmtDate);
+					$GMT_CEL = -(($tz_CEL["bias"] + $tz_CEL["dstbias"]) * 60);
+
+					$tz = date_default_timezone_get();
+					date_default_timezone_set('UTC');
+					$day_CEL = date("d", $message->starttime +  $GMT_CEL);
+					$day_EX = date("d", $message->starttime +  $GMT_EX);
+
+					if ($day_EX > $day_CEL) $arrayRecur["recur_enddate"] = $message->recurrence->until + $GMT_CEL - $GMT_EX + 86400;
+					else if ($day_EX < $day_CEL) $arrayRecur["recur_enddate"] = $message->recurrence->until + $GMT_CEL - $GMT_EX - 86400;
+					else $arrayRecur["recur_enddate"] = $message->recurrence->until;
+					date_default_timezone_set($tz);
+					/*					//$arrayRecur["recur_enddate"] = $message->recurrence->until - $offsetTZ;
+					 $hour = date("G", $message->starttime - (($tz["bias"] - $tz["dstbias"]) * 60));
+					 $min = date("i", $message->starttime - (($tz["bias"] - $tz["dstbias"]) * 60));
+					 $sec = date("s", $message->starttime - (($tz["bias"] - $tz["dstbias"]) * 60));
+					 	
+					 $arrayRecur["recur_enddate"] = $message->recurrence->until + ($hour * 3600) + ($min * 60) + $sec + $offsetTZ + (($tz["bias"] - $tz["dstbias"]) * 60);*/
+				} else {
+					$arrayRecur["recur_enddate"] = 1893283200; // Se nao tem data de termino seta para 30/12/2029
+				}
+
+				// Trata excecoes da recorrencia
+				if (isset($message->exceptions)){
+					//if ($recur_exception_changed_starttimes != "") {
+					//Tem Excecoes de recorrencia do tipo Edicao
+					// Verifica se ja existe o registro do evento de excecao de recorrencia na tabela phpgw_cal
+					//foreach ($array_recur_exception_changed_starttime as $except_starttime) {
+					$recur_exception_changed_ids = "";
+					$recur_exception_deleted_starttimes = "";
+					foreach ($message->exceptions as $recur_exception) {
+						if ($recur_exception->deleted == 0){ //Tem Excecoes do Tipo Edicao
+							$found_recur_exception_changed = true;
+							if (isset($arrayExceptCal)) unset($arrayExceptCal);
+							if (isset($arrayExceptCalUser)) unset($arrayExceptCalUser);
+							if (isset($except_cal_id)) unset($except_cal_id);
+							if (isset($except_cal_type_from_DB)) unset($except_cal_type_from_DB);
+							$found_except_cal_id = false;
+							$found_except_cal_user = false;
+							$result = pg_query($this->db, "select cal_id, cal_type from phpgw_cal where reference = " . $id . " and datetime = " . $recur_exception->exceptionstarttime . ";");
+							if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+							// tenta localizar cal_id para fazer Update
+							while ($row = pg_fetch_row($result)) {
+								if(isset($row[0])) {
+									$except_cal_id = $row[0];
+									$except_cal_type_from_DB = $row[1];
+									$found_except_cal_id = true;
+								}
+							}
+							// Verifica se ja existe o registro do evento de excecao de recorrencia na tabela phpgw_cal_user
+							if (isset($except_cal_id)) {
+								$result = pg_query($this->db, "select cal_id, cal_login from phpgw_cal_user where cal_id = " . $except_cal_id . " and cal_login = '" . $id_owner . "';");
+								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+								if ($row = pg_fetch_row($result)) {
+									if(isset($row[0])) {
+										$found_except_cal_user = true;
+									}
+								}
+							}
+							//Insere/Atualiza tabela phpgw_cal
+							$arrayExceptCal["reference"] = $cal_id;
+							if(isset($message->uid)) {
+								$arrayExceptCal["uid"] = $this->truncateString(utf8_decode($message->uid),255);
+							}
+							if(isset($recur_exception->subject)) {
+								$arrayExceptCal["title"] = $this->truncateString(utf8_decode($recur_exception->subject),80);
+							} elseif(isset($message->subject)) {
+								$arrayExceptCal["title"] = $this->truncateString(utf8_decode($message->subject),80);
+							}
+							if(isset($recur_exception->location)) {
+								$arrayExceptCal["location"] = $this->truncateString(utf8_decode($recur_exception->location),255);
+							} else if(isset($message->location)) {
+								$arrayExceptCal["location"] = $this->truncateString(utf8_decode($message->location),255);
+							}
+							if(isset($recur_exception->dtstamp)) {
+								$arrayExceptCal["mdatetime"] = $recur_exception->dtstamp;
+							} elseif(isset($message->dtstamp)) {
+								$arrayExceptCal["mdatetime"] = $message->dtstamp;
+							}
+							if(isset($recur_exception->starttime)) {
+								$arrayExceptCal["datetime"] = $recur_exception->starttime;
+							} else if(isset($message->starttime)) {
+								$arrayExceptCal["datetime"] = $message->starttime;
+							}
+							if(isset($recur_exception->endtime)) {
+								$arrayExceptCal["edatetime"] = $recur_exception->endtime;
+							}elseif(isset($message->endtime)) {
+								$arrayExceptCal["edatetime"] = $message->endtime;
+							}
+							if(isset($recur_exception->sensitivity) and $recur_exception->sensitivity == 0) {
+								$arrayExceptCal["is_public"] = 1; // 1 - Normal
+							}elseif (isset($recur_exception->sensitivity) and $recur_exception->sensitivity == 1){
+								$arrayExceptCal["is_public"] = 0; // 0 - Privado
+							}elseif(isset($message->sensitivity) and $message->sensitivity == 0) {
+								$arrayExceptCal["is_public"] = 1; // 1 - Normal
+							}else $arrayExceptCal["is_public"] = 0; // 0 - Privado
+							if(isset($recur_exception->rtf) or isset($message->rtf)) {
+								$rtf_to_ascii = new rtf();
+								$rtf_to_ascii->output("ascii");
+								if(isset($recur_exception->rtf)) $result_loadrtf = $rtf_to_ascii->loadrtf(base64_decode($recur_exception->rtf));
+								elseif(isset($message->rtf)) $result_loadrtf = $rtf_to_ascii->loadrtf(base64_decode($message->rtf));
+								if ($result_loadrtf == true) $rtf_to_ascii->parse();
+								$arrayExceptCal["description"] = $rtf_to_ascii->out;
+				//			} else {
+				//				$arrayExceptCal["description"] = '';
+							}
+							$arrayExceptCal["category"] = '';
+							if (isset($recur_exception->categories)) {
+								foreach ($recur_exception->categories as $category) {
+									$cat_id = $this->addCategory($category);
+									if ($cat_id != false) {
+										if ($arrayExceptCal["category"] == '') {
+											$arrayExceptCal["category"] = $cat_id;
+										} else {
+											$arrayExceptCal["category"] .= ',' . $cat_id;
+										}
+									}
+								}
+							}elseif (isset($message->categories)) {
+								foreach ($message->categories as $category) {
+									$cat_id = $this->addCategory($category);
+									if ($cat_id != false) {
+										if ($arrayExceptCal["category"] == '') {
+											$arrayExceptCal["category"] = $cat_id;
+										} else {
+											$arrayExceptCal["category"] .= ',' . $cat_id;
+										}
+									}
+								}
+							}
+							//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)) {
+							//		 	$arrayCal["alldayevent"] = $this->truncateString(utf8_decode($message->alldayevent),30);
+							//		 }
+							if (!$found_except_cal_id){
+								$result = pg_query("select nextval('seq_phpgw_cal');");
+								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+								$except_cal_id = pg_fetch_result($result, 0, 0);
+								$arrayExceptCal["cal_id"] = $except_cal_id;
+								$arrayExceptCal["owner"] = $id_owner;
+								$arrayExceptCal["cal_type"] = 'E';
+								$result = pg_insert($this->db, 'phpgw_cal', $arrayExceptCal);
+								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+							} 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));
+							}
+
+							// Insere/Atualiza Excecoes de Recorrencia na tabela phpgw_cal_user
+							$arrayExceptCalUser["cal_type"] = 'u';
+							if (!$found_except_cal_user){
+								$arrayExceptCalUser["cal_id"] = $except_cal_id;
+								$arrayExceptCalUser["cal_login"] = $id_owner;
+								//TODO: Tratar os outros valores de cal_status
+								$arrayExceptCalUser["cal_status"] = 'A';
+								$result = pg_insert($this->db, 'phpgw_cal_user', $arrayExceptCalUser);
+								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+							} else {
+								$result = pg_update($this->db, 'phpgw_cal_user', $arrayExceptCalUser, array('cal_id' => $except_cal_id, 'cal_login' => $id_owner));
+								if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+							}
+							if ($recur_exception_changed_ids == "") {
+								$recur_exception_changed_ids = $except_cal_id;
+							} else {
+								$recur_exception_changed_ids .= "," . $except_cal_id;
+							}
+						} else {
+							if ($recur_exception_deleted_starttimes == "") {
+								$recur_exception_deleted_starttimes = $recur_exception->exceptionstarttime;
+							} else {
+								$recur_exception_deleted_starttimes .= "," . $recur_exception->exceptionstarttime;
+							}
+						}
+					}
+					if ($recur_exception_deleted_starttimes != "") {
+						//Tem Excecoes de recorrencia do tipo Exclusao
+						$arrayRecur["recur_exception"] = $recur_exception_deleted_starttimes;
+					} else {
+						$arrayRecur["recur_exception"] = "";
+					}
+					if ($recur_exception_changed_ids == "") {
+						// Se nao tem excecoes de recorrencia do tipo edicao, entao deleta as excecoes da recorrencia
+						$result = pg_query($this->db, "delete from phpgw_cal_user where cal_id in (select cal_id from phpgw_cal where reference = " . $cal_id . ");");
+						if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+						$result = pg_query($this->db, "delete from phpgw_cal where reference = " . $cal_id . ";");
+						if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					} else {
+						//Deleta as excecoes da recorrencia, exceto as inseridas/atualizadas acima
+						$result = pg_query($this->db, "delete from phpgw_cal_user where cal_id in (select cal_id 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));
+						$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));
+					}
+				} else {
+					// Se nao tem excecoes da recorrencia entao
+					$arrayRecur["recur_exception"] = "";
+					$result = pg_query($this->db, "delete from phpgw_cal_user where cal_id in (select cal_id from phpgw_cal where reference = " . $cal_id . ");");
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$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 (!$found_cal_repeats){
+					$arrayRecur["cal_id"] = $cal_id;
+					$result = pg_insert($this->db, 'phpgw_cal_repeats', $arrayRecur);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} 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));
+				}
+			} elseif ($found_cal_id) {
+				// Se nao tem recorrencia entao
+				$result = pg_query($this->db, "delete from phpgw_cal_user where cal_id in (select cal_id from phpgw_cal where reference = " . $cal_id . ");");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_query($this->db, "delete from phpgw_cal where reference = " . $cal_id . ";");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$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));
+			}
+			$result = pg_query($this->db,"COMMIT;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			//TODO: Implementar Participantes
+		} catch (Exception $e) {
+			debugLog("exception -> " . $e->getMessage() . " - ARQUIVO: " . $e->getFile() . " - LINHA: " . $e->getLine());
+			pg_query($this->db,"ROLLBACK;");
+			return false;
+		}
+		return $id;
+	}
+
+	function ChangeMessage($folderid, $id, $message) {
+		debugLog('CalendarExpresso::ChangeMessage('.$folderid.', '.$id.', ..)');
+		$id = $this->ChangeMessageDAO($id, $message);
+		if ($id != false) return $this->StatMessage($folderid, $id);
+		else return false;
+	}
+
+	function MoveMessage($folderid, $id, $newfolderid) {
+		return false;
+	}
+
+	function addCategory($category) {
+		$cat_id = false;
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_query("SELECT cat_id from phpgw_categories WHERE cat_owner = -1 AND cat_appname = 'calendar' AND LOWER(to_ASCII(cat_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($category)))) . "';");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			if (!($cat_id = pg_fetch_result($result, 0, 0))){
+				$result = pg_query("SELECT cat_id from phpgw_categories WHERE cat_owner = " . $id_owner . " AND cat_appname = 'calendar' AND LOWER(to_ASCII(cat_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($category)))) . "';");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				if (!($cat_id = pg_fetch_result($result, 0, 0))){
+					$result = pg_query("select nextval('seq_phpgw_categories');");
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$cat_id = pg_fetch_result($result, 0, 0);
+					$arrayCat["cat_id"] = $cat_id;
+					$arrayCat["cat_owner"] = $id_owner;
+					$arrayCat["cat_access"] = 'public';
+					$arrayCat["cat_appname"] = 'calendar';
+					$arrayCat["cat_name"] = utf8_decode($category);
+					$arrayCat["cat_description"] = '';
+					$arrayCat["cat_data"] = 'N;';
+					$arrayCat["last_mod"] = time();
+					$result = pg_insert($this->db, 'phpgw_categories', $arrayCat);
+					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());
+			return false;
+		}
+		return $cat_id;
+	}
+
+	// -----------------------------------
+
+	function escape($data){
+		if (is_array($data)) {
+			foreach ($data as $key => $val) {
+				$data[$key] = $this->escape($val);
+			}
+			return $data;
+		}
+		$data = str_replace("\r\n", "\n", $data);
+		$data = str_replace("\r", "\n", $data);
+		$data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data);
+		//return u2wi($data);
+		return $data;
+	}
+
+	function unescape($data){
+		$data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
+		return $data;
+	}
+
+	function  removeAccents($data){
+		$data = strtr($data,"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÜÚÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùüúþÿ","aaaaaaaceeeeiiii noooooxouuutbaaaaaaaceeeeiiii nooooo/ouuuty");
+		return $data;
+	}
+
+	function truncateString($string, $size)
+	{
+		if(strlen($string) <= $size) return $string;
+		else return substr($string, 0, $size - 1);
+	}
+
+	function parseOwner($id_owner)
+	{
+		$pos = strripos($id_owner, '\\');
+		if ($pos === false) {
+			if (strlen($id_owner) <= 2) return false;
+			else return substr($id_owner, 0, strlen($id_owner)-2);
+		} else {
+			if (strlen($id_owner) <= $pos + 3) {
+				return false;
+			} else {
+				return substr(substr($id_owner, $pos + 1), 0, strlen(substr($id_owner, $pos + 1))-2);
+			}
+		}
+	}
+
+	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);
+		return $tz;
+	}
+
+	// Unpack timezone info from Sync
+	function _getTZFromSyncBlob($data) {
+		$tz = unpack(    "lbias/a64name/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" .
+                        "lstdbias/a64name/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis/" .
+                        "ldstbias", $data);
+
+		// Make the structure compatible with class.recurrence.php
+		$tz["timezone"] = $tz["bias"];
+		$tz["timezonedst"] = $tz["dstbias"];
+
+		return $tz;
+	}
+
+	// Pack timezone info for Sync
+	function _getSyncBlobFromTZ($tz) {
+		$packed = pack("la64vvvvvvvv" . "la64vvvvvvvv" . "l",
+		$tz["bias"], "", 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"],
+		$tz["stdbias"], "", 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"],
+		$tz["dstbias"]);
+
+		return $packed;
+	}
+};
+?>
Index: contrib/z-push/backend/BackendContactsExpresso.php
===================================================================
--- contrib/z-push/backend/BackendContactsExpresso.php	(revision 3637)
+++ contrib/z-push/backend/BackendContactsExpresso.php	(revision 3637)
@@ -0,0 +1,1105 @@
+<?php
+/***********************************************
+ * File      :   BackendContactsExpresso.php
+ * Project   :   Z-Push
+ * Descr     :   This Contact Backend is for Expresso Groupware.
+ *
+ * Created   :   29.09.2010 - emerson-faria.nobre@serpro.gov.br
+ *
+ * ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+ * This file is distributed under GPL v2.
+ * Consult LICENSE file for details
+ ************************************************/
+include_once('diffbackend.php');
+include_once('include/z_RTF.php');
+include_once "include/dbconnect.php";
+
+class BackendContactsExpresso extends BackendDiff {
+	var $_user;
+	var $_devid;
+	var $_protocolversion;
+	var $db;
+
+	function __construct()
+	{
+		$this->db = dbconnect();
+
+		if (!$this->db) {
+			echo "A DB connect error occured.\n";
+			exit;
+		}
+	}
+
+	function Setup($user, $devid, $protocolversion) {
+		$this->_user = $user;
+		$this->_devid = $devid;
+		$this->_protocolversion = $protocolversion;
+		//TODO: Autenticar Usuario no LDAP
+
+		return true;
+	}
+
+	function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
+		return false;
+	}
+
+	function GetWasteBasket() {
+		return false;
+	}
+
+	function GetMessageList($folderid, $cutoffdate) {
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+
+		debugLog('ContactsExpresso::GetMessageList('.$id_owner.')');
+		$messages = array();
+		$ids = array();
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result = pg_query($this->db, "select given_names, family_names, last_update, id_contact from phpgw_cc_contact where id_owner = " . $id_owner . ";");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row = pg_fetch_row($result)) {
+				$message = array();
+				$message["id"] = $row[3];
+				$message["mod"] = substr($row[2], 0, strlen($row[2])-3);
+				$message["flags"] = 1; // always 'read'
+				$messages[] = $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());
+		}
+		return $messages;
+	}
+
+	function GetFolderList() {
+		debugLog('ContactsExpresso::GetFolderList()');
+		$contacts = array();
+		$folder = $this->StatFolder("root");
+		$contacts[] = $folder;
+		return $folder;
+		//return $contacts;
+	}
+
+	function GetFolder($id) {
+		debugLog('ContactsExpresso::GetFolder('.$id.')');
+		if($id == "root") {
+			$folder = new SyncFolder();
+			$folder->serverid = $id;
+			$folder->parentid = "0";
+			$folder->displayname = "Contatos";
+			$folder->type = SYNC_FOLDER_TYPE_CONTACT;
+
+			return $folder;
+		} else return false;
+	}
+
+	function StatFolder($id) {
+		debugLog('ContactsExpresso::StatFolder('.$id.')');
+		$folder = $this->GetFolder($id);
+
+		$stat = array();
+		$stat["id"] = $id;
+		$stat["parent"] = $folder->parentid;
+		$stat["mod"] = $folder->displayname;
+
+		return $stat;
+	}
+
+	function GetAttachmentData($attname) {
+		return false;
+	}
+
+	function StatMessage($folderid, $id) {
+		debugLog('ContactsExpresso::StatMessage('.$folderid.', '.$id.')');
+		if($folderid != "root") return false;
+
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result_contact = pg_query($this->db, "select last_update from phpgw_cc_contact where id_contact = " . $id . ";");
+			if ($result_contact == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row_contact = pg_fetch_row($result_contact)) {
+				if(isset($row_contact[0])) {
+					$message = array();
+					$message["mod"] = substr($row_contact[0], 0, strlen($row_contact[0])-3);
+					$message["id"] = $id;
+					$message["flags"] = 1;
+					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());
+		}
+		return false;
+	}
+
+	function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) {
+		debugLog('ContactsExpresso::GetMessage('.$folderid.', '.$id.', ..)');
+		if($folderid != "root")	return;
+
+		// Parse the database into object
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return;
+
+		$message = new SyncContact();
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			$result_contact = pg_query($this->db, "select id_contact, id_owner, id_status, photo, alias, id_prefix, given_names, family_names, names_ordered, id_suffix, birthdate, sex, pgp_key, notes, is_global, last_status, last_update, category, web_page, corporate_name, job_title, department from phpgw_cc_contact where id_contact = " . $id . ";");
+			if ($result_contact == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row_contact = pg_fetch_row($result_contact)) {
+				//if(isset($row_contact[3])) {
+				//	$message->picture = base64_encode($row_contact[3]);
+				//}
+				if(isset($row_contact[4])) {
+					$message->nickname = utf8_encode($row_contact[4]);
+				}
+				if(isset($row_contact[7])) {
+					$arrayFamilyName = explode(' ',trim($row_contact[7]));
+					if (sizeof($arrayFamilyName) > 1) {
+						$message->lastname = utf8_encode($arrayFamilyName[sizeof($arrayFamilyName) - 1]);
+						for ($i = 0; $i < (sizeof($arrayFamilyName) - 1); $i++) {
+							$message->middlename .= " " . utf8_encode($arrayFamilyName[$i]);
+						}
+						$message->middlename = trim($message->middlename);
+					} else {
+						$message->lastname = utf8_encode(trim($row_contact[7]));
+					}
+					$message->fileas = $message->lastname;
+				}
+				if(isset($row_contact[6])) {
+					$message->firstname = utf8_encode(trim($row_contact[6]));
+					if(isset($row_contact[7])) $message->fileas .= ', ' . $message->firstname;
+					else $message->fileas = $message->firstname;
+				}
+				if(isset($row_contact[10])) {
+					$dataParte = explode('-',$row_contact[10]);
+					$data = $dataParte[2] . '-' . $dataParte[1] . '-' . $dataParte[0];
+					$tz = date_default_timezone_get();
+					date_default_timezone_set('UTC');
+					$message->birthday = strtotime($data);
+					$message->birthday += 3600 * 10; // Soma 10 horas para nao alterar a data quando mudar o Timezone
+					date_default_timezone_set($tz);
+				}
+				// TODO:Incluir campo de Aniversario na sincronizacao. O BD do Expresso ainda nao tem esse campo :-(
+				if(isset($row_contact[13])) {
+					$message->body = utf8_encode(str_replace('\\n', chr(13) . chr(10), $this->escape($row_contact[13])));
+					$message->bodysize = strlen($message->body);
+					$message->bodytruncated = 0;
+				}
+				//TODO:Tratar o conteudo do campo de categorias
+				//if(isset($row_contact[17])) {
+				//	$message->categories = utf8_encode($row_contact[17]);
+				//}
+				if(isset($row_contact[18])) {
+					$message->webpage = utf8_encode($row_contact[18]);
+				}
+				if(isset($row_contact[19])) {
+					$message->companyname = utf8_encode($row_contact[19]);
+					if (!isset($row_contact[6]) and !isset($row_contact[7])) $message->fileas = $message->companyname;
+				}
+				if(isset($row_contact[20])) {
+					$message->jobtitle = utf8_encode($row_contact[20]);
+				}
+				if(isset($row_contact[21])) {
+					$message->department = utf8_encode($row_contact[21]);
+				}
+
+				// Endereço Comercial
+				$result_addresses_comercial = pg_query($this->db,"select co.id_address, co.id_city, city_name, co.id_state, state_symbol, co.id_country, address1, address2, complement, address_other, postal_code, po_box, address_is_default from phpgw_cc_addresses co join phpgw_cc_contact_addrs ca on (co.id_address = ca.id_address) join phpgw_cc_typeof_ct_addrs tca on (ca.id_typeof_contact_address = tca.id_typeof_contact_address) left outer join phpgw_cc_city ci on (ci.id_city = co.id_city) left outer join phpgw_cc_state cs on (cs.id_state = co.id_state) where tca.contact_address_type_name = 'Comercial' and ca.id_contact = " . $row_contact[0] . ";");
+				if ($result_addresses_comercial == FALSE) throw new Exception(pg_last_error($this->db));
+				while ($row_addresses_comercial = pg_fetch_row($result_addresses_comercial)) {
+					if (isset($row_addresses_comercial[2])) {
+						$message->businesscity = utf8_encode($row_addresses_comercial[2]);
+					}
+					if (isset($row_addresses_comercial[5])) {
+						$message->businesscountry = utf8_encode($row_addresses_comercial[5]);
+					}
+					if (isset($row_addresses_comercial[10])) {
+						$message->businesspostalcode = utf8_encode($row_addresses_comercial[10]);
+					}
+					if (isset($row_addresses_comercial[4])) {
+						$message->businessstate = utf8_encode($row_addresses_comercial[4]);
+					}
+					if (isset($row_addresses_comercial[6])) {
+						$message->businessstreet = utf8_encode($row_addresses_comercial[6]);
+					}
+					if (isset($row_addresses_comercial[8])) {
+						if (isset($message->businessstreet)) {
+							$message->businessstreet .= ":";
+						}
+						$message->businessstreet .= utf8_encode($row_addresses_comercial[8]);
+					}
+					if (isset($row_addresses_comercial[7])) {
+						if (isset($message->businessstreet)) {
+							$message->businessstreet .= ":";
+						}
+						$message->businessstreet .= utf8_encode($row_addresses_comercial[7]);
+					}
+					if (isset($row_addresses_comercial[9])) {
+						if (isset($message->businessstreet)) {
+							$message->businessstreet .= ":";
+						}
+						$message->businessstreet .= utf8_encode($row_addresses_comercial[9]);
+					}
+					if (isset($row_addresses_comercial[11])) {
+						if (isset($message->businessstreet)) {
+							$message->businessstreet .= ":";
+						}
+						$message->businessstreet .= utf8_encode($row_addresses_comercial[11]);
+					}
+				}
+				// Endereço Residencial
+				$result_addresses_residencial = pg_query($this->db,"select co.id_address, co.id_city, city_name, co.id_state, state_name, co.id_country, address1, address2, complement, address_other, postal_code, po_box, address_is_default from phpgw_cc_addresses co join phpgw_cc_contact_addrs ca on (co.id_address = ca.id_address) join phpgw_cc_typeof_ct_addrs tca on (ca.id_typeof_contact_address = tca.id_typeof_contact_address) left outer join phpgw_cc_city ci on (ci.id_city = co.id_city) left outer join phpgw_cc_state cs on (cs.id_state = co.id_state) where tca.contact_address_type_name = 'Residencial' and ca.id_contact = " . $row_contact[0] . ";");
+				if ($result_addresses_residencial == FALSE) throw new Exception(pg_last_error($this->db));
+				while ($row_addresses_residencial = pg_fetch_row($result_addresses_residencial)) {
+					if (isset($row_addresses_residencial[2])) {
+						$message->homecity = utf8_encode($row_addresses_residencial[2]);
+					}
+					if (isset($row_addresses_residencial[5])) {
+						$message->homecountry = utf8_encode($row_addresses_residencial[5]);
+					}
+					if (isset($row_addresses_residencial[10])) {
+						$message->homepostalcode = utf8_encode($row_addresses_residencial[10]);
+					}
+					if (isset($row_addresses_residencial[4])) {
+						$message->homestate = utf8_encode($row_addresses_residencial[4]);
+					}
+					if (isset($row_addresses_residencial[6])) {
+						$message->homestreet = utf8_encode($row_addresses_residencial[6]);
+					}
+					if (isset($row_addresses_residencial[8])) {
+						if (isset($message->homestreet)) {
+							$message->homestreet .= ":";
+						}
+						$message->homestreet .= utf8_encode($row_addresses_residencial[8]);
+					}
+					if (isset($row_addresses_residencial[7])) {
+						if (isset($message->homestreet)) {
+							$message->homestreet .= ":";
+						}
+						$message->homestreet .= utf8_encode($row_addresses_residencial[7]);
+					}
+					if (isset($row_addresses_residencial[9])) {
+						if (isset($message->homestreet)) {
+							$message->homestreet .= ":";
+						}
+						$message->homestreet .= utf8_encode($row_addresses_residencial[9]);
+					}
+					if (isset($row_addresses_residencial[11])) {
+						if (isset($message->homestreet)) {
+							$message->homestreet .= ":";
+						}
+						$message->homestreet .= utf8_encode($row_addresses_residencial[11]);
+					}
+				}
+				// Emails
+				$result_emails = pg_query($this->db,"select cn.id_connection, connection_name, connection_value, connection_is_default from phpgw_cc_connections cn join phpgw_cc_contact_conns cc on (cn.id_connection = cc.id_connection) join phpgw_cc_typeof_ct_conns ct on (ct.id_typeof_contact_connection = cc.id_typeof_contact_connection) where ct.contact_connection_type_name = 'Email' and cc.id_contact = "  . $row_contact[0] . ";");
+				if ($result_emails == FALSE) throw new Exception(pg_last_error($this->db));
+				while ($row_emails = pg_fetch_row($result_emails)) {
+					if ($row_emails[1] == "Principal") {
+						$message->email1address = utf8_encode($row_emails[2]);
+					}
+					if ($row_emails[1] == "Alternativo") {
+						$message->email2address = utf8_encode($row_emails[2]);
+					}
+					//TODO : Atribuir o email3address. O Expresso ainda não tem campo para um terceiro email :(
+				}
+				// Telefones
+				$result_tel = pg_query($this->db,"select cn.id_connection, connection_name, connection_value, connection_is_default from phpgw_cc_connections cn join phpgw_cc_contact_conns cc on (cn.id_connection = cc.id_connection) join phpgw_cc_typeof_ct_conns ct on (ct.id_typeof_contact_connection = cc.id_typeof_contact_connection) where ct.contact_connection_type_name = 'Telefone' and cc.id_contact = "  . $row_contact[0] . ";");
+				if ($result_tel == FALSE) throw new Exception(pg_last_error($this->db));
+				while ($row_tel = pg_fetch_row($result_tel)) {
+					if ($row_tel[1] == "Trabalho") {
+						$message->businessphonenumber = utf8_encode($row_tel[2]);
+					}
+					if ($row_tel[1] == "Casa") {
+						$message->homephonenumber = utf8_encode($row_tel[2]);
+					}
+					if ($row_tel[1] == "Celular") {
+						$message->mobilephonenumber = utf8_encode($row_tel[2]);
+					}
+					if ($row_tel[1] == "Fax") {
+						$message->businessfaxnumber = utf8_encode($row_tel[2]);
+					}
+					if ($row_tel[1] == "Principal") {
+						$message->business2phonenumber = utf8_encode($row_tel[2]);
+					}
+					if ($row_tel[1] == "Alternativo") {
+						$message->home2phonenumber = utf8_encode($row_tel[2]);
+					}
+					//TODO : Permitir mais de um número de telefone para Trabalho, Casa e Celular. O Expresso ainda não suporta isso :(
+				}
+			}
+			$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());
+			return;
+		}
+		return $message;
+	}
+
+	function DeleteMessage($folderid, $id) {
+		debugLog('ContactsExpresso::DeleteMessage('.$folderid.', '.$id.', ..)');
+
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+
+		$result = pg_query($this->db,"BEGIN;");
+		try {
+			$result_contact = pg_query($this->db, "select id_contact from phpgw_cc_contact where id_contact = " . $id . ";");
+			if ($result_contact == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row_contact = pg_fetch_row($result_contact)) {
+				if(isset($row_contact[0])) {
+					$id_contact = $row_contact[0];
+				}
+			}
+			if(!isset($id_contact)){
+				return true;
+			}
+
+			$result_contact_addrs = pg_query($this->db, "select id_address from phpgw_cc_contact_addrs where id_contact = " . $id_contact . ";");
+			if ($result_contact_addrs == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row_contact_addrs = pg_fetch_row($result_contact_addrs)) {
+				if(isset($row_contact_addrs[0])) {
+					$id_address = $row_contact_addrs[0];
+					$result_delete_address = pg_delete($this->db, "phpgw_cc_addresses", array('id_address' => $id_address));
+					if ($result_delete_address == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+			}
+
+			$result_delete_contact_addrs = pg_delete($this->db, "phpgw_cc_contact_addrs", array('id_contact' => $id_contact));
+			if ($result_delete_contact_addrs == FALSE) throw new Exception(pg_last_error($this->db));
+
+			$result_contact_conns = pg_query($this->db, "select id_connection from phpgw_cc_contact_conns where id_contact = " . $id_contact . ";");
+			if ($result_contact_conns == FALSE) throw new Exception(pg_last_error($this->db));
+			while ($row_contact_conns = pg_fetch_row($result_contact_conns)) {
+				if(isset($row_contact_conns[0])) {
+					$id_connection = $row_contact_conns[0];
+					$result_delete_connections = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection));
+					if ($result_delete_connections == FALSE) throw new Exception(pg_last_error($this->db));
+					$result_delete_contact_grps = pg_delete($this->db,"phpgw_cc_contact_grps", array('id_connection' => $id_connection));
+					if ($result_delete_contact_grps == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+			}
+
+			$result_delete_contact_conns = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact));
+			if ($result_delete_contact_conns == FALSE) throw new Exception(pg_last_error($this->db));
+			$result_delete_contact = pg_delete($this->db, "phpgw_cc_contact", array('id_contact' => $id_contact));
+			if ($result_delete_contact == FALSE) throw new Exception(pg_last_error($this->db));
+			$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());
+			return false;
+		}
+		return true;
+	}
+
+	function SetReadFlag($folderid, $id, $flags) {
+		return false;
+	}
+
+	function ChangeMessage($folderid, $id, $message) {
+		debugLog('ContactsExpresso::ChangeMessage('.$folderid.', '.$id.', ..)');
+
+		$id_owner = $this->parseOwner($this->_user);
+		if ($id_owner == false) return false;
+		try {
+			$result = pg_query($this->db,"BEGIN;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+
+			// Obtem o id_contact
+			$found_id_contact = false;
+			if (trim($id) !== "") {
+				$result_contact = pg_query($this->db, "select id_contact from phpgw_cc_contact where id_contact = " . $id . ";");
+				if ($result_contact == FALSE) throw new Exception(pg_last_error($this->db));
+				// tenta localizar id_contact para fazer Update
+				while ($row_contact = pg_fetch_row($result_contact)) {
+					if(isset($row_contact[0])) {
+						$id_contact = $row_contact[0];
+						$found_id_contact = true;
+					}
+				}
+			}
+			// se não encontrou id_contact para fazer Update, define o próximo id_contact para fazer Insert
+			if (!isset($id_contact)) {
+				$result = pg_query($this->db,"LOCK TABLE phpgw_cc_contact IN ACCESS EXCLUSIVE MODE;");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result_contact_max_id = pg_query($this->db, "select max(id_contact) from phpgw_cc_contact;");
+				if ($result_contact_max_id == FALSE) throw new Exception(pg_last_error($this->db));
+				$row_contact_max_id = pg_fetch_row($result_contact_max_id);
+				if (isset($row_contact_max_id[0])) {
+					$id_contact = $row_contact_max_id[0] + 1;
+				} else $id_contact = 1;
+			}
+
+			// Procura o id_address comercial e residencial
+			$result_address = pg_query($this->db, "select co.id_address, tca.contact_address_type_name from phpgw_cc_addresses co join phpgw_cc_contact_addrs ca on (co.id_address = ca.id_address) join phpgw_cc_typeof_ct_addrs tca on (ca.id_typeof_contact_address = tca.id_typeof_contact_address) where ca.id_contact = " . $id_contact . ";");
+			if ($result_address == FALSE) throw new Exception(pg_last_error($this->db));
+			$found_id_address_comercial = false;
+			$found_id_address_residencial = false;
+			while ($row_address = pg_fetch_row($result_address)) {
+				if(isset($row_address[0])) {
+					if ($row_address[1] == "Comercial") {
+						$id_address_comercial = $row_address[0];
+						$found_id_address_comercial = true;
+					}
+					if ($row_address[1] == "Residencial") {
+						$id_address_residencial = $row_address[0];
+						$found_id_address_residencial = true;
+					}
+				}
+			}
+			// Verifica se os campos de endereco estao preenchidos na mensagem recebida do dispositivo movel
+			$isset_business_address_fields = false;
+			if(isset($message->businessstate) or isset($message->businesspostalcode) or isset($message->businessstreet)) {
+				$isset_business_address_fields = true;
+			}
+			$isset_home_address_fields = false;
+			if(isset($message->homestate) or isset($message->homepostalcode) or isset($message->homestreet)) {
+				$isset_home_address_fields = true;
+			}
+			// Obtem o ultimo id_address
+			if (!($found_id_address_comercial and $found_id_address_residencial) and ($isset_business_address_fields or $isset_home_address_fields)) {
+				$result = pg_query($this->db,"LOCK TABLE phpgw_cc_addresses IN ACCESS EXCLUSIVE MODE;");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result_address_max_id = pg_query($this->db, "select max(id_address) from phpgw_cc_addresses;");
+				if ($result_address_max_id == FALSE) throw new Exception(pg_last_error($this->db));
+				$array_row_address_max_id = pg_fetch_row($result_address_max_id);
+				if (isset($array_row_address_max_id)) $row_address_max_id = $array_row_address_max_id[0];
+				if (!isset($row_address_max_id)) $row_address_max_id = 0;
+			}
+			$next_offset_address_to_insert = 0;
+			// se não encontrou id_address_comercial para fazer Update, define o próximo id_address_comercial para fazer Insert
+			if (!$found_id_address_comercial and $isset_business_address_fields) {
+				$next_offset_address_to_insert += 1;
+				$id_address_comercial = $row_address_max_id + $next_offset_address_to_insert;
+			}
+			// se não encontrou id_address_residencial para fazer Update, define o próximo id_address_residencial para fazer Insert
+			if (!$found_id_address_residencial and $isset_home_address_fields) {
+				$next_offset_address_to_insert += 1;
+				$id_address_residencial = $row_address_max_id + $next_offset_address_to_insert;
+			}
+
+			// Procura o id_connection de Emails e Telefones
+			$result_connection = pg_query($this->db, "select cn.id_connection, connection_name, connection_is_default, cc.id_typeof_contact_connection from phpgw_cc_connections cn join phpgw_cc_contact_conns cc on (cn.id_connection = cc.id_connection) join phpgw_cc_typeof_ct_conns ct on (ct.id_typeof_contact_connection = cc.id_typeof_contact_connection) where cc.id_contact = " . $id_contact . ";");
+			if ($result_connection == FALSE) throw new Exception(pg_last_error($this->db));
+			$found_id_connection_email_principal = false;
+			$found_id_connection_email_alternativo = false;
+			$found_id_connection_tel_trabalho = false;
+			$found_id_connection_tel_casa = false;
+			$found_id_connection_tel_celular = false;
+			$found_id_connection_tel_fax = false;
+			$found_id_connection_tel_principal = false;
+			$found_id_connection_tel_alternativo = false;
+			$tel_default = "";
+			$email_default = "";
+			while ($row_connection = pg_fetch_row($result_connection)) {
+				if(isset($row_connection[0])) {
+					if ($row_connection[1] == "Principal" and $row_connection[3] == 1) {
+						$id_connection_email_principal = $row_connection[0];
+						$found_id_connection_email_principal = true;
+						if ($row_connection[2] == 't') $email_default = "Principal";
+					}
+					if ($row_connection[1] == "Alternativo" and $row_connection[3] == 1) {
+						$id_connection_email_alternativo = $row_connection[0];
+						$found_id_connection_email_alternativo = true;
+						if ($row_connection[2] == 't') $email_default = "Alternativo";
+					}
+					if ($row_connection[1] == "Trabalho") {
+						$id_connection_tel_trabalho = $row_connection[0];
+						$found_id_connection_tel_trabalho = true;
+						if ($row_connection[2] == 't') $tel_default = "Trabalho";
+					}
+					if ($row_connection[1] == "Casa") {
+						$id_connection_tel_casa = $row_connection[0];
+						$found_id_connection_tel_casa = true;
+						if ($row_connection[2] == 't') $tel_default = "Casa";
+					}
+					if ($row_connection[1] == "Celular") {
+						$id_connection_tel_celular = $row_connection[0];
+						$found_id_connection_tel_celular = true;
+						if ($row_connection[2] == 't') $tel_default = "Celular";
+					}
+					if ($row_connection[1] == "Fax") {
+						$id_connection_tel_fax = $row_connection[0];
+						$found_id_connection_tel_fax = true;
+						if ($row_connection[2] == 't') $tel_default = "Fax";
+					}
+					if ($row_connection[1] == "Principal" and $row_connection[3] == 2) {
+						$id_connection_tel_principal = $row_connection[0];
+						$found_id_connection_tel_principal = true;
+						if ($row_connection[2] == 't') $tel_default = "Principal";
+					}
+					if ($row_connection[1] == "Alternativo" and $row_connection[3] == 2) {
+						$id_connection_tel_alternativo = $row_connection[0];
+						$found_id_connection_tel_alternativo = true;
+						if ($row_connection[2] == 't') $tel_default = "Alternativo";
+					}
+				}
+			}
+			// Obtem o ultimo id_connection
+			if (!($found_id_connection_email_principal and $found_id_connection_email_alternativo and $found_id_connection_tel_trabalho and $found_id_connection_tel_celular and $found_id_connection_tel_casa and $found_id_connection_tel_fax and $found_id_connection_tel_principal and $found_id_connection_tel_alternativo) and (isset($message->email1address) or isset($message->email2address) or isset($message->businessphonenumber) or isset($message->homephonenumber) or isset($message->mobilephonenumber) or isset($message->businessfaxnumber) or isset($message->business2phonenumber) or isset($message->home2phonenumber))){
+				$result = pg_query($this->db,"LOCK TABLE phpgw_cc_connections IN ACCESS EXCLUSIVE MODE;");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result_connection_max_id = pg_query($this->db, "select max(id_connection) from phpgw_cc_connections;");
+				if ($result_connection_max_id == FALSE) throw new Exception(pg_last_error($this->db));
+				$array_row_connection_max_id = pg_fetch_row($result_connection_max_id);
+				if (isset($array_row_connection_max_id)) $row_connection_max_id = $array_row_connection_max_id[0];
+				if (!isset($row_connection_max_id)) $row_connection_max_id = 0;
+			}
+			$next_offset_connection_to_insert = 0;
+			// se não encontrou id_connection_email_principal para fazer Update, define o próximo id_connection_email_principal para fazer Insert
+			if (!$found_id_connection_email_principal and isset($message->email1address)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_email_principal = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou id_connection_email_alternativo para fazer Update, define o próximo id_connection_email_alternativo para fazer Insert
+			if (!$found_id_connection_email_alternativo and isset($message->email2address)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_email_alternativo = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_trabalho para fazer Update, define o próximo $id_connection_tel_trabalho para fazer Insert
+			if (!$found_id_connection_tel_trabalho and isset($message->businessphonenumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_trabalho = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_casa para fazer Update, define o próximo $id_connection_tel_casa para fazer Insert
+			if (!$found_id_connection_tel_casa and isset($message->homephonenumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_casa = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_celular para fazer Update, define o próximo $id_connection_tel_celular para fazer Insert
+			if (!$found_id_connection_tel_celular and isset($message->mobilephonenumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_celular = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_fax para fazer Update, define o próximo $id_connection_tel_fax para fazer Insert
+			if (!$found_id_connection_tel_fax and isset($message->businessfaxnumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_fax = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_principal para fazer Update, define o próximo $id_connection_tel_principal para fazer Insert
+			if (!$found_id_connection_tel_principal and isset($message->business2phonenumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_principal = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+			// se não encontrou $id_connection_tel_alternativo para fazer Update, define o próximo $id_connection_tel_alternativo para fazer Insert
+			if (!$found_id_connection_tel_alternativo and isset($message->home2phonenumber)) {
+				$next_offset_connection_to_insert += 1;
+				$id_connection_tel_alternativo = $row_connection_max_id + $next_offset_connection_to_insert;
+			}
+
+			// Incluir/Alterar registro na tabela phpgw_cc_contact no Banco de Dados
+			//if(isset($message->picture)) {
+			//	$arrayContact["photo"] = base64_decode($message->picture);
+			//}
+			if(isset($message->nickname)) {
+				$arrayContact["alias"] = utf8_decode($message->nickname);
+			}
+			if(isset($message->firstname)) {
+				$arrayContact["given_names"] = utf8_decode($message->firstname);
+				$arrayContact["names_ordered"] = utf8_decode($message->firstname);
+			}
+			if (isset($message->middlename)) {
+				$arrayContact["family_names"] = utf8_decode($message->middlename);
+				if(isset($message->firstname)) {
+					$arrayContact["names_ordered"] .= " ";
+				}
+				$arrayContact["names_ordered"] .= utf8_decode($message->middlename);
+			}
+			if(isset($message->lastname)) {
+				if(isset($message->middlename)) {
+					$arrayContact["family_names"] .= " ";
+				}
+				if (isset($arrayContact["family_names"])) $arrayContact["family_names"] .= utf8_decode($message->lastname);
+				else $arrayContact["family_names"] = utf8_decode($message->lastname);
+				if(isset($message->firstname) or isset($message->middlename)) {
+					$arrayContact["names_ordered"] .= " ";
+				}
+				$arrayContact["names_ordered"] .= utf8_decode($message->lastname);
+			}
+			if (isset($message->birthday)) {
+				$tz = date_default_timezone_get();
+				date_default_timezone_set('UTC');
+				$arrayContact["birthdate"] = date("Y-m-d",$message->birthday);
+				date_default_timezone_set($tz);
+			}
+			//TODO: Incluir o campo de Aniversario na Sincronizacao. O DB do Expresso nao tem esse campo :-(
+
+			if(isset($message->rtf)) {
+				$rtf_to_ascii = new rtf();
+				$rtf_to_ascii->output("ascii");
+				$result_loadrtf = $rtf_to_ascii->loadrtf(base64_decode($message->rtf));
+				if ($result_loadrtf == true) $rtf_to_ascii->parse();
+				$arrayContact["notes"] = $rtf_to_ascii->out;
+			}
+			//TODO: Tratar o conteudo do campo de categorias
+			//if(isset($message->categories)) {
+			//	$arrayContact["category"] = $this->truncateString(utf8_decode($message->categories),20);
+			//}
+			if(isset($message->webpage)) {
+				$arrayContact["web_page"] = $this->truncateString(utf8_decode($message->webpage),100);
+			}
+			if(isset($message->companyname)) {
+				$arrayContact["corporate_name"] = $this->truncateString(utf8_decode($message->companyname),100);
+			}
+			if(isset($message->jobtitle)) {
+				$arrayContact["job_title"] = $this->truncateString(utf8_decode($message->jobtitle),40);
+			}
+			if(isset($message->department)) {
+				$arrayContact["department"] = $this->truncateString(utf8_decode($message->department),30);
+			}
+			if (!$found_id_contact){
+				$arrayContact["id_contact"] = $id_contact;
+				$arrayContact["id_owner"] = $id_owner;
+				$result = pg_insert($this->db, 'phpgw_cc_contact', $arrayContact);
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			} else {
+				$result = pg_update($this->db, 'phpgw_cc_contact', $arrayContact, array('id_contact' => $id_contact));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Incluir/Alterar Endereco Comercial na tabela phpgw_cc_addresses no Banco de Dados
+			if(isset($message->businessstate)) {
+				$result = pg_query($this->db, "select id_state, id_country from phpgw_cc_state where LOWER(to_ASCII(state_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->businessstate)))) . "' or LOWER(to_ASCII(state_symbol)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->businessstate)))) . "';");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$row = pg_fetch_row($result);
+				if(isset($row[0])) {
+					$arrayAddressComercial["id_state"] = $row[0];
+				}
+				if(isset($row[1])) {
+					$arrayAddressComercial["id_country"] = $row[1];
+				}
+				if(isset($message->businesscity) and isset($arrayAddressComercial["id_state"])) {
+					$result = pg_query($this->db, "select id_city from phpgw_cc_city where id_state = " . $arrayAddressComercial["id_state"] . " and LOWER(to_ASCII(city_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->businesscity)))) . "';");
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$row = pg_fetch_row($result);
+					if(isset($row[0])) {
+						$arrayAddressComercial["id_city"] = $row[0];
+					}
+				}
+			}
+			if(isset($message->businesspostalcode)) {
+				$arrayAddressComercial["postal_code"] = $this->truncateString(utf8_decode($message->businesspostalcode),15);
+			}
+			if(isset($message->businessstreet)) {
+				$arrayAddressComercial["address1"] = $this->truncateString(utf8_decode($message->businessstreet),60);
+			}
+			if($isset_business_address_fields) {
+				if (!$found_id_address_comercial) {
+					$arrayAddressComercial["id_address"] = $id_address_comercial;
+					$result = pg_insert($this->db, 'phpgw_cc_addresses', $arrayAddressComercial);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactAddressComercial["id_contact"] = $id_contact;
+					$arrayContactAddressComercial["id_address"] = $id_address_comercial;
+					$arrayContactAddressComercial["id_typeof_contact_address"] = 1; //comercial
+					$result = pg_insert($this->db, 'phpgw_cc_contact_addrs', $arrayContactAddressComercial);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_addresses', $arrayAddressComercial, array('id_address' => $id_address_comercial));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+			} elseif ($found_id_address_comercial) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_addrs", array('id_contact' => $id_contact, 'id_address' => $id_address_comercial));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_addresses", array('id_address' => $id_address_comercial));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Incluir/Alterar Endereco Residencial na tabela phpgw_cc_addresses no Banco de Dados
+			if(isset($message->homestate)) {
+				$result = pg_query($this->db, "select id_state, id_country from phpgw_cc_state where LOWER(to_ASCII(state_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->homestate)))) . "' or LOWER(to_ASCII(state_symbol)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->homestate)))) . "';");
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$row = pg_fetch_row($result);
+				if(isset($row[0])) {
+					$arrayAddressResidencial["id_state"] = $row[0];
+				}
+				if(isset($row[1])) {
+					$arrayAddressResidencial["id_country"] = $row[1];
+				}
+				if(isset($message->homecity) and isset($arrayAddressResidencial["id_state"])) {
+					$result = pg_query($this->db, "select id_city from phpgw_cc_city where id_state = " . $arrayAddressResidencial["id_state"] . " and LOWER(to_ASCII(city_name)) = '" . trim(strtolower($this->removeAccents(utf8_decode($message->homecity)))) . "';");
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$row = pg_fetch_row($result);
+					if(isset($row[0])) {
+						$arrayAddressResidencial["id_city"] = $row[0];
+					}
+				}
+			}
+			if(isset($message->homepostalcode)) {
+				$arrayAddressResidencial["postal_code"] = $this->truncateString(utf8_decode($message->homepostalcode),15);
+			}
+			if(isset($message->homestreet)) {
+				$arrayAddressResidencial["address1"] = $this->truncateString(utf8_decode($message->homestreet),60);
+			}
+			if($isset_home_address_fields) {
+				if (!$found_id_address_residencial) {
+					$arrayAddressResidencial["id_address"] = $id_address_residencial;
+					$result = pg_insert($this->db, 'phpgw_cc_addresses', $arrayAddressResidencial);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactAddressResidencial["id_contact"] = $id_contact;
+					$arrayContactAddressResidencial["id_address"] = $id_address_residencial;
+					$arrayContactAddressResidencial["id_typeof_contact_address"] = 2; //residencial
+					$result = pg_insert($this->db, 'phpgw_cc_contact_addrs', $arrayContactAddressResidencial);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_addresses', $arrayAddressResidencial, array('id_address' => $id_address_residencial));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+			} elseif ($found_id_address_residencial) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_addrs", array('id_contact' => $id_contact, 'id_address' => $id_address_residencial));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_addresses", array('id_address' => $id_address_residencial));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Email Principal
+			if(isset($message->email1address)) {
+				$arrayConnectionEmailPrincipal["connection_value"] = $this->truncateString(utf8_decode($message->email1address),100);
+				if (!$found_id_connection_email_principal){
+					$arrayConnectionEmailPrincipal["id_connection"] = $id_connection_email_principal;
+					$arrayConnectionEmailPrincipal["connection_name"] = "Principal";
+					if ($email_default != "Alternativo"){
+						$arrayConnectionEmailPrincipal["connection_is_default"] = 't';
+						$email_default = "Principal";
+					} else {
+						$arrayConnectionEmailPrincipal["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionEmailPrincipal);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_email_principal;
+					$arrayContactConnection["id_typeof_contact_connection"] = 1;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionEmailPrincipal, array('id_connection' => $id_connection_email_principal));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_email_principal) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_email_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_email_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_email_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Email Alternativo
+			if(isset($message->email2address)) {
+				$arrayConnectionEmailAlternativo["connection_value"] = $this->truncateString(utf8_decode($message->email2address),100);
+				if (!$found_id_connection_email_alternativo){
+					$arrayConnectionEmailAlternativo["id_connection"] = $id_connection_email_alternativo;
+					$arrayConnectionEmailAlternativo["connection_name"] = "Alternativo";
+					if ($email_default != "Principal"){
+						$arrayConnectionEmailAlternativo["connection_is_default"] = 't';
+						$email_default = "Alternativo";
+					} else {
+						$arrayConnectionEmailAlternativo["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionEmailAlternativo);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_email_alternativo;
+					$arrayContactConnection["id_typeof_contact_connection"] = 1;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionEmailAlternativo, array('id_connection' => $id_connection_email_alternativo));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_email_alternativo) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_email_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_email_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_email_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+			//TODO : Atribuir o email3address. O Expresso ainda não tem campo para um terceiro email :(
+
+			// Telefone Trabalho
+			if(isset($message->businessphonenumber)) {
+				$arrayConnectionTelTrabalho["connection_value"] = $this->truncateString(utf8_decode($message->businessphonenumber),100);
+				if (!$found_id_connection_tel_trabalho){
+					$arrayConnectionTelTrabalho["id_connection"] = $id_connection_tel_trabalho;
+					$arrayConnectionTelTrabalho["connection_name"] = "Trabalho";
+					if ($tel_default != "Celular" and $tel_default != "Casa" and $tel_default != "Fax"  and $tel_default != "Principal"  and $tel_default != "Alternativo"){
+						$arrayConnectionTelTrabalho["connection_is_default"] = 't';
+						$tel_default = "Trabalho";
+					} else {
+						$arrayConnectionTelTrabalho["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionTelTrabalho);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_trabalho;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionTelTrabalho, array('id_connection' => $id_connection_tel_trabalho));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_trabalho) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_trabalho));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_trabalho));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_trabalho));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Telefone Celular
+			if(isset($message->mobilephonenumber)) {
+				$arrayConnectionTelCelular["connection_value"] = $this->truncateString(utf8_decode($message->mobilephonenumber),100);
+				if (!$found_id_connection_tel_celular){
+					$arrayConnectionTelCelular["id_connection"] = $id_connection_tel_celular;
+					$arrayConnectionTelCelular["connection_name"] = "Celular";
+					if ($tel_default != "Trabalho" and $tel_default != "Casa" and $tel_default != "Fax"   and $tel_default != "Principal"  and $tel_default != "Alternativo"){
+						$arrayConnectionTelCelular["connection_is_default"] = 't';
+						$tel_default = "Celular";
+					} else {
+						$arrayConnectionTelCelular["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionTelCelular);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_celular;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionTelCelular, array('id_connection' => $id_connection_tel_celular));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_celular) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_celular));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_celular));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_celular));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Telefone Casa
+			if(isset($message->homephonenumber)) {
+				$arrayConnectionTelCasa["connection_value"] = $this->truncateString(utf8_decode($message->homephonenumber),100);
+				if (!$found_id_connection_tel_casa){
+					$arrayConnectionTelCasa["id_connection"] = $id_connection_tel_casa;
+					$arrayConnectionTelCasa["connection_name"] = "Casa";
+					if ($tel_default != "Trabalho" and $tel_default != "Celular" and $tel_default != "Fax" and $tel_default != "Principal"  and $tel_default != "Alternativo"){
+						$arrayConnectionTelCasa["connection_is_default"] = 't';
+						$tel_default = "Casa";
+					} else {
+						$arrayConnectionTelCasa["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionTelCasa);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_casa;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionTelCasa, array('id_connection' => $id_connection_tel_casa));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_casa) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_casa));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_casa));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_casa));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Fax
+			if(isset($message->businessfaxnumber)) {
+				$arrayConnectionFax["connection_value"] = $this->truncateString(utf8_decode($message->businessfaxnumber),100);
+				if (!$found_id_connection_tel_fax){
+					$arrayConnectionFax["id_connection"] = $id_connection_tel_fax;
+					$arrayConnectionFax["connection_name"] = "Fax";
+					if ($tel_default != "Trabalho" and $tel_default != "Celular" and $tel_default != "Casa" and $tel_default != "Principal"  and $tel_default != "Alternativo"){
+						$arrayConnectionFax["connection_is_default"] = 't';
+						$tel_default = "Fax";
+					} else {
+						$arrayConnectionFax["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionFax);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_fax;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionFax, array('id_connection' => $id_connection_tel_fax));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_fax) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_fax));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_fax));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_fax));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Telefone Principal
+			if(isset($message->business2phonenumber)) {
+				$arrayConnectionTelPrincipal["connection_value"] = $this->truncateString(utf8_decode($message->business2phonenumber),100);
+				if (!$found_id_connection_tel_principal){
+					$arrayConnectionTelPrincipal["id_connection"] = $id_connection_tel_principal;
+					$arrayConnectionTelPrincipal["connection_name"] = "Principal";
+					if ($tel_default != "Celular" and $tel_default != "Casa" and $tel_default != "Fax"  and $tel_default != "Trabalho"  and $tel_default != "Alternativo"){
+						$arrayConnectionTelPrincipal["connection_is_default"] = 't';
+						$tel_default = "Principal";
+					} else {
+						$arrayConnectionTelPrincipal["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionTelPrincipal);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_principal;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionTelPrincipal, array('id_connection' => $id_connection_tel_principal));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_principal) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_principal));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+
+			// Telefone Alternativo
+			if(isset($message->home2phonenumber)) {
+				$arrayConnectionTelAlternativo["connection_value"] = $this->truncateString(utf8_decode($message->home2phonenumber),100);
+				if (!$found_id_connection_tel_alternativo){
+					$arrayConnectionTelAlternativo["id_connection"] = $id_connection_tel_alternativo;
+					$arrayConnectionTelAlternativo["connection_name"] = "Alternativo";
+					if ($tel_default != "Trabalho" and $tel_default != "Celular" and $tel_default != "Fax" and $tel_default != "Principal"  and $tel_default != "Casa"){
+						$arrayConnectionTelAlternativo["connection_is_default"] = 't';
+						$tel_default = "Alternativo";
+					} else {
+						$arrayConnectionTelAlternativo["connection_is_default"] = 'f';
+					}
+					$result = pg_insert($this->db, 'phpgw_cc_connections', $arrayConnectionTelAlternativo);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+					$arrayContactConnection["id_contact"] = $id_contact;
+					$arrayContactConnection["id_connection"] = $id_connection_tel_alternativo;
+					$arrayContactConnection["id_typeof_contact_connection"] = 2;
+					$result = pg_insert($this->db, 'phpgw_cc_contact_conns', $arrayContactConnection);
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				} else {
+					$result = pg_update($this->db, 'phpgw_cc_connections', $arrayConnectionTelAlternativo, array('id_connection' => $id_connection_tel_alternativo));
+					if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				}
+
+			} elseif ($found_id_connection_tel_alternativo) {
+				$result = pg_delete($this->db, "phpgw_cc_contact_conns", array('id_contact' => $id_contact, 'id_connection' => $id_connection_tel_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_contact_grps", array('id_connection' => $id_connection_tel_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+				$result = pg_delete($this->db, "phpgw_cc_connections", array('id_connection' => $id_connection_tel_alternativo));
+				if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			}
+			
+			//TODO : Permitir mais de um número de telefone para Trabalho, Celular e Casa. O Expresso ainda não suporta isso :(
+
+			$result = pg_query($this->db,"COMMIT;");
+			if ($result == FALSE) throw new Exception(pg_last_error($this->db));
+			if (!$id) {
+				$id = $id_contact;
+			}
+		} catch (Exception $e) {
+			debugLog("exception -> " . $e->getMessage() . " - ARQUIVO: " . $e->getFile() . " - LINHA: " . $e->getLine());
+			pg_query($this->db,"ROLLBACK;");
+			return false;
+		}
+		return $this->StatMessage($folderid, $id);
+	}
+
+	function MoveMessage($folderid, $id, $newfolderid) {
+		return false;
+	}
+
+	// -----------------------------------
+
+	function getPath() {
+		return str_replace('%u', $this->_user, VCARDDIR_DIR);
+	}
+
+	function escape($data){
+		if (is_array($data)) {
+			foreach ($data as $key => $val) {
+				$data[$key] = $this->escape($val);
+			}
+			return $data;
+		}
+		$data = str_replace("\r\n", "\n", $data);
+		$data = str_replace("\r", "\n", $data);
+		$data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data);
+		//return u2wi($data);
+		return $data;
+	}
+
+	function unescape($data){
+		$data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
+		return $data;
+	}
+
+	function  removeAccents($data){
+		$data = strtr($data,"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÜÚÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùüúþÿ","aaaaaaaceeeeiiii noooooxouuutbaaaaaaaceeeeiiii nooooo/ouuuty");
+		return $data;
+	}
+
+	function truncateString($string, $size)
+	{
+		if(strlen($string) <= $size) return $string;
+		else return substr($string, 0, $size - 1);
+	}
+
+	function parseOwner($id_owner)
+	{
+		$pos = strripos($id_owner, '\\');
+		if ($pos === false) {
+			if (strlen($id_owner) <= 2) return false;
+			else return substr($id_owner, 0, strlen($id_owner)-2);
+		} else {
+			if (strlen($id_owner) <= $pos + 3) {
+				return false;
+			} else {
+				return substr(substr($id_owner, $pos + 1), 0, strlen(substr($id_owner, $pos + 1))-2);
+			}
+		}
+	}
+};
+?>
Index: contrib/z-push/backend/diffbackend.php
===================================================================
--- contrib/z-push/backend/diffbackend.php	(revision 3637)
+++ contrib/z-push/backend/diffbackend.php	(revision 3637)
@@ -0,0 +1,749 @@
+<?php
+/***********************************************
+* File      :   diffbackend.php
+* Project   :   Z-Push
+* Descr     :   We do a standard differential
+*               change detection by sorting both
+*               lists of items by their unique id,
+*               and then traversing both arrays
+*               of items at once. Changes can be
+*               detected by comparing items at
+*               the same position in both arrays.
+*
+* 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('backend.php');
+
+
+
+function GetDiff($old, $new) {
+    $changes = array();
+
+    // Sort both arrays in the same way by ID
+    usort($old, "RowCmp");
+    usort($new, "RowCmp");
+
+    $inew = 0;
+    $iold = 0;
+
+    // Get changes by comparing our list of messages with
+    // our previous state
+    while(1) {
+        $change = array();
+
+        if($iold >= count($old) || $inew >= count($new))
+            break;
+
+        if($old[$iold]["id"] == $new[$inew]["id"]) {
+            // Both messages are still available, compare flags and mod
+            if(isset($old[$iold]["flags"]) && isset($new[$inew]["flags"]) && $old[$iold]["flags"] != $new[$inew]["flags"]) {
+                // Flags changed
+                $change["type"] = "flags";
+                $change["id"] = $new[$inew]["id"];
+                $change["flags"] = $new[$inew]["flags"];
+                $changes[] = $change;
+            }
+
+            if($old[$iold]["mod"] != $new[$inew]["mod"]) {
+                $change["type"] = "change";
+                $change["id"] = $new[$inew]["id"];
+                $changes[] = $change;
+            }
+
+            $inew++;
+            $iold++;
+        } else {
+            if($old[$iold]["id"] > $new[$inew]["id"]) {
+                // Message in state seems to have disappeared (delete)
+                $change["type"] = "delete";
+                $change["id"] = $old[$iold]["id"];
+                $changes[] = $change;
+                $iold++;
+            } else {
+                // Message in new seems to be new (add)
+                $change["type"] = "change";
+                $change["flags"] = SYNC_NEWMESSAGE;
+                $change["id"] = $new[$inew]["id"];
+                $changes[] = $change;
+                $inew++;
+            }
+        }
+    }
+
+    while($iold < count($old)) {
+        // All data left in _syncstate have been deleted
+        $change["type"] = "delete";
+        $change["id"] = $old[$iold]["id"];
+        $changes[] = $change;
+        $iold++;
+    }
+
+    while($inew < count($new)) {
+        // All data left in new have been added
+        $change["type"] = "change";
+        $change["flags"] = SYNC_NEWMESSAGE;
+        $change["id"] = $new[$inew]["id"];
+        $changes[] = $change;
+        $inew++;
+    }
+
+    return $changes;
+}
+
+function RowCmp($a, $b) {
+    return $a["id"] < $b["id"] ? 1 : -1;
+}
+
+class DiffState {
+    var $_syncstate;
+
+    // Update the state to reflect changes
+    function updateState($type, $change) {
+        // Change can be a change or an add
+        if($type == "change") {
+            for($i=0; $i < count($this->_syncstate); $i++) {
+                if($this->_syncstate[$i]["id"] == $change["id"]) {
+                    $this->_syncstate[$i] = $change;
+                    return;
+                }
+            }
+            // Not found, add as new
+            $this->_syncstate[] = $change;
+        } else {
+            for($i=0; $i < count($this->_syncstate); $i++) {
+                // Search for the entry for this item
+                if($this->_syncstate[$i]["id"] == $change["id"]) {
+                    if($type == "flags") {
+                        // Update flags
+                        $this->_syncstate[$i]["flags"] = $change["flags"];
+                    } else if($type == "delete") {
+                        // Delete item
+                        array_splice($this->_syncstate, $i, 1);
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    // Returns TRUE if the given ID conflicts with the given operation. This is only true in the following situations:
+    //
+    // - Changed here and changed there
+    // - Changed here and deleted there
+    // - Deleted here and changed there
+    //
+    // Any other combination of operations can be done (e.g. change flags & move or move & delete)
+    function isConflict($type, $folderid, $id) {
+        $stat = $this->_backend->StatMessage($folderid, $id);
+
+        if(!$stat) {
+            // Message is gone
+            if($type == "change")
+                return true; // deleted here, but changed there
+            else
+                return false; // all other remote changes still result in a delete (no conflict)
+        }
+
+        foreach($this->_syncstate as $state) {
+            if($state["id"] == $id) {
+                $oldstat = $state;
+                break;
+            }
+        }
+
+        if(!isset($oldstat)) {
+            // New message, can never conflict
+            return false;
+        }
+
+        if($state["mod"] != $oldstat["mod"]) {
+            // Changed here
+            if($type == "delete" || $type == "change")
+                return true; // changed here, but deleted there -> conflict, or changed here and changed there -> conflict
+            else
+                return false; // changed here, and other remote changes (move or flags)
+        }
+    }
+
+    function GetState() {
+        return serialize($this->_syncstate);
+    }
+
+}
+
+class ImportContentsChangesDiff extends DiffState {
+    var $_user;
+    var $_folderid;
+
+    function ImportContentsChangesDiff($backend, $folderid) {
+        $this->_backend = $backend;
+        $this->_folderid = $folderid;
+    }
+
+    function Config($state, $flags = 0) {
+        $this->_syncstate = unserialize($state);
+        $this->_flags = $flags;
+    }
+
+    function ImportMessageChange($id, $message) {
+        //do nothing if it is in a dummy folder
+        if ($this->_folderid == SYNC_FOLDER_TYPE_DUMMY)
+            return false;
+
+        if($id) {
+            // See if there's a conflict
+            $conflict = $this->isConflict("change", $this->_folderid, $id);
+
+            // Update client state if this is an update
+            $change = array();
+            $change["id"] = $id;
+            $change["mod"] = 0; // dummy, will be updated later if the change succeeds
+            $change["parent"] = $this->_folderid;
+            $change["flags"] = (isset($message->read)) ? $message->read : 0;
+            $this->updateState("change", $change);
+
+            if($conflict && $this->_flags == SYNC_CONFLICT_OVERWRITE_PIM)
+                return true;
+        }
+
+        $stat = $this->_backend->ChangeMessage($this->_folderid, $id, $message);
+
+        if(!is_array($stat))
+            return $stat;
+
+        // Record the state of the message
+        $this->updateState("change", $stat);
+
+        return $stat["id"];
+    }
+
+    // Import a deletion. This may conflict if the local object has been modified.
+    function ImportMessageDeletion($id) {
+        //do nothing if it is in a dummy folder
+        if ($this->_folderid == SYNC_FOLDER_TYPE_DUMMY)
+            return true;
+
+        // See if there's a conflict
+        $conflict = $this->isConflict("delete", $this->_folderid, $id);
+
+        // Update client state
+        $change = array();
+        $change["id"] = $id;
+        $this->updateState("delete", $change);
+
+        // If there is a conflict, and the server 'wins', then return OK without performing the change
+        // this will cause the exporter to 'see' the overriding item as a change, and send it back to the PIM
+        if($conflict && $this->_flags == SYNC_CONFLICT_OVERWRITE_PIM)
+            return true;
+
+        $this->_backend->DeleteMessage($this->_folderid, $id);
+
+        return true;
+    }
+
+    // Import a change in 'read' flags .. This can never conflict
+    function ImportMessageReadFlag($id, $flags) {
+        //do nothing if it is a dummy folder
+        if ($this->_folderid == SYNC_FOLDER_TYPE_DUMMY)
+            return true;
+
+        // Update client state
+        $change = array();
+        $change["id"] = $id;
+        $change["flags"] = $flags;
+        $this->updateState("flags", $change);
+
+        $this->_backend->SetReadFlag($this->_folderid, $id, $flags);
+
+        return true;
+    }
+
+    function ImportMessageMove($id, $newfolder) {
+	  $this->_backend->MoveMessage($this->_folderid, $id, $newfolder);
+        return true;
+    }
+};
+
+class ImportHierarchyChangesDiff extends DiffState {
+    var $_user;
+
+    function ImportHierarchyChangesDiff($backend) {
+        $this->_backend = $backend;
+    }
+
+    function Config($state) {
+        $this->_syncstate = unserialize($state);
+    }
+
+    function ImportFolderChange($id, $parent, $displayname, $type) {
+        //do nothing if it is a dummy folder
+        if ($parent == SYNC_FOLDER_TYPE_DUMMY)
+            return false;
+
+        if($id) {
+            $change = array();
+            $change["id"] = $id;
+            $change["mod"] = $displayname;
+            $change["parent"] = $parent;
+            $change["flags"] = 0;
+            $this->updateState("change", $change);
+        }
+
+        $stat = $this->_backend->ChangeFolder($parent, $id, $displayname, $type);
+
+        if($stat)
+            $this->updateState("change", $stat);
+
+        return $stat["id"];
+    }
+
+    function ImportFolderDeletion($id, $parent) {
+        //do nothing if it is a dummy folder
+        if ($parent == SYNC_FOLDER_TYPE_DUMMY)
+            return false;
+
+        $change = array();
+        $change["id"] = $id;
+
+        $this->updateState("delete", $change);
+
+        $this->_backend->DeleteFolder($parent, $id);
+
+        return true;
+    }
+};
+
+class ExportChangesDiff extends DiffState {
+    var $_importer;
+    var $_folderid;
+    var $_restrict;
+    var $_flags;
+    var $_user;
+
+    function ExportChangesDiff($backend, $folderid) {
+        $this->_backend = $backend;
+        $this->_folderid = $folderid;
+    }
+
+    function Config(&$importer, $folderid, $restrict, $syncstate, $flags, $truncation) {
+        $this->_importer = &$importer;
+        $this->_restrict = $restrict;
+        $this->_syncstate = unserialize($syncstate);
+        $this->_flags = $flags;
+        $this->_truncation = $truncation;
+
+        $this->_changes = array();
+        $this->_step = 0;
+
+        $cutoffdate = $this->getCutOffDate($restrict);
+
+        if($this->_folderid) {
+            // Get the changes since the last sync
+            debugLog("Initializing message diff engine");
+
+            if(!isset($this->_syncstate) || !$this->_syncstate)
+                $this->_syncstate = array();
+
+            debugLog(count($this->_syncstate) . " messages in state");
+
+            //do nothing if it is a dummy folder
+            if ($this->_folderid != SYNC_FOLDER_TYPE_DUMMY) {
+
+                // on ping: check if backend supports alternative PING mechanism & use it
+                if ($folderid === false && $this->_flags == BACKEND_DISCARD_DATA && $this->_backend->AlterPing()) {
+                    $this->_changes = $this->_backend->AlterPingChanges($this->_folderid, $this->_syncstate);
+                }
+                else {
+                    // Get our lists - syncstate (old)  and msglist (new)
+                    $msglist = $this->_backend->GetMessageList($this->_folderid, $cutoffdate);
+                    if($msglist === false)
+                        return false;
+
+                    $this->_changes = GetDiff($this->_syncstate, $msglist);
+                }
+            }
+
+            debugLog("Found " . count($this->_changes) . " message changes");
+        } else {
+            debugLog("Initializing folder diff engine");
+
+            $folderlist = $this->_backend->GetFolderList();
+            if($folderlist === false)
+                return false;
+
+            if(!isset($this->_syncstate) || !$this->_syncstate)
+                $this->_syncstate = array();
+
+            $this->_changes = GetDiff($this->_syncstate, $folderlist);
+
+            debugLog("Found " . count($this->_changes) . " folder changes");
+        }
+    }
+
+    function GetChangeCount() {
+        return count($this->_changes);
+    }
+
+    function Synchronize() {
+        $progress = array();
+
+        // Get one of our stored changes and send it to the importer, store the new state if
+        // it succeeds
+        if($this->_folderid == false) {
+            if($this->_step < count($this->_changes)) {
+                $change = $this->_changes[$this->_step];
+
+                switch($change["type"]) {
+                    case "change":
+                        $folder = $this->_backend->GetFolder($change["id"]);
+                        $stat = $this->_backend->StatFolder($change["id"]);
+
+                        if(!$folder)
+                            return;
+
+                        if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportFolderChange($folder))
+                            $this->updateState("change", $stat);
+                        break;
+                    case "delete":
+                        if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportFolderDeletion($change["id"]))
+                            $this->updateState("delete", $change);
+                        break;
+                }
+
+                $this->_step++;
+
+                $progress = array();
+                $progress["steps"] = count($this->_changes);
+                $progress["progress"] = $this->_step;
+
+                return $progress;
+            } else {
+                return false;
+            }
+        }
+        else {
+            if($this->_step < count($this->_changes)) {
+                $change = $this->_changes[$this->_step];
+
+                switch($change["type"]) {
+                    case "change":
+                        $truncsize = $this->getTruncSize($this->_truncation);
+
+                        // Note: because 'parseMessage' and 'statMessage' are two seperate
+                        // calls, we have a chance that the message has changed between both
+                        // calls. This may cause our algorithm to 'double see' changes.
+
+                        $stat = $this->_backend->StatMessage($this->_folderid, $change["id"]);
+                        $message = $this->_backend->GetMessage($this->_folderid, $change["id"], $truncsize);
+
+                        // copy the flag to the message
+                        $message->flags = (isset($change["flags"])) ? $change["flags"] : 0;
+
+                        if($stat && $message) {
+                            if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportMessageChange($change["id"], $message) == true)
+                                $this->updateState("change", $stat);
+                        }
+                        break;
+                    case "delete":
+                        if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportMessageDeletion($change["id"]) == true)
+                            $this->updateState("delete", $change);
+                        break;
+                    case "flags":
+                        if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportMessageReadFlag($change["id"], $change["flags"]) == true)
+                            $this->updateState("flags", $change);
+                        break;
+                    case "move":
+                        if($this->_flags & BACKEND_DISCARD_DATA || $this->_importer->ImportMessageMove($change["id"], $change["parent"]) == true)
+                            $this->updateState("move", $change);
+                        break;
+                }
+
+                $this->_step++;
+
+                $progress = array();
+                $progress["steps"] = count($this->_changes);
+                $progress["progress"] = $this->_step;
+
+                return $progress;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------
+
+    function getCutOffDate($restrict) {
+        switch($restrict) {
+            case SYNC_FILTERTYPE_1DAY:
+                $back = 60 * 60 * 24;
+                break;
+            case SYNC_FILTERTYPE_3DAYS:
+                $back = 60 * 60 * 24 * 3;
+                break;
+            case SYNC_FILTERTYPE_1WEEK:
+                $back = 60 * 60 * 24 * 7;
+                break;
+            case SYNC_FILTERTYPE_2WEEKS:
+                $back = 60 * 60 * 24 * 14;
+                break;
+            case SYNC_FILTERTYPE_1MONTH:
+                $back = 60 * 60 * 24 * 31;
+                break;
+            case SYNC_FILTERTYPE_3MONTHS:
+                $back = 60 * 60 * 24 * 31 * 3;
+                break;
+            case SYNC_FILTERTYPE_6MONTHS:
+                $back = 60 * 60 * 24 * 31 * 6;
+                break;
+            default:
+                break;
+        }
+
+        if(isset($back)) {
+            $date = time() - $back;
+            return $date;
+        } else
+            return 0; // unlimited
+    }
+
+    function getTruncSize($truncation) {
+        switch($truncation) {
+            case SYNC_TRUNCATION_HEADERS:
+                return 0;
+            case SYNC_TRUNCATION_512B:
+                return 512;
+            case SYNC_TRUNCATION_1K:
+                return 1024;
+            case SYNC_TRUNCATION_5K:
+                return 5*1024;
+            case SYNC_TRUNCATION_SEVEN:
+            case SYNC_TRUNCATION_ALL:
+                return 1024*1024; // We'll limit to 1MB anyway
+            default:
+                return 1024; // Default to 1Kb
+        }
+    }
+
+};
+
+class BackendDiff {
+    var $_user;
+    var $_devid;
+    var $_protocolversion;
+
+    function Logon($username, $domain, $password) {
+        return true;
+    }
+
+        // completing protocol
+    function Logoff() {
+        return true;
+    }
+
+    function Setup($user, $devid, $protocolversion) {
+        $this->_user = $user;
+        $this->_devid = $devid;
+        $this->_protocolversion = $protocolversion;
+
+        return true;
+    }
+
+    function GetHierarchyImporter() {
+        return new ImportHierarchyChangesDiff($this);
+    }
+
+    function GetContentsImporter($folderid) {
+        return new ImportContentsChangesDiff($this, $folderid);
+    }
+
+    function GetExporter($folderid = false) {
+        return new ExportChangesDiff($this, $folderid);
+    }
+
+    function GetHierarchy() {
+        $folders = array();
+
+        $fl = $this->getFolderList();
+        foreach($fl as $f){
+            $folders[] = $this->GetFolder($f['id']);
+        }
+
+        return $folders;
+    }
+
+    function Fetch($folderid, $id, $mimesupport = 0) {
+        return $this->GetMessage($folderid, $id, 1024*1024, $mimesupport); // Forces entire message (up to 1Mb)
+    }
+
+    function GetAttachmentData($attname) {
+        return false;
+    }
+
+    function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
+        return true;
+    }
+
+    function GetWasteBasket() {
+        return false;
+    }
+
+    function GetMessageList($folderid, $cutoffdate) {
+        return array();
+    }
+
+    function StatMessage($folderid, $id) {
+        return false;
+    }
+
+    function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) {
+        return false;
+    }
+
+    function DeleteMessage($folderid, $id) {
+        return false;
+    }
+
+    function SetReadFlag($folderid, $id, $flags) {
+        return false;
+    }
+
+    function ChangeMessage($folderid, $id, $message) {
+        return false;
+    }
+
+    function MoveMessage($folderid, $id, $newfolderid) {
+        return false;
+    }
+
+    function MeetingResponse($requestid, $folderid, $error, &$calendarid) {
+        return false;
+    }
+
+    function getTruncSize($truncation) {
+        switch($truncation) {
+            case SYNC_TRUNCATION_HEADERS:
+                return 0;
+            case SYNC_TRUNCATION_512B:
+                return 512;
+            case SYNC_TRUNCATION_1K:
+                return 1024;
+            case SYNC_TRUNCATION_5K:
+                return 5*1024;
+            case SYNC_TRUNCATION_ALL:
+                return 1024*1024; // We'll limit to 1MB anyway
+            default:
+                return 1024; // Default to 1Kb
+        }
+    }
+
+    /**
+     * Returns array of items which contain contact information
+     *
+     * @param string $searchquery
+     *
+     * @return array
+     */
+    function getSearchResults($searchquery) {
+        return false;
+    }
+
+    /**
+     * Checks if the sent policykey matches the latest policykey on the server
+     *
+     * @param string $policykey
+     * @param string $devid
+     *
+     * @return status flag
+     */
+    function CheckPolicy($policykey, $devid) {
+        global $user, $auth_pw;
+
+        $status = SYNC_PROVISION_STATUS_SUCCESS;
+
+        $user_policykey = $this->getPolicyKey($user, $auth_pw, $devid);
+
+        if ($user_policykey != $policykey) {
+            $status = SYNC_PROVISION_STATUS_POLKEYMISM;
+        }
+
+        if (!$policykey) $policykey = $user_policykey;
+        return $status;
+    }
+
+    /**
+     * Return a policy key for given user with a given device id.
+     * If there is no combination user-deviceid available, a new key
+     * should be generated.
+     *
+     * @param string $user
+     * @param string $pass
+     * @param string $devid
+     *
+     * @return unknown
+     */
+    function getPolicyKey($user, $pass, $devid) {
+        return false;
+    }
+
+    /**
+     * Generate a random policy key. Right now it's a 10-digit number.
+     *
+     * @return unknown
+     */
+    function generatePolicyKey(){
+        return mt_rand(1000000000, 9999999999);
+    }
+
+    /**
+     * Set a new policy key for the given device id.
+     *
+     * @param string $policykey
+     * @param string $devid
+     * @return unknown
+     */
+    function setPolicyKey($policykey, $devid) {
+        return false;
+    }
+
+    /**
+     * Return a device wipe status
+     *
+     * @param string $user
+     * @param string $pass
+     * @param string $devid
+     * @return int
+     */
+    function getDeviceRWStatus($user, $pass, $devid) {
+        return false;
+    }
+
+    /**
+     * Set a new rw status for the device
+     *
+     * @param string $user
+     * @param string $pass
+     * @param string $devid
+     * @param string $status
+     *
+     * @return boolean
+     */
+    function setDeviceRWStatus($user, $pass, $devid, $status) {
+        return false;
+    }
+
+    function AlterPing() {
+        return false;
+    }
+
+    function AlterPingChanges($folderid, &$syncstate) {
+        return array();
+    }
+}
+?>
Index: contrib/z-push/config.php
===================================================================
--- contrib/z-push/config.php	(revision 3637)
+++ contrib/z-push/config.php	(revision 3637)
@@ -0,0 +1,97 @@
+<?php
+/***********************************************
+* File      :   config.php
+* Project   :   Z-Push
+* Descr     :   Main configuration file
+*
+* Created   :   01.10.2007
+*
+* ï¿œ Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+    // Defines the default time zone
+    if (function_exists("date_default_timezone_set")){
+        date_default_timezone_set("America/Sao_Paulo");
+    }
+
+    // Defines the base path on the server, terminated by a slash
+    define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . "/");
+
+    // Define the include paths
+    ini_set('include_path',
+                        BASE_PATH. "include/" . PATH_SEPARATOR .
+                        BASE_PATH. PATH_SEPARATOR .
+                        ini_get('include_path') . PATH_SEPARATOR .
+                        "/usr/share/php/" . PATH_SEPARATOR .
+                        "/usr/share/php5/" . PATH_SEPARATOR .
+                        "/usr/share/pear/");
+
+    define('STATE_DIR', 'state');
+
+    // Try to set unlimited timeout
+    define('SCRIPT_TIMEOUT', 0);
+
+    //Max size of attachments to display inline. Default is 1MB
+    define('MAX_EMBEDDED_SIZE', 1048576);
+
+    // Device Provisioning
+    define('PROVISIONING', true);
+    
+    // This option allows the 'loose enforcement' of the provisioning policies for older 
+    // devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution
+    // false (default) - Enforce provisioning for all devices
+    // true - allow older devices, but enforce policies on devices which support it  
+    define('LOOSE_PROVISIONING', true); 
+    
+    // The data providers that we are using (see configuration below)
+    //$BACKEND_PROVIDER = "BackendIMAP";
+    //$BACKEND_PROVIDER = "BackendCalendarExpresso";
+    //$BACKEND_PROVIDER = "BackendContactsExpresso";
+    //$BACKEND_PROVIDER = "BackendVCDir";
+    //$BACKEND_PROVIDER = "BackendRSS";
+    $BACKEND_PROVIDER = "BackendProxy";
+    $BACKEND_EMAIL = "BackendIMAP";
+    $BACKEND_CONTACTS = "BackendContactsExpresso";
+    $BACKEND_CALENDAR = "BackendCalendarExpresso";
+
+    // ************************
+    //  BackendICS settings
+    // ************************
+
+    // Defines the server to which we want to connect
+    define('MAPI_SERVER', 'file:///var/run/zarafa');
+
+
+    // ************************
+    //  BackendIMAP settings
+    // ************************
+
+    // Defines the server to which we want to connect
+    // recommended to use local servers only
+    define('IMAP_SERVER', '10.200.112.132');
+    // connecting to default port (143)
+    define('IMAP_PORT', 143);
+    // best cross-platform compatibility (see http://php.net/imap_open for options)
+    define('IMAP_OPTIONS', '/notls/norsh');
+    // overwrite the "from" header if it isn't set when sending emails
+    // options: 'username'    - the username will be set (usefull if your login is equal to your emailaddress)
+    //        'domain'    - the value of the "domain" field is used
+    //        '@mydomain.com' - the username is used and the given string will be appended
+    define('IMAP_DEFAULTFROM', '');
+    // copy outgoing mail to this folder. If not set z-push will try the default folders
+    define('IMAP_SENTFOLDER', 'INBOX/Sent');
+
+
+    // ************************
+    //  BackendMaildir settings
+    // ************************
+    define('MAILDIR_BASE', '/tmp');
+    define('MAILDIR_SUBDIR', 'Maildir');
+
+    // **********************
+    //  BackendVCDir settings
+    // **********************
+    define('VCARDDIR_DIR', '/var/www/vcf');
+
+?>
