1 /**
2 * @file rawudp.c
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (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
18 * GNU 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 02111-1301 USA
23 */
24
25 #include "internal.h"
26
27 #include "rawudp.h"
28 #include "jingle.h"
29 #include "debug.h"
30
31 #include <string.h>
32
33 struct _JingleRawUdpPrivate
34 {
35 GList *local_candidates;
36 GList *remote_candidates;
37 };
38
39 #define JINGLE_RAWUDP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_RAWUDP, JingleRawUdpPrivate))
40
41 static void jingle_rawudp_class_init (JingleRawUdpClass *klass);
42 static void jingle_rawudp_init (JingleRawUdp *rawudp);
43 static void jingle_rawudp_finalize (GObject *object);
44 static void jingle_rawudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
45 static void jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
46 static JingleTransport *jingle_rawudp_parse_internal(xmlnode *rawudp);
47 static xmlnode *jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
48
49 static JingleTransportClass *parent_class = NULL;
50
51 enum {
52 PROP_0,
53 PROP_LOCAL_CANDIDATES,
54 PROP_REMOTE_CANDIDATES,
55 };
56
57 static JingleRawUdpCandidate *
jingle_rawudp_candidate_copy(JingleRawUdpCandidate * candidate)58 jingle_rawudp_candidate_copy(JingleRawUdpCandidate *candidate)
59 {
60 JingleRawUdpCandidate *new_candidate = g_new0(JingleRawUdpCandidate, 1);
61 new_candidate->generation = candidate->generation;
62 new_candidate->component = candidate->component;
63 new_candidate->id = g_strdup(candidate->id);
64 new_candidate->ip = g_strdup(candidate->ip);
65 new_candidate->port = candidate->port;
66
67 new_candidate->rem_known = candidate->rem_known;
68 return new_candidate;
69 }
70
71 static void
jingle_rawudp_candidate_free(JingleRawUdpCandidate * candidate)72 jingle_rawudp_candidate_free(JingleRawUdpCandidate *candidate)
73 {
74 g_free(candidate->id);
75 g_free(candidate->ip);
76 }
77
78 GType
jingle_rawudp_candidate_get_type()79 jingle_rawudp_candidate_get_type()
80 {
81 static GType type = 0;
82
83 if (type == 0) {
84 type = g_boxed_type_register_static("JingleRawUdpCandidate",
85 (GBoxedCopyFunc)jingle_rawudp_candidate_copy,
86 (GBoxedFreeFunc)jingle_rawudp_candidate_free);
87 }
88 return type;
89 }
90
91 JingleRawUdpCandidate *
jingle_rawudp_candidate_new(const gchar * id,guint generation,guint component,const gchar * ip,guint port)92 jingle_rawudp_candidate_new(const gchar *id, guint generation, guint component, const gchar *ip, guint port)
93 {
94 JingleRawUdpCandidate *candidate = g_new0(JingleRawUdpCandidate, 1);
95 candidate->generation = generation;
96 candidate->component = component;
97 candidate->id = g_strdup(id);
98 candidate->ip = g_strdup(ip);
99 candidate->port = port;
100
101 candidate->rem_known = FALSE;
102 return candidate;
103 }
104
105 GType
jingle_rawudp_get_type()106 jingle_rawudp_get_type()
107 {
108 static GType type = 0;
109
110 if (type == 0) {
111 static const GTypeInfo info = {
112 sizeof(JingleRawUdpClass),
113 NULL,
114 NULL,
115 (GClassInitFunc) jingle_rawudp_class_init,
116 NULL,
117 NULL,
118 sizeof(JingleRawUdp),
119 0,
120 (GInstanceInitFunc) jingle_rawudp_init,
121 NULL
122 };
123 type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleRawUdp", &info, 0);
124 }
125 return type;
126 }
127
128 static void
jingle_rawudp_class_init(JingleRawUdpClass * klass)129 jingle_rawudp_class_init (JingleRawUdpClass *klass)
130 {
131 GObjectClass *gobject_class = (GObjectClass*)klass;
132 parent_class = g_type_class_peek_parent(klass);
133
134 gobject_class->finalize = jingle_rawudp_finalize;
135 gobject_class->set_property = jingle_rawudp_set_property;
136 gobject_class->get_property = jingle_rawudp_get_property;
137 klass->parent_class.to_xml = jingle_rawudp_to_xml_internal;
138 klass->parent_class.parse = jingle_rawudp_parse_internal;
139 klass->parent_class.transport_type = JINGLE_TRANSPORT_RAWUDP;
140
141 g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
142 g_param_spec_pointer("local-candidates",
143 "Local candidates",
144 "The local candidates for this transport.",
145 G_PARAM_READABLE));
146
147 g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES,
148 g_param_spec_pointer("remote-candidates",
149 "Remote candidates",
150 "The remote candidates for this transport.",
151 G_PARAM_READABLE));
152
153 g_type_class_add_private(klass, sizeof(JingleRawUdpPrivate));
154 }
155
156 static void
jingle_rawudp_init(JingleRawUdp * rawudp)157 jingle_rawudp_init (JingleRawUdp *rawudp)
158 {
159 rawudp->priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp);
160 rawudp->priv->local_candidates = NULL;
161 rawudp->priv->remote_candidates = NULL;
162 }
163
164 static void
jingle_rawudp_finalize(GObject * rawudp)165 jingle_rawudp_finalize (GObject *rawudp)
166 {
167 /* JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp); */
168 purple_debug_info("jingle","jingle_rawudp_finalize\n");
169
170 G_OBJECT_CLASS(parent_class)->finalize(rawudp);
171 }
172
173 static void
jingle_rawudp_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)174 jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
175 {
176 JingleRawUdp *rawudp;
177
178 g_return_if_fail(object != NULL);
179 g_return_if_fail(JINGLE_IS_RAWUDP(object));
180
181 rawudp = JINGLE_RAWUDP(object);
182
183 switch (prop_id) {
184 case PROP_LOCAL_CANDIDATES:
185 rawudp->priv->local_candidates =
186 g_value_get_pointer(value);
187 break;
188 case PROP_REMOTE_CANDIDATES:
189 rawudp->priv->remote_candidates =
190 g_value_get_pointer(value);
191 break;
192 default:
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 break;
195 }
196 }
197
198 static void
jingle_rawudp_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)199 jingle_rawudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
200 {
201 JingleRawUdp *rawudp;
202
203 g_return_if_fail(object != NULL);
204 g_return_if_fail(JINGLE_IS_RAWUDP(object));
205
206 rawudp = JINGLE_RAWUDP(object);
207
208 switch (prop_id) {
209 case PROP_LOCAL_CANDIDATES:
210 g_value_set_pointer(value, rawudp->priv->local_candidates);
211 break;
212 case PROP_REMOTE_CANDIDATES:
213 g_value_set_pointer(value, rawudp->priv->remote_candidates);
214 break;
215 default:
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217 break;
218 }
219 }
220
221 void
jingle_rawudp_add_local_candidate(JingleRawUdp * rawudp,JingleRawUdpCandidate * candidate)222 jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
223 {
224 GList *iter = rawudp->priv->local_candidates;
225
226 for (; iter; iter = g_list_next(iter)) {
227 JingleRawUdpCandidate *c = iter->data;
228 if (purple_strequal(c->id, candidate->id)) {
229 guint generation = c->generation + 1;
230
231 g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c);
232 rawudp->priv->local_candidates = g_list_delete_link(
233 rawudp->priv->local_candidates, iter);
234
235 candidate->generation = generation;
236
237 rawudp->priv->local_candidates = g_list_append(
238 rawudp->priv->local_candidates, candidate);
239 return;
240 }
241 }
242
243 rawudp->priv->local_candidates = g_list_append(
244 rawudp->priv->local_candidates, candidate);
245 }
246
247 GList *
jingle_rawudp_get_remote_candidates(JingleRawUdp * rawudp)248 jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp)
249 {
250 return g_list_copy(rawudp->priv->remote_candidates);
251 }
252
253 static JingleRawUdpCandidate *
jingle_rawudp_get_remote_candidate_by_id(JingleRawUdp * rawudp,gchar * id)254 jingle_rawudp_get_remote_candidate_by_id(JingleRawUdp *rawudp, gchar *id)
255 {
256 GList *iter = rawudp->priv->remote_candidates;
257 for (; iter; iter = g_list_next(iter)) {
258 JingleRawUdpCandidate *candidate = iter->data;
259 if (purple_strequal(candidate->id, id)) {
260 return candidate;
261 }
262 }
263 return NULL;
264 }
265
266 static void
jingle_rawudp_add_remote_candidate(JingleRawUdp * rawudp,JingleRawUdpCandidate * candidate)267 jingle_rawudp_add_remote_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
268 {
269 JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp);
270 JingleRawUdpCandidate *rawudp_candidate =
271 jingle_rawudp_get_remote_candidate_by_id(rawudp, candidate->id);
272 if (rawudp_candidate != NULL) {
273 priv->remote_candidates = g_list_remove(
274 priv->remote_candidates, rawudp_candidate);
275 g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate);
276 }
277 priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
278 }
279
280 static JingleTransport *
jingle_rawudp_parse_internal(xmlnode * rawudp)281 jingle_rawudp_parse_internal(xmlnode *rawudp)
282 {
283 JingleTransport *transport = parent_class->parse(rawudp);
284 JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(transport);
285 xmlnode *candidate = xmlnode_get_child(rawudp, "candidate");
286 JingleRawUdpCandidate *rawudp_candidate = NULL;
287
288 for (; candidate; candidate = xmlnode_get_next_twin(candidate)) {
289 const gchar *id = xmlnode_get_attrib(candidate, "id");
290 const gchar *generation = xmlnode_get_attrib(candidate, "generation");
291 const gchar *component = xmlnode_get_attrib(candidate, "component");
292 const gchar *ip = xmlnode_get_attrib(candidate, "ip");
293 const gchar *port = xmlnode_get_attrib(candidate, "port");
294
295 if (!id || !generation || !component || !ip || !port)
296 continue;
297
298 rawudp_candidate = jingle_rawudp_candidate_new(
299 id,
300 atoi(generation),
301 atoi(component),
302 ip,
303 atoi(port));
304 rawudp_candidate->rem_known = TRUE;
305 jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
306 }
307
308 if (rawudp_candidate != NULL &&
309 g_list_length(priv->remote_candidates) == 1) {
310 /* manufacture rtcp candidate */
311 rawudp_candidate = g_boxed_copy(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate);
312 rawudp_candidate->component = 2;
313 rawudp_candidate->port = rawudp_candidate->port + 1;
314 rawudp_candidate->rem_known = TRUE;
315 jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
316 }
317
318 return transport;
319 }
320
321 static xmlnode *
jingle_rawudp_to_xml_internal(JingleTransport * transport,xmlnode * content,JingleActionType action)322 jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action)
323 {
324 xmlnode *node = parent_class->to_xml(transport, content, action);
325
326 if (action == JINGLE_SESSION_INITIATE ||
327 action == JINGLE_TRANSPORT_INFO ||
328 action == JINGLE_SESSION_ACCEPT) {
329 JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(transport);
330 GList *iter = priv->local_candidates;
331
332 for (; iter; iter = g_list_next(iter)) {
333 JingleRawUdpCandidate *candidate = iter->data;
334 xmlnode *xmltransport;
335 gchar *generation, *component, *port;
336
337 if (candidate->rem_known == TRUE)
338 continue;
339 candidate->rem_known = TRUE;
340
341 xmltransport = xmlnode_new_child(node, "candidate");
342 generation = g_strdup_printf("%d", candidate->generation);
343 component = g_strdup_printf("%d", candidate->component);
344 port = g_strdup_printf("%d", candidate->port);
345
346 xmlnode_set_attrib(xmltransport, "generation", generation);
347 xmlnode_set_attrib(xmltransport, "component", component);
348 xmlnode_set_attrib(xmltransport, "id", candidate->id);
349 xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
350 xmlnode_set_attrib(xmltransport, "port", port);
351
352 g_free(port);
353 g_free(generation);
354 }
355 }
356
357 return node;
358 }
359
360