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