+2006-05-23 Mickael Remond <mickael.remond@process-one.net>
+
+ * src/mod_roster.erl: The subscribe request are now resend at login as
+ long as they have not been answered. mod_roster do no more depends on
+ mod_offline.
+ * src/ejabberd_sm.erl: Likewise.
+ * src/ejabberd_c2s.erl: Likewise.
+ * src/mod_roster_odbc.erl: Likewise (The ODBC/relational support has
+ not yet been tested).
+ * src/mod_roster.hrl: Likewise.
+ * src/mod_offline.erl: Likewise.
+ * src/mod_offline_odbc.erl: Likewise.
+ * odbc/pg.sql: Likewise.
+ * odbc/mysql.sql: Likewise.
+
2006-05-22 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_sm.erl: The max_user_sessions has been moved to
StateData#state.server,
[StateData#state.jid]),
resend_offline_messages(StateData),
+ resend_subscription_requests(StateData),
presence_broadcast_first(
From, StateData#state{pres_last = Packet,
pres_invis = false
end, Rs)
end.
+resend_subscription_requests(#state{user = User,
+ server = Server} = StateData) ->
+ PendingSubscriptions = ejabberd_hooks:run_fold(
+ resend_subscription_requests_hook,
+ Server,
+ [],
+ [User, Server]),
+ lists:foreach(fun(XMLPacket) ->
+ send_element(StateData,
+ XMLPacket)
+ end,
+ PendingSubscriptions).
+
get_showtag(undefined) ->
"unavailable";
get_showtag(Presence) ->
{Pass, Subsc} =
case xml:get_attr_s("type", Attrs) of
"subscribe" ->
+ Reason = xml:get_tag_cdata(
+ xml:get_subtag(Packet, "status")),
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
- [User, Server, From, subscribe]),
+ [User, Server, From, subscribe, Reason]),
true};
"subscribed" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
- [User, Server, From, subscribed]),
+ [User, Server, From, subscribed, ""]),
true};
"unsubscribe" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
- [User, Server, From, unsubscribe]),
+ [User, Server, From, unsubscribe, ""]),
true};
"unsubscribed" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
- [User, Server, From, unsubscribed]),
+ [User, Server, From, unsubscribed, ""]),
true};
_ ->
{true, false}
LFrom = jlib:jid_tolower(From),
PResources = get_user_present_resources(
LUser, LServer),
- if
- PResources /= [] ->
- lists:foreach(
- fun({_, R}) ->
- if LFrom /=
- {LUser, LServer, R} ->
- do_route(
- From,
- jlib:jid_replace_resource(To, R),
- Packet);
- true ->
- ok
- end
- end, PResources);
- true ->
- if
- Subsc ->
- case ejabberd_auth:is_user_exists(
- LUser, LServer) of
- true ->
- ejabberd_hooks:run(
- offline_subscription_hook,
- LServer,
- [From, To, Packet]);
- _ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_SERVICE_UNAVAILABLE),
- ejabberd_router:route(To, From, Err)
- end;
- true ->
- ok
- end
- end;
+ lists:foreach(
+ fun({_, R}) ->
+ if LFrom /=
+ {LUser, LServer, R} ->
+ do_route(
+ From,
+ jlib:jid_replace_resource(To, R),
+ Packet);
+ true ->
+ ok
+ end
+ end, PResources);
true ->
ok
end;
update_table(),
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, store_packet, 50),
- ejabberd_hooks:add(offline_subscription_hook, Host,
- ?MODULE, store_packet, 50),
ejabberd_hooks:add(resend_offline_messages_hook, Host,
?MODULE, pop_offline_messages, 50),
ejabberd_hooks:add(remove_user, Host,
stop(Host) ->
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, store_packet, 50),
- ejabberd_hooks:delete(offline_subscription_hook, Host,
- ?MODULE, store_packet, 50),
ejabberd_hooks:delete(resend_offline_messages_hook, Host,
?MODULE, pop_offline_messages, 50),
ejabberd_hooks:delete(remove_user, Host,
start(Host, _Opts) ->
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, store_packet, 50),
- ejabberd_hooks:add(offline_subscription_hook, Host,
- ?MODULE, store_packet, 50),
ejabberd_hooks:add(resend_offline_messages_hook, Host,
?MODULE, pop_offline_messages, 50),
ejabberd_hooks:add(remove_user, Host,
stop(Host) ->
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, store_packet, 50),
- ejabberd_hooks:delete(offline_subscription_hook, Host,
- ?MODULE, store_packet, 50),
ejabberd_hooks:delete(resend_offline_messages_hook, Host,
?MODULE, pop_offline_messages, 50),
ejabberd_hooks:delete(remove_user, Host,
process_local_iq/3,
get_user_roster/2,
get_subscription_lists/3,
- in_subscription/5,
+ get_in_pending_subscriptions/3,
+ in_subscription/6,
out_subscription/4,
set_items/3,
remove_user/2,
?MODULE, get_jid_info, 50),
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
+ ejabberd_hooks:add(resend_subscription_requests_hook, Host,
+ ?MODULE, get_in_pending_subscriptions, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,
?MODULE, process_iq, IQDisc).
?MODULE, get_jid_info, 50),
ejabberd_hooks:delete(remove_user, Host,
?MODULE, remove_user, 50),
+ ejabberd_hooks:delete(resend_subscription_requests_hook, Host,
+ ?MODULE, get_in_pending_subscriptions, 50),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER).
end.
-
-
item_to_xml(Item) ->
Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
Attrs2 = case Item#roster.name of
_ ->
Attrs3
end,
- Attrs = Attrs4 ++ Item#roster.xattrs,
SubEls1 = lists:map(fun(G) ->
{xmlelement, "group", [], [{xmlcdata, G}]}
end, Item#roster.groups),
SubEls = SubEls1 ++ Item#roster.xs,
- {xmlelement, "item", Attrs, SubEls}.
+ {xmlelement, "item", Attrs4, SubEls}.
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
I#roster{jid = JID,
name = "",
groups = [],
- xattrs = [],
xs = []}
end,
Item1 = process_item_attrs(Item, Attrs),
"ask" ->
process_item_attrs(Item, Attrs);
_ ->
- XAttrs = Item#roster.xattrs,
- process_item_attrs(Item#roster{xattrs = [{Attr, Val} | XAttrs]},
- Attrs)
+ process_item_attrs(Item, Attrs)
end;
process_item_attrs(Item, []) ->
Item.
-in_subscription(_, User, Server, JID, Type) ->
- process_subscription(in, User, Server, JID, Type).
+in_subscription(_, User, Server, JID, Type, Reason) ->
+ process_subscription(in, User, Server, JID, Type, Reason).
out_subscription(User, Server, JID, Type) ->
- process_subscription(out, User, Server, JID, Type).
+ process_subscription(out, User, Server, JID, Type, []).
-process_subscription(Direction, User, Server, JID1, Type) ->
+process_subscription(Direction, User, Server, JID1, Type, Reason) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
Item#roster.ask,
Type)
end,
+ AskMessage = case NewState of
+ {_, both} -> Reason;
+ {_, in} -> Reason;
+ {_, _} -> []
+ end,
case NewState of
none ->
{none, AutoReply};
{Subscription, Pending} ->
NewItem = Item#roster{subscription = Subscription,
- ask = Pending},
+ ask = Pending,
+ askmessage = list_to_binary(AskMessage)},
mnesia:write(NewItem),
{{push, NewItem}, AutoReply}
end
"ask" ->
process_item_attrs_ws(Item, Attrs);
_ ->
- XAttrs = Item#roster.xattrs,
- process_item_attrs_ws(Item#roster{xattrs = [{Attr, Val} | XAttrs]},
- Attrs)
+ process_item_attrs_ws(Item, Attrs)
end;
process_item_attrs_ws(Item, []) ->
Item.
+get_in_pending_subscriptions(Ls, User, Server) ->
+ JID = jlib:make_jid(User, Server,""),
+ case mnesia:dirty_index_read(roster, {User,Server}, #roster.us) of
+ Result when list(Result) ->
+ Ls ++ lists:map(
+ fun(R) ->
+ Message = R#roster.askmessage,
+ Status = if is_binary(Message) ->
+ binary_to_list(Message);
+ true ->
+ []
+ end,
+ {xmlelement, "presence", [{"from", jlib:jid_to_string(R#roster.jid)},
+ {"to", jlib:jid_to_string(JID)},
+ {"type", "subscribe"}],
+ [{xmlelement, "status", [],
+ [{xmlcdata, Status}]}]}
+ end,
+ lists:filter(
+ fun(R) ->
+ case R#roster.ask of
+ in -> true;
+ both -> true;
+ _ -> false
+ end
+ end,
+ Result));
+ _ -> []
+ end.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Fields ->
ok;
[uj, user, jid, name, subscription, ask, groups, xattrs, xs] ->
- ?INFO_MSG("Converting roster table from "
- "{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []),
- Host = ?MYNAME,
- {atomic, ok} = mnesia:create_table(
- mod_roster_tmp_table,
- [{disc_only_copies, [node()]},
- {type, bag},
- {local_content, true},
- {record_name, roster},
- {attributes, record_info(fields, roster)}]),
- mnesia:del_table_index(roster, user),
- mnesia:transform_table(roster, ignore, Fields),
- F1 = fun() ->
- mnesia:write_lock_table(mod_roster_tmp_table),
- mnesia:foldl(
- fun(#roster{usj = {U, JID}, us = U} = R, _) ->
- mnesia:dirty_write(
- mod_roster_tmp_table,
- R#roster{usj = {U, Host, JID},
- us = {U, Host}})
- end, ok, roster)
- end,
- mnesia:transaction(F1),
- mnesia:clear_table(roster),
- F2 = fun() ->
- mnesia:write_lock_table(roster),
- mnesia:foldl(
- fun(R, _) ->
- mnesia:dirty_write(R)
- end, ok, mod_roster_tmp_table)
- end,
- mnesia:transaction(F2),
- mnesia:delete_table(mod_roster_tmp_table);
+ convert_table1(Fields);
+ [usj, us, jid, name, subscription, ask, groups, xattrs, xs] ->
+ convert_table2(Fields);
_ ->
?INFO_MSG("Recreating roster table", []),
mnesia:transform_table(roster, ignore, Fields)
end.
+%% Convert roster table to support virtual host
+convert_table1(Fields) ->
+ ?INFO_MSG("Virtual host support: converting roster table from "
+ "{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []),
+ Host = ?MYNAME,
+ {atomic, ok} = mnesia:create_table(
+ mod_roster_tmp_table,
+ [{disc_only_copies, [node()]},
+ {type, bag},
+ {local_content, true},
+ {record_name, roster},
+ {attributes, record_info(fields, roster)}]),
+ mnesia:del_table_index(roster, user),
+ mnesia:transform_table(roster, ignore, Fields),
+ F1 = fun() ->
+ mnesia:write_lock_table(mod_roster_tmp_table),
+ mnesia:foldl(
+ fun(#roster{usj = {U, JID}, us = U} = R, _) ->
+ mnesia:dirty_write(
+ mod_roster_tmp_table,
+ R#roster{usj = {U, Host, JID},
+ us = {U, Host}})
+ end, ok, roster)
+ end,
+ mnesia:transaction(F1),
+ mnesia:clear_table(roster),
+ F2 = fun() ->
+ mnesia:write_lock_table(roster),
+ mnesia:foldl(
+ fun(R, _) ->
+ mnesia:dirty_write(R)
+ end, ok, mod_roster_tmp_table)
+ end,
+ mnesia:transaction(F2),
+ mnesia:delete_table(mod_roster_tmp_table).
+
+
+%% Convert roster table: xattrs fields become
+convert_table2(Fields) ->
+ ?INFO_MSG("Converting roster table from "
+ "{usj, us, jid, name, subscription, ask, groups, xattrs, xs} format", []),
+ mnesia:transform_table(roster, ignore, Fields).
subscription = none,
ask = none,
groups = [],
- xattrs = [],
+ askmessage = [],
xs = []}).
process_local_iq/3,
get_user_roster/2,
get_subscription_lists/3,
- in_subscription/5,
+ get_in_pending_subscriptions/3,
+ in_subscription/6,
out_subscription/4,
set_items/3,
remove_user/2,
?MODULE, get_jid_info, 50),
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
+ ejabberd_hooks:add(resend_subscription_requests_hook, Host,
+ ?MODULE, get_in_pending_subscriptions, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,
?MODULE, process_iq, IQDisc).
?MODULE, get_jid_info, 50),
ejabberd_hooks:delete(remove_user, Host,
?MODULE, remove_user, 50),
+ ejabberd_hooks:delete(resend_subscription_requests_hook, Host,
+ ?MODULE, get_in_pending_subscriptions, 50),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER).
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
- " server, subscribe, type) "
+ " askmessage, server, "
+ " subscribe, type) "
" values ", ItemVals, ";"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
-in_subscription(_, User, Server, JID, Type) ->
- process_subscription(in, User, Server, JID, Type).
+in_subscription(_, User, Server, JID, Type, Reason) ->
+ process_subscription(in, User, Server, JID, Type, Reason).
out_subscription(User, Server, JID, Type) ->
- process_subscription(out, User, Server, JID, Type).
+ process_subscription(out, User, Server, JID, Type, []).
-process_subscription(Direction, User, Server, JID1, Type) ->
+process_subscription(Direction, User, Server, JID1, Type, Reason) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LJID = jlib:jid_tolower(JID1),
Item#roster.ask,
Type)
end,
+ AskMessage = case NewState of
+ {_, both} -> Reason;
+ {_, in} -> Reason;
+ {_, _} -> []
+ end,
case NewState of
none ->
{none, AutoReply};
{Subscription, Pending} ->
NewItem = Item#roster{subscription = Subscription,
- ask = Pending},
+ ask = Pending,
+ askmessage = AskMessage},
ItemVals = record_to_string(NewItem),
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
- " server, subscribe, type) "
+ " askmessage, server, subscribe, "
+ " type) "
" values ", ItemVals, ";"]),
{{push, NewItem}, AutoReply}
end
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
- " server, subscribe, type) "
+ " askmessage, server, subscribe, "
+ " type) "
" values ", ItemVals, ";"],
["delete from rostergroups "
" where username='", Username, "' "
Item.
+get_in_pending_subscriptions(Ls, User, Server) ->
+ JID = jlib:make_jid(User, Server,""),
+ case mnesia:dirty_index_read(roster, {User,Server}, #roster.us) of
+ Result when list(Result) ->
+ Ls ++ lists:map(
+ fun(R) ->
+ Message = R#roster.askmessage,
+ Status = if is_binary(Message) ->
+ binary_to_list(Message);
+ true ->
+ []
+ end,
+ {xmlelement, "presence", [{"from", jlib:jid_to_string(R#roster.jid)},
+ {"to", jlib:jid_to_string(JID)},
+ {"type", "subscribe"}],
+ [{xmlelement, "status", [],
+ [{xmlcdata, Status}]}]}
+ end,
+ lists:filter(
+ fun(R) ->
+ case R#roster.ask of
+ in -> true;
+ both -> true;
+ _ -> false
+ end
+ end,
+ Result));
+ _ -> []
+ end.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_jid_info(_, User, Server, JID) ->
jid = JID,
name = Name,
subscription = Subscription,
- ask = Ask}) ->
+ ask = Ask,
+ askmessage = AskMessage}) ->
Username = ejabberd_odbc:escape(User),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
Nick = ejabberd_odbc:escape(Name),
"'", Nick, "',"
"'", SSubscription, "',"
"'", SAsk, "',"
+ "'", AskMessage, "',"
"'N', '', 'item')"].
groups_to_string(#roster{us = {User, _Server},
nick text,
subscription character(1) NOT NULL,
ask character(1) NOT NULL,
+ askmessage text,
server character(1) NOT NULL,
subscribe text,
type text
CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
+--- To update from 1.x:
+-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask;
nick text,
subscription character(1) NOT NULL,
ask character(1) NOT NULL,
+ askmessage text,
server character(1) NOT NULL,
subscribe text,
"type" text
CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, jid);
---- To update from previous table definition:
--- CREATE SEQUENCE spool_seq_seq;
--- ALTER TABLE spool ADD COLUMN seq integer;
--- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
--- UPDATE spool SET seq = DEFAULT;
--- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
CREATE TABLE spool (
username text NOT NULL,
CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail);
CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
+
+--- To update from 0.9.8:
+-- CREATE SEQUENCE spool_seq_seq;
+-- ALTER TABLE spool ADD COLUMN seq integer;
+-- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
+-- UPDATE spool SET seq = DEFAULT;
+-- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
+
+--- To update from 1.x:
+-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask;