1<erl>
2%% inspired from upload.yaws, slightly simplified.
3
4-record(state, {acc,
5                last
6               }).
7
8out(A) ->
9    %% make sure it is post method
10    'POST' = yaws_api:http_request_method(yaws_api:arg_req(A)),
11    case catch get_props(A) of
12        {ok, AllParts} ->
13            Boundary = "3e9876546ecf",
14            RspCType = "multipart/form-data; boundary=" ++ Boundary,
15            case catch format_output(AllParts, Boundary, []) of
16                {ok, Html} ->
17                    [{content,RspCType,Html},break];
18                Else ->
19                    [{status,400},{html,io_lib:format("Error: ~p~n", [Else])}]
20            end;
21        {get_more, Cont, NewState} = GetMore ->
22            GetMore;
23        Else ->
24            [{status,400},{html,io_lib:format("Error: ~p~n", [Else])}]
25    end.
26
27format_output([], Boundary, Acc) ->
28    Part = ["--", Boundary, "--\r\n"],
29    {ok, lists:reverse([iolist_to_binary(Part)|Acc])};
30format_output([{head, {Name, Hdrs}}|Rest], Boundary, Acc0) ->
31    {_, Name}  = lists:keyfind("name", 1, Hdrs),
32    Acc1 = case lists:keyfind("filename", 1, Hdrs) of
33               {_, FName} ->
34                   {_, CType} = lists:keyfind(content_type, 1, Hdrs),
35                   Part = ["--", Boundary, "\r\n",
36                           "Content-Disposition: form-data; name=\"",Name,"\"; filename=\"",FName,"\"\r\n",
37                           "Content-Type: ",CType,"\r\n\r\n"],
38                   [iolist_to_binary(Part)|Acc0];
39               false ->
40                   Part = ["--", Boundary, "\r\n",
41                           "Content-Disposition: form-data; name=\"",Name,"\"",
42                           "\r\n\r\n"],
43                   [iolist_to_binary(Part)|Acc0]
44           end,
45    format_output(Rest, Boundary, Acc1);
46format_output([{part_body, Body}|Rest], Boundary, Acc) ->
47    format_output(Rest, Boundary, [Body|Acc]);
48format_output([{body, Body}|Rest], Boundary, Acc) ->
49    Part = [Body, "\r\n"],
50    format_output(Rest, Boundary, [iolist_to_binary(Part)|Acc]);
51format_output([H|_], _, _) ->
52    {error, {invalid_multipart_element, H}}.
53
54
55
56
57get_state(A) ->
58    case A#arg.state of
59        undefined -> #state{acc = [], last = false};
60        State0    -> State0
61    end.
62
63
64get_props(A) ->
65    Parse = yaws_api:parse_multipart_post(A),
66    State = get_state(A),
67    case Parse of
68        {cont, Cont, Res} ->
69            case add_file_chunk(A, Res, State) of
70                {done, Result}   -> {ok, Result};
71                {cont, NewState} -> {get_more, Cont, NewState}
72            end;
73        {result, Res} ->
74            case add_file_chunk(A, Res, State#state{last = true}) of
75                {done, Result} -> {ok, Result};
76                {cont, _}      -> {error, multipart_parsing_error}
77            end;
78        {error, Reason} = Error ->
79            Error
80    end.
81
82
83add_file_chunk(A, [H | Tail], #state{acc = Acc0} = State0) ->
84    Acc1 = [H | Acc0],
85    State1 = State0#state{acc = Acc1},
86    add_file_chunk(A, Tail, State1);
87add_file_chunk(A, [], #state{last = false} = State) ->
88    {cont, State};
89add_file_chunk(A, [], #state{last = true, acc = RAcc} = State) ->
90    Data = lists:reverse(RAcc),
91    {done, Data}.
92</erl>
93