%%% BEGIN openflax/handler/upload.erl %%% %%% %%% openflax - Open Source web server for Erlang/OTP %%% Copyright (c)2004 Cat's Eye Technologies. All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% %%% Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in %%% the documentation and/or other materials provided with the %%% distribution. %%% %%% Neither the name of Cat's Eye Technologies nor the names of its %%% contributors may be used to endorse or promote products derived %%% from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND %%% CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, %%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE %%% DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE %%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, %%% OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, %%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, %%% OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON %%% ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, %%% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY %%% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %% @doc Upload handler for OpenFlax. %% %%
This handler's security/featurefulness tradeoff level is %% 9 (Insecure.) It creates files in the filesystem. %% It never overwrites existing files.
%% %%This handler requires openflax.handler.file and
%% openflax.handler.template.
This handler is intended to serve mostly as an example for %% writing an upload handler. It is suitable within a trusted environment %% such as a home LAN, but cannot be used without risk on a public %% network.
%% %% @end -module(openflax.handler.upload). -vsn('$Id: upload.erl 31 2004-04-23 07:00:11Z catseye $'). -author('cpressey@catseye.mine.nu'). -copyright('Copyright (c)2004 Cat`s Eye Technologies. All rights reserved.'). -behaviour(openflax.handler). -export([start/1, stop/1, serve/1]). % behaviour -import(calendar). -import(lists). -import(io_lib). -import(filename). -import(file). -import(filelib). %% @spec start(conf()) -> conf() %% @doc Initializes the uploads subsystem. start(_Conf) -> openflax.conf:new(). %% @spec stop(conf()) -> conf() %% @doc Shuts down the uploads subsystem. stop(Conf) -> Conf. %% @spec serve(conf()) -> {response(), conf()} %% @doc Serves a resonse to an upload request. %% We assume the request was a multipart/form-data request, and %% instead of callingopenflax.http.request:collect_body/1, we
%% elect to receive the messages ourself.
serve(Conf) ->
% ContentLengthString = openflax.conf:get_string(req_content_length, Conf),
case collect_body(Conf) of
true ->
BasicResource = openflax.conf:get_string(sreq_basic_resource, Conf),
BRDir = filename:dirname(BasicResource) ++ "/",
% Conf0 = openflax.conf:put_value(sreq_basic_resource, BRDir, Conf),
% Conf1 = openflax.conf:delete_value(cfg_template_body, Conf0),
% {openflax.handler.ls, Conf1};
{{moved_temporarily, BRDir}, Conf};
false ->
{openflax.handler.template, Conf}
end.
%% ---- UPLOAD ---
collect_body(Conf) ->
{ok, Http} = openflax.conf:get_value(cfg_openflax_http, Conf),
UploadDir = filename:dirname(openflax.handler.file:document(Conf)),
collect_body(Http, UploadDir, undefined, undefined).
collect_body(Http, UploadDir, Filename, File) ->
receive
{Http, multipart_header, {req_content_disposition, Fields}} ->
Filename0 = get_upload_filename(UploadDir, Fields),
openflax.app:debug(upload_filename, Filename0),
{ok, File0} = file:open(Filename0, [raw, write, delayed_write]),
collect_body(Http, UploadDir, Filename0, File0);
{Http, multipart_header, Header} ->
openflax.app:debug(upload_multipart_header, Header),
collect_body(Http, UploadDir, Filename, File);
{Http, multipart_chunk, Chunk} ->
file:write(File, Chunk),
collect_body(Http, UploadDir, Filename, File);
{Http, multipart_end, _} ->
openflax.app:debug(multipart_end, ok),
file:close(File),
true;
{Http, request_body, _} ->
% we weren't called with a multipart form, obviously.
false;
Else ->
openflax.app:debug(what_the_heck, Else),
collect_body(Http, UploadDir, Filename, File)
end.
get_upload_filename(UploadDir, Fields) ->
UploadedFilename = case lists:keysearch("filename", 1, Fields) of
{value, {_, Filename}} ->
Filename0 = strip_quotes(Filename),
Filename1 = openflax.string:substitute($\\, $/, Filename0),
% silly IE and Netscape for Windows send full path in filename!
filename:basename(Filename1);
false ->
"no_name"
end,
NewFilename = filename:join([UploadDir, UploadedFilename]),
case filelib:is_file(NewFilename) of
true ->
BaseName = filename:basename(UploadedFilename),
Extension = filename:extension(UploadedFilename),
filename:join([UploadDir, BaseName ++ "-" ++ timestamp() ++ Extension]);
false ->
NewFilename
end.
%% @spec strip_quotes(string()) -> string()
%% @equiv strip_quotes(string(), $")
strip_quotes(S) -> strip_quotes(S, $").
%% @spec strip_quotes(string(), char()) -> string()
%% @doc Strips leading and trailing quotes from a string.
strip_quotes("", _) -> "";
strip_quotes([C], _) -> C;
strip_quotes(S=[Q | T], Q) ->
case lists:last(T) of
Q -> openflax.string:truncate(T);
_ -> S
end;
strip_quotes(S, _) -> S.
%% @spec timestamp() -> string()
%% @doc Returns a timestamp string which is both human-readable and unique
%% within the node on which it is generated.
%% This sort of date/time looks like "20030814.184025.148330".
timestamp() ->
{{Y, M, D}, {H, I, S}} = calendar:local_time(),
{_, _, Us} = erlang:now(),
lists:flatten(io_lib:format(
"~w~2.2.0w~2.2.0w.~2.2.0w~2.2.0w~2.2.0w.~6.6.0w",
[Y, M, D, H, I, S, Us])).
%%% END of openflax/handler/upload.erl %%%