christian-putzke / Roundcube-CardDAV

Roundcube CardDAV Plugin
http://www.graviox.de
GNU Affero General Public License v3.0
119 stars 28 forks source link

Mountain Lion Contacts cardDAV server sync problems. #32

Open geoxry opened 12 years ago

geoxry commented 12 years ago

Hi everyone,

I am trying to use the latest version of Roundcube along with the current version of the graviox/ Roundcube-CardDAV plugin.

When entering server and login info the connection with the server seems to be ok (server gets accepted and is added) but when I synchronize I cannot see any contacts in my cardDAV folder.

Does anyone else had the same problem? Any solutions or any hints about what could be wrong.

Thank you very much. George

catara commented 12 years ago

I get:

"capi:logs ladmin$ cat CardDAV [29-Aug-2012 01:14:53 +0200]: v0.5.1 | carddav_server_id: 1 | Starting CardDAV-Addressbook synchronization [29-Aug-2012 01:14:53 +0200]: v0.5.1 | carddav_server_id: 1 | Connected to the CardDAV-Server http://capi.ro/addressbooks/users/bogdan/addressbook/ [29-Aug-2012 01:14:53 +0200]: v0.5.1 | carddav_server_id: 1 | CardDAV-Server XML-Response is malformed. Synchronization aborted! [29-Aug-2012 01:15:07 +0200]: v0.5.1 | carddav_server_id: 1 | Starting CardDAV-Addressbook synchronization [29-Aug-2012 01:15:08 +0200]: v0.5.1 | carddav_server_id: 1 | Connected to the CardDAV-Server http://capi.ro/addressbooks/users/bogdan/addressbook/ [29-Aug-2012 01:15:08 +0200]: v0.5.1 | carddav_server_id: 1 | CardDAV-Server XML-Response is malformed. Synchronization aborted!"

niwrik commented 12 years ago

I am unable to get synchronization working as well.

OSX 10.8 Server RoundCube 0.8.1 CardDAV 0.5.1

I am able to get the CardDav settings to recgnize my server. However no contacts are synced to the addressbook. When I attempt to sync manually, I get the following error: An error occurred while synchronizing the CardDAV contacts

schum commented 11 years ago

possibly help section 2 in https://github.com/graviox/Roundcube-CardDAV/issues/36

mvdbent commented 11 years ago

I am also able to get the CardDav settings to recgnize my server. and like nlwrik no contacts are synced to the addressbook. When I attempt to sync manually, I get the following error: An error occurred while synchronizing the CardDAV contacts

anybody successful sync with a mountain lion server???

niwrik commented 11 years ago

