1 /*
2 * This file is part of the Nice GLib ICE library.
3 *
4 * (C) 2010, 2013 Collabora Ltd.
5 * Contact: Youness Alaoui
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is the Nice GLib ICE library.
18 *
19 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
20 * Corporation. All Rights Reserved.
21 *
22 * Contributors:
23 * Youness Alaoui, Collabora Ltd.
24 * Philip Withnall, Collabora Ltd.
25 *
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
35 */
36
37 /***
38 * SECTION:nice_io_stream
39 * @short_description: #GIOStream implementation for libnice
40 * @see_also: #NiceAgent
41 * @include: iostream.h
42 * @stability: Stable
43 *
44 * #NiceIOStream is a #GIOStream wrapper for a single reliable stream and
45 * component of a #NiceAgent. Given an existing reliable #NiceAgent, plus the
46 * IDs of an existing stream and component in the agent, it will provide a
47 * streaming input and output interface for communication over the given
48 * component.
49 *
50 * A single #NiceIOStream can only be used with a single agent, stream and
51 * component triple, and will be closed as soon as that stream is removed from
52 * the agent (e.g. if nice_agent_remove_stream() is called from another thread).
53 * If g_io_stream_close() is called on a #NiceIOStream, the I/O stream and
54 * underlying #NiceAgent stream will be closed in both directions, but the
55 * underlying stream will not be removed. Use nice_agent_remove_stream() to do
56 * that, but only do so after g_io_stream_close() has completed, or the stream
57 * will return broken pipe errors.
58 *
59 * Since: 0.1.5
60 */
61
62 #ifdef HAVE_CONFIG_H
63 # include "config.h"
64 #endif
65
66 #include "iostream.h"
67 #include "inputstream.h"
68 #include "outputstream.h"
69
70 G_DEFINE_TYPE (NiceIOStream, nice_io_stream, G_TYPE_IO_STREAM);
71
72 enum
73 {
74 PROP_AGENT = 1,
75 PROP_STREAM_ID,
76 PROP_COMPONENT_ID,
77 };
78
79 struct _NiceIOStreamPrivate
80 {
81 GWeakRef/*<NiceAgent>*/ agent_ref;
82 guint stream_id;
83 guint component_id;
84
85 GInputStream *input_stream; /* owned */
86 GOutputStream *output_stream; /* owned */
87 };
88
89 static void nice_io_stream_dispose (GObject *object);
90 static void nice_io_stream_get_property (GObject *object, guint prop_id,
91 GValue *value, GParamSpec *pspec);
92 static void nice_io_stream_set_property (GObject *object, guint prop_id,
93 const GValue *value, GParamSpec *pspec);
94 static GInputStream *nice_io_stream_get_input_stream (GIOStream *stream);
95 static GOutputStream *nice_io_stream_get_output_stream (GIOStream *stream);
96
97 static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
98 gpointer user_data);
99
100 static void
nice_io_stream_class_init(NiceIOStreamClass * klass)101 nice_io_stream_class_init (NiceIOStreamClass *klass)
102 {
103 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104 GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass);
105
106 g_type_class_add_private (klass, sizeof (NiceIOStreamPrivate));
107
108 gobject_class->set_property = nice_io_stream_set_property;
109 gobject_class->get_property = nice_io_stream_get_property;
110 gobject_class->dispose = nice_io_stream_dispose;
111
112 stream_class->get_input_stream = nice_io_stream_get_input_stream;
113 stream_class->get_output_stream = nice_io_stream_get_output_stream;
114
115 /*
116 * NiceIOStream:agent:
117 *
118 * The #NiceAgent to wrap with an I/O stream. This must be an existing
119 * reliable agent.
120 *
121 * A reference is not held on the #NiceAgent. If the agent is destroyed before
122 * the #NiceIOStream, %G_IO_ERROR_CLOSED will be returned for all subsequent
123 * operations on the stream.
124 *
125 * Since: 0.1.5
126 */
127 g_object_class_install_property (gobject_class, PROP_AGENT,
128 g_param_spec_object ("agent",
129 "NiceAgent",
130 "The underlying NiceAgent",
131 NICE_TYPE_AGENT,
132 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133
134 /*
135 * NiceIOStream:stream-id:
136 *
137 * ID of the stream to use in the #NiceIOStream:agent.
138 *
139 * Since: 0.1.5
140 */
141 g_object_class_install_property (gobject_class, PROP_STREAM_ID,
142 g_param_spec_uint (
143 "stream-id",
144 "Agent’s stream ID",
145 "The ID of the agent’s stream to wrap.",
146 0, G_MAXUINT,
147 0,
148 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
149
150 /*
151 * NiceIOStream:component-id:
152 *
153 * ID of the component to use in the #NiceIOStream:agent.
154 *
155 * Since: 0.1.5
156 */
157 g_object_class_install_property (gobject_class, PROP_COMPONENT_ID,
158 g_param_spec_uint (
159 "component-id",
160 "Agent’s component ID",
161 "The ID of the agent’s component to wrap.",
162 0, G_MAXUINT,
163 0,
164 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
165 }
166
167 static void
nice_io_stream_init(NiceIOStream * self)168 nice_io_stream_init (NiceIOStream *self)
169 {
170 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NICE_TYPE_IO_STREAM,
171 NiceIOStreamPrivate);
172
173 g_weak_ref_init (&self->priv->agent_ref, NULL);
174
175 /* Invalidate the stream/component IDs to begin with. */
176 self->priv->stream_id = 0;
177 self->priv->component_id = 0;
178 }
179
180 static void
nice_io_stream_dispose(GObject * object)181 nice_io_stream_dispose (GObject *object)
182 {
183 NiceIOStream *self = NICE_IO_STREAM (object);
184 NiceAgent *agent;
185
186 /* Ensure the stream is closed before continuing. Otherwise, if the input or
187 * output streams haven’t yet been lazily created, closing the stream in
188 * g_io_stream_dispose() will lazily create them, but NiceAgent will be NULL
189 * by that point and things will explode. */
190 if (!g_io_stream_is_closed (G_IO_STREAM (object)))
191 g_io_stream_close (G_IO_STREAM (object), NULL, NULL);
192
193 /* Clear everything away. */
194 if (self->priv->input_stream != NULL)
195 g_object_unref (self->priv->input_stream);
196 self->priv->input_stream = NULL;
197
198 if (self->priv->output_stream != NULL)
199 g_object_unref (self->priv->output_stream);
200 self->priv->output_stream = NULL;
201
202 agent = g_weak_ref_get (&self->priv->agent_ref);
203 if (agent != NULL) {
204 g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
205 g_object_unref (agent);
206 }
207
208 g_weak_ref_clear (&self->priv->agent_ref);
209
210 G_OBJECT_CLASS (nice_io_stream_parent_class)->dispose (object);
211 }
212
213 static void
nice_io_stream_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)214 nice_io_stream_get_property (GObject *object, guint prop_id,
215 GValue *value, GParamSpec *pspec)
216 {
217 NiceIOStream *self = NICE_IO_STREAM (object);
218
219 switch (prop_id) {
220 case PROP_AGENT:
221 g_value_take_object (value, g_weak_ref_get (&self->priv->agent_ref));
222 break;
223 case PROP_STREAM_ID:
224 g_value_set_uint (value, self->priv->stream_id);
225 break;
226 case PROP_COMPONENT_ID:
227 g_value_set_uint (value, self->priv->component_id);
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 }
232 }
233
234 static void
nice_io_stream_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)235 nice_io_stream_set_property (GObject *object, guint prop_id,
236 const GValue *value, GParamSpec *pspec)
237 {
238 NiceIOStream *self = NICE_IO_STREAM (object);
239
240 switch (prop_id) {
241 case PROP_AGENT: {
242 /* Construct only. */
243 NiceAgent *agent = g_value_dup_object (value);
244 g_weak_ref_set (&self->priv->agent_ref, agent);
245 g_signal_connect (agent, "streams-removed",
246 (GCallback) streams_removed_cb, self);
247 g_object_unref (agent);
248
249 break;
250 }
251 case PROP_STREAM_ID:
252 /* Construct only. */
253 self->priv->stream_id = g_value_get_uint (value);
254 break;
255 case PROP_COMPONENT_ID:
256 /* Construct only. */
257 self->priv->component_id = g_value_get_uint (value);
258 break;
259 default:
260 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
261 }
262 }
263
264 /***
265 * nice_io_stream_new:
266 * @agent: A #NiceAgent
267 * @stream_id: The ID of the agent’s stream to wrap
268 * @component_id: The ID of the agent’s component to wrap
269 *
270 * Create a new #NiceIOStream wrapping the given stream/component from @agent,
271 * which must be a reliable #NiceAgent.
272 *
273 * The constructed #NiceIOStream will not hold a reference to @agent. If @agent
274 * is destroyed before the I/O stream, %G_IO_ERROR_CLOSED will be returned for
275 * all subsequent operations on the stream.
276 *
277 * Returns: The new #NiceIOStream object
278 *
279 * Since: 0.1.5
280 */
281 GIOStream *
nice_io_stream_new(NiceAgent * agent,guint stream_id,guint component_id)282 nice_io_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
283 {
284 g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
285 g_return_val_if_fail (stream_id > 0, NULL);
286 g_return_val_if_fail (component_id > 0, NULL);
287
288 return g_object_new (NICE_TYPE_IO_STREAM,
289 "agent", agent,
290 "stream-id", stream_id,
291 "component-id", component_id,
292 NULL);
293 }
294
295 static GInputStream *
nice_io_stream_get_input_stream(GIOStream * stream)296 nice_io_stream_get_input_stream (GIOStream *stream)
297 {
298 NiceIOStream *self = NICE_IO_STREAM (stream);
299
300 if (G_UNLIKELY (self->priv->input_stream == NULL)) {
301 NiceAgent *agent;
302
303 /* Note that agent may be NULL here. NiceInputStream must support
304 * construction with a NULL agent. */
305 agent = g_weak_ref_get (&self->priv->agent_ref);
306 self->priv->input_stream = G_INPUT_STREAM (nice_input_stream_new (
307 agent, self->priv->stream_id, self->priv->component_id));
308 if (agent != NULL)
309 g_object_unref (agent);
310 }
311
312 return self->priv->input_stream;
313 }
314
315 static GOutputStream *
nice_io_stream_get_output_stream(GIOStream * stream)316 nice_io_stream_get_output_stream (GIOStream *stream)
317 {
318 NiceIOStream *self = NICE_IO_STREAM (stream);
319
320 if (G_UNLIKELY (self->priv->output_stream == NULL)) {
321 NiceAgent *agent;
322
323 /* Note that agent may be NULL here. NiceOutputStream must support
324 * construction with a NULL agent. */
325 agent = g_weak_ref_get (&self->priv->agent_ref);
326 self->priv->output_stream = g_object_new (NICE_TYPE_OUTPUT_STREAM,
327 "agent", agent,
328 "stream-id", self->priv->stream_id,
329 "component-id", self->priv->component_id,
330 NULL);
331
332 if (agent != NULL)
333 g_object_unref (agent);
334 }
335
336 return self->priv->output_stream;
337 }
338
339 static void
streams_removed_cb(NiceAgent * agent,guint * stream_ids,gpointer user_data)340 streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
341 {
342 NiceIOStream *self = NICE_IO_STREAM (user_data);
343 guint i;
344
345 for (i = 0; stream_ids[i] != 0; i++) {
346 if (stream_ids[i] == self->priv->stream_id) {
347 /* The socket has been closed. */
348 g_io_stream_close (G_IO_STREAM (self), NULL, NULL);
349 break;
350 }
351 }
352 }
353