1
/* GIO - GLib Input, Output and Streaming Library
3
* Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
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.
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.
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.
20
* Author: Benjamin Otte <otte@gnome.org>
25
#include "gvfsftpconnection.h"
28
#include <glib/gi18n.h>
30
#include "gvfsbackendftp.h"
32
/* used for identifying the connection during debugging */
33
static volatile int debug_id = 0;
35
struct _GVfsFtpConnection
37
GSocketClient * client; /* socket client used for opening connections */
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 */
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 */
46
int debug_id; /* unique id for debugging purposes */
50
enable_keepalive (GSocketConnection *conn)
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/
62
g_socket_set_keepalive (g_socket_connection_get_socket (conn), TRUE);
66
g_vfs_ftp_connection_new (GSocketConnectable *addr,
67
GCancellable * cancellable,
70
GVfsFtpConnection *conn;
72
g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (addr), NULL);
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,
81
if (conn->commands == NULL)
83
g_object_unref (conn->client);
84
g_slice_free (GVfsFtpConnection, conn);
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;
98
g_vfs_ftp_connection_stop_listening (GVfsFtpConnection *conn)
100
if (conn->listen_socket)
102
g_object_unref (conn->listen_socket);
103
conn->listen_socket = NULL;
108
g_vfs_ftp_connection_free (GVfsFtpConnection *conn)
110
g_return_if_fail (conn != NULL);
112
g_vfs_ftp_connection_stop_listening (conn);
113
g_vfs_ftp_connection_close_data_connection (conn);
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);
122
g_vfs_ftp_connection_send (GVfsFtpConnection *conn,
123
const char * command,
125
GCancellable * cancellable,
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);
133
len = strlen (command);
134
g_return_val_if_fail (command[len-2] == '\r' && command[len-1] == '\n', FALSE);
136
if (g_str_has_prefix (command, "PASS"))
137
g_debug ("--%2d -> PASS ***\r\n", conn->debug_id);
139
g_debug ("--%2d -> %s", conn->debug_id, command);
141
conn->waiting_for_reply = TRUE;
142
return g_output_stream_write_all (g_io_stream_get_output_stream (conn->commands),
151
g_vfs_ftp_connection_receive (GVfsFtpConnection *conn,
153
GCancellable * cancellable,
161
} reply_state = FIRST_LINE;
165
g_return_val_if_fail (conn != NULL, 0);
166
g_return_val_if_fail (conn->waiting_for_reply, 0);
169
lines = g_ptr_array_new_with_free_func (g_free);
173
while (reply_state != DONE)
175
line = g_data_input_stream_read_line (conn->commands_in, NULL, cancellable, error);
178
if (error && *error == NULL)
179
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
180
_("Unexpected end of stream"));
185
g_debug ("<-%2d -- %s\r\n", conn->debug_id, line);
187
g_ptr_array_add (lines, line);
189
if (reply_state == FIRST_LINE)
191
if (line[0] <= '0' || line[0] > '5' ||
192
line[1] < '0' || line[1] > '9' ||
193
line[2] < '0' || line[2] > '9')
195
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
199
response = 100 * (line[0] - '0') +
200
10 * (line[1] - '0') +
204
else if (line[3] == '-')
205
reply_state = MULTILINE;
208
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
215
if (line[0] - '0' == response / 100 &&
216
line[1] - '0' == (response / 10) % 10 &&
217
line[2] - '0' == response % 10 &&
227
g_ptr_array_add (lines, NULL);
228
*reply = (char **) g_ptr_array_free (lines, FALSE);
231
/* 1xx commands are intermediate commands and require a further
232
* message from the server to complete
235
conn->waiting_for_reply = FALSE;
241
g_ptr_array_free (lines, TRUE);
248
g_vfs_ftp_connection_get_address (GVfsFtpConnection *conn, GError **error)
250
g_return_val_if_fail (conn != NULL, NULL);
252
return g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (conn->commands), error);
256
g_vfs_ftp_connection_open_data_connection (GVfsFtpConnection *conn,
257
GSocketAddress * addr,
258
GCancellable * cancellable,
261
g_return_val_if_fail (conn != NULL, FALSE);
262
g_return_val_if_fail (conn->data == NULL, FALSE);
264
g_vfs_ftp_connection_stop_listening (conn);
266
conn->data = G_IO_STREAM (g_socket_client_connect (conn->client,
267
G_SOCKET_CONNECTABLE (addr),
271
return conn->data != NULL;
275
* g_vfs_ftp_connection_listen_data_connection:
276
* @conn: a connection
277
* @error: %NULL or location to take potential errors
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".
285
* Returns: the actual address the socket is listening on or %NULL on error
288
g_vfs_ftp_connection_listen_data_connection (GVfsFtpConnection *conn,
291
GSocketAddress *local, *addr;
293
g_return_val_if_fail (conn != NULL, NULL);
294
g_return_val_if_fail (conn->data == NULL, NULL);
296
g_vfs_ftp_connection_stop_listening (conn);
298
local = g_socket_connection_get_local_address (G_SOCKET_CONNECTION (conn->commands), error);
302
conn->listen_socket = g_socket_new (g_socket_address_get_family (local),
303
G_SOCKET_TYPE_STREAM,
304
G_SOCKET_PROTOCOL_TCP,
306
if (conn->listen_socket == NULL)
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);
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)))
317
g_object_unref (addr);
318
g_vfs_ftp_connection_stop_listening (conn);
322
g_object_unref (addr);
327
cancel_timer_cb (GCancellable *orig, GCancellable *to_cancel)
329
g_cancellable_cancel (to_cancel);
333
cancel_cancellable (gpointer cancellable)
335
g_cancellable_cancel (cancellable);
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
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.
351
* Returns: %TRUE if a connection was successfully acquired
354
g_vfs_ftp_connection_accept_data_connection (GVfsFtpConnection *conn,
355
GCancellable * cancellable,
361
GIOCondition condition;
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);
367
timer = g_cancellable_new ();
368
cancel_cb_id = g_cancellable_connect (cancellable,
369
G_CALLBACK (cancel_timer_cb),
372
g_object_ref (timer);
373
g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
374
G_VFS_FTP_TIMEOUT_IN_SECONDS,
379
condition = g_socket_condition_wait (conn->listen_socket, G_IO_IN, timer, error);
381
g_cancellable_disconnect (cancellable, cancel_cb_id);
382
g_object_unref (timer);
384
if ((condition & G_IO_IN) == 0)
386
if (g_cancellable_is_cancelled (timer) &&
387
!g_cancellable_is_cancelled (cancellable))
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?"));
395
else if (error && *error == NULL)
397
g_set_error_literal (error,
398
G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
399
_("Failed to create active FTP connection."));
404
accepted = g_socket_accept (conn->listen_socket, cancellable, error);
405
if (accepted == NULL)
408
conn->data = G_IO_STREAM (g_socket_connection_factory_create_connection (accepted));
409
g_object_unref (accepted);
414
g_vfs_ftp_connection_close_data_connection (GVfsFtpConnection *conn)
416
g_return_if_fail (conn != NULL);
418
if (conn->data == NULL)
421
g_object_unref (conn->data);
426
* g_vfs_ftp_connection_get_debug_id:
427
* @conn: the connection
429
* Gets an ID that uniquely identifies this connection. Intended for use in
430
* debug print statements.
432
* Returns: the ID to use for referring to this connection in debug messages
435
g_vfs_ftp_connection_get_debug_id (GVfsFtpConnection *conn)
437
g_return_val_if_fail (conn != NULL, 0);
439
return conn->debug_id;
443
* g_vfs_ftp_connection_get_data_stream:
444
* @conn: a connection
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().
450
* Returns: the data stream of @conn
453
g_vfs_ftp_connection_get_data_stream (GVfsFtpConnection *conn)
455
g_return_val_if_fail (conn != NULL, NULL);
456
g_return_val_if_fail (conn->data != NULL, NULL);
462
* g_vfs_ftp_connection_is_usable:
463
* @conn: a connection
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.
470
* Returns: %TRUE if the connection is still usable
473
g_vfs_ftp_connection_is_usable (GVfsFtpConnection *conn)
477
g_return_val_if_fail (conn != NULL, FALSE);
479
if (conn->waiting_for_reply)
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);