1 /*
2 * file-transfer-channel.c - Source for SalutFileTransferChannel
3 * Copyright (C) 2007 Marco Barisione <marco@barisione.org>
4 * Copyright (C) 2005, 2007, 2008 Collabora Ltd.
5 * @author: Sjoerd Simons <sjoerd@luon.net>
6 * @author: Jonny Lamb <jonny.lamb@collabora.co.uk>
7 * @author: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <glib/gstdio.h>
25 #include <dbus/dbus-glib.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #ifdef G_OS_WIN32
32 #include <windows.h>
33 #undef interface
34 #else
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 #include <gio/gunixsocketaddress.h>
39 #endif
40
41 #define DEBUG_FLAG DEBUG_FT
42 #include "debug.h"
43
44 #include "file-transfer-channel.h"
45 #include "signals-marshal.h"
46
47 #include "connection.h"
48 #include "im-manager.h"
49 #include "contact.h"
50 #include "namespaces.h"
51
52 #include <wocky/wocky.h>
53 #include <gibber/gibber-file-transfer.h>
54 #include <gibber/gibber-oob-file-transfer.h>
55
56 #include <telepathy-glib/channel-iface.h>
57 #include <telepathy-glib/interfaces.h>
58 #include <telepathy-glib/dbus.h>
59 #include <telepathy-glib/svc-generic.h>
60 #include <telepathy-glib/gtypes.h>
61 #include <telepathy-glib/gnio-util.h>
62
63 static void
64 file_transfer_iface_init (gpointer g_iface, gpointer iface_data);
65
66 G_DEFINE_TYPE_WITH_CODE (SalutFileTransferChannel, salut_file_transfer_channel,
67 TP_TYPE_BASE_CHANNEL,
68 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
69 file_transfer_iface_init);
70 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
71 NULL);
72 );
73
74 #define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
75
76 #define SALUT_UNDEFINED_FILE_SIZE G_MAXUINT64
77
78 static const char *salut_file_transfer_channel_interfaces[] = { NULL };
79
80 /* properties */
81 enum
82 {
83 PROP_STATE = 1,
84 PROP_CONTENT_TYPE,
85 PROP_FILENAME,
86 PROP_SIZE,
87 PROP_CONTENT_HASH_TYPE,
88 PROP_CONTENT_HASH,
89 PROP_DESCRIPTION,
90 PROP_DATE,
91 PROP_AVAILABLE_SOCKET_TYPES,
92 PROP_TRANSFERRED_BYTES,
93 PROP_INITIAL_OFFSET,
94 PROP_URI,
95
96 /* Chan.I.FileTransfer.Metadata */
97 PROP_SERVICE_NAME,
98 PROP_METADATA,
99
100 PROP_CONTACT,
101 PROP_CONNECTION,
102 LAST_PROPERTY
103 };
104
105 /* private structure */
106 struct _SalutFileTransferChannelPrivate {
107 gboolean dispose_has_run;
108 SalutContact *contact;
109 GibberFileTransfer *ft;
110 GTimeVal last_transferred_bytes_emitted;
111 guint progress_timer;
112 GSocket *socket;
113 gboolean remote_accepted;
114 GIOChannel *channel;
115
116 /* properties */
117 TpFileTransferState state;
118 gchar *content_type;
119 gchar *filename;
120 guint64 size;
121 TpFileHashType content_hash_type;
122 gchar *content_hash;
123 gchar *description;
124 GHashTable *available_socket_types;
125 guint64 transferred_bytes;
126 guint64 initial_offset;
127 guint64 date;
128 gchar *uri;
129 gchar *service_name;
130 GHashTable *metadata;
131 };
132
133 static void salut_file_transfer_channel_set_state (
134 TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state,
135 TpFileTransferStateChangeReason reason);
136
137 static void
salut_file_transfer_channel_close(TpBaseChannel * base)138 salut_file_transfer_channel_close (TpBaseChannel *base)
139 {
140 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (base);
141
142 if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
143 self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
144 {
145 gibber_file_transfer_cancel (self->priv->ft, 406);
146 salut_file_transfer_channel_set_state (
147 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
148 TP_FILE_TRANSFER_STATE_CANCELLED,
149 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED);
150 }
151
152 tp_base_channel_destroyed (base);
153 }
154
155 static void
salut_file_transfer_channel_init(SalutFileTransferChannel * obj)156 salut_file_transfer_channel_init (SalutFileTransferChannel *obj)
157 {
158 obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
159 SALUT_TYPE_FILE_TRANSFER_CHANNEL, SalutFileTransferChannelPrivate);
160
161 /* allocate any data required by the object here */
162 obj->priv->contact = NULL;
163 }
164
165 static void
contact_lost_cb(SalutContact * contact,SalutFileTransferChannel * self)166 contact_lost_cb (SalutContact *contact,
167 SalutFileTransferChannel *self)
168 {
169 g_assert (contact == self->priv->contact);
170
171 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
172 {
173 DEBUG ("%s was disconnected. Ignoring as there is still a chance to"
174 " be able to complete the transfer", contact->name);
175 return;
176 }
177
178 DEBUG ("%s was disconnected. Cancel file tranfer.", contact->name);
179 salut_file_transfer_channel_set_state (
180 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
181 TP_FILE_TRANSFER_STATE_CANCELLED,
182 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
183 }
184
185 static void
salut_file_transfer_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)186 salut_file_transfer_channel_get_property (GObject *object,
187 guint property_id,
188 GValue *value,
189 GParamSpec *pspec)
190 {
191 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
192
193 switch (property_id)
194 {
195 case PROP_CONTACT:
196 g_value_set_object (value, self->priv->contact);
197 break;
198 case PROP_STATE:
199 g_value_set_uint (value, self->priv->state);
200 break;
201 case PROP_CONTENT_TYPE:
202 g_value_set_string (value, self->priv->content_type);
203 break;
204 case PROP_FILENAME:
205 g_value_set_string (value, self->priv->filename);
206 break;
207 case PROP_SIZE:
208 g_value_set_uint64 (value, self->priv->size);
209 break;
210 case PROP_CONTENT_HASH_TYPE:
211 g_value_set_uint (value, self->priv->content_hash_type);
212 break;
213 case PROP_CONTENT_HASH:
214 g_value_set_string (value, self->priv->content_hash);
215 break;
216 case PROP_DESCRIPTION:
217 g_value_set_string (value, self->priv->description);
218 break;
219 case PROP_AVAILABLE_SOCKET_TYPES:
220 g_value_set_boxed (value, self->priv->available_socket_types);
221 break;
222 case PROP_TRANSFERRED_BYTES:
223 g_value_set_uint64 (value, self->priv->transferred_bytes);
224 break;
225 case PROP_INITIAL_OFFSET:
226 g_value_set_uint64 (value, self->priv->initial_offset);
227 break;
228 case PROP_DATE:
229 g_value_set_uint64 (value, self->priv->date);
230 break;
231 case PROP_URI:
232 g_value_set_string (value,
233 self->priv->uri != NULL ? self->priv->uri : "");
234 break;
235 case PROP_SERVICE_NAME:
236 g_value_set_string (value,
237 self->priv->service_name != NULL ? self->priv->service_name : "");
238 break;
239 case PROP_METADATA:
240 {
241 /* We're fine with priv->metadata being NULL but dbus-glib
242 * doesn't like iterating NULL as if it was a hash table. */
243 if (self->priv->metadata == NULL)
244 {
245 g_value_take_boxed (value,
246 g_hash_table_new (g_str_hash, g_str_equal));
247 }
248 else
249 {
250 g_value_set_boxed (value, self->priv->metadata);
251 }
252 }
253 break;
254 default:
255 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
256 break;
257 }
258 }
259
260 static void
salut_file_transfer_channel_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)261 salut_file_transfer_channel_set_property (GObject *object,
262 guint property_id,
263 const GValue *value,
264 GParamSpec *pspec)
265 {
266 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
267
268 switch (property_id)
269 {
270 case PROP_CONTACT:
271 self->priv->contact = g_value_dup_object (value);
272 g_signal_connect (self->priv->contact, "lost",
273 G_CALLBACK (contact_lost_cb), self);
274 break;
275 case PROP_STATE:
276 salut_file_transfer_channel_set_state (
277 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object),
278 g_value_get_uint (value),
279 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
280 break;
281 case PROP_CONTENT_TYPE:
282 g_free (self->priv->content_type);
283 self->priv->content_type = g_value_dup_string (value);
284 break;
285 case PROP_FILENAME:
286 g_free (self->priv->filename);
287 self->priv->filename = g_value_dup_string (value);
288 break;
289 case PROP_SIZE:
290 self->priv->size = g_value_get_uint64 (value);
291 break;
292 case PROP_CONTENT_HASH_TYPE:
293 self->priv->content_hash_type = g_value_get_uint (value);
294 break;
295 case PROP_CONTENT_HASH:
296 g_free (self->priv->content_hash);
297 self->priv->content_hash = g_value_dup_string (value);
298 break;
299 case PROP_DESCRIPTION:
300 g_free (self->priv->description);
301 self->priv->description = g_value_dup_string (value);
302 break;
303 case PROP_DATE:
304 self->priv->date = g_value_get_uint64 (value);
305 break;
306 case PROP_INITIAL_OFFSET:
307 self->priv->initial_offset = g_value_get_uint64 (value);
308 break;
309 case PROP_URI:
310 g_assert (self->priv->uri == NULL); /* construct only */
311 self->priv->uri = g_value_dup_string (value);
312 break;
313 case PROP_SERVICE_NAME:
314 g_assert (self->priv->service_name == NULL); /* construct only */
315 self->priv->service_name = g_value_dup_string (value);
316 break;
317 case PROP_METADATA:
318 self->priv->metadata = g_value_dup_boxed (value);
319 break;
320 default:
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
322 break;
323 }
324 }
325
326 static void
free_array(GArray * array)327 free_array (GArray *array)
328 {
329 g_array_unref (array);
330 }
331
332 static void
salut_file_transfer_channel_constructed(GObject * obj)333 salut_file_transfer_channel_constructed (GObject *obj)
334 {
335 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (obj);
336 TpBaseChannel *base = TP_BASE_CHANNEL (obj);
337 TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
338 TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
339 base_conn, TP_HANDLE_TYPE_CONTACT);
340 SalutConnection *conn = SALUT_CONNECTION (base_conn);
341 GArray *unix_access;
342 GArray *ip_access;
343 TpSocketAccessControl access_control;
344
345 /* Parent constructed chain */
346 void (*chain_up) (GObject *) =
347 ((GObjectClass *) salut_file_transfer_channel_parent_class)->constructed;
348
349 if (chain_up != NULL)
350 chain_up (obj);
351
352 /* ref our porter */
353 wocky_meta_porter_hold (WOCKY_META_PORTER (conn->porter),
354 WOCKY_CONTACT (self->priv->contact));
355
356 /* Initialise the available socket types hash table */
357 self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash,
358 g_direct_equal, NULL, (GDestroyNotify) free_array);
359
360 /* Socket_Address_Type_Unix */
361 unix_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
362 1);
363 access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
364 g_array_append_val (unix_access, access_control);
365 g_hash_table_insert (self->priv->available_socket_types,
366 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_access);
367
368 /* Socket_Address_Type_IPv4 */
369 ip_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
370 1);
371 g_array_append_val (ip_access, access_control);
372 g_hash_table_insert (self->priv->available_socket_types,
373 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV4), ip_access);
374
375 g_hash_table_insert (self->priv->available_socket_types,
376 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV6), ip_access);
377
378 DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, "
379 "file: \"%s\", size: %" G_GUINT64_FORMAT ")",
380 tp_base_channel_get_object_path (base),
381 tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)),
382 tp_handle_inspect (contact_repo, tp_base_channel_get_initiator (base)),
383 self->priv->filename, self->priv->size);
384
385 if (!tp_base_channel_is_requested (base))
386 /* Incoming transfer, URI has to be set by the handler */
387 g_assert (self->priv->uri == NULL);
388 }
389
390 static void
391 salut_file_transfer_channel_dispose (GObject *object);
392 static void
393 salut_file_transfer_channel_finalize (GObject *object);
394
395 static gboolean
file_transfer_channel_properties_setter(GObject * object,GQuark interface,GQuark name,const GValue * value,gpointer setter_data,GError ** error)396 file_transfer_channel_properties_setter (GObject *object,
397 GQuark interface,
398 GQuark name,
399 const GValue *value,
400 gpointer setter_data,
401 GError **error)
402 {
403 SalutFileTransferChannel *self = (SalutFileTransferChannel *) object;
404
405 g_return_val_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER,
406 FALSE);
407
408 /* There is only one property with write access. So TpDBusPropertiesMixin
409 * already checked this. */
410 g_assert (name == g_quark_from_static_string ("URI"));
411
412 /* TpDBusPropertiesMixin already checked this */
413 g_assert (G_VALUE_HOLDS_STRING (value));
414
415 if (self->priv->uri != NULL)
416 {
417 g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
418 "URI has already be set");
419 return FALSE;
420 }
421
422 if (tp_base_channel_is_requested (TP_BASE_CHANNEL (self)))
423 {
424 g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
425 "Channel is not an incoming transfer");
426 return FALSE;
427 }
428
429 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
430 {
431 g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
432 "State is not pending; cannot set URI");
433 return FALSE;
434 }
435
436 self->priv->uri = g_value_dup_string (value);
437
438 tp_svc_channel_type_file_transfer_emit_uri_defined (self, self->priv->uri);
439
440 return TRUE;
441 }
442
443 static void
salut_file_transfer_channel_fill_immutable_properties(TpBaseChannel * chan,GHashTable * properties)444 salut_file_transfer_channel_fill_immutable_properties (TpBaseChannel *chan,
445 GHashTable *properties)
446 {
447 TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS (
448 salut_file_transfer_channel_parent_class);
449
450 cls->fill_immutable_properties (chan, properties);
451
452 tp_dbus_properties_mixin_fill_properties_hash (
453 G_OBJECT (chan), properties,
454 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State",
455 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
456 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
457 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
458 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType",
459 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash",
460 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
461 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
462 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
463 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
464 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
465 TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
466 TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
467 NULL);
468
469 /* URI is immutable only for outgoing transfers */
470 if (tp_base_channel_is_requested (chan))
471 {
472 tp_dbus_properties_mixin_fill_properties_hash (
473 G_OBJECT (chan), properties,
474 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL);
475 }
476 }
477
478 static gchar *
salut_file_transfer_channel_get_object_path_suffix(TpBaseChannel * chan)479 salut_file_transfer_channel_get_object_path_suffix (TpBaseChannel *chan)
480 {
481 return g_strdup_printf ("FileTransferChannel/%p", chan);
482 }
483
484 static void
salut_file_transfer_channel_class_init(SalutFileTransferChannelClass * salut_file_transfer_channel_class)485 salut_file_transfer_channel_class_init (
486 SalutFileTransferChannelClass *salut_file_transfer_channel_class)
487 {
488 GObjectClass *object_class = G_OBJECT_CLASS (
489 salut_file_transfer_channel_class);
490 TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (
491 salut_file_transfer_channel_class);
492 GParamSpec *param_spec;
493
494 static TpDBusPropertiesMixinPropImpl file_props[] = {
495 { "State", "state", NULL },
496 { "ContentType", "content-type", "content-type" },
497 { "Filename", "filename", "filename" },
498 { "Size", "size", "size" },
499 { "ContentHashType", "content-hash-type", "content-hash-type" },
500 { "ContentHash", "content-hash", "content-hash" },
501 { "Description", "description", "description" },
502 { "AvailableSocketTypes", "available-socket-types", NULL },
503 { "TransferredBytes", "transferred-bytes", NULL },
504 { "InitialOffset", "initial-offset", NULL },
505 { "Date", "date", "date" },
506 { "URI", "uri", NULL },
507 { NULL }
508 };
509
510 static TpDBusPropertiesMixinPropImpl file_metadata_props[] = {
511 { "ServiceName", "service-name", NULL },
512 { "Metadata", "metadata", NULL },
513 { NULL }
514 };
515
516 static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
517 { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
518 tp_dbus_properties_mixin_getter_gobject_properties,
519 file_transfer_channel_properties_setter,
520 file_props
521 },
522 { TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
523 tp_dbus_properties_mixin_getter_gobject_properties,
524 NULL,
525 file_metadata_props
526 }, { NULL }
527 };
528
529 g_type_class_add_private (salut_file_transfer_channel_class,
530 sizeof (SalutFileTransferChannelPrivate));
531
532 object_class->dispose = salut_file_transfer_channel_dispose;
533 object_class->finalize = salut_file_transfer_channel_finalize;
534 object_class->constructed = salut_file_transfer_channel_constructed;
535 object_class->get_property = salut_file_transfer_channel_get_property;
536 object_class->set_property = salut_file_transfer_channel_set_property;
537
538 base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER;
539 base_class->interfaces = salut_file_transfer_channel_interfaces;
540 base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
541 base_class->close = salut_file_transfer_channel_close;
542 base_class->fill_immutable_properties =
543 salut_file_transfer_channel_fill_immutable_properties;
544 base_class->get_object_path_suffix =
545 salut_file_transfer_channel_get_object_path_suffix;
546
547 param_spec = g_param_spec_object ("contact",
548 "SalutContact object",
549 "Salut Contact to which this channel is dedicated",
550 SALUT_TYPE_CONTACT,
551 G_PARAM_CONSTRUCT_ONLY |
552 G_PARAM_READWRITE |
553 G_PARAM_STATIC_NICK |
554 G_PARAM_STATIC_BLURB);
555 g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
556
557 param_spec = g_param_spec_uint (
558 "state",
559 "TpFileTransferState state",
560 "State of the file transfer in this channel",
561 0,
562 G_MAXUINT,
563 0,
564 G_PARAM_CONSTRUCT_ONLY |
565 G_PARAM_READWRITE |
566 G_PARAM_STATIC_NICK |
567 G_PARAM_STATIC_BLURB);
568 g_object_class_install_property (object_class, PROP_STATE, param_spec);
569
570 param_spec = g_param_spec_string (
571 "content-type",
572 "gchar *content-type",
573 "ContentType of the file",
574 "application/octet-stream",
575 G_PARAM_CONSTRUCT_ONLY |
576 G_PARAM_READWRITE |
577 G_PARAM_STATIC_NICK |
578 G_PARAM_STATIC_BLURB);
579 g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
580 param_spec);
581
582 param_spec = g_param_spec_string (
583 "filename",
584 "gchar *filename",
585 "Name of the file",
586 "",
587 G_PARAM_CONSTRUCT_ONLY |
588 G_PARAM_READWRITE |
589 G_PARAM_STATIC_NICK |
590 G_PARAM_STATIC_BLURB);
591 g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
592
593 param_spec = g_param_spec_uint64 (
594 "size",
595 "guint size",
596 "Size of the file in bytes",
597 0,
598 G_MAXUINT64,
599 SALUT_UNDEFINED_FILE_SIZE,
600 G_PARAM_CONSTRUCT_ONLY |
601 G_PARAM_READWRITE |
602 G_PARAM_STATIC_NICK |
603 G_PARAM_STATIC_BLURB);
604 g_object_class_install_property (object_class, PROP_SIZE, param_spec);
605
606 param_spec = g_param_spec_uint (
607 "content-hash-type",
608 "SalutFileHashType content-hash-type",
609 "Hash type",
610 0,
611 G_MAXUINT,
612 TP_FILE_HASH_TYPE_NONE,
613 G_PARAM_READWRITE |
614 G_PARAM_CONSTRUCT_ONLY |
615 G_PARAM_STATIC_NICK |
616 G_PARAM_STATIC_BLURB);
617 g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE,
618 param_spec);
619
620 param_spec = g_param_spec_string (
621 "content-hash",
622 "gchar *content-hash",
623 "Hash of the file contents",
624 "",
625 G_PARAM_CONSTRUCT_ONLY |
626 G_PARAM_READWRITE |
627 G_PARAM_STATIC_NICK |
628 G_PARAM_STATIC_BLURB);
629 g_object_class_install_property (object_class, PROP_CONTENT_HASH,
630 param_spec);
631
632 param_spec = g_param_spec_string (
633 "description",
634 "gchar *description",
635 "Description of the file",
636 "",
637 G_PARAM_CONSTRUCT_ONLY |
638 G_PARAM_READWRITE |
639 G_PARAM_STATIC_NICK |
640 G_PARAM_STATIC_BLURB);
641 g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
642
643 param_spec = g_param_spec_boxed (
644 "available-socket-types",
645 "SalutSupportedSocketMap available-socket-types",
646 "Available socket types",
647 dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, DBUS_TYPE_G_UINT_ARRAY),
648 G_PARAM_READABLE |
649 G_PARAM_STATIC_NICK |
650 G_PARAM_STATIC_BLURB);
651 g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
652 param_spec);
653
654 param_spec = g_param_spec_uint64 (
655 "transferred-bytes",
656 "guint64 transferred-bytes",
657 "Bytes transferred",
658 0,
659 G_MAXUINT64,
660 0,
661 G_PARAM_READABLE |
662 G_PARAM_STATIC_NICK |
663 G_PARAM_STATIC_BLURB);
664 g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
665 param_spec);
666
667 param_spec = g_param_spec_uint64 (
668 "initial-offset",
669 "guint64 initial_offset",
670 "Offset set at the beginning of the transfer",
671 0,
672 G_MAXUINT64,
673 0,
674 G_PARAM_READWRITE |
675 G_PARAM_STATIC_NICK |
676 G_PARAM_STATIC_BLURB);
677 g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
678 param_spec);
679
680 param_spec = g_param_spec_uint64 (
681 "date",
682 "Epoch time",
683 "the last modification time of the file being transferred",
684 0,
685 G_MAXUINT64,
686 0,
687 G_PARAM_CONSTRUCT_ONLY |
688 G_PARAM_READWRITE |
689 G_PARAM_STATIC_NICK |
690 G_PARAM_STATIC_BLURB);
691 g_object_class_install_property (object_class, PROP_DATE,
692 param_spec);
693
694 param_spec = g_param_spec_string (
695 "uri", "URI",
696 "URI of the file being transferred",
697 NULL,
698 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
699 g_object_class_install_property (object_class, PROP_URI,
700 param_spec);
701
702 param_spec = g_param_spec_string ("service-name",
703 "ServiceName",
704 "The Metadata.ServiceName property of this channel",
705 "",
706 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
707 g_object_class_install_property (object_class, PROP_SERVICE_NAME,
708 param_spec);
709
710 param_spec = g_param_spec_boxed ("metadata",
711 "Metadata",
712 "The Metadata.Metadata property of this channel",
713 TP_HASH_TYPE_METADATA,
714 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
715 g_object_class_install_property (object_class, PROP_METADATA,
716 param_spec);
717
718 salut_file_transfer_channel_class->dbus_props_class.interfaces = \
719 prop_interfaces;
720 tp_dbus_properties_mixin_class_init (object_class,
721 G_STRUCT_OFFSET (SalutFileTransferChannelClass, dbus_props_class));
722 }
723
724 void
salut_file_transfer_channel_dispose(GObject * object)725 salut_file_transfer_channel_dispose (GObject *object)
726 {
727 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
728
729 if (self->priv->dispose_has_run)
730 return;
731
732 self->priv->dispose_has_run = TRUE;
733
734 if (self->priv->progress_timer != 0)
735 {
736 g_source_remove (self->priv->progress_timer);
737 self->priv->progress_timer = 0;
738 }
739
740 if (self->priv->contact)
741 {
742 g_signal_handlers_disconnect_by_func (self->priv->contact,
743 contact_lost_cb, self);
744 g_object_unref (self->priv->contact);
745 self->priv->contact = NULL;
746 }
747
748 if (self->priv->ft != NULL)
749 {
750 g_object_unref (self->priv->ft);
751 self->priv->ft = NULL;
752 }
753
754 if (self->priv->channel != NULL)
755 {
756 g_io_channel_unref (self->priv->channel);
757 self->priv->channel = NULL;
758 }
759
760 /* release any references held by the object here */
761
762 if (G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->dispose)
763 G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->dispose (object);
764 }
765
766 static void
salut_file_transfer_channel_finalize(GObject * object)767 salut_file_transfer_channel_finalize (GObject *object)
768 {
769 #ifdef G_OS_UNIX
770 GSocketAddress *addr;
771 #endif
772 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
773
774 /* free any data held directly by the object here */
775 g_free (self->priv->filename);
776 #ifdef G_OS_UNIX
777 if (self->priv->socket != NULL)
778 {
779 addr = g_socket_get_local_address (self->priv->socket, NULL);
780 if (g_socket_address_get_family (addr) == G_SOCKET_FAMILY_UNIX)
781 {
782 const gchar *path;
783 path = g_unix_socket_address_get_path ((GUnixSocketAddress *) addr);
784 g_unlink (path);
785 }
786 g_object_unref (addr);
787 g_object_unref (self->priv->socket);
788 }
789 #endif
790 g_free (self->priv->content_type);
791 g_free (self->priv->content_hash);
792 g_free (self->priv->description);
793 g_hash_table_unref (self->priv->available_socket_types);
794 g_free (self->priv->uri);
795 g_free (self->priv->service_name);
796 if (self->priv->metadata != NULL)
797 g_hash_table_unref (self->priv->metadata);
798
799 if (G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->finalize)
800 G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->finalize (object);
801 }
802
803 static void
error_cb(GibberFileTransfer * ft,guint domain,gint code,const gchar * message,SalutFileTransferChannel * self)804 error_cb (GibberFileTransfer *ft,
805 guint domain,
806 gint code,
807 const gchar *message,
808 SalutFileTransferChannel *self)
809 {
810 gboolean receiver = !tp_base_channel_is_requested (TP_BASE_CHANNEL (self));
811
812 if (domain == GIBBER_FILE_TRANSFER_ERROR && code ==
813 GIBBER_FILE_TRANSFER_ERROR_NOT_FOUND && receiver)
814 {
815 /* Inform the sender we weren't able to retrieve the file */
816 gibber_file_transfer_cancel (self->priv->ft, 404);
817 }
818
819 salut_file_transfer_channel_set_state (
820 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
821 TP_FILE_TRANSFER_STATE_CANCELLED,
822 receiver ?
823 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR :
824 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR);
825 }
826
827 static void
ft_finished_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)828 ft_finished_cb (GibberFileTransfer *ft,
829 SalutFileTransferChannel *self)
830 {
831 SalutConnection *conn = SALUT_CONNECTION (
832 tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
833 WockyPorter *porter = conn->porter;
834
835 salut_file_transfer_channel_set_state (
836 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
837 TP_FILE_TRANSFER_STATE_COMPLETED,
838 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
839
840 wocky_meta_porter_unhold (WOCKY_META_PORTER (porter),
841 WOCKY_CONTACT (self->priv->contact));
842 }
843
844 static void
ft_remote_cancelled_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)845 ft_remote_cancelled_cb (GibberFileTransfer *ft,
846 SalutFileTransferChannel *self)
847 {
848 SalutConnection *conn = SALUT_CONNECTION (
849 tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
850 WockyPorter *porter = conn->porter;
851
852 gibber_file_transfer_cancel (ft, 406);
853 salut_file_transfer_channel_set_state (
854 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
855 TP_FILE_TRANSFER_STATE_CANCELLED,
856 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
857
858 wocky_meta_porter_unhold (WOCKY_META_PORTER (porter),
859 WOCKY_CONTACT (self->priv->contact));
860 }
861
862 static void
remote_accepted_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)863 remote_accepted_cb (GibberFileTransfer *ft,
864 SalutFileTransferChannel *self)
865 {
866 self->priv->remote_accepted = TRUE;
867
868 /* if we've got the IO channel here then gibber_file_transfer_send
869 * hasn't been called so let's call it now before doing anything
870 * else. */
871 if (self->priv->channel != NULL)
872 {
873 gibber_file_transfer_send (ft, self->priv->channel);
874
875 /* we have no need for this anymore */
876 g_io_channel_unref (self->priv->channel);
877 self->priv->channel = NULL;
878 }
879
880 if (self->priv->socket != NULL)
881 {
882 /* ProvideFile has already been called. Channel is Open */
883 tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
884 self->priv->initial_offset);
885
886 salut_file_transfer_channel_set_state (
887 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
888 TP_FILE_TRANSFER_STATE_OPEN,
889 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
890 }
891 else
892 {
893 /* Client has to call ProvideFile to open the channel */
894 salut_file_transfer_channel_set_state (
895 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
896 TP_FILE_TRANSFER_STATE_ACCEPTED,
897 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
898 }
899
900 g_signal_connect (ft, "finished", G_CALLBACK (ft_finished_cb), self);
901 }
902
903 static gboolean setup_local_socket (SalutFileTransferChannel *self,
904 TpSocketAddressType address_type, guint access_control);
905 static void ft_transferred_chunk_cb (GibberFileTransfer *ft, guint64 count,
906 SalutFileTransferChannel *self);
907
908 static GList *
add_metadata_forms(SalutFileTransferChannel * self,GibberFileTransfer * ft)909 add_metadata_forms (SalutFileTransferChannel *self,
910 GibberFileTransfer *ft)
911 {
912 GError *error = NULL;
913 GQueue queue = G_QUEUE_INIT;
914
915 if (!tp_str_empty (self->priv->service_name))
916 {
917 WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
918 WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
919 '(', "x",
920 ':', WOCKY_XMPP_NS_DATA,
921 '@', "type", "result",
922 '(', "field",
923 '@', "var", "FORM_TYPE",
924 '@', "type", "hidden",
925 '(', "value",
926 '$', NS_TP_FT_METADATA_SERVICE,
927 ')',
928 ')',
929 '(', "field",
930 '@', "var", "ServiceName",
931 '(', "value",
932 '$', self->priv->service_name,
933 ')',
934 ')',
935 ')',
936 NULL);
937 WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
938 WockyDataForm *form = wocky_data_form_new_from_node (x, &error);
939
940 if (form == NULL)
941 {
942 DEBUG ("Failed to parse form (wat): %s", error->message);
943 g_clear_error (&error);
944 }
945 else
946 {
947 g_queue_push_tail (&queue, form);
948 }
949
950 g_object_unref (tmp);
951 }
952
953 if (self->priv->metadata != NULL
954 && g_hash_table_size (self->priv->metadata) > 0)
955 {
956 WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
957 WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
958 '(', "x",
959 ':', WOCKY_XMPP_NS_DATA,
960 '@', "type", "result",
961 '(', "field",
962 '@', "var", "FORM_TYPE",
963 '@', "type", "hidden",
964 '(', "value",
965 '$', NS_TP_FT_METADATA,
966 ')',
967 ')',
968 ')',
969 NULL);
970 WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
971 WockyDataForm *form;
972 GHashTableIter iter;
973 gpointer key, val;
974
975 g_hash_table_iter_init (&iter, self->priv->metadata);
976 while (g_hash_table_iter_next (&iter, &key, &val))
977 {
978 const gchar * const *values = val;
979
980 WockyNode *field = wocky_node_add_child (x, "field");
981 wocky_node_set_attribute (field, "var", key);
982
983 for (; values != NULL && *values != NULL; values++)
984 {
985 wocky_node_add_child_with_content (field, "value", *values);
986 }
987 }
988
989 form = wocky_data_form_new_from_node (x, &error);
990
991 if (form == NULL)
992 {
993 DEBUG ("Failed to parse form (wat): %s", error->message);
994 g_clear_error (&error);
995 }
996 else
997 {
998 g_queue_push_tail (&queue, form);
999 }
1000
1001 g_object_unref (tmp);
1002 }
1003
1004 return queue.head;
1005 }
1006
1007 static void
send_file_offer(SalutFileTransferChannel * self)1008 send_file_offer (SalutFileTransferChannel *self)
1009 {
1010 GibberFileTransfer *ft;
1011 SalutConnection *conn = SALUT_CONNECTION (
1012 tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
1013 WockyPorter *porter = conn->porter;
1014
1015 ft = g_object_new (GIBBER_TYPE_OOB_FILE_TRANSFER,
1016 "self-id", conn->name,
1017 "peer-id", self->priv->contact->name,
1018 "filename", self->priv->filename,
1019 "porter", porter,
1020 "contact", self->priv->contact,
1021 "description", self->priv->description,
1022 "content-type", self->priv->content_type,
1023 NULL);
1024
1025 g_signal_connect (ft, "remote-accepted",
1026 G_CALLBACK (remote_accepted_cb), self);
1027 g_signal_connect (ft, "error", G_CALLBACK (error_cb), self);
1028 g_signal_connect (ft, "cancelled", G_CALLBACK (ft_remote_cancelled_cb), self);
1029
1030 self->priv->ft = ft;
1031
1032 g_signal_connect (ft, "transferred-chunk",
1033 G_CALLBACK (ft_transferred_chunk_cb), self);
1034
1035 gibber_file_transfer_set_size (ft, self->priv->size);
1036
1037 g_assert (ft->dataforms == NULL);
1038 ft->dataforms = add_metadata_forms (self, ft);
1039
1040 gibber_file_transfer_offer (ft);
1041 }
1042
1043 static void
salut_file_transfer_channel_set_state(TpSvcChannelTypeFileTransfer * iface,TpFileTransferState state,TpFileTransferStateChangeReason reason)1044 salut_file_transfer_channel_set_state (
1045 TpSvcChannelTypeFileTransfer *iface,
1046 TpFileTransferState state,
1047 TpFileTransferStateChangeReason reason)
1048 {
1049 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1050
1051 if (self->priv->state == state)
1052 return;
1053
1054 self->priv->state = state;
1055 tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface,
1056 state, reason);
1057 }
1058
1059 static void
emit_progress_update(SalutFileTransferChannel * self)1060 emit_progress_update (SalutFileTransferChannel *self)
1061 {
1062 TpSvcChannelTypeFileTransfer *iface = \
1063 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self);
1064
1065 g_get_current_time (&self->priv->last_transferred_bytes_emitted);
1066
1067 tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed (
1068 iface, self->priv->transferred_bytes);
1069
1070 if (self->priv->progress_timer != 0)
1071 {
1072 g_source_remove (self->priv->progress_timer);
1073 self->priv->progress_timer = 0;
1074 }
1075 }
1076
1077 static gboolean
emit_progress_update_cb(gpointer user_data)1078 emit_progress_update_cb (gpointer user_data)
1079 {
1080 SalutFileTransferChannel *self = \
1081 SALUT_FILE_TRANSFER_CHANNEL (user_data);
1082
1083 emit_progress_update (self);
1084
1085 return FALSE;
1086 }
1087
1088 static void
ft_transferred_chunk_cb(GibberFileTransfer * ft,guint64 count,SalutFileTransferChannel * self)1089 ft_transferred_chunk_cb (GibberFileTransfer *ft,
1090 guint64 count,
1091 SalutFileTransferChannel *self)
1092 {
1093 GTimeVal timeval;
1094 gint interval;
1095
1096 self->priv->transferred_bytes += count;
1097
1098 if (self->priv->transferred_bytes >= self->priv->size)
1099 {
1100 /* If the transfer has finished send an update right away */
1101 emit_progress_update (self);
1102 return;
1103 }
1104
1105 if (self->priv->progress_timer != 0)
1106 {
1107 /* A progress update signal is already scheduled */
1108 return;
1109 }
1110
1111 /* Only emit the TransferredBytes signal if it has been one second since its
1112 * last emission.
1113 */
1114 g_get_current_time (&timeval);
1115 interval = timeval.tv_sec -
1116 self->priv->last_transferred_bytes_emitted.tv_sec;
1117
1118 if (interval > 1)
1119 {
1120 /* At least more then a second apart, emit right away */
1121 emit_progress_update (self);
1122 return;
1123 }
1124
1125 /* Convert interval to milliseconds and calculate it more precisely */
1126 interval *= 1000;
1127
1128 interval += (timeval.tv_usec -
1129 self->priv->last_transferred_bytes_emitted.tv_usec)/1000;
1130
1131 /* Protect against clock skew, if the interval is negative the worst thing
1132 * that can happen is that we wait an extra second before emitting the signal
1133 */
1134 interval = ABS(interval);
1135
1136 if (interval > 1000)
1137 emit_progress_update (self);
1138 else
1139 self->priv->progress_timer = g_timeout_add (1000 - interval,
1140 emit_progress_update_cb, self);
1141 }
1142
1143 static gboolean
check_address_and_access_control(SalutFileTransferChannel * self,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param,GError ** error)1144 check_address_and_access_control (SalutFileTransferChannel *self,
1145 TpSocketAddressType address_type,
1146 TpSocketAccessControl access_control,
1147 const GValue *access_control_param,
1148 GError **error)
1149 {
1150 GArray *access_arr;
1151 guint i;
1152
1153 /* Do we support this AddressType? */
1154 access_arr = g_hash_table_lookup (self->priv->available_socket_types,
1155 GUINT_TO_POINTER (address_type));
1156 if (access_arr == NULL)
1157 {
1158 g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
1159 "AddressType %u is not implemented", address_type);
1160 return FALSE;
1161 }
1162
1163 /* Do we support this AccessControl? */
1164 for (i = 0; i < access_arr->len; i++)
1165 {
1166 TpSocketAccessControl control;
1167
1168 control = g_array_index (access_arr, TpSocketAccessControl, i);
1169 if (control == access_control)
1170 return TRUE;
1171 }
1172
1173 g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
1174 "AccesControl %u is not implemented with AddressType %u",
1175 access_control, address_type);
1176
1177 return FALSE;
1178 }
1179
1180 gboolean
salut_file_transfer_channel_offer_file(SalutFileTransferChannel * self,GError ** error)1181 salut_file_transfer_channel_offer_file (SalutFileTransferChannel *self,
1182 GError **error)
1183 {
1184 g_assert (!CHECK_STR_EMPTY (self->priv->filename));
1185 g_assert (self->priv->size != SALUT_UNDEFINED_FILE_SIZE);
1186
1187 DEBUG ("Offering file transfer");
1188
1189 send_file_offer (self);
1190
1191 return TRUE;
1192 }
1193
1194 /**
1195 * salut_file_transfer_channel_accept_file
1196 *
1197 * Implements D-Bus method AcceptFile
1198 * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1199 */
1200 static void
salut_file_transfer_channel_accept_file(TpSvcChannelTypeFileTransfer * iface,TpSocketAddressType address_type,guint access_control,const GValue * access_control_param,guint64 offset,DBusGMethodInvocation * context)1201 salut_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface,
1202 TpSocketAddressType address_type,
1203 guint access_control,
1204 const GValue *access_control_param,
1205 guint64 offset,
1206 DBusGMethodInvocation *context)
1207 {
1208 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1209 GError *error = NULL;
1210 GibberFileTransfer *ft;
1211 GValue *addr;
1212 GSocketAddress *socket_addr;
1213
1214 ft = self->priv->ft;
1215 if (ft == NULL)
1216 {
1217 dbus_g_method_return_error (context, error);
1218 g_error_free (error);
1219 }
1220
1221 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
1222 {
1223 g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1224 "State is not pending; cannot accept file");
1225 dbus_g_method_return_error (context, error);
1226 return;
1227 }
1228
1229 if (!check_address_and_access_control (self, address_type, access_control,
1230 access_control_param, &error))
1231 {
1232 dbus_g_method_return_error (context, error);
1233 g_error_free (error);
1234 return;
1235 }
1236
1237 g_signal_connect (ft, "finished", G_CALLBACK (ft_finished_cb), self);
1238 g_signal_connect (ft, "transferred-chunk",
1239 G_CALLBACK (ft_transferred_chunk_cb), self);
1240 g_signal_connect (ft, "cancelled", G_CALLBACK (ft_remote_cancelled_cb), self);
1241
1242 if (!setup_local_socket (self, address_type, access_control))
1243 {
1244 DEBUG ("Could not set up local socket");
1245 g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1246 "Could not set up local socket");
1247 dbus_g_method_return_error (context, error);
1248 }
1249
1250 salut_file_transfer_channel_set_state (iface,
1251 TP_FILE_TRANSFER_STATE_ACCEPTED,
1252 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1253
1254 socket_addr = g_socket_get_local_address (self->priv->socket, NULL);
1255 addr = tp_address_variant_from_g_socket_address (socket_addr, NULL, NULL);
1256 tp_svc_channel_type_file_transfer_return_from_accept_file (context,
1257 addr);
1258 tp_g_value_slice_free (addr);
1259 g_object_unref (socket_addr);
1260
1261 self->priv->initial_offset = 0;
1262
1263 tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
1264 self->priv->initial_offset);
1265
1266 salut_file_transfer_channel_set_state (iface, TP_FILE_TRANSFER_STATE_OPEN,
1267 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1268 }
1269
1270 /**
1271 * salut_file_transfer_channel_provide_file
1272 *
1273 * Implements D-Bus method ProvideFile
1274 * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1275 */
1276 static void
salut_file_transfer_channel_provide_file(TpSvcChannelTypeFileTransfer * iface,guint address_type,guint access_control,const GValue * access_control_param,DBusGMethodInvocation * context)1277 salut_file_transfer_channel_provide_file (
1278 TpSvcChannelTypeFileTransfer *iface,
1279 guint address_type,
1280 guint access_control,
1281 const GValue *access_control_param,
1282 DBusGMethodInvocation *context)
1283 {
1284 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1285 TpBaseChannel *base = TP_BASE_CHANNEL (self);
1286 GError *error = NULL;
1287 GValue *addr;
1288 GSocketAddress *socket_addr;
1289
1290 if (!tp_base_channel_is_requested (base))
1291 {
1292 g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1293 "Channel is not an outgoing transfer");
1294 dbus_g_method_return_error (context, error);
1295 return;
1296 }
1297
1298 if (self->priv->socket != NULL)
1299 {
1300 g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1301 "ProvideFile has already been called for this channel");
1302 dbus_g_method_return_error (context, error);
1303 return;
1304 }
1305
1306 if (!check_address_and_access_control (self, address_type, access_control,
1307 access_control_param, &error))
1308 {
1309 dbus_g_method_return_error (context, error);
1310 g_error_free (error);
1311 return;
1312 }
1313
1314 if (!setup_local_socket (self, address_type, access_control))
1315 {
1316 DEBUG ("Could not set up local socket");
1317 g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1318 "Could not set up local socket");
1319 dbus_g_method_return_error (context, error);
1320 }
1321
1322 if (self->priv->remote_accepted)
1323 {
1324 /* Remote already accepted the file. Channel is Open.
1325 * If not channel stay Pending. */
1326 tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
1327 self->priv->initial_offset);
1328
1329 salut_file_transfer_channel_set_state (iface,
1330 TP_FILE_TRANSFER_STATE_OPEN,
1331 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1332 }
1333
1334 socket_addr = g_socket_get_local_address (self->priv->socket, &error);
1335 addr = tp_address_variant_from_g_socket_address (socket_addr, NULL, NULL);
1336 tp_svc_channel_type_file_transfer_return_from_provide_file (context,
1337 addr);
1338 tp_g_value_slice_free (addr);
1339 g_object_unref (socket_addr);
1340 }
1341
1342 static void
file_transfer_iface_init(gpointer g_iface,gpointer iface_data)1343 file_transfer_iface_init (gpointer g_iface,
1344 gpointer iface_data)
1345 {
1346 TpSvcChannelTypeFileTransferClass *klass =
1347 (TpSvcChannelTypeFileTransferClass *) g_iface;
1348
1349 #define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\
1350 klass, salut_file_transfer_channel_##x)
1351 IMPLEMENT (accept_file);
1352 IMPLEMENT (provide_file);
1353 #undef IMPLEMENT
1354 }
1355
1356 #ifdef G_OS_UNIX
1357 static GSocketAddress *
get_local_unix_socket_address(SalutFileTransferChannel * self)1358 get_local_unix_socket_address (SalutFileTransferChannel *self)
1359 {
1360 GSocketAddress *addr = NULL;
1361 gchar *path = NULL;
1362 gint32 random_int;
1363 gchar *random_str;
1364 struct stat buf;
1365
1366 while (TRUE)
1367 {
1368 random_int = g_random_int_range (0, G_MAXINT32);
1369 random_str = g_strdup_printf ("tp-ft-%i", random_int);
1370 path = g_build_filename (g_get_tmp_dir (), random_str, NULL);
1371 g_free (random_str);
1372
1373 if (g_stat (path, &buf) != 0)
1374 break;
1375
1376 g_free (path);
1377 }
1378
1379 addr = g_unix_socket_address_new (path);
1380 g_free (path);
1381
1382 return addr;
1383 }
1384 #endif
1385
1386 static GSocketAddress *
get_local_tcp_socket_address(SalutFileTransferChannel * self,GSocketFamily family)1387 get_local_tcp_socket_address (SalutFileTransferChannel *self, GSocketFamily family)
1388 {
1389 GInetAddress *inet_address;
1390 GSocketAddress *addr;
1391 inet_address = g_inet_address_new_loopback (family);
1392 addr = g_inet_socket_address_new (inet_address, 0);
1393 g_object_unref (inet_address);
1394 return addr;
1395 }
1396
1397 /*
1398 * Return a GIOChannel for a local socket
1399 */
1400 static GIOChannel *
get_socket_channel(SalutFileTransferChannel * self,TpSocketAddressType address_type,guint access_control)1401 get_socket_channel (SalutFileTransferChannel *self,
1402 TpSocketAddressType address_type, guint access_control)
1403 {
1404 GSocket *sock;
1405 GSocketAddress *addr;
1406 GIOChannel *io_channel = NULL;
1407 GError *error = NULL;
1408 int fd;
1409
1410 switch (address_type)
1411 {
1412 #ifdef G_OS_UNIX
1413 case TP_SOCKET_ADDRESS_TYPE_UNIX:
1414 sock = g_socket_new (G_SOCKET_FAMILY_UNIX,
1415 G_SOCKET_TYPE_STREAM,
1416 G_SOCKET_PROTOCOL_DEFAULT,
1417 &error);
1418 addr = get_local_unix_socket_address (self);
1419 break;
1420 #endif
1421 case TP_SOCKET_ADDRESS_TYPE_IPV4:
1422 sock = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
1423 addr = get_local_tcp_socket_address (self, G_SOCKET_FAMILY_IPV4);
1424 break;
1425 case TP_SOCKET_ADDRESS_TYPE_IPV6:
1426 sock = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
1427 addr = get_local_tcp_socket_address (self, G_SOCKET_FAMILY_IPV6);
1428 break;
1429 default:
1430 return NULL;
1431 }
1432
1433 if (sock == NULL)
1434 {
1435 DEBUG ("Socket creation error: %s", error->message);
1436 g_error_free (error);
1437 return NULL;
1438 }
1439
1440 if (!g_socket_bind (sock, addr, FALSE, &error))
1441 {
1442 DEBUG ("Bind error: %s", error->message);
1443 g_error_free (error);
1444 g_object_unref (addr);
1445 g_object_unref (sock);
1446 return NULL;
1447 }
1448 g_object_unref (addr);
1449
1450 if (!g_socket_listen (sock, &error))
1451 {
1452 DEBUG ("Listen error: %s", error->message);
1453 g_error_free (error);
1454 g_object_unref (sock);
1455 return NULL;
1456 }
1457
1458 self->priv->socket = sock;
1459
1460 fd = g_socket_get_fd (sock);
1461 io_channel = g_io_channel_unix_new (fd);
1462 return io_channel;
1463 }
1464
1465 /*
1466 * Some client is connecting to the Unix socket.
1467 */
1468 static gboolean
accept_local_socket_connection(GIOChannel * source,GIOCondition condition,gpointer user_data)1469 accept_local_socket_connection (GIOChannel *source,
1470 GIOCondition condition,
1471 gpointer user_data)
1472 {
1473 SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (user_data);
1474 GibberFileTransfer *ft;
1475 int new_fd;
1476 GIOChannel *channel;
1477
1478 ft = self->priv->ft;
1479
1480 g_assert (ft != NULL);
1481
1482 if (condition & G_IO_IN)
1483 {
1484 DEBUG ("Client connected to local socket");
1485
1486 new_fd = accept (g_io_channel_unix_get_fd (source),
1487 NULL, NULL);
1488 if (new_fd < 0)
1489 {
1490 DEBUG ("accept() failed");
1491 return FALSE;
1492 }
1493 #ifdef G_OS_WIN32
1494 channel = g_io_channel_win32_new_fd (new_fd);
1495 #else
1496 channel = g_io_channel_unix_new (new_fd);
1497 #endif
1498 g_io_channel_set_close_on_unref (channel, TRUE);
1499 g_io_channel_set_encoding (channel, NULL, NULL);
1500 if (ft->direction == GIBBER_FILE_TRANSFER_DIRECTION_INCOMING)
1501 {
1502 gibber_file_transfer_receive (ft, channel);
1503 g_io_channel_unref (channel);
1504 }
1505 else
1506 {
1507 /* gibber_file_transfer_send needs ::remote-accepted to have
1508 * already been fired, so let's wait for that, keeping
1509 * around the GIOChannel, if it hasn't already happened. */
1510 if (self->priv->remote_accepted)
1511 {
1512 gibber_file_transfer_send (ft, channel);
1513 g_io_channel_unref (channel);
1514 }
1515 else
1516 {
1517 self->priv->channel = channel;
1518 }
1519 }
1520 }
1521
1522 return FALSE;
1523 }
1524
1525 static gboolean
setup_local_socket(SalutFileTransferChannel * self,TpSocketAddressType address_type,guint access_control)1526 setup_local_socket (SalutFileTransferChannel *self,
1527 TpSocketAddressType address_type,
1528 guint access_control)
1529 {
1530 GIOChannel *io_channel;
1531
1532 io_channel = get_socket_channel (self, address_type, access_control);
1533 if (io_channel == NULL)
1534 {
1535 return FALSE;
1536 }
1537
1538 g_io_add_watch (io_channel, G_IO_IN | G_IO_HUP,
1539 accept_local_socket_connection, self);
1540 g_io_channel_unref (io_channel);
1541
1542 return TRUE;
1543 }
1544
1545 static WockyDataForm *
find_data_form(GibberFileTransfer * ft,const gchar * form_type)1546 find_data_form (GibberFileTransfer *ft,
1547 const gchar *form_type)
1548 {
1549 GList *l;
1550
1551 for (l = ft->dataforms; l != NULL; l = l->next)
1552 {
1553 WockyDataForm *form = l->data;
1554 WockyDataFormField *field;
1555
1556 field = g_hash_table_lookup (form->fields, "FORM_TYPE");
1557
1558 if (field == NULL)
1559 {
1560 DEBUG ("Data form doesn't have FORM_TYPE field!");
1561 continue;
1562 }
1563
1564 /* found it! */
1565 if (!tp_strdiff (field->raw_value_contents[0], form_type))
1566 return form;
1567 }
1568
1569 return NULL;
1570 }
1571
1572 static gchar *
extract_service_name(GibberFileTransfer * ft)1573 extract_service_name (GibberFileTransfer *ft)
1574 {
1575 WockyDataForm *form = find_data_form (ft, NS_TP_FT_METADATA_SERVICE);
1576 WockyDataFormField *field;
1577 gchar *service_name = NULL;
1578
1579 if (form == NULL)
1580 return NULL;
1581
1582 field = g_hash_table_lookup (form->fields, "ServiceName");
1583
1584 if (field == NULL)
1585 {
1586 DEBUG ("ServiceName propery not present in data form; odd...");
1587 goto out;
1588 }
1589
1590 if (field->raw_value_contents == NULL
1591 || field->raw_value_contents[0] == NULL)
1592 {
1593 DEBUG ("ServiceName property doesn't have a real value; odd...");
1594 }
1595 else
1596 {
1597 service_name = g_strdup (field->raw_value_contents[0]);
1598 }
1599
1600 out:
1601 return service_name;
1602 }
1603
1604 static GHashTable *
extract_metadata(GibberFileTransfer * ft)1605 extract_metadata (GibberFileTransfer *ft)
1606 {
1607 WockyDataForm *form = find_data_form (ft, NS_TP_FT_METADATA);
1608 GHashTable *metadata;
1609 GHashTableIter iter;
1610 gpointer key, value;
1611
1612 if (form == NULL)
1613 return NULL;
1614
1615 metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
1616 g_free, (GDestroyNotify) g_strfreev);
1617
1618 g_hash_table_iter_init (&iter, form->fields);
1619 while (g_hash_table_iter_next (&iter, &key, &value))
1620 {
1621 const gchar *var = key;
1622 WockyDataFormField *field = value;
1623
1624 if (!tp_strdiff (var, "FORM_TYPE"))
1625 continue;
1626
1627 g_hash_table_insert (metadata,
1628 g_strdup (var),
1629 g_strdupv (field->raw_value_contents));
1630 }
1631
1632 return metadata;
1633 }
1634
1635 SalutFileTransferChannel *
salut_file_transfer_channel_new(SalutConnection * conn,SalutContact * contact,TpHandle handle,TpHandle initiator_handle,TpFileTransferState state,const gchar * content_type,const gchar * filename,guint64 size,TpFileHashType content_hash_type,const gchar * content_hash,const gchar * description,guint64 date,guint64 initial_offset,const gchar * file_uri,const gchar * service_name,const GHashTable * metadata)1636 salut_file_transfer_channel_new (SalutConnection *conn,
1637 SalutContact *contact,
1638 TpHandle handle,
1639 TpHandle initiator_handle,
1640 TpFileTransferState state,
1641 const gchar *content_type,
1642 const gchar *filename,
1643 guint64 size,
1644 TpFileHashType content_hash_type,
1645 const gchar *content_hash,
1646 const gchar *description,
1647 guint64 date,
1648 guint64 initial_offset,
1649 const gchar *file_uri,
1650 const gchar *service_name,
1651 const GHashTable *metadata)
1652 {
1653 return g_object_new (SALUT_TYPE_FILE_TRANSFER_CHANNEL,
1654 "connection", conn,
1655 "contact", contact,
1656 "handle", handle,
1657 "initiator-handle", initiator_handle,
1658 "requested", TRUE,
1659 "state", state,
1660 "content-type", content_type,
1661 "filename", filename,
1662 "size", size,
1663 "content-hash-type", content_hash_type,
1664 "content-hash", content_hash,
1665 "description", description,
1666 "date", date,
1667 "initial-offset", initial_offset,
1668 "uri", file_uri,
1669 "service-name", service_name,
1670 "metadata", metadata,
1671 NULL);
1672 }
1673
1674 SalutFileTransferChannel *
salut_file_transfer_channel_new_from_stanza(SalutConnection * connection,SalutContact * contact,TpHandle handle,TpFileTransferState state,WockyStanza * stanza)1675 salut_file_transfer_channel_new_from_stanza (SalutConnection *connection,
1676 SalutContact *contact,
1677 TpHandle handle,
1678 TpFileTransferState state,
1679 WockyStanza *stanza)
1680 {
1681 GError *error = NULL;
1682 GibberFileTransfer *ft;
1683 SalutFileTransferChannel *chan;
1684 gchar *service_name;
1685 GHashTable *metadata;
1686
1687 ft = gibber_file_transfer_new_from_stanza_with_from (stanza, connection->porter,
1688 WOCKY_CONTACT (contact), contact->name, &error);
1689
1690 if (ft == NULL)
1691 {
1692 /* Reply with an error */
1693 WockyStanza *reply;
1694 GError err = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST,
1695 "failed to parse file offer" };
1696
1697 DEBUG ("%s", error->message);
1698 reply = wocky_stanza_build_iq_error (stanza, NULL);
1699 wocky_stanza_error_to_node (&err, wocky_stanza_get_top_node (reply));
1700
1701 wocky_porter_send (connection->porter, reply);
1702
1703 g_object_unref (reply);
1704 g_clear_error (&error);
1705 return NULL;
1706 }
1707
1708 /* Metadata */
1709 service_name = extract_service_name (ft);
1710 metadata = extract_metadata (ft);
1711
1712 DEBUG ("Received file offer with id '%s'", ft->id);
1713
1714 chan = g_object_new (SALUT_TYPE_FILE_TRANSFER_CHANNEL,
1715 "connection", connection,
1716 "contact", contact,
1717 "handle", handle,
1718 "initiator-handle", handle,
1719 "requested", FALSE,
1720 "state", state,
1721 "filename", ft->filename,
1722 "size", gibber_file_transfer_get_size (ft),
1723 "description", ft->description,
1724 "content-type", ft->content_type,
1725 "service-name", service_name,
1726 "metadata", metadata,
1727 NULL);
1728
1729 g_free (service_name);
1730 if (metadata != NULL)
1731 g_hash_table_unref (metadata);
1732
1733 chan->priv->ft = ft;
1734
1735 g_signal_connect (ft, "error", G_CALLBACK (error_cb), chan);
1736
1737 return chan;
1738 }
1739