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 appengine_wrappers import IsDevServer 11 from caching_file_system import CachingFileSystem 12 from caching_rietveld_patcher import CachingRietveldPatcher 13 from chained_compiled_file_system import ChainedCompiledFileSystem 14 from compiled_file_system import CompiledFileSystem 15 from host_file_system_creator import HostFileSystemCreator 16 from instance_servlet import InstanceServlet 17 from render_servlet import RenderServlet 18 from rietveld_patcher import RietveldPatcher, RietveldPatcherError 19 from object_store_creator import ObjectStoreCreator 20 from patched_file_system import PatchedFileSystem 21 from server_instance import ServerInstance 22 from servlet import Request, Response, Servlet 23 import svn_constants 24 import url_constants 25 26 class _PatchServletDelegate(RenderServlet.Delegate): 27 def __init__(self, issue, delegate): 28 self._issue = issue 29 self._delegate = delegate 30 31 def CreateServerInstance(self): 32 object_store_creator = ObjectStoreCreator(start_empty=False) 33 branch_utility = self._delegate.CreateBranchUtility(object_store_creator) 34 host_file_system_creator = self._delegate.CreateHostFileSystemCreator( 35 object_store_creator) 36 # offline=False because a patch can rely on files that are already in SVN 37 # repository but not yet pulled into data store by cron jobs (a typical 38 # example is to add documentation for an existing API). 39 base_file_system = CachingFileSystem( 40 host_file_system_creator.Create(offline=False), 41 object_store_creator) 42 base_compiled_fs_factory = CompiledFileSystem.Factory(base_file_system, 43 object_store_creator) 44 45 rietveld_patcher = CachingRietveldPatcher( 46 RietveldPatcher(svn_constants.EXTENSIONS_PATH, 47 self._issue, 48 AppEngineUrlFetcher(url_constants.CODEREVIEW_SERVER)), 49 object_store_creator) 50 patched_file_system = PatchedFileSystem(base_file_system, 51 rietveld_patcher) 52 patched_compiled_fs_factory = CompiledFileSystem.Factory( 53 patched_file_system, object_store_creator) 54 55 compiled_fs_factory = ChainedCompiledFileSystem.Factory( 56 [(patched_compiled_fs_factory, patched_file_system), 57 (base_compiled_fs_factory, base_file_system)]) 58 59 return ServerInstance(object_store_creator, 60 patched_file_system, 61 self._delegate.CreateAppSamplesFileSystem( 62 object_store_creator), 63 '/_patch/%s' % self._issue, 64 compiled_fs_factory, 65 branch_utility, 66 host_file_system_creator) 67 68 class PatchServlet(Servlet): 69 '''Servlet which renders patched docs. 70 ''' 71 def __init__(self, request, delegate=None): 72 self._request = request 73 self._delegate = delegate or InstanceServlet.Delegate() 74 75 def Get(self): 76 if (not IsDevServer() and 77 not fnmatch(urlparse(self._request.host).netloc, '*.appspot.com')): 78 # Only allow patches on appspot URLs; it doesn't matter if appspot.com is 79 # XSS'ed, but it matters for chrome.com. 80 redirect_host = 'https://chrome-apps-doc.appspot.com' 81 logging.info('Redirecting from XSS-able host %s to %s' % ( 82 self._request.host, redirect_host)) 83 return Response.Redirect( 84 '%s/_patch/%s' % (redirect_host, self._request.path)) 85 86 path_with_issue = self._request.path.lstrip('/') 87 if '/' in path_with_issue: 88 issue, path_without_issue = path_with_issue.split('/', 1) 89 else: 90 return Response.NotFound('Malformed URL. It should look like ' + 91 'https://developer.chrome.com/_patch/12345/extensions/...') 92 93 try: 94 response = RenderServlet( 95 Request(path_without_issue, 96 self._request.host, 97 self._request.headers), 98 _PatchServletDelegate(issue, self._delegate)).Get() 99 # Disable cache for patched content. 100 response.headers.pop('cache-control', None) 101 except RietveldPatcherError as e: 102 response = Response.NotFound(e.message, {'Content-Type': 'text/plain'}) 103 104 redirect_url, permanent = response.GetRedirect() 105 if redirect_url is not None: 106 response = Response.Redirect('/_patch/%s%s' % (issue, redirect_url), 107 permanent) 108 return response 109