Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / agent / iostream.c
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
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
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
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
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
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 *
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 *
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 *
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
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 }