summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/couchdb.tpl.in2
-rw-r--r--src/mochiweb/Makefile.am2
-rw-r--r--src/mochiweb/mochiweb.erl8
-rw-r--r--src/mochiweb/mochiweb_request.erl27
-rw-r--r--src/mochiweb/mochiweb_util.erl88
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
@@ -77,6 +77,14 @@ new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri},
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,
+ Version,
mochiweb_headers:make(Headers)).
%% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse
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.