Example #1
0
 /**
  * Searches for purchased items in the WoltLab Plugin-Store.
  * 
  * @return	array<string>
  */
 public function searchForPurchasedItems()
 {
     if (!RemoteFile::supportsSSL()) {
         return array('noSSL' => WCF::getLanguage()->get('wcf.acp.pluginStore.api.noSSL'));
     }
     if (empty($this->parameters['username']) || empty($this->parameters['password'])) {
         return array('template' => $this->renderAuthorizationDialog(false));
     }
     $request = new HTTPRequest('https://api.woltlab.com/1.0/customer/purchases/list.json', array('method' => 'POST'), array('username' => $this->parameters['username'], 'password' => $this->parameters['password'], 'wcfVersion' => WCF_VERSION));
     $request->execute();
     $reply = $request->getReply();
     $response = JSON::decode($reply['body']);
     $code = isset($response['status']) ? $response['status'] : 500;
     switch ($code) {
         case 200:
             if (empty($response['products'])) {
                 return array('noResults' => WCF::getLanguage()->get('wcf.acp.pluginStore.purchasedItems.noResults'));
             } else {
                 WCF::getSession()->register('__pluginStoreProducts', $response['products']);
                 WCF::getSession()->register('__pluginStoreWcfMajorReleases', $response['wcfMajorReleases']);
                 return array('redirectURL' => LinkHandler::getInstance()->getLink('PluginStorePurchasedItems'));
             }
             break;
             // authentication error
         // authentication error
         case 401:
             return array('template' => $this->renderAuthorizationDialog(true));
             break;
             // any other kind of errors
         // any other kind of errors
         default:
             throw new SystemException(WCF::getLanguage()->getDynamicVariable('wcf.acp.pluginStore.api.error', array('status' => $code)));
             break;
     }
 }