I found a message board where someone had posted a fix for this. I was able to get it running in the end. It involved modifying some code (that I didn't understand) in one of the files. I'll look later and see if I can find it for you.

BK

Sent from my iPhone

On Oct 17, 2012, at 4:00 PM, mvdbent notifications@github.com wrote:

I am also able to get the CardDav settings to recgnize my server. and like nlwrik no contacts are synced to the addressbook. When I attempt to sync manually, I get the following error: An error occurred while synchronizing the CardDAV contacts

anybody successful sync with a mountain lion server???

— Reply to this email directly or view it on GitHub.

niwrik commented 11 years ago

@mvdbent schum posted the answer above. I was able to make this work following the link he posted. If you are unable to get it working, I'll look back at my config and reference the changes I made that worked.

catara commented 11 years ago

@niwrik Can you please share your carddav config files, and also the carddav user config (e.g. http://example.com:8080...)

beornlake commented 11 years ago

I'm seeing the CardDAV-Server XML-Response is malformed. Synchronization aborted! error as well. My setup is as follows:

One note for setup: I had to use http instead of https when configuring the server connection, even though the OS X machine is properly configured to serve https. Trying a secure connection just caused the setup to fail. As soon as I changed to normal http it connected just fine.

I added a self::write_log($e); line to the carddav_addressbook.php file to get a better look at the exception when it gets thrown, and this is what I see:

[25-Jan-2013 15:10:35 -0800]: v0.5.1 |  carddav_server_id: 2 | Starting CardDAV-Addressbook synchronization
[25-Jan-2013 15:10:35 -0800]: v0.5.1 |  carddav_server_id: 2 | Connected to the CardDAV-Server http://SERVER.ADDRESS/addressbooks/users/michael/addressbook/
[25-Jan-2013 15:10:35 -0800]: v0.5.1 |  carddav_server_id: 2 | CardDAV-Server XML-Response is malformed. Synchronization aborted!
[25-Jan-2013 15:10:35 -0800]: v0.5.1 |  carddav_server_id: 2 | exception 'Exception' with message 'String could not be parsed as XML' in /usr/local/etc/roundcube/plugins/carddav/carddav_addressbook.php:402
Stack trace:
#0 /usr/local/etc/roundcube/plugins/carddav/carddav_addressbook.php(402): SimpleXMLElement->__construct('')
#1 /usr/local/etc/roundcube/plugins/carddav/carddav.php(305): carddav_addressbook->carddav_addressbook_sync(Array)
#2 [internal function]: carddav->carddav_addressbook_sync()
#3 /usr/local/etc/roundcube/program/include/rcube_plugin_api.php(316): call_user_func(Array)
#4 /usr/local/etc/roundcube/index.php(279): rcube_plugin_api->exec_action('plugin.carddav-...')
#5 {main}

It looks like carddav_addressbook.php:402 is trying to make a new SimpleXMLElement object by passing an empty string instead of valid XML data.

I tried replacing the PROPFIND line in carddav_backend.php with the three lines from #29, but that has no effect on the problem.

bryan977 commented 11 years ago

Have anyone got this plugin working on Mountain Lion? I've tried the fixes in #29 and #36; however, I still have problems. I still receive the "ardDAV-Server XML-Response is malformed. Synchronization aborted!" error. Thanks so much!

niwrik commented 11 years ago

Hello. I stopped using this awhile back and haven't gotten back to it. An OSX update broke roundcude on me (something to do with postgres), so I had to recreate the database using mysql. Prior to that, I was able to sync carddav address book after making the changes to carddav_backend.php. Sorry, but I don't recall the exact lines of code I change (per instructions in the message board). If it helps, here's the code from the file that worked for me:

<?php

/**

class carddav_backend { /* * CardDAV PHP Version * @constant string */ const VERSION = '0.5.1';

/**
 * User agent displayed in http requests
 *
 * @constant string
 */
const USERAGENT = 'CardDAV PHP/';

/**
 * CardDAV server url
 *
 * @var string
 */
private $url = null;

/**
 * CardDAV server url_parts
 *
 * @var array
 */
private $url_parts = null;

/**
 * Authentication string
 *
 * @var string
 */
private $auth = null;

/**
* Authentication: username
*
* @var string
*/
private $username = null;

/**
* Authentication: password
*
* @var string
*/
private $password = null;

/**
 * Characters used for vCard id generation
 *
 * @var array
 */
private $vcard_id_chars = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F');

/**
 * CardDAV server connection (curl handle)
 *
 * @var resource
 */
private $curl;

/**
 * Constructor
 * Sets the CardDAV server url
 *
 * @param   string  $url    CardDAV server url
 * @return  void
 */
public function __construct($url = null)
{
    if ($url !== null)
    {
        $this->set_url($url);
    }
}

/**
* Sets the CardDAV server url
*
* @param    string  $url    CardDAV server url
* @return   void
*/
public function set_url($url)
{
    $this->url = $url;

    if (substr($this->url, -1, 1) !== '/')
    {
        $this->url = $this->url . '/';
    }

    $this->url_parts = parse_url($this->url);
}

/**
 * Sets authentication string
 *
 * @param   string  $username   CardDAV server username
 * @param   string  $password   CardDAV server password
 * @return  void
 */
public function set_auth($username, $password)
{
    $this->username = $username;
    $this->password = $password;
    $this->auth = $username . ':' . $password;
}

/**
 * Gets propfind XML response from the CardDAV server
 *
 * @param   boolean $include_vcards     vCards include vCards in the response (simplified only)
 * @param   boolean $raw                Get response raw or simplified
 * @return  string                      Raw or simplified XML response
 */
public function get($include_vcards = true, $raw = false)
{
/** $response = $this->query($this->url, 'PROPFIND'); */

    $content = '<?xml version="1.0" encoding="utf-8" ?>
                    <D:sync-collection xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
                    <D:sync-token></D:sync-token>
                    <D:sync-level>1</D:sync-level>
                    <D:prop><D:getcontenttype/><D:getetag/><D:allprop/>
                    <C:address-data><C:allprop/></C:address-data></D:prop>
                    <C:filter/></D:sync-collection>';
    $content_type = 'application/xml';
    $response = $this->query($this->url, 'REPORT', $content, $content_type);

    if ($response === false || $raw === true)
    {
        return $response;
    }
    else
    {
        return $this->simplify($response, $include_vcards);
    }
}

/**
* Gets a clean vCard from the CardDAV server
*
* @param    string  $vcard_id   vCard id on the CardDAV server
* @return   string              vCard (text/vcard)
*/
public function get_vcard($vcard_id)
{
    $vcard_id = str_replace('.vcf', null, $vcard_id);
    return $this->query($this->url . $vcard_id . '.vcf', 'GET');
}

/**
 * Gets a vCard + XML from the CardDAV Server
 *
 * @param   string      $vcard_id   vCard id on the CardDAV Server
 * @param   boolean     $raw        Get response raw or simplified
 * @return  string                  Raw or simplified vCard (text/xml)
 */
public function get_xml_vcard($vcard_id, $raw = false)
{
    $vcard_id = str_replace('.vcf', null, $vcard_id);

    $xml = new XMLWriter();
    $xml->openMemory();
    $xml->setIndent(4);
    $xml->startDocument('1.0', 'utf-8');
        $xml->startElement('C:addressbook-multiget');
            $xml->writeAttribute('xmlns:D', 'DAV:');
            $xml->writeAttribute('xmlns:C', 'urn:ietf:params:xml:ns:carddav');
            $xml->startElement('D:prop');
                $xml->writeElement('D:getetag');
                $xml->writeElement('D:getlastmodified');
            $xml->endElement();
            $xml->writeElement('D:href', $this->url_parts['path'] . $vcard_id . '.vcf');
        $xml->endElement();
    $xml->endDocument();

    $response = $this->query($this->url, 'REPORT', $xml->outputMemory(), 'text/xml');

    if ($raw === true || $response === false)
    {
        return $response;
    }
    else
    {
        return $this->simplify($response, true);
    }
}

/**
* Checks if the CardDAV server is reachable
*
* @return   boolean
*/
public function check_connection()
{
    return $this->query($this->url, 'OPTIONS', null, null, true);
}

/**
 * Cleans the vCard
 *
 * @param   string  $vcard  vCard
 * @return  string  $vcard  vCard
 */
private function clean_vcard($vcard)
{
    $vcard = str_replace("\t", null, $vcard);

    return $vcard;
}

/**
 * Deletes an entry from the CardDAV server
 *
 * @param   string  $vcard_id   vCard id on the CardDAV server
 * @return  boolean
 */
public function delete($vcard_id)
{
    return $this->query($this->url . $vcard_id . '.vcf', 'DELETE', null, null, true);
}

/**
 * Adds an entry to the CardDAV server
 *
 * @param   string  $vcard  vCard
 * @return  string          The new vCard id
 */
public function add($vcard)
{
    $vcard_id = $this->generate_vcard_id();
    $vcard = $this->clean_vcard($vcard);

    if ($this->query($this->url . $vcard_id . '.vcf', 'PUT', $vcard, 'text/vcard', true) === true)
    {
        return $vcard_id;
    }
    else
    {
        return false;
    }
}

/**
 * Updates an entry to the CardDAV server
 *
 * @param   string  $vcard      vCard
 * @param   string  $vcard_id   vCard id on the CardDAV server
 * @return  boolean
 */
public function update($vcard, $vcard_id)
{
    $vcard_id = str_replace('.vcf', null, $vcard_id);
    $vcard = $this->clean_vcard($vcard);

    return $this->query($this->url . $vcard_id . '.vcf', 'PUT', $vcard, 'text/vcard', true);
}

/**
 * Simplify CardDAV XML response
 *
 * @param   string  $response           CardDAV XML response
 * @param   boolean $include_vcards     Include vCards or not
 * @return  string                      Simplified CardDAV XML response
 */
private function simplify($response, $include_vcards = true)
{
    $response = $this->clean_response($response);
    $xml = new SimpleXMLElement($response);

    $simplified_xml = new XMLWriter();
    $simplified_xml->openMemory();
    $simplified_xml->setIndent(4);

    $simplified_xml->startDocument('1.0', 'utf-8');
        $simplified_xml->startElement('response');

            foreach ($xml->response as $response)
            {
                if (preg_match('/vcard/', $response->propstat->prop->getcontenttype) || preg_match('/vcf/', $response->href))
                {
                    $id = basename($response->href);
                    $id = str_replace('.vcf', null, $id);

                    if (!empty($id))
                    {
                        $simplified_xml->startElement('element');
                            $simplified_xml->writeElement('id', $id);
                            $simplified_xml->writeElement('etag', str_replace('"', null, $response->propstat->prop->getetag));
                            $simplified_xml->writeElement('last_modified', $response->propstat->prop->getlastmodified);

                            if ($include_vcards === true)
                            {
                                $simplified_xml->writeElement('vcard', $this->get_vcard($id));
                            }
                        $simplified_xml->endElement();
                    }
                }
                else if (preg_match('/unix-directory/', $response->propstat->prop->getcontenttype))
                {
                    if (isset($response->propstat->prop->href))
                    {
                        $href = $response->propstat->prop->href;
                    }
                    else if (isset($response->href))
                    {
                        $href = $response->href;
                    }
                    else
                    {
                        $href = null;
                    }

                    $url = str_replace($this->url_parts['path'], null, $this->url) . $href;
                    $simplified_xml->startElement('addressbook_element');
                        $simplified_xml->writeElement('display_name', $response->propstat->prop->displayname);
                        $simplified_xml->writeElement('url', $url);
                        $simplified_xml->writeElement('last_modified', $response->propstat->prop->getlastmodified);
                    $simplified_xml->endElement();
                }
            }

        $simplified_xml->endElement();
    $simplified_xml->endDocument();

    return $simplified_xml->outputMemory();
}

/**
 * Cleans CardDAV XML response
 *
 * @param   string  $response   CardDAV XML response
 * @return  string  $response   Cleaned CardDAV XML response
 */
private function clean_response($response)
{
    $response = utf8_encode($response);
    $response = str_replace('D:', null, $response);
    $response = str_replace('d:', null, $response);
    $response = str_replace('C:', null, $response);
    $response = str_replace('c:', null, $response);

    return $response;
}

/**
 * Curl initialization
 *
 * @return void
 */
public function curl_init()
{
    if (empty($this->curl))
    {
        $this->curl = curl_init();
        curl_setopt($this->curl, CURLOPT_HEADER, false);
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->curl, CURLOPT_USERAGENT, self::USERAGENT.self::VERSION);

        if ($this->auth !== null)
        {
            curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
            curl_setopt($this->curl, CURLOPT_USERPWD, $this->auth);
        }
    }
}

/**
 * Quries the CardDAV server via curl and returns the response
 *
 * @param   string  $url                CardDAV server URL
 * @param   string  $method             HTTP-Method like (OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE)
 * @param   string  $content            Content for CardDAV queries
 * @param   string  $content_type       Set content type
 * @param   boolean $return_boolean     Return just a boolean
 * @return  string                      CardDAV XML response
 */
private function query($url, $method, $content = null, $content_type = null, $return_boolean = false)
{
    $this->curl_init();

    curl_setopt($this->curl, CURLOPT_URL, $url);
    curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method);

    if ($content !== null)
    {
        curl_setopt($this->curl, CURLOPT_POST, true);
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $content);
    }
    else
    {
        curl_setopt($this->curl, CURLOPT_POST, false);
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, null);
    }

    if ($content_type !== null)
    {
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-type: '.$content_type));
    }
    else
    {
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array());
    }

    $response = curl_exec($this->curl);
    $http_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);

    if (in_array($http_code, array(200, 207)))
    {
        return ($return_boolean === true ? true : $response);
    }
    else if ($return_boolean === true && in_array($http_code, array(201, 204)))
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * Returns a valid and unused vCard id
 *
 * @return  string  Valid vCard id
 */
