Overhauled package update server API
authorAlexander Ebert <ebert@woltlab.com>
Tue, 23 Dec 2014 18:38:01 +0000 (19:38 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 23 Dec 2014 18:38:01 +0000 (19:38 +0100)
wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php
wcfsetup/install/files/lib/system/package/PackageInstallationScheduler.class.php
wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php
wcfsetup/install/files/style/redactor.less
wcfsetup/setup/db/install.sql

index 9542942612703655479b0bc5e0f82974c0f959d4..024b7c37082bd002cb2aac6815faa51155fde401 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 namespace wcf\data\package\update\server;
 use wcf\data\DatabaseObject;
+use wcf\system\io\RemoteFile;
 use wcf\system\Regex;
 use wcf\system\WCF;
 use wcf\util\FileUtil;
-use wcf\system\io\RemoteFile;
 
 /**
  * Represents a package update server.
@@ -27,6 +27,28 @@ class PackageUpdateServer extends DatabaseObject {
         */
        protected static $databaseTableIndexName = 'packageUpdateServerID';
        
+       /**
+        * API meta data
+        * @var array
+        */
+       protected $metaData = array();
+       
+       /**
+        * @see \wcf\data\DatabaseObject::handleData()
+        */
+       protected function handleData($data) {
+               if (!empty($data['metaData'])) {
+                       $metaData = @unserialize($data['metaData']);
+                       if (is_array($metaData)) {
+                               $this->metaData = $metaData;
+                       }
+                       
+                       unset($data['metaData']);
+               }
+               
+               parent::handleData($data);
+       }
+       
        /**
         * Returns all active update package servers sorted by hostname.
         * 
@@ -139,7 +161,7 @@ class PackageUpdateServer extends DatabaseObject {
        }
        
        /**
-        * Returns the list URL for package servers.
+        * Returns the list endpoint for package servers.
         * 
         * @return      string
         */
@@ -149,14 +171,39 @@ class PackageUpdateServer extends DatabaseObject {
                }
                
                $serverURL = FileUtil::addTrailingSlash($this->serverURL) . 'list/' . WCF::getLanguage()->getFixedLanguageCode() . '.xml';
-               $serverURL = preg_replace_callback('~^https?://~', function($matches) {
-                       if (RemoteFile::supportsSSL()) {
-                               return 'https://';
-                       }
-                       
-                       return 'http://';
-               }, $serverURL);
                
-               return $serverURL;
+               $metaData = $this->getMetaData();
+               if (!RemoteFile::supportsSSL() || !$metaData['ssl']) {
+                       return preg_replace('~^https://~', 'http://', $serverURL);
+               }
+               
+               return preg_replace('~^http://~', 'https://', $serverURL);
+       }
+       
+       /**
+        * Returns the download endpoint for package servers.
+        * 
+        * @return      string
+        */
+       public function getDownloadURL() {
+               if ($this->apiVersion == '2.0') {
+                       return $this->serverURL;
+               }
+               
+               $metaData = $this->getMetaData();
+               if (!RemoteFile::supportsSSL() || !$metaData['ssl']) {
+                       return preg_replace('~^https://~', 'http://', $this->serverURL);
+               }
+               
+               return preg_replace('~^http://~', 'https://', $this->serverURL);
+       }
+       
+       /**
+        * Returns API meta data.
+        * 
+        * @return      array
+        */
+       public function getMetaData() {
+               return $this->metaData;
        }
 }
