1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% File : service_admin_extra_stanza.erl |
3 |
|
%%% Author : Badlop <badlop@process-one.net>, Piotr Nosek <piotr.nosek@erlang-solutions.com> |
4 |
|
%%% Purpose : Contributed administrative functions and commands |
5 |
|
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2008 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(service_admin_extra_stanza). |
27 |
|
-author('badlop@process-one.net'). |
28 |
|
|
29 |
|
-export([ |
30 |
|
commands/0, |
31 |
|
|
32 |
|
send_message_headline/4, |
33 |
|
send_message_chat/3, |
34 |
|
send_stanza_c2s/4 |
35 |
|
]). |
36 |
|
|
37 |
|
-ignore_xref([commands/0, send_message_chat/3, send_message_headline/4, send_stanza_c2s/4]). |
38 |
|
|
39 |
|
-include("mongoose.hrl"). |
40 |
|
-include("ejabberd_commands.hrl"). |
41 |
|
-include("jlib.hrl"). |
42 |
|
-include_lib("exml/include/exml.hrl"). |
43 |
|
|
44 |
|
%%% |
45 |
|
%%% Register commands |
46 |
|
%%% |
47 |
|
|
48 |
|
-spec commands() -> [ejabberd_commands:cmd(), ...]. |
49 |
|
commands() -> |
50 |
166 |
[ |
51 |
|
#ejabberd_commands{name = send_message_chat, tags = [stanza], |
52 |
|
desc = "Send a chat message to a local or remote bare of full JID", |
53 |
|
module = ?MODULE, function = send_message_chat, |
54 |
|
args = [{from, binary}, {to, binary}, {body, binary}], |
55 |
|
result = {res, restuple}}, |
56 |
|
#ejabberd_commands{name = send_message_headline, tags = [stanza], |
57 |
|
desc = "Send a headline message to a local or remote bare of full JID", |
58 |
|
module = ?MODULE, function = send_message_headline, |
59 |
|
args = [{from, binary}, {to, binary}, |
60 |
|
{subject, binary}, {body, binary}], |
61 |
|
result = {res, restuple}}, |
62 |
|
#ejabberd_commands{name = send_stanza_c2s, tags = [stanza], |
63 |
|
desc = "Send a stanza as if sent from a c2s session", |
64 |
|
module = ?MODULE, function = send_stanza_c2s, |
65 |
|
args = [{user, binary}, {host, binary}, |
66 |
|
{resource, binary}, {stanza, binary}], |
67 |
|
result = {res, restuple}} |
68 |
|
]. |
69 |
|
|
70 |
|
%%% |
71 |
|
%%% Stanza |
72 |
|
%%% |
73 |
|
|
74 |
|
%% @doc Send a chat message to a Jabber account. |
75 |
|
-spec send_message_chat(From :: binary(), To :: binary(), |
76 |
|
Body :: binary() | string()) -> {Res, string()} when |
77 |
|
Res :: bad_jid | ok. |
78 |
|
send_message_chat(From, To, Body) -> |
79 |
2 |
Packet = build_packet(message_chat, [Body]), |
80 |
2 |
send_packet_all_resources(From, To, Packet). |
81 |
|
|
82 |
|
|
83 |
|
%% @doc Send a headline message to a Jabber account. |
84 |
|
-spec send_message_headline(From :: binary(), To :: binary(), |
85 |
|
Subject:: binary() | string(), |
86 |
|
Body :: binary() | string()) -> {Res, string()} when |
87 |
|
Res :: ok | bad_jid. |
88 |
|
send_message_headline(From, To, Subject, Body) -> |
89 |
2 |
Packet = build_packet(message_headline, [Subject, Body]), |
90 |
2 |
send_packet_all_resources(From, To, Packet). |
91 |
|
|
92 |
|
|
93 |
|
%% @doc Send a packet to a Jabber account. |
94 |
|
%% If a resource was specified in the JID, the packet is sent only to that |
95 |
|
%% specific resource. |
96 |
|
%% If no resource was specified in the JID, and the user is remote or local but |
97 |
|
%% offline, the packet is sent to the bare JID. |
98 |
|
%% If the user is local and is online in several resources, the packet is sent |
99 |
|
%% to all its resources. |
100 |
|
-spec send_packet_all_resources( |
101 |
|
FromJIDStr :: binary() | jid:jid(), |
102 |
|
ToJIDString :: binary() | jid:jid(), |
103 |
|
exml:element()) -> |
104 |
|
{Res, string()} when |
105 |
|
Res :: bad_jid | ok. |
106 |
|
send_packet_all_resources(FromJIDString, ToJIDString, Packet) when is_binary(FromJIDString) -> |
107 |
4 |
case jid:from_binary(FromJIDString) of |
108 |
|
error -> |
109 |
2 |
{bad_jid, "Sender JID is invalid"}; |
110 |
|
FromJID -> |
111 |
2 |
send_packet_all_resources(FromJID, ToJIDString, Packet), |
112 |
2 |
{ok, ""} |
113 |
|
end; |
114 |
|
send_packet_all_resources(FromJID, ToJIDString, Packet) when is_binary(ToJIDString) -> |
115 |
2 |
case jid:from_binary(ToJIDString) of |
116 |
|
error -> |
117 |
:-( |
{bad_jid, "Receiver JID is invalid"}; |
118 |
|
ToJID -> |
119 |
2 |
send_packet_all_resources(FromJID, ToJID, Packet), |
120 |
2 |
{ok, ""} |
121 |
|
end; |
122 |
|
send_packet_all_resources(#jid{} = FromJID, #jid{} = ToJID, Packet) -> |
123 |
2 |
case ToJID#jid.lresource of |
124 |
|
<<>> -> |
125 |
1 |
send_packet_all_resources_2(FromJID, ToJID, Packet), |
126 |
1 |
{ok, ""}; |
127 |
|
_Res -> |
128 |
1 |
route_packet(FromJID, ToJID, Packet), |
129 |
1 |
{ok, ""} |
130 |
|
end. |
131 |
|
|
132 |
|
|
133 |
|
-spec send_packet_all_resources_2(FromJID :: jid:jid(), |
134 |
|
ToJID :: jid:jid(), |
135 |
|
exml:element()) -> ok. |
136 |
|
send_packet_all_resources_2(FromJID, ToJID, Packet) -> |
137 |
1 |
case ejabberd_sm:get_user_resources(ToJID) of |
138 |
|
[] -> |
139 |
:-( |
route_packet(FromJID, ToJID, Packet); |
140 |
|
ToResources -> |
141 |
1 |
lists:foreach( |
142 |
|
fun(ToResource) -> |
143 |
2 |
route_packet(FromJID, jid:replace_resource(ToJID, ToResource), Packet) |
144 |
|
end, |
145 |
|
ToResources) |
146 |
|
end. |
147 |
|
|
148 |
|
|
149 |
|
-spec route_packet(jid:jid(), jid:jid(), exml:element()) -> mongoose_acc:t(). |
150 |
|
route_packet(FromJID, ToJID, Packet) -> |
151 |
3 |
ejabberd_router:route(FromJID, ToJID, Packet). |
152 |
|
|
153 |
|
|
154 |
|
-spec build_packet('message_chat' | 'message_headline', |
155 |
|
SubjectBody :: [binary() | string(), ...]) -> exml:element(). |
156 |
|
build_packet(message_chat, [Body]) -> |
157 |
2 |
#xmlel{ name = <<"message">>, |
158 |
|
attrs = [{<<"type">>, <<"chat">>}, {<<"id">>, mongoose_bin:gen_from_crypto()}], |
159 |
|
children = [#xmlel{ name = <<"body">>, children = [#xmlcdata{content = Body}]}] |
160 |
|
}; |
161 |
|
build_packet(message_headline, [Subject, Body]) -> |
162 |
2 |
#xmlel{ name = <<"message">>, |
163 |
|
attrs = [{<<"type">>, <<"headline">>}, {<<"id">>, mongoose_bin:gen_from_crypto()}], |
164 |
|
children = [#xmlel{ name = <<"subject">>, children = [#xmlcdata{content = Subject}]}, |
165 |
|
#xmlel{ name = <<"body">>, children = [#xmlcdata{content = Body}]} |
166 |
|
] |
167 |
|
}. |
168 |
|
|
169 |
|
|
170 |
|
-spec send_stanza_c2s(jid:user(), jid:server(), jid:resource(), |
171 |
|
Stanza :: binary()) -> {Res, string()} when |
172 |
|
Res :: user_does_not_exist | bad_stanza | ok. |
173 |
|
send_stanza_c2s(Username, Host, Resource, Stanza) -> |
174 |
3 |
C2sPid = ejabberd_sm:get_session_pid(jid:make(Username, Host, Resource)), |
175 |
3 |
case C2sPid of |
176 |
|
none -> |
177 |
1 |
{user_does_not_exist, |
178 |
|
io_lib:format("User ~s@~s/~s does not exist", [Username, Host, Resource])}; |
179 |
|
_ -> |
180 |
2 |
case exml:parse(Stanza) of |
181 |
|
{ok, XmlEl} -> |
182 |
1 |
p1_fsm_old:send_event(C2sPid, {xmlstreamelement, XmlEl}), |
183 |
1 |
{ok, "Stanza has been sent"}; |
184 |
|
{error, _} -> |
185 |
1 |
{bad_stanza, "Stanza is incorrect"} |
186 |
|
end |
187 |
|
end. |
188 |
|
|