def _process_request_body(environ, tiddler): """ Read request body to set tiddler content. If a serializer exists for the content type, use it, otherwise treat the content as binary or pseudo-binary tiddler. """ length, content_type = content_length_and_type(environ) content = read_request_body(environ, length) try: try: serialize_type = get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) # Short circuit de-serialization attempt to avoid # decoding content multiple times. if hasattr(serializer.serialization, 'as_tiddler'): serializer.object = tiddler try: serializer.from_string(content.decode('utf-8')) except TiddlerFormatError as exc: raise HTTP400('unable to put tiddler: %s' % exc) else: raise NoSerializationError() except NoSerializationError: tiddler.type = content_type if pseudo_binary(tiddler.type): tiddler.text = content.decode('utf-8') else: tiddler.text = content except UnicodeDecodeError as exc: raise HTTP400('unable to decode tiddler, utf-8 expected: %s' % exc)
def _get_tiddler_content(environ, tiddler): """ Extract the content of the tiddler, either straight up if the content is not considered text, or serialized if it is """ config = environ['tiddlyweb.config'] default_serializer = config['default_serializer'] default_serialize_type = config['serializers'][default_serializer][0] serialize_type, mime_type = web.get_serialize_type(environ) extension = environ.get('tiddlyweb.extension') if not renderable(tiddler, environ): if (serialize_type == default_serialize_type or mime_type.startswith(tiddler.type) or extension == 'html'): mime_type = tiddler.type content = tiddler.text return content, mime_type serializer = Serializer(serialize_type, environ) serializer.object = tiddler try: content = serializer.to_string() except (TiddlerFormatError, NoSerializationError), exc: raise HTTP415(exc)
def _get_tiddler_content(environ, tiddler): """ Extract the content of the tiddler, either straight up if the content is not considered text, or serialized if it is. """ config = environ['tiddlyweb.config'] default_serializer = config['default_serializer'] default_serialize_type = config['serializers'][default_serializer][0] serialize_type, mime_type = get_serialize_type(environ) extension = environ.get('tiddlyweb.extension') serialized = False # If this is a tiddler with a CANONICAL_URI_FIELD redirect # there unless we are requesting a json form if (CANONICAL_URI_FIELD in tiddler.fields and not CANONICAL_URI_PASS_TYPE in mime_type): raise HTTP302(tiddler.fields[CANONICAL_URI_FIELD]) if not renderable(tiddler, environ): if (serialize_type == default_serialize_type or mime_type.startswith(tiddler.type) or extension == 'html'): mime_type = tiddler.type content = tiddler.text return content, mime_type, serialized serializer = Serializer(serialize_type, environ) serializer.object = tiddler try: content = serializer.to_string() serialized = True except (TiddlerFormatError, NoSerializationError) as exc: raise HTTP415(exc) return content, mime_type, serialized
def send_entity(environ, start_response, entity): """ Send a bag or recipe out HTTP, first serializing to the correct type. """ username = environ['tiddlyweb.usersign']['name'] try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) etag_string = '"%s"' % (sha(_entity_etag(entity) + encode_name(username) + encode_name(mime_type)).hexdigest()) start_response("200 OK", [('Content-Type', mime_type), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def list(environ, start_response): """ List all the bags that the current user can read. """ store = environ["tiddlyweb.store"] bags = store.list_bags() kept_bags = [] for bag in bags: try: bag.skinny = True bag = store.get(bag) bag.policy.allows(environ["tiddlyweb.usersign"], "read") kept_bags.append(bag) except (UserRequiredError, ForbiddenError): pass try: serialize_type, mime_type = web.get_serialize_type(environ) serializer = Serializer(serialize_type, environ) content = serializer.list_bags(kept_bags) except NoSerializationError: raise HTTP415("Content type not supported: %s" % mime_type) start_response("200 OK", [("Content-Type", mime_type)]) return [content]
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if policy allows. """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ['tiddlyweb.store'] length = environ['CONTENT_LENGTH'] usersign = environ['tiddlyweb.usersign'] try: bag = store.get(bag) bag.policy.allows(usersign, 'manage') except NoBagError: create_policy_check(environ, 'bag', usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = environ['wsgi.input'].read(int(length)) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except BagFormatError, exc: raise HTTP400('unable to put bag: %s' % exc)
def send_entity(environ, start_response, entity): """ Send a bag or recipe out HTTP, first serializing to the correct type. If the incoming etag matches, raise 304. """ etag_string = entity_etag(environ, entity) incoming_etag = environ.get('HTTP_IF_NONE_MATCH', None) if incoming_etag: if incoming_etag == etag_string: raise HTTP304(incoming_etag) try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_string = http_date_from_timestamp(tiddlers.modified) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s"' % (tiddlers.hexdigest(), sha('%s:%s' % (username.encode('utf-8'), mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = check_incoming_etag(environ, etag_string) if not incoming_etag: # only check last modified when no etag check_last_modified(environ, last_modified_string) return last_modified, etag
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if policy allows. """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ['tiddlyweb.store'] length, _ = web.content_length_and_type(environ) usersign = environ['tiddlyweb.usersign'] try: bag = store.get(bag) bag.policy.allows(usersign, 'manage') except NoBagError: create_policy_check(environ, 'bag', usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = web.read_request_body(environ, length) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except BagFormatError, exc: raise HTTP400('unable to put bag: %s' % exc)
def send_entity(environ, start_response, entity): """ Send a bag or recipe out HTTP, first serializing to the correct type. """ username = environ['tiddlyweb.usersign']['name'] try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) etag_string = '"%s"' % (sha( _entity_etag(entity) + encode_name(username) + encode_name(mime_type)).hexdigest()) start_response("200 OK", [('Content-Type', mime_type), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def _get_tiddler_content(environ, tiddler): """ Extract the content of the tiddler, either straight up if the content is not considered text, or serialized if it is. """ config = environ['tiddlyweb.config'] default_serializer = config['default_serializer'] default_serialize_type = config['serializers'][default_serializer][0] serialize_type, mime_type, accept = get_serialize_type(environ, accept_type=True) extension = environ.get('tiddlyweb.extension') serialized = False # If this is a tiddler with a CANONICAL_URI_FIELD redirect # there unless we are requesting a json form if (CANONICAL_URI_FIELD in tiddler.fields and CANONICAL_URI_PASS_TYPE not in mime_type): raise HTTP302(tiddler.fields[CANONICAL_URI_FIELD]) if not renderable(tiddler, environ): if (serialize_type == default_serialize_type or accept.startswith(tiddler.type) or extension == 'html'): mime_type = tiddler.type content = tiddler.text return content, mime_type, serialized serializer = Serializer(serialize_type, environ) serializer.object = tiddler try: content = serializer.to_string() serialized = True except (TiddlerFormatError, NoSerializationError) as exc: raise HTTP415(exc) return content, mime_type, serialized
def get(environ, start_response): """ Get a representation in some serialization of a bag (the bag itself not the tiddlers within). """ bag_name = _determine_bag_name(environ) bag_name = web.handle_extension(environ, bag_name) bag = _get_bag(environ, bag_name, True) bag.policy.allows(environ['tiddlyweb.usersign'], 'manage') try: serialize_type, mime_type = web.get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = bag content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type not supported: %s' % mime_type) start_response("200 Ok", [('Content-Type', mime_type), ('Vary', 'Accept')]) return [content]
def _generate_response(content, environ, start_response): serialize_type, mime_type = web.get_serialize_type(environ) # XXX: not suitable here!? cache_header = ("Cache-Control", "no-cache") # ensure accessing latest HEAD revision content_header = ("Content-Type", mime_type) # XXX: should be determined by diff format response = [cache_header, content_header] start_response("200 OK", response) return [content]
def send_entity(environ, start_response, entity): """ Send a :py:class:`bag <tiddlyweb.model.bag.Bag>` or :py:class:`recipe <tiddlyweb.model.recipe.Recipe>` out over HTTP, first :py:class:`serializing <tiddlyweb.serializer.Serializer>` to the correct type. If an incoming ``Etag`` validates, raise a ``304`` response. """ etag_string = entity_etag(environ, entity) check_incoming_etag(environ, etag_string) try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def send_tiddlers(environ, start_response, bag): """ Output the tiddlers contained in the provided bag in a Negotiated representation. Often, but not always, a wiki. """ last_modified = None etag = None bags_tiddlers = bag.list_tiddlers() download = environ['tiddlyweb.query'].get('download', [None])[0] if bags_tiddlers: last_modified, etag = _validate_tiddler_list(environ, bags_tiddlers) else: raise HTTP404('No tiddlers in container') serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) content_header = ('Content-Type', mime_type) cache_header = ('Cache-Control', 'no-cache') response = [content_header, cache_header] if serialize_type == 'wiki': if download: response.append(('Content-Disposition', 'attachment; filename="%s"' % download)) if last_modified: response.append(last_modified) if etag: response.append(etag) output = serializer.list_tiddlers(bag) start_response("200 OK", response) return [output]
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_string = http_date_from_timestamp(tiddlers.modified) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s"' % (tiddlers.hexdigest(), sha('%s:%s' % (username, mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = check_incoming_etag(environ, etag_string, last_modified=last_modified_string) if not incoming_etag: # only check last modified when no etag check_last_modified(environ, last_modified_string, etag=etag_string) return last_modified, etag
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if policy allows. """ bag_name = _determine_bag_name(environ) bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ["tiddlyweb.store"] length = environ["CONTENT_LENGTH"] usersign = environ["tiddlyweb.usersign"] try: bag = store.get(bag) bag.policy.allows(usersign, "manage") except NoBagError: create_policy_check(environ, "bag", usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = environ["wsgi.input"].read(int(length)) serializer.from_string(content.decode("utf-8")) bag.policy.owner = usersign["name"] _validate_bag(environ, bag) store.put(bag) except BagFormatError, exc: raise HTTP400("unable to put bag: %s" % exc)
def list_bags(environ, start_response): """ List all the bags that the current user can read. """ store = environ["tiddlyweb.store"] serialize_type, mime_type = web.get_serialize_type(environ) serializer = Serializer(serialize_type, environ) return list_entities(environ, start_response, mime_type, store.list_bags, serializer.list_bags)
def list_recipes(environ, start_response): """ Get a list of all recipes the current user can read. """ store = environ['tiddlyweb.store'] serialize_type, mime_type = web.get_serialize_type(environ) serializer = Serializer(serialize_type, environ) return list_entities(environ, start_response, mime_type, store.list_recipes, serializer.list_recipes)
def _is_html_out(environ, status): if environ['REQUEST_METHOD'] == 'GET': try: _, mime_type = get_serialize_type(environ) return (not status.startswith('3') and 'html' in mime_type) except HTTP415: return False else: return False
def replacement_root_handler(environ, start_response): """ Check if we want HAL, if so send it, otherwise do the default. """ _, mime_type = get_serialize_type(environ) if 'application/hal+json' in mime_type: return _hal_root(environ, start_response) else: return ORIGINAL_ROOT_HANDLER(environ, start_response)
def get_space_tiddlers(environ, start_response): """ Get the tiddlers that make up the current space in whatever the reqeusted representation is. Choose recipe based on membership status. """ _setup_friendly_environ(environ) serializer, _ = get_serialize_type(environ) _extra_query_update(environ) return get_tiddlers(environ, start_response)
def send_tiddlers(environ, start_response, tiddlers=None): """ Output the :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` contained in the provided :py:class:`Tiddlers collection <tiddlyweb.model.collections.Tiddlers>` in a :py:mod:`Negotiated <tiddlyweb.web.negotiate>` representation. """ download = environ['tiddlyweb.query'].get('download', [None])[0] filters = environ['tiddlyweb.filters'] store = environ['tiddlyweb.store'] if tiddlers.store is None and not filters: LOGGER.warn('Incoming tiddlers no store set %s', inspect.stack()[1]) if filters: candidate_tiddlers = _filter_tiddlers(filters, store, tiddlers) else: candidate_tiddlers = tiddlers last_modified, etag = _validate_tiddler_list(environ, candidate_tiddlers) serialize_type, mime_type = get_serialize_type(environ, collection=True) response = [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('Vary', 'Accept')] if download: response.append(('Content-Disposition', 'attachment; filename="%s"' % encode_name(download))) if last_modified: response.append(last_modified) if etag: response.append(etag) try: serializer = Serializer(serialize_type, environ) output = serializer.list_tiddlers(candidate_tiddlers) except NoSerializationError as exc: raise HTTP415('Content type not supported: %s:%s, %s' % (serialize_type, mime_type, exc)) except FilterError as exc: # serializations may filter tildders raise HTTP400('malformed filter or tiddler during filtering: %s' % exc) start_response("200 OK", response) if isinstance(output, basestring): return [output] else: return output
def serve_space(environ, start_response, http_host): """ Serve a space determined from the current virtual host and user. The user determines whether the recipe uses is public or private. """ space_name = determine_space(environ, http_host) recipe_name = determine_space_recipe(environ, space_name) environ['wsgiorg.routing_args'][1]['recipe_name'] = recipe_name.encode( 'UTF-8') _, mime_type = get_serialize_type(environ) if 'text/html' in mime_type: environ['tiddlyweb.type'] = 'text/x-tiddlywiki' return get_tiddlers(environ, start_response)
def _tiddler_etag(environ, tiddler): """ Calculate the ETAG of a tiddler, based on bag name, tiddler title and revision. """ try: mime_type = web.get_serialize_type(environ)[1] mime_type = mime_type.split(';', 1)[0].strip() except (TypeError, AttributeError): mime_type = tiddler.type or '' username = environ.get('tiddlyweb.usersign', {}).get('name', '') digest = sha('%s:%s' % (username, mime_type)).hexdigest() return str('"%s/%s/%s;%s"' % (web.encode_name(tiddler.bag), web.encode_name(tiddler.title), tiddler.revision, digest))
def put(environ, start_response): """ Handle ``PUT`` on a single recipe URI. Put a :py:class:`recipe <tiddlyweb.model.recipe.Recipe>` to the server, meaning the description, policy and recipe list of the recipe, if :py:class:`policy <tiddlyweb.model.policy.Policy>` allows. """ recipe_name = web.get_route_value(environ, 'recipe_name') recipe_name = web.handle_extension(environ, recipe_name) recipe = Recipe(recipe_name) store = environ['tiddlyweb.store'] length, _ = web.content_length_and_type(environ) usersign = environ['tiddlyweb.usersign'] try: recipe = store.get(recipe) recipe.policy.allows(usersign, 'manage') except NoRecipeError: create_policy_check(environ, 'recipe', usersign) try: serialize_type = web.get_serialize_type(environ)[0] except TypeError: raise HTTP400('Content-type header required') try: serializer = Serializer(serialize_type, environ) serializer.object = recipe content = web.read_request_body(environ, length) serializer.from_string(content.decode('utf-8')) recipe.policy.owner = usersign['name'] _validate_recipe(environ, recipe) store.put(recipe) except RecipeFormatError as exc: raise HTTP400('unable to put recipe: %s' % exc) except TypeError as exc: raise HTTP400('malformed input: %s' % exc) except NoSerializationError: raise HTTP415('Content type %s not supported' % serialize_type) start_response("204 No Content", [('Location', web.recipe_url(environ, recipe))]) return []
def get_space_tiddlers(environ, start_response): """ Get the tiddlers that make up the current space in whatever the reqeusted representation is. Choose recipe based on membership status. """ space_name = _setup_friendly_environ(environ) serializer, _ = get_serialize_type(environ) # If we are a wiki read ServerSettings, but ignore index if ('betaserialization' in serializer or 'betalazyserialization' in serializer): _, lazy = update_space_settings(environ, space_name) if lazy: environ['tiddlyweb.type'] = 'text/x-ltiddlywiki' return get_tiddlers(environ, start_response)
def _tiddler_etag(environ, tiddler): """ Calculate the ETAG of a tiddler, based on bag name, tiddler title and revision. """ try: mime_type = web.get_serialize_type(environ)[1] mime_type = mime_type.split(';', 1)[0].strip() except (TypeError, AttributeError): mime_type = tiddler.type or '' username = environ.get('tiddlyweb.usersign', {}).get('name', '') digest = sha('%s:%s' % (username, mime_type)).hexdigest() return str('"%s/%s/%s;%s"' % (web.encode_name(tiddler.bag), web.encode_name( tiddler.title), tiddler.revision, digest))
def _validate_tiddler_list(environ, bag): """ Calculate the Last modified and ETag for the tiddlers in bag. If the ETag matches an incoming If-None-Match, then raise a 304 and don't send the tiddler content. If the modified string in an If-Modified-Since is newer than the last-modified on the tiddlers, raise 304. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_number = _last_modified_tiddler(bag) last_modified = None if last_modified_number: last_modified_string = http_date_from_timestamp(last_modified_number) last_modified = ("Last-Modified", last_modified_string) username = environ.get("tiddlyweb.usersign", {}).get("name", "") try: serialize_type, mime_type = get_serialize_type(environ) mime_type = mime_type.split(";", 1)[0].strip() except TypeError: mime_type = "" etag_string = '"%s:%s;%s"' % ( _sha_tiddler_titles(bag), last_modified_number, sha("%s:%s" % (username, mime_type)).hexdigest(), ) etag = ("Etag", etag_string) incoming_etag = environ.get("HTTP_IF_NONE_MATCH", None) if incoming_etag: if incoming_etag == etag_string: raise HTTP304(incoming_etag) else: incoming_modified = environ.get("HTTP_IF_MODIFIED_SINCE", None) if incoming_modified and ( datetime_from_http_date(incoming_modified) >= datetime_from_http_date(last_modified_string) ): raise HTTP304("") return last_modified, etag
def profile(environ, start_response): """ Choose between an atom or html profile. """ http_host, host_url = determine_host(environ) if http_host != host_url: # Or should it be a 302? raise HTTP404('No profiles at this host: %s' % http_host) _, mime_type = get_serialize_type(environ) if 'atom' in mime_type: return atom_profile(environ, start_response) elif 'html' in mime_type: return html_profile(environ, start_response) else: raise HTTP415('Atom and HTML only')
def _check_etag(self, environ, url): if '/bags/' in url: try: etag = environ['HTTP_IF_NONE_MATCH'] content_type = get_serialize_type(environ)[1] content_type = content_type.split(';', 1)[0] username = environ['tiddlyweb.usersign']['name'] key = '%s:%s:%s' % (url, content_type, username) found_etag = ETAGS.get(str(key)) logging.debug('checking etag: %s:%s:%s' % (etag, key, found_etag)) if etag == found_etag: logging.debug('etag match in cache: %s:%s' % (etag, key)) raise HTTP304(found_etag) except KeyError: pass # no etag just carry on with normal processing return # etags didn't match we carry on
def _put_tiddler(environ, start_response, tiddler): """ The guts of putting a tiddler into the store. """ store = environ['tiddlyweb.store'] length = environ['CONTENT_LENGTH'] content_type = environ['tiddlyweb.type'] if content_type != 'text/plain' and content_type != 'application/json': tiddler.type = content_type try: bag = Bag(tiddler.bag) try: try: revision = store.list_tiddler_revisions(tiddler)[0] except StoreMethodNotImplemented: revision = 1 tiddler.revision = revision # These both next will raise exceptions if # the contraints don't match. _check_bag_constraint(environ, bag, 'write') _validate_tiddler(environ, tiddler) except NoTiddlerError: _check_bag_constraint(environ, bag, 'create') content = environ['wsgi.input'].read(int(length)) if not tiddler.type: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = tiddler serializer.from_string(content.decode('UTF-8')) else: tiddler.text = content user = environ['tiddlyweb.usersign']['name'] if not user == 'GUEST': tiddler.modifier = user store.put(tiddler) except NoBagError, exc: raise HTTP409("Unable to put tiddler, %s. There is no bag named: " \ "%s (%s). Create the bag." % (tiddler.title, tiddler.bag, exc))
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if policy allows. """ bag_name = _determine_bag_name(environ) bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ['tiddlyweb.store'] length = environ['CONTENT_LENGTH'] usersign = environ['tiddlyweb.usersign'] try: bag.skinny = True bag = store.get(bag) bag.policy.allows(usersign, 'manage') try: delattr(bag, 'skinny') except AttributeError: pass except NoBagError: create_policy_check(environ, 'bag', usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = environ['wsgi.input'].read(int(length)) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except TypeError: raise HTTP400('Content-type header required') except NoSerializationError: raise HTTP415('Content type not supported: %s' % serialize_type) start_response("204 No Content", [('Location', web.bag_url(environ, bag))]) return []
def put(environ, start_response): """ Handle ``PUT`` on a single bag URI. Put a :py:class:`bag <tiddlyweb.model.bag.Bag>` to the server, meaning the description and policy of the bag, if :py:class:`policy <tiddlyweb.model.policy.Policy>` allows. """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ['tiddlyweb.store'] length, _ = web.content_length_and_type(environ) usersign = environ['tiddlyweb.usersign'] try: bag = store.get(bag) bag.policy.allows(usersign, 'manage') except NoBagError: create_policy_check(environ, 'bag', usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = web.read_request_body(environ, length) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except BagFormatError as exc: raise HTTP400('unable to put bag: %s' % exc) except TypeError: raise HTTP400('Content-type header required') except NoSerializationError: raise HTTP415('Content type not supported: %s' % serialize_type) start_response("204 No Content", [('Location', web.bag_url(environ, bag))]) return []
def list_entities(environ, start_response, method_name, store_list=None, serializer_list=None): """ Get a list of all the bags or recipes the current user can read. """ store = environ['tiddlyweb.store'] serialize_type, mime_type = get_serialize_type(environ, collection=True) serializer = Serializer(serialize_type, environ) filters = environ['tiddlyweb.filters'] if method_name: if not store_list: store_list = getattr(store, method_name) if not serializer_list: serializer_list = getattr(serializer, method_name) try: kept_entities = _filter_readable(environ, store_list(), filters) except FilterError, exc: raise HTTP400(exc)
def list_entities(environ, start_response, method_name, store_list=None, serializer_list=None): """ Get an optionally :py:mod:`filtered <tiddlyweb.filters>` list of all the :py:class:`bags <tiddlyweb.model.bag.Bag>` or :py:class:`recipes <tiddlyweb.model.recipe.Recipe>` the current ``tiddlyweb.usersign`` can read. """ store = environ['tiddlyweb.store'] serialize_type, mime_type = get_serialize_type(environ, collection=True) serializer = Serializer(serialize_type, environ) filters = environ['tiddlyweb.filters'] if method_name: if not store_list: store_list = getattr(store, method_name) if not serializer_list: serializer_list = getattr(serializer, method_name) try: kept_entities = _filter_readable(environ, store_list(), filters) except FilterError as exc: raise HTTP400(exc) etag_string = '"%s:%s"' % (kept_entities.hexdigest(), sha(mime_type).hexdigest()) check_incoming_etag(environ, etag_string) try: output = serializer_list(kept_entities) except NoSerializationError: raise HTTP415('Content type not supported: %s' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Vary', 'Accept'), ('Cache-Control', 'no-cache'), ('Etag', etag_string)]) if isinstance(output, basestring): return [output] else: return output
def send_tiddlers(environ, start_response, tiddlers=None): """ Output the tiddlers contained in the provided Tiddlers collection in a Negotiated representation. Often, but not always, a wiki. """ download = environ['tiddlyweb.query'].get('download', [None])[0] filters = environ['tiddlyweb.filters'] store = environ['tiddlyweb.store'] if tiddlers.store is None and not filters: LOGGER.warn('Incoming tiddlers no store set %s', inspect.stack()[1]) if filters: candidate_tiddlers = _filter_tiddlers(filters, store, tiddlers) else: candidate_tiddlers = tiddlers last_modified, etag = _validate_tiddler_list(environ, candidate_tiddlers) serialize_type, mime_type = get_serialize_type(environ, collection=True) response = [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('Vary', 'Accept')] if download: response.append(('Content-Disposition', 'attachment; filename="%s"' % download.encode('utf-8'))) if last_modified: response.append(last_modified) if etag: response.append(etag) try: serializer = Serializer(serialize_type, environ) output = serializer.list_tiddlers(candidate_tiddlers) except NoSerializationError, exc: raise HTTP415('Content type not supported: %s:%s, %s' % (serialize_type, mime_type, exc))
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_number = tiddlers.modified last_modified_string = http_date_from_timestamp(last_modified_number) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s;%s"' % (tiddlers.hexdigest(), str(last_modified_number), sha('%s:%s' % (username, mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = environ.get('HTTP_IF_NONE_MATCH', None) if incoming_etag: if incoming_etag == etag_string: raise HTTP304(incoming_etag) else: incoming_modified = environ.get('HTTP_IF_MODIFIED_SINCE', None) if incoming_modified and \ (datetime_from_http_date(incoming_modified) >= \ datetime_from_http_date(last_modified_string)): raise HTTP304('') return last_modified, etag
def put(environ, start_response): """ Put a new recipe to the server. """ recipe_name = environ['wsgiorg.routing_args'][1]['recipe_name'] recipe_name = urllib.unquote(recipe_name) recipe_name = unicode(recipe_name, 'utf-8') recipe_name = web.handle_extension(environ, recipe_name) recipe = Recipe(recipe_name) store = environ['tiddlyweb.store'] length = environ['CONTENT_LENGTH'] usersign = environ['tiddlyweb.usersign'] try: recipe = store.get(recipe) recipe.policy.allows(usersign, 'manage') except NoRecipeError: create_policy_check(environ, 'recipe', usersign) try: serialize_type = web.get_serialize_type(environ)[0] except TypeError: raise HTTP400('Content-type header required') try: serializer = Serializer(serialize_type, environ) serializer.object = recipe content = environ['wsgi.input'].read(int(length)) serializer.from_string(content.decode('utf-8')) recipe.policy.owner = usersign['name'] _validate_recipe(environ, recipe) store.put(recipe) except RecipeFormatError, exc: raise HTTP400('unable to put recipe: %s' % exc)
def _handle_core_request(self, environ, req_uri, start_response): """ Override a core request, adding filters or sending 404s where necessary to limit the view of entities. filtering can be disabled with a custom HTTP header X-ControlView set to false """ http_host, host_url = determine_host(environ) disable_ControlView = environ.get('HTTP_X_CONTROLVIEW') == 'false' if http_host != host_url and not disable_ControlView: space_name = determine_space(environ, http_host) if space_name == None: return None recipe_name = determine_space_recipe(environ, space_name) store = environ['tiddlyweb.store'] try: recipe = store.get(Recipe(recipe_name)) except NoRecipeError, exc: raise HTTP404('No recipe for space: %s', exc) space = Space(space_name) template = recipe_template(environ) bags = space.extra_bags() for bag, _ in recipe.get_recipe(template): bags.append(bag) bags.extend(ADMIN_BAGS) search_string = None if req_uri.startswith('/recipes') and req_uri.count('/') == 1: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) if recipe_name == space.private_recipe(): recipes = space.list_recipes() else: recipes = [space.public_recipe()] def lister(): for recipe in recipes: yield Recipe(recipe) return list_entities(environ, start_response, mime_type, lister, serializer.list_recipes) elif req_uri.startswith('/bags') and req_uri.count('/') == 1: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) def lister(): for bag in bags: yield Bag(bag) return list_entities(environ, start_response, mime_type, lister, serializer.list_bags) elif req_uri.startswith('/search') and req_uri.count('/') == 1: search_string = ' OR '.join(['bag:%s' % bag for bag in bags]) else: entity_name = urllib.unquote( req_uri.split('/')[2]).decode('utf-8') if '/recipes/' in req_uri: valid_recipes = space.list_recipes() if entity_name not in valid_recipes: raise HTTP404( 'recipe %s not found due to ControlView' % entity_name) else: if entity_name not in bags: raise HTTP404('bag %s not found due to ControlView' % entity_name) if search_string: search_query = environ['tiddlyweb.query'].get('q', [''])[0] environ['tiddlyweb.query.original'] = search_query if search_query: search_query = '%s AND (%s)' % (search_query, search_string) environ['tiddlyweb.query']['q'][0] = search_query else: search_query = '(%s)' % search_string environ['tiddlyweb.query']['q'] = [search_query]
candidate_tiddlers = control.get_tiddlers_from_recipe(recipe, environ) except NoBagError, exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc)) tiddlers_to_send = Tiddlers() for tiddler in candidate_tiddlers: if not tiddler.store: tiddler = store.get(tiddler) if tiddler.bag not in [recipe_bag for recipe_bag, _ in Space.CORE_RECIPE]: if 'systemConfig' in tiddler.tags: tiddler.tags.append('systemConfigDisable') tiddler.recipe = recipe.name tiddlers_to_send.add(tiddler) _, mime_type = get_serialize_type(environ) if 'text/html' in mime_type or 'x-www-form' in environ['tiddlyweb.type']: environ['tiddlyweb.type'] = 'text/x-tiddlywiki' return send_tiddlers(environ, start_response, tiddlers=tiddlers_to_send) def _delete_duplicates(environ, titles, recipe_name, space_name): store = environ['tiddlyweb.store'] try: recipe = store.get(Recipe(recipe_name)) template = control.recipe_template(environ) recipe_list = recipe.get_recipe(template) space_bags = [bag for bag, _ in recipe_list if bag.startswith('%s_' % space_name)] for title in titles: for bag in space_bags:
# We have to deserialize the tiddler here so that # PUTs to recipes are aware of values like tags when # doing filter checks. if environ['REQUEST_METHOD'] == 'PUT': length, content_type = _length_and_type(environ) content = environ['wsgi.input'].read(int(length)) try: try: # XXX HACK! We don't want to decode content unless # the serializer has a as_tiddler. We should be able # to just rely on NoSerializationError, but we need # to call the method to do that, and to call the method we # need to decode the string... serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = tiddler try: serializer.from_string(content.decode('utf-8')) except TiddlerFormatError, exc: raise HTTP400('unable to put tiddler: %s' % exc) except NoSerializationError: tiddler.type = content_type if pseudo_binary(tiddler.type): tiddler.text = content.decode('utf-8') else: tiddler.text = content except UnicodeDecodeError, exc: raise HTTP400('unable to decode tiddler, utf-8 expected: %s', exc)