diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ejabberd_c2s.erl | 3 | ||||
| -rw-r--r-- | src/ejabberd_local.erl | 7 | ||||
| -rw-r--r-- | src/ejabberd_receiver.erl | 6 | ||||
| -rw-r--r-- | src/ejabberd_sm.erl | 15 | ||||
| -rw-r--r-- | src/mod_caps.erl | 12 | ||||
| -rw-r--r-- | src/mod_pubsub/gen_pubsub_node.erl | 2 | ||||
| -rw-r--r-- | src/mod_pubsub/mod_pubsub.erl | 102 | ||||
| -rw-r--r-- | src/mod_pubsub/node.template | 9 | ||||
| -rw-r--r-- | src/mod_pubsub/node_buddy.erl | 8 | ||||
| -rw-r--r-- | src/mod_pubsub/node_club.erl | 9 | ||||
| -rw-r--r-- | src/mod_pubsub/node_default.erl | 116 | ||||
| -rw-r--r-- | src/mod_pubsub/node_dispatch.erl | 9 | ||||
| -rw-r--r-- | src/mod_pubsub/node_pep.erl | 11 | ||||
| -rw-r--r-- | src/mod_pubsub/node_private.erl | 9 | ||||
| -rw-r--r-- | src/mod_pubsub/node_public.erl | 9 | ||||
| -rw-r--r-- | src/mod_pubsub/node_zoo.erl | 177 | ||||
| -rw-r--r-- | src/web/ejabberd_http.erl | 14 | ||||
| -rw-r--r-- | src/web/ejabberd_http_poll.erl | 40 | 
18 files changed, 484 insertions, 74 deletions
| diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 358b1fe32..5afd6cc53 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1267,6 +1267,9 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->  handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData)    when Monitor == StateData#state.socket_monitor ->      {stop, normal, StateData}; +handle_info({peername, IP}, StateName, StateData) -> +    ejabberd_sm:set_session_ip(StateData#state.sid, IP), +    fsm_next_state(StateName, StateData#state{ip = IP});  handle_info(Info, StateName, StateData) ->      ?ERROR_MSG("Unexpected info: ~p", [Info]),      fsm_next_state(StateName, StateData). diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index b54173581..67f5b9636 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -37,6 +37,7 @@  	 register_iq_handler/5,  	 register_iq_response_handler/4,  	 unregister_iq_handler/2, +	 unregister_iq_response_handler/2,  	 refresh_iq_handlers/0,  	 bounce_resource_packet/3  	]). @@ -138,6 +139,9 @@ register_iq_handler(Host, XMLNS, Module, Fun) ->  register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->      ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. +unregister_iq_response_handler(Host, ID) -> +    ejabberd_local ! {unregister_iq_response_handler, Host, ID}. +  unregister_iq_handler(Host, XMLNS) ->      ejabberd_local ! {unregister_iq_handler, Host, XMLNS}. @@ -214,6 +218,9 @@ handle_info({route, From, To, Packet}, State) ->  handle_info({register_iq_response_handler, _Host, ID, Module, Function}, State) ->      mnesia:dirty_write(#iq_response{id = ID, module = Module, function = Function}),      {noreply, State}; +handle_info({unregister_iq_response_handler, _Host, ID}, State) -> +    mnesia:dirty_delete({iq_response, ID}), +    {noreply, State};  handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->      ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),      catch mod_disco:register_feature(Host, XMLNS), diff --git a/src/ejabberd_receiver.erl b/src/ejabberd_receiver.erl index 72b3608d6..c178df62a 100644 --- a/src/ejabberd_receiver.erl +++ b/src/ejabberd_receiver.erl @@ -269,7 +269,8 @@ code_change(_OldVsn, State, _Extra) ->  %%--------------------------------------------------------------------  activate_socket(#state{socket = Socket, -		       sock_mod = SockMod}) -> +		       sock_mod = SockMod, +		       c2s_pid = C2SPid}) ->      PeerName =  	case SockMod of  	    gen_tcp -> @@ -282,7 +283,8 @@ activate_socket(#state{socket = Socket,      case PeerName of  	{error, _Reason} ->  	    self() ! {tcp_closed, Socket}; -	{ok, _} -> +	{ok, IP} -> +	    C2SPid ! {peername, IP},  	    ok      end. diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 2d4b7c5b9..bb543707f 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -49,7 +49,8 @@  	 ctl_process/2,  	 get_session_pid/3,  	 get_user_info/3, -	 get_user_ip/3 +	 get_user_ip/3, +	 set_session_ip/2  	]).  %% gen_server callbacks @@ -164,6 +165,18 @@ get_user_info(User, Server, Resource) ->  	    [{node, Node}, {conn, Conn}, {ip, IP}]      end. +set_session_ip(SID, IP) -> +    case mnesia:dirty_read(session, SID) of +    [#session{info = Info} = Session] -> +	NewInfo = case lists:keymember(ip, 1, Info) of +	true -> lists:keyreplace(ip, 1, Info, {ip, IP}); +	false -> [{ip, IP}|Info] +	end, +	mnesia:dirty_write(Session#session{info = NewInfo}); +    _ -> +	ok +    end. +  set_presence(SID, User, Server, Resource, Priority, Presence, Info) ->      set_session(SID, User, Server, Resource, Priority, Info),      ejabberd_hooks:run(set_presence_hook, jlib:nameprep(Server), diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 2be0fac66..3240aa6e2 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -53,6 +53,7 @@  -define(PROCNAME, ejabberd_mod_caps).  -define(DICT, dict). +-define(CAPS_QUERY_TIMEOUT, 60000). % 1mn without answer, consider client never answer  -record(caps, {node, version, exts}).  -record(caps_features, {node_pair, features}). @@ -220,6 +221,7 @@ handle_cast({note_caps, From,  			  ejabberd_local:register_iq_response_handler  			    (Host, ID, ?MODULE, handle_disco_response),  			  ejabberd_router:route(jlib:make_jid("", Host, ""), From, Stanza), +			  timer:send_after(?CAPS_QUERY_TIMEOUT, self(), {disco_timeout, ID}),  			  ?DICT:store(ID, {Node, SubNode}, Dict)  		  end, Requests, Missing),  	    {noreply, State#state{disco_requests = NewRequests}}; @@ -274,6 +276,16 @@ handle_cast({disco_response, From, _To,      end,      NewRequests = ?DICT:erase(ID, Requests),      {noreply, State#state{disco_requests = NewRequests}}; +handle_cast({disco_timeout, ID}, #state{host = Host, disco_requests = Requests} = State) -> +    %% do not wait a response anymore for this IQ, client certainly will never answer +    NewRequests = case ?DICT:is_key(ID, Requests) of +    true -> +	ejabberd_local:unregister_iq_response_handler(Host, ID), +	?DICT:erase(ID, Requests); +    false -> +	Requests +    end, +    {noreply, State#state{disco_requests = NewRequests}};  handle_cast(visit_feature_queries, #state{feature_queries = FeatureQueries} = State) ->      Timestamp = timestamp(),      NewFeatureQueries = diff --git a/src/mod_pubsub/gen_pubsub_node.erl b/src/mod_pubsub/gen_pubsub_node.erl index 195ed3934..bbbb5c409 100644 --- a/src/mod_pubsub/gen_pubsub_node.erl +++ b/src/mod_pubsub/gen_pubsub_node.erl @@ -62,7 +62,9 @@ behaviour_info(callbacks) ->       {get_states, 2},       {get_state, 3},       {set_state, 1}, +     {get_items, 7},       {get_items, 2}, +     {get_item, 8},       {get_item, 3},       {set_item, 1},       {get_item_name, 3} diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 349c7ad4f..92f5cf959 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -30,16 +30,17 @@  %%%  %%% @reference See <a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060: Pubsub</a> for  %%% the latest version of the PubSub specification. -%%% This module uses version 1.10 of the specification as a base. +%%% This module uses version 1.11 of the specification as a base.  %%% Most of the specification is implemented. -%%% Code is derivated from the original pubsub v1.7, functions concerning config may be rewritten. -%%% Code also inspired from the original PEP patch by Magnus Henoch (mangeATfreemail.hu) +%%% Functions concerning configuration should be rewritten. +%%% Code is derivated from the original pubsub v1.7, by Alexey Shchepin <alexey@process-one.net>  %%% TODO  %%% plugin: generate Reply (do not use broadcast atom anymore)  -module(mod_pubsub). --version('1.10-01'). +-author('christophe.romain@process-one.net'). +-version('1.11-01').  -behaviour(gen_server).  -behaviour(gen_mod). @@ -912,7 +913,17 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, _Lang, Access, Plugins) ->  		    unsubscribe_node(Host, Node, From, JID, SubId);  		{get, "items"} ->  		    MaxItems = xml:get_attr_s("max_items", Attrs), -		    get_items(Host, Node, From, MaxItems); +		    SubId = xml:get_attr_s("subid", Attrs), +		    ItemIDs = lists:foldl(fun +			({xmlelement, "item", ItemAttrs, _}, Acc) -> +			    case xml:get_attr_s("id", ItemAttrs) of +			    "" -> Acc; +			    ItemID -> ItemID +			    end; +			(_, Acc) -> +			    Acc +			end, [], xml:remove_cdata(Els)), +		    get_items(Host, Node, From, SubId, MaxItems, ItemIDs);  		{get, "subscriptions"} ->  		    get_subscriptions(Host, From, Plugins);  		{get, "affiliations"} -> @@ -1436,7 +1447,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->      Action = fun(#pubsub_node{options = Options, type = Type}) ->  		     Features = features(Type),  		     PublishFeature = lists:member("publish", Features), -		     Model = get_option(Options, publish_model), +		     PublishModel = get_option(Options, publish_model),  		     MaxItems = max_items(Options),  		     PayloadSize = size(term_to_binary(Payload)),  		     PayloadMaxSize = get_option(Options, max_payload_size), @@ -1459,7 +1470,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->  			 %%	% Publisher attempts to publish to transient notification node with item  			 %%	{error, extended_error(?ERR_BAD_REQUEST, "item-forbidden")};  			 true -> -			     node_call(Type, publish_item, [Host, Node, Publisher, Model, MaxItems, ItemId, Payload]) +			     node_call(Type, publish_item, [Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload])  		     end  	     end,      %%ejabberd_hooks:run(pubsub_publish_item, Host, [Host, Node, JID, service_jid(Host), ItemId, Payload]), @@ -1622,7 +1633,7 @@ purge_node(Host, Node, Owner) ->  %% <p>The permission are not checked in this function.</p>  %% @todo We probably need to check that the user doing the query has the right  %% to read the items. -get_items(Host, Node, _JID, SMaxItems) -> +get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->      MaxItems =  	if  	    SMaxItems == "" -> ?MAXITEMS; @@ -1636,24 +1647,60 @@ get_items(Host, Node, _JID, SMaxItems) ->  	{error, Error} ->  	    {error, Error};  	_ -> -	    case get_items(Host, Node) of -		[] -> -		    {error, ?ERR_ITEM_NOT_FOUND}; -		Items -> +	    Action = fun(#pubsub_node{options = Options, type = Type}) -> +		     Features = features(Type), +		     RetreiveFeature = lists:member("retrieve-items", Features), +		     PersistentFeature = lists:member("persistent-items", Features), +		     AccessModel = get_option(Options, access_model), +		     AllowedGroups = get_option(Options, roster_groups_allowed), +		     {PresenceSubscription, RosterGroup} = +			 case Host of +			     {OUser, OServer, _} -> +				 get_roster_info(OUser, OServer, +						 jlib:jid_tolower(From), AllowedGroups); +			     _ -> +				 {true, true} +			 end, +		     if +			 not RetreiveFeature -> +			     %% Item Retrieval Not Supported +			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "retrieve-items")}; +			 not PersistentFeature -> +			     %% Persistent Items Not Supported +			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "persistent-items")}; +			 true -> +			     node_call(Type, get_items, +				       [Host, Node, From, +					AccessModel, PresenceSubscription, RosterGroup, +					SubId]) +		     end +	     end, +	     case transaction(Host, Node, Action, sync_dirty) of +		{error, Reason} -> +		    {error, Reason}; +		{result, Items} -> +		    SendItems = case ItemIDs of +			[] ->  +			    Items; +			_ -> +			    lists:filter(fun(Item) -> +				lists:member(Item, ItemIDs) +			    end, Items)  +			end,  		    %% Generate the XML response (Item list), limiting the  		    %% number of items sent to MaxItems:  		    ItemsEls = lists:map( -				 fun(#pubsub_item{itemid = {ItemId, _}, -						  payload = Payload}) -> -					 ItemAttrs = case ItemId of -							 "" -> []; -							 _ -> [{"id", ItemId}] -						     end, -					 {xmlelement, "item", ItemAttrs, Payload} -				 end, lists:sublist(Items, MaxItems)), +				    fun(#pubsub_item{itemid = {ItemId, _}, +						    payload = Payload}) -> +					    ItemAttrs = case ItemId of +							    "" -> []; +							    _ -> [{"id", ItemId}] +							end, +					    {xmlelement, "item", ItemAttrs, Payload} +				    end, lists:sublist(SendItems, MaxItems)),  		    {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], -			       [{xmlelement, "items", [{"node", node_to_string(Node)}], -				 ItemsEls}]}]} +				[{xmlelement, "items", [{"node", node_to_string(Node)}], +				    ItemsEls}]}]}  	    end      end. @@ -1809,7 +1856,7 @@ set_affiliations(Host, Node, From, EntitiesEls) ->  				       end, Entities),  				     {result, []};  				 _ -> -				     {error, ?ERR_NOT_ALLOWED} +				     {error, ?ERR_FORBIDDEN}  			     end  		     end,  	    transaction(Host, Node, Action, sync_dirty) @@ -1870,7 +1917,7 @@ get_subscriptions(Host, Node, JID) ->  			     %% Service does not support manage subscriptions  			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-affiliations")};  			 Affiliation /= {result, owner} -> -						% Entity is not an owner +			     %% Entity is not an owner  			     {error, ?ERR_FORBIDDEN};  			 true ->  			     node_call(Type, get_node_subscriptions, [Host, Node]) @@ -1938,7 +1985,7 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->  						   end, Entities),  				     {result, []};  				 _ -> -				     {error, ?ERR_NOT_ALLOWED} +				     {error, ?ERR_FORBIDDEN}  			     end  		     end,  	    transaction(Host, Node, Action, sync_dirty) @@ -2438,6 +2485,7 @@ get_configure_xfields(_Type, Options, Lang, _Owners) ->       ?BOOL_CONFIG_FIELD("Notify subscribers when the node is deleted", notify_delete),       ?BOOL_CONFIG_FIELD("Notify subscribers when items are removed from the node", notify_retract),       ?BOOL_CONFIG_FIELD("Persist items to storage", persist_items), +     ?STRING_CONFIG_FIELD("A friendly name for the node", title),       ?INTEGER_CONFIG_FIELD("Max # of items to persist", max_items),       ?BOOL_CONFIG_FIELD("Whether to allow subscriptions", subscribe),       ?ALIST_CONFIG_FIELD("Specify the access model", access_model, @@ -2605,7 +2653,7 @@ features() ->  	[  	 %"access-authorize",   % OPTIONAL  	 "access-open",   % OPTIONAL this relates to access_model option in node_default -	 %"access-presence",   % OPTIONAL +	 "access-presence",   % OPTIONAL this relates to access_model option in node_pep  	 %"access-roster",   % OPTIONAL  	 %"access-whitelist",   % OPTIONAL  	 % see plugin "auto-create",   % OPTIONAL @@ -2619,7 +2667,7 @@ features() ->  	 % see plugin "filtered-notifications",   % RECOMMENDED  	 %TODO "get-pending",   % OPTIONAL  	 % see plugin "instant-nodes",   % RECOMMENDED -	 %TODO "item-ids",   % RECOMMENDED +	 "item-ids",   % RECOMMENDED  	 "last-published",   % RECOMMENDED  	 %TODO "cache-last-item",  	 %TODO "leased-subscription",   % OPTIONAL diff --git a/src/mod_pubsub/node.template b/src/mod_pubsub/node.template index 850a38ec9..973aa38a4 100644 --- a/src/mod_pubsub/node.template +++ b/src/mod_pubsub/node.template @@ -61,7 +61,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1  	]). @@ -94,7 +96,6 @@ features() ->      ["create-nodes",       "delete-nodes",       "instant-nodes", -     "item-ids",       "outcast-affiliation",       "persistent-items",       "publish", @@ -170,8 +171,14 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_buddy.erl b/src/mod_pubsub/node_buddy.erl index cdf3a696f..cd051b66f 100644 --- a/src/mod_pubsub/node_buddy.erl +++ b/src/mod_pubsub/node_buddy.erl @@ -62,7 +62,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -172,9 +174,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_club.erl b/src/mod_pubsub/node_club.erl index d927053fb..155edc10a 100644 --- a/src/mod_pubsub/node_club.erl +++ b/src/mod_pubsub/node_club.erl @@ -62,7 +62,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -96,7 +98,6 @@ features() ->      ["create-nodes",       "delete-nodes",       "instant-nodes", -     "item-ids",       "outcast-affiliation",       "persistent-items",       "publish", @@ -172,9 +173,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_default.erl b/src/mod_pubsub/node_default.erl index fc4194c32..491676269 100644 --- a/src/mod_pubsub/node_default.erl +++ b/src/mod_pubsub/node_default.erl @@ -69,7 +69,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -158,7 +160,6 @@ features() ->       "auto-create",       "delete-nodes",       "instant-nodes", -     "item-ids",       "manage-subscriptions",       "modify-affiliations",       "outcast-affiliation", @@ -192,18 +193,14 @@ features() ->  %% module by implementing this function like this:  %% ```check_create_user_permission(Host, Node, Owner, Access) ->  %%	   node_default:check_create_user_permission(Host, Node, Owner, Access).'''</p> -create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> +create_node_permission(_Host, ServerHost, Node, _ParentNode, Owner, Access) ->      LOwner = jlib:jid_tolower(Owner),      {User, Server, _Resource} = LOwner,      Allowed = case acl:match_rule(ServerHost, Access, LOwner) of  		allow -> -		    if Server == Host ->  %% Server == ServerHost ?? -			true; -		    true -> -			case Node of -			    ["home", Server, User | _] -> true; -			    _ -> false -			end +		    case Node of +			["home", Server, User | _] -> true; +			_ -> false  		    end;  		_ ->  		    case Owner of @@ -211,8 +208,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->  			_ -> false  		    end  	    end, -    ChildOK = true, %% TODO test with ParentNode -    {result, Allowed and ChildOK}. +    {result, Allowed}.  %% @spec (Host, Node, Owner) ->  %%		  {result, Result} | exit @@ -297,12 +293,12 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel,  	not Authorized ->  	    %% JIDs do not match  	    {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "invalid-jid")}; -	Subscription == pending -> -	    %% Requesting entity has pending subscription -	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "pending-subscription")};  	Affiliation == outcast ->  	    %% Requesting entity is blocked  	    {error, ?ERR_FORBIDDEN}; +	Subscription == pending -> +	    %% Requesting entity has pending subscription +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "pending-subscription")};  	(AccessModel == presence) and (not PresenceSubscription) ->  	    %% Entity is not authorized to create a subscription (presence subscription required)  	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "presence-subscription-required")}; @@ -446,6 +442,7 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->  	    {error, ?ERR_FORBIDDEN};  	true ->  	    PubId = {PublisherKey, now()}, +	    %% TODO: check creation, presence, roster (EJAB-663)  	    Item = case get_item(Host, Node, ItemId) of  		       {error, ?ERR_ITEM_NOT_FOUND} ->  			   #pubsub_item{itemid = {ItemId, {Host, Node}}, @@ -501,7 +498,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) ->  %%	 ItemId = string()  %% @doc <p>Triggers item deletion.</p>  %% <p>Default plugin: The user performing the deletion must be the node owner -%% or a node publisher e item publisher.</p> +%% or a publisher.</p>  delete_item(Host, Node, Publisher, ItemId) ->      PublisherKey = jlib:jid_tolower(jlib:jid_remove_resource(Publisher)),      State = case get_state(Host, Node, PublisherKey) of @@ -542,17 +539,16 @@ delete_item(Host, Node, Publisher, ItemId) ->  purge_node(Host, Node, Owner) ->      OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),      case get_state(Host, Node, OwnerKey) of -	{error, ?ERR_ITEM_NOT_FOUND} -> -	    %% This should not append (case node does not exists) -	    {error, ?ERR_ITEM_NOT_FOUND};  	{result, #pubsub_state{items = Items, affiliation = owner}} ->  	    lists:foreach(fun(ItemId) ->  				  mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})  			  end, Items),  	    {result, {default, broadcast}}; +	{result, _} -> +	    %% Entity is not owner +	    {error, ?ERR_FORBIDDEN};  	_ -> -	    %% Entity is not an owner -	    {error, ?ERR_FORBIDDEN} +	    {error, ?ERR_ITEM_NOT_FOUND}      end.  %% @spec (Host, JID) -> [{Node,Affiliation}] @@ -588,7 +584,7 @@ get_affiliation(Host, Node, Owner) ->      OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),      Affiliation = case get_state(Host, Node, OwnerKey) of  		      {result, #pubsub_state{affiliation = A}} -> A; -		      _ -> unknown +		      _ -> none  		  end,      {result, Affiliation}. @@ -638,7 +634,7 @@ get_subscription(Host, Node, Owner) ->      OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),      Subscription = case get_state(Host, Node, OwnerKey) of  		       {result, #pubsub_state{subscription = S}} -> S; -		       _ -> unknown +		       _ -> none  		   end,      {result, Subscription}. @@ -713,6 +709,44 @@ get_items(Host, Node) ->      Items = mnesia:match_object(  	      #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}),      {result, Items}. +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> +    {Affiliation, Subscription} =  +	case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of +	{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S};  +	_ -> {none, none} +	end,  +    Subscribed = not ((Subscription == none) or (Subscription == pending)), +    if +	%%SubID == "", ?? -> +	    %% Entity has multiple subscriptions to the node but does not specify a subscription ID +	    %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; +	%%InvalidSubID -> +	    %% Entity is subscribed but specifies an invalid subscription ID +	    %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; +	Affiliation == outcast -> +	    %% Requesting entity is blocked +	    {error, ?ERR_FORBIDDEN}; +	(AccessModel == open) and (not Subscribed) -> +	    %% Entity is not subscribed +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "not-subscribed")}; +	(AccessModel == presence) and (not PresenceSubscription) -> +	    %% Entity is not authorized to create a subscription (presence subscription required) +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "presence-subscription-required")}; +	(AccessModel == roster) and (not RosterGroup) -> +	    %% Entity is not authorized to create a subscription (not in roster group) +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "not-in-roster-group")}; +	(AccessModel == whitelist) ->  % TODO: to be done +	    %% Node has whitelist access model +	    {error, ?ERR_EXTENDED(?ERR_NOT_ALLOWED, "closed-node")}; +	(AccessModel == authorize) -> % TODO: to be done +	    %% Node has authorize access model +	    {error, ?ERR_FORBIDDEN}; +	%%MustPay -> +	%%	% Payment is required for a subscription +	%%	{error, ?ERR_PAYMENT_REQUIRED}; +	true -> +	    get_items(Host, Node) +    end.  %% @spec (Host, Node, ItemId) -> [Item] | []  %%	 Host = mod_pubsub:host() @@ -727,6 +761,44 @@ get_item(Host, Node, ItemId) ->  	_ ->  	    {error, ?ERR_ITEM_NOT_FOUND}      end. +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> +    {Affiliation, Subscription} =  +	case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of +	{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S};  +	_ -> {none, none} +	end,  +    Subscribed = not ((Subscription == none) or (Subscription == pending)), +    if +	%%SubID == "", ?? -> +	    %% Entity has multiple subscriptions to the node but does not specify a subscription ID +	    %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; +	%%InvalidSubID -> +	    %% Entity is subscribed but specifies an invalid subscription ID +	    %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; +	Affiliation == outcast -> +	    %% Requesting entity is blocked +	    {error, ?ERR_FORBIDDEN}; +	(AccessModel == open) and (not Subscribed) -> +	    %% Entity is not subscribed +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "not-subscribed")}; +	(AccessModel == presence) and (not PresenceSubscription) -> +	    %% Entity is not authorized to create a subscription (presence subscription required) +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "presence-subscription-required")}; +	(AccessModel == roster) and (not RosterGroup) -> +	    %% Entity is not authorized to create a subscription (not in roster group) +	    {error, ?ERR_EXTENDED(?ERR_NOT_AUTHORIZED, "not-in-roster-group")}; +	(AccessModel == whitelist) ->  % TODO: to be done +	    %% Node has whitelist access model +	    {error, ?ERR_EXTENDED(?ERR_NOT_ALLOWED, "closed-node")}; +	(AccessModel == authorize) -> % TODO: to be done +	    %% Node has authorize access model +	    {error, ?ERR_FORBIDDEN}; +	%%MustPay -> +	%%	% Payment is required for a subscription +	%%	{error, ?ERR_PAYMENT_REQUIRED}; +	true -> +	    get_item(Host, Node, ItemId) +    end.  %% @spec (Item) -> ok | {error, Reason::stanzaError()}  %%	 Item = mod_pubsub:pubsubItems() diff --git a/src/mod_pubsub/node_dispatch.erl b/src/mod_pubsub/node_dispatch.erl index 3b4418c3e..0532826d2 100644 --- a/src/mod_pubsub/node_dispatch.erl +++ b/src/mod_pubsub/node_dispatch.erl @@ -60,7 +60,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -94,7 +96,6 @@ features() ->      ["create-nodes",       "delete-nodes",       "instant-nodes", -     "item-ids",       "outcast-affiliation",       "persistent-items",       "publish", @@ -175,9 +176,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index 2fceb03ed..2c648148d 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -58,7 +58,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -96,7 +98,6 @@ features() ->       "auto-subscribe", %*       "delete-nodes", %*       "filtered-notifications", %* -     "item-ids",       "modify-affiliations",       "outcast-affiliation",       "persistent-items", @@ -185,7 +186,7 @@ get_node_subscriptions(_Host, _Node) ->      {result, []}.  get_subscription(_Host, _Node, _Owner) -> -    {result, unknown}. +    {result, none}.  set_subscription(_Host, _Node, _Owner, _Subscription) ->      ok. @@ -202,9 +203,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +  set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_private.erl b/src/mod_pubsub/node_private.erl index 35f63c925..00e952392 100644 --- a/src/mod_pubsub/node_private.erl +++ b/src/mod_pubsub/node_private.erl @@ -62,7 +62,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -96,7 +98,6 @@ features() ->      ["create-nodes",       "delete-nodes",       "instant-nodes", -     "item-ids",       "outcast-affiliation",       "persistent-items",       "publish", @@ -175,9 +176,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_public.erl b/src/mod_pubsub/node_public.erl index 1586abe86..ab4107a76 100644 --- a/src/mod_pubsub/node_public.erl +++ b/src/mod_pubsub/node_public.erl @@ -62,7 +62,9 @@  	 get_states/2,  	 get_state/3,  	 set_state/1, +	 get_items/7,  	 get_items/2, +	 get_item/8,  	 get_item/3,  	 set_item/1,  	 get_item_name/3 @@ -96,7 +98,6 @@ features() ->      ["create-nodes",       "delete-nodes",       "instant-nodes", -     "item-ids",       "outcast-affiliation",       "persistent-items",       "publish", @@ -172,9 +173,15 @@ set_state(State) ->  get_items(Host, Node) ->      node_default:get_items(Host, Node). +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      get_item(Host, Node, ItemId) ->      node_default:get_item(Host, Node, ItemId). +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +      set_item(Item) ->      node_default:set_item(Item). diff --git a/src/mod_pubsub/node_zoo.erl b/src/mod_pubsub/node_zoo.erl new file mode 100644 index 000000000..1cb8a73cb --- /dev/null +++ b/src/mod_pubsub/node_zoo.erl @@ -0,0 +1,177 @@ +%%% ==================================================================== +%%% ``The contents of this file are subject to the Erlang Public License, +%%% Version 1.1, (the "License"); you may not use this file except in +%%% compliance with the License. You should have received a copy of the +%%% Erlang Public License along with this software. If not, it can be +%%% retrieved via the world wide web at http://www.erlang.org/. +%%%  +%%% Software distributed under the License is distributed on an "AS IS" +%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%%% the License for the specific language governing rights and limitations +%%% under the License. +%%%  +%%% The Initial Developer of the Original Code is Process-one. +%%% Portions created by Process-one are Copyright 2006-2008, Process-one +%%% All Rights Reserved.'' +%%% This software is copyright 2006-2008, Process-one. +%%% +%%% @copyright 2006-2008 Process-one +%%% @author Christophe romain <christophe.romain@process-one.net> +%%%   [http://www.process-one.net/] +%%% @version {@vsn}, {@date} {@time} +%%% @end +%%% ==================================================================== + +-module(node_zoo). +-author('christophe.romain@process-one.net'). + +-include("pubsub.hrl"). +-include("jlib.hrl"). + +-behaviour(gen_pubsub_node). + +%% API definition +-export([init/3, terminate/2, +	 options/0, features/0, +	 create_node_permission/6, +	 create_node/3, +	 delete_node/2, +	 purge_node/3, +	 subscribe_node/8, +	 unsubscribe_node/5, +	 publish_item/7, +	 delete_item/4, +	 remove_extra_items/4, +	 get_entity_affiliations/2, +	 get_node_affiliations/2, +	 get_affiliation/3, +	 set_affiliation/4, +	 get_entity_subscriptions/2, +	 get_node_subscriptions/2, +	 get_subscription/3, +	 set_subscription/4, +	 get_states/2, +	 get_state/3, +	 set_state/1, +	 get_items/7, +	 get_items/2, +	 get_item/8, +	 get_item/3, +	 set_item/1 +	]). + + +init(Host, ServerHost, Opts) -> +    node_default:init(Host, ServerHost, Opts). + +terminate(Host, ServerHost) -> +    node_default:terminate(Host, ServerHost). + +options() -> +    [{node_type, zoo}, +     {deliver_payloads, true}, +     {notify_config, false}, +     {notify_delete, false}, +     {notify_retract, true}, +     {persist_items, true}, +     {max_items, ?MAXITEMS div 2}, +     {subscribe, true}, +     {access_model, open}, +     {roster_groups_allowed, []}, +     {publish_model, publishers}, +     {max_payload_size, ?MAX_PAYLOAD_SIZE}, +     {send_last_published_item, never}, +     {deliver_notifications, true}, +     {presence_based_delivery, false}]. + +features() -> +    node_default:features(). + +%% use same code as node_default, but do not limite node to +%% the home/localhost/user/... hierarchy +%% any node is allowed +create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> +    LOwner = jlib:jid_tolower(Owner), +    {User, Server, _Resource} = LOwner,  +    Allowed = case acl:match_rule(ServerHost, Access, LOwner) of +		allow -> +		    true; +		_ ->     +		    case Owner of +		    {jid, "", _, "", "", _, ""} -> true; +		    _ -> false +		    end      +		end,     +    {result, Allowed}. + +create_node(Host, Node, Owner) -> +    node_default:create_node(Host, Node, Owner). + +delete_node(Host, Removed) -> +    node_default:delete_node(Host, Removed). + +subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> +    node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). + +unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> +    node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). + +publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> +    node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). + +remove_extra_items(Host, Node, MaxItems, ItemIds) -> +    node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). + +delete_item(Host, Node, JID, ItemId) -> +    node_default:delete_item(Host, Node, JID, ItemId). + +purge_node(Host, Node, Owner) -> +    node_default:purge_node(Host, Node, Owner). + +get_entity_affiliations(Host, Owner) -> +    node_default:get_entity_affiliations(Host, Owner). + +get_node_affiliations(Host, Node) -> +    node_default:get_node_affiliations(Host, Node). + +get_affiliation(Host, Node, Owner) -> +    node_default:get_affiliation(Host, Node, Owner). + +set_affiliation(Host, Node, Owner, Affiliation) -> +    node_default:set_affiliation(Host, Node, Owner, Affiliation). + +get_entity_subscriptions(Host, Owner) -> +    node_default:get_entity_subscriptions(Host, Owner). + +get_node_subscriptions(Host, Node) -> +    node_default:get_node_subscriptions(Host, Node). + +get_subscription(Host, Node, Owner) -> +    node_default:get_subscription(Host, Node, Owner). + +set_subscription(Host, Node, Owner, Subscription) -> +    node_default:set_subscription(Host, Node, Owner, Subscription). + +get_states(Host, Node) -> +    node_default:get_states(Host, Node). + +get_state(Host, Node, JID) -> +    node_default:get_state(Host, Node, JID). + +set_state(State) -> +    node_default:set_state(State). + +get_items(Host, Node) -> +    node_default:get_items(Host, Node). + +get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + +get_item(Host, Node, ItemId) -> +    node_default:get_item(Host, Node, ItemId). +        +get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> +    node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + +set_item(Item) -> +    node_default:set_item(Item). diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index 51384a216..34eeef739 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -290,7 +290,7 @@ process_request(#state{request_method = Method,  			     LQ  		     end,  	    LPath = string:tokens(NPath, "/"), -	    {ok, {IP, _Port}} = +	    {ok, IP} =  		case SockMod of  		    gen_tcp ->  			inet:peername(Socket); @@ -302,7 +302,7 @@ process_request(#state{request_method = Method,  			       q = LQuery,  			       auth = Auth,  			       lang = Lang, -			       ip=IP}, +			       ip = IP},  	    %% XXX bard: This previously passed control to  	    %% ejabberd_web:process_get, now passes it to a local  	    %% procedure (process) that handles dispatching based on @@ -348,12 +348,20 @@ process_request(#state{request_method = Method,  			 LQ ->  			     LQ  		     end, +	    {ok, IP} = +		case SockMod of +		    gen_tcp -> +			inet:peername(Socket); +		    _ -> +			SockMod:peername(Socket) +		end,  	    Request = #request{method = Method,  			       path = LPath,  			       q = LQuery,  			       auth = Auth,  			       data = Data, -			       lang = Lang}, +			       lang = Lang, +			       ip = IP},  	    case process(RequestHandlers, Request) of  		El when element(1, El) == xmlelement ->  		    make_xhtml_output(State, 200, [], El); diff --git a/src/web/ejabberd_http_poll.erl b/src/web/ejabberd_http_poll.erl index f5b419275..d93bec791 100644 --- a/src/web/ejabberd_http_poll.erl +++ b/src/web/ejabberd_http_poll.erl @@ -50,13 +50,16 @@  -record(http_poll, {id, pid}). +-define(NULL_PEER, {{0, 0, 0, 0}, 0}). +  -record(state, {id,  		key,  		output = "",  		input = "",  		waiting_input = false, %% {ReceiverPid, Tag}  		last_receiver, -		timer}). +		timer, +		ip = ?NULL_PEER }).  %-define(DBGFSM, true). @@ -94,11 +97,18 @@ setopts({http_poll, FsmRef}, Opts) ->  	    ok      end. -sockname(_Socket) -> -    {ok, {{0, 0, 0, 0}, 0}}. +sockname(_) -> +    {ok, ?NULL_PEER}. -peername(_Socket) -> -    {ok, {{0, 0, 0, 0}, 0}}. +peername({http_poll, FsmRef}) -> +    gen_fsm:send_all_state_event(FsmRef, {peername, self()}), +    %% XXX should improve that, but sync call seems not possible +    receive +	{peername, PeerName} -> {ok, PeerName} +	after 1000 -> {ok, ?NULL_PEER} +    end; +peername(_) -> +    {ok, ?NULL_PEER}.  controlling_process(_Socket, _Pid) ->      ok. @@ -107,7 +117,7 @@ close({http_poll, FsmRef}) ->      catch gen_fsm:sync_send_all_state_event(FsmRef, close). -process([], #request{data = Data} = _Request) -> +process([], #request{data = Data, ip = IP} = _Request) ->      case catch parse_request(Data) of  	{ok, ID1, Key, NewKey, Packet} ->  	    ID = if @@ -123,7 +133,7 @@ process([], #request{data = Data} = _Request) ->  		     true ->  			 ID1  		 end, -	    case http_put(ID, Key, NewKey, Packet) of +	    case http_put(ID, Key, NewKey, Packet, IP) of  		{error, not_exists} ->  		    {200, ?BAD_REQUEST, ""};  		{error, bad_key} -> @@ -228,6 +238,10 @@ handle_event({activate, From}, StateName, StateData) ->  						   }}      end; +handle_event({peername, From}, StateName, StateData) -> +    From ! {peername, StateData#state.ip}, +    {next_state, StateName, StateData}; +  handle_event(_Event, StateName, StateData) ->      {next_state, StateName, StateData}. @@ -249,7 +263,7 @@ handle_sync_event(stop, _From, _StateName, StateData) ->      Reply = ok,      {stop, normal, Reply, StateData}; -handle_sync_event({http_put, Key, NewKey, Packet}, +handle_sync_event({http_put, Key, NewKey, Packet, IP},  		  _From, StateName, StateData) ->      Allow = case StateData#state.key of  		"" -> @@ -271,7 +285,8 @@ handle_sync_event({http_put, Key, NewKey, Packet},  		    Input = [StateData#state.input|Packet],  		    Reply = ok,  		    {reply, Reply, StateName, StateData#state{input = Input, -							      key = NewKey}}; +							      key = NewKey, +							      ip = IP}};  		{Receiver, _Tag} ->  		    Receiver ! {tcp, {http_poll, self()},  				list_to_binary(Packet)}, @@ -282,7 +297,8 @@ handle_sync_event({http_put, Key, NewKey, Packet},  		     StateData#state{waiting_input = false,  				     last_receiver = Receiver,  				     key = NewKey, -				     timer = Timer}} +				     timer = Timer, +				     ip = IP}}  	    end;  	true ->  	    Reply = {error, bad_key}, @@ -343,13 +359,13 @@ terminate(_Reason, _StateName, StateData) ->  %%% Internal functions  %%%---------------------------------------------------------------------- -http_put(ID, Key, NewKey, Packet) -> +http_put(ID, Key, NewKey, Packet, IP) ->      case mnesia:dirty_read({http_poll, ID}) of  	[] ->  	    {error, not_exists};  	[#http_poll{pid = FsmRef}] ->  	    gen_fsm:sync_send_all_state_event( -	      FsmRef, {http_put, Key, NewKey, Packet}) +	      FsmRef, {http_put, Key, NewKey, Packet, IP})      end.  http_get(ID) -> | 