Example #2
0
 /**
  * Downloads a package archive from an http URL.
  * 
  * @param	string		$httpUrl
  * @param	string		$prefix
  * @param	array		$options
  * @param	array		$postParameters
  * @param	array		$headers		Should be either an empty array or a not initialized variable.
  * @return	string					path to the downloaded file
  */
 public static function downloadFileFromHttp($httpUrl, $prefix = 'package', array $options = array(), array $postParameters = array(), &$headers = array())
 {
     $newFileName = self::getTemporaryFilename($prefix . '_');
     $localFile = new File($newFileName);
     // the file to write.
     if (!isset($options['timeout'])) {
         $options['timeout'] = 30;
     }
     if (!isset($options['method'])) {
         $options['method'] = !empty($postParameters) ? 'POST' : 'GET';
     }
     // parse URL
     $parsedUrl = parse_url($httpUrl);
     $port = $parsedUrl['scheme'] == 'https' ? 443 : 80;
     $host = $parsedUrl['host'];
     $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '/';
     // proxy is set
     if (PROXY_SERVER_HTTP) {
         $parsedProxy = parse_url(PROXY_SERVER_HTTP);
         $host = $parsedProxy['host'];
         $port = $parsedProxy['scheme'] == 'https' ? 443 : 80;
         $path = $httpUrl;
         $parsedUrl['query'] = '';
     }
     // build parameter-string
     $parameterString = '';
     foreach ($postParameters as $key => $value) {
         if (is_array($value)) {
             foreach ($value as $value2) {
                 $parameterString .= urlencode($key) . '[]=' . urlencode($value2) . '&';
             }
         } else {
             $parameterString .= urlencode($key) . '=' . urlencode($value) . '&';
         }
     }
     $parameterString = rtrim($parameterString, '&');
     // connect
     try {
         $remoteFile = new RemoteFile(($port === 443 ? 'ssl://' : '') . $host, $port, $options['timeout']);
     } catch (SystemException $e) {
         $localFile->close();
         unlink($newFileName);
         throw $e;
     }
     // build and send the http request.
     $request = $options['method'] . " " . $path . (!empty($parsedUrl['query']) ? '?' . $parsedUrl['query'] : '') . " HTTP/1.0\r\n";
     $request .= "User-Agent: HTTP.PHP (FileUtil.class.php; WoltLab Community Framework/" . WCF_VERSION . "; " . WCF::getLanguage()->languageCode . ")\r\n";
     $request .= "Accept: */*\r\n";
     $request .= "Accept-Language: " . WCF::getLanguage()->languageCode . "\r\n";
     if ($options['method'] !== 'GET') {
         $request .= "Content-length: " . strlen($parameterString) . "\r\n";
         $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
     }
     $request .= "Host: " . $host . "\r\n";
     $request .= "Connection: Close\r\n\r\n";
     if ($options['method'] !== 'GET') {
         $request .= $parameterString . "\r\n\r\n";
     }
     $remoteFile->puts($request);
     $inHeader = true;
     $readResponse = array();
     // read http response.
     while (!$remoteFile->eof()) {
         $readResponse[] = $remoteFile->gets();
         if ($inHeader) {
             if (rtrim(end($readResponse)) == '') {
                 $inHeader = false;
             }
         } else {
             // look if the webserver sent an error http statuscode
             // This has still to be checked if really sufficient!
             $arrayHeader = array('201', '301', '302', '303', '307', '404');
             foreach ($arrayHeader as $code) {
                 $error = strpos($readResponse[0], $code);
             }
             if ($error !== false) {
                 $localFile->close();
                 unlink($newFileName);
                 throw new SystemException("file " . $path . " not found at host '" . $host . "'");
             }
             // write to the target system.
             $localFile->write(end($readResponse));
         }
     }
     foreach ($readResponse as $line) {
         if (rtrim($line) == '') {
             break;
         }
         if (strpos($line, ':') === false) {
             $headers[trim($line)] = trim($line);
             continue;
         }
         list($key, $value) = explode(':', $line, 2);
         $headers[$key] = trim($value);
     }
     $remoteFile->close();
     $localFile->close();
     return $newFileName;
 }
 /**
  * Sends a request to a remote (update) server.
  * 
  * @param	string		$url
  * @param	array		$values
  * @param	array		$authData
  * @return	array		$response
  */
 public static function sendRequest($url, array $values = array(), array $authData = array())
 {
     // default values
     $host = '';
     $path = '/';
     $port = 80;
     $postString = '';
     // parse url
     $parsedURL = parse_url($url);
     if (!empty($parsedURL['host'])) {
         $host = $parsedURL['host'];
     }
     if (!empty($parsedURL['path'])) {
         $path = $parsedURL['path'];
     }
     if (!empty($parsedURL['query'])) {
         $postString = $parsedURL['query'];
     }
     if (!empty($parsedURL['port'])) {
         $port = $parsedURL['port'];
     }
     // connect to server
     if (PROXY_SERVER_HTTP) {
         $parsedProxyURL = parse_url(PROXY_SERVER_HTTP);
         $remoteFile = new RemoteFile($parsedProxyURL['host'], $parsedProxyURL['port'], 30);
         $path = $url;
         $host = $parsedProxyURL['host'];
     } else {
         $remoteFile = new RemoteFile($host, $port, 30);
     }
     // Build and send the http request
     $request = "POST " . $path . " HTTP/1.0\r\n";
     if (isset($authData['authType'])) {
         $request .= "Authorization: Basic " . base64_encode($authData['loginUsername'] . ":" . $authData['loginPassword']) . "\r\n";
     }
     $request .= "User-Agent: HTTP.PHP (PackageUpdateDispatcher.class.php; WoltLab Community Framework/" . WCF_VERSION . "; " . WCF::getLanguage()->languageCode . ")\r\n";
     $request .= "Accept: */*\r\n";
     $request .= "Accept-Language: " . WCF::getLanguage()->languageCode . "\r\n";
     $request .= "Host: " . $host . "\r\n";
     // build post string
     foreach ($values as $name => $value) {
         if (!empty($postString)) {
             $postString .= '&';
         }
         $postString .= $name . '=' . $value;
     }
     // send content type and length
     $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
     $request .= "Content-Length: " . strlen($postString) . "\r\n";
     // if it is a POST request, there MUST be a blank line before the POST data, but there MUST NOT be
     // another blank line before, and of course there must be another blank line at the end of the request!
     $request .= "\r\n";
     if (!empty($postString)) {
         $request .= $postString . "\r\n";
     }
     // send close
     $request .= "Connection: Close\r\n\r\n";
     // send request
     $remoteFile->puts($request);
     unset($request, $postString);
     // define response vars
     $header = $content = '';
     // fetch the response.
     while (!$remoteFile->eof()) {
         $line = $remoteFile->gets();
         if (rtrim($line) != '') {
             $header .= $line;
         } else {
             break;
         }
     }
     while (!$remoteFile->eof()) {
         $content .= $remoteFile->gets();
     }
     // clean up and return the server's response.
     $remoteFile->close();
     // get http status code / line
     $httpStatusCode = 0;
     $httpStatusLine = '';
     if (preg_match('%http/\\d\\.\\d (\\d{3})[^\\n]*%i', $header, $match)) {
         $httpStatusLine = trim($match[0]);
         $httpStatusCode = $match[1];
     }
     // catch http 301 Moved Permanently
     // catch http 302 Found
     // catch http 303 See Other
     if ($httpStatusCode == 301 || $httpStatusCode == 302 || $httpStatusCode == 303) {
         // find location
         if (preg_match('/location:([^\\n]*)/i', $header, $match)) {
             $location = trim($match[1]);
             if ($location != $url) {
                 return self::sendRequest($location, $values, $authData);
             }
         }
     }
     // catch other http codes here
     return array('httpStatusLine' => $httpStatusLine, 'httpStatusCode' => $httpStatusCode, 'header' => $header, 'content' => $content);
 }
 /**
  * Returns true if a request to this server would make use of a secure connection.
  * 
  * @return	boolean
  */
 public function attemptSecureConnection()
 {
     if ($this->apiVersion == '2.0') {
         return false;
     }
     $metaData = $this->getMetaData();
     if (RemoteFile::supportsSSL() && $metaData['ssl']) {
         return true;
     }
     return false;
 }
