1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
3 /* ftp-method.c - VFS modules for FTP
5 Copyright (C) 2000 Ian McKellar, Eazel Inc.
7 The Gnome Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 The Gnome Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with the Gnome Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
22 Author: Ian McKellar <yakk@yakk.net> */
24 /* see RFC 959 for protocol details */
28 /* Keep <sys/types.h> above any network includes for FreeBSD. */
29 #include <sys/types.h>
31 /* Keep <netinet/in.h> above <arpa/inet.h> for FreeBSD. */
32 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <libgnomevfs/gnome-vfs-context.h>
36 #include <libgnomevfs/gnome-vfs-inet-connection.h>
37 #include <libgnomevfs/gnome-vfs-socket-buffer.h>
38 #include <libgnomevfs/gnome-vfs-method.h>
39 #include <libgnomevfs/gnome-vfs-mime.h>
40 #include <libgnomevfs/gnome-vfs-mime-utils.h>
41 #include <libgnomevfs/gnome-vfs-module.h>
42 #include <libgnomevfs/gnome-vfs-module-shared.h>
43 #include <libgnomevfs/gnome-vfs-parse-ls.h>
44 #include <libgnomevfs/gnome-vfs-utils.h>
45 #include <stdio.h> /* for sscanf */
46 #include <stdlib.h> /* for atoi */
53 /* maximum size of response we're expecting to get */
54 #define MAX_RESPONSE_SIZE 4096
56 /* macros for the checking of FTP response codes */
57 #define IS_100(X) ((X) >= 100 && (X) < 200)
58 #define IS_200(X) ((X) >= 200 && (X) < 300)
59 #define IS_300(X) ((X) >= 300 && (X) < 400)
60 #define IS_400(X) ((X) >= 400 && (X) < 500)
61 #define IS_500(X) ((X) >= 500 && (X) < 600)
64 GnomeVFSMethodHandle method_handle;
65 GnomeVFSInetConnection *inet_connection;
66 GnomeVFSSocketBuffer *socket_buf;
69 GString *response_buffer;
70 gchar *response_message;
72 GnomeVFSInetConnection *data_connection;
73 GnomeVFSSocketBuffer *data_socketbuf;
82 gchar *server_type; /* the response from TYPE */
84 GnomeVFSResult fivefifty; /* the result to return for an FTP 550 */
85 GnomeVFSFileInfoOptions file_info_options;
88 static const char USE_PROXY_KEY[] = "/system/http_proxy/use_http_proxy";
91 static GnomeVFSResult do_open (GnomeVFSMethod *method,
92 GnomeVFSMethodHandle **method_handle,
94 GnomeVFSOpenMode mode,
95 GnomeVFSContext *context);
96 static gboolean do_is_local (GnomeVFSMethod *method,
97 const GnomeVFSURI *uri);
98 static GnomeVFSResult do_open_directory (GnomeVFSMethod *method,
99 GnomeVFSMethodHandle **method_handle,
101 GnomeVFSFileInfoOptions options,
102 GnomeVFSContext *context);
103 static GnomeVFSResult do_close_directory (GnomeVFSMethod *method,
104 GnomeVFSMethodHandle *method_handle,
105 GnomeVFSContext *context);
106 static GnomeVFSResult do_read_directory (GnomeVFSMethod *method,
107 GnomeVFSMethodHandle *method_handle,
108 GnomeVFSFileInfo *file_info,
109 GnomeVFSContext *context);
111 guint ftp_connection_uri_hash (gconstpointer c);
112 gint ftp_connection_uri_equal (gconstpointer c, gconstpointer d);
113 static GnomeVFSResult ftp_connection_acquire (GnomeVFSURI *uri,
114 FtpConnection **connection,
115 GnomeVFSContext *context);
116 static void ftp_connection_release (FtpConnection *conn);
119 static const char *anon_user = "anonymous";
120 static const char *anon_pass = "nobody@gnome.org";
121 static const int control_port = 21;
124 /* A GHashTable of GLists of FtpConnections */
125 static GHashTable *spare_connections = NULL;
126 G_LOCK_DEFINE_STATIC (spare_connections);
127 static gint total_connections = 0;
128 static gint allocated_connections = 0;
132 #define ftp_debug(c,g) FTP_DEBUG((c),(g),__FILE__, __LINE__, __PRETTY_FUNCTION__)
134 FTP_DEBUG (FtpConnection *conn,
141 g_print ("%s:%d (%s) [ftp conn=%p]\n %s\n", file, line,
144 g_print ("%s:%d (%s) [ftp]\n %s\n", file, line, func, text);
152 #define ftp_debug(c,g) (g)
156 static GnomeVFSResult
157 ftp_response_to_vfs_result (FtpConnection *conn)
159 gint response = conn->response_code;
164 return GNOME_VFS_ERROR_CANCELLED;
166 /*FIXME this looks like a bad mapping.
167 * 425 is "could not open data connection" which
168 * probably doesn't have anything to do with file permissions
170 return GNOME_VFS_ERROR_ACCESS_DENIED;
175 return GNOME_VFS_ERROR_LOGIN_FAILED;
179 return GNOME_VFS_ERROR_NOT_FOUND;
181 return conn->fivefifty;
184 return GNOME_VFS_ERROR_NO_SPACE;
186 return GNOME_VFS_ERROR_BAD_FILE;
189 /* is this the correct interpretation of this error? */
190 if (IS_100 (response)) return GNOME_VFS_OK;
191 if (IS_200 (response)) return GNOME_VFS_OK;
192 /* is this the correct interpretation of this error? */
193 if (IS_300 (response)) return GNOME_VFS_OK;
194 if (IS_400 (response)) return GNOME_VFS_ERROR_GENERIC;
195 if (IS_500 (response)) return GNOME_VFS_ERROR_INTERNAL;
197 return GNOME_VFS_ERROR_GENERIC;
201 static GnomeVFSResult read_response_line(FtpConnection *conn, gchar **line) {
202 GnomeVFSFileSize bytes = MAX_RESPONSE_SIZE, bytes_read;
203 gchar *ptr, *buf = g_malloc (MAX_RESPONSE_SIZE+1);
205 GnomeVFSResult result = GNOME_VFS_OK;
207 while (!strstr (conn->response_buffer->str, "\r\n")) {
208 /* we don't have a full line. Lets read some... */
209 /*ftp_debug (conn,g_strdup_printf ("response `%s' is incomplete", conn->response_buffer->str));*/
211 result = gnome_vfs_socket_buffer_read (conn->socket_buf, buf,
213 buf[bytes_read] = '\0';
214 /*ftp_debug (conn,g_strdup_printf ("read `%s'", buf));*/
215 conn->response_buffer = g_string_append (conn->response_buffer,
217 if (result != GNOME_VFS_OK) {
218 g_warning ("Error `%s' during read\n",
219 gnome_vfs_result_to_string(result));
227 ptr = strstr (conn->response_buffer->str, "\r\n");
228 line_length = ptr - conn->response_buffer->str;
230 *line = g_strndup (conn->response_buffer->str, line_length);
232 g_string_erase (conn->response_buffer, 0 , line_length + 2);
237 static GnomeVFSResult
238 get_response (FtpConnection *conn)
240 /* all that should be pending is a response to the last command */
241 GnomeVFSResult result;
243 /*ftp_debug (conn,g_strdup_printf ("get_response(%p)", conn));*/
247 result = read_response_line (conn, &line);
249 if (result != GNOME_VFS_OK) {
251 g_warning ("Error reading response line.");
255 #ifdef FTP_RESPONSE_DEBUG
256 g_print ("FTP: %s\n", line);
259 /* response needs to be at least: "### x" - I think*/
260 if (g_ascii_isdigit (line[0]) &&
261 g_ascii_isdigit (line[1]) &&
262 g_ascii_isdigit (line[2]) &&
263 g_ascii_isspace (line[3])) {
265 conn->response_code = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
267 if (conn->response_message) g_free (conn->response_message);
268 conn->response_message = g_strdup (line+4);
271 ftp_debug (conn,g_strdup_printf ("got response %d (%s)",
272 conn->response_code, conn->response_message));
277 return ftp_response_to_vfs_result (conn);
281 /* hmm - not a valid line - lets ignore it :-) */
286 return GNOME_VFS_OK; /* should never be reached */
290 static GnomeVFSResult do_control_write (FtpConnection *conn,
293 gchar *actual_command = g_strdup_printf ("%s\r\n", command);
294 GnomeVFSFileSize bytes = strlen (actual_command), bytes_written;
295 GnomeVFSResult result = gnome_vfs_socket_buffer_write (conn->socket_buf,
296 actual_command, bytes, &bytes_written);
298 ftp_debug (conn, g_strdup_printf ("sent \"%s\\r\\n\"", command));
300 gnome_vfs_socket_buffer_flush (conn->socket_buf);
302 if(result != GNOME_VFS_OK) {
303 g_free (actual_command);
307 if(bytes != bytes_written) {
308 g_free (actual_command);
312 g_free (actual_command);
317 static GnomeVFSResult
318 do_basic_command (FtpConnection *conn,
321 GnomeVFSResult result = do_control_write(conn, command);
323 if (result != GNOME_VFS_OK) {
327 result = get_response (conn);
332 static GnomeVFSResult
333 do_path_command (FtpConnection *conn,
338 char *actual_command;
339 GnomeVFSResult result;
341 /* as some point we may need to make this execute a CD and then
342 * a command using the basename rather than the full path. I am yet
343 * to come across such a system.
345 path = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
347 if (path == NULL || path[0] == '\0') {
348 actual_command = g_strconcat (command, " /", NULL);
350 actual_command = g_strconcat (command, " ", path, NULL);
354 result = do_basic_command (conn, actual_command);
355 g_free (actual_command);
359 static GnomeVFSResult
360 do_path_command_completely (gchar *command,
362 GnomeVFSContext *context,
363 GnomeVFSResult fivefifty)
366 GnomeVFSResult result;
368 result = ftp_connection_acquire (uri, &conn, context);
369 if (result != GNOME_VFS_OK) {
373 conn->fivefifty = fivefifty;
374 result = do_path_command (conn, command, uri);
375 ftp_connection_release (conn);
380 static GnomeVFSResult
381 do_transfer_command (FtpConnection *conn, gchar *command, GnomeVFSContext *context)
385 GnomeVFSResult result;
386 GnomeVFSSocket *socket;
388 /* Image mode (binary to the uninitiated) */
389 do_basic_command (conn, "TYPE I");
391 /* FIXME bugzilla.eazel.com 1464: implement non-PASV mode */
394 do_basic_command (conn, "PASV");
398 gint a1, a2, a3, a4, p1, p2;
399 gchar *ptr, *response = g_strdup (conn->response_message);
400 ptr = strchr (response, '(');
402 (sscanf (ptr+1,"%d,%d,%d,%d,%d,%d", &a1, &a2, &a3,
403 &a4, &p1, &p2) != 6)) {
405 return GNOME_VFS_ERROR_CORRUPTED_DATA;
408 host = g_strdup_printf ("%d.%d.%d.%d", a1, a2, a3, a4);
416 result = gnome_vfs_inet_connection_create (&conn->data_connection,
419 context ? gnome_vfs_context_get_cancellation(context) : NULL);
422 if (result != GNOME_VFS_OK) {
426 socket = gnome_vfs_inet_connection_to_socket (conn->data_connection);
427 conn->data_socketbuf = gnome_vfs_socket_buffer_new (socket);
429 if (conn->socket_buf == NULL) {
430 gnome_vfs_inet_connection_destroy (conn->data_connection, NULL);
431 return GNOME_VFS_ERROR_GENERIC;
434 result = do_control_write (conn, command);
436 if (result != GNOME_VFS_OK) {
437 gnome_vfs_socket_buffer_destroy (conn->data_socketbuf, FALSE);
438 gnome_vfs_inet_connection_destroy (conn->data_connection, NULL);
442 result = get_response (conn);
444 if (result != GNOME_VFS_OK) {
445 gnome_vfs_socket_buffer_destroy (conn->data_socketbuf, FALSE);
446 gnome_vfs_inet_connection_destroy (conn->data_connection, NULL);
453 static GnomeVFSResult
454 do_path_transfer_command (FtpConnection *conn, gchar *command, GnomeVFSURI *uri, GnomeVFSContext *context)
457 char *actual_command;
458 GnomeVFSResult result;
460 /* as some point we may need to make this execute a CD and then
461 * a command using the basename rather than the full path. I am yet
462 * to come across such a system.
464 path = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
466 if (path == NULL || path[0] == '\0') {
467 actual_command = g_strconcat (command, " /", NULL);
469 actual_command = g_strconcat (command, " ", path, NULL);
473 result = do_transfer_command (conn, actual_command, context);
474 g_free (actual_command);
479 static GnomeVFSResult
480 end_transfer (FtpConnection *conn)
482 GnomeVFSResult result;
484 /*ftp_debug (conn, g_strdup ("end_transfer()"));*/
486 if(conn->data_socketbuf) {
487 gnome_vfs_socket_buffer_flush (conn->data_socketbuf);
488 gnome_vfs_socket_buffer_destroy (conn->data_socketbuf, FALSE);
489 conn->data_socketbuf = NULL;
492 if (conn->data_connection) {
493 gnome_vfs_inet_connection_destroy (conn->data_connection, NULL);
494 conn->data_connection = NULL;
497 result = get_response (conn);
504 static GnomeVFSResult ftp_login (FtpConnection *conn,
505 const char *user, const char *password)
508 GnomeVFSResult result;
510 tmpstring = g_strdup_printf ("USER %s", user);
511 result = do_basic_command (conn, tmpstring);
514 if (IS_300 (conn->response_code)) {
515 tmpstring = g_strdup_printf ("PASS %s", password);
516 result = do_basic_command (conn, tmpstring);
524 static GnomeVFSResult
525 ftp_connection_create (FtpConnection **connptr, GnomeVFSURI *uri, GnomeVFSContext *context)
527 FtpConnection *conn = g_new0 (FtpConnection, 1);
528 GnomeVFSResult result;
529 gint port = control_port;
530 const gchar *user = anon_user;
531 const gchar *pass = anon_pass;
533 conn->uri = gnome_vfs_uri_dup (uri);
534 conn->response_buffer = g_string_new ("");
535 conn->response_code = -1;
536 conn->anonymous = TRUE;
537 conn->fivefifty = GNOME_VFS_ERROR_NOT_FOUND;
539 if (gnome_vfs_uri_get_host_port (uri)) {
540 port = gnome_vfs_uri_get_host_port (uri);
543 if (gnome_vfs_uri_get_user_name (uri)) {
544 user = gnome_vfs_uri_get_user_name (uri);
545 conn->anonymous = FALSE;
548 if (gnome_vfs_uri_get_password (uri)) {
549 pass = gnome_vfs_uri_get_password (uri);
552 result = gnome_vfs_inet_connection_create (&conn->inet_connection,
553 gnome_vfs_uri_get_host_name (uri),
555 context ? gnome_vfs_context_get_cancellation(context) : NULL);
557 if (result != GNOME_VFS_OK) {
558 g_warning ("gnome_vfs_inet_connection_create (\"%s\", %d) = \"%s\"",
559 gnome_vfs_uri_get_host_name (uri),
560 gnome_vfs_uri_get_host_port (uri),
561 gnome_vfs_result_to_string (result));
562 gnome_vfs_uri_unref (conn->uri);
563 g_string_free (conn->response_buffer, TRUE);
568 conn->socket_buf = gnome_vfs_inet_connection_to_socket_buffer (conn->inet_connection);
570 if (conn->socket_buf == NULL) {
571 g_warning ("Getting socket buffer failed");
572 gnome_vfs_inet_connection_destroy (conn->inet_connection, NULL);
573 gnome_vfs_uri_unref (conn->uri);
574 g_string_free (conn->response_buffer, TRUE);
576 return GNOME_VFS_ERROR_GENERIC;
579 result = get_response (conn);
581 if (result != GNOME_VFS_OK) {
582 g_warning ("ftp server (%s:%d) said `%d %s'",
583 gnome_vfs_uri_get_host_name (uri),
584 gnome_vfs_uri_get_host_port (uri),
585 conn->response_code, conn->response_message);
586 gnome_vfs_uri_unref (conn->uri);
587 g_string_free (conn->response_buffer, TRUE);
592 result = ftp_login(conn, user, pass);
594 if (result != GNOME_VFS_OK) {
596 g_warning ("FTP server said: \"%d %s\"\n", conn->response_code,
597 conn->response_message);
598 gnome_vfs_socket_buffer_destroy (conn->socket_buf, FALSE);
599 gnome_vfs_inet_connection_destroy (conn->inet_connection, NULL);
600 gnome_vfs_uri_unref (conn->uri);
601 g_string_free (conn->response_buffer, TRUE);
607 /* okay, we should be connected now */
609 /* Image mode (binary to the uninitiated) */
611 do_basic_command (conn, "TYPE I");
613 /* Get the system type */
615 do_basic_command (conn, "SYST");
616 conn->server_type=g_strdup(conn->response_message);
620 ftp_debug (conn, g_strdup ("created"));
628 ftp_connection_destroy (FtpConnection *conn)
631 if (conn->inet_connection)
632 gnome_vfs_inet_connection_destroy (conn->inet_connection, NULL);
634 if (conn->socket_buf)
635 gnome_vfs_socket_buffer_destroy (conn->socket_buf, FALSE);
637 gnome_vfs_uri_unref (conn->uri);
640 if (conn->response_buffer)
641 g_string_free(conn->response_buffer, TRUE);
642 g_free (conn->response_message);
643 g_free (conn->server_type);
645 if (conn->data_connection)
646 gnome_vfs_inet_connection_destroy(conn->data_connection, NULL);
648 if (conn->data_socketbuf)
649 gnome_vfs_socket_buffer_destroy (conn->data_socketbuf, FALSE);
651 g_free (conn->dirlist);
652 g_free (conn->dirlistptr);
657 /* g_str_hash and g_str_equal don't take null arguments */
660 my_str_hash (const char *c)
663 return g_str_hash (c);
668 my_str_equal (const char *c,
671 if ((c && !d) || (d &&!c))
675 return strcmp (c,d) == 0;
678 /* hash the bits of a GnomeVFSURI that distingush FTP connections */
680 ftp_connection_uri_hash (gconstpointer c)
682 GnomeVFSURI *uri = (GnomeVFSURI *) c;
684 return my_str_hash (gnome_vfs_uri_get_host_name (uri)) +
685 my_str_hash (gnome_vfs_uri_get_user_name (uri)) +
686 my_str_hash (gnome_vfs_uri_get_password (uri)) +
687 gnome_vfs_uri_get_host_port (uri);
690 /* test the equality of the bits of a GnomeVFSURI that distingush FTP
693 ftp_connection_uri_equal (gconstpointer c,
696 GnomeVFSURI *uri1 = (GnomeVFSURI *)c;
697 GnomeVFSURI *uri2 = (GnomeVFSURI *) d;
699 return my_str_equal (gnome_vfs_uri_get_host_name(uri1),
700 gnome_vfs_uri_get_host_name (uri2)) &&
701 my_str_equal (gnome_vfs_uri_get_user_name (uri1),
702 gnome_vfs_uri_get_user_name (uri2)) &&
703 my_str_equal (gnome_vfs_uri_get_password (uri1),
704 gnome_vfs_uri_get_password (uri2)) &&
705 gnome_vfs_uri_get_host_port (uri1) ==
706 gnome_vfs_uri_get_host_port (uri2);
709 static GnomeVFSResult
710 ftp_connection_acquire (GnomeVFSURI *uri, FtpConnection **connection, GnomeVFSContext *context)
712 GList *possible_connections;
713 FtpConnection *conn = NULL;
714 GnomeVFSResult result = GNOME_VFS_OK;
716 G_LOCK (spare_connections);
718 if (spare_connections == NULL) {
719 spare_connections = g_hash_table_new (ftp_connection_uri_hash,
720 ftp_connection_uri_equal);
723 possible_connections = g_hash_table_lookup (spare_connections, uri);
725 if (possible_connections) {
726 /* spare connection(s) found */
727 conn = (FtpConnection *) possible_connections->data;
729 ftp_debug (conn, strdup ("found a connection"));
731 possible_connections = g_list_remove (possible_connections, conn);
732 g_hash_table_insert (spare_connections, uri, possible_connections);
734 /* make sure connection hasn't timed out */
735 result = do_basic_command(conn, "PWD");
736 if (result != GNOME_VFS_OK) {
737 ftp_connection_destroy (conn);
738 result = ftp_connection_create (&conn, uri, context);
742 result = ftp_connection_create (&conn, uri, context);
745 G_UNLOCK (spare_connections);
749 if (result == GNOME_VFS_OK) {
750 allocated_connections++;
758 ftp_connection_release (FtpConnection *conn)
760 GList *possible_connections;
763 g_return_if_fail (conn);
765 /* reset the 550 result */
766 conn->fivefifty = GNOME_VFS_ERROR_NOT_FOUND;
768 G_LOCK (spare_connections);
769 if (spare_connections == NULL)
771 g_hash_table_new (ftp_connection_uri_hash,
772 ftp_connection_uri_equal);
774 possible_connections = g_hash_table_lookup (spare_connections,
777 ftp_debug (conn, g_strdup_printf ("releasing [len = %d]",
778 g_list_length (possible_connections)));
780 possible_connections = g_list_append (possible_connections, conn);
782 if (g_hash_table_lookup (spare_connections, conn->uri)) {
783 uri = conn->uri; /* no need to duplicate uri */
785 /* uri will be used as key */
786 uri = gnome_vfs_uri_dup (conn->uri);
788 g_hash_table_insert (spare_connections, uri, possible_connections);
789 allocated_connections--;
791 G_UNLOCK(spare_connections);
795 do_is_local (GnomeVFSMethod *method,
796 const GnomeVFSURI *uri)
802 static GnomeVFSResult
803 do_open (GnomeVFSMethod *method,
804 GnomeVFSMethodHandle **method_handle,
806 GnomeVFSOpenMode mode,
807 GnomeVFSContext *context)
809 GnomeVFSResult result;
812 result = ftp_connection_acquire (uri, &conn, context);
813 if (result != GNOME_VFS_OK)
816 if (mode == GNOME_VFS_OPEN_READ) {
817 conn->operation = FTP_READ;
818 result = do_path_transfer_command (conn, "RETR", uri, context);
819 } else if (mode == GNOME_VFS_OPEN_WRITE) {
820 conn->operation = FTP_WRITE;
821 conn->fivefifty = GNOME_VFS_ERROR_ACCESS_DENIED;
822 result = do_path_transfer_command (conn, "STOR", uri, context);
823 conn->fivefifty = GNOME_VFS_ERROR_NOT_FOUND;
825 g_warning ("Unsupported open mode %d\n", mode);
826 ftp_connection_release (conn);
827 return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
829 if (result == GNOME_VFS_OK) {
830 *method_handle = (GnomeVFSMethodHandle *) conn;
832 *method_handle = NULL;
833 ftp_connection_release (conn);
838 static GnomeVFSResult
839 do_create (GnomeVFSMethod *method,
840 GnomeVFSMethodHandle **method_handle,
842 GnomeVFSOpenMode mode,
845 GnomeVFSContext *context) {
846 return do_open(method, method_handle, uri, mode, context);
849 static GnomeVFSResult
850 do_close (GnomeVFSMethod *method,
851 GnomeVFSMethodHandle *method_handle,
852 GnomeVFSContext *context)
854 FtpConnection *conn = (FtpConnection *) method_handle;
856 GnomeVFSResult result = end_transfer (conn);
858 ftp_connection_release (conn);
863 static GnomeVFSResult
864 do_read (GnomeVFSMethod *method,
865 GnomeVFSMethodHandle *method_handle,
867 GnomeVFSFileSize num_bytes,
868 GnomeVFSFileSize *bytes_read,
869 GnomeVFSContext *context)
871 FtpConnection *conn = (FtpConnection * )method_handle;
872 GnomeVFSResult result;
875 if (conn->operation != FTP_READ) {
876 g_print ("attempted to read when conn->operation = %d\n", conn->operation);
877 return GNOME_VFS_ERROR_NOT_PERMITTED;
879 g_print ("do_read (%p)\n", method_handle);
882 result = gnome_vfs_socket_buffer_read (conn->data_socketbuf, buffer, num_bytes, bytes_read);
884 if (*bytes_read == 0) {
885 result = GNOME_VFS_ERROR_EOF;
890 static GnomeVFSResult
891 do_write (GnomeVFSMethod *method,
892 GnomeVFSMethodHandle *method_handle,
893 gconstpointer buffer,
894 GnomeVFSFileSize num_bytes,
895 GnomeVFSFileSize *bytes_written,
896 GnomeVFSContext *context)
898 FtpConnection *conn = (FtpConnection *) method_handle;
899 GnomeVFSResult result;
902 g_print ("do_write ()\n");
905 if (conn->operation != FTP_WRITE)
906 return GNOME_VFS_ERROR_NOT_PERMITTED;
908 result = gnome_vfs_socket_buffer_write (conn->data_socketbuf, buffer,
914 /* parse one directory listing from the string pointed to by ls. Parse
915 * only one line from that string. Fill in the appropriate fields of file_info.
916 * return TRUE if a directory entry was found, FALSE otherwise
919 winnt_ls_to_file_info (gchar *ls, GnomeVFSFileInfo *file_info,
920 GnomeVFSFileInfoOptions options)
924 const char *mime_type;
926 /* check parameters */
927 g_return_val_if_fail (file_info != NULL, FALSE);
929 /* fill in bits of valid_fields as we go along */
930 file_info->valid_fields = 0;
932 /* First 17 chars are DOS date */
933 file_info->mtime = 0;
934 mtime_str = g_strndup (ls, 17);
935 if (sscanf (mtime_str, "%2d-%2d-%2d %2d:%2d",
936 &m, &d, &y, &h, &mn) == 5) {
937 /* yes it's a dos date */
938 struct tm mtime_parts;
939 mtime_parts.tm_mon = m - 1; /* tm_mon is zero-based */
940 mtime_parts.tm_mday = d;
941 mtime_parts.tm_year = y >= 70 ? y : y + 100; /* handle y2k */
942 mtime_parts.tm_hour = strcasecmp (mtime_str + 15, "pm") == 0 ?
944 mtime_parts.tm_min = mn;
945 mtime_parts.tm_sec = 0;
946 mtime_parts.tm_isdst = -1;
947 file_info->mtime = mktime (&mtime_parts);
948 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MTIME;
950 /* TODO: if there isn't a date it's probably a Unix-style ftp server
951 * running under Windows */
955 /* just in case client doesn't check valid_fields */
956 file_info->atime = file_info->mtime;
957 file_info->ctime = file_info->mtime;
959 /* filename begins in column 39 */
960 if (strlen (ls) >= 39) {
962 i = strcspn (ls + 39, "\r\n");
963 file_info->name = g_strndup (ls + 39, i);
965 file_info->name = NULL;
969 /* if it's a directory, columns 24-29 contains "<DIR>" */
970 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
971 if (strlen (ls) >= 24) {
973 dirflag_str = g_strndup (ls + 24, 5);
974 if (strcmp (dirflag_str, "<DIR>") == 0) {
975 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
977 g_free (dirflag_str);
979 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
981 /* if not a directory, we should find right-aligned size ending
983 if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR &&
985 file_info->size = strtol (ls + 17, NULL, 0);
986 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE;
990 if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
991 mime_type = gnome_vfs_mime_type_from_name_or_default (
993 GNOME_VFS_MIME_TYPE_UNKNOWN);
995 mime_type = gnome_vfs_mime_type_from_mode (S_IFDIR);
997 file_info->mime_type = g_strdup (mime_type);
998 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
1000 /* fill in other structures with meaningful data, even though
1001 * it may not be valid */
1002 file_info->permissions = GNOME_VFS_PERM_USER_ALL |
1003 GNOME_VFS_PERM_GROUP_ALL |
1004 GNOME_VFS_PERM_OTHER_ALL;
1005 file_info->flags = GNOME_VFS_FILE_FLAGS_NONE;
1011 * return TRUE if entry found, FALSE otherwise
1014 netware_ls_to_file_info (gchar *ls, GnomeVFSFileInfo *file_info,
1015 GnomeVFSFileInfoOptions options)
1017 const char *mime_type;
1019 /* check parameters */
1020 g_return_val_if_fail (file_info != NULL, FALSE);
1022 /* start by knowing nothing */
1023 file_info->valid_fields = 0;
1025 /* If line starts with "total" then we should skip it */
1026 if (strncmp (ls, "total", 5) == 0) {
1030 /* First char is 'd' for directory, '-' for regular file */
1031 file_info->type = GNOME_VFS_FILE_TYPE_UNKNOWN;
1032 if (strlen (ls) >= 1) {
1034 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
1035 } else if (ls[0] == '-') {
1036 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
1038 g_warning ("netware_ls_to_file_info: unknown file type '%c'", ls[0]);
1041 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
1043 /* Next is a listing of Netware permissions */
1046 /* Following the permissions is the "owner/creator" of the file */
1047 /* file info structure requires a UID, which of course is not available */
1050 /* following type, permissions, and owner is the size, right justified up
1051 * to but not including column 50. */
1052 /* if we start at column 35, that allows 15 chars for size--that should be
1054 if (strlen (ls) > 35) {
1055 file_info->size = strtol (ls + 35, NULL, 0);
1056 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE;
1059 /* columns 51-63 contain modification date of file/directory */
1060 file_info->mtime = 0;
1061 if (strlen (ls) >= 51) {
1062 char *mtime_str = g_strndup (ls + 51, 12);
1065 /* mtime_str is one of two formats...
1066 * 1) "mmm dd hh:mm" (24hr time)
1069 mtime_date = g_date_new ();
1070 if (index (mtime_str, ':') != NULL) {
1072 char *date_str = g_strndup (mtime_str, 6);
1073 g_date_set_parse (mtime_date, date_str);
1076 g_date_set_parse (mtime_date, mtime_str);
1079 if (!g_date_valid (mtime_date)) {
1080 g_warning ("netware_ls_to_file_info: cannot parse date '%s'",
1084 struct tm mtime_parts;
1085 g_date_to_struct_tm (mtime_date, &mtime_parts);
1086 mtime_parts.tm_hour = 0;
1087 mtime_parts.tm_min = 0;
1088 mtime_parts.tm_sec = 0;
1089 mtime_parts.tm_isdst = -1;
1090 if (index (mtime_str, ':')) {
1093 if (sscanf (mtime_str + 7, "%2d:%2d", &h, &mn) == 2) {
1094 mtime_parts.tm_hour = h;
1095 mtime_parts.tm_min = mn;
1097 g_warning ("netware_ls_to_file_info: invalid time '%s'",
1101 file_info->mtime = mktime (&mtime_parts);
1102 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MTIME;
1105 g_date_free (mtime_date);
1109 /* just in case client doesn't check valid_fields */
1110 file_info->atime = file_info->mtime;
1111 file_info->ctime = file_info->mtime;
1113 /* finally, the file/directory name (column 64) */
1114 if (strlen (ls) >= 64) {
1116 i = strcspn (ls + 64, "\r\n");
1117 file_info->name = g_strndup (ls + 64, i);
1119 file_info->name = NULL;
1123 if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1124 mime_type = gnome_vfs_mime_type_from_name_or_default (
1126 GNOME_VFS_MIME_TYPE_UNKNOWN);
1128 mime_type = gnome_vfs_mime_type_from_mode (S_IFDIR);
1130 file_info->mime_type = g_strdup (mime_type);
1131 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
1133 /* fill in other structures with meaningful data, even though
1134 * it may not be valid */
1135 file_info->permissions = GNOME_VFS_PERM_USER_ALL |
1136 GNOME_VFS_PERM_GROUP_ALL |
1137 GNOME_VFS_PERM_OTHER_ALL;
1138 file_info->flags = GNOME_VFS_FILE_FLAGS_NONE;
1144 unix_ls_to_file_info (gchar *ls, GnomeVFSFileInfo *file_info,
1145 GnomeVFSFileInfoOptions options)
1148 gchar *filename = NULL, *linkname = NULL;
1149 const char *mime_type;
1151 gnome_vfs_parse_ls_lga (ls, &s, &filename, &linkname);
1153 /* g_print ("filename: %s, linkname: %s\n", filename, linkname); */
1157 gnome_vfs_stat_to_file_info (file_info, &s);
1159 /* FIXME: This is a hack, but we can't change
1160 the above API until after Gnome 1.4. Ideally, we
1161 would give the stat_to_file_info function this
1162 information. Also, there may be more fields here that are not
1163 valid that we haven't dealt with. */
1164 file_info->valid_fields |= ~(GNOME_VFS_FILE_INFO_FIELDS_DEVICE
1165 | GNOME_VFS_FILE_INFO_FIELDS_INODE
1166 | GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE);
1167 file_info->io_block_size = 0;
1169 file_info->name = g_path_get_basename (filename);
1171 if(*(file_info->name) == '\0') {
1172 g_free (file_info->name);
1173 file_info->name = g_strdup ("/");
1177 file_info->symlink_name = linkname;
1178 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
1179 file_info->flags |= GNOME_VFS_FILE_FLAGS_SYMLINK;
1182 if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1183 mime_type = gnome_vfs_mime_type_from_name_or_default (file_info->name, GNOME_VFS_MIME_TYPE_UNKNOWN);
1184 /*ftp_debug (conn, g_strdup_printf ("mimetype = %s", mime_type));*/
1186 mime_type = gnome_vfs_mime_type_from_mode (s.st_mode);
1188 file_info->mime_type = g_strdup (mime_type);
1189 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
1191 /*ftp_debug (conn, g_strdup_printf ("info got name `%s'", file_info->name));*/
1203 static GnomeVFSResult
1204 internal_get_file_info (GnomeVFSMethod *method,
1206 GnomeVFSFileInfo *file_info,
1207 GnomeVFSFileInfoOptions options,
1208 GnomeVFSContext *context)
1210 FtpConnection *conn;
1211 /* FIXME bugzilla.eazel.com 1463 */
1212 GnomeVFSResult result;
1213 GnomeVFSFileSize num_bytes = 1024, bytes_read;
1214 gchar buffer[num_bytes+1];
1216 result = ftp_connection_acquire(uri, &conn);
1217 if (result != GNOME_VFS_OK) {
1222 g_print ("do_get_file_info()\n");
1225 if(strstr(conn->server_type,"MACOS")) {
1226 /* don't ask for symlinks from MacOS servers */
1227 do_path_transfer_command (conn, "LIST -ld", uri, context);
1229 do_path_transfer_command (conn, "LIST -ldL", uri, context);
1232 result = gnome_vfs_socket_buffer_read (conn->data_socketbuf, buffer,
1233 num_bytes, &bytes_read);
1235 if (result != GNOME_VFS_OK) {
1236 /*ftp_debug (conn, g_strdup ("gnome_vfs_socket_buffer_read failed"));*/
1237 ftp_connection_release (conn);
1241 result = end_transfer (conn);
1243 /* FIXME bugzilla.eazel.com 2793: check return? */
1245 ftp_connection_release (conn);
1247 if (result != GNOME_VFS_OK) {
1248 /*ftp_debug (conn,g_strdup ("LIST for get_file_info failed."));*/
1254 buffer[bytes_read] = '\0';
1255 file_info->valid_fields = 0; /* make sure valid_fields is 0 */
1257 if (ls_to_file_info (buffer, file_info)) {
1258 return GNOME_VFS_OK;
1263 return GNOME_VFS_ERROR_NOT_FOUND;
1268 static GnomeVFSResult
1269 do_get_file_info (GnomeVFSMethod *method,
1271 GnomeVFSFileInfo *file_info,
1272 GnomeVFSFileInfoOptions options,
1273 GnomeVFSContext *context)
1275 GnomeVFSURI *parent = gnome_vfs_uri_get_parent (uri);
1276 GnomeVFSResult result;
1278 if (parent == NULL) {
1279 FtpConnection *conn;
1280 /* this is a request for info about the root directory */
1282 /* is the host there? */
1283 result = ftp_connection_acquire (uri, &conn, context);
1285 if (result != GNOME_VFS_OK) {
1286 /* doesn't look like it */
1290 ftp_connection_release (conn);
1292 file_info->name = g_strdup ("/");
1293 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
1294 file_info->mime_type = g_strdup ("x-directory/normal");
1295 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE |
1296 GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
1298 GnomeVFSMethodHandle *method_handle;
1302 name = gnome_vfs_uri_extract_short_name (uri);
1304 gnome_vfs_uri_unref (parent);
1305 return GNOME_VFS_ERROR_NOT_SUPPORTED;
1308 result = do_open_directory (method, &method_handle, parent,
1311 gnome_vfs_uri_unref (parent);
1313 if (result != GNOME_VFS_OK) {
1319 result = do_read_directory (method, method_handle,
1320 file_info, context);
1321 if (result != GNOME_VFS_OK) {
1322 result = GNOME_VFS_ERROR_NOT_FOUND;
1325 if (file_info->name != NULL
1326 && strcmp (file_info->name, name) == 0) {
1331 do_close_directory (method, method_handle, context);
1337 static GnomeVFSResult
1338 do_get_file_info_from_handle (GnomeVFSMethod *method,
1339 GnomeVFSMethodHandle *method_handle,
1340 GnomeVFSFileInfo *file_info,
1341 GnomeVFSFileInfoOptions options,
1342 GnomeVFSContext *context)
1344 return do_get_file_info (method,
1345 ((FtpConnection *)method_handle)->uri,
1346 file_info, options, context);
1349 static GnomeVFSResult
1350 do_open_directory (GnomeVFSMethod *method,
1351 GnomeVFSMethodHandle **method_handle,
1353 GnomeVFSFileInfoOptions options,
1354 GnomeVFSContext *context)
1356 FtpConnection *conn;
1357 GnomeVFSResult result;
1358 GnomeVFSFileSize num_bytes = 1024, bytes_read;
1359 gchar buffer[num_bytes+1];
1360 GString *dirlist = g_string_new ("");
1362 result = ftp_connection_acquire (uri, &conn, context);
1363 if (result != GNOME_VFS_OK) {
1364 g_string_free (dirlist, TRUE);
1368 /*g_print ("do_open_directory () in uri: %s\n", gnome_vfs_uri_get_path(uri));*/
1370 /* LIST does not return an error if called on a file, but CWD
1371 * should. This allows us to have proper gnome-vfs semantics.
1372 * does the cwd break other things though? ie, are
1373 * connections stateless?
1375 conn->fivefifty = GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1376 result = do_path_command (conn, "CWD", uri);
1377 if (result != GNOME_VFS_OK) {
1378 ftp_connection_release (conn);
1382 if(strstr(conn->server_type,"MACOS")) {
1383 /* don't ask for symlinks from MacOS servers */
1384 result = do_transfer_command (conn, "LIST", context);
1386 result = do_transfer_command (conn, "LIST -L", context);
1389 if (result != GNOME_VFS_OK) {
1390 g_warning ("opendir failed because \"%s\"",
1391 gnome_vfs_result_to_string (result));
1392 ftp_connection_release (conn);
1393 g_string_free (dirlist, TRUE);
1397 while (result == GNOME_VFS_OK) {
1398 result = gnome_vfs_socket_buffer_read (conn->data_socketbuf, buffer,
1399 num_bytes, &bytes_read);
1400 if (result == GNOME_VFS_OK && bytes_read > 0) {
1401 buffer[bytes_read] = '\0';
1402 dirlist = g_string_append (dirlist, buffer);
1408 result = end_transfer (conn);
1410 if(result != GNOME_VFS_OK) g_warning ("end_transfer (conn) failed!!!!");
1412 conn->dirlist = g_strdup (dirlist->str);
1413 conn->dirlistptr = conn->dirlist;
1414 conn->file_info_options = options;
1416 g_string_free (dirlist,TRUE);
1418 *method_handle = (GnomeVFSMethodHandle *) conn;
1423 static GnomeVFSResult
1424 do_close_directory (GnomeVFSMethod *method,
1425 GnomeVFSMethodHandle *method_handle,
1426 GnomeVFSContext *context)
1428 FtpConnection *conn = (FtpConnection *) method_handle;
1431 g_print ("do_close_directory ()\n");
1434 g_free (conn->dirlist);
1435 conn->dirlist = NULL;
1436 conn->dirlistptr = NULL;
1437 ftp_connection_release (conn);
1439 return GNOME_VFS_OK;
1442 static GnomeVFSResult
1443 do_read_directory (GnomeVFSMethod *method,
1444 GnomeVFSMethodHandle *method_handle,
1445 GnomeVFSFileInfo *file_info,
1446 GnomeVFSContext *context)
1448 FtpConnection *conn = (FtpConnection *) method_handle;
1450 if (!conn->dirlistptr || *(conn->dirlistptr) == '\0')
1451 return GNOME_VFS_ERROR_EOF;
1456 if (strncmp (conn->server_type, "Windows_NT", 10) == 0) {
1457 success = winnt_ls_to_file_info (conn->dirlistptr, file_info,
1458 conn->file_info_options);
1460 else if (strncmp (conn->server_type, "NETWARE", 7) == 0) {
1461 success = netware_ls_to_file_info (conn->dirlistptr, file_info,
1462 conn->file_info_options);
1465 success = unix_ls_to_file_info (conn->dirlistptr, file_info,
1466 conn->file_info_options);
1469 /* permissions aren't valid */
1470 file_info->valid_fields &= ~GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
1472 if ((conn->file_info_options &
1473 GNOME_VFS_FILE_INFO_FOLLOW_LINKS) &&
1475 GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK)) {
1476 /* Need to follow symbolic links to match behavior Nautilus
1477 * requires for sensible display (otherwise symlinks all appear
1481 g_print("[debug] expand symlink for: %s\n", file_info->name);
1486 if (*(conn->dirlistptr) == '\0')
1487 return GNOME_VFS_ERROR_EOF;
1489 /* go till we find \r\n */
1490 while (conn->dirlistptr &&
1491 *conn->dirlistptr &&
1492 *conn->dirlistptr != '\r' &&
1493 *conn->dirlistptr != '\n') {
1497 while (conn->dirlistptr && g_ascii_isspace (*conn->dirlistptr)) {
1504 return GNOME_VFS_OK;
1507 static GnomeVFSResult
1508 do_check_same_fs (GnomeVFSMethod *method,
1511 gboolean *same_fs_return,
1512 GnomeVFSContext *context)
1514 *same_fs_return = ftp_connection_uri_equal (a,b);
1515 return GNOME_VFS_OK;
1518 static GnomeVFSResult
1519 do_make_directory (GnomeVFSMethod *method, GnomeVFSURI *uri, guint perm, GnomeVFSContext *context)
1521 GnomeVFSResult result;
1522 gchar *chmod_command;
1524 result = do_path_command_completely ("MKD", uri, context,
1525 GNOME_VFS_ERROR_ACCESS_DENIED);
1527 if (result == GNOME_VFS_OK) {
1528 /* try to set the permissions */
1529 /* this is a non-standard extension, so we'll just do our
1530 * best. We can ignore error codes. */
1531 chmod_command = g_strdup_printf("SITE CHMOD %o", perm);
1532 do_path_command_completely (chmod_command, uri, context,
1533 GNOME_VFS_ERROR_ACCESS_DENIED);
1534 g_free(chmod_command);
1541 static GnomeVFSResult
1542 do_remove_directory (GnomeVFSMethod *method,
1544 GnomeVFSContext *context)
1546 return do_path_command_completely ("RMD", uri, context,
1547 GNOME_VFS_ERROR_ACCESS_DENIED);
1551 static GnomeVFSResult
1552 do_move (GnomeVFSMethod *method,
1553 GnomeVFSURI *old_uri,
1554 GnomeVFSURI *new_uri,
1555 gboolean force_replace,
1556 GnomeVFSContext *context)
1558 GnomeVFSResult result;
1559 GnomeVFSFileInfo *p_file_info;
1561 if (!force_replace) {
1562 p_file_info = gnome_vfs_file_info_new ();
1563 result = do_get_file_info (method, new_uri, p_file_info, GNOME_VFS_FILE_INFO_DEFAULT, context);
1564 gnome_vfs_file_info_unref (p_file_info);
1567 if (result == GNOME_VFS_OK) {
1568 return GNOME_VFS_ERROR_FILE_EXISTS;
1573 if (ftp_connection_uri_equal (old_uri, new_uri)) {
1574 FtpConnection *conn;
1575 GnomeVFSResult result;
1577 result = ftp_connection_acquire (old_uri, &conn, context);
1578 if (result != GNOME_VFS_OK) {
1581 result = do_path_command (conn, "RNFR", old_uri);
1583 if (result == GNOME_VFS_OK) {
1584 conn->fivefifty = GNOME_VFS_ERROR_ACCESS_DENIED;
1585 result = do_path_command (conn, "RNTO", new_uri);
1586 conn->fivefifty = GNOME_VFS_ERROR_NOT_FOUND;
1589 ftp_connection_release (conn);
1593 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
1597 static GnomeVFSResult
1598 do_unlink (GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSContext *context)
1600 return do_path_command_completely ("DELE", uri, context,
1601 GNOME_VFS_ERROR_ACCESS_DENIED);
1604 static GnomeVFSResult
1605 do_set_file_info (GnomeVFSMethod *method,
1607 const GnomeVFSFileInfo *info,
1608 GnomeVFSSetFileInfoMask mask,
1609 GnomeVFSContext *context)
1611 GnomeVFSURI *parent_uri, *new_uri;
1612 GnomeVFSResult result;
1614 /* FIXME: For now, we only support changing the name. */
1615 if ((mask & ~(GNOME_VFS_SET_FILE_INFO_NAME)) != 0) {
1616 return GNOME_VFS_ERROR_NOT_SUPPORTED;
1619 /* FIXME bugzilla.eazel.com 645: Make sure this returns an
1620 * error for incoming names with "/" characters in them,
1621 * instead of moving the file.
1624 /* Share code with do_move. */
1625 parent_uri = gnome_vfs_uri_get_parent (uri);
1626 if (parent_uri == NULL) {
1627 return GNOME_VFS_ERROR_NOT_FOUND;
1629 new_uri = gnome_vfs_uri_append_file_name (parent_uri, info->name);
1630 gnome_vfs_uri_unref (parent_uri);
1631 result = do_move (method, uri, new_uri, FALSE, context);
1632 gnome_vfs_uri_unref (new_uri);
1636 static GnomeVFSMethod method = {
1637 sizeof (GnomeVFSMethod),
1645 NULL, /* truncate */
1650 do_get_file_info_from_handle,
1653 do_remove_directory,
1658 NULL, /* truncate */
1659 NULL, /* find_directory */
1660 NULL /* create_symbolic_link */
1664 vfs_module_init (const char *method_name,
1671 vfs_module_shutdown (GnomeVFSMethod *method)