index c1dfef41476bc3f1ad1ad6e860dba115aeafbb7d..623a874e46e452443919a4c4b63da2cd4142154a 100644 (file)
@@ -230,7 +230,7 @@ class PackageInstallationScheduler {
                        else {
                                // create request
                                $request = new HTTPRequest(
-                                       $this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']]->serverURL,
+                                       $this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']]->getDownloadURL(),
                                        (!empty($authData) ? array('auth' => $authData) : array()),
                                        array(
                                                'apiVersion' => PackageUpdate::API_VERSION,
index 31bd9afc455c0963b253f43a911facce6593cd56..880e81d74f013011c355a0eb607cf5ff125c5d2d 100644 (file)
@@ -79,10 +79,8 @@ class PackageUpdateDispatcher extends SingletonFactory {
         */
        protected function getPackageUpdateXML(PackageUpdateServer $updateServer) {
                $settings = array();
-               if ($updateServer->apiVersion == '2.0') {
-                       $authData = $updateServer->getAuthData();
-                       if ($authData) $settings['auth'] = $authData;
-               }
+               $authData = $updateServer->getAuthData();
+               if ($authData) $settings['auth'] = $authData;
                
                // append auth code if set and update server resolves to woltlab.com
                /*if (PACKAGE_SERVER_AUTH_CODE && Regex::compile('^https?://[a-z]+.woltlab.com/')->match($updateServer->serverURL)) {
@@ -91,9 +89,18 @@ class PackageUpdateDispatcher extends SingletonFactory {
                
                $request = new HTTPRequest($updateServer->getListURL(), $settings);
                
+               if ($updateServer->apiVersion == '2.1') {
+                       $metaData = $updateServer->getMetaData();
+                       if (!empty($metaData['list'])) {
+                               $request->addHeader('if-none-match', $metaData['list']['etag']);
+                               $request->addHeader('if-modified-since', $metaData['list']['lastModified']);
+                       }
+               }
+               
                try {
                        $request->execute();
                        $reply = $request->getReply();
+                       //die("<pre>".print_r($request, true));
                }
                catch (HTTPUnauthorizedException $e) {
                        throw new PackageUpdateUnauthorizedException($request, $updateServer);
@@ -104,9 +111,12 @@ class PackageUpdateDispatcher extends SingletonFactory {
                        $statusCode = (is_array($reply['statusCode'])) ? reset($reply['statusCode']) : $reply['statusCode'];
                        throw new SystemException(WCF::getLanguage()->get('wcf.acp.package.update.error.listNotFound') . ' ('.$statusCode.')');
                }
-               
+               //echo "<pre>" . $updateServer->getListURL() . ": " . ((is_array($reply['statusCode'])) ? reset($reply['statusCode']) : $reply['statusCode']) . "</pre>";
                // parse given package update xml
-               $allNewPackages = $this->parsePackageUpdateXML($updateServer, $reply['body']);
+               $allNewPackages = false;
+               if ($updateServer->apiVersion == '2.0' || $reply['statusCode'] != 304) {
+                       $allNewPackages = $this->parsePackageUpdateXML($reply['body']);
+               }
                
                $data = array(
                        'lastUpdateTime' => TIME_NOW,
@@ -122,6 +132,23 @@ class PackageUpdateDispatcher extends SingletonFactory {
                        }
                }
                
+               $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/or '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(
+                               'etag' => reset($reply['httpHeaders']['etag']),
+                               '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) {
@@ -144,28 +171,17 @@ class PackageUpdateDispatcher extends SingletonFactory {
        }
        
        /**
-        * Parses a stream containing info from a packages_update.xml, returns false if package list
-        * has not changed since last update.
+        * Parses a stream containing info from a packages_update.xml.
         * 
-        * @param       \wcf\data\package\update\server\PackageUpdateServer     $updateServer
-        * @param       string                                                  $content
-        * @return      array                                                   $allNewPackages
+        * @param       string          $content
+        * @return      array           $allNewPackages
         */
-       protected function parsePackageUpdateXML(PackageUpdateServer $updateServer, $content) {
+       protected function parsePackageUpdateXML($content) {
                // load xml document
                $xml = new XML();
                $xml->loadXML('packageUpdateServer.xml', $content);
                $xpath = $xml->xpath();
                
-               // loop through <package> tags inside the <section> tag.
-               $section = $xpath->query('/ns:section');
-               if ($section->item(0)->hasAttribute('lastUpdateTime')) {
-                       $lastUpdateTime = intval($section->item(0)->getAttribute('lastUpdateTime'));
-                       if ($lastUpdateTime && $updateServer->lastUpdateTime > $lastUpdateTime) {
-                               return false;
-                       }
-               }
-               
                $allNewPackages = array();
                $packages = $xpath->query('/ns:section/ns:package');
                foreach ($packages as $package) {
index 623e9c6ac367f511edd27934094b8a640bca6b2d..8ebc2f9ac625b318880a2f9211b4cd91c5f5306b 100644 (file)
                        &.redactor-btn-image {
                                background-position: center;
                                background-repeat: no-repeat;
+                               
+                               &.redactor-button-disabled {
+                                       opacity: .5;
+                               }
                        }
                }
        }
index e951c2f20771d09a5ca6c01dd53d0ef4d75dfad6..645a267f1586444be8081426eca145384270e4e3 100644 (file)
@@ -760,7 +760,8 @@ CREATE TABLE wcf1_package_update_server (
        lastUpdateTime INT(10) NOT NULL DEFAULT 0,
        status ENUM('online', 'offline') NOT NULL DEFAULT 'online',
        errorMessage TEXT,
-       apiVersion ENUM('2.0', '2.1') NOT NULL DEFAULT '2.0'
+       apiVersion ENUM('2.0', '2.1') NOT NULL DEFAULT '2.0',
+       metaData TEXT
 );
 
 DROP TABLE IF EXISTS wcf1_package_update_version;