def get(self, name): """Catalog get handler. Args: name: string catalog name to get. Returns: A webapp.Response() response. """ auth.DoAnyAuth() catalog = models.Catalog.MemcacheWrappedGet(name) if not catalog: self.response.set_status(httplib.NOT_FOUND) return header_date_str = self.request.headers.get('If-Modified-Since', '') if not handlers.IsClientResourceExpired(catalog.mtime, header_date_str): self.response.set_status(httplib.NOT_MODIFIED) return self.response.headers['Last-Modified'] = catalog.mtime.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['Content-Type'] = 'text/xml; charset=utf-8' self.response.out.write(catalog.plist_xml)
def get(self, client_id_str=''): """AppleSUS get handler. Args: name: str, optional, catalog name to get. """ session = auth.DoAnyAuth() client_id = handlers.GetClientIdForRequest( self.request, session=session, client_id_str=client_id_str) # get only major.minor os_version, stripping miniscule versioning. # i.e. 10.6.6 becomes 10.6, 10.23.6.x.x becomes 10.23 full_os_version = client_id.get('os_version', '') or '' os_version = '.'.join(full_os_version.split('.', 2)[:2]) track = client_id.get('track', 'stable') catalog_name = '%s_%s' % (os_version, track) catalog = models.AppleSUSCatalog.MemcacheWrappedGet(catalog_name) if not catalog: logging.warning('Apple SUS catalog not found: %s', catalog_name) self.response.set_status(404) return header_date_str = self.request.headers.get('If-Modified-Since', '') catalog_date = catalog.mtime if handlers.IsClientResourceExpired(catalog_date, header_date_str): self.response.headers['Last-Modified'] = catalog_date.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['Content-Type'] = 'text/xml; charset=utf-8' self.response.out.write(catalog.plist) else: self.response.set_status(304)
def get(self, msg='', unused_provided_by_softwareupdate=''): """AppleSUS get handler. Args: name: str, catalog name to get. unused_provided_by_softwareupdate: Apple softwareupdate appends filename to our url. """ if msg: # Clients first POST to this handler and receive a URL with an embedded, # encrypted cookie-set containing an Auth1Token. When GET is called on # this URL, we must decode and unpack the cookie/Auth1Token, and set # the HTTP_COOKIE environment variable for DoMunkiAuth to validate. try: d = _DecodeMsg(msg) except ValueError: self.response.set_status(httplib.BAD_REQUEST) return os.environ['HTTP_COOKIE'] = str(d['cookies']) self.request.headers[MUNKI_CLIENT_ID_HEADER_KEY] = d['header'] session = gaeserver.DoMunkiAuth(require_level=gaeserver.LEVEL_APPLESUS) client_id = handlers.GetClientIdForRequest(self.request, session=session) # get only major.minor os_version, stripping miniscule versioning. # i.e. 10.6.6 becomes 10.6, 10.23.6.x.x becomes 10.23 full_os_version = client_id.get('os_version', '') os_version = '.'.join(full_os_version.split('.', 2)[:2]) track = client_id.get('track', 'stable') catalog_name = '%s_%s' % (os_version, track) catalog = models.AppleSUSCatalog.MemcacheWrappedGet(catalog_name) if not catalog: logging.warning('Apple SUS catalog not found: %s', catalog_name) self.response.set_status(httplib.NOT_FOUND) return header_date_str = self.request.headers.get('If-Modified-Since', '') catalog_date = catalog.mtime if handlers.IsClientResourceExpired(catalog_date, header_date_str): self.response.headers['Last-Modified'] = catalog_date.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['Content-Type'] = 'text/xml; charset=utf-8' self.response.out.write(catalog.plist) else: self.response.set_status(httplib.NOT_MODIFIED)
def get(self, filename): """GET Args: filename: str, package filename like 'foo.dmg' Returns: None if a blob is being returned, or a response object """ auth_return = auth.DoAnyAuth() if hasattr(auth_return, 'email'): email = auth_return.email() if not auth.IsAdminUser(email) and not auth.IsSupportUser(email): raise auth.IsAdminMismatch filename = urllib.unquote(filename) pkg = models.PackageInfo.MemcacheWrappedGet(filename) if pkg is None or not pkg.blobstore_key: self.error(404) return if common.IsPanicModeNoPackages(): self.error(503) return # Get the Blobstore BlobInfo for this package; memcache wrapped. memcache_key = 'blobinfo_%s' % filename blob_info = memcache.get(memcache_key) if not blob_info: blob_info = blobstore.BlobInfo.get(pkg.blobstore_key) if blob_info: memcache.set(memcache_key, blob_info, 300) # cache for 5 minutes. else: logging.critical( 'Failure fetching BlobInfo for %s. Verify the blob exists: %s', pkg.filename, pkg.blobstore_key) self.error(404) return header_date_str = self.request.headers.get('If-Modified-Since', '') etag_nomatch_str = self.request.headers.get('If-None-Match', 0) etag_match_str = self.request.headers.get('If-Match', 0) pkg_date = blob_info.creation pkg_size_bytes = blob_info.size # TODO(user): The below can be simplified once all of our clients # have ETag values set on the filesystem for these files. The # parsing of If-Modified-Since could be removed. Removing it prematurely # will cause a re-download of all packages on all clients for 1 iteration # until they all have ETag values. # Reduce complexity of elif conditional below. # If an If-None-Match: ETag is supplied, don't worry about a # missing file modification date -- the ETag supplies everything needed. if etag_nomatch_str and not header_date_str: resource_expired = False else: resource_expired = handlers.IsClientResourceExpired( pkg_date, header_date_str) # Client supplied If-Match: etag, but that etag does not match current # etag. return 412. if (etag_match_str and pkg.pkgdata_sha256 and etag_match_str != pkg.pkgdata_sha256): self.response.set_status(412) # Client supplied no etag or If-No-Match: etag, and the etag did not # match, or the client's file is older than the mod time of this package. elif ((etag_nomatch_str and pkg.pkgdata_sha256 and etag_nomatch_str != pkg.pkgdata_sha256) or resource_expired): self.response.headers['Content-Disposition'] = str( 'attachment; filename=%s' % filename) # header date empty or package has changed, send blob with last-mod date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.headers['Last-Modified'] = pkg_date.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['X-Download-Size'] = str(pkg_size_bytes) self.send_blob(pkg.blobstore_key) else: # Client doesn't need to do anything, current version is OK based on # ETag and/or last modified date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.set_status(304)
def testPackageModifiedWherePackageDateNewer(self): """Tests IsClientResourceExpired() with matching header str date.""" header_dt_str = 'Mon, 01 Jan 1930 01:00:00 GMT' dt = datetime.datetime(2010, 10, 06, 03, 23, 34) # later date self.assertTrue(handlers.IsClientResourceExpired(dt, header_dt_str))
def testPackageModifiedMatchingDate(self): """Tests IsClientResourceExpired() with matching header str date.""" header_dt_str = 'Wed, 06 Oct 2010 03:23:34 GMT' dt = datetime.datetime(2010, 10, 06, 03, 23, 34) # same date self.assertFalse(handlers.IsClientResourceExpired(dt, header_dt_str))
def testPackageModifiedWithInvalidDate(self): """Tests IsClientResourceExpired() with non-parsable header str date.""" self.assertTrue( handlers.IsClientResourceExpired(None, 'date will not parse'))
def testIsClientResourceExpiredWithEmptyDate(self): """Tests IsClientResourceExpired() with empty header str date.""" self.assertTrue(handlers.IsClientResourceExpired(None, ''))