/**
  * @param $url: url of the requested resource
  *
  * @param $http_opts: Options for the HTTP request, keys:
  *             - method: request method (GET, PROPFIND, etc.)
  *             - content: request body
  *             - header: array of HTTP request headers as simple strings
  *
  * @param $carddav: config array containing at least the keys
  *             - url: base url, used if $url is a relative url
  *             - username
  *             - password: password (encoded/encrypted form as stored in DB)
  */
 public function cdfopen($url, $http_opts, $carddav)
 {
     $redirect_limit = 5;
     $rcmail = rcmail::get_instance();
     $username = $carddav['username'];
     $password = self::decrypt_password($carddav['password']);
     $baseurl = $carddav['url'];
     // determine calling function for debug output
     $caller = self::getCaller();
     $local = $rcmail->user->get_username('local');
     $domain = $rcmail->user->get_username('domain');
     // Substitute Placeholders
     if ($username == '%u') {
         $username = $_SESSION['username'];
     }
     if ($username == '%l') {
         $username = $local;
     }
     if ($password == '%p') {
         $password = $rcmail->decrypt($_SESSION['password']);
     }
     $baseurl = str_replace("%u", $username, $carddav['url']);
     $url = str_replace("%u", $username, $url);
     $baseurl = str_replace("%l", $local, $baseurl);
     $url = str_replace("%l", $local, $url);
     $baseurl = str_replace("%d", $domain, $baseurl);
     $url = str_replace("%d", $domain, $url);
     // if $url is relative, prepend the base url
     $url = self::concaturl($baseurl, $url);
     do {
         $isRedirect = false;
         if (self::DEBUG) {
             $this->debug("{$caller} requesting {$url} [RL {$redirect_limit}]");
         }
         $httpful = \Httpful\Request::init();
         $scheme = strtolower($carddav['authentication_scheme']);
         if ($scheme != "basic" && $scheme != "digest") {
             /* figure out authentication */
             $httpful->addHeader("User-Agent", "RCM CardDAV plugin/1.0.0");
             $httpful->uri($url);
             $httpful->method($http_opts['method']);
             $error = $httpful->send();
             $httpful = \Httpful\Request::init();
             $scheme = "unknown";
             if (preg_match("/\\bDigest\\b/i", $error->headers["www-authenticate"])) {
                 $httpful->digestAuth($username, $password);
                 $scheme = "digest";
             } else {
                 if (preg_match("/\\bBasic\\b/i", $error->headers["www-authenticate"])) {
                     $httpful->basicAuth($username, $password);
                     $scheme = "basic";
                 }
             }
             if ($scheme != "unknown") {
                 carddav_backend::update_addressbook($carddav['abookid'], array("authentication_scheme"), array($scheme));
             }
         } else {
             if (strtolower($scheme) == "digest") {
                 $httpful->digestAuth($username, $password);
             } else {
                 if (strtolower($scheme) == "basic") {
                     $httpful->basicAuth($username, $password);
                 }
             }
         }
         $httpful->addHeader("User-Agent", "RCM CardDAV plugin/1.0.0");
         $httpful->uri($url);
         $httpful->method($http_opts['method']);
         if (array_key_exists('content', $http_opts) && strlen($http_opts['content']) > 0 && $http_opts['method'] != "GET") {
             $httpful->body($http_opts['content']);
         }
         if (array_key_exists('header', $http_opts)) {
             foreach ($http_opts['header'] as $header) {
                 $h = explode(": ", $header);
                 if (strlen($h[0]) > 0 && strlen($h[1]) > 0) {
                     // Only append headers with key AND value
                     $httpful->addHeader($h[0], $h[1]);
                 }
             }
         }
         $reply = $httpful->send();
         $scode = $reply->code;
         if (self::DEBUG) {
             $this->debug("Code: {$scode}");
         }
         $isRedirect = $scode > 300 && $scode < 304 || $scode == 307;
         if ($isRedirect && strlen($reply->headers['location']) > 0) {
             $url = self::concaturl($baseurl, $reply->headers['location']);
         } else {
             $retVal["status"] = $scode;
             $retVal["headers"] = $reply->headers;
             $retVal["body"] = $reply->raw_body;
             if (self::DEBUG_HTTP) {
                 $this->debug_http("success: " . var_export($retVal, true));
             }
             return $retVal;
         }
     } while ($redirect_limit-- > 0 && $isRedirect);
     return $reply->code;
 }