/** * Discover all the addressbooks collections for a user under a root. * */ private function discoverAddressbooks() { unset($this->addressbooks); $this->addressbooks = array(); $raw = $this->server->get(false, false, true); if ($raw !== false) { $xml = new SimpleXMLElement($raw); foreach ($xml->addressbook_element as $response) { if ($this->gal_url !== false) { if (strcmp(urldecode($response->url), $this->gal_url) == 0) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Ignoring GAL addressbook '%s'", $this->gal_url)); continue; } } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Found addressbook '%s'", urldecode($response->url))); $this->addressbooks[] = urldecode($response->url); } unset($xml); } }
public static function initClass() { self::$helper = new carddav_common('BACKEND: '); }
/** * Returns a valid and unused vCard id * * @return string $vcard_id Valid vCard id */ private function generate_vcard_id() { $vcard_id = null; for ($number = 0; $number <= 25; $number++) { if ($number == 8 || $number == 17) { $vcard_id .= '-'; } else { $vcard_id .= $this->vcard_id_chars[mt_rand(0, count($this->vcard_id_chars) - 1)]; } } try { $carddav = new carddav_backend($this->url); $carddav->set_auth($this->username, $this->password); $result = $carddav->query($this->url . $vcard_id . $this->url_vcard_extension, 'GET'); if ($result['http_code'] !== 404) { $vcard_id = $this->generate_vcard_id(); } return $vcard_id; } catch (Exception $e) { throw new Exception($e->getMessage(), self::EXCEPTION_COULD_NOT_GENERATE_NEW_VCARD_ID); } }
/** * Deletes the CardDAV server contact * * @param array $carddav_contact_ids CardDAV contact ids * @return mixed affected CardDAV contacts or false */ private function carddav_delete($carddav_contact_ids) { $server = current(carddav::get_carddav_server($this->carddav_server_id)); $carddav_backend = new carddav_backend($server['url']); if ($this->rc->decrypt($server['password']) == '%p') { $server['password'] = $this->rcpassword; } $carddav_backend->set_auth($server['username'], $this->rc->decrypt($server['password']), $server['authtype']); if ($carddav_backend->check_connection()) { foreach ($carddav_contact_ids as $carddav_contact_id) { $contact = $this->get_carddav_addressbook_contact($carddav_contact_id); $carddav_backend->delete($contact['vcard_id']); $this->counter++; if ($this->counter < count($carddav_contact_ids)) { continue; } if ($this->counter > 1) { $contact['vcard_id'] = false; } $this->carddav_addressbook_sync($server, $carddav_contact_id, $contact['vcard_id']); } return count($carddav_contact_ids); } return false; }
/** * @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; }
Considering we are running as a long running process, we should definetely run as a non-privileged user to protect in case of a worst-case exploit. Note that this doesn't apply unless the process is launched by the root user, such as when started by init */ if ($config["SMStoXMPP"]["app_user"] && $config["SMStoXMPP"]["app_group"]) { $user = posix_getpwnam($config["SMStoXMPP"]["app_user"]); @posix_setuid($user['uid']); $group = posix_getgrnam($config["SMStoXMPP"]["app_group"]); @posix_setgid($group['gid']); } // connect to CardDAV $carddav = new carddav_backend($config["SMStoXMPP"]["contacts_url"]); $carddav->set_auth($config["SMStoXMPP"]["contacts_username"], $config["SMStoXMPP"]["contacts_password"]); // handle posix signals in a sane way pcntl_signal(SIGTERM, "sig_handler_child", false); pcntl_signal(SIGHUP, "sig_handler_child", false); pcntl_signal(SIGINT, "sig_handler_child", false); pcntl_signal(SIGUSR1, "sig_handler_child", false); // we set the rescan option here, and do the actual CardDAV download // and sync inside the loop - this allows us to schedule automatic // rechecks $address_rescan = true; // store address to contact mapping in memory $address_map = array(); // background worker loop while (true) { // garbage collect - needed with long running scripts
private static function delete_abook($abookid) { carddav_backend::delete_dbrecord($abookid, 'addressbooks'); // we explicitly delete all data belonging to the addressbook, since // cascaded deleted are not supported by all database backends // ...contacts carddav_backend::delete_dbrecord($abookid, 'contacts', 'abook_id'); // ...custom subtypes carddav_backend::delete_dbrecord($abookid, 'xsubtypes', 'abook_id'); // ...groups and memberships $delgroups = carddav_backend::get_dbrecord($abookid, 'id as group_id', 'groups', false, 'abook_id'); carddav_backend::delete_dbrecord($abookid, 'groups', 'abook_id'); carddav_backend::delete_dbrecord($delgroups, 'group_user', 'group_id'); }
/** * 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)]; } } $this->current_id = $id; $carddav = new carddav_backend($this->url); $carddav->set_auth($this->username, $this->password); if ($carddav->query($this->url . $id . $this->ext, 'GET', null, null, true)) { return $this->generate_vcard_id(); } else { return $id; } }
define('CARDDAV_PATH', '/SOGo/dav/%u/Contacts/'); define('CARDDAV_DEFAULT_PATH', CARDDAV_PATH . 'personal/'); $username = "******"; $password = "******"; $domain = ""; $url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_PATH)); $default_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_DEFAULT_PATH)); if (defined('CARDDAV_GAL_PATH')) { $gal_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_GAL_PATH)); } else { $gal_url = false; } echo "{$url}\n"; echo "{$default_url}\n"; echo "{$gal_url}\n"; $server = new carddav_backend($url); $server->set_auth($username, $password); //$server->enable_debug(); $raw = $server->get(false, false, true); echo "{$raw}\n"; //var_dump($server->get_debug()); if ($raw !== false) { $xml = new SimpleXMLElement($raw); foreach ($xml->addressbook_element as $response) { if ($gal_url !== false) { if (strcmp(urldecode($response->url), $gal_url) == 0) { echo sprintf("BackendCardDAV::discoverAddressbooks() Ignoring GAL addressbook '%s'\n", $this->gal_url); continue; } } echo sprintf("BackendCardDAV::discoverAddressbooks() Found addressbook '%s'\n", urldecode($response->url));