private function generate_vcard_id()
{
    $id = null;

    for ($number = 0; $number <= 25; $number ++)
    {
        if ($number == 8 || $number == 17)
        {
            $id .= '-';
        }
        else
        {
            $id .= $this->vcard_id_chars[mt_rand(0, (count($this->vcard_id_chars) - 1))];
        }
    }

    $carddav = new carddav_backend($this->url);
    $carddav->set_auth($this->username, $this->password);

    if ($carddav->query($this->url . $id . '.vcf', 'GET', null, null, true))
    {
        return $this->generate_vcard_id();
    }
    else
    {
        return $id;
    }
}

/**
 * Destructor
 * Close curl connection if it's open
 *
 * @return  void
 */
public function __destruct()
{
    if (!empty($this->curl))
    {
        curl_close($this->curl);
    }
}

}

On Feb 1, 2013, at 10:33 AM, bryan977 notifications@github.com wrote:

Have anyone got this plugin working on Mountain Lion? I've tried the fixes in #29 and #36; however, I still have problems. I still receive the "ardDAV-Server XML-Response is malformed. Synchronization aborted!" error. Thanks so much!

— Reply to this email directly or view it on GitHub.

bryan977 commented 11 years ago

Thank you niwrik! I'll give this a try. I've been using Kerio Connect for the last several years, but, was considering going the "free" route and using the butil-in services to ML. This is just my home personal email server for family and friends, so, I can be pretty nimble. Thanks so much!

