From 65e850a01858509e0bc519832686aa52d58809b0 Mon Sep 17 00:00:00 2001 From: Christopher Lenz Date: Mon, 26 May 2008 09:06:49 +0000 Subject: Updated MochiWeb in trunk to r76. Closes COUCHDB-42. git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@660136 13f79535-47bb-0310-9956-ffa450edef68 --- bin/couchdb.tpl.in | 2 +- src/mochiweb/Makefile.am | 2 +- src/mochiweb/mochiweb.erl | 8 ++++ src/mochiweb/mochiweb_request.erl | 27 ++++++------ src/mochiweb/mochiweb_util.erl | 88 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 15 deletions(-) diff --git a/bin/couchdb.tpl.in b/bin/couchdb.tpl.in index c5f81c46..eab2e52b 100644 --- a/bin/couchdb.tpl.in +++ b/bin/couchdb.tpl.in @@ -220,7 +220,7 @@ start_couchdb () { command="`%ICU_CONFIG% --invoke` \ %ERL% $interactive_option -smp auto -sasl errlog_type error \ -pa %erlanglibdir%/couch-%version%/ebin \ - %erlanglibdir%/mochiweb-r73/ebin \ + %erlanglibdir%/mochiweb-r76/ebin \ -eval \"application:load(inets)\" \ -eval \"application:load(crypto)\" \ -eval \"application:load(couch)\" \ diff --git a/src/mochiweb/Makefile.am b/src/mochiweb/Makefile.am index a4021bb4..aa7a670a 100644 --- a/src/mochiweb/Makefile.am +++ b/src/mochiweb/Makefile.am @@ -12,7 +12,7 @@ datarootdir = @prefix@/share -mochiwebebindir = $(erlanglibdir)/mochiweb-r73/ebin +mochiwebebindir = $(erlanglibdir)/mochiweb-r76/ebin mochiweb_file_collection = \ mochifmt.erl \ diff --git a/src/mochiweb/mochiweb.erl b/src/mochiweb/mochiweb.erl index be30fccf..0f4d52a6 100644 --- a/src/mochiweb/mochiweb.erl +++ b/src/mochiweb/mochiweb.erl @@ -73,6 +73,14 @@ new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) -> % this case probably doesn't "exist". new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri}, Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)); +%% Request-URI is "*" +%% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 +new_request({Socket, {Method, '*'=Uri, Version}, Headers}) -> mochiweb_request:new(Socket, Method, Uri, diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl index b4a7bcf7..ec41338e 100644 --- a/src/mochiweb/mochiweb_request.erl +++ b/src/mochiweb/mochiweb_request.erl @@ -325,7 +325,8 @@ should_close() -> andalso get_header_value("connection") =/= "Keep-Alive") %% unread data left on the socket, can't safely continue orelse (DidNotRecv - andalso get_header_value("content-length") =/= undefined). + andalso get_header_value("content-length") =/= undefined + andalso list_to_integer(get_header_value("content-length")) > 0). %% @spec cleanup() -> ok %% @doc Clean up any junk in the process dictionary, required before continuing @@ -454,15 +455,17 @@ read_chunk(Length) -> %% @spec serve_file(Path, DocRoot) -> Response %% @doc Serve a file relative to DocRoot. serve_file(Path, DocRoot) -> - FullPath = filename:join([DocRoot, Path]), - File = case filelib:is_dir(FullPath) of - true -> - filename:join([FullPath, "index.html"]); - false -> - FullPath - end, - case lists:prefix(DocRoot, File) of - true -> + case mochiweb_util:safe_relative_path(Path) of + undefined -> + not_found(); + RelPath -> + FullPath = filename:join([DocRoot, RelPath]), + File = case filelib:is_dir(FullPath) of + true -> + filename:join([FullPath, "index.html"]); + false -> + FullPath + end, case file:read_file_info(File) of {ok, FileInfo} -> LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), @@ -482,9 +485,7 @@ serve_file(Path, DocRoot) -> end; {error, _} -> not_found() - end; - false -> - not_found() + end end. diff --git a/src/mochiweb/mochiweb_util.erl b/src/mochiweb/mochiweb_util.erl index e7026194..63d0b986 100644 --- a/src/mochiweb/mochiweb_util.erl +++ b/src/mochiweb/mochiweb_util.erl @@ -11,6 +11,7 @@ -export([guess_mime/1, parse_header/1]). -export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]). -export([record_to_proplist/2, record_to_proplist/3]). +-export([safe_relative_path/1, partition/2]). -export([to_lower/1]). -export([test/0]). @@ -32,6 +33,69 @@ unhexdigit(C) when C >= $0, C =< $9 -> C - $0; unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10; unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10. +%% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix} +%% @doc Inspired by Python 2.5's str.partition: +%% partition("foo/bar", "/") = {"foo", "/", "bar"}, +%% partition("foo", "/") = {"foo", "", ""}. +partition(String, Sep) -> + case partition(String, Sep, []) of + undefined -> + {String, "", ""}; + Result -> + Result + end. + +partition("", _Sep, _Acc) -> + undefined; +partition(S, Sep, Acc) -> + case partition2(S, Sep) of + undefined -> + [C | Rest] = S, + partition(Rest, Sep, [C | Acc]); + Rest -> + {lists:reverse(Acc), Sep, Rest} + end. + +partition2(Rest, "") -> + Rest; +partition2([C | R1], [C | R2]) -> + partition2(R1, R2); +partition2(_S, _Sep) -> + undefined. + + + +%% @spec safe_relative_path(string()) -> string() | undefined +%% @doc Return the reduced version of a relative path or undefined if it +%% is not safe. safe relative paths can be joined with an absolute path +%% and will result in a subdirectory of the absolute path. +safe_relative_path("/" ++ _) -> + undefined; +safe_relative_path(P) -> + safe_relative_path(P, []). + +safe_relative_path("", Acc) -> + case Acc of + [] -> + ""; + _ -> + join(lists:reverse(Acc), "/") + end; +safe_relative_path(P, Acc) -> + case partition(P, "/") of + {"", "/", _} -> + %% /foo or foo//bar + undefined; + {"..", _, _} when Acc =:= [] -> + undefined; + {"..", _, Rest} -> + safe_relative_path(Rest, tl(Acc)); + {Part, "/", ""} -> + safe_relative_path("", ["", Part | Acc]); + {Part, _, Rest} -> + safe_relative_path(Rest, [Part | Acc]) + end. + %% @spec shell_quote(string()) -> string() %% @doc Quote a string according to UNIX shell quoting rules, returns a string %% surrounded by double quotes. @@ -400,6 +464,8 @@ test() -> test_shell_quote(), test_cmd(), test_cmd_string(), + test_partition(), + test_safe_relative_path(), ok. test_shell_quote() -> @@ -499,3 +565,25 @@ test_parse_qs() -> [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] = parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"), ok. + +test_partition() -> + {"foo", "", ""} = partition("foo", "/"), + {"foo", "/", "bar"} = partition("foo/bar", "/"), + {"foo", "/", ""} = partition("foo/", "/"), + {"", "/", "bar"} = partition("/bar", "/"), + {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"), + ok. + +test_safe_relative_path() -> + "foo" = safe_relative_path("foo"), + "foo/" = safe_relative_path("foo/"), + "foo" = safe_relative_path("foo/bar/.."), + "bar" = safe_relative_path("foo/../bar"), + "bar/" = safe_relative_path("foo/../bar/"), + "" = safe_relative_path("foo/.."), + "" = safe_relative_path("foo/../"), + undefined = safe_relative_path("/foo"), + undefined = safe_relative_path("../foo"), + undefined = safe_relative_path("foo/../.."), + undefined = safe_relative_path("foo//"), + ok. -- cgit v1.2.3