ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / ssh-method.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* ssh-method.c - VFS Access to the GConf configuration database.
3
4    Copyright (C) 1999 Free Software Foundation
5
6    The Gnome Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The Gnome Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the Gnome Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.
20
21    Author: Ian McKellar <yakk@yakk.net> */
22
23 #include <config.h>
24
25 #include <errno.h>
26 #include <glib/gstrfuncs.h>
27 #include <libgnomevfs/gnome-vfs-cancellation.h>
28 #include <libgnomevfs/gnome-vfs-context.h>
29 #include <libgnomevfs/gnome-vfs-mime.h>
30 #include <libgnomevfs/gnome-vfs-module-shared.h>
31 #include <libgnomevfs/gnome-vfs-module.h>
32 #include <libgnomevfs/gnome-vfs-parse-ls.h>
33 #include <libgnomevfs/gnome-vfs-utils.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <sys/signal.h>
40 #include <fcntl.h>
41
42 #define D(x)
43 /* #define D(x) g_print x */
44
45 #define LINE_LENGTH 4096        /* max line length we'll grok */
46 #define SLEEP_TIME 300000       /* time we'll wait for the process to finish */
47
48 typedef struct {
49         GnomeVFSMethodHandle method_handle;
50         GnomeVFSURI *uri;
51         enum {
52                 SSH_FILE,
53                 SSH_LIST
54         } type;
55         GnomeVFSOpenMode open_mode;
56         int read_fd;
57         int write_fd;
58         int error_fd;
59         pid_t pid;
60         GnomeVFSFileInfoOptions info_opts;
61 } SshHandle;
62
63 static GnomeVFSResult ssh_read_error    (int error_fd,
64                                          gpointer buffer,
65                                          GnomeVFSFileSize num_bytes,
66                                          GnomeVFSFileSize *bytes_read);
67 static GnomeVFSResult do_open           (GnomeVFSMethod *method,
68                                          GnomeVFSMethodHandle **method_handle,
69                                          GnomeVFSURI *uri,
70                                          GnomeVFSOpenMode mode,
71                                          GnomeVFSContext *context);
72 static GnomeVFSResult do_create         (GnomeVFSMethod *method,
73                                          GnomeVFSMethodHandle **method_handle,
74                                          GnomeVFSURI *uri,
75                                          GnomeVFSOpenMode mode,
76                                          gboolean exclusive,
77                                          guint perm,
78                                          GnomeVFSContext *context);
79 static GnomeVFSResult do_close          (GnomeVFSMethod *method,
80                                          GnomeVFSMethodHandle *method_handle,
81                                          GnomeVFSContext *context);
82 static GnomeVFSResult do_read           (GnomeVFSMethod *method,
83                                          GnomeVFSMethodHandle *method_handle,
84                                          gpointer buffer,
85                                          GnomeVFSFileSize num_bytes,
86                                          GnomeVFSFileSize *bytes_read,
87                                          GnomeVFSContext *context);
88 static GnomeVFSResult do_write          (GnomeVFSMethod *method,
89                                          GnomeVFSMethodHandle *method_handle,
90                                          gconstpointer buffer,
91                                          GnomeVFSFileSize num_bytes,
92                                          GnomeVFSFileSize *bytes_written,
93                                          GnomeVFSContext *context);
94 static GnomeVFSResult do_open_directory (GnomeVFSMethod *method,
95                                          GnomeVFSMethodHandle **method_handle,
96                                          GnomeVFSURI *uri,
97                                          GnomeVFSFileInfoOptions options,
98                                          GnomeVFSContext *context);
99 static GnomeVFSResult do_close_directory(GnomeVFSMethod *method,
100                                          GnomeVFSMethodHandle *method_handle,
101                                          GnomeVFSContext *context);
102 static GnomeVFSResult do_read_directory (GnomeVFSMethod *method,
103                                          GnomeVFSMethodHandle *method_handle,
104                                          GnomeVFSFileInfo *file_info,
105                                          GnomeVFSContext *context);
106 static GnomeVFSResult do_get_file_info  (GnomeVFSMethod *method,
107                                          GnomeVFSURI *uri,
108                                          GnomeVFSFileInfo *file_info,
109                                          GnomeVFSFileInfoOptions options,
110                                          GnomeVFSContext *context);
111 static GnomeVFSResult do_make_directory (GnomeVFSMethod *method,
112                                          GnomeVFSURI *uri,
113                                          guint perm,
114                                          GnomeVFSContext *context);
115 static GnomeVFSResult do_remove_directory(GnomeVFSMethod *method,
116                                           GnomeVFSURI *uri,
117                                           GnomeVFSContext *context);
118 static GnomeVFSResult do_move            (GnomeVFSMethod *method,
119                                           GnomeVFSURI *old_uri,
120                                           GnomeVFSURI *new_uri,
121                                           gboolean force_replace,
122                                           GnomeVFSContext *context);
123 static GnomeVFSResult do_unlink         (GnomeVFSMethod *method,
124                                          GnomeVFSURI *uri,
125                                          GnomeVFSContext *context);
126 static GnomeVFSResult do_check_same_fs  (GnomeVFSMethod  *method,
127                                          GnomeVFSURI     *a,
128                                          GnomeVFSURI     *b,
129                                          gboolean        *same_fs_return,
130                                          GnomeVFSContext *context);
131 static GnomeVFSResult do_set_file_info  (GnomeVFSMethod *method,
132                                          GnomeVFSURI *uri,
133                                          const GnomeVFSFileInfo *info,
134                                          GnomeVFSSetFileInfoMask mask,
135                                          GnomeVFSContext *context);
136 #if 0
137 static GnomeVFSResult do_get_file_info_from_handle
138                                         (GnomeVFSMethodHandle *method_handle,
139                                          GnomeVFSFileInfo *file_info,
140                                          GnomeVFSFileInfoOptions options);
141 #endif
142 static gboolean       do_is_local       (GnomeVFSMethod *method,
143                                          const GnomeVFSURI *uri);
144
145 static GnomeVFSMethod method = {
146         sizeof (GnomeVFSMethod),
147         do_open,
148         do_create, /* create */
149         do_close,
150         do_read, /* read */
151         do_write, /* write */
152         NULL, /* seek */
153         NULL, /* tell */
154         NULL, /* truncate */
155         do_open_directory,
156         do_close_directory,
157         do_read_directory,
158         do_get_file_info,
159         NULL, /* get_file_info_from_handle */
160         do_is_local,
161         do_make_directory, /* make directory */
162         do_remove_directory, /* remove directory */
163         do_move,
164         do_unlink, /* unlink */
165         do_check_same_fs,
166         do_set_file_info, /* set_file_info */
167         NULL, /* truncate */
168         NULL, /* find_directory */
169         NULL /* create_symbolic_link */
170 };
171
172 /* FIXME: does this like FDs? */
173 static GnomeVFSResult
174 ssh_connect (SshHandle **handle_return,
175              GnomeVFSURI *uri, const char *command)
176 {
177         char ** argv;
178         SshHandle *handle;
179         char *command_line, *host_port;
180         const gchar *username, *hostname;
181         int argc;
182         GError *gerror = NULL;
183         GnomeVFSFileSize bytes_read;
184         char buffer[LINE_LENGTH];
185         GnomeVFSResult res;
186
187         /* We do support ssh:/// as ssh://localhost/ */
188         hostname = gnome_vfs_uri_get_host_name (uri);
189         if (hostname == NULL) {
190                 hostname = "localhost";
191         }
192
193         username = gnome_vfs_uri_get_user_name(uri);
194         if (username == NULL) {
195                 username = g_get_user_name();
196         }
197
198         host_port = g_strdup_printf("%d", gnome_vfs_uri_get_host_port(uri) ?
199                         gnome_vfs_uri_get_host_port(uri) : 22);
200
201         command_line  = g_strconcat ("ssh -oBatchMode=yes -x -l ", 
202                                      username,
203                                      " -p ", host_port,
204                                      " ", hostname,
205                                      " ",
206                                      "\"LC_ALL=C; echo READY > /dev/stderr;",
207                                      command, "; echo DONE > /dev/stderr\"",
208                                      NULL);
209         g_free (host_port);
210
211         D(("ssh_connect () command: %s\n", command_line));
212
213         g_shell_parse_argv (command_line, &argc, &argv, &gerror);
214
215         g_free (command_line);
216         if (gerror) {
217                 g_warning (gerror->message);
218                 return GNOME_VFS_ERROR_BAD_PARAMETERS;
219         }
220
221         handle = g_new0 (SshHandle, 1);
222         handle->uri = uri;
223
224         g_spawn_async_with_pipes (NULL, argv, NULL, 
225                                   G_SPAWN_SEARCH_PATH,
226                                   NULL, NULL,
227                                   (gint *)&handle->pid, &handle->write_fd, 
228                                   &handle->read_fd, &handle->error_fd, 
229                                   &gerror);
230         g_strfreev (argv);
231
232         if (gerror) {
233                 g_warning (gerror->message);
234                 g_free (handle);
235                 return GNOME_VFS_ERROR_GENERIC;
236         }
237
238         gnome_vfs_uri_ref (handle->uri);
239
240         *handle_return = handle;
241
242         /* You can add more error checking here */
243         memset (buffer, '\0', LINE_LENGTH);
244         res = ssh_read_error (handle->error_fd, &buffer,
245                         LINE_LENGTH, &bytes_read);
246
247         if (bytes_read != 0 && res == GNOME_VFS_OK) {
248                 if (strncmp ("READY", buffer, strlen ("READY")) == 0) {
249                         res = GNOME_VFS_OK;
250                 } else if (strncmp ("Permission denied", buffer,
251                                         strlen("Permission denied")) == 0) {
252                         res = GNOME_VFS_ERROR_LOGIN_FAILED;
253                 } else if (strncmp ("Host key verification failed", buffer,
254                                         strlen("Host key verification failed"))
255                                 == 0) {
256                         res = GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE;
257                 } else if (strstr (buffer, "Connection refused") != NULL) {
258                         res = GNOME_VFS_ERROR_ACCESS_DENIED;
259                 }
260         }
261
262         return res;
263 }
264
265 static GnomeVFSResult
266 ssh_destroy (SshHandle *handle)
267 {
268         close (handle->read_fd);
269         close (handle->write_fd);
270         close (handle->error_fd);
271         gnome_vfs_uri_unref (handle->uri);
272         kill (handle->pid, SIGINT);
273         g_free (handle);
274
275         return GNOME_VFS_OK;
276 }
277
278 static GnomeVFSResult
279 ssh_read (SshHandle *handle,
280            gpointer buffer,
281            GnomeVFSFileSize num_bytes,
282            GnomeVFSFileSize *bytes_read)
283 {
284         GnomeVFSFileSize my_read;
285
286         my_read = (GnomeVFSFileSize) read (handle->read_fd, buffer, 
287                                            (size_t) num_bytes);
288
289         if (my_read == -1) {
290                 return gnome_vfs_result_from_errno ();
291         }
292
293         *bytes_read = my_read;
294
295         return GNOME_VFS_OK;
296 }
297
298 static GnomeVFSResult
299 ssh_read_error (int error_fd,
300                 gpointer buffer,
301                 GnomeVFSFileSize num_bytes,
302                 GnomeVFSFileSize *bytes_read)
303 {
304         GnomeVFSFileSize my_read;
305
306         my_read = (GnomeVFSFileSize) read (error_fd, buffer,
307                         (size_t) num_bytes);
308
309         if (my_read == -1) {
310                 return gnome_vfs_result_from_errno ();
311         }
312
313         *bytes_read = my_read;
314
315         return GNOME_VFS_OK;
316 }
317
318 static GnomeVFSResult
319 ssh_check_for_done (SshHandle *handle)
320 {
321         char buffer[LINE_LENGTH];
322         GnomeVFSResult res;
323         GnomeVFSFileSize bytes_read;
324
325         memset (buffer, '\0', LINE_LENGTH);
326         res = ssh_read_error (handle->error_fd, &buffer,
327                         LINE_LENGTH, &bytes_read);
328
329         if (bytes_read != 0 && res == GNOME_VFS_OK) {
330                 if (strncmp ("DONE", buffer, strlen ("DONE")) == 0) {
331                         return res;
332                 }
333         }
334
335         return GNOME_VFS_ERROR_GENERIC;
336 }
337
338 static GnomeVFSResult
339 ssh_write (SshHandle *handle,
340            gconstpointer buffer,
341            GnomeVFSFileSize num_bytes,
342            GnomeVFSFileSize *bytes_written)
343 {
344         GnomeVFSFileSize written;
345         int count=0;
346
347         do {
348                 errno = 0;
349                 written = (GnomeVFSFileSize) write (handle->write_fd, buffer, 
350                                                     (size_t) num_bytes);
351                 if (written == -1 && errno == EINTR) {
352                         count++;
353                         usleep (10);
354                 }
355         } while (written == -1 && errno == EINTR && count < 5);
356
357         if (written == -1) {
358                 return gnome_vfs_result_from_errno ();
359         }
360
361         *bytes_written = written;
362
363         return GNOME_VFS_OK;
364 }
365
366 static GnomeVFSResult
367 ssh_wait_and_destroy (SshHandle *handle, GnomeVFSContext *context)
368 {
369         int i;
370
371         /* That's 30 seconds */
372         for (i = 0; i < 100 && kill (handle->pid, 0) != -1; i++) {
373                 if (gnome_vfs_context_check_cancellation (context) == TRUE) {
374                         break;
375                 }
376                 usleep (SLEEP_TIME);
377         }
378
379         return ssh_destroy (handle);
380 }
381
382
383 #if 0
384 static GnomeVFSResult
385 ssh_send (SshHandle *handle,
386           const char *string)
387 {
388         GnomeVFSFileSize len, written;
389         GnomeVFSResult result = GNOME_VFS_OK;
390         
391         len = strlen (string);
392
393         while (len > 0 && result == GNOME_VFS_OK) {
394                 result = ssh_write (handle, string, len, &written);
395                 len -= written;
396                 string += written;
397         }
398
399         return result;
400 }
401 #endif
402
403 static GnomeVFSResult
404 do_open (GnomeVFSMethod *method,
405          GnomeVFSMethodHandle **method_handle,
406          GnomeVFSURI *uri,
407          GnomeVFSOpenMode mode,
408          GnomeVFSContext *context)
409 {
410         GnomeVFSResult result = GNOME_VFS_OK;
411         char *cmd;
412         SshHandle *handle = NULL;
413
414         if (mode == GNOME_VFS_OPEN_READ) {
415                 char *name;
416                 char *quoted_name;
417                 name = gnome_vfs_unescape_string (uri->text, 
418                                                   G_DIR_SEPARATOR_S);
419                 if (name == NULL) {
420                         return GNOME_VFS_ERROR_INVALID_URI;
421                 }
422                 quoted_name = g_shell_quote (name);
423                 g_free (name);
424
425                 cmd = g_strdup_printf ("cat %s", quoted_name);
426                 result = ssh_connect (&handle, uri, cmd);
427                 g_free (cmd);
428                 g_free (quoted_name);
429                 if (result != GNOME_VFS_OK) {
430                         return result;
431                 }
432         } else {
433                 return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
434         }
435
436         handle->open_mode = mode;
437         handle->type = SSH_FILE;
438         *method_handle = (GnomeVFSMethodHandle *)handle;
439
440         return GNOME_VFS_OK;
441 }
442
443 static GnomeVFSResult
444 do_create (GnomeVFSMethod *method,
445            GnomeVFSMethodHandle **method_handle,
446            GnomeVFSURI *uri,
447            GnomeVFSOpenMode mode,
448            gboolean exclusive,
449            guint perm,
450            GnomeVFSContext *context)
451 {
452         SshHandle *handle = NULL;
453         char *cmd;
454         GnomeVFSResult result;
455         char *name;
456         char *quoted_name;
457
458         if (mode != GNOME_VFS_OPEN_WRITE) {
459                 return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
460         }
461
462         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
463         if (name == NULL) {
464                 return GNOME_VFS_ERROR_INVALID_URI;
465         }
466         quoted_name = g_shell_quote (name);
467
468         cmd = g_strdup_printf ("cat > %s", quoted_name);
469         result = ssh_connect (&handle, uri, cmd);
470         g_free (cmd);
471         g_free (name);
472         g_free (quoted_name);
473
474         if (result != GNOME_VFS_OK) {
475                 return result;
476         }
477
478         /* FIXME: set perm */
479
480         handle->open_mode = mode;
481         handle->type = SSH_FILE;
482         *method_handle = (GnomeVFSMethodHandle *)handle;
483
484         return result;
485 }
486
487 static GnomeVFSResult   
488 do_close (GnomeVFSMethod *method,
489           GnomeVFSMethodHandle *method_handle,
490           GnomeVFSContext *context)
491 {
492         return ssh_wait_and_destroy ((SshHandle *)method_handle, context);
493 }
494
495 static GnomeVFSResult
496 do_read (GnomeVFSMethod *method,
497          GnomeVFSMethodHandle *method_handle,
498          gpointer buffer,
499          GnomeVFSFileSize num_bytes,
500          GnomeVFSFileSize *bytes_read,
501          GnomeVFSContext *context)
502 {
503         GnomeVFSResult result, res_secondary;
504
505         result =  ssh_read ((SshHandle *)method_handle, buffer, num_bytes,
506                         bytes_read);
507         if (result != GNOME_VFS_OK) {
508                 return result;
509         }
510
511         if (*bytes_read == 0) { 
512                 res_secondary = ssh_check_for_done ((SshHandle *)method_handle);
513                 if (res_secondary != GNOME_VFS_OK) {
514                         result = res_secondary;
515                 } else {
516                         result = GNOME_VFS_ERROR_EOF;
517                 }
518         }
519         return result;
520 }
521
522 /* alternative impl:
523  * dd bs=1 conv=notrunc count=5 seek=60 of=/tmp/foo-test
524  */
525 static GnomeVFSResult   
526 do_write (GnomeVFSMethod *method,
527           GnomeVFSMethodHandle *method_handle,
528           gconstpointer buffer,
529           GnomeVFSFileSize num_bytes,
530           GnomeVFSFileSize *bytes_written,
531           GnomeVFSContext *context)
532 {
533         return ssh_write ((SshHandle *)method_handle, buffer, num_bytes,
534                         bytes_written);
535 }
536
537 static GnomeVFSResult 
538 do_open_directory (GnomeVFSMethod *method,
539                    GnomeVFSMethodHandle **method_handle,
540                    GnomeVFSURI *uri,
541                    GnomeVFSFileInfoOptions options,
542                    GnomeVFSContext *context)
543 {
544         SshHandle *handle = NULL;
545         char *cmd = NULL;
546         GnomeVFSResult result;
547         char *name;
548         char *quoted_name;
549
550         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
551         if (name == NULL) {
552                 return GNOME_VFS_ERROR_NOT_FOUND;
553         }
554
555         quoted_name = g_shell_quote (name);
556
557         if (*name != '\0') {
558                 cmd = g_strdup_printf ("ls -l %s", quoted_name);
559         } else {
560                 cmd = g_strdup_printf ("ls -l '/'");
561         }
562
563         result = ssh_connect (&handle, uri, cmd);
564         g_free (quoted_name);
565         g_free (name);
566         g_free (cmd);
567
568         if (result != GNOME_VFS_OK) {
569                 return result;
570         }
571         handle->info_opts = options;
572         handle->open_mode = GNOME_VFS_OPEN_NONE;
573         handle->type = SSH_LIST;
574         *method_handle = (GnomeVFSMethodHandle *)handle;
575
576         return GNOME_VFS_OK;
577 }
578
579 static GnomeVFSResult 
580 do_close_directory (GnomeVFSMethod *method,
581                     GnomeVFSMethodHandle *method_handle,
582                     GnomeVFSContext *context)
583 {
584         return ssh_destroy ((SshHandle *)method_handle);
585 }
586
587 static void
588 get_access_info (GnomeVFSURI *uri, GnomeVFSFileInfo *file_info)
589 {
590      gint i;
591      gchar *name;
592      gchar *quoted_name;
593      struct param {
594              char c;
595              GnomeVFSFilePermissions perm;
596      };
597      struct param params[2] = {{'r', GNOME_VFS_PERM_ACCESS_READABLE},
598                                {'w', GNOME_VFS_PERM_ACCESS_WRITABLE}};
599
600      name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
601      if (name == NULL) {
602              return;
603      }
604
605      if ( *name == '\0' ) {
606              quoted_name = g_shell_quote ("/");
607      } else {
608              quoted_name = g_shell_quote (name);
609      }
610      g_free (name);
611
612      for (i = 0; i<2; i++) {
613              gchar c;
614              gchar *cmd;
615              SshHandle *handle;
616              GnomeVFSFileSize bytes_read;
617              GnomeVFSResult result;
618
619              cmd = g_strdup_printf ("test -%c %s && echo $?", 
620                                     params[i].c, quoted_name);
621              result = ssh_connect (&handle, uri, cmd);
622              g_free (cmd);
623              
624              if (result != GNOME_VFS_OK) {
625                      g_free(quoted_name);
626                      return;
627              }               
628              
629              result = ssh_read (handle, &c, 1, &bytes_read);
630              if ((bytes_read > 0) && (c == '0')) {
631                      file_info->permissions |= params[i].perm;
632              } else {
633                      file_info->permissions &= ~params[i].perm;
634              }
635                      
636              ssh_destroy (handle);
637      }
638
639      file_info->permissions &= ~GNOME_VFS_PERM_ACCESS_EXECUTABLE;
640      file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_ACCESS;
641
642      g_free(quoted_name);
643 }
644
645 static GnomeVFSResult 
646 do_read_directory (GnomeVFSMethod *method,
647                    GnomeVFSMethodHandle *method_handle,
648                    GnomeVFSFileInfo *file_info,
649                    GnomeVFSContext *context)
650 {
651         GnomeVFSResult result = GNOME_VFS_OK, res_secondary;
652         char line[LINE_LENGTH+1];
653         char c;
654         int i=0;
655         GnomeVFSFileSize bytes_read;
656         struct stat st;
657         char *tempfilename, *filename, *linkname;
658         SshHandle *handle = (SshHandle *)method_handle;
659         const char *mime_type;
660         char *target_name, *parent;
661         GnomeVFSFileInfo *target_info;
662         GnomeVFSURI *target_uri;
663
664         for (;;) {
665                 tempfilename = NULL;
666                 filename = NULL;
667                 linkname = NULL;
668                 i = 0;
669                 bytes_read = 0;
670
671                 while (i<LINE_LENGTH) {
672                         result = ssh_read (handle, &c,
673                                            sizeof(char), &bytes_read);
674                         if (bytes_read == 0 || c == '\r' || c == '\n') {
675                                 break;
676                         }
677
678                         if (result != GNOME_VFS_OK) {
679                                 res_secondary = ssh_check_for_done (handle);
680                                 if (res_secondary != GNOME_VFS_OK) {
681                                         result = res_secondary;
682                                 } else {
683                                         return result;
684                                 }
685                         }
686
687                         line[i] = c;
688                         i++;
689                 }
690                 /* Here we can have i == LINE_LENGTH which explains 
691                  * why the size of line is LINE_LENGTH+1
692                  */
693                 line[i] = 0;
694                 if (i == 0) {
695                         return GNOME_VFS_ERROR_EOF;
696                 }
697
698                 if (!gnome_vfs_parse_ls_lga (line, &st, &tempfilename, &linkname)) {
699                         /* Maybe the file doesn't exist? */
700                         if (strstr (line, "No such file or directory"))
701                                 return GNOME_VFS_ERROR_NOT_FOUND;
702                         continue; /* skip to next line */
703                 }
704
705                 /* Get rid of the path */
706                 if (strrchr (tempfilename, '/') != NULL) {
707                         filename = g_strdup (strrchr (tempfilename,'/') + 1);
708                 } else {
709                         filename = g_strdup (tempfilename);
710                 }
711                 g_free (tempfilename);
712
713                 gnome_vfs_stat_to_file_info (file_info, &st);
714                 file_info->name = filename;
715                 if (linkname) {
716                         GNOME_VFS_FILE_INFO_SET_SYMLINK (file_info, TRUE);
717                         file_info->valid_fields
718                                 |= GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
719                         file_info->symlink_name = linkname;
720                         D(("symlink: %s\n", file_info->symlink_name));
721                 }
722
723                 /* FIXME: support symlinks to directories correctly */
724
725                 if (file_info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
726                         parent = gnome_vfs_uri_to_string
727                                 (handle->uri, 0);
728                         target_name =
729                                 gnome_vfs_make_uri_full_from_relative
730                                 (parent, file_info->symlink_name);
731
732                         if ((handle->info_opts
733                                 & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == 0) {
734                                 mime_type = "x-special/symlink";
735                         } else {
736                                 target_uri = gnome_vfs_uri_new (target_name);
737                                 mime_type = gnome_vfs_get_file_mime_type
738                                         (target_name, NULL, FALSE);
739                                 D(("Looking up mime-type for '%s'\n\n",
740                                                         target_name));
741                                 target_info = gnome_vfs_file_info_new ();
742                                 do_get_file_info (method, target_uri,
743                                                 target_info, handle->info_opts,
744                                                 context);
745                                 file_info->type = target_info->type;
746                                 gnome_vfs_file_info_unref (target_info);
747                                 gnome_vfs_uri_unref (target_uri);
748                         }
749
750                         g_free (target_name);
751                         g_free (parent);
752                 } else {
753                         mime_type = (gnome_vfs_get_file_mime_type
754                                         (filename, &st, FALSE));
755                 }
756
757                 if (handle->info_opts & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
758                         file_info->mime_type = g_strdup (mime_type);
759                         file_info->valid_fields
760                                 |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
761                 }
762
763                 file_info->valid_fields &= 
764                         ~GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT;
765                 file_info->valid_fields &= 
766                         ~GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE;
767                 if (handle->info_opts & GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS) {
768                         get_access_info (handle->uri, file_info);
769                 }
770
771                 /* Break out.
772                    We are in a loop so we get the first 'ls' line;
773                    often it starts with 'total 2213' etc.
774                 */
775                 break;
776         }
777
778         return result;
779 }
780
781 GnomeVFSResult
782 do_get_file_info (GnomeVFSMethod *method,
783                   GnomeVFSURI *uri,
784                   GnomeVFSFileInfo *file_info,
785                   GnomeVFSFileInfoOptions options,
786                   GnomeVFSContext *context)
787 {
788         SshHandle *handle = NULL;
789         char *cmd = NULL;
790         GnomeVFSResult result;
791         char *name;
792         char *quoted_name;
793
794         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
795         if (name == NULL) {
796                 return GNOME_VFS_ERROR_INVALID_URI;
797         }
798                 
799         quoted_name = g_shell_quote (name);
800
801         if (*name != '\0') {
802                 cmd = g_strdup_printf ("ls -ld %s 2>&1", quoted_name);
803         } else {
804                 cmd = g_strdup_printf ("ls -ld '/' 2>&1");
805         }
806
807         result = ssh_connect (&handle, uri, cmd);
808         g_free (cmd);
809         g_free (name);
810         g_free (quoted_name);
811
812         if (result != GNOME_VFS_OK) {
813                 return result;
814         }
815         handle->info_opts = options;
816         handle->open_mode = GNOME_VFS_OPEN_NONE;
817         handle->type = SSH_LIST;
818
819         result = do_read_directory (method, (GnomeVFSMethodHandle *)handle,
820                                     file_info, context);
821
822         ssh_destroy (handle);
823
824         return (result == GNOME_VFS_ERROR_EOF ? GNOME_VFS_OK : result);
825 }
826
827 static GnomeVFSResult
828 do_make_directory (GnomeVFSMethod *method,
829                    GnomeVFSURI *uri,
830                    guint perm,
831                    GnomeVFSContext *context)
832 {
833         SshHandle *handle = NULL;
834         char *cmd = NULL;
835         GnomeVFSResult result;
836         char *name;
837         char *quoted_name;
838
839         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
840         if (name == NULL) {
841                 return GNOME_VFS_ERROR_INVALID_URI;
842         }
843         quoted_name = g_shell_quote (name);
844
845         cmd = g_strdup_printf ("mkdir %s", quoted_name);
846         result = ssh_connect (&handle, uri, cmd);
847         g_free (cmd);
848         g_free (name);
849         g_free (quoted_name);
850
851         if (result != GNOME_VFS_OK) {
852                 return result;
853         }
854
855         ssh_wait_and_destroy (handle, context);
856
857         return result;
858 }
859
860 static GnomeVFSResult
861 do_remove_directory (GnomeVFSMethod *method,
862                      GnomeVFSURI *uri,
863                      GnomeVFSContext *context)
864 {
865         SshHandle *handle = NULL;
866         char *cmd = NULL;
867         GnomeVFSResult result;
868         gchar *name;
869         gchar *quoted_name;
870
871         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
872         if (name == NULL) {
873                 return GNOME_VFS_ERROR_INVALID_URI;
874         }
875         quoted_name = g_shell_quote (name);
876
877         cmd = g_strdup_printf ("rm -rf %s", quoted_name);
878         result = ssh_connect (&handle, uri, cmd);
879         g_free (cmd);
880         g_free (name);
881         g_free (quoted_name);
882
883         if (result != GNOME_VFS_OK) {
884                 return result;
885         }
886
887         ssh_wait_and_destroy (handle, context);
888
889         return result;
890 }
891
892 static GnomeVFSResult
893 do_move (GnomeVFSMethod *method,
894          GnomeVFSURI *old_uri,
895          GnomeVFSURI *new_uri,
896          gboolean force_replace,
897          GnomeVFSContext *context)
898 {
899         SshHandle *handle = NULL;
900         GnomeVFSResult res;
901         gboolean same_fs;
902         char *old_name, *new_name;
903         char *quoted_old_name, *quoted_new_name;
904         char *cmd;
905
906         res = do_check_same_fs (method, old_uri, new_uri, &same_fs, context);
907         if (res != GNOME_VFS_OK) {
908                 return res;
909         }
910
911         old_name = gnome_vfs_unescape_string (old_uri->text, G_DIR_SEPARATOR_S);
912         new_name = gnome_vfs_unescape_string (new_uri->text, G_DIR_SEPARATOR_S);
913
914         if ((old_name == NULL) || (new_name == NULL)) {
915                 g_free (old_name);
916                 g_free (new_name);
917                 return GNOME_VFS_ERROR_INVALID_URI;
918         }
919         quoted_old_name = g_shell_quote (old_name);
920         quoted_new_name = g_shell_quote (new_name);
921
922         cmd = g_strdup_printf ("mv %s %s", quoted_old_name, quoted_new_name);
923         res = ssh_connect (&handle, old_uri, cmd);
924         g_free (cmd);
925         g_free (old_name);
926         g_free (new_name);
927         g_free (quoted_old_name);
928         g_free (quoted_new_name);
929
930         if (res != GNOME_VFS_OK) {
931                 return res;
932         }
933
934         ssh_wait_and_destroy (handle, context);
935
936         return res;
937 }
938
939 GnomeVFSResult
940 do_unlink (GnomeVFSMethod *method,
941            GnomeVFSURI *uri,
942            GnomeVFSContext *context)
943 {
944         SshHandle *handle = NULL;
945         char *cmd;
946         GnomeVFSResult result;
947         gchar *name;
948         gchar *quoted_name;
949
950         name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
951         if (name == NULL) {
952                 return GNOME_VFS_ERROR_INVALID_URI;
953         }
954         quoted_name = g_shell_quote (name);
955
956         cmd = g_strdup_printf ("rm -rf %s", quoted_name);
957         result = ssh_connect (&handle, uri, cmd);
958         g_free (cmd);
959         g_free (name);
960         g_free (quoted_name);
961
962         if (result != GNOME_VFS_OK) {
963                 return result;
964         }
965
966         ssh_wait_and_destroy (handle, context);
967
968         return result;
969 }
970
971 static GnomeVFSResult
972 do_check_same_fs (GnomeVFSMethod  *method,
973                 GnomeVFSURI     *a,
974                 GnomeVFSURI     *b,
975                 gboolean        *same_fs_return,
976                 GnomeVFSContext *context)
977 {
978         const gchar *a_host_name, *b_host_name;
979         const gchar *a_user_name, *b_user_name;
980
981         g_return_val_if_fail (a != NULL, GNOME_VFS_ERROR_INTERNAL);
982         g_return_val_if_fail (b != NULL, GNOME_VFS_ERROR_INTERNAL);
983
984         a_host_name = gnome_vfs_uri_get_host_name (a);
985         b_host_name = gnome_vfs_uri_get_host_name (b);
986         a_user_name = gnome_vfs_uri_get_user_name (a);
987         b_user_name = gnome_vfs_uri_get_user_name (b);
988
989         g_return_val_if_fail (a_host_name != NULL, GNOME_VFS_ERROR_INVALID_URI);        g_return_val_if_fail (b_host_name != NULL, GNOME_VFS_ERROR_INVALID_URI);                                                                                
990         if (a_user_name == NULL) {
991                 a_user_name = g_get_user_name ();
992         }
993         if (b_user_name == NULL) {
994                 b_user_name = g_get_user_name ();
995         }
996
997         if (same_fs_return != NULL) {
998                 *same_fs_return =
999                         ((!strcmp (a_host_name, b_host_name))
1000                          && (!strcmp (a_user_name, b_user_name)));
1001         }
1002
1003         return GNOME_VFS_OK;
1004 }
1005
1006 static GnomeVFSResult
1007 do_set_file_info (GnomeVFSMethod *method,
1008                   GnomeVFSURI *uri,
1009                   const GnomeVFSFileInfo *info,
1010                   GnomeVFSSetFileInfoMask mask,
1011                   GnomeVFSContext *context)
1012 {
1013         SshHandle *handle = NULL;
1014         char *cmd = NULL;
1015         GnomeVFSResult result=GNOME_VFS_OK;
1016         gchar *full_name;
1017
1018         full_name = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
1019         if (full_name == NULL) {
1020                 return GNOME_VFS_ERROR_INVALID_URI;
1021         }
1022
1023         if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
1024                 char *encoded_dir;
1025                 char *dir;
1026                 char *new_name;
1027                 char *quoted_full_name;
1028                 char *quoted_new_name;
1029
1030                 encoded_dir = gnome_vfs_uri_extract_dirname (uri);
1031                 dir = gnome_vfs_unescape_string (encoded_dir, G_DIR_SEPARATOR_S);
1032                 g_free (encoded_dir);
1033                 g_assert (dir != NULL);
1034
1035                 /* FIXME bugzilla.eazel.com 645: This needs to return
1036                  * an error for incoming names with "/" characters in
1037                  * them, instead of moving the file.
1038                  */
1039
1040                 if (dir[strlen (dir) - 1] != '/') {
1041                         new_name = g_strconcat (dir, "/", info->name, NULL);
1042                 } else {
1043                         new_name = g_strconcat (dir, info->name, NULL);
1044                 }
1045
1046                 /* FIXME: escape for shell */
1047                 quoted_new_name = g_shell_quote (new_name);
1048                 quoted_full_name = g_shell_quote (full_name);
1049                 cmd = g_strdup_printf ("mv %s %s", quoted_full_name,
1050                                        quoted_new_name);
1051                 result = ssh_connect (&handle, uri, cmd);
1052                 g_free (cmd);
1053                 g_free (dir);
1054                 g_free (new_name);
1055                 g_free (quoted_new_name);
1056                 g_free (quoted_full_name);
1057                 g_free (full_name);
1058
1059                 if (result != GNOME_VFS_OK) {
1060                         return result;
1061                 }
1062
1063                 /* Read all info from remote host */
1064                 while (1) {
1065                         char c;
1066                         GnomeVFSResult res;
1067                         GnomeVFSFileSize bytes_read;
1068                         res = ssh_read (handle, &c, 1, &bytes_read);
1069                         if (bytes_read == 0 || res != GNOME_VFS_OK)
1070                                 break;
1071                 }
1072
1073                 ssh_wait_and_destroy (handle, context);
1074         }
1075
1076         return result;
1077 }
1078
1079 #if 0
1080 static GnomeVFSResult  
1081 do_get_file_info_from_handle (GnomeVFSMethodHandle *method_handle,
1082                               GnomeVFSFileInfo *file_info,
1083                               GnomeVFSFileInfoOptions options)
1084 {
1085         return GNOME_VFS_ERROR_WRONG_FORMAT;    
1086 }
1087 #endif
1088
1089 gboolean
1090 do_is_local (GnomeVFSMethod *method, const GnomeVFSURI *uri)
1091 {
1092         return FALSE;
1093 }
1094
1095 GnomeVFSMethod *
1096 vfs_module_init (const char *method_name, const char *args)
1097 {
1098         return &method;
1099 }
1100
1101 void
1102 vfs_module_shutdown (GnomeVFSMethod *method)
1103 {
1104 }