benjennings commented 11 years ago

has anyone managed to get CardDAV working with Mountain Lion Server (10.8.2) and RoundCube 0.8.5? I can add the server but then on sync it just says:

"an error occurred while synchronizing the CardDave contacts"

any pointers?

bryan977 commented 11 years ago

I have not. I am hoping, at some point, someone smarter than I can make it work and share. Or, the connector is updated to work with ML Server.

benjennings commented 11 years ago

hey @bryan977 thanks for getting back - glad it's not just me! so any genius out there with a solution? :)

hscissors commented 11 years ago

Have you tried using another CardDav service like Radicale? There is a repo on Github. I haven't used it with Mountain Lion, but its a pretty lightweight python application that works behind Apache or on its on. I have it working with RoundCube.

benjennings commented 11 years ago

@hscissors thanks for the pointer - but it would be great if it were possible to get CardDAV working with a default Mountain Lion Server install :)

niwrik commented 11 years ago

I had a request this morning to get carddav working on roundcube at the office. I hadn't gotten back to this since my earlier post, as I'd never bothered to restore the configuration following my move from postgres to mysql for the roundcube db. Again, I ran into the same problems as above. Luckily, I had made a backup of the working plugin folder, and restored it to my current config. Confirmed working on 10.8.2 - I have no idea which files were modified to make it work, but I can post the folder in .zip format somewhere if anyone wants to try it out in their deployment.

bpwats commented 11 years ago

I would be interested in that.

niwrik commented 11 years ago

@bpwats Send me a message and I'll send you a link. My public e-mail address is now published.

niwrik commented 11 years ago

Hello Everyone. I have this running on OSX 10.8.2 Server 2.2.1 with RC 0.8.2. I found the solution here: https://github.com/graviox/Roundcube-CardDAV/issues/36 (about half-way down the page)

In carddav_backend.php replace the following line:

$response = $this->query($this->url, 'PROPFIND');

with this: screen shot 2013-06-26 at 2 27 04 pm

sylvestrerd commented 11 years ago

Hello You can modify in the carddav_backend.php the auth type. I had the same problem of XML because the response is empty. change this line curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); by curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); the url : https://xxxxx:8843/addressbooks/users/[username]/addressbook/ For me it's working. Is it possible to add a DropDownList to configure the server like calendar.