1%% ``Licensed under the Apache License, Version 2.0 (the "License");
2%% you may not use this file except in compliance with the License.
3%% You may obtain a copy of the License at
4%%
5%%     http://www.apache.org/licenses/LICENSE-2.0
6%%
7%% Unless required by applicable law or agreed to in writing, software
8%% distributed under the License is distributed on an "AS IS" BASIS,
9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10%% See the License for the specific language governing permissions and
11%% limitations under the License.
12%%
13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15%% AB. All Rights Reserved.''
16%%
17%%     $Id: httpd_util.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
18%%
19-module(httpd_util).
20-export([key1search/2, key1search/3, lookup/2, lookup/3, multi_lookup/2,
21	 lookup_mime/2, lookup_mime/3, lookup_mime_default/2,
22	 lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0,
23	 rfc1123_date/1, day/1, month/1, decode_hex/1, decode_base64/1, encode_base64/1,
24	 flatlength/1, split_path/1, split_script_path/1, suffix/1, to_upper/1,
25	 to_lower/1, split/3, header/2, header/3, header/4, uniq/1,
26	 make_name/2,make_name/3,make_name/4,strip/1,
27	 hexlist_to_integer/1,integer_to_hexlist/1,
28	 convert_request_date/1,create_etag/1,create_etag/2,getSize/1,
29	 response_generated/1]).
30
31%%Since hexlist_to_integer is a lousy name make a name convert
32-export([encode_hex/1]).
33-include("httpd.hrl").
34
35%% key1search
36
37key1search(TupleList,Key) ->
38  key1search(TupleList,Key,undefined).
39
40key1search(TupleList,Key,Undefined) ->
41  case lists:keysearch(Key,1,TupleList) of
42    {value,{Key,Value}} ->
43      Value;
44    false ->
45      Undefined
46  end.
47
48%% lookup
49
50lookup(Table,Key) ->
51  lookup(Table,Key,undefined).
52
53lookup(Table,Key,Undefined) ->
54    case catch ets:lookup(Table,Key) of
55	[{Key,Value}|_] ->
56	    Value;
57	_->
58	    Undefined
59    end.
60
61%% multi_lookup
62
63multi_lookup(Table,Key) ->
64    remove_key(ets:lookup(Table,Key)).
65
66remove_key([]) ->
67    [];
68remove_key([{_Key,Value}|Rest]) ->
69    [Value|remove_key(Rest)].
70
71%% lookup_mime
72
73lookup_mime(ConfigDB,Suffix) ->
74    lookup_mime(ConfigDB,Suffix,undefined).
75
76lookup_mime(ConfigDB,Suffix,Undefined) ->
77    [{mime_types,MimeTypesDB}|_]=ets:lookup(ConfigDB,mime_types),
78    case ets:lookup(MimeTypesDB,Suffix) of
79	[] ->
80	    Undefined;
81	[{Suffix,MimeType}|_] ->
82	    MimeType
83    end.
84
85%% lookup_mime_default
86
87lookup_mime_default(ConfigDB,Suffix) ->
88    lookup_mime_default(ConfigDB,Suffix,undefined).
89
90lookup_mime_default(ConfigDB,Suffix,Undefined) ->
91    [{mime_types,MimeTypesDB}|_]=ets:lookup(ConfigDB,mime_types),
92    case ets:lookup(MimeTypesDB,Suffix) of
93	[] ->
94	    case ets:lookup(ConfigDB,default_type) of
95		[] ->
96		    Undefined;
97		[{default_type,DefaultType}|_] ->
98		    DefaultType
99	    end;
100	[{Suffix,MimeType}|_] ->
101	    MimeType
102    end.
103
104%% reason_phrase
105reason_phrase(100) -> "Continue";
106reason_phrase(101) -> "Swithing protocol";
107reason_phrase(200) -> "OK";
108reason_phrase(201) -> "Created";
109reason_phrase(202) -> "Accepted";
110reason_phrase(204) -> "No Content";
111reason_phrase(205) -> "Reset Content";
112reason_phrase(206) -> "Partial Content";
113reason_phrase(301) -> "Moved Permanently";
114reason_phrase(302) -> "Moved Temporarily";
115reason_phrase(304) -> "Not Modified";
116reason_phrase(400) -> "Bad Request";
117reason_phrase(401) -> "Unauthorized";
118reason_phrase(402) -> "Payment Required";
119reason_phrase(403) -> "Forbidden";
120reason_phrase(404) -> "Not Found";
121reason_phrase(405) -> "Method Not Allowed";
122reason_phrase(408) -> "Request Timeout";
123reason_phrase(411) -> "Length Required";
124reason_phrase(414) -> "Request-URI Too Long";
125reason_phrase(412) -> "Precondition Failed";
126reason_phrase(416) -> "request Range Not Satisfiable";
127reason_phrase(417) -> "Expectation failed";
128reason_phrase(500) -> "Internal Server Error";
129reason_phrase(501) -> "Not Implemented";
130reason_phrase(502) -> "Bad Gateway";
131reason_phrase(503) -> "Service Unavailable";
132reason_phrase(_) -> "Internal Server Error".
133
134%% message
135
136message(301,URL,_) ->
137  "The document has moved <A HREF=\""++URL++"\">here</A>.";
138message(304,_URL,_) ->
139    "The document has not been changed.";
140message(400,none,_) ->
141  "Your browser sent a query that this server could not understand.";
142message(401,none,_) ->
143  "This server could not verify that you
144are authorized to access the document you
145requested.  Either you supplied the wrong
146credentials (e.g., bad password), or your
147browser does not understand how to supply
148the credentials required.";
149message(403,RequestURI,_) ->
150  "You do not have permission to access "++RequestURI++" on this server.";
151message(404,RequestURI,_) ->
152  "The requested URL "++RequestURI++" was not found on this server.";
153message(412,none,_) ->
154  "The requested preconditions where false";
155message(414,ReasonPhrase,_) ->
156  "Message "++ReasonPhrase++".";
157message(416,ReasonPhrase,_) ->
158    ReasonPhrase;
159
160message(500,none,ConfigDB) ->
161  ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"),
162  "The server encountered an internal error or
163misconfiguration and was unable to complete
164your request.
165<P>Please contact the server administrator "++ServerAdmin++",
166and inform them of the time the error occurred
167and anything you might have done that may have
168caused the error.";
169message(501,{Method,RequestURI,HTTPVersion},_ConfigDB) ->
170  Method++" to "++RequestURI++" ("++HTTPVersion++") not supported.";
171message(503,String,_ConfigDB) ->
172  "This service in unavailable due to: "++String.
173
174%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
175
176convert_request_date([D,A,Y,DateType|Rest]) ->
177    Func=case DateType of
178	     $\, ->
179		 fun convert_rfc1123_date/1;
180	     $\  ->
181		 fun convert_ascii_date/1;
182	     _ ->
183		 fun convert_rfc850_date/1
184	 end,
185    case catch Func([D,A,Y,DateType|Rest])of
186	{ok,Date} ->
187	    Date;
188	_Error ->
189	    bad_date
190    end.
191
192convert_rfc850_date(DateStr) ->
193    case string:tokens(DateStr," ") of
194	[_WeekDay,Date,Time,_TimeZone|_Rest] ->
195	   convert_rfc850_date(Date,Time);
196	_Error ->
197	    bad_date
198    end.
199
200convert_rfc850_date([D1,D2,_,M,O,N,_,Y1,Y2|_Rest],[H1,H2,_Col,M1,M2,_Col,S1,S2|_Rest2])->
201    Year=list_to_integer([50,48,Y1,Y2]),
202    Day=list_to_integer([D1,D2]),
203    Month=convert_month([M,O,N]),
204    Hour=list_to_integer([H1,H2]),
205    Min=list_to_integer([M1,M2]),
206    Sec=list_to_integer([S1,S2]),
207    {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
208convert_rfc850_date(_BadDate,_BadTime)->
209    bad_date.
210
211convert_ascii_date([_D,_A,_Y,_SP,M,O,N,_SP,D1,D2,_SP,H1,H2,_Col,M1,M2,_Col,S1,S2,_SP,Y1,Y2,Y3,Y4|_Rest])->
212    Year=list_to_integer([Y1,Y2,Y3,Y4]),
213    Day=case D1 of
214	$\ ->
215	    list_to_integer([D2]);
216	_->
217	    list_to_integer([D1,D2])
218    end,
219    Month=convert_month([M,O,N]),
220    Hour=list_to_integer([H1,H2]),
221    Min=list_to_integer([M1,M2]),
222    Sec=list_to_integer([S1,S2]),
223    {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
224convert_ascii_date(BadDate)->
225    bad_date.
226convert_rfc1123_date([_D,_A,_Y,_C,_SP,D1,D2,_SP,M,O,N,_SP,Y1,Y2,Y3,Y4,_SP,H1,H2,_Col,M1,M2,_Col,S1,S2|Rest])->
227    Year=list_to_integer([Y1,Y2,Y3,Y4]),
228    Day=list_to_integer([D1,D2]),
229    Month=convert_month([M,O,N]),
230    Hour=list_to_integer([H1,H2]),
231    Min=list_to_integer([M1,M2]),
232    Sec=list_to_integer([S1,S2]),
233    {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
234convert_rfc1123_date(BadDate)->
235    bad_date.
236
237convert_month("Jan")->1;
238convert_month("Feb") ->2;
239convert_month("Mar") ->3;
240convert_month("Apr") ->4;
241convert_month("May") ->5;
242convert_month("Jun") ->6;
243convert_month("Jul") ->7;
244convert_month("Aug") ->8;
245convert_month("Sep") ->9;
246convert_month("Oct") ->10;
247convert_month("Nov") ->11;
248convert_month("Dec") ->12.
249
250
251%% rfc1123_date
252
253rfc1123_date() ->
254  {{YYYY,MM,DD},{Hour,Min,Sec}}=calendar:universal_time(),
255  DayNumber=calendar:day_of_the_week({YYYY,MM,DD}),
256  lists:flatten(io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
257                        [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
258
259rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) ->
260  DayNumber=calendar:day_of_the_week({YYYY,MM,DD}),
261  lists:flatten(io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
262                        [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
263
264%% uniq
265
266uniq([]) ->
267    [];
268uniq([First,First|Rest]) ->
269    uniq([First|Rest]);
270uniq([First|Rest]) ->
271    [First|uniq(Rest)].
272
273
274%% day
275
276day(1) -> "Mon";
277day(2) -> "Tue";
278day(3) -> "Wed";
279day(4) -> "Thu";
280day(5) -> "Fri";
281day(6) -> "Sat";
282day(7) -> "Sun".
283
284%% month
285
286month(1) -> "Jan";
287month(2) -> "Feb";
288month(3) -> "Mar";
289month(4) -> "Apr";
290month(5) -> "May";
291month(6) -> "Jun";
292month(7) -> "Jul";
293month(8) -> "Aug";
294month(9) -> "Sep";
295month(10) -> "Oct";
296month(11) -> "Nov";
297month(12) -> "Dec".
298
299%% decode_hex
300
301decode_hex([$%,Hex1,Hex2|Rest]) ->
302  [hex2dec(Hex1)*16+hex2dec(Hex2)|decode_hex(Rest)];
303decode_hex([First|Rest]) ->
304  [First|decode_hex(Rest)];
305decode_hex([]) ->
306  [].
307
308hex2dec(X) when X>=$0,X=<$9 -> X-$0;
309hex2dec(X) when X>=$A,X=<$F -> X-$A+10;
310hex2dec(X) when X>=$a,X=<$f -> X-$a+10.
311
312%% decode_base64 (DEBUG STRING: QWxhZGRpbjpvcGVuIHNlc2FtZQ==)
313
314decode_base64([]) ->
315  [];
316decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
317  Bits2x6=
318    (d(Sextet1) bsl 18) bor
319    (d(Sextet2) bsl 12),
320  Octet1=Bits2x6 bsr 16,
321  [Octet1|decode_base64(Rest)];
322decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
323  Bits3x6=
324    (d(Sextet1) bsl 18) bor
325    (d(Sextet2) bsl 12) bor
326    (d(Sextet3) bsl 6),
327  Octet1=Bits3x6 bsr 16,
328  Octet2=(Bits3x6 bsr 8) band 16#ff,
329  [Octet1,Octet2|decode_base64(Rest)];
330decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
331  Bits4x6=
332    (d(Sextet1) bsl 18) bor
333    (d(Sextet2) bsl 12) bor
334    (d(Sextet3) bsl 6) bor
335    d(Sextet4),
336  Octet1=Bits4x6 bsr 16,
337  Octet2=(Bits4x6 bsr 8) band 16#ff,
338  Octet3=Bits4x6 band 16#ff,
339  [Octet1,Octet2,Octet3|decode_base64(Rest)];
340decode_base64(CatchAll) ->
341  "BAD!".
342
343d(X) when X >= $A, X =<$Z ->
344    X-65;
345d(X) when X >= $a, X =<$z ->
346    X-71;
347d(X) when X >= $0, X =<$9 ->
348    X+4;
349d($+) -> 62;
350d($/) -> 63;
351d(_) -> 63.
352
353
354encode_base64([]) ->
355    [];
356encode_base64([A]) ->
357    [e(A bsr 2), e((A band 3) bsl 4), $=, $=];
358encode_base64([A,B]) ->
359    [e(A bsr 2), e(((A band 3) bsl 4) bor (B bsr 4)), e((B band 15) bsl 2), $=];
360encode_base64([A,B,C|Ls]) ->
361    encode_base64_do(A,B,C, Ls).
362encode_base64_do(A,B,C, Rest) ->
363    BB = (A bsl 16) bor (B bsl 8) bor C,
364    [e(BB bsr 18), e((BB bsr 12) band 63),
365     e((BB bsr 6) band 63), e(BB band 63)|encode_base64(Rest)].
366
367e(X) when X >= 0, X < 26 -> X+65;
368e(X) when X>25, X<52 ->     X+71;
369e(X) when X>51, X<62 ->     X-4;
370e(62) ->                    $+;
371e(63) ->                    $/;
372e(X) ->                     exit({bad_encode_base64_token, X}).
373
374
375%% flatlength
376
377flatlength(List) ->
378    flatlength(List, 0).
379
380flatlength([H|T],L) when list(H) ->
381    flatlength(H,flatlength(T,L));
382flatlength([H|T],L) when binary(H) ->
383    flatlength(T,L+size(H));
384flatlength([H|T],L) ->
385    flatlength(T,L+1);
386flatlength([],L) ->
387  L.
388
389%% split_path
390
391split_path(Path) ->
392    case regexp:match(Path,"[\?].*\$") of
393	%% A QUERY_STRING exists!
394	{match,Start,Length} ->
395	    {httpd_util:decode_hex(string:substr(Path,1,Start-1)),
396	     string:substr(Path,Start,Length)};
397	%% A possible PATH_INFO exists!
398	nomatch ->
399	    split_path(Path,[])
400    end.
401
402split_path([],SoFar) ->
403    {httpd_util:decode_hex(lists:reverse(SoFar)),[]};
404split_path([$/|Rest],SoFar) ->
405    Path=httpd_util:decode_hex(lists:reverse(SoFar)),
406    case file:read_file_info(Path) of
407	{ok,FileInfo} when FileInfo#file_info.type == regular ->
408	    {Path,[$/|Rest]};
409	{ok,FileInfo} ->
410	    split_path(Rest,[$/|SoFar]);
411	{error,Reason} ->
412	    split_path(Rest,[$/|SoFar])
413    end;
414split_path([C|Rest],SoFar) ->
415    split_path(Rest,[C|SoFar]).
416
417%% split_script_path
418
419split_script_path(Path) ->
420    case split_script_path(Path, []) of
421	{Script, AfterPath} ->
422	    {PathInfo, QueryString} = pathinfo_querystring(AfterPath),
423	    {Script, {PathInfo, QueryString}};
424	not_a_script ->
425	    not_a_script
426    end.
427
428pathinfo_querystring(Str) ->
429    pathinfo_querystring(Str, []).
430pathinfo_querystring([], SoFar) ->
431    {lists:reverse(SoFar), []};
432pathinfo_querystring([$?|Rest], SoFar) ->
433    {lists:reverse(SoFar), Rest};
434pathinfo_querystring([C|Rest], SoFar) ->
435    pathinfo_querystring(Rest, [C|SoFar]).
436
437split_script_path([$?|QueryString], SoFar) ->
438    Path = httpd_util:decode_hex(lists:reverse(SoFar)),
439    case file:read_file_info(Path) of
440	{ok,FileInfo} when FileInfo#file_info.type == regular ->
441	    {Path, [$?|QueryString]};
442	{ok,FileInfo} ->
443	    not_a_script;
444	{error,Reason} ->
445	    not_a_script
446    end;
447split_script_path([], SoFar) ->
448    Path = httpd_util:decode_hex(lists:reverse(SoFar)),
449    case file:read_file_info(Path) of
450	{ok,FileInfo} when FileInfo#file_info.type == regular ->
451	    {Path, []};
452	{ok,FileInfo} ->
453	    not_a_script;
454	{error,Reason} ->
455	    not_a_script
456    end;
457split_script_path([$/|Rest], SoFar) ->
458    Path = httpd_util:decode_hex(lists:reverse(SoFar)),
459    case file:read_file_info(Path) of
460	{ok, FileInfo} when FileInfo#file_info.type == regular ->
461	    {Path, [$/|Rest]};
462	{ok, _FileInfo} ->
463	    split_script_path(Rest, [$/|SoFar]);
464	{error, _Reason} ->
465	    split_script_path(Rest, [$/|SoFar])
466    end;
467split_script_path([C|Rest], SoFar) ->
468    split_script_path(Rest,[C|SoFar]).
469
470%% suffix
471
472suffix(Path) ->
473    case filename:extension(Path) of
474	[] ->
475	    [];
476	Extension ->
477	    tl(Extension)
478    end.
479
480%% to_upper
481
482to_upper([C|Cs]) when C >= $a, C =< $z ->
483    [C-($a-$A)|to_upper(Cs)];
484to_upper([C|Cs]) ->
485    [C|to_upper(Cs)];
486to_upper([]) ->
487    [].
488
489%% to_lower
490
491to_lower([C|Cs]) when C >= $A, C =< $Z ->
492    [C+($a-$A)|to_lower(Cs)];
493to_lower([C|Cs]) ->
494    [C|to_lower(Cs)];
495to_lower([]) ->
496    [].
497
498
499%% strip
500strip(Value)->
501    lists:reverse(remove_ws(lists:reverse(remove_ws(Value)))).
502
503remove_ws([$\s|Rest])->
504    remove_ws(Rest);
505remove_ws([$\t|Rest]) ->
506    remove_ws(Rest);
507remove_ws(Rest) ->
508    Rest.
509
510%% split
511
512split(String,RegExp,Limit) ->
513    case regexp:parse(RegExp) of
514	{error,Reason} ->
515	    {error,Reason};
516	{ok,_} ->
517	    {ok,do_split(String,RegExp,Limit)}
518    end.
519
520do_split(String,RegExp,1) ->
521    [String];
522
523do_split(String,RegExp,Limit) ->
524    case regexp:first_match(String,RegExp) of
525	{match,Start,Length} ->
526	    [string:substr(String,1,Start-1)|
527	     do_split(lists:nthtail(Start+Length-1,String),RegExp,Limit-1)];
528	nomatch ->
529	    [String]
530    end.
531
532%% header
533header(StatusCode,Date)when list(Date)->
534    header(StatusCode,"text/plain",false);
535
536header(StatusCode, PersistentConnection) when integer(StatusCode)->
537    Date = rfc1123_date(),
538    Connection =
539	case PersistentConnection of
540	    true ->
541		"";
542	    _ ->
543		"Connection: close \r\n"
544	end,
545    io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n~s",
546		  [StatusCode, httpd_util:reason_phrase(StatusCode),
547		   Date, ?SERVER_SOFTWARE, Connection]).
548
549%%----------------------------------------------------------------------
550
551header(StatusCode, MimeType, Date) when list(Date) ->
552    header(StatusCode, MimeType, false,rfc1123_date());
553
554
555header(StatusCode, MimeType, PersistentConnection) when integer(StatusCode) ->
556    header(StatusCode, MimeType, PersistentConnection,rfc1123_date()).
557
558
559%%----------------------------------------------------------------------
560
561header(416, MimeType,PersistentConnection,Date)->
562    Connection =
563	case PersistentConnection of
564	    true ->
565		"";
566	    _ ->
567		"Connection: close \r\n"
568	end,
569    io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n"
570		  "Content-Range:bytes *"
571		  "Content-Type: ~s\r\n~s",
572		  [416, httpd_util:reason_phrase(416),
573		   Date, ?SERVER_SOFTWARE, MimeType, Connection]);
574
575
576header(StatusCode, MimeType,PersistentConnection,Date) when integer(StatusCode)->
577    Connection =
578	case PersistentConnection of
579	    true ->
580		"";
581	    _ ->
582		"Connection: close \r\n"
583	end,
584    io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n"
585		  "Content-Type: ~s\r\n~s",
586		  [StatusCode, httpd_util:reason_phrase(StatusCode),
587		   Date, ?SERVER_SOFTWARE, MimeType, Connection]).
588
589
590
591%% make_name/2, make_name/3
592%% Prefix  -> string()
593%%            First part of the name, e.g. "httpd"
594%% Addr    -> {A,B,C,D} | string() | undefined
595%%            The address part of the name.
596%%            e.g. "123.234.55.66" or {123,234,55,66} or "otp.ericsson.se"
597%%            for a host address or undefined if local host.
598%% Port    -> integer()
599%%            Last part of the name, such as the HTTPD server port
600%%            number (80).
601%% Postfix -> Any string that will be added last to the name
602%%
603%% Example:
604%% make_name("httpd","otp.ericsson.se",80) => httpd__otp_ericsson_se__80
605%% make_name("httpd",undefined,8088)       => httpd_8088
606
607make_name(Prefix,Port) ->
608    make_name(Prefix,undefined,Port,"").
609
610make_name(Prefix,Addr,Port) ->
611    make_name(Prefix,Addr,Port,"").
612
613make_name(Prefix,"*",Port,Postfix) ->
614    make_name(Prefix,undefined,Port,Postfix);
615
616make_name(Prefix,any,Port,Postfix) ->
617    make_name1(io_lib:format("~s_~w~s",[Prefix,Port,Postfix]));
618
619make_name(Prefix,undefined,Port,Postfix) ->
620    make_name1(io_lib:format("~s_~w~s",[Prefix,Port,Postfix]));
621
622make_name(Prefix,Addr,Port,Postfix) ->
623    NameString =
624        Prefix ++ "__" ++ make_name2(Addr) ++ "__" ++
625	integer_to_list(Port) ++ Postfix,
626    make_name1(NameString).
627
628make_name1(String) ->
629    list_to_atom(lists:flatten(String)).
630
631make_name2({A,B,C,D}) ->
632    io_lib:format("~w_~w_~w_~w",[A,B,C,D]);
633make_name2(Addr) ->
634    search_and_replace(Addr,$.,$_).
635
636search_and_replace(S,A,B) ->
637    Fun = fun(What) ->
638                  case What of
639                      A -> B;
640                      O -> O
641                  end
642          end,
643    lists:map(Fun,S).
644
645
646
647%%----------------------------------------------------------------------
648%% Converts  a string that constists of 0-9,A-F,a-f to a
649%% integer
650%%----------------------------------------------------------------------
651
652hexlist_to_integer([])->
653    empty;
654
655
656%%When the string only contains one value its eaasy done.
657%% 0-9
658hexlist_to_integer([Size]) when Size>=48 , Size=<57 ->
659   Size-48;
660%% A-F
661hexlist_to_integer([Size]) when Size>=65 , Size=<70 ->
662    Size-55;
663%% a-f
664hexlist_to_integer([Size]) when Size>=97 , Size=<102 ->
665    Size-87;
666hexlist_to_integer([Size]) ->
667    not_a_num;
668
669hexlist_to_integer(Size) ->
670    Len=string:span(Size,"1234567890abcdefABCDEF"),
671    hexlist_to_integer2(Size,16 bsl (4 *(Len-2)),0).
672
673hexlist_to_integer2([],_Pos,Sum)->
674    Sum;
675hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=48,HexVal=<57->
676    hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-48)*Pos));
677
678hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=65,HexVal=<70->
679    hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-55)*Pos));
680
681hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=97,HexVal=<102->
682    hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-87)*Pos));
683
684hexlist_to_integer2(_AfterHexString,_Pos,Sum)->
685    Sum.
686
687%%----------------------------------------------------------------------
688%%Converts an integer to an hexlist
689%%----------------------------------------------------------------------
690encode_hex(Num)->
691    integer_to_hexlist(Num).
692
693
694integer_to_hexlist(Num)->
695    integer_to_hexlist(Num,getSize(Num),[]).
696
697integer_to_hexlist(Num,Pot,Res) when Pot<0 ->
698    convert_to_ascii([Num|Res]);
699
700integer_to_hexlist(Num,Pot,Res) ->
701    Position=(16 bsl (Pot*4)),
702    PosVal=Num div Position,
703    integer_to_hexlist(Num-(PosVal*Position),Pot-1,[PosVal|Res]).
704convert_to_ascii(RevesedNum)->
705    convert_to_ascii(RevesedNum,[]).
706
707convert_to_ascii([],Num)->
708    Num;
709convert_to_ascii([Num|Reversed],Number)when Num>-1, Num<10 ->
710    convert_to_ascii(Reversed,[Num+48|Number]);
711convert_to_ascii([Num|Reversed],Number)when Num>9, Num<16 ->
712    convert_to_ascii(Reversed,[Num+55|Number]);
713convert_to_ascii(NumReversed,Number) ->
714    error.
715
716
717
718getSize(Num)->
719    getSize(Num,0).
720
721getSize(Num,Pot)when Num<(16 bsl(Pot *4))  ->
722    Pot-1;
723
724getSize(Num,Pot) ->
725    getSize(Num,Pot+1).
726
727
728
729
730
731create_etag(FileInfo)->
732    create_etag(FileInfo#file_info.mtime,FileInfo#file_info.size).
733
734create_etag({{Year,Month,Day},{Hour,Min,Sec}},Size)->
735    create_part([Year,Month,Day,Hour,Min,Sec])++io_lib:write(Size);
736
737create_etag(FileInfo,Size)->
738    create_etag(FileInfo#file_info.mtime,Size).
739
740create_part(Values)->
741    lists:map(fun(Val0)->
742		      Val=Val0 rem 60,
743			  if
744			      Val=<25 ->
745				  65+Val;  % A-Z
746			      Val=<50 ->
747				  72+Val;  % a-z
748			      %%Since no date s
749			      true ->
750				  Val-3
751			  end
752	      end,Values).
753
754
755
756%%----------------------------------------------------------------------
757%%Function that controls whether a response is generated or not
758%%----------------------------------------------------------------------
759response_generated(Info)->
760    case httpd_util:key1search(Info#mod.data,status) of
761	%% A status code has been generated!
762	{StatusCode,PhraseArgs,Reason}->
763	    true;
764	%%No status code control repsonsxe
765	undefined ->
766	    case httpd_util:key1search(Info#mod.data, response) of
767		%% No response has been generated!
768		undefined ->
769		    false;
770		%% A response has been generated or sent!
771		Response ->
772		    true
773	    end
774    end.
775