update
[midnight-commander.git] / vfs / shared_ftp_fish.c
blobe1c0eb4e477f969b18429637de93be84602a99b3
1 /*
2 * Shared code between the fish.c and the ftp.c file systems
4 * Actually, this code is not being used by fish.c any more :-).
6 * Namespace pollution: X_hint_reread, X_flushdir.
7 */
8 static int store_file (struct direntry *fe);
9 static int retrieve_file (struct direntry *fe);
10 static int remove_temp_file (char *file_name);
11 static struct dir *retrieve_dir (struct connection *bucket,
12 char *remote_path,
13 int resolve_symlinks);
14 static void my_forget (char *path);
16 static int linear_start (struct direntry *fe, int from);
17 static int linear_read (struct direntry *fe, void *buf, int len);
18 static void linear_close (struct direntry *fe);
20 static int
21 select_on_two (int fd1, int fd2)
23 fd_set set;
24 struct timeval timeout;
25 int v;
26 int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
28 timeout.tv_sec = 1;
29 timeout.tv_usec = 0;
30 FD_ZERO(&set);
31 FD_SET(fd1, &set);
32 FD_SET(fd2, &set);
33 v = select (maxfd, &set, 0, 0, &timeout);
34 if (v <= 0)
35 return v;
36 if (FD_ISSET (fd1, &set))
37 return 1;
38 if (FD_ISSET (fd2, &set))
39 return 2;
40 return -1;
43 static int
44 get_line (int sock, char *buf, int buf_len, char term)
46 int i, status;
47 char c;
49 for (i = 0; i < buf_len; i++, buf++) {
50 if (read(sock, buf, sizeof(char)) <= 0)
51 return 0;
52 if (logfile){
53 fwrite (buf, 1, 1, logfile);
54 fflush (logfile);
56 if (*buf == term) {
57 *buf = 0;
58 return 1;
61 *buf = 0;
62 while ((status = read(sock, &c, sizeof(c))) > 0){
63 if (logfile){
64 fwrite (&c, 1, 1, logfile);
65 fflush (logfile);
67 if (c == '\n')
68 return 1;
70 return 0;
73 static void
74 direntry_destructor (void *data)
76 struct direntry *fe = data;
78 fe->count--;
80 if (fe->count > 0)
81 return;
82 g_free(fe->name);
83 if (fe->linkname)
84 g_free(fe->linkname);
85 if (fe->local_filename) {
86 if (fe->local_is_temp) {
87 if (!fe->local_stat.st_mtime)
88 unlink(fe->local_filename);
89 else {
90 struct stat sb;
92 if (stat (fe->local_filename, &sb) >=0 &&
93 fe->local_stat.st_mtime == sb.st_mtime)
94 unlink (fe->local_filename); /* Delete only if it hasn't changed */
97 g_free(fe->local_filename);
98 fe->local_filename = NULL;
100 if (fe->remote_filename)
101 g_free(fe->remote_filename);
102 if (fe->l_stat)
103 g_free(fe->l_stat);
104 g_free(fe);
107 static void
108 dir_destructor(void *data)
110 struct dir *fd = data;
112 fd->count--;
113 if (fd->count > 0)
114 return;
115 g_free(fd->remote_path);
116 linklist_destroy(fd->file_list, direntry_destructor);
117 g_free(fd);
120 static int
121 get_line_interruptible (char *buffer, int size, int fd)
123 int n;
124 int i = 0;
126 for (i = 0; i < size-1; i++) {
127 n = read (fd, buffer+i, 1);
128 if (n == -1 && errno == EINTR){
129 buffer [i] = 0;
130 return EINTR;
132 if (n == 0){
133 buffer [i] = 0;
134 return 0;
136 if (buffer [i] == '\n'){
137 buffer [i] = 0;
138 return 1;
141 buffer [size-1] = 0;
142 return 0;
145 static void
146 free_bucket (void *data)
148 struct connection *bucket = data;
150 g_free(qhost(bucket));
151 g_free(quser(bucket));
152 if (qcdir(bucket))
153 g_free(qcdir(bucket));
154 if (qhome(bucket))
155 g_free(qhome(bucket));
156 if (qupdir(bucket))
157 g_free(qupdir(bucket));
158 if (bucket->password)
159 wipe_password (bucket->password);
160 linklist_destroy(qdcache(bucket), dir_destructor);
161 g_free(bucket);
165 static void
166 connection_destructor(void *data)
168 connection_close (data);
169 free_bucket (data);
172 static void
173 flush_all_directory(struct connection *bucket)
175 linklist_delete_all(qdcache(bucket), dir_destructor);
178 static void X_fill_names (vfs *me, void (*func)(char *))
180 struct linklist *lptr;
181 char *path_name;
182 struct connection *bucket;
184 if (!connections_list)
185 return;
186 lptr = connections_list;
187 do {
188 if ((bucket = lptr->data) != 0){
190 path_name = g_strconcat ( X_myname, quser (bucket),
191 "@", qhost (bucket),
192 qcdir(bucket), NULL);
193 (*func)(path_name);
194 g_free (path_name);
196 lptr = lptr->next;
197 } while (lptr != connections_list);
200 /* get_path:
201 * makes BUCKET point to the connection bucket descriptor for PATH
202 * returns a malloced string with the pathname relative to BUCKET.
204 * path must _not_ contain initial /bla/#ftp:
206 static char*
207 s_get_path (struct connection **bucket, char *path, char *name)
209 char *user, *host, *remote_path, *pass;
210 int port;
212 #ifndef BROKEN_PATHS
213 if (strncmp (path, name, strlen (name)))
214 return NULL; /* Normal: consider cd /bla/#ftp */
215 #else
216 if (!(path = strstr (path, name)))
217 return NULL;
218 #endif
219 path += strlen (name);
221 if (!(remote_path = my_get_host_and_username (path, &host, &user, &port, &pass)))
222 my_errno = ENOENT;
223 else {
224 if ((*bucket = open_link (host, user, port, pass)) == NULL) {
225 g_free (remote_path);
226 remote_path = NULL;
229 if (host)
230 g_free (host);
231 if (user)
232 g_free (user);
233 if (pass)
234 wipe_password (pass);
236 if (!remote_path)
237 return NULL;
239 /* NOTE: Usage of tildes is deprecated, consider:
240 * cd /#ftp:pavel@hobit
241 * cd ~
242 * And now: what do I want to do? Do I want to go to /home/pavel or to
243 * /#ftp:hobit/home/pavel? I think first has better sense...
246 int f = !strcmp( remote_path, "/~" );
247 if (f || !strncmp( remote_path, "/~/", 3 )) {
248 char *s;
249 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
250 g_free (remote_path);
251 remote_path = s;
254 return remote_path;
257 void
258 X_flushdir (void)
260 force_expiration = 1;
264 static int
265 s_setctl (vfs *me, char *path, int ctlop, char *arg)
267 switch (ctlop) {
268 case MCCTL_REMOVELOCALCOPY:
269 return remove_temp_file (path);
270 return 0;
271 case MCCTL_FORGET_ABOUT:
272 my_forget(path);
273 return 0;
275 return 0;
278 static struct direntry *
279 _get_file_entry(struct connection *bucket, char *file_name,
280 int op, int flags)
282 char *p, q;
283 struct direntry *ent;
284 struct linklist *file_list, *lptr;
285 struct dir *dcache;
286 struct stat sb;
288 p = strrchr(file_name, '/');
289 q = *p;
290 *p = '\0';
291 dcache = retrieve_dir(bucket, *file_name ? file_name : "/", op & DO_RESOLVE_SYMLINK);
292 if (dcache == NULL)
293 return NULL;
294 file_list = dcache->file_list;
295 *p++ = q;
296 if (!*p)
297 p = ".";
298 for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
299 ent = lptr->data;
300 if (strcmp(p, ent->name) == 0) {
301 if (S_ISLNK(ent->s.st_mode) && (op & DO_RESOLVE_SYMLINK)) {
302 if (ent->l_stat == NULL) ERRNOR (ENOENT, NULL);
303 if (S_ISLNK(ent->l_stat->st_mode)) ERRNOR (ELOOP, NULL);
305 if (ent && (op & DO_OPEN)) {
306 mode_t fmode;
308 fmode = S_ISLNK(ent->s.st_mode)
309 ? ent->l_stat->st_mode
310 : ent->s.st_mode;
311 if (S_ISDIR(fmode)) ERRNOR (EISDIR, NULL);
312 if (!S_ISREG(fmode)) ERRNOR (EPERM, NULL);
313 if ((flags & O_EXCL) && (flags & O_CREAT)) ERRNOR (EEXIST, NULL);
314 if (ent->remote_filename == NULL)
315 if (!(ent->remote_filename = g_strdup(file_name))) ERRNOR (ENOMEM, NULL);
316 if (ent->local_filename == NULL ||
317 !ent->local_stat.st_mtime ||
318 stat (ent->local_filename, &sb) < 0 ||
319 sb.st_mtime != ent->local_stat.st_mtime) {
320 int handle;
322 if (ent->local_filename){
323 g_free (ent->local_filename);
324 ent->local_filename = NULL;
326 if (flags & O_TRUNC) {
327 ent->local_filename = tempnam (NULL, X "fs");
328 if (ent->local_filename == NULL) ERRNOR (ENOMEM, NULL);
329 handle = open(ent->local_filename, O_CREAT | O_TRUNC | O_RDWR | O_EXCL, 0600);
330 if (handle < 0) ERRNOR (EIO, NULL);
331 close(handle);
332 if (stat (ent->local_filename, &ent->local_stat) < 0)
333 ent->local_stat.st_mtime = 0;
335 else {
336 if (IS_LINEAR(flags)) {
337 ent->local_is_temp = 0;
338 ent->local_filename = NULL;
339 ent->linear_state = LS_LINEAR_CLOSED;
340 return ent;
342 if (!retrieve_file(ent))
343 return NULL;
346 else if (flags & O_TRUNC) {
347 truncate(ent->local_filename, 0);
350 return ent;
353 if ((op & DO_OPEN) && (flags & O_CREAT)) {
354 int handle;
356 ent = g_new (struct direntry, 1);
357 ent->freshly_created = 0;
358 if (ent == NULL) ERRNOR (ENOMEM, NULL);
359 ent->count = 1;
360 ent->linkname = NULL;
361 ent->l_stat = NULL;
362 ent->bucket = bucket;
363 ent->name = g_strdup(p);
364 ent->remote_filename = g_strdup(file_name);
365 ent->local_filename = tempnam (NULL, X "fs");
366 if (!ent->name && !ent->remote_filename && !ent->local_filename) {
367 direntry_destructor(ent);
368 ERRNOR (ENOMEM, NULL);
370 handle = creat(ent->local_filename, 0700);
371 if (handle == -1) {
372 my_errno = EIO;
373 goto error;
375 fstat(handle, &ent->s);
376 close(handle);
377 #if 0
378 /* This is very wrong - like this a zero length file will be always created
379 and usually preclude uploading anything more desirable */
380 #if defined(UPLOAD_ZERO_LENGTH_FILE)
381 if (!store_file(ent)) goto error;
382 #endif
383 #endif
384 if (!linklist_insert(file_list, ent)) {
385 my_errno = ENOMEM;
386 goto error;
388 ent->freshly_created = 1;
389 return ent;
391 else ERRNOR (ENOENT, NULL);
392 error:
393 direntry_destructor(ent);
394 return NULL;
398 /* this just free's the local temp file. I don't know if the
399 remote file can be used after this without crashing - paul
400 psheer@obsidian.co.za psheer@icon.co.za */
401 static int
402 remove_temp_file (char *file_name)
404 char *p, q;
405 struct connection *bucket;
406 struct direntry *ent;
407 struct linklist *file_list, *lptr;
408 struct dir *dcache;
410 if (!(file_name = get_path (&bucket, file_name)))
411 return -1;
412 p = strrchr (file_name, '/');
413 q = *p;
414 *p = '\0';
415 dcache = retrieve_dir (bucket, *file_name ? file_name : "/", 0);
416 if (dcache == NULL)
417 return -1;
418 file_list = dcache->file_list;
419 *p++ = q;
420 if (!*p)
421 p = ".";
422 for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
423 ent = lptr->data;
424 if (strcmp (p, ent->name) == 0) {
425 if (ent->local_filename) {
426 unlink (ent->local_filename);
427 g_free (ent->local_filename);
428 ent->local_filename = NULL;
429 return 0;
433 return -1;
436 static struct direntry *
437 get_file_entry(char *path, int op, int flags)
439 struct connection *bucket;
440 struct direntry *fe;
441 char *remote_path;
443 if (!(remote_path = get_path (&bucket, path)))
444 return NULL;
445 fe = _get_file_entry(bucket, remote_path, op,
446 flags);
447 g_free(remote_path);
448 #if 0
449 if (op & DO_FREE_RESOURCE)
450 vfs_add_noncurrent_stamps (&vfs_X_ops, (vfsid) bucket, NULL);
451 #endif
452 return fe;
455 #define OPT_FLUSH 1
456 #define OPT_IGNORE_ERROR 2
458 static int normal_flush = 1;
460 void X_hint_reread(int reread)
462 if (reread)
463 normal_flush++;
464 else
465 normal_flush--;
469 /* The callbacks */
471 struct filp {
472 unsigned int has_changed:1;
473 struct direntry *fe;
474 int local_handle;
477 static void *s_open (vfs *me, char *file, int flags, int mode)
479 struct filp *fp;
480 struct direntry *fe;
482 fp = g_new (struct filp, 1);
483 if (fp == NULL) ERRNOR (ENOMEM, NULL);
484 fe = get_file_entry(file, DO_OPEN | DO_RESOLVE_SYMLINK, flags);
485 if (!fe) {
486 g_free(fp);
487 return NULL;
489 fe->linear_state = IS_LINEAR(flags);
490 if (!fe->linear_state) {
491 fp->local_handle = open(fe->local_filename, flags, mode);
492 if (fp->local_handle < 0) {
493 g_free(fp);
494 ERRNOR (errno, NULL);
496 } else fp->local_handle = -1;
497 #ifdef UPLOAD_ZERO_LENGTH_FILE
498 fp->has_changed = fe->freshly_created;
499 #else
500 fp->has_changed = 0;
501 #endif
502 fp->fe = fe;
503 qlock(fe->bucket)++;
504 fe->count++;
505 return fp;
508 static int s_read (void *data, char *buffer, int count)
510 struct filp *fp;
511 int n;
513 fp = data;
514 if (fp->fe->linear_state == LS_LINEAR_CLOSED) {
515 print_vfs_message ("Starting linear transfer...");
516 if (!linear_start (fp->fe, 0))
517 return -1;
520 if (fp->fe->linear_state == LS_LINEAR_CLOSED)
521 vfs_die ("linear_start() did not set linear_state!");
523 if (fp->fe->linear_state == LS_LINEAR_OPEN)
524 return linear_read (fp->fe, buffer, count);
526 n = read (fp->local_handle, buffer, count);
527 if (n < 0)
528 my_errno = errno;
529 return n;
532 static int s_write (void *data, char *buf, int nbyte)
534 struct filp *fp = data;
535 int n;
537 if (fp->fe->linear_state)
538 vfs_die ("You may not write to linear file");
539 n = write (fp->local_handle, buf, nbyte);
540 if (n < 0)
541 my_errno = errno;
542 fp->has_changed = 1;
543 return n;
546 static int s_close (void *data)
548 struct filp *fp = data;
549 int result = 0;
551 if (fp->has_changed) {
552 if (!store_file(fp->fe))
553 result = -1;
554 if (normal_flush)
555 flush_all_directory(fp->fe->bucket);
557 if (fp->fe->linear_state == LS_LINEAR_OPEN)
558 linear_close(fp->fe);
559 if (fp->local_handle >= 0)
560 close(fp->local_handle);
561 qlock(fp->fe->bucket)--;
562 direntry_destructor(fp->fe);
563 g_free(fp);
564 return result;
567 static int s_errno (vfs *me)
569 return my_errno;
573 /* Explanation:
574 * On some operating systems (Slowaris 2 for example)
575 * the d_name member is just a char long (nice trick that break everything),
576 * so we need to set up some space for the filename.
578 struct my_dirent {
579 struct dirent dent;
580 #ifdef NEED_EXTRA_DIRENT_BUFFER
581 char extra_buffer [MC_MAXPATHLEN];
582 #endif
583 struct linklist *pos;
584 struct dir *dcache;
587 /* Possible FIXME: what happens if one directory is opened twice ? */
589 static void *s_opendir (vfs *me, char *dirname)
591 struct connection *bucket;
592 char *remote_path;
593 struct my_dirent *dirp;
595 if (!(remote_path = get_path (&bucket, dirname)))
596 return NULL;
597 dirp = g_new (struct my_dirent, 1);
598 if (dirp == NULL) {
599 my_errno = ENOMEM;
600 goto error_return;
602 dirp->dcache = retrieve_dir(bucket, remote_path, 1);
603 if (dirp->dcache == NULL)
604 goto error_return;
605 dirp->pos = dirp->dcache->file_list->next;
606 g_free(remote_path);
607 dirp->dcache->count++;
608 return (void *)dirp;
609 error_return:
610 vfs_add_noncurrent_stamps (&vfs_X_ops, (vfsid) bucket, NULL);
611 g_free(remote_path);
612 g_free(dirp);
613 return NULL;
616 static void *s_readdir (void *data)
618 struct direntry *fe;
619 struct my_dirent *dirp = data;
621 if (dirp->pos == dirp->dcache->file_list)
622 return NULL;
623 fe = dirp->pos->data;
624 strcpy (&(dirp->dent.d_name [0]), fe->name);
625 #ifndef DIRENT_LENGTH_COMPUTED
626 dirp->d_namlen = strlen (dirp->d_name);
627 #endif
628 dirp->pos = dirp->pos->next;
629 return (void *) &dirp->dent;
632 static int s_telldir (void *data)
634 struct my_dirent *dirp = data;
635 struct linklist *pos;
636 int i = 0;
638 pos = dirp->dcache->file_list->next;
639 while( pos!=dirp->dcache->file_list) {
640 if (pos == dirp->pos)
641 return i;
642 pos = pos->next;
643 i++;
645 return -1;
648 static void s_seekdir (void *data, int pos)
650 struct my_dirent *dirp = data;
651 int i;
653 dirp->pos = dirp->dcache->file_list->next;
654 for (i=0; i<pos; i++)
655 s_readdir(data);
658 static int s_closedir (void *info)
660 struct my_dirent *dirp = info;
661 dir_destructor(dirp->dcache);
662 g_free(dirp);
663 return 0;
666 static int s_lstat (vfs *me, char *path, struct stat *buf)
668 struct direntry *fe;
670 fe = get_file_entry(path, DO_FREE_RESOURCE, 0);
671 if (fe) {
672 *buf = fe->s;
673 return 0;
675 else
676 return -1;
679 static int s_stat (vfs *me, char *path, struct stat *buf)
681 struct direntry *fe;
683 fe = get_file_entry(path, DO_RESOLVE_SYMLINK | DO_FREE_RESOURCE, 0);
684 if (fe) {
685 if (!S_ISLNK(fe->s.st_mode))
686 *buf = fe->s;
687 else
688 *buf = *fe->l_stat;
689 return 0;
691 else
692 return -1;
695 static int s_fstat (void *data, struct stat *buf)
697 struct filp *fp = data;
699 if (!S_ISLNK(fp->fe->s.st_mode))
700 *buf = fp->fe->s;
701 else
702 *buf = *fp->fe->l_stat;
703 return 0;
706 static int s_readlink (vfs *me, char *path, char *buf, int size)
708 struct direntry *fe;
710 fe = get_file_entry(path, DO_FREE_RESOURCE, 0);
711 if (!fe)
712 return -1;
713 if (!S_ISLNK(fe->s.st_mode)) ERRNOR (EINVAL, -1);
714 if (fe->linkname == NULL) ERRNOR (EACCES, -1);
715 if (strlen(fe->linkname) >= size) ERRNOR (ERANGE, -1);
716 strncpy(buf, fe->linkname, size);
717 return strlen(fe->linkname);
720 static int s_chdir (vfs *me, char *path)
722 char *remote_path;
723 struct connection *bucket;
725 if (!(remote_path = get_path(&bucket, path)))
726 return -1;
727 if (qcdir(bucket))
728 g_free(qcdir(bucket));
729 qcdir(bucket) = remote_path;
730 bucket->cwd_defered = 1;
732 vfs_add_noncurrent_stamps (&vfs_X_ops, (vfsid) bucket, NULL);
733 return 0;
736 static int s_lseek (void *data, off_t offset, int whence)
738 struct filp *fp = data;
740 if (fp->fe->linear_state == LS_LINEAR_OPEN)
741 vfs_die ("You promissed not to seek!");
742 if (fp->fe->linear_state == LS_LINEAR_CLOSED) {
743 print_vfs_message ("Preparing reget...");
744 if (whence != SEEK_SET)
745 vfs_die ("You may not do such seek on linear file");
746 if (!linear_start (fp->fe, offset))
747 return -1;
748 return offset;
750 return lseek(fp->local_handle, offset, whence);
753 static vfsid s_getid (vfs *me, char *p, struct vfs_stamping **parent)
755 struct connection *bucket;
756 char *remote_path;
758 *parent = NULL; /* We are not enclosed in any other fs */
760 if (!(remote_path = get_path (&bucket, p)))
761 return (vfsid) -1;
762 else {
763 g_free(remote_path);
764 return (vfsid) bucket;
768 static int s_nothingisopen (vfsid id)
770 return qlock((struct connection *)id) == 0;
773 static void s_free (vfsid id)
775 struct connection *bucket = (struct connection *) id;
777 connection_destructor(bucket);
778 linklist_delete(connections_list, bucket);
781 static char *s_getlocalcopy (vfs *me, char *path)
783 struct filp *fp = (struct filp *) s_open (me, path, O_RDONLY, 0);
784 char *p;
786 if (fp == NULL)
787 return NULL;
788 if (fp->fe->local_filename == NULL) {
789 s_close ((void *) fp);
790 return NULL;
792 p = g_strdup (fp->fe->local_filename);
793 qlock(fp->fe->bucket)++;
794 fp->fe->count++;
795 s_close ((void *) fp);
796 return p;
799 static void s_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
801 struct filp *fp = (struct filp *) s_open (me, path, O_WRONLY, 0);
803 if (fp == NULL)
804 return;
805 if (!strcmp (fp->fe->local_filename, local)) {
806 fp->has_changed = has_changed;
807 qlock(fp->fe->bucket)--;
808 direntry_destructor(fp->fe);
809 s_close ((void *) fp);
810 } else {
811 /* Should not happen */
812 s_close ((void *) fp);
813 mc_def_ungetlocalcopy (me, path, local, has_changed);
817 static void
818 X_done(vfs *me)
820 linklist_destroy(connections_list, connection_destructor);
821 connections_list = NULL;
822 if (logfile)
823 fclose (logfile);
824 logfile = NULL;
827 static int retrieve_file(struct direntry *fe)
829 /* If you want reget, you'll have to open file with O_LINEAR */
830 int total = 0;
831 char buffer[8192];
832 int local_handle, n;
833 int stat_size = fe->s.st_size;
835 if (fe->local_filename)
836 return 1;
837 if (!(fe->local_filename = tempnam (NULL, X))) ERRNOR (ENOMEM, 0);
838 fe->local_is_temp = 1;
840 local_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
841 if (local_handle == -1) {
842 my_errno = EIO;
843 goto error_4;
846 if (!linear_start (fe, 0))
847 goto error_3;
849 /* Clear the interrupt status */
850 enable_interrupt_key ();
852 while (1) {
853 if ((n = linear_read(fe, buffer, sizeof(buffer))) < 0)
854 goto error_1;
855 if (!n)
856 break;
858 total += n;
859 vfs_print_stats (X, "Getting file", fe->remote_filename, total, stat_size);
861 while (write(local_handle, buffer, n) < 0) {
862 if (errno == EINTR) {
863 if (got_interrupt()) {
864 my_errno = EINTR;
865 goto error_2;
867 else
868 continue;
870 my_errno = errno;
871 goto error_1;
874 linear_close(fe);
875 disable_interrupt_key();
876 close(local_handle);
878 if (stat (fe->local_filename, &fe->local_stat) < 0)
879 fe->local_stat.st_mtime = 0;
881 return 1;
882 error_1:
883 error_2:
884 linear_close(fe);
885 error_3:
886 disable_interrupt_key();
887 close(local_handle);
888 unlink(fe->local_filename);
889 error_4:
890 g_free(fe->local_filename);
891 fe->local_filename = NULL;
892 return 0;