Example #5
0
 /**
  * Executes the HTTP request.
  */
 public function execute()
 {
     // connect
     $remoteFile = new RemoteFile(($this->useSSL ? 'ssl://' : '') . $this->host, $this->port, $this->options['timeout'], array('ssl' => array('peer_name' => $this->originHost)));
     if ($this->originUseSSL && PROXY_SERVER_HTTP) {
         if ($this->useSSL) {
             throw new SystemException("Unable to proxy HTTPS when using TLS for proxy connection");
         }
         $request = "CONNECT " . $this->originHost . ":" . $this->originPort . " HTTP/1.0\r\n";
         if (isset($this->headers['user-agent'])) {
             $request .= 'user-agent: ' . reset($this->headers['user-agent']) . "\r\n";
         }
         $request .= "Host: " . $this->originHost . ":" . $this->originPort . "\r\n";
         $request .= "\r\n";
         $remoteFile->puts($request);
         $this->replyHeaders = array();
         while (!$remoteFile->eof()) {
             $line = $remoteFile->gets();
             if (rtrim($line) === '') {
                 $this->parseReplyHeaders();
                 break;
             }
             $this->replyHeaders[] = $line;
         }
         if ($this->statusCode != 200) {
             throw new SystemException("Expected 200 Ok as reply to my CONNECT, got '" . $this->statusCode . "'");
         }
         $remoteFile->setTLS(true);
     }
     $request = $this->options['method'] . " " . $this->path . ($this->query ? '?' . $this->query : '') . " HTTP/1.1\r\n";
     // add headers
     foreach ($this->headers as $name => $values) {
         foreach ($values as $value) {
             $request .= $name . ": " . $value . "\r\n";
         }
     }
     $request .= "\r\n";
     // add post parameters
     if ($this->options['method'] !== 'GET') {
         $request .= $this->body . "\r\n\r\n";
     }
     $remoteFile->puts($request);
     $inHeader = true;
     $this->replyHeaders = array();
     $this->replyBody = '';
     $chunkLength = 0;
     $bodyLength = 0;
     $chunkedTransferRegex = new Regex('(^|,)[ \\t]*chunked[ \\t]*$', Regex::CASE_INSENSITIVE);
     // read http response, until one of is true
     // a) EOF is reached
     // b) bodyLength is at least maxLength
     // c) bodyLength is at least Content-Length
     while (!($remoteFile->eof() || isset($this->options['maxLength']) && $bodyLength >= $this->options['maxLength'] || isset($this->replyHeaders['content-length']) && $bodyLength >= end($this->replyHeaders['content-length']))) {
         if ($chunkLength) {
             if (isset($this->options['maxLength'])) {
                 $chunkLength = min($chunkLength, $this->options['maxLength'] - $bodyLength);
             }
             $line = $remoteFile->read($chunkLength);
         } else {
             if (!$inHeader && (!isset($this->replyHeaders['transfer-encoding']) || !$chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding'])))) {
                 $length = 1024;
                 if (isset($this->options['maxLength'])) {
                     $length = min($length, $this->options['maxLength'] - $bodyLength);
                 }
                 if (isset($this->replyHeaders['content-length'])) {
                     $length = min($length, end($this->replyHeaders['content-length']) - $bodyLength);
                 }
                 $line = $remoteFile->read($length);
             } else {
                 $line = $remoteFile->gets();
             }
         }
         if ($inHeader) {
             if (rtrim($line) === '') {
                 $inHeader = false;
                 $this->parseReplyHeaders();
                 continue;
             }
             $this->replyHeaders[] = $line;
         } else {
             if (isset($this->replyHeaders['transfer-encoding']) && $chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding']))) {
                 // last chunk finished
                 if ($chunkLength === 0) {
                     // read hex data and trash chunk-extension
                     list($hex) = explode(';', $line, 2);
                     $chunkLength = hexdec($hex);
                     // $chunkLength === 0 -> no more data
                     if ($chunkLength === 0) {
                         // clear remaining response
                         while (!$remoteFile->gets(1024)) {
                         }
                         // remove chunked from transfer-encoding
                         $this->replyHeaders['transfer-encoding'] = array_filter(array_map(function ($element) use($chunkedTransferRegex) {
                             return $chunkedTransferRegex->replace($element, '');
                         }, $this->replyHeaders['transfer-encoding']), 'trim');
                         if (empty($this->replyHeaders['transfer-encoding'])) {
                             unset($this->replyHeaders['transfer-encoding']);
                         }
                         // break out of main reading loop
                         break;
                     }
                 } else {
                     $this->replyBody .= $line;
                     $chunkLength -= strlen($line);
                     $bodyLength += strlen($line);
                     if ($chunkLength === 0) {
                         $remoteFile->read(2);
                     }
                     // CRLF
                 }
             } else {
                 $this->replyBody .= $line;
                 $bodyLength += strlen($line);
             }
         }
     }
     if (isset($this->options['maxLength'])) {
         $this->replyBody = substr($this->replyBody, 0, $this->options['maxLength']);
     }
     $remoteFile->close();
     $this->parseReply();
 }
 /**
  * Downloads and imports a language file from a language server.
  * 
  * @param	string		$location
  * @param	array<string>	$packageList
  */
 protected function importLanguageFile($location, array $packageList)
 {
     // get proxy
     $options = array();
     if (PROXY_SERVER_HTTP) {
         $options['http']['proxy'] = PROXY_SERVER_HTTP;
         $options['http']['request_fulluri'] = true;
     }
     // parse url
     $parsedURL = parse_url($location);
     $port = $parsedURL['scheme'] == 'https' ? 443 : 80;
     $host = $parsedURL['host'];
     $path = isset($parsedURL['path']) ? $parsedURL['path'] : '/';
     $remoteFile = new RemoteFile(($parsedURL['scheme'] == 'https' ? 'ssl://' : '') . $host, $port, 30, $options);
     // the file to read.
     if (!isset($remoteFile)) {
         throw new SystemException("cannot connect to http host '" . $host . "'");
     }
     // build and send the http request
     $request = "POST " . $path . " HTTP/1.0\r\n";
     $request .= "User-Agent: HTTP.PHP (LanguageServerProcessor.class.php; WoltLab Community Framework/" . WCF_VERSION . "; " . WCF::getLanguage()->languageCode . ")\r\n";
     $request .= "Accept: */*\r\n";
     $request .= "Accept-Language: " . WCF::getLanguage()->languageCode . "\r\n";
     $request .= "Host: " . $host . "\r\n";
     // build post string
     $postString = 'languageCode=' . $this->language->languageCode;
     foreach ($packageList as $package => $packageVersion) {
         $postString .= '&packages[' . urlencode($package) . ']=' . urlencode($packageVersion);
     }
     // send content type and length
     $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
     $request .= "Content-Length: " . strlen($postString) . "\r\n";
     // if it is a POST request, there MUST be a blank line before the POST data, but there MUST NOT be
     // another blank line before, and of course there must be another blank line at the end of the request!
     $request .= "\r\n";
     if (!empty($postString)) {
         $request .= $postString . "\r\n";
     }
     // send close
     $request .= "Connection: Close\r\n\r\n";
     // send request
     $remoteFile->puts($request);
     // define response vars
     $header = $content = '';
     // fetch the response.
     while (!$remoteFile->eof()) {
         $line = $remoteFile->gets();
         if (rtrim($line) != '') {
             $header .= $line;
         } else {
             break;
         }
     }
     while (!$remoteFile->eof()) {
         $content .= $remoteFile->gets();
     }
     // clean up and return the server's response.
     $remoteFile->close();
     // get http status code / line
     $httpStatusCode = 0;
     $httpStatusLine = '';
     if (preg_match('%http/\\d\\.\\d (\\d{3})[^\\n]*%i', $header, $match)) {
         $httpStatusLine = trim($match[0]);
         $httpStatusCode = $match[1];
     }
     // catch http 301 Moved Permanently
     // catch http 302 Found
     // catch http 303 See Other
     if ($httpStatusCode == 301 || $httpStatusCode == 302 || $httpStatusCode == 303) {
         // find location
         if (preg_match('/location:([^\\n]*)/i', $header, $match)) {
             $newLocation = trim($match[1]);
             if ($newLocation != $location) {
                 $this->importLanguageFile($location, $packageList);
                 return;
             }
         }
     }
     $this->parseResponse($content);
 }
 /**
  * Gets the package_update.xml from an update server.
  * 
  * @param	\wcf\data\package\update\server\PackageUpdateServer	$updateServer
  * @param	boolean							$forceHTTP
  */
 protected function getPackageUpdateXML(PackageUpdateServer $updateServer, $forceHTTP = false)
 {
     $settings = array();
     $authData = $updateServer->getAuthData();
     if ($authData) {
         $settings['auth'] = $authData;
     }
     $secureConnection = $updateServer->attemptSecureConnection();
     if ($secureConnection && !$forceHTTP) {
         $settings['timeout'] = 5;
     }
     $request = new HTTPRequest($updateServer->getListURL($forceHTTP), $settings);
     if ($updateServer->apiVersion == '2.1') {
         $metaData = $updateServer->getMetaData();
         if (isset($metaData['list']['etag'])) {
             $request->addHeader('if-none-match', $metaData['list']['etag']);
         }
         if (isset($metaData['list']['lastModified'])) {
             $request->addHeader('if-modified-since', $metaData['list']['lastModified']);
         }
     }
     try {
         $request->execute();
         $reply = $request->getReply();
     } catch (HTTPUnauthorizedException $e) {
         throw new PackageUpdateUnauthorizedException($request, $updateServer);
     } catch (SystemException $e) {
         $reply = $request->getReply();
         $statusCode = is_array($reply['statusCode']) ? reset($reply['statusCode']) : $reply['statusCode'];
         // status code 0 is a connection timeout
         if (!$statusCode && $secureConnection) {
             if (preg_match('~https?://(?:update|store)\\.woltlab\\.com~', $updateServer->serverURL)) {
                 // woltlab.com servers are most likely to be available, thus we assume that SSL connections are dropped
                 RemoteFile::disableSSL();
             }
             // retry via http
             $this->getPackageUpdateXML($updateServer, true);
             return;
         }
         throw new SystemException(WCF::getLanguage()->get('wcf.acp.package.update.error.listNotFound') . ' (' . $statusCode . ')');
     }
     // parse given package update xml
     $allNewPackages = false;
     if ($updateServer->apiVersion == '2.0' || $reply['statusCode'] != 304) {
         $allNewPackages = $this->parsePackageUpdateXML($reply['body']);
     }
     $data = array('lastUpdateTime' => TIME_NOW, 'status' => 'online', 'errorMessage' => '');
     // check if server indicates support for a newer API
     if ($updateServer->apiVersion == '2.0' && !empty($reply['httpHeaders']['wcf-update-server-api'])) {
         $apiVersions = explode(' ', reset($reply['httpHeaders']['wcf-update-server-api']));
         if (in_array('2.1', $apiVersions)) {
             $data['apiVersion'] = '2.1';
         }
     }
     $metaData = array();
     if ($updateServer->apiVersion == '2.1' || isset($data['apiVersion']) && $data['apiVersion'] == '2.1') {
         if (empty($reply['httpHeaders']['etag']) && empty($reply['httpHeaders']['last-modified'])) {
             throw new SystemException("Missing required HTTP headers 'etag' and 'last-modified'.");
         } else {
             if (empty($reply['httpHeaders']['wcf-update-server-ssl'])) {
                 throw new SystemException("Missing required HTTP header 'wcf-update-server-ssl'.");
             }
         }
         $metaData['list'] = array();
         if (!empty($reply['httpHeaders']['etag'])) {
             $metaData['list']['etag'] = reset($reply['httpHeaders']['etag']);
         }
         if (!empty($reply['httpHeaders']['last-modified'])) {
             $metaData['list']['lastModified'] = reset($reply['httpHeaders']['last-modified']);
         }
         $metaData['ssl'] = reset($reply['httpHeaders']['wcf-update-server-ssl']) == 'true' ? true : false;
     }
     $data['metaData'] = serialize($metaData);
     unset($request, $reply);
     if ($allNewPackages !== false) {
         // purge package list
         $sql = "DELETE FROM\twcf" . WCF_N . "_package_update\n\t\t\t\tWHERE\t\tpackageUpdateServerID = ?";
         $statement = WCF::getDB()->prepareStatement($sql);
         $statement->execute(array($updateServer->packageUpdateServerID));
         // save packages
         if (!empty($allNewPackages)) {
             $this->savePackageUpdates($allNewPackages, $updateServer->packageUpdateServerID);
         }
         unset($allNewPackages);
     }
     // update server status
     $updateServerEditor = new PackageUpdateServerEditor($updateServer);
     $updateServerEditor->update($data);
 }
Example #8
0
	/**
	 * Executes the HTTP request.
	 */
	public function execute() {
		// connect
		$remoteFile = new RemoteFile(($this->useSSL ? 'ssl://' : '').$this->host, $this->port, $this->options['timeout']);
		
		$request = $this->options['method']." ".$this->path.($this->query ? '?'.$this->query : '')." HTTP/1.0\r\n";
		
		// add headers
		foreach ($this->headers as $name => $values) {
			foreach ($values as $value) {
				$request .= $name.": ".$value."\r\n";
			}
		}
		$request .= "\r\n";
		// add post parameters
		if ($this->options['method'] !== 'GET') $request .= http_build_query($this->postParameters)."\r\n\r\n";
		$remoteFile->puts($request);
		
		$inHeader = true;
		$this->replyHeaders = array();
		$this->replyBody = '';
		// read http response.
		while (!$remoteFile->eof()) {
			$line = $remoteFile->gets();
			if ($inHeader) {
				if (rtrim($line) === '') {
					$inHeader = false;
					continue;
				}
				$this->replyHeaders[] = $line;
			}
			else {
				$this->replyBody .= $line;
			}
		}
		
		$this->parseReply();
	}
Example #9
0
 /**
  * Sends Information to the smtp-Server
  * 
  * @param	string		$data
  */
 protected function write($data)
 {
     $this->connection->puts($data . Mail::$lineEnding);
 }