]> git.0d.be Git - empathy.git/blob - libempathy/empathy-ft-factory.c
9cf90014b458b14bc38ad39993572cddf54ce55d
[empathy.git] / libempathy / empathy-ft-factory.c
1 /*
2  * empathy-ft-factory.c - Source for EmpathyFTFactory
3  * Copyright (C) 2009 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Author: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
20  */
21
22 /* empathy-ft-factory.c */
23
24 #include "empathy-ft-factory.h"
25 #include "empathy-request-util.h"
26 #include "empathy-utils.h"
27
28 /**
29  * SECTION:empathy-ft-factory
30  * @title:EmpathyFTFactory
31  * @short_description: creates #EmpathyFTHandler objects
32  * @include: libempathy/empathy-ft-factory.h
33  *
34  * #EmpathyFTFactory takes care of the creation of the #EmpathyFTHandler
35  * objects used for file transfer. As the creation of the handlers is
36  * async, a client will have to connect to the ::new-ft-handler signal
37  * to receive the handler.
38  * In case of an incoming file transfer, the handler will need the destination
39  * file before being useful; as this is usually decided by the user (e.g. with
40  * a file selector), a ::new-incoming-transfer is emitted by the factory when
41  * a destination file is needed, which can be set later with
42  * empathy_ft_factory_set_destination_for_incoming_handler().
43  */
44
45 G_DEFINE_TYPE (EmpathyFTFactory, empathy_ft_factory, G_TYPE_OBJECT);
46
47 enum {
48   NEW_FT_HANDLER,
49   NEW_INCOMING_TRANSFER,
50   LAST_SIGNAL
51 };
52
53 static EmpathyFTFactory *factory_singleton = NULL;
54 static guint signals[LAST_SIGNAL] = { 0 };
55
56 /* private structure */
57 typedef struct {
58   TpBaseClient *handler;
59 } EmpathyFTFactoryPriv;
60
61 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFTFactory)
62
63 static GObject *
64 do_constructor (GType type,
65     guint n_props,
66     GObjectConstructParam *props)
67 {
68   GObject *retval;
69
70   if (factory_singleton != NULL)
71     {
72       retval = g_object_ref (factory_singleton);
73     }
74   else
75     {
76       retval = G_OBJECT_CLASS (empathy_ft_factory_parent_class)->constructor
77         (type, n_props, props);
78
79       factory_singleton = EMPATHY_FT_FACTORY (retval);
80       g_object_add_weak_pointer (retval, (gpointer *) &factory_singleton);
81     }
82
83   return retval;
84 }
85
86 static void
87 empathy_ft_factory_dispose (GObject *object)
88 {
89   EmpathyFTFactory *self = (EmpathyFTFactory *) object;
90   EmpathyFTFactoryPriv *priv = GET_PRIV (self);
91
92   tp_clear_object (&priv->handler);
93
94   (G_OBJECT_CLASS (empathy_ft_factory_parent_class)->dispose) (object);
95 }
96
97 static void
98 empathy_ft_factory_class_init (EmpathyFTFactoryClass *klass)
99 {
100   GObjectClass *object_class = G_OBJECT_CLASS (klass);
101
102   g_type_class_add_private (klass, sizeof (EmpathyFTFactoryPriv));
103
104   object_class->constructor = do_constructor;
105   object_class->dispose = empathy_ft_factory_dispose;
106
107   /**
108    * EmpathyFTFactory::new-ft-handler
109    * @factory: the object which received the signal
110    * @handler: the handler made available by the factory
111    * @error: a #GError or %NULL
112    *
113    * The signal is emitted when a new #EmpathyFTHandler is available.
114    * Note that @handler is never %NULL even if @error is set, as you might want
115    * to display the error in an UI; in that case, the handler won't support
116    * any transfer.
117    */
118   signals[NEW_FT_HANDLER] =
119     g_signal_new ("new-ft-handler",
120       G_TYPE_FROM_CLASS (klass),
121       G_SIGNAL_RUN_LAST, 0,
122       NULL, NULL,
123       g_cclosure_marshal_generic,
124       G_TYPE_NONE, 2, EMPATHY_TYPE_FT_HANDLER, G_TYPE_POINTER);
125
126   /**
127    * EmpathyFTFactory::new-incoming-transfer
128    * @factory: the object which received the signal
129    * @handler: the incoming handler being constructed
130    * @error: a #GError or %NULL
131    *
132    * The signal is emitted when a new incoming #EmpathyFTHandler is being
133    * constructed, and needs a destination #GFile to be useful.
134    * Clients that connect to this signal will have to call
135    * empathy_ft_factory_set_destination_for_incoming_handler() when they
136    * have a #GFile.
137    * Note that @handler is never %NULL even if @error is set, as you might want
138    * to display the error in an UI; in that case, the handler won't support
139    * any transfer.
140    */
141   signals[NEW_INCOMING_TRANSFER] =
142     g_signal_new ("new-incoming-transfer",
143       G_TYPE_FROM_CLASS (klass),
144       G_SIGNAL_RUN_LAST, 0,
145       NULL, NULL,
146       g_cclosure_marshal_generic,
147       G_TYPE_NONE, 2, EMPATHY_TYPE_FT_HANDLER, G_TYPE_POINTER);
148 }
149
150 static void
151 ft_handler_incoming_ready_cb (EmpathyFTHandler *handler,
152     GError *error,
153     gpointer user_data)
154 {
155   EmpathyFTFactory *factory = user_data;
156
157   g_signal_emit (factory, signals[NEW_INCOMING_TRANSFER], 0, handler, error);
158 }
159
160 static void
161 handle_channels_cb (TpSimpleHandler *handler,
162     TpAccount *account,
163     TpConnection *connection,
164     GList *channels,
165     GList *requests_satisfied,
166     gint64 user_action_time,
167     TpHandleChannelsContext *context,
168     gpointer user_data)
169 {
170   EmpathyFTFactory *self = user_data;
171   GList *l;
172
173   for (l = channels; l != NULL; l = g_list_next (l))
174     {
175       TpChannel *channel = l->data;
176
177       if (tp_proxy_get_invalidated (channel) != NULL)
178         continue;
179
180       if (!TP_IS_FILE_TRANSFER_CHANNEL (channel))
181         continue;
182
183       /* We handle only incoming FT */
184       empathy_ft_handler_new_incoming ((TpFileTransferChannel *) channel,
185           ft_handler_incoming_ready_cb, self);
186     }
187
188
189   tp_handle_channels_context_accept (context);
190 }
191
192 static void
193 empathy_ft_factory_init (EmpathyFTFactory *self)
194 {
195   EmpathyFTFactoryPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
196     EMPATHY_TYPE_FT_FACTORY, EmpathyFTFactoryPriv);
197   TpAccountManager *am;
198
199   self->priv = priv;
200
201   am = tp_account_manager_dup ();
202
203   priv->handler = tp_simple_handler_new_with_am (am, FALSE, FALSE,
204       EMPATHY_FT_BUS_NAME_SUFFIX, FALSE, handle_channels_cb, self, NULL);
205
206   tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
207         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
208           TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
209         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
210         /* Only handle *incoming* channels as outgoing FT channels has to be
211          * handled by the requester. */
212         TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, FALSE,
213         NULL));
214
215   g_object_unref (am);
216 }
217
218 static void
219 ft_handler_outgoing_ready_cb (EmpathyFTHandler *handler,
220     GError *error,
221     gpointer user_data)
222 {
223   EmpathyFTFactory *factory = EMPATHY_FT_FACTORY (user_data);
224
225   g_signal_emit (factory, signals[NEW_FT_HANDLER], 0, handler, error);
226
227   g_object_unref (factory);
228 }
229
230 /* public methods */
231
232 /**
233  * empathy_ft_factory_dup_singleton:
234  *
235  * Gives the caller a reference to the #EmpathyFTFactory singleton,
236  * (creating it if necessary).
237  *
238  * Return value: an #EmpathyFTFactory object
239  */
240 EmpathyFTFactory *
241 empathy_ft_factory_dup_singleton (void)
242 {
243   return g_object_new (EMPATHY_TYPE_FT_FACTORY, NULL);
244 }
245
246 /**
247  * empathy_ft_factory_new_transfer_outgoing:
248  * @factory: an #EmpathyFTFactory
249  * @contact: the #EmpathyContact destination of the transfer
250  * @source: the #GFile to be transferred to @contact
251  *
252  * Trigger the creation of an #EmpathyFTHandler object to send @source to
253  * the specified @contact.
254  */
255 void
256 empathy_ft_factory_new_transfer_outgoing (EmpathyFTFactory *factory,
257     EmpathyContact *contact,
258     GFile *source,
259     gint64 action_time)
260 {
261   g_return_if_fail (EMPATHY_IS_FT_FACTORY (factory));
262   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
263   g_return_if_fail (G_IS_FILE (source));
264
265   empathy_ft_handler_new_outgoing (contact, source, action_time,
266       ft_handler_outgoing_ready_cb, g_object_ref (factory));
267 }
268
269 /**
270  * empathy_ft_factory_set_destination_for_incoming_handler:
271  * @factory: an #EmpathyFTFactory
272  * @handler: the #EmpathyFTHandler to set the destination of
273  * @destination: the #GFile destination of the transfer
274  *
275  * Sets @destination as destination file for the transfer. After the call of
276  * this method, the ::new-ft-handler will be emitted for the incoming handler.
277  */
278 void
279 empathy_ft_factory_set_destination_for_incoming_handler (
280     EmpathyFTFactory *factory,
281     EmpathyFTHandler *handler,
282     GFile *destination)
283 {
284   g_return_if_fail (EMPATHY_IS_FT_FACTORY (factory));
285   g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler));
286   g_return_if_fail (G_IS_FILE (destination));
287
288   empathy_ft_handler_incoming_set_destination (handler, destination);
289
290   g_signal_emit (factory, signals[NEW_FT_HANDLER], 0, handler, NULL);
291 }
292
293 gboolean
294 empathy_ft_factory_register (EmpathyFTFactory *self,
295     GError **error)
296 {
297   EmpathyFTFactoryPriv *priv = GET_PRIV (self);
298
299   return tp_base_client_register (priv->handler, error);
300 }