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