anchor.diff:      couch_doc_open(Db, DocId, nil, []),
       case chttpd:qs_value(Req, "rev") of
       undefined ->
  -        update_doc(Req, Db, DocId, {[{<<"_deleted">>,true}]});
  +        Body = {[{<<"_deleted">>,true}]};
       Rev ->
  -        update_doc(Req, Db, DocId, {[{<<"_rev">>, ?l2b(Rev)},{<<"_deleted">>,true}]})
  -    end;
  +        Body = {[{<<"_rev">>, ?l2b(Rev)},{<<"_deleted">>,true}]}
  +    end,
  +    update_doc(Req, Db, DocId, couch_doc_from_req(Req, DocId, Body));
   
   db_doc_req(#httpd{method='GET'}=Req, Db, DocId) ->
       #doc_query_args{
           rev = Rev,
           open_revs = Revs,
  -        options = Options
  +        options = Options,
  +        atts_since = AttsSince
       } = parse_doc_query(Req),
       case Revs of
       [] ->
  -        Doc = couch_doc_open(Db, DocId, Rev, Options),
  -        DiskEtag = chttpd:doc_etag(Doc),
  -        case Doc#doc.meta of
  -        [] ->
  -            % output etag only when we have no meta
  -            chttpd:etag_respond(Req, DiskEtag, fun() ->
  -                send_json(Req, 200, [{"Etag", DiskEtag}], couch_doc:to_json_obj(Doc, Options))
  -            end);
  -        _ ->
  -            send_json(Req, 200, [], couch_doc:to_json_obj(Doc, Options))
  -        end;
  +        Options2 =
  +        if AttsSince /= nil ->
  +            [{atts_since, AttsSince}, attachments | Options];
  +        true -> Options
  +        end,
  +        Doc = couch_doc_open(Db, DocId, Rev, Options2),
  +        send_doc(Req, Doc, Options2);
       _ ->
           {ok, Results} = fabric:open_revs(Db, DocId, Revs, Options),
  -        {ok, Resp} = start_json_response(Req, 200),
  -        send_chunk(Resp, "["),
  -        % We loop through the docs. The first time through the separator
  -        % is whitespace, then a comma on subsequent iterations.
  -        lists:foldl(
  -            fun(Result, AccSeparator) ->
  -                case Result of
  -                {ok, Doc} ->
  -                    JsonDoc = couch_doc:to_json_obj(Doc, Options),
  -                    Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
  -                    send_chunk(Resp, AccSeparator ++ Json);
  -                {{not_found, missing}, RevId} ->
  -                    Json = ?JSON_ENCODE({[{"missing", RevId}]}),
  -                    send_chunk(Resp, AccSeparator ++ Json)
  +        AcceptedTypes = case couch_httpd:header_value(Req, "Accept") of
  +            undefined       -> [];
  +            AcceptHeader    -> string:tokens(AcceptHeader, "; ")
  +        end,
  +        case lists:member("multipart/mixed", AcceptedTypes) of
  +        false ->
  +            {ok, Resp} = start_json_response(Req, 200),
  +            send_chunk(Resp, "["),
  +            % We loop through the docs. The first time through the separator
  +            % is whitespace, then a comma on subsequent iterations.
  +            lists:foldl(
  +                fun(Result, AccSeparator) ->
  +                    case Result of
  +                    {ok, Doc} ->
  +                        JsonDoc = couch_doc:to_json_obj(Doc, Options),
  +                        Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
  +                        send_chunk(Resp, AccSeparator ++ Json);
  +                    {{not_found, missing}, RevId} ->
  +                        RevStr = couch_doc:rev_to_str(RevId),
  +                        Json = ?JSON_ENCODE({[{"missing", RevStr}]}),
  +                        send_chunk(Resp, AccSeparator ++ Json)
  +                    end,
  +                    "," % AccSeparator now has a comma
                   end,
  -                "," % AccSeparator now has a comma
  -            end,
  -            "", Results),
  -        send_chunk(Resp, "]"),
  -        end_json_response(Resp)
  +                "", Results),
  +            send_chunk(Resp, "]"),
  +            end_json_response(Resp);
  +        true ->
  +            send_docs_multipart(Req, Results, Options)
  +        end
       end;
   
   db_doc_req(#httpd{method='POST', user_ctx=Ctx}=Req, Db, DocId) ->
  +    couch_httpd:validate_referer(Req),
       couch_doc:validate_docid(DocId),
  -    case chttpd:header_value(Req, "content-type") of
  -    "multipart/form-data" ++  _Rest ->
  -        ok;
  -    _Else ->
  -        throw({bad_ctype, <<"Invalid Content-Type header for form upload">>})
  -    end,
  -    Form = chttpd:parse_form(Req),
  -    Rev = couch_doc:parse_rev(list_to_binary(couch_util:get_value("_rev", Form))),
  -    {ok, [{ok, Doc}]} = fabric:open_revs(Db, DocId, [Rev], []),
  -
  +    couch_httpd:validate_ctype(Req, "multipart/form-data"),
  +    Form = couch_httpd:parse_form(Req),
  +    case proplists:is_defined("_doc", Form) of
  +    true ->
  +        Json = ?JSON_DECODE(couch_util:get_value("_doc", Form)),
  +        Doc = couch_doc_from_req(Req, DocId, Json);
  +    false ->
  +        Rev = couch_doc:parse_rev(list_to_binary(couch_util:get_value("_rev", Form))),
  +        {ok, [{ok, Doc}]} = fabric:open_revs(Db, DocId, [Rev], [])
  +    end,
       UpdatedAtts = [
           #att{name=validate_attachment_name(Name),
               type=list_to_binary(ContentType),
  
positive.code: % Special case to allow for accessing local documents without %2F
  % encoding the docid. Throws out requests that don't have the second
  % path part or that specify an attachment name.
  db_req(#httpd{path_parts=[_DbName, <<"_local">>, Name]}=Req, Db) ->
      db_doc_req(Req, Db, <<"_local/", Name/binary>>);
  
  db_req(#httpd{path_parts=[_DbName, <<"_local">>]}, _Db) ->
      throw({bad_request, <<"Missing _local document id.">>});
  
  db_req(#httpd{path_parts=[_DbName, <<"_local/">>]}, _Db) ->
      throw({bad_request, <<"Missing _local document id.">>});
  
  db_req(#httpd{path_parts=[_DbName, <<"_local">> | _Rest]}, _Db) ->
      throw({bad_request, <<"_local documents do not accept attachments.">>});
  
  db_req(#httpd{path_parts=[_DbName, <<"_local/", _/binary>>, _ | _]}, _Db) ->
      throw({bad_request, <<"_local documents do not accept attachments.">>});
  
  db_req(#httpd{path_parts=[_, DocId]}=Req, Db) ->
  
negative.code:     send_method_not_allowed(Req, "GET").
  
  create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
      N = couch_httpd:qs_value(Req, "n", couch_config:get("cluster", "n", "3")),
      Q = couch_httpd:qs_value(Req, "q", couch_config:get("cluster", "q", "8")),
      case fabric:create_db(DbName, [{user_ctx,UserCtx}, {n,N}, {q,Q}]) of
      ok ->
          DocUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
          send_json(Req, 201, [{"Location", DocUrl}], {[{ok, true}]});
  