PageRenderTime 416ms CodeModel.GetById 80ms app.highlight 263ms RepoModel.GetById 60ms app.codeStats 1ms

/ucengine/src/core/uce_http.erl

http://github.com/AF83/ucengine
Erlang | 200 lines | 168 code | 15 blank | 17 comment | 1 complexity | 3f845a89a4accfdcc320e80d866669ec MD5 | raw file
  1%%
  2%%  U.C.Engine - Unified Collaboration Engine
  3%%  Copyright (C) 2011 af83
  4%%
  5%%  This program is free software: you can redistribute it and/or modify
  6%%  it under the terms of the GNU Affero General Public License as published by
  7%%  the Free Software Foundation, either version 3 of the License, or
  8%%  (at your option) any later version.
  9%%
 10%%  This program is distributed in the hope that it will be useful,
 11%%  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12%%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13%%  GNU Affero General Public License for more details.
 14%%
 15%%  You should have received a copy of the GNU Affero General Public License
 16%%  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17%%
 18-module(uce_http).
 19
 20-include("uce.hrl").
 21-include_lib("yaws/include/yaws_api.hrl").
 22
 23-export([parse/2]).
 24
 25write_file(Data, File) ->
 26    case file:write(File#file_upload.fd, Data) of
 27        ok ->
 28            ok;
 29        Err ->
 30            ?ERROR_MSG("Upload error: ~p.~n", [Err]),
 31            error
 32    end.
 33
 34process_part(_Host, _Arg, [], Params) ->
 35    Params;
 36process_part(_Host, _Arg, [{part_body, Data}|_Res], [{_Name, File}|_] = Params) when is_record(File, file_upload) ->
 37    case write_file(Data, File) of
 38        ok ->
 39            Params;
 40        error ->
 41            {error, unexpected_error}
 42    end;
 43process_part(_Host, _Arg, [{part_body, Data}|_Res], [{Name, Value}|OtherParams]) ->
 44    [{Name, Value ++ Data}] ++ OtherParams;
 45process_part(Host, Arg, [{body, Data}|Res], [{_Name, File}|_] = Params) when is_record(File, file_upload) ->
 46    case write_file(Data, File) of
 47        ok ->
 48            ok = file:close(File#file_upload.fd),
 49            process_part(Host, Arg, Res, Params);
 50        error ->
 51            {error, unexpected_error}
 52    end;
 53process_part(Host, Arg, [{body, Data}|Rest], [{Name, Value}|OtherParams]) ->
 54    process_part(Host, Arg, Rest, [{Name, Value ++ Data}] ++ OtherParams);
 55process_part(Host, Arg, [{head, {Name, Opts}}|Res], Params) ->
 56    case lists:keyfind(filename, 1, Opts) of
 57        {_, Fname} ->
 58            Dir = lists:concat([config:get(Host, data), "/", utils:random(3)]),
 59            FilePath = lists:concat([Dir, "/", utils:random()]),
 60            file:make_dir(Dir),
 61            case file:open(FilePath,[write]) of
 62                {ok, Fd} ->
 63                    File = #file_upload{filename = Fname,
 64                                        uri = "file://"++ FilePath,
 65                                        fd = Fd},
 66                    process_part(Host, Arg, Res, [{Name, File}] ++ Params);
 67                Err ->
 68                    ?ERROR_MSG("Upload error: ~p.", [Err]),
 69                    {error, unexpected_error}
 70            end;
 71        false ->
 72            process_part(Host, Arg, Res, [{Name, ""}] ++ Params)
 73    end.
 74
 75parse_multipart(Host, Arg, State) ->
 76    case yaws_api:parse_multipart_post(Arg) of
 77        {cont, Cont, Res} ->
 78            NewState = process_part(Host, Arg, Res, State),
 79            case NewState of
 80                {error, _Error} ->
 81                    NewState;
 82                NewState ->
 83                    {get_more, Cont, NewState}
 84            end;
 85        {result, Res} ->
 86            FormDataParams = process_part(Host, Arg, Res, State),
 87            case FormDataParams of
 88                {error, _Error} ->
 89                    FormDataParams;
 90                FormDataParams ->
 91                    Params = yaws_api:parse_query(Arg) ++ FormDataParams,
 92                    {'POST', Arg#arg.pathinfo, parse_query(Params)}
 93            end
 94    end.
 95
 96extract(Host, Arg, State) ->
 97    Request = Arg#arg.req,
 98    case Request#http_request.method of
 99        'GET' ->
100            {'GET', Arg#arg.pathinfo, parse_query(yaws_api:parse_query(Arg))};
101        _ ->
102            case Arg#arg.headers#headers.content_type of
103                "multipart/form-data;"++ _Boundary ->
104                    parse_multipart(Host, Arg, State);
105                _ContentType ->
106                    OriginalMethod = Request#http_request.method,
107                    NewArg = Arg#arg{req = Arg#arg.req#http_request{method = 'POST'}},
108                    Query = yaws_api:parse_post(NewArg) ++ yaws_api:parse_query(NewArg),
109                    Method = case utils:get(Query, ["_method"]) of
110                                 [none] ->
111                                     OriginalMethod;
112                                 [StringMethod] ->
113                                     list_to_atom(string:to_upper(StringMethod))
114                             end,
115                    {Method, Arg#arg.pathinfo, parse_query(Query)}
116            end
117    end.
118
119parse(Host, #arg{} = Arg)
120  when Arg#arg.state == undefined ->
121    extract(Host, Arg, []);
122
123parse(Host, #arg{} = Arg) ->
124    extract(Host, Arg, Arg#arg.state).
125
126extract_dictionary([], _) ->
127    [];
128extract_dictionary([{DictElem, Value}|Tail], Name) ->
129    Elem = case re:run(DictElem, "^(.*)\\[([^\]]+)\\]$ ?", [{capture, all, list}]) of
130               {match, [_, Name, Key]} ->
131                   [{Key, Value}];
132               _ ->
133                   []
134           end,
135    Elem ++ extract_dictionary(Tail, Name).
136
137remove_key([], _) ->
138    [];
139remove_key([{Key, Value}|Tail], Name) ->
140    ElemName = case re:run(Key, "^(.*)\\[([^\]]+)\\]$ ?", [{capture, all, list}]) of
141                   {match, [_, Name, _]} ->
142                       Name;
143                   _ ->
144                       Key
145               end,
146    Elem = case ElemName of
147               Name ->
148                   [];
149               _ ->
150                   [{Key, Value}]
151           end,
152    Elem ++ remove_key(Tail, Name).
153
154parse_query_elems([]) ->
155    [];
156parse_query_elems([{Key, Value}|Tail]=Query) ->
157    Elem = case re:run(Key, "^(.*)\\[([^\]]+)\\]$ ?", [{capture, all, list}]) of
158               {match, [_, Name, _]} ->
159                   [{Name, extract_dictionary(Query, Name)}];
160               _ ->
161                   [{Key, Value}]
162           end,
163    [{ToRemove, _}] = Elem,
164    Elem ++ parse_query_elems(remove_key(Tail, ToRemove)).
165
166parse_query(AsciiDirtyQuery) ->
167    AsciiQuery = lists:filter(fun({Key, _}) ->
168                                      case Key of
169                                          [] ->
170                                              false;
171                                          _ ->
172                                              true
173                                      end
174                              end,
175                              AsciiDirtyQuery),
176    Query = lists:map(fun({Key, Value}) ->
177                              case Value of
178                                  undefined ->
179                                      {unicode_helpers:normalize_unicode(Key), ""};
180                                  Value when is_record(Value, file_upload) ->
181                                      FileName = Value#file_upload.filename,
182                                      {unicode_helpers:normalize_unicode(Key), Value#file_upload{
183                                        filename=unicode_helpers:normalize_unicode(FileName)
184                                     }};
185                                  _ ->
186                                      {unicode_helpers:normalize_unicode(Key),
187                                       unicode_helpers:normalize_unicode(Value)}
188                              end
189                      end,
190                      AsciiQuery),
191    parse_query_elems(Query).
192
193-ifdef(TEST).
194-include_lib("eunit/include/eunit.hrl").
195
196parse_query_test() ->
197    ?assertEqual([{"Test", "test"}], parse_query([{"Test", "test"}])),
198    ?assertEqual([{"test", [{"to", "test"}]}], parse_query([{"test[to]", "test"}])).
199
200-endif.