def _post_tiddler_revisions(environ, start_response, tiddler): """ We have a list of revisions, put them in a new place. """ content_type = environ['tiddlyweb.type'] if content_type != 'application/json': raise HTTP415('application/json required') # we need a matching etag in order to be able to do # this operation. This will raise exception if there # isn't a valid etag. _require_valid_etag_for_write(environ, tiddler) bag = Bag(tiddler.bag) # both create and write required for this action _check_bag_constraint(environ, bag, 'create') _check_bag_constraint(environ, bag, 'write') length = environ['CONTENT_LENGTH'] content = environ['wsgi.input'].read(int(length)) _store_tiddler_revisions(environ, content, tiddler) response = [('Location', web.tiddler_url(environ, tiddler))] start_response("204 No Content", response) return []
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 put_user(environ, start_response): """ Allow a user or an admin to set the password for a user at /users/{usersign}. A non-admin can only set their own password. The sent data is a JSON dict with at least the key 'password' with a value of whatever the password should be. Users of this method should take not that that password is being sent in the clear over what is likely an unencrypted network. """ store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign'] target_user = environ['wsgiorg.routing_args'][1]['usersign'] target_user = urllib.unquote(target_user) target_user = unicode(target_user, 'utf-8') if not ('ADMIN' in current_user['roles'] or current_user['name'] == target_user): raise HTTP403('Incorrect User') content_type = environ['tiddlyweb.type'] length = environ['CONTENT_LENGTH'] if content_type != 'application/json': raise HTTP415('application/json required') content = environ['wsgi.input'].read(int(length)) try: user_info = simplejson.loads(content) except ValueError, exc: raise HTTP400('Invalid JSON, %s' % exc)
def get_serialize_type(environ): """ Look in the environ to determine which serializer we should use for this request. """ config = environ['tiddlyweb.config'] accept = environ.get('tiddlyweb.type')[:] ext = environ.get('tiddlyweb.extension') serializers = config['serializers'] serialize_type, mime_type = None, None if type(accept) == str: accept = [accept] while len(accept) and serialize_type == None: candidate_type = accept.pop(0) try: serialize_type, mime_type = serializers[candidate_type] except KeyError: pass if not serialize_type: if ext: raise HTTP415('%s type unsupported' % ext) # If we are a PUT and we haven't found a serializer, don't # state a default as that makes no sense. if environ['REQUEST_METHOD'] == 'GET': default_serializer = config['default_serializer'] serialize_type, mime_type = serializers[default_serializer] return serialize_type, mime_type
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 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 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 post_user(environ, start_response): """ Create a new user through a JSON POST to /users. If the not JSON, return 415. If users exists, return 409. The JSON should be a dict with two keys: 'username' and 'password'. Future iterations of this code will take additional keys and save them as fields to be used with the tiddlywebplugins.magicuser extractor. """ content_type = environ['tiddlyweb.type'] if content_type != 'application/json': raise HTTP415('application/json required') length = environ['CONTENT_LENGTH'] content = environ['wsgi.input'].read(int(length)) store = environ['tiddlyweb.store'] try: user_info = simplejson.loads(content) except ValueError, exc: raise HTTP400('Invalid JSON, %s' % exc)
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) except TypeError, 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 _validate_recipe(environ, recipe): """ Unless recipe is valid raise a 409 with the reason why. """ try: validate_recipe(recipe, environ) except InvalidBagError, exc: raise HTTP409('Recipe content is invalid: %s' % exc)
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) 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 _validate_bag(environ, bag): """ Unless bag is valid raise a 409 with the reason why. """ try: validate_bag(bag, environ) except InvalidBagError, exc: raise HTTP409('Bag content is invalid: %s' % exc)
username = environ['tiddlyweb.usersign']['name'] try: kept_entities = _filter_readable(environ, store_list(), filters) except FilterError, exc: raise HTTP400(exc) etag_string = '"%s:%s"' % (kept_entities.hexdigest(), sha('%s:%s' % (username, mime_type)).hexdigest()) start_response("200 OK", [('Content-Type', mime_type), ('Vary', 'Accept'), ('Etag', etag_string)]) try: output = serializer_list(kept_entities) except NoSerializationError: raise HTTP415('Content type not supported: %s' % mime_type) if isinstance(output, basestring): return [output] else: return output def _filter_readable(environ, entities, filters): """ Traverse entities to get those that are readable and those that pass the filter. XXX: There is a bug here, depending on how filters are to be interpreted: If limit is used it is being calculated before the readability
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)) except FilterError, 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 _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers.