~ubuntu-branches/ubuntu/natty/gvfs/natty

« back to all changes in this revision

Viewing changes to .pc/debian-changes-1.7.3-0ubuntu1/daemon/gvfsftpconnection.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-03-22 11:11:57 UTC
  • Revision ID: james.westby@ubuntu.com-20110322111157-tn8edztqci0xr9hk
Correctly update using merge-upstream otherwise the diff will be reverted
in the diff.gz.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* GIO - GLib Input, Output and Streaming Library
2
 
 *
3
 
 * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
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 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
16
 
 * Public License along with this library; if not, write to the
17
 
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18
 
 * Boston, MA 02111-1307, USA.
19
 
 *
20
 
 * Author: Benjamin Otte <otte@gnome.org>
21
 
 */
22
 
 
23
 
#include <config.h>
24
 
 
25
 
#include "gvfsftpconnection.h"
26
 
 
27
 
#include <string.h>
28
 
#include <glib/gi18n.h>
29
 
 
30
 
#include "gvfsbackendftp.h"
31
 
 
32
 
/* used for identifying the connection during debugging */
33
 
static volatile int debug_id = 0;
34
 
 
35
 
struct _GVfsFtpConnection
36
 
{
37
 
  GSocketClient *       client;                 /* socket client used for opening connections */
38
 
 
39
 
  GIOStream *           commands;               /* ftp command stream */
40
 
  GDataInputStream *    commands_in;            /* wrapper around in stream to allow line-wise reading */
41
 
  gboolean              waiting_for_reply;           /* TRUE if a command was sent but no reply received yet */
42
 
 
43
 
  GSocket *             listen_socket;          /* socket we are listening on for active FTP connections */
44
 
  GIOStream *           data;                   /* ftp data stream or NULL if not in use */
45
 
 
46
 
  int                   debug_id;               /* unique id for debugging purposes */
47
 
};
48
 
 
49
 
static void
50
 
enable_keepalive (GSocketConnection *conn)
51
 
{
52
 
  /* We enable keepalive on the socket, because data connections can be
53
 
   * idle for a long time while data is transferred using the data
54
 
   * connection. And there are still buggy routers in existance that purge
55
 
   * idle connections from time to time.
56
 
   * To work around this problem, we set the keep alive flag here. It's the
57
 
   * user's responsibility to configure his kernel properly so that the
58
 
   * keepalive packets are sent before the buggy router disconnects the
59
 
   * TCP connection. If a user asks, a howto is at
60
 
   * http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
61
 
   */
62
 
  g_socket_set_keepalive (g_socket_connection_get_socket (conn), TRUE);
63
 
}
64
 
 
65
 
GVfsFtpConnection *
66
 
g_vfs_ftp_connection_new (GSocketConnectable *addr,
67
 
                          GCancellable *      cancellable,
68
 
                          GError **           error)
69
 
{
70
 
  GVfsFtpConnection *conn;
71
 
 
72
 
  g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (addr), NULL);
73
 
 
74
 
  conn = g_slice_new0 (GVfsFtpConnection);
75
 
  conn->client = g_socket_client_new ();
76
 
  conn->debug_id = g_atomic_int_exchange_and_add (&debug_id, 1);
77
 
  conn->commands = G_IO_STREAM (g_socket_client_connect (conn->client,
78
 
                                                         addr,
79
 
                                                         cancellable,
80
 
                                                         error));
81
 
  if (conn->commands == NULL)
82
 
    {
83
 
      g_object_unref (conn->client);
84
 
      g_slice_free (GVfsFtpConnection, conn);
85
 
      return NULL;
86
 
    }
87
 
 
88
 
  enable_keepalive (G_SOCKET_CONNECTION (conn->commands));
89
 
  conn->commands_in = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (conn->commands)));
90
 
  g_data_input_stream_set_newline_type (conn->commands_in, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
91
 
  /* The first thing that needs to happen is receiving the welcome message */
92
 
  conn->waiting_for_reply = TRUE;
93
 
 
94
 
  return conn;
95
 
}
96
 
 
97
 
static void
98
 
g_vfs_ftp_connection_stop_listening (GVfsFtpConnection *conn)
99
 
{
100
 
  if (conn->listen_socket)
101
 
    {
102
 
      g_object_unref (conn->listen_socket);
103
 
      conn->listen_socket = NULL;
104
 
    }
105
 
}
106
 
 
107
 
void
108
 
g_vfs_ftp_connection_free (GVfsFtpConnection *conn)
109
 
{
110
 
  g_return_if_fail (conn != NULL);
111
 
 
112
 
  g_vfs_ftp_connection_stop_listening (conn);
113
 
  g_vfs_ftp_connection_close_data_connection (conn);
114
 
 
115
 
  g_object_unref (conn->commands_in);
116
 
  g_object_unref (conn->commands);
117
 
  g_object_unref (conn->client);
118
 
  g_slice_free (GVfsFtpConnection, conn);
119
 
}
120
 
 
121
 
gboolean
122
 
g_vfs_ftp_connection_send (GVfsFtpConnection *conn,
123
 
                           const char *       command,
124
 
                           int                len,
125
 
                           GCancellable *     cancellable,
126
 
                           GError **          error)
127
 
{
128
 
  g_return_val_if_fail (conn != NULL, FALSE);
129
 
  g_return_val_if_fail (!conn->waiting_for_reply, FALSE);
130
 
  g_return_val_if_fail (command != NULL, FALSE);
131
 
  g_return_val_if_fail (len >= -1, FALSE);
132
 
  if (len < 0)
133
 
    len = strlen (command);
134
 
  g_return_val_if_fail (command[len-2] == '\r' && command[len-1] == '\n', FALSE);
135
 
 
136
 
  if (g_str_has_prefix (command, "PASS"))
137
 
    g_debug ("--%2d ->  PASS ***\r\n", conn->debug_id);
138
 
  else
139
 
    g_debug ("--%2d ->  %s", conn->debug_id, command);
140
 
 
141
 
  conn->waiting_for_reply = TRUE;
142
 
  return g_output_stream_write_all (g_io_stream_get_output_stream (conn->commands),
143
 
                                    command,
144
 
                                    len,
145
 
                                    NULL,
146
 
                                    cancellable,
147
 
                                    error);
148
 
}
149
 
 
150
 
guint
151
 
g_vfs_ftp_connection_receive (GVfsFtpConnection *conn,
152
 
                              char ***           reply,
153
 
                              GCancellable *     cancellable,
154
 
                              GError **          error)
155
 
{
156
 
  char *line;
157
 
  enum {
158
 
    FIRST_LINE,
159
 
    MULTILINE,
160
 
    DONE
161
 
  } reply_state = FIRST_LINE;
162
 
  GPtrArray *lines;
163
 
  guint response = 0;
164
 
 
165
 
  g_return_val_if_fail (conn != NULL, 0);
166
 
  g_return_val_if_fail (conn->waiting_for_reply, 0);
167
 
 
168
 
  if (reply)
169
 
    lines = g_ptr_array_new_with_free_func (g_free);
170
 
  else
171
 
    lines = NULL;
172
 
 
173
 
  while (reply_state != DONE)
174
 
    {
175
 
      line = g_data_input_stream_read_line (conn->commands_in, NULL, cancellable, error);
176
 
      if (line == NULL)
177
 
        {
178
 
          if (error && *error == NULL)
179
 
            g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
180
 
                                 _("Unexpected end of stream"));
181
 
 
182
 
          goto fail;
183
 
        }
184
 
 
185
 
      g_debug ("<-%2d --  %s\r\n", conn->debug_id, line);
186
 
      if (lines)
187
 
        g_ptr_array_add (lines, line);
188
 
 
189
 
      if (reply_state == FIRST_LINE)
190
 
        {
191
 
          if (line[0] <= '0' || line[0] > '5' ||
192
 
              line[1] < '0' || line[1] > '9' ||
193
 
              line[2] < '0' || line[2] > '9')
194
 
            {
195
 
              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
196
 
                                   _("Invalid reply"));
197
 
              goto fail;
198
 
            }
199
 
          response = 100 * (line[0] - '0') +
200
 
                      10 * (line[1] - '0') +
201
 
                           (line[2] - '0');
202
 
          if (line[3] == ' ')
203
 
            reply_state = DONE;
204
 
          else if (line[3] == '-')
205
 
            reply_state = MULTILINE;
206
 
          else
207
 
            {
208
 
              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
209
 
                                   _("Invalid reply"));
210
 
              goto fail;
211
 
            }
212
 
        }
213
 
      else
214
 
        {
215
 
          if (line[0] - '0' == response / 100 &&
216
 
              line[1] - '0' == (response / 10) % 10 &&
217
 
              line[2] - '0' == response % 10 &&
218
 
              line[3] == ' ')
219
 
            reply_state = DONE;
220
 
        }
221
 
      if (!lines)
222
 
        g_free (line);
223
 
    }
224
 
 
225
 
  if (lines)
226
 
    {
227
 
      g_ptr_array_add (lines, NULL);
228
 
      *reply = (char **) g_ptr_array_free (lines, FALSE);
229
 
    }
230
 
 
231
 
  /* 1xx commands are intermediate commands and require a further
232
 
   * message from the server to complete
233
 
   */
234
 
  if (response >= 200)
235
 
    conn->waiting_for_reply = FALSE;
236
 
 
237
 
  return response;
238
 
 
239
 
fail:
240
 
  if (lines)
241
 
    g_ptr_array_free (lines, TRUE);
242
 
  else
243
 
    g_free (line);
244
 
  return 0;
245
 
}
246
 
 
247
 
GSocketAddress *
248
 
g_vfs_ftp_connection_get_address (GVfsFtpConnection *conn, GError **error)
249
 
{
250
 
  g_return_val_if_fail (conn != NULL, NULL);
251
 
 
252
 
  return g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (conn->commands), error);
253
 
}
254
 
 
255
 
gboolean
256
 
g_vfs_ftp_connection_open_data_connection (GVfsFtpConnection *conn,
257
 
                                           GSocketAddress *   addr,
258
 
                                           GCancellable *     cancellable,
259
 
                                           GError **          error)
260
 
{
261
 
  g_return_val_if_fail (conn != NULL, FALSE);
262
 
  g_return_val_if_fail (conn->data == NULL, FALSE);
263
 
 
264
 
  g_vfs_ftp_connection_stop_listening (conn);
265
 
 
266
 
  conn->data = G_IO_STREAM (g_socket_client_connect (conn->client,
267
 
                                                     G_SOCKET_CONNECTABLE (addr),
268
 
                                                     cancellable,
269
 
                                                     error));
270
 
 
271
 
  return conn->data != NULL;
272
 
}
273
 
 
274
 
/**
275
 
 * g_vfs_ftp_connection_listen_data_connection:
276
 
 * @conn: a connection
277
 
 * @error: %NULL or location to take potential errors
278
 
 *
279
 
 * Initiates a listening socket that the FTP server can connect to. To accept 
280
 
 * connections and initialize data transfers, use 
281
 
 * g_vfs_ftp_connection_accept_data_connection().
282
 
 * This function supports what is known as "active FTP", while
283
 
 * g_vfs_ftp_connection_open_data_connection() is to be used for "passive FTP".
284
 
 *
285
 
 * Returns: the actual address the socket is listening on or %NULL on error
286
 
 **/
287
 
GSocketAddress *
288
 
g_vfs_ftp_connection_listen_data_connection (GVfsFtpConnection *conn,
289
 
                                             GError **          error)
290
 
{
291
 
  GSocketAddress *local, *addr;
292
 
 
293
 
  g_return_val_if_fail (conn != NULL, NULL);
294
 
  g_return_val_if_fail (conn->data == NULL, NULL);
295
 
 
296
 
  g_vfs_ftp_connection_stop_listening (conn);
297
 
 
298
 
  local = g_socket_connection_get_local_address (G_SOCKET_CONNECTION (conn->commands), error);
299
 
  if (local == NULL)
300
 
    return NULL;
301
 
 
302
 
  conn->listen_socket = g_socket_new (g_socket_address_get_family (local),
303
 
                                      G_SOCKET_TYPE_STREAM,
304
 
                                      G_SOCKET_PROTOCOL_TCP,
305
 
                                      error);
306
 
  if (conn->listen_socket == NULL)
307
 
    return NULL;
308
 
 
309
 
  g_assert (G_IS_INET_SOCKET_ADDRESS (local));
310
 
  addr = g_inet_socket_address_new (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (local)), 0);
311
 
  g_object_unref (local);
312
 
 
313
 
  if (!g_socket_bind (conn->listen_socket, addr, TRUE, error) ||
314
 
      !g_socket_listen (conn->listen_socket, error) ||
315
 
      !(local = g_socket_get_local_address (conn->listen_socket, error)))
316
 
    {
317
 
      g_object_unref (addr);
318
 
      g_vfs_ftp_connection_stop_listening (conn);
319
 
      return NULL;
320
 
    }
321
 
 
322
 
  g_object_unref (addr);
323
 
  return local;
324
 
}
325
 
 
326
 
static void
327
 
cancel_timer_cb (GCancellable *orig, GCancellable *to_cancel)
328
 
{
329
 
  g_cancellable_cancel (to_cancel);
330
 
}
331
 
 
332
 
static gboolean
333
 
cancel_cancellable (gpointer cancellable)
334
 
{
335
 
  g_cancellable_cancel (cancellable);
336
 
  return FALSE;
337
 
}
338
 
 
339
 
/**
340
 
 * g_vfs_ftp_connection_accept_data_connection:
341
 
 * @conn: a listening connection
342
 
 * @cancellable: cancellable to interrupt wait
343
 
 * @error: %NULL or location to take a potential error
344
 
 *
345
 
 * Opens a data connection for @conn by accepting an incoming connection on the
346
 
 * address it is listening on via g_vfs_ftp_connection_listen_data_connection(),
347
 
 * which must have been called prior to this function.
348
 
 * If this function succeeds, a data connection will have been opened, and calls
349
 
 * to g_vfs_ftp_connection_get_data_stream() will work.
350
 
 *
351
 
 * Returns: %TRUE if a connection was successfully acquired
352
 
 **/
353
 
gboolean
354
 
g_vfs_ftp_connection_accept_data_connection (GVfsFtpConnection *conn,
355
 
                                             GCancellable *     cancellable,
356
 
                                             GError **          error)
357
 
{
358
 
  GSocket *accepted;
359
 
  GCancellable *timer;
360
 
  gulong cancel_cb_id;
361
 
  GIOCondition condition;
362
 
 
363
 
  g_return_val_if_fail (conn != NULL, FALSE);
364
 
  g_return_val_if_fail (conn->data == NULL, FALSE);
365
 
  g_return_val_if_fail (G_IS_SOCKET (conn->listen_socket), FALSE);
366
 
 
367
 
  timer = g_cancellable_new ();
368
 
  cancel_cb_id = g_cancellable_connect (cancellable, 
369
 
                                        G_CALLBACK (cancel_timer_cb),
370
 
                                        timer,
371
 
                                        NULL);
372
 
  g_object_ref (timer);
373
 
  g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
374
 
                              G_VFS_FTP_TIMEOUT_IN_SECONDS,
375
 
                              cancel_cancellable,
376
 
                              timer,
377
 
                              g_object_unref);
378
 
 
379
 
  condition = g_socket_condition_wait (conn->listen_socket, G_IO_IN, timer, error);
380
 
 
381
 
  g_cancellable_disconnect (cancellable, cancel_cb_id);
382
 
  g_object_unref (timer);
383
 
 
384
 
  if ((condition & G_IO_IN) == 0)
385
 
    {
386
 
      if (g_cancellable_is_cancelled (timer) &&
387
 
          !g_cancellable_is_cancelled (cancellable))
388
 
        {
389
 
          g_clear_error (error);
390
 
          g_set_error_literal (error, 
391
 
                               G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
392
 
                               _("Failed to create active FTP connection. "
393
 
                                 "Maybe your router does not support this?"));
394
 
        }
395
 
      else if (error && *error == NULL)
396
 
        {
397
 
          g_set_error_literal (error, 
398
 
                               G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
399
 
                               _("Failed to create active FTP connection."));
400
 
        }
401
 
      return FALSE;
402
 
    }
403
 
 
404
 
  accepted = g_socket_accept (conn->listen_socket, cancellable, error);
405
 
  if (accepted == NULL)
406
 
    return FALSE;
407
 
 
408
 
  conn->data = G_IO_STREAM (g_socket_connection_factory_create_connection (accepted));
409
 
  g_object_unref (accepted);
410
 
  return TRUE;
411
 
}
412
 
 
413
 
void
414
 
g_vfs_ftp_connection_close_data_connection (GVfsFtpConnection *conn)
415
 
{
416
 
  g_return_if_fail (conn != NULL);
417
 
 
418
 
  if (conn->data == NULL)
419
 
    return;
420
 
 
421
 
  g_object_unref (conn->data);
422
 
  conn->data = NULL;
423
 
}
424
 
 
425
 
/**
426
 
 * g_vfs_ftp_connection_get_debug_id:
427
 
 * @conn: the connection
428
 
 *
429
 
 * Gets an ID that uniquely identifies this connection. Intended for use in 
430
 
 * debug print statements.
431
 
 *
432
 
 * Returns: the ID to use for referring to this connection in debug messages
433
 
 **/
434
 
guint
435
 
g_vfs_ftp_connection_get_debug_id (GVfsFtpConnection *conn)
436
 
{
437
 
  g_return_val_if_fail (conn != NULL, 0);
438
 
 
439
 
  return conn->debug_id;
440
 
}
441
 
 
442
 
/**
443
 
 * g_vfs_ftp_connection_get_data_stream:
444
 
 * @conn: a connection
445
 
 *
446
 
 * Gets the data stream in use by @conn. It is an error to call this function
447
 
 * when no data stream exists. Be sure to check the return value of
448
 
 * g_vfs_ftp_connection_open_data_connection().
449
 
 *
450
 
 * Returns: the data stream of @conn
451
 
 **/
452
 
GIOStream *
453
 
g_vfs_ftp_connection_get_data_stream (GVfsFtpConnection *conn)
454
 
{
455
 
  g_return_val_if_fail (conn != NULL, NULL);
456
 
  g_return_val_if_fail (conn->data != NULL, NULL);
457
 
 
458
 
  return conn->data;
459
 
}
460
 
 
461
 
/**
462
 
 * g_vfs_ftp_connection_is_usable:
463
 
 * @conn: a connection
464
 
 *
465
 
 * Checks if this connection can be used to send new commands. For
466
 
 * example, if the connection is in the process of a command sequence or
467
 
 * if the connection was closed, this is not possible and this function 
468
 
 * will return %FALSE.
469
 
 *
470
 
 * Returns: %TRUE if the connection is still usable
471
 
 **/
472
 
gboolean
473
 
g_vfs_ftp_connection_is_usable (GVfsFtpConnection *conn)
474
 
{
475
 
  GIOCondition cond;
476
 
 
477
 
  g_return_val_if_fail (conn != NULL, FALSE);
478
 
 
479
 
  if (conn->waiting_for_reply)
480
 
    return FALSE;
481
 
 
482
 
  cond = G_IO_ERR | G_IO_HUP;
483
 
  cond = g_socket_condition_check (g_socket_connection_get_socket (G_SOCKET_CONNECTION (conn->commands)), cond);
484
 
  if (cond)
485
 
    return FALSE;
486
 
 
487
 
  return TRUE;
488
 
}
489