68 def render(self, request): |
69 def render(self, request): |
69 return HTTPResponse(twisted_request=request, |
70 return HTTPResponse(twisted_request=request, |
70 code=http.FORBIDDEN, |
71 code=http.FORBIDDEN, |
71 stream='Access forbidden') |
72 stream='Access forbidden') |
72 |
73 |
73 class File(static.File): |
74 |
74 """Prevent from listing directories""" |
75 class NoListingFile(static.File): |
75 def directoryListing(self): |
76 def directoryListing(self): |
76 return ForbiddenDirectoryLister() |
77 return ForbiddenDirectoryLister() |
77 |
78 |
78 |
79 |
79 class LongTimeExpiringFile(File): |
80 class DataLookupDirectory(NoListingFile): |
|
81 def __init__(self, config, path): |
|
82 NoListingFile.__init__(self, path) |
|
83 self.config = config |
|
84 self.here = path |
|
85 # backward-compatiblity: take care fckeditor may appears as |
|
86 # root directory or as a data subdirectory. XXX (adim) : why |
|
87 # that ? |
|
88 self.putChild('fckeditor', FCKEditorResource(self.config, '')) |
|
89 |
|
90 def getChild(self, path, request): |
|
91 if not path: |
|
92 return self.directoryListing() |
|
93 childpath = join(self.here, path) |
|
94 dirpath, rid = self.config.locate_resource(childpath) |
|
95 if dirpath is None: |
|
96 # resource not found |
|
97 return self.childNotFound |
|
98 filepath = os.path.join(dirpath, rid) |
|
99 if os.path.isdir(filepath): |
|
100 resource = DataLookupDirectory(self.config, childpath) |
|
101 # cache resource for this segment path to avoid recomputing |
|
102 # directory lookup |
|
103 self.putChild(path, resource) |
|
104 return resource |
|
105 else: |
|
106 return NoListingFile(filepath) |
|
107 |
|
108 |
|
109 class FCKEditorResource(NoListingFile): |
|
110 def __init__(self, config, path): |
|
111 NoListingFile.__init__(self, path) |
|
112 self.config = config |
|
113 |
|
114 def getChild(self, path, request): |
|
115 pre_path = request.path.split('/')[1:] |
|
116 if pre_path[0] == 'https': |
|
117 pre_path.pop(0) |
|
118 uiprops = self.config.https_uiprops |
|
119 else: |
|
120 uiprops = self.config.uiprops |
|
121 return static.File(osp.join(uiprops['FCKEDITOR_PATH'], path)) |
|
122 |
|
123 |
|
124 class LongTimeExpiringFile(DataLookupDirectory): |
80 """overrides static.File and sets a far future ``Expires`` date |
125 """overrides static.File and sets a far future ``Expires`` date |
81 on the resouce. |
126 on the resouce. |
82 |
127 |
83 versions handling is done by serving static files by different |
128 versions handling is done by serving static files by different |
84 URLs for each version. For instance:: |
129 URLs for each version. For instance:: |
92 # XXX: Don't provide additional resource information to error responses |
137 # XXX: Don't provide additional resource information to error responses |
93 # |
138 # |
94 # the HTTP RFC recommands not going further than 1 year ahead |
139 # the HTTP RFC recommands not going further than 1 year ahead |
95 expires = date.today() + timedelta(days=6*30) |
140 expires = date.today() + timedelta(days=6*30) |
96 request.setHeader('Expires', generateDateTime(mktime(expires.timetuple()))) |
141 request.setHeader('Expires', generateDateTime(mktime(expires.timetuple()))) |
97 return File.render(self, request) |
142 return DataLookupDirectory.render(self, request) |
98 |
143 |
99 |
144 |
100 class CubicWebRootResource(resource.Resource): |
145 class CubicWebRootResource(resource.Resource): |
101 def __init__(self, config, vreg=None): |
146 def __init__(self, config, vreg=None): |
|
147 resource.Resource.__init__(self) |
102 self.config = config |
148 self.config = config |
103 # instantiate publisher here and not in init_publisher to get some |
149 # instantiate publisher here and not in init_publisher to get some |
104 # checks done before daemonization (eg versions consistency) |
150 # checks done before daemonization (eg versions consistency) |
105 self.appli = CubicWebPublisher(config, vreg=vreg) |
151 self.appli = CubicWebPublisher(config, vreg=vreg) |
106 self.base_url = config['base-url'] |
152 self.base_url = config['base-url'] |
107 self.https_url = config['https-url'] |
153 self.https_url = config['https-url'] |
108 self.children = {} |
|
109 self.static_directories = set(('data%s' % config.instance_md5_version(), |
|
110 'data', 'static', 'fckeditor')) |
|
111 global MAX_POST_LENGTH |
154 global MAX_POST_LENGTH |
112 MAX_POST_LENGTH = config['max-post-length'] |
155 MAX_POST_LENGTH = config['max-post-length'] |
|
156 self.putChild('static', NoListingFile(config.static_directory)) |
|
157 self.putChild('fckeditor', FCKEditorResource(self.config, '')) |
|
158 self.putChild('data', DataLookupDirectory(self.config, '')) |
|
159 self.putChild('data%s' % config.instance_md5_version(), |
|
160 LongTimeExpiringFile(self.config, '')) |
113 |
161 |
114 def init_publisher(self): |
162 def init_publisher(self): |
115 config = self.config |
163 config = self.config |
116 # when we have an in-memory repository, clean unused sessions every XX |
164 # when we have an in-memory repository, clean unused sessions every XX |
117 # seconds and properly shutdown the server |
165 # seconds and properly shutdown the server |
150 except select.error: |
198 except select.error: |
151 return |
199 return |
152 |
200 |
153 def getChild(self, path, request): |
201 def getChild(self, path, request): |
154 """Indicate which resource to use to process down the URL's path""" |
202 """Indicate which resource to use to process down the URL's path""" |
155 pre_path = request.path.split('/')[1:] |
|
156 if pre_path[0] == 'https': |
|
157 pre_path.pop(0) |
|
158 uiprops = self.config.https_uiprops |
|
159 else: |
|
160 uiprops = self.config.uiprops |
|
161 directory = pre_path[0] |
|
162 # Anything in data/, static/, fckeditor/ and the generated versioned |
|
163 # data directory is treated as static files |
|
164 if directory in self.static_directories: |
|
165 # take care fckeditor may appears as root directory or as a data |
|
166 # subdirectory |
|
167 if directory == 'static': |
|
168 return File(self.config.static_directory) |
|
169 if directory == 'fckeditor': |
|
170 return File(uiprops['FCKEDITOR_PATH']) |
|
171 if directory != 'data': |
|
172 # versioned directory, use specific file with http cache |
|
173 # headers so their are cached for a very long time |
|
174 cls = LongTimeExpiringFile |
|
175 else: |
|
176 cls = File |
|
177 if path == 'fckeditor': |
|
178 return cls(uiprops['FCKEDITOR_PATH']) |
|
179 if path == directory: # recurse |
|
180 return self |
|
181 datadir, path = self.config.locate_resource(path) |
|
182 if datadir is None: |
|
183 return self # recurse |
|
184 self.debug('static file %s from %s', path, datadir) |
|
185 return cls(join(datadir, path)) |
|
186 # Otherwise we use this single resource |
|
187 return self |
203 return self |
188 |
204 |
189 def render(self, request): |
205 def render(self, request): |
190 """Render a page from the root resource""" |
206 """Render a page from the root resource""" |
191 # reload modified files in debug mode |
207 # reload modified files in debug mode |