1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2003 Imendio AB
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, see <https://www.gnu.org/licenses>
17 */
18
19 /**
20 * SECTION:lm-message
21 * @Title: LmMessage
22 * @Short_description: A message is built up like a tree of message nodes.
23 *
24 * Represents a message that can be sent with lm_connection_send(), lm_connection_send_with_reply() or lm_connection_send_with_reply_and_block(). Either use lm_message_new() or lm_message_new_with_subtype() to create a message. You need to call lm_message_unref() when are finished with it.
25 */
26
27 #include <config.h>
28 #include <string.h>
29
30 #include "lm-internals.h"
31 #include "lm-message.h"
32
33 #define PRIV(o) ((LmMessage *)o)->priv
34
35 static struct TypeNames
36 {
37 LmMessageType type;
38 const gchar *name;
39 } type_names[] = {
40 { LM_MESSAGE_TYPE_MESSAGE, "message" },
41 { LM_MESSAGE_TYPE_PRESENCE, "presence" },
42 { LM_MESSAGE_TYPE_IQ, "iq" },
43 { LM_MESSAGE_TYPE_STREAM, "stream:stream" },
44 { LM_MESSAGE_TYPE_STREAM_ERROR, "stream:error" },
45 { LM_MESSAGE_TYPE_STREAM_FEATURES, "stream:features" },
46 { LM_MESSAGE_TYPE_AUTH, "auth" },
47 { LM_MESSAGE_TYPE_CHALLENGE, "challenge" },
48 { LM_MESSAGE_TYPE_RESPONSE, "response" },
49 { LM_MESSAGE_TYPE_SUCCESS, "success" },
50 { LM_MESSAGE_TYPE_FAILURE, "failure" },
51 { LM_MESSAGE_TYPE_PROCEED, "proceed" },
52 { LM_MESSAGE_TYPE_STARTTLS, "starttls" },
53 { LM_MESSAGE_TYPE_UNKNOWN, NULL }
54 };
55
56 static struct SubTypeNames
57 {
58 LmMessageSubType type;
59 const gchar *name;
60 } sub_type_names[] = {
61 { LM_MESSAGE_SUB_TYPE_NORMAL, "normal" },
62 { LM_MESSAGE_SUB_TYPE_CHAT, "chat" },
63 { LM_MESSAGE_SUB_TYPE_GROUPCHAT, "groupchat" },
64 { LM_MESSAGE_SUB_TYPE_HEADLINE, "headline" },
65 { LM_MESSAGE_SUB_TYPE_UNAVAILABLE, "unavailable" },
66 { LM_MESSAGE_SUB_TYPE_PROBE, "probe" },
67 { LM_MESSAGE_SUB_TYPE_SUBSCRIBE, "subscribe" },
68 { LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE, "unsubscribe" },
69 { LM_MESSAGE_SUB_TYPE_SUBSCRIBED, "subscribed" },
70 { LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED, "unsubscribed" },
71 { LM_MESSAGE_SUB_TYPE_GET, "get" },
72 { LM_MESSAGE_SUB_TYPE_SET, "set" },
73 { LM_MESSAGE_SUB_TYPE_RESULT, "result" },
74 { LM_MESSAGE_SUB_TYPE_ERROR, "error" }
75 };
76
77 struct LmMessagePriv {
78 LmMessageType type;
79 LmMessageSubType sub_type;
80 gint ref_count;
81 };
82
83 static LmMessageType
message_type_from_string(const gchar * type_str)84 message_type_from_string (const gchar *type_str)
85 {
86 gint i;
87
88 if (!type_str) {
89 return LM_MESSAGE_TYPE_UNKNOWN;
90 }
91
92 for (i = LM_MESSAGE_TYPE_MESSAGE;
93 i < LM_MESSAGE_TYPE_UNKNOWN;
94 ++i) {
95 if (strcmp (type_str, type_names[i].name) == 0) {
96 return type_names[i].type;
97 }
98 }
99
100 return LM_MESSAGE_TYPE_UNKNOWN;
101 }
102
103
104 const gchar *
_lm_message_type_to_string(LmMessageType type)105 _lm_message_type_to_string (LmMessageType type)
106 {
107 if (type < LM_MESSAGE_TYPE_MESSAGE ||
108 type > LM_MESSAGE_TYPE_STARTTLS) {
109 type = LM_MESSAGE_TYPE_UNKNOWN;
110 }
111
112 return type_names[type].name;
113 }
114
115 static LmMessageSubType
message_sub_type_from_string(const gchar * type_str)116 message_sub_type_from_string (const gchar *type_str)
117 {
118 gint i;
119
120 if (!type_str) {
121 return LM_MESSAGE_SUB_TYPE_NOT_SET;
122 }
123
124 for (i = LM_MESSAGE_SUB_TYPE_NORMAL;
125 i <= LM_MESSAGE_SUB_TYPE_ERROR;
126 ++i) {
127 if (g_ascii_strcasecmp (type_str,
128 sub_type_names[i].name) == 0) {
129 return i;
130 }
131 }
132
133 return LM_MESSAGE_SUB_TYPE_NOT_SET;
134 }
135
136 const gchar *
_lm_message_sub_type_to_string(LmMessageSubType type)137 _lm_message_sub_type_to_string (LmMessageSubType type)
138 {
139 if (type < LM_MESSAGE_SUB_TYPE_NORMAL ||
140 type > LM_MESSAGE_SUB_TYPE_ERROR) {
141 return NULL;
142 }
143
144 return sub_type_names[type].name;
145 }
146
147 static LmMessageSubType
message_sub_type_when_unset(LmMessageType type)148 message_sub_type_when_unset (LmMessageType type) {
149 LmMessageSubType sub_type = LM_MESSAGE_SUB_TYPE_NORMAL;
150
151 switch (type) {
152 case LM_MESSAGE_TYPE_MESSAGE:
153 /* A message without type should be handled like a message with
154 * type=normal, but we won't set it to that since then the user
155 * will not know if it's set or not.
156 */
157 sub_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
158 break;
159 case LM_MESSAGE_TYPE_PRESENCE:
160 sub_type = LM_MESSAGE_SUB_TYPE_AVAILABLE;
161 break;
162 case LM_MESSAGE_TYPE_IQ:
163 sub_type = LM_MESSAGE_SUB_TYPE_GET;
164 break;
165 default:
166 break;
167 }
168
169 return sub_type;
170 }
171
172 LmMessage *
_lm_message_new_from_node(LmMessageNode * node)173 _lm_message_new_from_node (LmMessageNode *node)
174 {
175 LmMessage *m;
176 LmMessageType type;
177 LmMessageSubType sub_type;
178 const gchar *sub_type_str;
179
180 type = message_type_from_string (node->name);
181
182 if (type == LM_MESSAGE_TYPE_UNKNOWN) {
183 return NULL;
184 }
185
186 sub_type_str = lm_message_node_get_attribute (node, "type");
187 if (sub_type_str) {
188 sub_type = message_sub_type_from_string (sub_type_str);
189 } else {
190 sub_type = message_sub_type_when_unset (type);
191 }
192
193 m = g_new0 (LmMessage, 1);
194 m->priv = g_new0 (LmMessagePriv, 1);
195
196 PRIV(m)->ref_count = 1;
197 PRIV(m)->type = type;
198 PRIV(m)->sub_type = sub_type;
199
200 m->node = lm_message_node_ref (node);
201
202 return m;
203 }
204
205 /**
206 * lm_message_new:
207 * @to: receipient jid
208 * @type: message type
209 *
210 * Creates a new #LmMessage which can be sent with lm_connection_send() or
211 * lm_connection_send_with_reply(). If @to is %NULL the message is sent to the
212 * server. The returned message should be unreferenced with lm_message_unref()
213 * when caller is finished with it.
214 *
215 * Return value: a newly created #LmMessage
216 **/
217 LmMessage *
lm_message_new(const gchar * to,LmMessageType type)218 lm_message_new (const gchar *to, LmMessageType type)
219 {
220 LmMessage *m;
221 gchar *id;
222
223 m = g_new0 (LmMessage, 1);
224 m->priv = g_new0 (LmMessagePriv, 1);
225
226 PRIV(m)->ref_count = 1;
227 PRIV(m)->type = type;
228 PRIV(m)->sub_type = message_sub_type_when_unset (type);
229
230 m->node = _lm_message_node_new (_lm_message_type_to_string (type));
231
232 if (type != LM_MESSAGE_TYPE_STREAM) {
233 id = _lm_utils_generate_id ();
234 lm_message_node_set_attribute (m->node, "id", id);
235 g_free (id);
236 }
237
238 if (to) {
239 lm_message_node_set_attribute (m->node, "to", to);
240 }
241
242 if (type == LM_MESSAGE_TYPE_IQ) {
243 lm_message_node_set_attribute (m->node, "type", "get");
244 }
245
246 return m;
247 }
248
249 /**
250 * lm_message_new_with_sub_type:
251 * @to: receipient jid
252 * @type: message type
253 * @sub_type: message sub type
254 *
255 * Creates a new #LmMessage with sub type set. See lm_message_new() for more
256 * information.
257 *
258 * Return value: a newly created #LmMessage
259 **/
260 LmMessage *
lm_message_new_with_sub_type(const gchar * to,LmMessageType type,LmMessageSubType sub_type)261 lm_message_new_with_sub_type (const gchar *to,
262 LmMessageType type,
263 LmMessageSubType sub_type)
264 {
265 LmMessage *m;
266 const gchar *type_str;
267
268 m = lm_message_new (to, type);
269
270 type_str = _lm_message_sub_type_to_string (sub_type);
271
272 if (type_str) {
273 lm_message_node_set_attributes (m->node,
274 "type", type_str, NULL);
275 PRIV(m)->sub_type = sub_type;
276 }
277
278 return m;
279 }
280
281 /**
282 * lm_message_get_type:
283 * @message: an #LmMessage
284 *
285 * Fetches the type of @message.
286 *
287 * Return value: the message type
288 **/
289 LmMessageType
lm_message_get_type(LmMessage * message)290 lm_message_get_type (LmMessage *message)
291 {
292 g_return_val_if_fail (message != NULL, LM_MESSAGE_TYPE_UNKNOWN);
293
294 return PRIV(message)->type;
295 }
296
297 /**
298 * lm_message_get_sub_type:
299 * @message:
300 *
301 * Fetches the sub type of @message.
302 *
303 * Return value: the message sub type
304 **/
305 LmMessageSubType
lm_message_get_sub_type(LmMessage * message)306 lm_message_get_sub_type (LmMessage *message)
307 {
308 g_return_val_if_fail (message != NULL, LM_MESSAGE_SUB_TYPE_NOT_SET);
309
310 return PRIV(message)->sub_type;
311 }
312
313 /**
314 * lm_message_get_node:
315 * @message: an #LmMessage
316 *
317 * Retrieves the root node from @message.
318 *
319 * Return value: an #LmMessageNode
320 **/
321 LmMessageNode *
lm_message_get_node(LmMessage * message)322 lm_message_get_node (LmMessage *message)
323 {
324 g_return_val_if_fail (message != NULL, NULL);
325
326 return message->node;
327 }
328
329 /**
330 * lm_message_ref:
331 * @message: an #LmMessage
332 *
333 * Adds a reference to @message.
334 *
335 * Return value: the message
336 **/
337 LmMessage *
lm_message_ref(LmMessage * message)338 lm_message_ref (LmMessage *message)
339 {
340 g_return_val_if_fail (message != NULL, NULL);
341
342 PRIV(message)->ref_count++;
343
344 return message;
345 }
346
347 /**
348 * lm_message_unref:
349 * @message: an #LmMessage
350 *
351 * Removes a reference from @message. When no more references are present the
352 * message is freed.
353 **/
354 void
lm_message_unref(LmMessage * message)355 lm_message_unref (LmMessage *message)
356 {
357 g_return_if_fail (message != NULL);
358
359 PRIV(message)->ref_count--;
360
361 if (PRIV(message)->ref_count == 0) {
362 lm_message_node_unref (message->node);
363 g_free (message->priv);
364 g_free (message);
365 }
366 }
367