1 # Copyright 2013 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 from fnmatch import fnmatch 6 import logging 7 from urlparse import urlparse 8 9 from appengine_url_fetcher import AppEngineUrlFetcher 10 from caching_rietveld_patcher import CachingRietveldPatcher 11 from chained_compiled_file_system import ChainedCompiledFileSystem 12 from environment import IsDevServer 13 from extensions_paths import CONTENT_PROVIDERS 14 from instance_servlet import InstanceServlet 15 from render_servlet import RenderServlet 16 from rietveld_patcher import RietveldPatcher, RietveldPatcherError 17 from object_store_creator import ObjectStoreCreator 18 from patched_file_system import PatchedFileSystem 19 from server_instance import ServerInstance 20 from servlet import Request, Response, Servlet 21 import url_constants 22 23 24 class _PatchServletDelegate(RenderServlet.Delegate): 25 def __init__(self, issue, delegate): 26 self._issue = issue 27 self._delegate = delegate 28 29 def CreateServerInstance(self): 30 # start_empty=False because a patch can rely on files that are already in 31 # SVN repository but not yet pulled into data store by cron jobs (a typical 32 # example is to add documentation for an existing API). 33 object_store_creator = ObjectStoreCreator(start_empty=False) 34 35 unpatched_file_system = self._delegate.CreateHostFileSystemProvider( 36 object_store_creator).GetTrunk() 37 38 rietveld_patcher = CachingRietveldPatcher( 39 RietveldPatcher(self._issue, 40 AppEngineUrlFetcher(url_constants.CODEREVIEW_SERVER)), 41 object_store_creator) 42 43 patched_file_system = PatchedFileSystem(unpatched_file_system, 44 rietveld_patcher) 45 46 patched_host_file_system_provider = ( 47 self._delegate.CreateHostFileSystemProvider( 48 object_store_creator, 49 # The patched file system needs to be online otherwise it'd be 50 # impossible to add files in the patches. 51 offline=False, 52 # The trunk file system for this creator should be the patched one. 53 default_trunk_instance=patched_file_system)) 54 55 combined_compiled_fs_factory = ChainedCompiledFileSystem.Factory( 56 [unpatched_file_system], object_store_creator) 57 58 branch_utility = self._delegate.CreateBranchUtility(object_store_creator) 59 60 server_instance = ServerInstance( 61 object_store_creator, 62 combined_compiled_fs_factory, 63 branch_utility, 64 patched_host_file_system_provider, 65 self._delegate.CreateGithubFileSystemProvider(object_store_creator), 66 base_path='/_patch/%s/' % self._issue) 67 68 # HACK: if content_providers.json changes in this patch then the cron needs 69 # to be re-run to pull in the new configuration. 70 _, _, modified = rietveld_patcher.GetPatchedFiles() 71 if CONTENT_PROVIDERS in modified: 72 server_instance.content_providers.Cron().Get() 73 74 return server_instance 75 76 class PatchServlet(Servlet): 77 '''Servlet which renders patched docs. 78 ''' 79 def __init__(self, request, delegate=None): 80 self._request = request 81 self._delegate = delegate or InstanceServlet.Delegate() 82 83 def Get(self): 84 if (not IsDevServer() and 85 not fnmatch(urlparse(self._request.host).netloc, '*.appspot.com')): 86 # Only allow patches on appspot URLs; it doesn't matter if appspot.com is 87 # XSS'ed, but it matters for chrome.com. 88 redirect_host = 'https://chrome-apps-doc.appspot.com' 89 logging.info('Redirecting from XSS-able host %s to %s' % ( 90 self._request.host, redirect_host)) 91 return Response.Redirect( 92 '%s/_patch/%s' % (redirect_host, self._request.path)) 93 94 path_with_issue = self._request.path.lstrip('/') 95 if '/' in path_with_issue: 96 issue, path_without_issue = path_with_issue.split('/', 1) 97 else: 98 return Response.NotFound('Malformed URL. It should look like ' + 99 'https://developer.chrome.com/_patch/12345/extensions/...') 100 101 try: 102 response = RenderServlet( 103 Request(path_without_issue, 104 self._request.host, 105 self._request.headers), 106 _PatchServletDelegate(issue, self._delegate)).Get() 107 # Disable cache for patched content. 108 response.headers.pop('cache-control', None) 109 except RietveldPatcherError as e: 110 response = Response.NotFound(e.message, {'Content-Type': 'text/plain'}) 111 112 redirect_url, permanent = response.GetRedirect() 113 if redirect_url is not None: 114 response = Response.Redirect('/_patch/%s%s' % (issue, redirect_url), 115 permanent) 116 return response 117