Added graceful downgrade to HTTP if SSL is being blocked
authorAlexander Ebert <ebert@woltlab.com>
Fri, 6 Feb 2015 13:18:37 +0000 (14:18 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 6 Feb 2015 13:18:37 +0000 (14:18 +0100)
wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php
wcfsetup/install/files/lib/system/io/RemoteFile.class.php
wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php

index 024b7c37082bd002cb2aac6815faa51155fde401..e70dc9fde3adebdfab78bb1ddea6191739323c59 100644 (file)
@@ -163,9 +163,10 @@ class PackageUpdateServer extends DatabaseObject {
        /**
         * Returns the list endpoint for package servers.
         * 
+        * @param       boolean         $forceHTTP
         * @return      string
         */
-       public function getListURL() {
+       public function getListURL($forceHTTP = false) {
                if ($this->apiVersion == '2.0') {
                        return $this->serverURL;
                }
@@ -173,7 +174,7 @@ class PackageUpdateServer extends DatabaseObject {
                $serverURL = FileUtil::addTrailingSlash($this->serverURL) . 'list/' . WCF::getLanguage()->getFixedLanguageCode() . '.xml';
                
                $metaData = $this->getMetaData();
-               if (!RemoteFile::supportsSSL() || !$metaData['ssl']) {
+               if ($forceHTTP || !RemoteFile::supportsSSL() || !$metaData['ssl']) {
                        return preg_replace('~^https://~', 'http://', $serverURL);
                }
                
@@ -206,4 +207,22 @@ class PackageUpdateServer extends DatabaseObject {
        public function getMetaData() {
                return $this->metaData;
        }
+       
+       /**
+        * 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;
+       }
 }
index ba17cb01d5f45ddc5e9d998493ea1cf1ffebf523..dc137ebf3b7acba6454e4a7e9f8436f84773aa1b 100644 (file)
@@ -124,4 +124,11 @@ class RemoteFile extends File {
                
                return static::$hasSSLSupport;
        }
+       
+       /**
+        * Disables SSL/TLS support on runtime regardless if PHP is theoretically capable of it.
+        */
+       public static function disableSSL() {
+               static::$hasSSLSupport = false;
+       }
 }
index 6fa770eb22129d59b94afd75911a2dba55fc2f7e..47fea1058e8862d036db42f473385fd5efdf4794 100644 (file)
@@ -3,7 +3,6 @@ namespace wcf\system\package;
 use wcf\data\package\update\server\PackageUpdateServer;
 use wcf\data\package\update\server\PackageUpdateServerEditor;
 use wcf\data\package\update\version\PackageUpdateVersionEditor;
-use wcf\data\package\update\PackageUpdate;
 use wcf\data\package\update\PackageUpdateEditor;
 use wcf\data\package\Package;
 use wcf\system\cache\builder\PackageUpdateCacheBuilder;
@@ -11,17 +10,17 @@ use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\HTTPUnauthorizedException;
 use wcf\system\exception\SystemException;
 use wcf\system\package\PackageUpdateUnauthorizedException;
-use wcf\system\Regex;
 use wcf\system\SingletonFactory;
 use wcf\system\WCF;
 use wcf\util\HTTPRequest;
 use wcf\util\XML;
+use wcf\system\io\RemoteFile;
 
 /**
  * Provides functions to manage package updates.
  * 
  * @author     Alexander Ebert
- * @copyright  2001-2014 WoltLab GmbH
+ * @copyright  2001-2015 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.package
@@ -36,34 +35,49 @@ class PackageUpdateDispatcher extends SingletonFactory {
         */
        public function refreshPackageDatabase(array $packageUpdateServerIDs = array(), $ignoreCache = false) {
                // get update server data
-               $updateServers = PackageUpdateServer::getActiveUpdateServers($packageUpdateServerIDs);
+               $tmp = PackageUpdateServer::getActiveUpdateServers($packageUpdateServerIDs);
                
                // loop servers
-               $refreshedPackageLists = false;
-               foreach ($updateServers as $updateServer) {
+               $updateServers = array();
+               $foundWoltLabServer = false;
+               foreach ($tmp as $updateServer) {
                        if ($ignoreCache || $updateServer->lastUpdateTime < TIME_NOW - 600) {
-                               $errorMessage = '';
-                               
-                               try {
-                                       $this->getPackageUpdateXML($updateServer);
-                                       $refreshedPackageLists = true;
-                               }
-                               catch (SystemException $e) {
-                                       $errorMessage = $e->getMessage();
-                               }
-                               catch (PackageUpdateUnauthorizedException $e) {
-                                       $reply = $e->getRequest()->getReply();
-                                       list($errorMessage) = reset($reply['httpHeaders']);
+                               // try to queue a woltlab.com update server first to probe for SSL support
+                               if (!$foundWoltLabServer && preg_match('~^https?://(?:update|store)\.woltlab\.com~', $updateServer->serverURL)) {
+                                       array_unshift($updateServers, $updateServer);
+                                       $foundWoltLabServer = true;
+                                       
+                                       continue;
                                }
                                
-                               if ($errorMessage) {
-                                       // save error status
-                                       $updateServerEditor = new PackageUpdateServerEditor($updateServer);
-                                       $updateServerEditor->update(array(
-                                               'status' => 'offline',
-                                               'errorMessage' => $errorMessage
-                                       ));
-                               }
+                               $updateServers[] = $updateServer;
+                       }
+               } 
+               
+               // loop servers
+               $refreshedPackageLists = false;
+               foreach ($updateServers as $updateServer) {
+                       $errorMessage = '';
+                       
+                       try {
+                               $this->getPackageUpdateXML($updateServer);
+                               $refreshedPackageLists = true;
+                       }
+                       catch (SystemException $e) {
+                               $errorMessage = $e->getMessage();
+                       }
+                       catch (PackageUpdateUnauthorizedException $e) {
+                               $reply = $e->getRequest()->getReply();
+                               list($errorMessage) = reset($reply['httpHeaders']);
+                       }
+                       
+                       if ($errorMessage) {
+                               // save error status
+                               $updateServerEditor = new PackageUpdateServerEditor($updateServer);
+                               $updateServerEditor->update(array(
+                                       'status' => 'offline',
+                                       'errorMessage' => $errorMessage
+                               ));
                        }
                }
                
@@ -76,8 +90,9 @@ class PackageUpdateDispatcher extends SingletonFactory {
         * 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) {
+       protected function getPackageUpdateXML(PackageUpdateServer $updateServer, $forceHTTP = false) {
                $settings = array();
                $authData = $updateServer->getAuthData();
                if ($authData) $settings['auth'] = $authData;
@@ -87,7 +102,7 @@ class PackageUpdateDispatcher extends SingletonFactory {
                        $postData['authCode'] = PACKAGE_SERVER_AUTH_CODE;
                }*/
                
-               $request = new HTTPRequest($updateServer->getListURL(), $settings);
+               $request = new HTTPRequest($updateServer->getListURL($forceHTTP), $settings);
                
                if ($updateServer->apiVersion == '2.1') {
                        $metaData = $updateServer->getMetaData();
@@ -106,6 +121,18 @@ class PackageUpdateDispatcher extends SingletonFactory {
                        $reply = $request->getReply();
                        
                        $statusCode = (is_array($reply['statusCode'])) ? reset($reply['statusCode']) : $reply['statusCode'];
+                       // status code 0 is a connection timeout
+                       if (!$statusCode && $updateServer->attemptSecureConnection()) {
+                               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.')');
                }