Line 0
Link Here
|
|
|
1 |
%%%---------------------------------------------------------------------- |
2 |
%%% File : mod_presence.erl |
3 |
%%% Author : Igor Goryachev <igor@goryachev.org> |
4 |
%%% Purpose : Module for showing presences via web |
5 |
%%% Created : 30 Apr 2006 by Igor Goryachev <igor@goryachev.org> |
6 |
%%% Id : $Id$ |
7 |
%%%---------------------------------------------------------------------- |
8 |
|
9 |
-module(mod_presence). |
10 |
-author('igor@goryachev.org'). |
11 |
-vsn('$Revision$'). |
12 |
|
13 |
-behaviour(gen_server). |
14 |
-behaviour(gen_mod). |
15 |
|
16 |
%% API |
17 |
-export([start_link/2, |
18 |
start/2, |
19 |
stop/1, |
20 |
get_info/2, |
21 |
show_presence/1]). |
22 |
|
23 |
%% gen_server callbacks |
24 |
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, |
25 |
terminate/2, code_change/3]). |
26 |
|
27 |
-include("ejabberd.hrl"). |
28 |
-include("jlib.hrl"). |
29 |
|
30 |
-record(presence_registered, {us_host, xml, icon}). |
31 |
-record(state, {host, server_host, access}). |
32 |
-record(session, {sid, usr, us, priority}). |
33 |
-record(presence, {resource, status, priority, text}). |
34 |
|
35 |
-define(PROCNAME, ejabberd_mod_presence). |
36 |
-define(SERVICE_NAME(Host), "presence." ++ Host). |
37 |
|
38 |
-define(PIXMAPS_DIR, "pixmaps"). |
39 |
|
40 |
|
41 |
%%==================================================================== |
42 |
%% API |
43 |
%%==================================================================== |
44 |
%%-------------------------------------------------------------------- |
45 |
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} |
46 |
%% Description: Starts the server |
47 |
%%-------------------------------------------------------------------- |
48 |
start_link(Host, Opts) -> |
49 |
Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
50 |
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). |
51 |
|
52 |
start(Host, Opts) -> |
53 |
Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
54 |
ChildSpec = |
55 |
{Proc, |
56 |
{?MODULE, start_link, [Host, Opts]}, |
57 |
temporary, |
58 |
1000, |
59 |
worker, |
60 |
[?MODULE]}, |
61 |
Dir = |
62 |
case os:getenv("EJABBERD_PIXMAPS_PATH") of |
63 |
false -> |
64 |
case code:priv_dir(ejabberd) of |
65 |
{error, _} -> |
66 |
?PIXMAPS_DIR; |
67 |
Path -> |
68 |
filename:join([Path, ?PIXMAPS_DIR]) |
69 |
end; |
70 |
Path -> |
71 |
Path |
72 |
end, |
73 |
ets:new(pixmaps_dirs, [named_table, public]), |
74 |
ets:insert(pixmaps_dirs, {directory, Dir}), |
75 |
supervisor:start_child(ejabberd_sup, ChildSpec). |
76 |
|
77 |
stop(Host) -> |
78 |
Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
79 |
gen_server:call(Proc, stop), |
80 |
supervisor:stop_child(ejabberd_sup, Proc). |
81 |
|
82 |
%%==================================================================== |
83 |
%% gen_server callbacks |
84 |
%%==================================================================== |
85 |
|
86 |
%%-------------------------------------------------------------------- |
87 |
%% Function: init(Args) -> {ok, State} | |
88 |
%% {ok, State, Timeout} | |
89 |
%% ignore | |
90 |
%% {stop, Reason} |
91 |
%% Description: Initiates the server |
92 |
%%-------------------------------------------------------------------- |
93 |
init([Host, Opts]) -> |
94 |
mnesia:create_table(presence_registered, |
95 |
[{disc_copies, [node()]}, |
96 |
{attributes, record_info(fields, presence_registered)}]), |
97 |
MyHost = gen_mod:get_opt(host, Opts, ?SERVICE_NAME(Host)), |
98 |
mnesia:add_table_index(presence_registered, xml), |
99 |
mnesia:add_table_index(presence_registered, icon), |
100 |
Access = gen_mod:get_opt(access, Opts, all), |
101 |
AccessCreate = gen_mod:get_opt(access_create, Opts, all), |
102 |
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none), |
103 |
ejabberd_router:register_route(MyHost), |
104 |
{ok, #state{host = MyHost, |
105 |
server_host = Host, |
106 |
access = {Access, AccessCreate, AccessAdmin}}}. |
107 |
|
108 |
%%-------------------------------------------------------------------- |
109 |
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | |
110 |
%% {reply, Reply, State, Timeout} | |
111 |
%% {noreply, State} | |
112 |
%% {noreply, State, Timeout} | |
113 |
%% {stop, Reason, Reply, State} | |
114 |
%% {stop, Reason, State} |
115 |
%% Description: Handling call messages |
116 |
%%-------------------------------------------------------------------- |
117 |
handle_call(stop, _From, State) -> |
118 |
{stop, normal, ok, State}. |
119 |
|
120 |
%%-------------------------------------------------------------------- |
121 |
%% Function: handle_cast(Msg, State) -> {noreply, State} | |
122 |
%% {noreply, State, Timeout} | |
123 |
%% {stop, Reason, State} |
124 |
%% Description: Handling cast messages |
125 |
%%-------------------------------------------------------------------- |
126 |
handle_cast(_Msg, State) -> |
127 |
{noreply, State}. |
128 |
|
129 |
%%-------------------------------------------------------------------- |
130 |
%% Function: handle_info(Info, State) -> {noreply, State} | |
131 |
%% {noreply, State, Timeout} | |
132 |
%% {stop, Reason, State} |
133 |
%% Description: Handling all non call/cast messages |
134 |
%%-------------------------------------------------------------------- |
135 |
handle_info({route, From, To, Packet}, |
136 |
#state{host = Host, |
137 |
server_host = ServerHost, |
138 |
access = Access} = State) -> |
139 |
case catch do_route(Host, ServerHost, Access, From, To, Packet) of |
140 |
{'EXIT', Reason} -> |
141 |
?ERROR_MSG("~p", [Reason]); |
142 |
_ -> |
143 |
ok |
144 |
end, |
145 |
{noreply, State}; |
146 |
handle_info(_Info, State) -> |
147 |
{noreply, State}. |
148 |
|
149 |
%%-------------------------------------------------------------------- |
150 |
%% Function: terminate(Reason, State) -> void() |
151 |
%% Description: This function is called by a gen_server when it is about to |
152 |
%% terminate. It should be the opposite of Module:init/1 and do any necessary |
153 |
%% cleaning up. When it returns, the gen_server terminates with Reason. |
154 |
%% The return value is ignored. |
155 |
%%-------------------------------------------------------------------- |
156 |
terminate(_Reason, State) -> |
157 |
ejabberd_router:unregister_route(State#state.host), |
158 |
ok. |
159 |
|
160 |
%%-------------------------------------------------------------------- |
161 |
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} |
162 |
%% Description: Convert process state when code is changed |
163 |
%%-------------------------------------------------------------------- |
164 |
code_change(_OldVsn, State, _Extra) -> |
165 |
{ok, State}. |
166 |
|
167 |
%%-------------------------------------------------------------------- |
168 |
%%% Internal functions |
169 |
%%-------------------------------------------------------------------- |
170 |
|
171 |
do_route(Host, ServerHost, Access, From, To, Packet) -> |
172 |
{AccessRoute, _AccessCreate, _AccessAdmin} = Access, |
173 |
case acl:match_rule(ServerHost, AccessRoute, From) of |
174 |
allow -> |
175 |
do_route1(Host, ServerHost, Access, From, To, Packet); |
176 |
_ -> |
177 |
{xmlelement, _Name, Attrs, _Els} = Packet, |
178 |
Lang = xml:get_attr_s("xml:lang", Attrs), |
179 |
ErrText = "Access denied by service policy", |
180 |
Err = jlib:make_error_reply(Packet, |
181 |
?ERRT_FORBIDDEN(Lang, ErrText)), |
182 |
ejabberd_router:route(To, From, Err) |
183 |
end. |
184 |
|
185 |
do_route1(Host, ServerHost, Access, From, To, Packet) -> |
186 |
{_AccessRoute, AccessCreate, AccessAdmin} = Access, |
187 |
{xmlelement, Name, Attrs, _Els} = Packet, |
188 |
case Name of |
189 |
"iq" -> |
190 |
case jlib:iq_query_info(Packet) of |
191 |
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, |
192 |
sub_el = _SubEl} = IQ -> |
193 |
Res = IQ#iq{type = result, |
194 |
sub_el = [{xmlelement, "query", |
195 |
[{"xmlns", XMLNS}], |
196 |
iq_disco_info()}]}, |
197 |
ejabberd_router:route(To, |
198 |
From, |
199 |
jlib:iq_to_xml(Res)); |
200 |
#iq{type = get, |
201 |
xmlns = ?NS_DISCO_ITEMS} = IQ -> |
202 |
ok; |
203 |
#iq{type = get, |
204 |
xmlns = ?NS_REGISTER = XMLNS, |
205 |
lang = Lang, |
206 |
sub_el = _SubEl} = IQ -> |
207 |
Res = IQ#iq{type = result, |
208 |
sub_el = |
209 |
[{xmlelement, "query", |
210 |
[{"xmlns", XMLNS}], |
211 |
iq_get_register_info( |
212 |
Host, From, Lang)}]}, |
213 |
ejabberd_router:route(To, |
214 |
From, |
215 |
jlib:iq_to_xml(Res)); |
216 |
#iq{type = set, |
217 |
xmlns = ?NS_REGISTER = XMLNS, |
218 |
lang = Lang, |
219 |
sub_el = SubEl} = IQ -> |
220 |
case process_iq_register_set(Host, From, SubEl, Lang) of |
221 |
{result, IQRes} -> |
222 |
Res = IQ#iq{type = result, |
223 |
sub_el = |
224 |
[{xmlelement, "query", |
225 |
[{"xmlns", XMLNS}], |
226 |
IQRes}]}, |
227 |
ejabberd_router:route( |
228 |
To, From, jlib:iq_to_xml(Res)); |
229 |
{error, Error} -> |
230 |
Err = jlib:make_error_reply( |
231 |
Packet, Error), |
232 |
ejabberd_router:route( |
233 |
To, From, Err) |
234 |
end; |
235 |
#iq{type = get, |
236 |
xmlns = ?NS_VCARD = XMLNS, |
237 |
lang = Lang, |
238 |
sub_el = _SubEl} = IQ -> |
239 |
Res = IQ#iq{type = result, |
240 |
sub_el = |
241 |
[{xmlelement, "vCard", |
242 |
[{"xmlns", XMLNS}], |
243 |
iq_get_vcard(Lang)}]}, |
244 |
ejabberd_router:route(To, |
245 |
From, |
246 |
jlib:iq_to_xml(Res)); |
247 |
#iq{} -> |
248 |
Err = jlib:make_error_reply( |
249 |
Packet, |
250 |
?ERR_FEATURE_NOT_IMPLEMENTED), |
251 |
ejabberd_router:route(To, From, Err); |
252 |
_ -> |
253 |
ok |
254 |
end; |
255 |
_ -> |
256 |
case xml:get_attr_s("type", Attrs) of |
257 |
"error" -> |
258 |
ok; |
259 |
"result" -> |
260 |
ok; |
261 |
_ -> |
262 |
Err = jlib:make_error_reply( |
263 |
Packet, ?ERR_ITEM_NOT_FOUND), |
264 |
ejabberd_router:route(To, From, Err) |
265 |
end |
266 |
end. |
267 |
|
268 |
iq_disco_info() -> |
269 |
[{xmlelement, "identity", |
270 |
[{"category", "presence"}, |
271 |
{"type", "text"}, |
272 |
{"name", "ejabberd/mod_presence"}], []}, |
273 |
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []}, |
274 |
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}]. |
275 |
|
276 |
-define(XFIELD(Type, Label, Var, Val), |
277 |
{xmlelement, "field", [{"type", Type}, |
278 |
{"label", translate:translate(Lang, Label)}, |
279 |
{"var", Var}], |
280 |
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}). |
281 |
|
282 |
iq_get_register_info(Host, From, Lang) -> |
283 |
{LUser, LServer, _} = jlib:jid_tolower(From), |
284 |
LUS = {LUser, LServer}, |
285 |
{XML, Icon, Registered} = |
286 |
case catch mnesia:dirty_read(presence_registered, {LUS, Host}) of |
287 |
{'EXIT', _Reason} -> |
288 |
{"false", "disabled", []}; |
289 |
[] -> |
290 |
{"false", "disabled", []}; |
291 |
[#presence_registered{xml = X, icon = I}] -> |
292 |
{X, I, [{xmlelement, "registered", [], []}]} |
293 |
end, |
294 |
Registered ++ |
295 |
[{xmlelement, "instructions", [], |
296 |
[{xmlcdata, |
297 |
translate:translate( |
298 |
Lang, "You need an x:data capable client to register presence")}]}, |
299 |
{xmlelement, "x", |
300 |
[{"xmlns", ?NS_XDATA}], |
301 |
[{xmlelement, "title", [], |
302 |
[{xmlcdata, |
303 |
translate:translate( |
304 |
Lang, "Presence registration at ") ++ Host}]}, |
305 |
{xmlelement, "instructions", [], |
306 |
[{xmlcdata, |
307 |
translate:translate( |
308 |
Lang, "What presence features do you want to register?")}]}, |
309 |
{xmlelement, "field", [{"type", "list-single"}, |
310 |
{"label", "Icon theme"}, |
311 |
{"var", "icon"}], |
312 |
[{xmlelement, "value", [], [{xmlcdata, Icon}]}, |
313 |
{xmlelement, "option", [{"label", "disabled"}], |
314 |
[{xmlelement, "value", [], [{xmlcdata, "disabled"}]}]} |
315 |
] ++ available_themes(xdata)}, |
316 |
?XFIELD("boolean", "Raw XML", "xml", XML)]}]. |
317 |
|
318 |
iq_set_register_info(Host, From, XML, Icon, Lang) -> |
319 |
{LUser, LServer, _} = jlib:jid_tolower(From), |
320 |
LUS = {LUser, LServer}, |
321 |
F = fun() -> |
322 |
case XML of |
323 |
"" -> |
324 |
mnesia:delete({presence_registered, {LUS, Host}}), |
325 |
ok; |
326 |
_ -> |
327 |
Allow = |
328 |
case mnesia:select( |
329 |
presence_registered, |
330 |
[{#presence_registered{us_host = '$1', |
331 |
xml = XML, |
332 |
icon = Icon, |
333 |
_ = '_'}, |
334 |
[{'==', {element, 2, '$1'}, Host}], |
335 |
['$_']}]) of |
336 |
[] -> |
337 |
true; |
338 |
[#presence_registered{us_host = {U, _Host}}] -> |
339 |
U == LUS |
340 |
end, |
341 |
if |
342 |
Allow -> |
343 |
mnesia:write( |
344 |
#presence_registered{us_host = {LUS, Host}, |
345 |
xml = XML, |
346 |
icon = Icon}), |
347 |
ok; |
348 |
true -> |
349 |
false |
350 |
end |
351 |
end |
352 |
end, |
353 |
case mnesia:transaction(F) of |
354 |
{atomic, ok} -> |
355 |
{result, []}; |
356 |
{atomic, false} -> |
357 |
ErrText = "Specified presence is already registered", |
358 |
{error, ?ERRT_CONFLICT(Lang, ErrText)}; |
359 |
_ -> |
360 |
{error, ?ERR_INTERNAL_SERVER_ERROR} |
361 |
end. |
362 |
|
363 |
process_iq_register_set(Host, From, SubEl, Lang) -> |
364 |
{xmlelement, _Name, _Attrs, Els} = SubEl, |
365 |
case xml:get_subtag(SubEl, "remove") of |
366 |
false -> |
367 |
case xml:remove_cdata(Els) of |
368 |
[{xmlelement, "x", _Attrs1, _Els1} = XEl] -> |
369 |
case {xml:get_tag_attr_s("xmlns", XEl), |
370 |
xml:get_tag_attr_s("type", XEl)} of |
371 |
{?NS_XDATA, "cancel"} -> |
372 |
{result, []}; |
373 |
{?NS_XDATA, "submit"} -> |
374 |
XData = jlib:parse_xdata_submit(XEl), |
375 |
case XData of |
376 |
invalid -> |
377 |
{error, ?ERR_BAD_REQUEST}; |
378 |
_ -> |
379 |
case lists:keysearch("xml", 1, XData) of |
380 |
false -> |
381 |
ErrText = "You must fill in field \"Xml\" in the form", |
382 |
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}; |
383 |
{value, {_, [XML]}} -> |
384 |
case lists:keysearch("icon", 1, XData) of |
385 |
false -> |
386 |
ErrText = "You must fill in field \"Icon\" in the form", |
387 |
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}; |
388 |
{value, {_, [Icon]}} -> |
389 |
iq_set_register_info(Host, From, XML, Icon, Lang) |
390 |
end |
391 |
end |
392 |
end; |
393 |
_ -> |
394 |
{error, ?ERR_BAD_REQUEST} |
395 |
end; |
396 |
_ -> |
397 |
{error, ?ERR_BAD_REQUEST} |
398 |
end; |
399 |
_ -> |
400 |
iq_set_register_info(Host, From, "false", "disabled", Lang) |
401 |
end. |
402 |
|
403 |
iq_get_vcard(Lang) -> |
404 |
[{xmlelement, "FN", [], |
405 |
[{xmlcdata, "ejabberd/mod_presence"}]}, |
406 |
{xmlelement, "URL", [], |
407 |
[{xmlcdata, |
408 |
"http://ejabberd.jabberstudio.org/"}]}, |
409 |
{xmlelement, "DESC", [], |
410 |
[{xmlcdata, translate:translate(Lang, "ejabberd presence module\n" |
411 |
"Copyright (c) 2006 Igor Goryachev")}]}]. |
412 |
|
413 |
get_info(LUser, LServer) -> |
414 |
LUS = {LUser, LServer}, |
415 |
case catch mnesia:dirty_read(presence_registered, {LUS, ?SERVICE_NAME(LServer)}) of |
416 |
{'EXIT', _Reason} -> |
417 |
{false, disabled}; |
418 |
[] -> |
419 |
{false, disabled}; |
420 |
[#presence_registered{xml = X, icon = I}] -> |
421 |
X1 = case X of |
422 |
"0" -> false; |
423 |
"1" -> true; |
424 |
_ -> list_to_atom(X) |
425 |
end, |
426 |
{X1, list_to_atom(I)} |
427 |
end. |
428 |
|
429 |
get_status_weight(Status) -> |
430 |
case Status of |
431 |
"chat" -> 0; |
432 |
"available" -> 1; |
433 |
"away" -> 2; |
434 |
"xa" -> 3; |
435 |
"dnd" -> 4; |
436 |
_ -> 9 |
437 |
end. |
438 |
|
439 |
|
440 |
get_presences({bare, LUser, LServer}) -> |
441 |
Resources = ejabberd_sm:get_user_resources(LUser, LServer), |
442 |
lists:map( |
443 |
fun(Resource) -> |
444 |
[Session] = mnesia:dirty_index_read(session, |
445 |
{LUser, LServer, Resource}, |
446 |
#session.usr), |
447 |
Pid = element(2, Session#session.sid), |
448 |
{_User, _Resource, Status, Text} = |
449 |
rpc:call(node(Pid), ejabberd_c2s, get_presence, [Pid]), |
450 |
Priority = Session#session.priority, |
451 |
#presence{resource = Resource, |
452 |
status = Status, |
453 |
priority = Priority, |
454 |
text = Text} |
455 |
end, |
456 |
Resources); |
457 |
get_presences({sorted, LUser, LServer}) -> |
458 |
lists:sort( |
459 |
fun(A, B) -> |
460 |
if |
461 |
A#presence.priority == B#presence.priority -> |
462 |
WA = get_status_weight(A#presence.status), |
463 |
WB = get_status_weight(B#presence.status), |
464 |
WA < WB; |
465 |
true -> |
466 |
A#presence.priority > B#presence.priority |
467 |
end |
468 |
end, |
469 |
get_presences({bare, LUser, LServer})); |
470 |
get_presences({xml, LUser, LServer}) -> |
471 |
{xmlelement, "presence", |
472 |
[{"user", LUser}, {"server", LServer}], |
473 |
lists:map( |
474 |
fun(Presence) -> |
475 |
{xmlelement, "resource", |
476 |
[{"name", Presence#presence.resource}, |
477 |
{"status", Presence#presence.status}, |
478 |
{"priority", integer_to_list(Presence#presence.priority)}], |
479 |
[{xmlcdata, Presence#presence.text}]} |
480 |
end, |
481 |
get_presences({sorted, LUser, LServer}))}; |
482 |
get_presences({status, LUser, LServer}) -> |
483 |
case get_presences({sorted, LUser, LServer}) of |
484 |
[Highest | _Rest] -> |
485 |
Highest#presence.status; |
486 |
_ -> |
487 |
"unavailable" |
488 |
end; |
489 |
get_presences(_) -> |
490 |
[]. |
491 |
|
492 |
-define(XML_HEADER, "<?xml version='1.0' encoding='utf-8'?>"). |
493 |
|
494 |
get_pixmaps_directory() -> |
495 |
[{directory, Path} | _] = ets:lookup(pixmaps_dirs, directory), |
496 |
Path. |
497 |
|
498 |
available_themes(list) -> |
499 |
case file:list_dir(get_pixmaps_directory()) of |
500 |
{ok, List} -> |
501 |
List; |
502 |
{error, _} -> |
503 |
[] |
504 |
end; |
505 |
available_themes(xdata) -> |
506 |
lists:map( |
507 |
fun(Theme) -> |
508 |
{xmlelement, "option", [{"label", Theme}], |
509 |
[{xmlelement, "value", [], [{xmlcdata, Theme}]}]} |
510 |
end, available_themes(list)); |
511 |
available_themes(_) -> |
512 |
[]. |
513 |
|
514 |
-define(XE(Name, Els), {xmlelement, Name, [], Els}). |
515 |
-define(C(Text), {xmlcdata, Text}). |
516 |
-define(XC(Name, Text), ?XE(Name, [?C(Text)])). |
517 |
|
518 |
show_presence({xml, LUser, LServer}) -> |
519 |
{XML, _Icon} = get_info(LUser, LServer), |
520 |
case XML of |
521 |
true -> |
522 |
{200, [{"Content-Type", "text/xml; charset=utf-8"}], |
523 |
?XML_HEADER ++ xml:element_to_string( |
524 |
get_presences({xml, LUser, LServer}))}; |
525 |
_ -> |
526 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])} |
527 |
end; |
528 |
show_presence({image, LUser, LServer}) -> |
529 |
{_XML, Icon} = get_info(LUser, LServer), |
530 |
case Icon of |
531 |
disabled -> |
532 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])}; |
533 |
_ -> |
534 |
show_presence({image_no_check, LUser, LServer, atom_to_list(Icon)}) |
535 |
end; |
536 |
show_presence({image, LUser, LServer, Theme}) -> |
537 |
{_XML, Icon} = get_info(LUser, LServer), |
538 |
case Icon of |
539 |
disabled -> |
540 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])}; |
541 |
_ -> |
542 |
show_presence({image_no_check, LUser, LServer, Theme}) |
543 |
end; |
544 |
show_presence({image_no_check, LUser, LServer, Theme}) -> |
545 |
case lists:member(Theme, available_themes(list)) of |
546 |
true -> |
547 |
case filelib:wildcard( |
548 |
filename:join([get_pixmaps_directory(), Theme, |
549 |
get_presences( |
550 |
{status, LUser, LServer}) ++ ".{gif,png,jpg}"])) of |
551 |
[First | _Rest] -> |
552 |
CT = case string:substr(First, string:len(First) - 2, 3) of |
553 |
"gif" -> "gif"; |
554 |
"png" -> "png"; |
555 |
"jpg" -> "jpeg" |
556 |
end, |
557 |
case file:read_file(First) of |
558 |
{ok, Content} -> |
559 |
{200, [{"Content-Type", "image/" ++ CT}], |
560 |
binary_to_list(Content)}; |
561 |
_ -> |
562 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])} |
563 |
end; |
564 |
_ -> |
565 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])} |
566 |
end; |
567 |
false -> |
568 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])} |
569 |
end; |
570 |
show_presence(_) -> |
571 |
{404, [], ejabberd_web:make_xhtml([?XC("h1", "Not found")])}. |
572 |
|