1 |
slords |
1.1 |
Binary files ejabberd-2.0.4/src/.DS_Store and ejabberd-2.0.4-new/src/.DS_Store differ |
2 |
|
|
diff -urN ejabberd-2.0.4/src/ejabberd_captcha.erl ejabberd-2.0.4-new/src/ejabberd_captcha.erl |
3 |
|
|
--- ejabberd-2.0.4/src/ejabberd_captcha.erl 1970-01-01 01:00:00.000000000 +0100 |
4 |
|
|
+++ ejabberd-2.0.4-new/src/ejabberd_captcha.erl 2009-03-14 07:27:05.000000000 +0100 |
5 |
|
|
@@ -0,0 +1,312 @@ |
6 |
|
|
+%%%------------------------------------------------------------------- |
7 |
|
|
+%%% File : ejabberd_captcha.erl |
8 |
|
|
+%%% Author : Evgeniy Khramtsov <xramtsov@gmail.com> |
9 |
|
|
+%%% Description : CAPTCHA processing. |
10 |
|
|
+%%% |
11 |
|
|
+%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com> |
12 |
|
|
+%%%------------------------------------------------------------------- |
13 |
|
|
+-module(ejabberd_captcha). |
14 |
|
|
+ |
15 |
|
|
+-behaviour(gen_server). |
16 |
|
|
+ |
17 |
|
|
+%% API |
18 |
|
|
+-export([start_link/0]). |
19 |
|
|
+ |
20 |
|
|
+%% gen_server callbacks |
21 |
|
|
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, |
22 |
|
|
+ terminate/2, code_change/3]). |
23 |
|
|
+ |
24 |
|
|
+-export([create_captcha/6, process_reply/1, process/2]). |
25 |
|
|
+ |
26 |
|
|
+-include("jlib.hrl"). |
27 |
|
|
+-include("ejabberd.hrl"). |
28 |
|
|
+-include("web/ejabberd_http.hrl"). |
29 |
|
|
+ |
30 |
|
|
+-define(VFIELD(Type, Var, Value), |
31 |
|
|
+ {xmlelement, "field", [{"type", Type}, {"var", Var}], |
32 |
|
|
+ [{xmlelement, "value", [], [Value]}]}). |
33 |
|
|
+ |
34 |
|
|
+-define(CAPTCHA_BODY(Lang, Room, URL), |
35 |
|
|
+ translate:translate(Lang, "Your messages to ") ++ Room |
36 |
|
|
+ ++ translate:translate(Lang, " are being blocked. To unblock them, visit ") |
37 |
|
|
+ ++ URL). |
38 |
|
|
+ |
39 |
|
|
+-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")). |
40 |
|
|
+-define(CAPTCHA_LIFETIME, 120000). % two minutes |
41 |
|
|
+ |
42 |
|
|
+-record(state, {}). |
43 |
|
|
+-record(captcha, {id, pid, key, tref, args}). |
44 |
|
|
+ |
45 |
|
|
+-define(T(S), |
46 |
|
|
+ case catch mnesia:transaction(fun() -> S end) of |
47 |
|
|
+ {atomic, Res} -> |
48 |
|
|
+ Res; |
49 |
|
|
+ {_, Reason} -> |
50 |
|
|
+ ?ERROR_MSG("mnesia transaction failed: ~p", [Reason]), |
51 |
|
|
+ {error, Reason} |
52 |
|
|
+ end). |
53 |
|
|
+ |
54 |
|
|
+%%==================================================================== |
55 |
|
|
+%% API |
56 |
|
|
+%%==================================================================== |
57 |
|
|
+%%-------------------------------------------------------------------- |
58 |
|
|
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} |
59 |
|
|
+%% Description: Starts the server |
60 |
|
|
+%%-------------------------------------------------------------------- |
61 |
|
|
+start_link() -> |
62 |
|
|
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). |
63 |
|
|
+ |
64 |
|
|
+create_captcha(Id, SID, From, To, Lang, Args) |
65 |
|
|
+ when is_list(Id), is_list(Lang), is_list(SID), |
66 |
|
|
+ is_record(From, jid), is_record(To, jid) -> |
67 |
|
|
+ case create_image() of |
68 |
|
|
+ {ok, Type, Key, Image} -> |
69 |
|
|
+ B64Image = jlib:encode_base64(binary_to_list(Image)), |
70 |
|
|
+ JID = jlib:jid_to_string(From), |
71 |
|
|
+ CID = "sha1+" ++ sha:sha(Image) ++ "@bob.xmpp.org", |
72 |
|
|
+ Data = {xmlelement, "data", |
73 |
|
|
+ [{"xmlns", ?NS_BOB}, {"cid", CID}, |
74 |
|
|
+ {"max-age", "0"}, {"type", Type}], |
75 |
|
|
+ [{xmlcdata, B64Image}]}, |
76 |
|
|
+ Captcha = |
77 |
|
|
+ {xmlelement, "captcha", [{"xmlns", ?NS_CAPTCHA}], |
78 |
|
|
+ [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], |
79 |
|
|
+ [?VFIELD("hidden", "FORM_TYPE", {xmlcdata, ?NS_CAPTCHA}), |
80 |
|
|
+ ?VFIELD("hidden", "from", {xmlcdata, jlib:jid_to_string(To)}), |
81 |
|
|
+ ?VFIELD("hidden", "challenge", {xmlcdata, Id}), |
82 |
|
|
+ ?VFIELD("hidden", "sid", {xmlcdata, SID}), |
83 |
|
|
+ {xmlelement, "field", [{"var", "ocr"}], |
84 |
|
|
+ [{xmlelement, "media", [{"xmlns", ?NS_MEDIA}], |
85 |
|
|
+ [{xmlelement, "uri", [{"type", Type}], |
86 |
|
|
+ [{xmlcdata, "cid:" ++ CID}]}]}]}]}]}, |
87 |
|
|
+ Body = {xmlelement, "body", [], |
88 |
|
|
+ [{xmlcdata, ?CAPTCHA_BODY(Lang, JID, get_url(Id))}]}, |
89 |
|
|
+ OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}], |
90 |
|
|
+ [{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]}, |
91 |
|
|
+ Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), |
92 |
|
|
+ ?T(mnesia:write(#captcha{id=Id, pid=self(), key=Key, |
93 |
|
|
+ tref=Tref, args=Args})), |
94 |
|
|
+ {ok, [Body, OOB, Captcha, Data]}; |
95 |
|
|
+ _Err -> |
96 |
|
|
+ error |
97 |
|
|
+ end. |
98 |
|
|
+ |
99 |
|
|
+process_reply({xmlelement, "captcha", _, _} = El) -> |
100 |
|
|
+ case xml:get_subtag(El, "x") of |
101 |
|
|
+ false -> |
102 |
|
|
+ {error, malformed}; |
103 |
|
|
+ Xdata -> |
104 |
|
|
+ Fields = jlib:parse_xdata_submit(Xdata), |
105 |
|
|
+ [Id | _] = proplists:get_value("challenge", Fields, [none]), |
106 |
|
|
+ [OCR | _] = proplists:get_value("ocr", Fields, [none]), |
107 |
|
|
+ ?T(case mnesia:read(captcha, Id, write) of |
108 |
|
|
+ [#captcha{pid=Pid, args=Args, key=Key, tref=Tref}] -> |
109 |
|
|
+ mnesia:delete({captcha, Id}), |
110 |
|
|
+ erlang:cancel_timer(Tref), |
111 |
|
|
+ if OCR == Key -> |
112 |
|
|
+ Pid ! {captcha_succeed, Args}, |
113 |
|
|
+ ok; |
114 |
|
|
+ true -> |
115 |
|
|
+ Pid ! {captcha_failed, Args}, |
116 |
|
|
+ {error, bad_match} |
117 |
|
|
+ end; |
118 |
|
|
+ _ -> |
119 |
|
|
+ {error, not_found} |
120 |
|
|
+ end) |
121 |
|
|
+ end; |
122 |
|
|
+process_reply(_) -> |
123 |
|
|
+ {error, malformed}. |
124 |
|
|
+ |
125 |
|
|
+process(_Handlers, #request{method='GET', lang=Lang, path=[_, Id]}) -> |
126 |
|
|
+ case mnesia:dirty_read(captcha, Id) of |
127 |
|
|
+ [#captcha{}] -> |
128 |
|
|
+ Form = |
129 |
|
|
+ {xmlelement, "div", [{"align", "center"}], |
130 |
|
|
+ [{xmlelement, "form", [{"action", get_url(Id)}, |
131 |
|
|
+ {"name", "captcha"}, |
132 |
|
|
+ {"method", "POST"}], |
133 |
|
|
+ [{xmlelement, "img", [{"src", get_url(Id ++ "/image")}], []}, |
134 |
|
|
+ {xmlelement, "br", [], []}, |
135 |
|
|
+ {xmlcdata, ?CAPTCHA_TEXT(Lang)}, |
136 |
|
|
+ {xmlelement, "br", [], []}, |
137 |
|
|
+ {xmlelement, "input", [{"type", "text"}, |
138 |
|
|
+ {"name", "key"}, |
139 |
|
|
+ {"size", "10"}], []}, |
140 |
|
|
+ {xmlelement, "br", [], []}, |
141 |
|
|
+ {xmlelement, "input", [{"type", "submit"}, |
142 |
|
|
+ {"name", "enter"}, |
143 |
|
|
+ {"value", "OK"}], []}]}]}, |
144 |
|
|
+ ejabberd_web:make_xhtml([Form]); |
145 |
|
|
+ _ -> |
146 |
|
|
+ ejabberd_web:error(not_found) |
147 |
|
|
+ end; |
148 |
|
|
+ |
149 |
|
|
+process(_Handlers, #request{method='GET', path=[_, Id, "image"]}) -> |
150 |
|
|
+ case mnesia:dirty_read(captcha, Id) of |
151 |
|
|
+ [#captcha{key=Key}] -> |
152 |
|
|
+ case create_image(Key) of |
153 |
|
|
+ {ok, Type, _, Img} -> |
154 |
|
|
+ {200, |
155 |
|
|
+ [{"Content-Type", Type}, |
156 |
|
|
+ {"Cache-Control", "no-cache"}, |
157 |
|
|
+ {"Last-Modified", httpd_util:rfc1123_date()}], |
158 |
|
|
+ Img}; |
159 |
|
|
+ _ -> |
160 |
|
|
+ ejabberd_web:error(not_found) |
161 |
|
|
+ end; |
162 |
|
|
+ _ -> |
163 |
|
|
+ ejabberd_web:error(not_found) |
164 |
|
|
+ end; |
165 |
|
|
+ |
166 |
|
|
+process(_Handlers, #request{method='POST', q=Q, path=[_, Id]}) -> |
167 |
|
|
+ ?T(case mnesia:read(captcha, Id, write) of |
168 |
|
|
+ [#captcha{pid=Pid, args=Args, key=Key, tref=Tref}] -> |
169 |
|
|
+ mnesia:delete({captcha, Id}), |
170 |
|
|
+ erlang:cancel_timer(Tref), |
171 |
|
|
+ Input = proplists:get_value("key", Q, none), |
172 |
|
|
+ if Input == Key -> |
173 |
|
|
+ Pid ! {captcha_succeed, Args}, |
174 |
|
|
+ ejabberd_web:make_xhtml([]); |
175 |
|
|
+ true -> |
176 |
|
|
+ Pid ! {captcha_failed, Args}, |
177 |
|
|
+ ejabberd_web:error(not_allowed) |
178 |
|
|
+ end; |
179 |
|
|
+ _ -> |
180 |
|
|
+ ejabberd_web:error(not_found) |
181 |
|
|
+ end). |
182 |
|
|
+ |
183 |
|
|
+%%==================================================================== |
184 |
|
|
+%% gen_server callbacks |
185 |
|
|
+%%==================================================================== |
186 |
|
|
+init([]) -> |
187 |
|
|
+ mnesia:create_table(captcha, |
188 |
|
|
+ [{ram_copies, [node()]}, |
189 |
|
|
+ {attributes, record_info(fields, captcha)}]), |
190 |
|
|
+ mnesia:add_table_copy(captcha, node(), ram_copies), |
191 |
|
|
+ {ok, #state{}}. |
192 |
|
|
+ |
193 |
|
|
+handle_call(_Request, _From, State) -> |
194 |
|
|
+ {reply, bad_request, State}. |
195 |
|
|
+ |
196 |
|
|
+handle_cast(_Msg, State) -> |
197 |
|
|
+ {noreply, State}. |
198 |
|
|
+ |
199 |
|
|
+handle_info({remove_id, Id}, State) -> |
200 |
|
|
+ ?DEBUG("captcha ~p timed out", [Id]), |
201 |
|
|
+ ?T(case mnesia:read(captcha, Id, write) of |
202 |
|
|
+ [#captcha{args=Args, pid=Pid}] -> |
203 |
|
|
+ Pid ! {captcha_failed, Args}, |
204 |
|
|
+ mnesia:delete({captcha, Id}); |
205 |
|
|
+ _ -> |
206 |
|
|
+ ok |
207 |
|
|
+ end), |
208 |
|
|
+ {noreply, State}; |
209 |
|
|
+ |
210 |
|
|
+handle_info(_Info, State) -> |
211 |
|
|
+ {noreply, State}. |
212 |
|
|
+ |
213 |
|
|
+terminate(_Reason, _State) -> |
214 |
|
|
+ ok. |
215 |
|
|
+ |
216 |
|
|
+code_change(_OldVsn, State, _Extra) -> |
217 |
|
|
+ {ok, State}. |
218 |
|
|
+ |
219 |
|
|
+%%-------------------------------------------------------------------- |
220 |
|
|
+%%% Internal functions |
221 |
|
|
+%%-------------------------------------------------------------------- |
222 |
|
|
+%%-------------------------------------------------------------------- |
223 |
|
|
+%% Function: create_image() -> {ok, Type, Key, Image} | {error, Reason} |
224 |
|
|
+%% Type = "image/png" | "image/jpeg" | "image/gif" |
225 |
|
|
+%% Key = string() |
226 |
|
|
+%% Image = binary() |
227 |
|
|
+%% Reason = atom() |
228 |
|
|
+%%-------------------------------------------------------------------- |
229 |
|
|
+create_image() -> |
230 |
|
|
+ %% Six numbers from 1 to 9. |
231 |
|
|
+ Key = string:substr(randoms:get_string(), 1, 6), |
232 |
|
|
+ create_image(Key). |
233 |
|
|
+ |
234 |
|
|
+create_image(Key) -> |
235 |
|
|
+ FileName = get_prog_name(), |
236 |
|
|
+ Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])), |
237 |
|
|
+ case cmd(Cmd) of |
238 |
|
|
+ {ok, <<16#89, $P, $N, $G, $\r, $\n, 16#1a, $\n, _/binary>> = Img} -> |
239 |
|
|
+ {ok, "image/png", Key, Img}; |
240 |
|
|
+ {ok, <<16#ff, 16#d8, _/binary>> = Img} -> |
241 |
|
|
+ {ok, "image/jpeg", Key, Img}; |
242 |
|
|
+ {ok, <<$G, $I, $F, $8, X, $a, _/binary>> = Img} when X==$7; X==$9 -> |
243 |
|
|
+ {ok, "image/gif", Key, Img}; |
244 |
|
|
+ {error, Reason} -> |
245 |
|
|
+ ?ERROR_MSG("Failed to process an output from \"~s\": ~p", |
246 |
|
|
+ [Cmd, Reason]), |
247 |
|
|
+ {error, Reason}; |
248 |
|
|
+ _ -> |
249 |
|
|
+ Reason = malformed_image, |
250 |
|
|
+ ?ERROR_MSG("Failed to process an output from \"~s\": ~p", |
251 |
|
|
+ [Cmd, Reason]), |
252 |
|
|
+ {error, Reason} |
253 |
|
|
+ end. |
254 |
|
|
+ |
255 |
|
|
+get_prog_name() -> |
256 |
|
|
+ case ejabberd_config:get_local_option(captcha_cmd) of |
257 |
|
|
+ FileName when is_list(FileName) -> |
258 |
|
|
+ FileName; |
259 |
|
|
+ _ -> |
260 |
|
|
+ "" |
261 |
|
|
+ end. |
262 |
|
|
+ |
263 |
|
|
+get_url(Str) -> |
264 |
|
|
+ case ejabberd_config:get_local_option(captcha_host) of |
265 |
|
|
+ Host when is_list(Host) -> |
266 |
|
|
+ "http://" ++ Host ++ "/captcha/" ++ Str; |
267 |
|
|
+ _ -> |
268 |
|
|
+ "http://" ++ ?MYNAME ++ "/captcha/" ++ Str |
269 |
|
|
+ end. |
270 |
|
|
+ |
271 |
|
|
+%%-------------------------------------------------------------------- |
272 |
|
|
+%% Function: cmd(Cmd) -> Data | {error, Reason} |
273 |
|
|
+%% Cmd = string() |
274 |
|
|
+%% Data = binary() |
275 |
|
|
+%% Description: os:cmd/1 replacement |
276 |
|
|
+%%-------------------------------------------------------------------- |
277 |
|
|
+-define(CMD_TIMEOUT, 5000). |
278 |
|
|
+-define(MAX_FILE_SIZE, 64*1024). |
279 |
|
|
+ |
280 |
|
|
+cmd(Cmd) -> |
281 |
|
|
+ Port = open_port({spawn, Cmd}, [stream, eof, binary]), |
282 |
|
|
+ TRef = erlang:start_timer(?CMD_TIMEOUT, self(), timeout), |
283 |
|
|
+ recv_data(Port, TRef, <<>>). |
284 |
|
|
+ |
285 |
|
|
+recv_data(Port, TRef, Buf) -> |
286 |
|
|
+ receive |
287 |
|
|
+ {Port, {data, Bytes}} -> |
288 |
|
|
+ NewBuf = <<Buf/binary, Bytes/binary>>, |
289 |
|
|
+ if size(NewBuf) > ?MAX_FILE_SIZE -> |
290 |
|
|
+ return(Port, TRef, {error, efbig}); |
291 |
|
|
+ true -> |
292 |
|
|
+ recv_data(Port, TRef, NewBuf) |
293 |
|
|
+ end; |
294 |
|
|
+ {Port, {data, _}} -> |
295 |
|
|
+ return(Port, TRef, {error, efbig}); |
296 |
|
|
+ {Port, eof} when Buf /= <<>> -> |
297 |
|
|
+ return(Port, TRef, {ok, Buf}); |
298 |
|
|
+ {Port, eof} -> |
299 |
|
|
+ return(Port, TRef, {error, enodata}); |
300 |
|
|
+ {timeout, TRef, _} -> |
301 |
|
|
+ return(Port, TRef, {error, timeout}) |
302 |
|
|
+ end. |
303 |
|
|
+ |
304 |
|
|
+return(Port, TRef, Result) -> |
305 |
|
|
+ case erlang:cancel_timer(TRef) of |
306 |
|
|
+ false -> |
307 |
|
|
+ receive |
308 |
|
|
+ {timeout, TRef, _} -> |
309 |
|
|
+ ok |
310 |
|
|
+ after 0 -> |
311 |
|
|
+ ok |
312 |
|
|
+ end; |
313 |
|
|
+ _ -> |
314 |
|
|
+ ok |
315 |
|
|
+ end, |
316 |
|
|
+ catch port_close(Port), |
317 |
|
|
+ Result. |
318 |
|
|
diff -urN ejabberd-2.0.4/src/ejabberd_config.erl ejabberd-2.0.4-new/src/ejabberd_config.erl |
319 |
|
|
--- ejabberd-2.0.4/src/ejabberd_config.erl 2009-03-12 09:41:02.000000000 +0100 |
320 |
|
|
+++ ejabberd-2.0.4-new/src/ejabberd_config.erl 2009-03-14 11:43:35.000000000 +0100 |
321 |
|
|
@@ -164,6 +164,10 @@ |
322 |
|
|
add_option(watchdog_admins, Admins, State); |
323 |
|
|
{registration_timeout, Timeout} -> |
324 |
|
|
add_option(registration_timeout, Timeout, State); |
325 |
|
|
+ {captcha_cmd, Cmd} -> |
326 |
|
|
+ add_option(captcha_cmd, Cmd, State); |
327 |
|
|
+ {captcha_host, Host} -> |
328 |
|
|
+ add_option(captcha_host, Host, State); |
329 |
|
|
{loglevel, Loglevel} -> |
330 |
|
|
ejabberd_loglevel:set(Loglevel), |
331 |
|
|
State; |
332 |
|
|
diff -urN ejabberd-2.0.4/src/ejabberd_sup.erl ejabberd-2.0.4-new/src/ejabberd_sup.erl |
333 |
|
|
--- ejabberd-2.0.4/src/ejabberd_sup.erl 2009-03-12 09:41:02.000000000 +0100 |
334 |
|
|
+++ ejabberd-2.0.4-new/src/ejabberd_sup.erl 2009-03-14 12:36:43.000000000 +0100 |
335 |
|
|
@@ -84,6 +84,13 @@ |
336 |
|
|
brutal_kill, |
337 |
|
|
worker, |
338 |
|
|
[ejabberd_local]}, |
339 |
|
|
+ Captcha = |
340 |
|
|
+ {ejabberd_captcha, |
341 |
|
|
+ {ejabberd_captcha, start_link, []}, |
342 |
|
|
+ permanent, |
343 |
|
|
+ brutal_kill, |
344 |
|
|
+ worker, |
345 |
|
|
+ [ejabberd_captcha]}, |
346 |
|
|
Listener = |
347 |
|
|
{ejabberd_listener, |
348 |
|
|
{ejabberd_listener, start_link, []}, |
349 |
|
|
@@ -170,6 +177,7 @@ |
350 |
|
|
SM, |
351 |
|
|
S2S, |
352 |
|
|
Local, |
353 |
|
|
+ Captcha, |
354 |
|
|
ReceiverSupervisor, |
355 |
|
|
C2SSupervisor, |
356 |
|
|
S2SInSupervisor, |
357 |
|
|
diff -urN ejabberd-2.0.4/src/jlib.hrl ejabberd-2.0.4-new/src/jlib.hrl |
358 |
|
|
--- ejabberd-2.0.4/src/jlib.hrl 2009-03-12 09:41:02.000000000 +0100 |
359 |
|
|
+++ ejabberd-2.0.4-new/src/jlib.hrl 2009-03-14 11:41:46.000000000 +0100 |
360 |
|
|
@@ -74,6 +74,12 @@ |
361 |
|
|
|
362 |
|
|
-define(NS_CAPS, "http://jabber.org/protocol/caps"). |
363 |
|
|
|
364 |
|
|
+%% CAPTCHA related NSes. |
365 |
|
|
+-define(NS_OOB, "jabber:x:oob"). |
366 |
|
|
+-define(NS_CAPTCHA, "urn:xmpp:captcha"). |
367 |
|
|
+-define(NS_MEDIA, "urn:xmpp:media-element"). |
368 |
|
|
+-define(NS_BOB, "urn:xmpp:bob"). |
369 |
|
|
+ |
370 |
|
|
% TODO: remove "code" attribute (currently it used for backward-compatibility) |
371 |
|
|
-define(STANZA_ERROR(Code, Type, Condition), |
372 |
|
|
{xmlelement, "error", |
373 |
|
|
diff -urN ejabberd-2.0.4/src/mod_muc/mod_muc_room.erl ejabberd-2.0.4-new/src/mod_muc/mod_muc_room.erl |
374 |
|
|
--- ejabberd-2.0.4/src/mod_muc/mod_muc_room.erl 2009-03-12 09:41:02.000000000 +0100 |
375 |
|
|
+++ ejabberd-2.0.4-new/src/mod_muc/mod_muc_room.erl 2009-03-14 11:40:40.000000000 +0100 |
376 |
|
|
@@ -69,6 +69,7 @@ |
377 |
|
|
public_list = true, |
378 |
|
|
persistent = false, |
379 |
|
|
moderated = true, |
380 |
|
|
+ captcha_protected = false, |
381 |
|
|
members_by_default = true, |
382 |
|
|
members_only = false, |
383 |
|
|
allow_user_invites = false, |
384 |
|
|
@@ -98,6 +99,7 @@ |
385 |
|
|
jid, |
386 |
|
|
config = #config{}, |
387 |
|
|
users = ?DICT:new(), |
388 |
|
|
+ robots = ?DICT:new(), |
389 |
|
|
affiliations = ?DICT:new(), |
390 |
|
|
history = lqueue_new(20), |
391 |
|
|
subject = "", |
392 |
|
|
@@ -382,7 +384,8 @@ |
393 |
|
|
(XMLNS == ?NS_MUC_ADMIN) or |
394 |
|
|
(XMLNS == ?NS_MUC_OWNER) or |
395 |
|
|
(XMLNS == ?NS_DISCO_INFO) or |
396 |
|
|
- (XMLNS == ?NS_DISCO_ITEMS) -> |
397 |
|
|
+ (XMLNS == ?NS_DISCO_ITEMS) or |
398 |
|
|
+ (XMLNS == ?NS_CAPTCHA) -> |
399 |
|
|
Res1 = case XMLNS of |
400 |
|
|
?NS_MUC_ADMIN -> |
401 |
|
|
process_iq_admin(From, Type, Lang, SubEl, StateData); |
402 |
|
|
@@ -391,7 +394,9 @@ |
403 |
|
|
?NS_DISCO_INFO -> |
404 |
|
|
process_iq_disco_info(From, Type, Lang, StateData); |
405 |
|
|
?NS_DISCO_ITEMS -> |
406 |
|
|
- process_iq_disco_items(From, Type, Lang, StateData) |
407 |
|
|
+ process_iq_disco_items(From, Type, Lang, StateData); |
408 |
|
|
+ ?NS_CAPTCHA -> |
409 |
|
|
+ process_iq_captcha(From, Type, Lang, SubEl, StateData) |
410 |
|
|
end, |
411 |
|
|
{IQRes, NewStateData} = |
412 |
|
|
case Res1 of |
413 |
|
|
@@ -761,6 +766,30 @@ |
414 |
|
|
{empty, _} -> |
415 |
|
|
{next_state, StateName, StateData} |
416 |
|
|
end; |
417 |
|
|
+handle_info({captcha_succeed, From}, normal_state, StateData) -> |
418 |
|
|
+ NewState = case ?DICT:find(From, StateData#state.robots) of |
419 |
|
|
+ {ok, {Nick, Packet}} -> |
420 |
|
|
+ Robots = ?DICT:store(From, passed, StateData#state.robots), |
421 |
|
|
+ add_new_user(From, Nick, Packet, StateData#state{robots=Robots}); |
422 |
|
|
+ _ -> |
423 |
|
|
+ StateData |
424 |
|
|
+ end, |
425 |
|
|
+ {next_state, normal_state, NewState}; |
426 |
|
|
+handle_info({captcha_failed, From}, normal_state, StateData) -> |
427 |
|
|
+ NewState = case ?DICT:find(From, StateData#state.robots) of |
428 |
|
|
+ {ok, {Nick, Packet}} -> |
429 |
|
|
+ Robots = ?DICT:erase(From, StateData#state.robots), |
430 |
|
|
+ Err = jlib:make_error_reply( |
431 |
|
|
+ Packet, ?ERR_NOT_AUTHORIZED), |
432 |
|
|
+ ejabberd_router:route( % TODO: s/Nick/""/ |
433 |
|
|
+ jlib:jid_replace_resource( |
434 |
|
|
+ StateData#state.jid, Nick), |
435 |
|
|
+ From, Err), |
436 |
|
|
+ StateData#state{robots=Robots}; |
437 |
|
|
+ _ -> |
438 |
|
|
+ StateData |
439 |
|
|
+ end, |
440 |
|
|
+ {next_state, normal_state, NewState}; |
441 |
|
|
handle_info(_Info, StateName, StateData) -> |
442 |
|
|
{next_state, StateName, StateData}. |
443 |
|
|
|
444 |
|
|
@@ -1461,7 +1490,8 @@ |
445 |
|
|
From, Err), |
446 |
|
|
StateData; |
447 |
|
|
{_, _, _, Role} -> |
448 |
|
|
- case check_password(ServiceAffiliation, Els, StateData) of |
449 |
|
|
+ case check_password(ServiceAffiliation, Affiliation, |
450 |
|
|
+ Els, From, StateData) of |
451 |
|
|
true -> |
452 |
|
|
NewState = |
453 |
|
|
add_user_presence( |
454 |
|
|
@@ -1494,7 +1524,8 @@ |
455 |
|
|
true -> |
456 |
|
|
NewState#state{just_created = false}; |
457 |
|
|
false -> |
458 |
|
|
- NewState |
459 |
|
|
+ Robots = ?DICT:erase(From, StateData#state.robots), |
460 |
|
|
+ NewState#state{robots = Robots} |
461 |
|
|
end; |
462 |
|
|
nopass -> |
463 |
|
|
ErrText = "Password required to enter this room", |
464 |
|
|
@@ -1505,6 +1536,29 @@ |
465 |
|
|
StateData#state.jid, Nick), |
466 |
|
|
From, Err), |
467 |
|
|
StateData; |
468 |
|
|
+ captcha_required -> |
469 |
|
|
+ ID = randoms:get_string(), |
470 |
|
|
+ SID = xml:get_attr_s("id", Attrs), |
471 |
|
|
+ RoomJID = StateData#state.jid, |
472 |
|
|
+ To = jlib:jid_replace_resource(RoomJID, Nick), |
473 |
|
|
+ case ejabberd_captcha:create_captcha( |
474 |
|
|
+ ID, SID, RoomJID, To, Lang, From) of |
475 |
|
|
+ {ok, CaptchaEls} -> |
476 |
|
|
+ MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls}, |
477 |
|
|
+ Robots = ?DICT:store(From, |
478 |
|
|
+ {Nick, Packet}, StateData#state.robots), |
479 |
|
|
+ ejabberd_router:route(RoomJID, From, MsgPkt), |
480 |
|
|
+ StateData#state{robots = Robots}; |
481 |
|
|
+ error -> |
482 |
|
|
+ ErrText = "Unable to generate a captcha", |
483 |
|
|
+ Err = jlib:make_error_reply( |
484 |
|
|
+ Packet, ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText)), |
485 |
|
|
+ ejabberd_router:route( % TODO: s/Nick/""/ |
486 |
|
|
+ jlib:jid_replace_resource( |
487 |
|
|
+ StateData#state.jid, Nick), |
488 |
|
|
+ From, Err), |
489 |
|
|
+ StateData |
490 |
|
|
+ end; |
491 |
|
|
_ -> |
492 |
|
|
ErrText = "Incorrect password", |
493 |
|
|
Err = jlib:make_error_reply( |
494 |
|
|
@@ -1517,13 +1571,13 @@ |
495 |
|
|
end |
496 |
|
|
end. |
497 |
|
|
|
498 |
|
|
-check_password(owner, _Els, _StateData) -> |
499 |
|
|
+check_password(owner, _Affiliation, _Els, _From, _StateData) -> |
500 |
|
|
%% Don't check pass if user is owner in MUC service (access_admin option) |
501 |
|
|
true; |
502 |
|
|
-check_password(_ServiceAffiliation, Els, StateData) -> |
503 |
|
|
+check_password(_ServiceAffiliation, Affiliation, Els, From, StateData) -> |
504 |
|
|
case (StateData#state.config)#config.password_protected of |
505 |
|
|
false -> |
506 |
|
|
- true; |
507 |
|
|
+ check_captcha(Affiliation, From, StateData); |
508 |
|
|
true -> |
509 |
|
|
Pass = extract_password(Els), |
510 |
|
|
case Pass of |
511 |
|
|
@@ -1539,6 +1593,19 @@ |
512 |
|
|
end |
513 |
|
|
end. |
514 |
|
|
|
515 |
|
|
+check_captcha(Affiliation, From, StateData) -> |
516 |
|
|
+ case (StateData#state.config)#config.captcha_protected of |
517 |
|
|
+ true when Affiliation == none -> |
518 |
|
|
+ case ?DICT:find(From, StateData#state.robots) of |
519 |
|
|
+ {ok, passed} -> |
520 |
|
|
+ true; |
521 |
|
|
+ _ -> |
522 |
|
|
+ captcha_required |
523 |
|
|
+ end; |
524 |
|
|
+ _ -> |
525 |
|
|
+ true |
526 |
|
|
+ end. |
527 |
|
|
+ |
528 |
|
|
extract_password([]) -> |
529 |
|
|
false; |
530 |
|
|
extract_password([{xmlelement, _Name, Attrs, _SubEls} = El | Els]) -> |
531 |
|
|
@@ -2713,6 +2780,9 @@ |
532 |
|
|
?BOOLXFIELD("Make room members-only", |
533 |
|
|
"muc#roomconfig_membersonly", |
534 |
|
|
Config#config.members_only), |
535 |
|
|
+ ?BOOLXFIELD("Make room captcha protected", |
536 |
|
|
+ "captcha_protected", |
537 |
|
|
+ Config#config.captcha_protected), |
538 |
|
|
?BOOLXFIELD("Make room moderated", |
539 |
|
|
"muc#roomconfig_moderatedroom", |
540 |
|
|
Config#config.moderated), |
541 |
|
|
@@ -2823,6 +2893,8 @@ |
542 |
|
|
?SET_BOOL_XOPT(members_by_default, Val); |
543 |
|
|
set_xoption([{"muc#roomconfig_membersonly", [Val]} | Opts], Config) -> |
544 |
|
|
?SET_BOOL_XOPT(members_only, Val); |
545 |
|
|
+set_xoption([{"captcha_protected", [Val]} | Opts], Config) -> |
546 |
|
|
+ ?SET_BOOL_XOPT(captcha_protected, Val); |
547 |
|
|
set_xoption([{"muc#roomconfig_allowinvites", [Val]} | Opts], Config) -> |
548 |
|
|
?SET_BOOL_XOPT(allow_user_invites, Val); |
549 |
|
|
set_xoption([{"muc#roomconfig_passwordprotectedroom", [Val]} | Opts], Config) -> |
550 |
|
|
@@ -2913,6 +2985,7 @@ |
551 |
|
|
?CASE_CONFIG_OPT(members_only); |
552 |
|
|
?CASE_CONFIG_OPT(allow_user_invites); |
553 |
|
|
?CASE_CONFIG_OPT(password_protected); |
554 |
|
|
+ ?CASE_CONFIG_OPT(captcha_protected); |
555 |
|
|
?CASE_CONFIG_OPT(password); |
556 |
|
|
?CASE_CONFIG_OPT(anonymous); |
557 |
|
|
?CASE_CONFIG_OPT(logging); |
558 |
|
|
@@ -2954,6 +3027,7 @@ |
559 |
|
|
?MAKE_CONFIG_OPT(members_only), |
560 |
|
|
?MAKE_CONFIG_OPT(allow_user_invites), |
561 |
|
|
?MAKE_CONFIG_OPT(password_protected), |
562 |
|
|
+ ?MAKE_CONFIG_OPT(captcha_protected), |
563 |
|
|
?MAKE_CONFIG_OPT(password), |
564 |
|
|
?MAKE_CONFIG_OPT(anonymous), |
565 |
|
|
?MAKE_CONFIG_OPT(logging), |
566 |
|
|
@@ -3074,6 +3148,17 @@ |
567 |
|
|
{error, ?ERR_FORBIDDEN} |
568 |
|
|
end. |
569 |
|
|
|
570 |
|
|
+process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) -> |
571 |
|
|
+ {error, ?ERR_NOT_ALLOWED}; |
572 |
|
|
+ |
573 |
|
|
+process_iq_captcha(_From, set, _Lang, SubEl, StateData) -> |
574 |
|
|
+ case ejabberd_captcha:process_reply(SubEl) of |
575 |
|
|
+ ok -> |
576 |
|
|
+ {result, [], StateData}; |
577 |
|
|
+ _ -> |
578 |
|
|
+ {error, ?ERR_NOT_ACCEPTABLE} |
579 |
|
|
+ end. |
580 |
|
|
+ |
581 |
|
|
get_title(StateData) -> |
582 |
|
|
case (StateData#state.config)#config.title of |
583 |
|
|
"" -> |
584 |
|
|
diff -urN ejabberd-2.0.4/src/web/ejabberd_http.erl ejabberd-2.0.4-new/src/web/ejabberd_http.erl |
585 |
|
|
--- ejabberd-2.0.4/src/web/ejabberd_http.erl 2009-03-12 09:41:02.000000000 +0100 |
586 |
|
|
+++ ejabberd-2.0.4-new/src/web/ejabberd_http.erl 2009-03-14 10:57:34.000000000 +0100 |
587 |
|
|
@@ -106,6 +106,10 @@ |
588 |
|
|
{value, {request_handlers, H}} -> H; |
589 |
|
|
false -> [] |
590 |
|
|
end ++ |
591 |
|
|
+ case lists:member(captcha, Opts) of |
592 |
|
|
+ true -> [{["captcha"], ejabberd_captcha}]; |
593 |
|
|
+ false -> [] |
594 |
|
|
+ end ++ |
595 |
|
|
case lists:member(web_admin, Opts) of |
596 |
|
|
true -> [{["admin"], ejabberd_web_admin}]; |
597 |
|
|
false -> [] |
598 |
|
|
Binary files ejabberd-2.0.4/tools/.DS_Store and ejabberd-2.0.4-new/tools/.DS_Store differ |
599 |
|
|
diff -urN ejabberd-2.0.4/tools/captcha.sh ejabberd-2.0.4-new/tools/captcha.sh |
600 |
|
|
--- ejabberd-2.0.4/tools/captcha.sh 1970-01-01 01:00:00.000000000 +0100 |
601 |
|
|
+++ ejabberd-2.0.4-new/tools/captcha.sh 2009-03-14 11:46:42.000000000 +0100 |
602 |
|
|
@@ -0,0 +1,21 @@ |
603 |
|
|
+#!/bin/sh |
604 |
|
|
+ |
605 |
|
|
+SIGN=$(($RANDOM % 2)) |
606 |
|
|
+ |
607 |
|
|
+R1=$(($RANDOM % 20)) |
608 |
|
|
+R2=$(($RANDOM % 10 + 40)) |
609 |
|
|
+ |
610 |
|
|
+if [ $SIGN -eq "0" ]; then |
611 |
|
|
+ S1=$(( -1*($RANDOM % 20 + 50) )) |
612 |
|
|
+ S2=$(( $RANDOM % 20 + 50 )) |
613 |
|
|
+else |
614 |
|
|
+ S2=$(( -1*($RANDOM % 20 + 50) )) |
615 |
|
|
+ S1=$(( $RANDOM % 20 + 50 )) |
616 |
|
|
+fi |
617 |
|
|
+ |
618 |
|
|
+convert -size 140x60 xc:white \ |
619 |
|
|
+ -pointsize 30 -draw "text 20,30 '$1'" \ |
620 |
|
|
+ -roll -$R2+$R1 -swirl $S1 \ |
621 |
|
|
+ -roll +$R2-$R1 -swirl $S2 \ |
622 |
|
|
+ +repage -resize 120x60 \ |
623 |
|
|
+ -quality 90 -depth 8 png:- |
624 |
|
|
--- ejabberd-2.0.5/ChangeLog~ 2009-04-01 19:23:52.000000000 +0400 |
625 |
|
|
+++ ejabberd-2.0.5/ChangeLog 2009-04-03 23:45:03.174979944 +0400 |
626 |
|
|
@@ -15,6 +15,15 @@ |
627 |
|
|
stanza (EJAB-300). |
628 |
|
|
* src/ejabberd_c2s.erl: Likewise |
629 |
|
|
|
630 |
|
|
+2009-03-13 Evgeniy Khramtsov <ekhramtsov@process-one.net> |
631 |
|
|
+ |
632 |
|
|
+ * src/ejabberd_captcha.erl: XEP-158 (CAPTCHA Forms). |
633 |
|
|
+ * src/ejabberd_config.erl: likewise. |
634 |
|
|
+ * src/ejabberd_sup.erl: likewise. |
635 |
|
|
+ * src/jlib.hrl: likewise. |
636 |
|
|
+ * src/web/ejabberd_http.erl: likewise. |
637 |
|
|
+ * src/mod_muc/mod_muc_room.erl: CAPTCHA support. |
638 |
|
|
+ |
639 |
|
|
2009-03-10 Badlop <badlop@process-one.net> |
640 |
|
|
|
641 |
|
|
* doc/release_notes_2.0.4.txt: Added file for new release |