Current Path : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/modules/dav/classes/general/ |
Current File : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/modules/dav/classes/general/groupdavclient.php |
<? IncludeModuleLangFile(__FILE__); class CDavGroupdavClient { private $scheme = "http"; private $server = null; private $port = '80'; private $userName = null; private $userPassword = null; private $proxyScheme = null; private $proxyServer = null; private $proxyPort = null; private $proxyUserName = null; private $proxyUserPassword = null; private $proxyUsed = false; private $fp = null; private $socketTimeout = 5; private $connected = false; private $userAgent = 'Bitrix CalDAV/CardDAV/GroupDAV client'; private $path = '/'; private $principalUrl = null; private $arError = array(); private $debug = false; private $encoding = "windows-1251"; private $googleAuth = null; private $googleOAuth = null; public function __construct($scheme, $server, $port, $userName, $userPassword) { $this->scheme = ((strtolower($scheme) == "https") ? "https" : "http"); $this->server = $server; $this->port = $port; $this->userName = $userName; $this->userPassword = $userPassword; $this->connected = false; $this->debug = false; $this->proxyScheme = null; $this->proxyServer = null; $this->proxyPort = null; $this->proxyUserName = null; $this->proxyUserPassword = null; $this->proxyUsed = false; } public function Debug() { $this->debug = true; } public function SetCurrentEncoding($siteId = null) { $this->encoding = CDav::GetCharset($siteId); if (is_null($this->encoding) || empty($this->encoding)) $this->encoding = "utf-8"; } public function SetProxy($proxyScheme, $proxyServer, $proxyPort, $proxyUserName, $proxyUserPassword) { $this->proxyScheme = ((strtolower($proxyScheme) == "https") ? "https" : "http"); $this->proxyServer = $proxyServer; $this->proxyPort = $proxyPort; $this->proxyUserName = $proxyUserName; $this->proxyUserPassword = $proxyUserPassword; $this->proxyUsed = (strlen($this->proxyServer) > 0 && strlen($this->proxyPort) > 0); } public function setGoogleOAuth($token) { $this->googleOAuth = $token; } public function GetPath() { return $this->path; } public function Connect() { if ($this->connected) return true; $requestScheme = $this->scheme; $requestServer = $this->server; $requestPort = $this->port; if ($this->proxyUsed) { $requestScheme = $this->proxyScheme; $requestServer = $this->proxyServer; $requestPort = $this->proxyPort; } switch ($requestScheme) { case 'https': if (!function_exists("openssl_verify")) { $this->arError[] = array("EC0", "OpenSSL PHP extention required"); $this->connected = false; return false; } $requestScheme = 'ssl://'; $requestPort = ($requestPort === null) ? 443 : $requestPort; break; case 'http': $requestScheme = ''; $requestPort = ($requestPort === null) ? 80 : $requestPort; break; default: $this->arError[] = array("EC1", "Invalid protocol"); $this->connected = false; return false; } $this->fp = @fsockopen($requestScheme.$requestServer, $requestPort, $errno, $errstr, $this->socketTimeout); if (!$this->fp) { $this->arError[] = array($errno, $errstr); $this->connected = false; return false; } else { socket_set_blocking($this->fp, 1); $this->connected = true; return true; } } public function Disconnect() { if (!$this->connected) return; fclose($this->fp); $this->connected = false; } public function GetErrors() { return $this->arError; } public function AddError($code, $message) { $this->arError[] = array($code, $message); } public function ClearErrors() { $this->arError = array(); } public function CheckWebdavServer($path) { $response = $this->Options($path); if (is_null($response)) return false; if ($dav = $response->GetHeader('DAV')) { $ar = explode(",", $dav); foreach ($ar as $v) { if (trim($v)."!" == "1!") return true; } } return false; } public function Options($path) { $path = $this->FormatUri($path); $request = $this->CreateBasicRequest('OPTIONS', $path); return $this->Send($request); } public function Propfind($path, $arProperties = null, $arFilter = null, $depth = 1) { $path = $this->FormatUri($path); $request = $this->CreateBasicRequest('PROPFIND', $path); $request->AddHeader('Depth', intval($depth)); $request->AddHeader('Content-type', 'text/xml'); $request->CreatePropfindBody($arProperties, $arFilter); $response = $this->Send($request); if (!is_null($response)) { if (($statusCode = $response->GetStatus('code')) && (strcmp($statusCode, '207') == 0)) { if (($contentType = $response->GetHeader('Content-Type')) && (strpos($contentType, '/xml') !== false)) return $response->GetBodyXml(); } } return null; } public function Report($path, $arProperties = null, $arFilter = null, $arHref = null, $depth = 1) { $path = $this->FormatUri($path); $request = $this->CreateBasicRequest('REPORT', $path); $request->AddHeader('Depth', intval($depth)); $request->AddHeader('Content-type', 'text/xml'); $request->CreateReportBody($arProperties, $arFilter, $arHref); $response = $this->Send($request); if (!is_null($response)) { if (($statusCode = $response->GetStatus('code')) && (strcmp($statusCode, '207') == 0)) { if (($contentType = $response->GetHeader('Content-Type')) && (strpos($contentType, '/xml') !== false)) return $response->GetBodyXml(); } } return null; } public function Delete($path) { $path = $this->FormatUri($path); $request = $this->CreateBasicRequest('DELETE', $path); $response = $this->Send($request); if (!is_null($response)) { $statusCode = $response->GetStatus('code'); if (strcmp($statusCode, '207') == 0) { if (($contentType = $response->GetHeader('Content-Type')) && (strpos($contentType, '/xml') !== false)) return $response->GetBodyXml(); } elseif (strcmp($statusCode, '404') == 0) { $this->AddError("404", "Item is not found"); return false; } elseif (strcmp($statusCode, '200') == 0 || strcmp($statusCode, '201') == 0 || strcmp($statusCode, '204') == 0) { return true; } } return null; } public function Put($path, $data) { $path = $this->FormatUri($path); $request = $this->CreateBasicRequest('PUT', $path); $request->AddHeader('Content-type', 'text/calendar; charset=UTF-8'); $request->SetBody($data); $response = $this->Send($request); if (!is_null($response)) { $code = $response->GetStatus('code'); if ($code != 200 && $code != 201 && $code != 204) $this->AddError($code, GetMessage("DAV_GDC_SERVER_ERROR").$response->GetStatus('phrase')); return $response->GetStatus('code'); } $this->AddError("NA", "Unknown error"); return null; } public function Encode($text) { if (is_null($text) || empty($text)) return $text; if ($this->encoding == "utf-8") return $text; global $APPLICATION; return $APPLICATION->ConvertCharset($text, "utf-8", $this->encoding); } public function Decode($text) { if (is_null($text) || empty($text)) return $text; if ($this->encoding == "utf-8") return $text; global $APPLICATION; return $APPLICATION->ConvertCharset($text, $this->encoding, "utf-8"); } private function FormatUri($path) { return $path; $path = html_entity_decode($path); $arParts = explode('/', $path); $arPartsNew = array(); for ($i = 0, $cnt = count($arParts); $i < $cnt; $i++) { if (strlen($arParts[$i]) > 0) $arPartsNew[] = str_replace("%40", "@", rawurlencode($arParts[$i])); } return "/".implode('/', $arPartsNew); } private function CreateBasicRequest($method, $path) { $request = new CDavGroupdavClientRequest($this); $request->SetMethod($method); if ($this->proxyUsed) $request->SetPath($this->scheme."://".$this->server.((intval($this->port) > 0) ? ":".$this->port : "").$path); else $request->SetPath($path); $request->AddHeader('Host', $this->server); $request->AddHeader('User-Agent', $this->userAgent); $request->AddHeader("Connection", "Keep-Alive"); if ($this->googleAuth != null) $request->AddHeader("Authorization", sprintf("GoogleLogin auth=%s", $this->googleAuth)); if ($this->googleOAuth != null) $request->AddHeader('Authorization', sprintf('Bearer %s', $this->googleOAuth)); return $request; } private function Send($request) { $i = 0; while (true) { $i++; if ($i > 3) break; if ($this->debug) { $f = fopen($_SERVER["DOCUMENT_ROOT"]."/++++++++.+++", "a"); fwrite($f, "\n>>>>>>>>>>>>>>>>>> REQUEST ".$i." >>>>>>>>>>>>>>>>\n"); fwrite($f, $request ? $request->ToString() : "???"); fwrite($f, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); fclose($f); } $this->SendRequest($request); $response = $this->GetResponse(); if ($this->debug) { $f = fopen($_SERVER["DOCUMENT_ROOT"]."/++++++++.+++", "a"); fwrite($f, "\n>>>>>>>>>>>>>>>>>> RESPONCE ".$i." >>>>>>>>>>>>>>>>\n"); fwrite($f, $response ? $response->Dump() : "???"); fwrite($f, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); fclose($f); } if (!is_null($response)) { if (($location = $response->GetHeader('Location')) && !is_null($location) && $response->GetStatus('code') != 201) { if ($this->proxyUsed) $request->SetPath($this->scheme."://".$this->server.((intval($this->port) > 0) ? ":".$this->port : "").$location); else $request->SetPath($location); continue; } elseif (($statusCode = $response->GetStatus('code')) && (intval($statusCode) == 401)) { $request = $this->Authenticate($request, $response); if (is_null($request)) { return null; } continue; } } break; } if (!is_null($response) && ($statusCode = $response->GetStatus('code')) && (intval($statusCode) == 401)) $this->AddError("401", GetMessage("DAV_GDC_NOT_AUTH")); if ($this->debug) { $f = fopen($_SERVER["DOCUMENT_ROOT"]."/++++++++.+++", "a"); fwrite($f, "\n>>>>>>>>>>>>>>>>>> RESPONSE >>>>>>>>>>>>>>>>\n"); if (is_null($response)) fwrite($f, "NULL"); else fwrite($f, $response->Dump()); fwrite($f, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); fclose($f); } return $response; } private function Authenticate($request, $response) { $authenticate = $response->GetHeader('WWW-Authenticate'); $authenticateProxy = $response->GetHeader('Proxy-Authenticate'); if (is_null($authenticate) && is_null($authenticateProxy)) return null; if (!is_null($authenticate) && !is_array($authenticate)) $authenticate = array($authenticate); if (!is_null($authenticateProxy) && !is_array($authenticateProxy)) $authenticateProxy = array($authenticateProxy); if (!is_null($authenticate)) { $arAuth = array(); foreach ($authenticate as $auth) { $auth = trim($auth); $p = strpos($auth, " "); if ($p !== false) $arAuth[strtolower(substr($auth, 0, $p))] = trim(substr($auth, $p)); else $arAuth[strtolower($auth)] = ""; } if (array_key_exists("digest", $arAuth)) { $request = $this->AuthenticateDigest(CDavExchangeClientResponce::ExtractArray($arAuth["digest"]), $request, $response, "Authorization"); } elseif (array_key_exists("basic", $arAuth)) { $request = $this->AuthenticateBasic(CDavExchangeClientResponce::ExtractArray($arAuth["basic"]), $request, $response, "Authorization"); } elseif (array_key_exists("googlelogin", $arAuth)) { $request = $this->AuthenticateGoogleLogin(CDavExchangeClientResponce::ExtractArray($arAuth["basic"]), $request, $response, "Authorization"); if ($request === null) return null; } else return null; } if (!is_null($authenticateProxy)) { $arAuthProxy = array(); foreach ($authenticateProxy as $auth) { $auth = trim($auth); $p = strpos($auth, " "); if ($p !== false) $arAuthProxy[strtolower(substr($auth, 0, $p))] = trim(substr($auth, $p)); else $arAuthProxy[strtolower($auth)] = ""; } if (array_key_exists("digest", $arAuthProxy)) $request = $this->AuthenticateDigest(CDavExchangeClientResponce::ExtractArray($arAuthProxy["digest"]), $request, $response, "Proxy-Authorization"); elseif (array_key_exists("basic", $arAuthProxy)) $request = $this->AuthenticateBasic(CDavExchangeClientResponce::ExtractArray($arAuthProxy["basic"]), $request, $response, "Proxy-Authorization"); else return null; } return $request; } private static function gmailUsernameFix($name, $link) { if (strpos($name, '@gmail.com') !== false) { $arN = explode('@', $name); $name = str_replace('.', '', $arN[0]).'@'.$arN[1]; } return $name; } private function AuthenticateDigest($arDigestRequest, $request, $response, $verb = "Authorization") { // qop="auth",algorithm=MD5-sess,nonce="+Upgraded+v1fdcb1e18d2cc7a72322c81c0d8d2a3c332f7908ef0dfcb01aa9fb63930eadf5722dc8f6ce7b82912353531b18360cd62382a6c2433939d3f",charset=utf-8,realm="Digest" /* TODO: If the "qop" value is "auth" or "auth-int": request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) <"> If the "qop" directive is not present (this construction is for compatibility with RFC 2069): request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> If the "algorithm" directive's value is "MD5" or is unspecified, then A1 is: A1 = unq(username-value) ":" unq(realm-value) ":" passwd where passwd = < user's password > If the "algorithm" directive's value is "MD5-sess", then A1 is calculated only once - on the first request by the client following receipt of a WWW-Authenticate challenge from the server. It uses the server nonce from that challenge, and the first client nonce value to construct A1 as follows: A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value) If the "qop" directive's value is "auth" or is unspecified, then A2 is: A2 = Method ":" digest-uri-value If the "qop" value is "auth-int", then A2 is: A2 = Method ":" digest-uri-value ":" H(entity-body) */ $cn = md5(uniqid()); $userName = static::gmailUsernameFix($this->userName, $this->server); $a1 = md5($userName.':'.$arDigestRequest["realm"].':'.$this->userPassword).":".$arDigestRequest["nonce"].":".$cn; $a2 = $request->GetMethod().":".$request->GetPath(); $hash = md5(md5($a1).":".$arDigestRequest["nonce"].":00000001:".$cn.":".$arDigestRequest["qop"].":".md5($a2)); $request->SetHeader( $verb, sprintf( "Digest username=\"%s\",realm=\"%s\",nonce=\"%s\",uri=\"%s\",cnonce=\"%s\",nc=00000001,algorithm=%s,response=\"%s\",qop=\"%s\",charset=utf-8", $userName, $arDigestRequest["realm"], $arDigestRequest["nonce"], $request->GetPath(), $cn, $arDigestRequest["algorithm"], $hash, $arDigestRequest["qop"] ) ); return $request; } private function AuthenticateBasic($arBasicRequest, $request, $response, $verb = "Authorization") { // realm="test-exch2007" $userName = static::gmailUsernameFix($this->userName, $this->server); $request->SetHeader( $verb, sprintf( "Basic %s", base64_encode($userName.":".$this->userPassword) ) ); return $request; } private function AuthenticateGoogleLogin($arBasicRequest, $request, $response, $verb = "Authorization") { $request1 = $this->CreateBasicRequest("POST", "/accounts/ClientLogin"); $request1->SetHeader("Content-Type", "application/x-www-form-urlencoded"); $request1->SetBody('accountType='.urlencode('HOSTED_OR_GOOGLE').'&Email='.urlencode($this->userName).'&Passwd='.urlencode($this->userPassword).'&service='.urlencode('cl').'&source='.urlencode("none-none-1")); if ($this->debug) { $f = fopen($_SERVER["DOCUMENT_ROOT"]."/++++++++.+++", "a"); fwrite($f, "\n>>>>>>>>>>>>>>>>>> GOOGLEREQUEST >>>>>>>>>>>>>>>>\n"); fwrite($f, $request1 ? $request1->ToString() : "???"); fwrite($f, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); fclose($f); } $this->SendRequest($request1); $response1 = $this->GetResponse(); if ($this->debug) { $f = fopen($_SERVER["DOCUMENT_ROOT"]."/++++++++.+++", "a"); fwrite($f, "\n>>>>>>>>>>>>>>>>>> GOOGLERESPONCE >>>>>>>>>>>>>>>>\n"); fwrite($f, $response1 ? $response1->Dump() : "???"); fwrite($f, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); fclose($f); } if (($statusCode = $response1->GetStatus('code')) && (intval($statusCode) == 200)) { preg_match('/Auth=(.*)/', $response1->GetBody(), $matches); if (isset($matches[1])) { $this->googleAuth = $matches[1]; $request->SetHeader($verb, sprintf("GoogleLogin auth=%s", $this->googleAuth)); } else { return null; } } else { return null; } return $request; } private function SendRequest($request) { if (!$this->connected) $this->Connect(); if (!$this->connected) return null; fputs($this->fp, $request->ToString()); } private function GetResponse() { if (!$this->connected) return null; $arHeaders = array(); $body = ""; while ($line = fgets($this->fp, 4096)) { if ($line == "\r\n") break; $arHeaders[] = trim($line); } if (count($arHeaders) <= 0) return null; $bChunked = $bConnectionClosed = false; $contentLength = null; foreach ($arHeaders as $value) { if (!$bChunked && preg_match("#Transfer-Encoding:\s*chunked#i", $value)) $bChunked = true; if (!$bConnectionClosed && preg_match('#Connection:\s*close#i', $value)) $bConnectionClosed = true; if (is_null($contentLength)) { if (preg_match('#Content-Length:\s*([0-9]*)#i', $value, $arMatches)) $contentLength = intval($arMatches[1]); if (preg_match('#HTTP/1\.1\s+204#i', $value)) $contentLength = 0; } } if ($bChunked) { do { $line = fgets($this->fp, 4096); $line = strtolower($line); $chunkSize = ""; $i = 0; while ($i < strlen($line)) { $c = substr($line, $i, 1); if (in_array($c, array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"))) $chunkSize .= $c; else break; $i++; } $chunkSize = hexdec($chunkSize); if ($chunkSize > 0) { $lb = $chunkSize; $body1 = ''; $crutchCnt = 0; while ($lb > 0) { $d = fread($this->fp, $lb); if ($d === false) break; if ($d === '') { $crutchCnt++; if ($crutchCnt > 10) break; } else { $crutchCnt = 0; $body1 .= $d; $lb = $chunkSize - ((function_exists('mb_strlen') ? mb_strlen($body1, 'latin1') : strlen($body1))); } } $body .= $body1; } fgets($this->fp, 4096); } while ($chunkSize); } elseif ($contentLength === 0) { } elseif ($contentLength > 0) { $lb = $contentLength; while ($lb > 0) { $d = fread($this->fp, $lb); if ($d === false) break; $body .= $d; $lb = $contentLength - (function_exists('mb_strlen') ? mb_strlen($body, 'latin1') : strlen($body)); } } else { socket_set_timeout($this->fp, 0); while (!feof($this->fp)) { $d = fread($this->fp, 4096); if ($d === false) break; $body .= $d; if (substr($body, -9) == "\r\n\r\n0\r\n\r\n") { $body = substr($body, 0, -9); break; } } socket_set_timeout($this->fp, $this->socketTimeout); } if ($bConnectionClosed) $this->Disconnect(); $responce = new CDavGroupdavClientResponce($arHeaders, $body); $httpVersion = $responce->GetStatus('version'); if (is_null($httpVersion) || ($httpVersion != 'HTTP/1.1' && $httpVersion != 'HTTP/1.0')) return null; return $responce; } } ?>