aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2009-07-21 17:31:09 +0000
committerBadlop <badlop@process-one.net>2009-07-21 17:31:09 +0000
commit5346a7df024cabddf251174ad0c04b667e715ffe (patch)
tree7bc98d1da0d39ec90bce43a6a2c7bcb063da4d8c
parentfix get_entity_subscriptions result match (diff)
Allow content types to be configured in ejabberd.cfg (EJAB-975)(thanks to Brian Cully)
SVN Revision: 2376
-rw-r--r--doc/guide.html19
-rw-r--r--doc/guide.tex19
-rw-r--r--src/web/mod_http_fileserver.erl91
3 files changed, 95 insertions, 34 deletions
diff --git a/doc/guide.html b/doc/guide.html
index 2d1fb8eaa..13e104e96 100644
--- a/doc/guide.html
+++ b/doc/guide.html
@@ -2111,17 +2111,32 @@ Indicate one or more directory index files, similarly to Apache&#X2019;s
DirectoryIndex variable. When a web request hits a directory
instead of a regular file, those directory indices are looked in
order, and the first one found is returned.
+</DD><DT CLASS="dt-description"><B><TT>content_types</TT></B></DT><DD CLASS="dd-description">
+Specify a mapping of extensions to content types.
+There are several content types already defined,
+with this option you can add new definitions, modify or delete existing ones.
+To delete an existing definition, simply define it with a value: &#X2018;undefined&#X2019;.
+</DD><DT CLASS="dt-description"><B><TT>default_content_type</TT></B></DT><DD CLASS="dd-description">
+Specify the content type to use for unknown extensions.
+Default value is &#X2018;application/octet-stream&#X2019;.
</DD></DL><P>This example configuration will serve the files from
the local directory <CODE>/var/www</CODE>
in the address <CODE>http://example.org:5280/pub/archive/</CODE>.
+In this example a new content type <TT>ogg</TT> is defined,
+<TT>png</TT> is redefined, and <TT>jpg</TT> definition is deleted.
To use this module you must enable it:
</P><PRE CLASS="verbatim">{modules,
[
...
{mod_http_fileserver, [
{docroot, "/var/www"},
- {directory_indices, ["index.html", "main.htm"]},
- {accesslog, "/var/log/ejabberd/access.log"}
+ {accesslog, "/var/log/ejabberd/access.log"},
+ {directory_indices, ["index.html", "main.htm"]},
+ {content_types, [{".ogg", "audio/ogg"},
+ {".png", "image/png"},
+ {".jpg", undefined}
+ ]},
+ {default_content_type, "text/html"}
]
},
...
diff --git a/doc/guide.tex b/doc/guide.tex
index 7c8466eed..ffbc24181 100644
--- a/doc/guide.tex
+++ b/doc/guide.tex
@@ -2788,11 +2788,21 @@ Options:
DirectoryIndex variable. When a web request hits a directory
instead of a regular file, those directory indices are looked in
order, and the first one found is returned.
+ \titem{content\_types} \ind{options!contenttypes}
+ Specify a mapping of extensions to content types.
+ There are several content types already defined,
+ with this option you can add new definitions, modify or delete existing ones.
+ To delete an existing definition, simply define it with a value: `undefined'.
+ \titem{default\_content\_type} \ind{options!defaultcontenttype}
+ Specify the content type to use for unknown extensions.
+ Default value is `application/octet-stream'.
\end{description}
This example configuration will serve the files from
the local directory \verb|/var/www|
in the address \verb|http://example.org:5280/pub/archive/|.
+In this example a new content type \term{ogg} is defined,
+\term{png} is redefined, and \term{jpg} definition is deleted.
To use this module you must enable it:
\begin{verbatim}
{modules,
@@ -2800,8 +2810,13 @@ To use this module you must enable it:
...
{mod_http_fileserver, [
{docroot, "/var/www"},
- {directory_indices, ["index.html", "main.htm"]},
- {accesslog, "/var/log/ejabberd/access.log"}
+ {accesslog, "/var/log/ejabberd/access.log"},
+ {directory_indices, ["index.html", "main.htm"]},
+ {content_types, [{".ogg", "audio/ogg"},
+ {".png", "image/png"},
+ {".jpg", undefined}
+ ]},
+ {default_content_type, "text/html"}
]
},
...
diff --git a/src/web/mod_http_fileserver.erl b/src/web/mod_http_fileserver.erl
index 5d5eebb7f..21dd6fddf 100644
--- a/src/web/mod_http_fileserver.erl
+++ b/src/web/mod_http_fileserver.erl
@@ -72,7 +72,8 @@
-define(STRING2LOWER, httpd_util).
-endif.
--record(state, {host, docroot, accesslog, accesslogfd, directory_indices}).
+-record(state, {host, docroot, accesslog, accesslogfd, directory_indices,
+ default_content_type, content_types = []}).
-define(PROCNAME, ejabberd_mod_http_fileserver).
@@ -80,6 +81,19 @@
-define(HTTP_ERR_FILE_NOT_FOUND, {-1, 404, [], "Not found"}).
-define(HTTP_ERR_FORBIDDEN, {-1, 403, [], "Forbidden"}).
+-define(DEFAULT_CONTENT_TYPE, "application/octet-stream").
+-define(DEFAULT_CONTENT_TYPES, [{".css", "text/css"},
+ {".gif", "image/gif"},
+ {".html", "text/html"},
+ {".jar", "application/java-archive"},
+ {".jpeg", "image/jpeg"},
+ {".jpg", "image/jpeg"},
+ {".js", "text/javascript"},
+ {".png", "image/png"},
+ {".txt", "text/plain"},
+ {".xpi", "application/x-xpinstall"},
+ {".xul", "application/vnd.mozilla.xul+xml"}]).
+
-compile(export_all).
%%====================================================================
@@ -91,7 +105,7 @@ start(Host, Opts) ->
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
- temporary,
+ transient, % if process crashes abruptly, it gets restarted
1000,
worker,
[?MODULE]},
@@ -126,12 +140,15 @@ start_link(Host, Opts) ->
%%--------------------------------------------------------------------
init([Host, Opts]) ->
try initialize(Host, Opts) of
- {DocRoot, AccessLog, AccessLogFD, DirectoryIndices} ->
+ {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
+ DefaultContentType, ContentTypes} ->
{ok, #state{host = Host,
accesslog = AccessLog,
accesslogfd = AccessLogFD,
docroot = DocRoot,
- directory_indices = DirectoryIndices}}
+ directory_indices = DirectoryIndices,
+ default_content_type = DefaultContentType,
+ content_types = ContentTypes}}
catch
throw:Reason ->
{stop, Reason}
@@ -146,7 +163,23 @@ initialize(Host, Opts) ->
AccessLog = gen_mod:get_opt(accesslog, Opts, undefined),
AccessLogFD = try_open_log(AccessLog, Host),
DirectoryIndices = gen_mod:get_opt(directory_indices, Opts, []),
- {DocRoot, AccessLog, AccessLogFD, DirectoryIndices}.
+ DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
+ ?DEFAULT_CONTENT_TYPE),
+ ContentTypes = build_list_content_types(gen_mod:get_opt(content_types, Opts, []), ?DEFAULT_CONTENT_TYPES),
+ ?INFO_MSG("initialize: ~n ~p", [ContentTypes]),%+++
+ {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
+ DefaultContentType, ContentTypes}.
+
+%% @spec (AdminCTs::[CT], Default::[CT]) -> [CT]
+%% where CT = {Extension::string(), Value}
+%% Value = string() | undefined
+%% Returns a unified list without duplicates where elements of AdminCTs have more priority.
+%% If a CT is declared as 'undefined', then it is not included in the result.
+build_list_content_types(AdminCTsUnsorted, DefaultCTsUnsorted) ->
+ AdminCTs = lists:ukeysort(1, AdminCTsUnsorted),
+ DefaultCTs = lists:ukeysort(1, DefaultCTsUnsorted),
+ CTsUnfiltered = lists:ukeymerge(1, AdminCTs, DefaultCTs),
+ [{Extension, Value} || {Extension, Value} <- CTsUnfiltered, Value /= undefined].
check_docroot_defined(DocRoot, Host) ->
case DocRoot of
@@ -196,7 +229,8 @@ try_open_log(FN, Host) ->
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({serve, LocalPath}, _From, State) ->
- Reply = serve(LocalPath, State#state.docroot, State#state.directory_indices),
+ Reply = serve(LocalPath, State#state.docroot, State#state.directory_indices,
+ State#state.default_content_type, State#state.content_types),
{reply, Reply, State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
@@ -263,36 +297,42 @@ process(LocalPath, Request) ->
ejabberd_web:error(not_found)
end.
-serve(LocalPath, DocRoot, DirectoryIndices) ->
+serve(LocalPath, DocRoot, DirectoryIndices, DefaultContentType, ContentTypes) ->
FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
case file:read_file_info(FileName) of
{error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND;
{error, eacces} -> ?HTTP_ERR_FORBIDDEN;
- {ok, #file_info{type = directory}} -> serve_index(FileName, DirectoryIndices);
- {ok, FileInfo} -> serve_file(FileInfo, FileName)
+ {ok, #file_info{type = directory}} -> serve_index(FileName,
+ DirectoryIndices,
+ DefaultContentType,
+ ContentTypes);
+ {ok, FileInfo} -> serve_file(FileInfo, FileName,
+ DefaultContentType,
+ ContentTypes)
end.
%% Troll through the directory indices attempting to find one which
%% works, if none can be found, return a 404.
-serve_index(_FileName, []) ->
+serve_index(_FileName, [], _DefaultContentType, _ContentTypes) ->
?HTTP_ERR_FILE_NOT_FOUND;
-serve_index(FileName, [Index | T]) ->
+serve_index(FileName, [Index | T], DefaultContentType, ContentTypes) ->
IndexFileName = filename:join([FileName] ++ [Index]),
case file:read_file_info(IndexFileName) of
- {error, _Error} -> serve_index(FileName, T);
- {ok, #file_info{type = directory}} -> serve_index(FileName, T);
- {ok, FileInfo} -> serve_file(FileInfo, IndexFileName)
+ {error, _Error} -> serve_index(FileName, T, DefaultContentType, ContentTypes);
+ {ok, #file_info{type = directory}} -> serve_index(FileName, T, DefaultContentType, ContentTypes);
+ {ok, FileInfo} -> serve_file(FileInfo, IndexFileName, DefaultContentType, ContentTypes)
end.
%% Assume the file exists if we got this far and attempt to read it in
%% and serve it up.
-serve_file(FileInfo, FileName) ->
+serve_file(FileInfo, FileName, DefaultContentType, ContentTypes) ->
?DEBUG("Delivering: ~s", [FileName]),
{ok, FileContents} = file:read_file(FileName),
+ ContentType = content_type(FileName, DefaultContentType, ContentTypes),
{FileInfo#file_info.size,
200, [{"Server", "ejabberd"},
{"Last-Modified", last_modified(FileInfo)},
- {"Content-Type", content_type(FileName)}],
+ {"Content-Type", ContentType}],
FileContents}.
%%----------------------------------------------------------------------
@@ -369,20 +409,11 @@ join([E], _) ->
join([H | T], Separator) ->
lists:foldl(fun(E, Acc) -> lists:concat([Acc, Separator, E]) end, H, T).
-content_type(Filename) ->
- case ?STRING2LOWER:to_lower(filename:extension(Filename)) of
- ".jpg" -> "image/jpeg";
- ".jpeg" -> "image/jpeg";
- ".gif" -> "image/gif";
- ".png" -> "image/png";
- ".html" -> "text/html";
- ".css" -> "text/css";
- ".txt" -> "text/plain";
- ".xul" -> "application/vnd.mozilla.xul+xml";
- ".jar" -> "application/java-archive";
- ".xpi" -> "application/x-xpinstall";
- ".js" -> "application/x-javascript";
- _Else -> "application/octet-stream"
+content_type(Filename, DefaultContentType, ContentTypes) ->
+ Extension = ?STRING2LOWER:to_lower(filename:extension(Filename)),
+ case lists:keysearch(Extension, 1, ContentTypes) of
+ {value, {_, ContentType}} -> ContentType;
+ false -> DefaultContentType
end.
last_modified(FileInfo) ->