rpms/ejabberd/F-9 ejabberd-captcha.patch, NONE, 1.1 .cvsignore, 1.12, 1.13 ejabberd.spec, 1.30, 1.31 import.log, 1.6, 1.7 mod_ctlextra.erl, 1.4, 1.5 sources, 1.14, 1.15

Peter Lemenkov peter at fedoraproject.org
Fri Apr 3 20:20:14 UTC 2009


Author: peter

Update of /cvs/pkgs/rpms/ejabberd/F-9
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv10402/F-9

Modified Files:
	.cvsignore ejabberd.spec import.log mod_ctlextra.erl sources 
Added Files:
	ejabberd-captcha.patch 
Log Message:
Ver. 2.0.5

ejabberd-captcha.patch:

--- NEW FILE ejabberd-captcha.patch ---
Binary files ejabberd-2.0.4/src/.DS_Store and ejabberd-2.0.4-new/src/.DS_Store differ
diff -urN ejabberd-2.0.4/src/ejabberd_captcha.erl ejabberd-2.0.4-new/src/ejabberd_captcha.erl
--- ejabberd-2.0.4/src/ejabberd_captcha.erl	1970-01-01 01:00:00.000000000 +0100
+++ ejabberd-2.0.4-new/src/ejabberd_captcha.erl	2009-03-14 07:27:05.000000000 +0100
@@ -0,0 +1,312 @@
+%%%-------------------------------------------------------------------
+%%% File    : ejabberd_captcha.erl
+%%% Author  : Evgeniy Khramtsov <xramtsov at gmail.com>
+%%% Description : CAPTCHA processing.
+%%%
+%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov at gmail.com>
+%%%-------------------------------------------------------------------
+-module(ejabberd_captcha).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+	 terminate/2, code_change/3]).
+
+-export([create_captcha/6, process_reply/1, process/2]).
+
+-include("jlib.hrl").
+-include("ejabberd.hrl").
+-include("web/ejabberd_http.hrl").
+
+-define(VFIELD(Type, Var, Value),
+	{xmlelement, "field", [{"type", Type}, {"var", Var}],
+	 [{xmlelement, "value", [], [Value]}]}).
+
+-define(CAPTCHA_BODY(Lang, Room, URL),
+	translate:translate(Lang, "Your messages to ") ++ Room
+	++ translate:translate(Lang, " are being blocked. To unblock them, visit ")
+	++ URL).
+
+-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")).
+-define(CAPTCHA_LIFETIME, 120000). % two minutes
+
+-record(state, {}).
+-record(captcha, {id, pid, key, tref, args}).
+
+-define(T(S),
+	case catch mnesia:transaction(fun() -> S end) of
+	    {atomic, Res} ->
+		Res;
+	    {_, Reason} ->
+		?ERROR_MSG("mnesia transaction failed: ~p", [Reason]),
+		{error, Reason}
+	end).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+create_captcha(Id, SID, From, To, Lang, Args)
+  when is_list(Id), is_list(Lang), is_list(SID),
+       is_record(From, jid), is_record(To, jid) ->
+    case create_image() of
+	{ok, Type, Key, Image} ->
+	    B64Image = jlib:encode_base64(binary_to_list(Image)),
+	    JID = jlib:jid_to_string(From),
+	    CID = "sha1+" ++ sha:sha(Image) ++ "@bob.xmpp.org",
+	    Data = {xmlelement, "data",
+		    [{"xmlns", ?NS_BOB}, {"cid", CID},
+		     {"max-age", "0"}, {"type", Type}],
+		    [{xmlcdata, B64Image}]},
+	    Captcha =
+		{xmlelement, "captcha", [{"xmlns", ?NS_CAPTCHA}],
+		 [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
+		   [?VFIELD("hidden", "FORM_TYPE", {xmlcdata, ?NS_CAPTCHA}),
+		    ?VFIELD("hidden", "from", {xmlcdata, jlib:jid_to_string(To)}),
+		    ?VFIELD("hidden", "challenge", {xmlcdata, Id}),
+		    ?VFIELD("hidden", "sid", {xmlcdata, SID}),
+		    {xmlelement, "field", [{"var", "ocr"}],
+		     [{xmlelement, "media", [{"xmlns", ?NS_MEDIA}],
+		       [{xmlelement, "uri", [{"type", Type}],
+			 [{xmlcdata, "cid:" ++ CID}]}]}]}]}]},
+	    Body = {xmlelement, "body", [],
+		    [{xmlcdata, ?CAPTCHA_BODY(Lang, JID, get_url(Id))}]},
+	    OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}],
+		   [{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]},
+	    Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
+	    ?T(mnesia:write(#captcha{id=Id, pid=self(), key=Key,
+				     tref=Tref, args=Args})),
+	    {ok, [Body, OOB, Captcha, Data]};
+	_Err ->
+	    error
+    end.
+
+process_reply({xmlelement, "captcha", _, _} = El) ->
+    case xml:get_subtag(El, "x") of
+	false ->
+	    {error, malformed};
+	Xdata ->
+	    Fields = jlib:parse_xdata_submit(Xdata),
+	    [Id | _] = proplists:get_value("challenge", Fields, [none]),
+	    [OCR | _] = proplists:get_value("ocr", Fields, [none]),
+	    ?T(case mnesia:read(captcha, Id, write) of
+		   [#captcha{pid=Pid, args=Args, key=Key, tref=Tref}] ->
+		       mnesia:delete({captcha, Id}),
+		       erlang:cancel_timer(Tref),
+		       if OCR == Key ->
+			       Pid ! {captcha_succeed, Args},
+			       ok;
+			  true ->
+			       Pid ! {captcha_failed, Args},
+			       {error, bad_match}
+		       end;
+		   _ ->
+		       {error, not_found}
+	       end)
+    end;
+process_reply(_) ->
+    {error, malformed}.
+
+process(_Handlers, #request{method='GET', lang=Lang, path=[_, Id]}) ->
+    case mnesia:dirty_read(captcha, Id) of
+	[#captcha{}] ->
+	    Form =
+		{xmlelement, "div", [{"align", "center"}],
+		 [{xmlelement, "form", [{"action", get_url(Id)},
+					{"name", "captcha"},
+					{"method", "POST"}],
+		   [{xmlelement, "img", [{"src", get_url(Id ++ "/image")}], []},
+		    {xmlelement, "br", [], []},
+		    {xmlcdata, ?CAPTCHA_TEXT(Lang)},
+		    {xmlelement, "br", [], []},
+		    {xmlelement, "input", [{"type", "text"},
+					   {"name", "key"},
+					   {"size", "10"}], []},
+		    {xmlelement, "br", [], []},
+		    {xmlelement, "input", [{"type", "submit"},
+					   {"name", "enter"},
+					   {"value", "OK"}], []}]}]},
+	    ejabberd_web:make_xhtml([Form]);
+	_ ->
+	    ejabberd_web:error(not_found)
+    end;
+
+process(_Handlers, #request{method='GET', path=[_, Id, "image"]}) ->
+    case mnesia:dirty_read(captcha, Id) of
+	[#captcha{key=Key}] ->
+	    case create_image(Key) of
+		{ok, Type, _, Img} ->
+		    {200,
+		     [{"Content-Type", Type},
+		      {"Cache-Control", "no-cache"},
+		      {"Last-Modified", httpd_util:rfc1123_date()}],
+		     Img};
+		_ ->
+		    ejabberd_web:error(not_found)
+	    end;
+	_ ->
+	    ejabberd_web:error(not_found)
+    end;
+
+process(_Handlers, #request{method='POST', q=Q, path=[_, Id]}) ->
+    ?T(case mnesia:read(captcha, Id, write) of
+	   [#captcha{pid=Pid, args=Args, key=Key, tref=Tref}] ->
+	       mnesia:delete({captcha, Id}),
+	       erlang:cancel_timer(Tref),
+	       Input = proplists:get_value("key", Q, none),
+	       if Input == Key ->
+		       Pid ! {captcha_succeed, Args},
+		       ejabberd_web:make_xhtml([]);
+		  true ->
+		       Pid ! {captcha_failed, Args},
+		       ejabberd_web:error(not_allowed)
+	       end;
+	   _ ->
+	       ejabberd_web:error(not_found)
+       end).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+init([]) ->
+    mnesia:create_table(captcha,
+			[{ram_copies, [node()]},
+			 {attributes, record_info(fields, captcha)}]),
+    mnesia:add_table_copy(captcha, node(), ram_copies),
+    {ok, #state{}}.
+
+handle_call(_Request, _From, State) ->
+    {reply, bad_request, State}.
+
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+handle_info({remove_id, Id}, State) ->
+    ?DEBUG("captcha ~p timed out", [Id]),
+    ?T(case mnesia:read(captcha, Id, write) of
+	   [#captcha{args=Args, pid=Pid}] ->
+	       Pid ! {captcha_failed, Args},
+	       mnesia:delete({captcha, Id});
+	   _ ->
+	       ok
+       end),
+    {noreply, State};
+
+handle_info(_Info, State) ->
+    {noreply, State}.
+
+terminate(_Reason, _State) ->
+    ok.
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% Function: create_image() -> {ok, Type, Key, Image} | {error, Reason}
+%% Type = "image/png" | "image/jpeg" | "image/gif"
+%% Key = string()
+%% Image = binary()
+%% Reason = atom()
+%%--------------------------------------------------------------------
+create_image() ->
+    %% Six numbers from 1 to 9.
+    Key = string:substr(randoms:get_string(), 1, 6),
+    create_image(Key).
+
+create_image(Key) ->
+    FileName = get_prog_name(),
+    Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
+    case cmd(Cmd) of
+	{ok, <<16#89, $P, $N, $G, $\r, $\n, 16#1a, $\n, _/binary>> = Img} ->
+	    {ok, "image/png", Key, Img};
+	{ok, <<16#ff, 16#d8, _/binary>> = Img} ->
+	    {ok, "image/jpeg", Key, Img};
+	{ok, <<$G, $I, $F, $8, X, $a, _/binary>> = Img} when X==$7; X==$9 ->
+	    {ok, "image/gif", Key, Img};
+	{error, Reason} ->
+	    ?ERROR_MSG("Failed to process an output from \"~s\": ~p",
+		       [Cmd, Reason]),
+	    {error, Reason};
+	_ ->
+	    Reason = malformed_image,
+	    ?ERROR_MSG("Failed to process an output from \"~s\": ~p",
+		       [Cmd, Reason]),
+	    {error, Reason}
+    end.
+
+get_prog_name() ->
+    case ejabberd_config:get_local_option(captcha_cmd) of
+	FileName when is_list(FileName) ->
+	    FileName;
+	_ ->
+	    ""
+    end.
+
+get_url(Str) ->
+    case ejabberd_config:get_local_option(captcha_host) of
+	Host when is_list(Host) ->
+	    "http://" ++ Host ++ "/captcha/" ++ Str;
+	_ ->
+	    "http://" ++ ?MYNAME ++ "/captcha/" ++ Str
+    end.
+
+%%--------------------------------------------------------------------
+%% Function: cmd(Cmd) -> Data | {error, Reason}
+%% Cmd = string()
+%% Data = binary()
+%% Description: os:cmd/1 replacement
+%%--------------------------------------------------------------------
+-define(CMD_TIMEOUT, 5000).
+-define(MAX_FILE_SIZE, 64*1024).
+
+cmd(Cmd) ->
+    Port = open_port({spawn, Cmd}, [stream, eof, binary]),
+    TRef = erlang:start_timer(?CMD_TIMEOUT, self(), timeout),
+    recv_data(Port, TRef, <<>>).
+
+recv_data(Port, TRef, Buf) ->
+    receive
+	{Port, {data, Bytes}} ->
+	    NewBuf = <<Buf/binary, Bytes/binary>>,
+	    if size(NewBuf) > ?MAX_FILE_SIZE ->
+		    return(Port, TRef, {error, efbig});
+	       true ->
+		    recv_data(Port, TRef, NewBuf)
+	    end;
+	{Port, {data, _}} ->
+	    return(Port, TRef, {error, efbig});
+	{Port, eof} when Buf /= <<>> ->
+	    return(Port, TRef, {ok, Buf});
+	{Port, eof} ->
+	    return(Port, TRef, {error, enodata});
+	{timeout, TRef, _} ->
+	    return(Port, TRef, {error, timeout})
+    end.
+
+return(Port, TRef, Result) ->
+    case erlang:cancel_timer(TRef) of
+	false ->
+	    receive
+		{timeout, TRef, _} ->
+		    ok
+	    after 0 ->
+		    ok
+	    end;
+	_ ->
+	    ok
+    end,
+    catch port_close(Port),
+    Result.
diff -urN ejabberd-2.0.4/src/ejabberd_config.erl ejabberd-2.0.4-new/src/ejabberd_config.erl
--- ejabberd-2.0.4/src/ejabberd_config.erl	2009-03-12 09:41:02.000000000 +0100
+++ ejabberd-2.0.4-new/src/ejabberd_config.erl	2009-03-14 11:43:35.000000000 +0100
@@ -164,6 +164,10 @@
 	    add_option(watchdog_admins, Admins, State);
 	{registration_timeout, Timeout} ->
 	    add_option(registration_timeout, Timeout, State);
+	{captcha_cmd, Cmd} ->
+	    add_option(captcha_cmd, Cmd, State);
+	{captcha_host, Host} ->
+            add_option(captcha_host, Host, State);
 	{loglevel, Loglevel} ->
 	    ejabberd_loglevel:set(Loglevel),
 	    State;
diff -urN ejabberd-2.0.4/src/ejabberd_sup.erl ejabberd-2.0.4-new/src/ejabberd_sup.erl
--- ejabberd-2.0.4/src/ejabberd_sup.erl	2009-03-12 09:41:02.000000000 +0100
+++ ejabberd-2.0.4-new/src/ejabberd_sup.erl	2009-03-14 12:36:43.000000000 +0100
@@ -84,6 +84,13 @@
 	 brutal_kill,
 	 worker,
 	 [ejabberd_local]},
+    Captcha =
+	{ejabberd_captcha,
+	 {ejabberd_captcha, start_link, []},
+	 permanent,
+	 brutal_kill,
+	 worker,
+	 [ejabberd_captcha]},
     Listener =
 	{ejabberd_listener,
 	 {ejabberd_listener, start_link, []},
@@ -170,6 +177,7 @@
 	   SM,
 	   S2S,
 	   Local,
+           Captcha,
 	   ReceiverSupervisor,
 	   C2SSupervisor,
 	   S2SInSupervisor,
diff -urN ejabberd-2.0.4/src/jlib.hrl ejabberd-2.0.4-new/src/jlib.hrl
--- ejabberd-2.0.4/src/jlib.hrl	2009-03-12 09:41:02.000000000 +0100
+++ ejabberd-2.0.4-new/src/jlib.hrl	2009-03-14 11:41:46.000000000 +0100
@@ -74,6 +74,12 @@
 
 -define(NS_CAPS,          "http://jabber.org/protocol/caps").
 
+%% CAPTCHA related NSes.
+-define(NS_OOB, "jabber:x:oob").
+-define(NS_CAPTCHA, "urn:xmpp:captcha").
+-define(NS_MEDIA, "urn:xmpp:media-element").
+-define(NS_BOB, "urn:xmpp:bob").
+
 % TODO: remove "code" attribute (currently it used for backward-compatibility)
 -define(STANZA_ERROR(Code, Type, Condition),
 	{xmlelement, "error",
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
--- ejabberd-2.0.4/src/mod_muc/mod_muc_room.erl	2009-03-12 09:41:02.000000000 +0100
+++ ejabberd-2.0.4-new/src/mod_muc/mod_muc_room.erl	2009-03-14 11:40:40.000000000 +0100
@@ -69,6 +69,7 @@
 		 public_list = true,
 		 persistent = false,
 		 moderated = true,
+		 captcha_protected = false,
 		 members_by_default = true,
 		 members_only = false,
 		 allow_user_invites = false,
@@ -98,6 +99,7 @@
 		jid,
 		config = #config{},
 		users = ?DICT:new(),
+		robots = ?DICT:new(),
 		affiliations = ?DICT:new(),
 		history = lqueue_new(20),
 		subject = "",
@@ -382,7 +384,8 @@
 	      (XMLNS == ?NS_MUC_ADMIN) or
 	      (XMLNS == ?NS_MUC_OWNER) or
 	      (XMLNS == ?NS_DISCO_INFO) or
-	      (XMLNS == ?NS_DISCO_ITEMS) ->
+	      (XMLNS == ?NS_DISCO_ITEMS) or 
+	      (XMLNS == ?NS_CAPTCHA) ->
 	    Res1 = case XMLNS of
 		       ?NS_MUC_ADMIN ->
 			   process_iq_admin(From, Type, Lang, SubEl, StateData);
@@ -391,7 +394,9 @@
 		       ?NS_DISCO_INFO ->
 			   process_iq_disco_info(From, Type, Lang, StateData);
 		       ?NS_DISCO_ITEMS ->
-			   process_iq_disco_items(From, Type, Lang, StateData)
+			   process_iq_disco_items(From, Type, Lang, StateData);
+		       ?NS_CAPTCHA ->
+			   process_iq_captcha(From, Type, Lang, SubEl, StateData)
 		   end,
 	    {IQRes, NewStateData} =
 		case Res1 of
@@ -761,6 +766,30 @@
 	{empty, _} ->
 	    {next_state, StateName, StateData}
     end;
+handle_info({captcha_succeed, From}, normal_state, StateData) ->
+    NewState = case ?DICT:find(From, StateData#state.robots) of
+		   {ok, {Nick, Packet}} ->
+		       Robots = ?DICT:store(From, passed, StateData#state.robots),
+		       add_new_user(From, Nick, Packet, StateData#state{robots=Robots});
+		   _ ->
+		       StateData
+	       end,
+    {next_state, normal_state, NewState};
+handle_info({captcha_failed, From}, normal_state, StateData) ->
+    NewState = case ?DICT:find(From, StateData#state.robots) of
+		   {ok, {Nick, Packet}} ->
+		       Robots = ?DICT:erase(From, StateData#state.robots),
+		       Err = jlib:make_error_reply(
+			       Packet, ?ERR_NOT_AUTHORIZED),
+		       ejabberd_router:route( % TODO: s/Nick/""/
+			 jlib:jid_replace_resource(
+			   StateData#state.jid, Nick),
+			 From, Err),
+		       StateData#state{robots=Robots};
+		   _ ->
+		       StateData
+	       end,
+    {next_state, normal_state, NewState};
 handle_info(_Info, StateName, StateData) ->
     {next_state, StateName, StateData}.
 
@@ -1461,7 +1490,8 @@
 	      From, Err),
 	    StateData;
 	{_, _, _, Role} ->
-	    case check_password(ServiceAffiliation, Els, StateData) of
+	    case check_password(ServiceAffiliation, Affiliation,
+				Els, From, StateData) of
 		true ->
 		    NewState =
 			add_user_presence(
@@ -1494,7 +1524,8 @@
 			true ->
 			    NewState#state{just_created = false};
 			false ->
-			    NewState
+			    Robots = ?DICT:erase(From, StateData#state.robots),
+			    NewState#state{robots = Robots}
 		    end;
 		nopass ->
 		    ErrText = "Password required to enter this room",
@@ -1505,6 +1536,29 @@
 			StateData#state.jid, Nick),
 		      From, Err),
 		    StateData;
+                captcha_required ->
+		    ID = randoms:get_string(),
+		    SID = xml:get_attr_s("id", Attrs),
+		    RoomJID = StateData#state.jid,
+		    To = jlib:jid_replace_resource(RoomJID, Nick),
+		    case ejabberd_captcha:create_captcha(
+			   ID, SID, RoomJID, To, Lang, From) of
+			{ok, CaptchaEls} ->
+			    MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls},
+			    Robots = ?DICT:store(From,
+						 {Nick, Packet}, StateData#state.robots),
+			    ejabberd_router:route(RoomJID, From, MsgPkt),
+			    StateData#state{robots = Robots};
+			error ->
+			    ErrText = "Unable to generate a captcha",
+			    Err = jlib:make_error_reply(
+				    Packet, ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText)),
+			    ejabberd_router:route( % TODO: s/Nick/""/
+			      jlib:jid_replace_resource(
+				StateData#state.jid, Nick),
+			      From, Err),
+			    StateData
+		    end;
 		_ ->
 		    ErrText = "Incorrect password",
 		    Err = jlib:make_error_reply(
@@ -1517,13 +1571,13 @@
 	   end
     end.
 
-check_password(owner, _Els, _StateData) ->
+check_password(owner, _Affiliation, _Els, _From, _StateData) ->
     %% Don't check pass if user is owner in MUC service (access_admin option)
     true;
-check_password(_ServiceAffiliation, Els, StateData) ->
+check_password(_ServiceAffiliation, Affiliation, Els, From, StateData) ->
     case (StateData#state.config)#config.password_protected of
 	false ->
-	    true;
+	    check_captcha(Affiliation, From, StateData);
 	true ->
 	    Pass = extract_password(Els),
 	    case Pass of
@@ -1539,6 +1593,19 @@
 	    end
     end.
 
+check_captcha(Affiliation, From, StateData) ->
+    case (StateData#state.config)#config.captcha_protected of
+	true when Affiliation == none ->
+	    case ?DICT:find(From, StateData#state.robots) of
+		{ok, passed} ->
+		    true;
+		_ ->
+		    captcha_required
+	    end;
+	_ ->
+	    true
+    end.
+
 extract_password([]) ->
     false;
 extract_password([{xmlelement, _Name, Attrs, _SubEls} = El | Els]) ->
@@ -2713,6 +2780,9 @@
 	 ?BOOLXFIELD("Make room members-only",
 		     "muc#roomconfig_membersonly",
 		     Config#config.members_only),
+	 ?BOOLXFIELD("Make room captcha protected",
+		     "captcha_protected",
+		     Config#config.captcha_protected),
 	 ?BOOLXFIELD("Make room moderated",
 		     "muc#roomconfig_moderatedroom",
 		     Config#config.moderated),
@@ -2823,6 +2893,8 @@
     ?SET_BOOL_XOPT(members_by_default, Val);
 set_xoption([{"muc#roomconfig_membersonly", [Val]} | Opts], Config) ->
     ?SET_BOOL_XOPT(members_only, Val);
+set_xoption([{"captcha_protected", [Val]} | Opts], Config) ->
+    ?SET_BOOL_XOPT(captcha_protected, Val);
 set_xoption([{"muc#roomconfig_allowinvites", [Val]} | Opts], Config) ->
     ?SET_BOOL_XOPT(allow_user_invites, Val);
 set_xoption([{"muc#roomconfig_passwordprotectedroom", [Val]} | Opts], Config) ->
@@ -2913,6 +2985,7 @@
 	      ?CASE_CONFIG_OPT(members_only);
 	      ?CASE_CONFIG_OPT(allow_user_invites);
 	      ?CASE_CONFIG_OPT(password_protected);
+	      ?CASE_CONFIG_OPT(captcha_protected);
 	      ?CASE_CONFIG_OPT(password);
 	      ?CASE_CONFIG_OPT(anonymous);
 	      ?CASE_CONFIG_OPT(logging);
@@ -2954,6 +3027,7 @@
      ?MAKE_CONFIG_OPT(members_only),
      ?MAKE_CONFIG_OPT(allow_user_invites),
      ?MAKE_CONFIG_OPT(password_protected),
+     ?MAKE_CONFIG_OPT(captcha_protected),
      ?MAKE_CONFIG_OPT(password),
      ?MAKE_CONFIG_OPT(anonymous),
      ?MAKE_CONFIG_OPT(logging),
@@ -3074,6 +3148,17 @@
 	    {error, ?ERR_FORBIDDEN}
     end.
 
+process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) ->
+    {error, ?ERR_NOT_ALLOWED};
+
+process_iq_captcha(_From, set, _Lang, SubEl, StateData) ->
+    case ejabberd_captcha:process_reply(SubEl) of
+	ok ->
+	    {result, [], StateData};
+	_ ->
+	    {error, ?ERR_NOT_ACCEPTABLE}
+    end.
+
 get_title(StateData) ->
     case (StateData#state.config)#config.title of
 	"" ->
diff -urN ejabberd-2.0.4/src/web/ejabberd_http.erl ejabberd-2.0.4-new/src/web/ejabberd_http.erl
--- ejabberd-2.0.4/src/web/ejabberd_http.erl	2009-03-12 09:41:02.000000000 +0100
+++ ejabberd-2.0.4-new/src/web/ejabberd_http.erl	2009-03-14 10:57:34.000000000 +0100
@@ -106,6 +106,10 @@
 	    {value, {request_handlers, H}} -> H;
 	    false -> []
         end ++
+	case lists:member(captcha, Opts) of
+	    true -> [{["captcha"], ejabberd_captcha}];
+	    false -> []
+	end ++
         case lists:member(web_admin, Opts) of
             true -> [{["admin"], ejabberd_web_admin}];
             false -> []
Binary files ejabberd-2.0.4/tools/.DS_Store and ejabberd-2.0.4-new/tools/.DS_Store differ
diff -urN ejabberd-2.0.4/tools/captcha.sh ejabberd-2.0.4-new/tools/captcha.sh
--- ejabberd-2.0.4/tools/captcha.sh	1970-01-01 01:00:00.000000000 +0100
+++ ejabberd-2.0.4-new/tools/captcha.sh	2009-03-14 11:46:42.000000000 +0100
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+SIGN=$(($RANDOM % 2))
+
+R1=$(($RANDOM % 20))
+R2=$(($RANDOM % 10 + 40))
+
+if [ $SIGN -eq "0" ]; then
+    S1=$(( -1*($RANDOM % 20 + 50) ))
+    S2=$(( $RANDOM % 20 + 50 ))
+else
+    S2=$(( -1*($RANDOM % 20 + 50) ))
+    S1=$(( $RANDOM % 20 + 50 ))
+fi
+
+convert -size 140x60 xc:white \
+    -pointsize 30 -draw "text 20,30 '$1'" \
+    -roll -$R2+$R1 -swirl $S1 \
+    -roll +$R2-$R1 -swirl $S2 \
+    +repage -resize 120x60 \
+    -quality 90 -depth 8 png:-
--- ejabberd-2.0.5/ChangeLog~	2009-04-01 19:23:52.000000000 +0400
+++ ejabberd-2.0.5/ChangeLog	2009-04-03 23:45:03.174979944 +0400
@@ -15,6 +15,15 @@
 	stanza (EJAB-300).
 	* src/ejabberd_c2s.erl: Likewise
 
+2009-03-13  Evgeniy Khramtsov <ekhramtsov at process-one.net>
+
+	* src/ejabberd_captcha.erl: XEP-158 (CAPTCHA Forms).
+	* src/ejabberd_config.erl: likewise.
+	* src/ejabberd_sup.erl: likewise.
+	* src/jlib.hrl: likewise.
+	* src/web/ejabberd_http.erl: likewise.
+	* src/mod_muc/mod_muc_room.erl: CAPTCHA support.
+
 2009-03-10  Badlop  <badlop at process-one.net>
 
 	* doc/release_notes_2.0.4.txt: Added file for new release


Index: .cvsignore
===================================================================
RCS file: /cvs/pkgs/rpms/ejabberd/F-9/.cvsignore,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- .cvsignore	15 Mar 2009 11:43:08 -0000	1.12
+++ .cvsignore	3 Apr 2009 20:19:43 -0000	1.13
@@ -1 +1 @@
-ejabberd-2.0.4.tar.gz
+ejabberd-2.0.5.tar.gz


Index: ejabberd.spec
===================================================================
RCS file: /cvs/pkgs/rpms/ejabberd/F-9/ejabberd.spec,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -r1.30 -r1.31
--- ejabberd.spec	15 Mar 2009 11:43:08 -0000	1.30
+++ ejabberd.spec	3 Apr 2009 20:19:43 -0000	1.31
@@ -1,5 +1,5 @@
 Name:           ejabberd
-Version:        2.0.4
+Version:        2.0.5
 Release:        1%{?dist}
 Summary:        A distributed, fault-tolerant Jabber/XMPP server
 
@@ -12,7 +12,7 @@
 Source3:	ejabberd.sysconfig
 
 # http://www.ejabberd.im/mod_ctlextra
-# svn export -r 557 https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl
+# svn export -r 873 https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl
 Source4:        mod_ctlextra.erl
 
 # The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html
@@ -31,6 +31,10 @@
 Patch2: ejabberd-ejabberdctl_fix.diff
 # Use ejabberd as an example for PAM service name
 Patch3: ejabberd-ejabberd_cfg_pam_name.diff
+# CAPTCHA support, see https://support.process-one.net/doc/display/XAAI/Fighting+Multi-User+Chat+SPAM+with+CAPTCHA
+# wget https://support.process-one.net/secure/attachment/13851/XEP-0158_ejabberd-2.0.4.patch -O ejabberd-captcha.patch
+Patch4: ejabberd-captcha.patch
+
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -113,6 +117,7 @@
 %patch1 -p0 -b .fix_paths
 %patch2 -p0 -b .fix_user
 %patch3 -p0 -b .pam_name
+#%patch4 -p1 -b .captcha
 dos2unix src/odbc/mssql.sql
 
 cp %{S:4} src
@@ -172,7 +177,7 @@
 
 %files
 %defattr(-,root,root,-)
-%doc COPYING src/odbc/mysql.sql src/odbc/mssql.sql src/odbc/pg.sql
+%doc COPYING src/odbc/mysql.sql src/odbc/mssql.sql src/odbc/pg.sql tools/captcha.sh
 
 %attr(750,ejabberd,ejabberd) %dir %{_sysconfdir}/ejabberd
 %attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberd.cfg
@@ -214,6 +219,14 @@
 %endif
 
 %changelog
+* Fri Apr  3 2009 Peter Lemenkov <lemenkov at gmail.com> 2.0.5-1
+- Ver. 2.0.5
+- Temporarily disabled CAPTCHA support
+
+* Sun Mar 15 2009 Peter Lemenkov <lemenkov at gmail.com> 2.0.4-2
+- Support for CAPTCHA (XEP-0158)
+- Updated mod_ctlextra.erl (fixed EJAB-789, EJAB-864)
+
 * Sun Mar 15 2009 Peter Lemenkov <lemenkov at gmail.com> 2.0.4-1
 - Ver. 2.0.4
 


Index: import.log
===================================================================
RCS file: /cvs/pkgs/rpms/ejabberd/F-9/import.log,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- import.log	15 Mar 2009 11:43:08 -0000	1.6
+++ import.log	3 Apr 2009 20:19:43 -0000	1.7
@@ -4,3 +4,4 @@
 ejabberd-2_0_2-3_fc9:F-9:ejabberd-2.0.2-3.fc9.src.rpm:1222925461
 ejabberd-2_0_3-1_fc10:F-9:ejabberd-2.0.3-1.fc10.src.rpm:1232988519
 ejabberd-2_0_4-1_fc10:F-9:ejabberd-2.0.4-1.fc10.src.rpm:1237117113
+ejabberd-2_0_5-1_fc10:F-9:ejabberd-2.0.5-1.fc10.src.rpm:1238789863


Index: mod_ctlextra.erl
===================================================================
RCS file: /cvs/pkgs/rpms/ejabberd/F-9/mod_ctlextra.erl,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- mod_ctlextra.erl	29 Aug 2008 20:18:07 -0000	1.4
+++ mod_ctlextra.erl	3 Apr 2009 20:19:43 -0000	1.5
@@ -47,14 +47,12 @@
 
      %% ejabberd_auth
      {"delete-older-users days", "delete users that have not logged in the last 'days'"},
+     {"delete-older-users-vhost host days", "delete users that not logged in last 'days' in 'host'"},
      {"set-password user server password", "set password to user at server"},
 
      %% ejd2odbc
      {"export2odbc server output", "export Mnesia tables on server to files on output directory"},
 
-     %% mod_offline
-     {"delete-older-messages days", "delete offline messages older than 'days'"},
-
      %% mod_shared_roster
      {"srg-create group host name description display", "create the group with options"},
      {"srg-delete group host", "delete the group"},
@@ -113,15 +111,16 @@
 %% Commands global
 %%-------------
 
-ctl_process(_Val, ["delete-older-messages", Days]) ->
-    mod_offline:remove_old_messages(list_to_integer(Days)),
-    ?STATUS_SUCCESS;
-
 ctl_process(_Val, ["delete-older-users", Days]) ->
     {removed, N, UR} = delete_older_users(list_to_integer(Days)),
     io:format("Deleted ~p users: ~p~n", [N, UR]),
     ?STATUS_SUCCESS;
 
+ctl_process(_Val, ["delete-older-users-vhost", Host, Days]) ->
+    {removed, N, UR} = delete_older_users_vhost(Host, list_to_integer(Days)),
+    io:format("Deleted ~p users: ~p~n", [N, UR]),
+    ?STATUS_SUCCESS;
+
 ctl_process(_Val, ["export2odbc", Server, Output]) ->
     Tables = [
 	      {export_last, last},
@@ -357,7 +356,7 @@
 ctl_process(_Val, ["stats", Stat]) ->
     Res = case Stat of
 	      "uptime-seconds" -> uptime_seconds();
-	      "registeredusers" -> mnesia:table_info(passwd, size);
+	      "registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
 	      "onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
 	      "onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
 	  end,
@@ -752,6 +751,16 @@
 -record(last_activity, {us, timestamp, status}).
 
 delete_older_users(Days) ->
+    %% Get the list of registered users
+    Users = ejabberd_auth:dirty_get_registered_users(),
+    delete_older_users(Days, Users).
+
+delete_older_users_vhost(Host, Days) ->
+    %% Get the list of registered users
+    Users = ejabberd_auth:get_vh_registered_users(Host),
+    delete_older_users(Days, Users).
+
+delete_older_users(Days, Users) ->
     %% Convert older time
     SecOlder = Days*24*60*60,
 
@@ -759,9 +768,6 @@
     {MegaSecs, Secs, _MicroSecs} = now(),
     TimeStamp_now = MegaSecs * 1000000 + Secs,
 
-    %% Get the list of registered users
-    Users = ejabberd_auth:dirty_get_registered_users(),
-
     %% For a user, remove if required and answer true
     F = fun({LUser, LServer}) ->
 		%% Check if the user is logged


Index: sources
===================================================================
RCS file: /cvs/pkgs/rpms/ejabberd/F-9/sources,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- sources	15 Mar 2009 11:43:08 -0000	1.14
+++ sources	3 Apr 2009 20:19:43 -0000	1.15
@@ -1 +1 @@
-1ff37c329e9bc1ddf3c458e051b81d63  ejabberd-2.0.4.tar.gz
+2d85b47df969daf0a78ed3b16562d731  ejabberd-2.0.5.tar.gz




More information about the fedora-extras-commits mailing list