Пример #1
0
void cmd_cat(int argc, char **argv)
{
	int i;
	struct option longopts[] = {
		{"type", required_argument, 0, 't'},
		{"help", no_argument, 0, 'h'},
		{0, 0, 0, 0}
	};
	int c;
	transfer_mode_t mode = tmAscii;

	optind = 0;
	while((c = getopt_long(argc, argv, "t:h", longopts, 0)) != EOF) {
		switch(c) {
		case 't':
			if(strncmp(optarg, "ascii", strlen(optarg)) == 0)
				mode = tmAscii;
			else if(strncmp(optarg, "binary", strlen(optarg)) == 0)
				mode = tmBinary;
			else {
				fprintf(stderr,
						_("Invalid option argument --type=%s\n"), optarg);
				return;
			}
			break;
		case 'h':
			fprintf(stderr, "Print file(s) on standard output.  Usage:\n"
					"  cat [options] <file>...\n"
					"Options:\n"
					"  -t, --type=TYPE    set transfer TYPE to ascii"
					" or binary\n"
					"  -h, --help         show this help\n");
			return;
		case '?':
			optind = -1;
			return;
		}
	}

	minargs(optind);
	need_connected();
	need_loggedin();

	for(i = optind; i < argc; i++) {
		listitem *gli;
		list *gl = rglob_create();
		stripslash(argv[i]);
		if(rglob_glob(gl, argv[i], true, false, 0) == -1)
			fprintf(stderr, _("%s: no matches found\n"), argv[i]);
		for(gli = gl->first; gli; gli=gli->next) {
			rfile *rf = (rfile *)gli->data;
			const char *fn = base_name_ptr(rf->path);
			if(strcmp(fn, ".") != 0 && strcmp(fn, "..") != 0) {
				ftp_receive(rf->path, stdout, mode, 0);
				fflush(stdout);
			}
		}
		rglob_destroy(gl);
	}
}
Пример #2
0
/* appends char * items in list LP matching MASK
 * EXCLUDE_FUNC (if not 0) is called for each path found
 * and that path is excluded if EXCLUDE_FUNC returns true
 *
 * returns 0 if successful, -1 if failure
 */
int lglob_glob(list *gl, const char *mask, bool ignore_multiples,
			   lglobfunc exclude_func)
{
	struct dirent *de;
	DIR *dp;
	char *directory;
	char tmp[PATH_MAX];
	bool added = false, found = false;

	directory = base_dir_xptr(mask);

	if((dp = opendir(directory ? directory : ".")) == 0) {
		ftp_err("Unable to read directory %s\n", directory ? directory : ".");
		return -1;
	}

	if (!getcwd(tmp, PATH_MAX)) {
		if (ERANGE == errno) {
			ftp_err("cwd too long\n");
		} else {
			ftp_err("getcwd(): %s\n", strerror(errno));
		}
		return -1;
	}

	while((de = readdir(dp)) != 0) {
		char *path;

		asprintf(&path, "%s/%s", directory ? directory : ".", de->d_name);

		if(fnmatch(base_name_ptr(mask), de->d_name, 0) == 0) {
			if(!(exclude_func && exclude_func(path))) {
				char *p;
				bool ignore_item;

				p = path_absolute(path, tmp, gvLocalHomeDir);

				ignore_item = 
					(ignore_multiples &&
					 (list_search(gl, (listsearchfunc)strcmp, p) != 0));

				if(!ignore_item) {
					list_additem(gl, p);
					added = true;
				}
			}
			found = true;
		}
		free(path);
	}
	closedir(dp);

	if(!found) {
		ftp_err("%s: no matches found\n", mask);
		return -1;
	}

	return added ? 0 : -1;
}
Пример #3
0
/* returns the rfile at PATH
 * if it's not in the cache, reads the directory
 * returns 0 if not found
 */
rfile *ftp_get_file(const char *path)
{
    rfile *f;
    char *ap;

    if(!path)
        return 0;

    ap = ftp_path_absolute(path);
    stripslash(ap);

    f = ftp_cache_get_file(ap);
    if(!f) {
        char *p = base_dir_xptr(ap);
        rdirectory *rdir = ftp_get_directory(p);
        free(p);
        if(rdir)
            f = rdir_get_file(rdir, base_name_ptr(ap));
    }
    free(ap);
    return f;
}
Пример #4
0
void rfile_parse_colors(rfile *f)
{
	char *e, *q;
	int i;

	/* colors should already been initialized by init_colors */

	e = 0;
	if(risdir(f))
		e = diclr;
	else if(rislink(f))
		e = lnclr;
	else if(rischardev(f))
		e = cdclr;
	else if(risblockdev(f))
		e = bdclr;
	else if(rispipe(f))
		e = piclr;
	else if(rissock(f))
		e = soclr;

	/* do this before checking for executable, because
	 * on [v]fat filesystems, all files are 'executable'
	 */
	for(i=0;i<number_of_colors && !e;i++) {
		if(fnmatch(clrs[i].msk, base_name_ptr(f->path), 0) == 0)
			e = clrs[i].clr;
	}

	/* a file is considered executable if there is
	 * an 'x' anywhere in its permission string */
	if(!e && risexec(f))
		e = exclr;

	q = e ? e : ficlr;
	f->color = (char *)xmalloc(strlen(lc) + strlen(q) + strlen(rc) + 1);
	sprintf(f->color, "%s%s%s", lc, q, rc);
}
Пример #5
0
static int print_transfer_string(char *str,
								 FILE *fp,
								 transfer_info *ti,
								 float bps,
								 unsigned int secs,
								 unsigned long long int eta)
{
	int i;
	int len = 0;
	char *e = str;
	bool leftjust;
	int inescape = 0;
	int minlen;
	int number_of_dots;  /* used by visual progress bar (%v) */

	while(e && *e) {
		if(*e == '%') {
			leftjust = false;
			minlen = 0;
			e++;
			if(!*e)
				break;
			if(*e == '-') {
				leftjust = true;
				e++;
			}
			if(isdigit((int)*e)) {
				minlen = atoi(e);
				while(isdigit((int)*e))
					e++;
			}
			if(leftjust)
				minlen = -minlen;

			char* sp = NULL;
			switch(*e) {
			case 'r':
				sp = shortpath(base_name_ptr(ti->remote_name),
											leftjust ? -minlen : minlen,
											ftp->homedir);
				len += max_printf(fp, minlen, "%s", sp);
				break;
			case 'R':
				sp = shortpath(ti->remote_name,
											leftjust ? -minlen : minlen,
											ftp->homedir);
				len += max_printf(fp, minlen, "%s", sp);
				break;
			case 'l':
				sp = shortpath(base_name_ptr(ti->local_name),
											leftjust ? -minlen : minlen,
											gvLocalHomeDir);
				len += max_printf(fp, minlen, "%s", sp);
				break;
			case 'L':
				sp = shortpath(ti->local_name,
											leftjust ? -minlen : minlen,
											gvLocalHomeDir);
				len += max_printf(fp, minlen, "%s", sp);
				break;
			case 's':
				len += max_printf(fp, minlen, "%sB", human_size(ti->size));
				break;
			case 'S':
				len += max_printf(fp, minlen, "%sB",
								  (ti->total_size == -1L
								   ? "??"
								   : human_size(ti->total_size)));
				break;
			case 'b':
				len += max_printf(fp, minlen < 2 ? minlen : minlen-2,
								  "%sB/s", human_size(bps));
				break;
			case 'B':
				if(ti->stalled >= 5)
					len += max_printf(fp, minlen, "%s", _("stalled"));
				else
					len += max_printf(fp, minlen < 2 ? minlen : minlen-2,
									  "%sB/s", human_size(bps));
				break;
			case 'e':
				if(eta != (unsigned) -1)
					len += max_printf(fp, minlen, "%s", human_time(eta));
				else
					len += max_printf(fp, minlen, "%s", "--:--");
				break;
			case 't':
				len += max_printf(fp, minlen, "%s", human_time(secs));
				break;
			case '%':
				len += fprintf(fp, "%%");
				break;
			case 'p':
				if(ti->total_size != -1L)
					len += max_printf(fp, minlen, "%.1f",
									  (double)100*ti->size /
									  (ti->total_size +
									   (ti->total_size ? 0 : 1)));
				else
					len += fprintf(fp, "?");
				break;
			case 'v':
				if(ti->total_size != -1L) {
					if(ti->total_size == ti->size)
						number_of_dots = minlen;
					else
						number_of_dots = (double)minlen *
							ti->size / (ti->total_size + 1);
					if(number_of_dots > minlen || number_of_dots < 0)
						/* just in case */
						number_of_dots = minlen;
					i = minlen - number_of_dots;
					while(number_of_dots--)
						len += fprintf(fp, "#");
					while(i--)
						len += fprintf(fp, " ");
				} else {
					number_of_dots = minlen / 2;
					i = minlen - number_of_dots;
					while(number_of_dots--)
						len += fprintf(fp, " ");
					if(i) {
						i--;
						len += fprintf(fp, "?");
						while(i--)
							len += fprintf(fp, " ");
					}
				}
				break;
			case '{':
				inescape++;
				break;
			case '}':
				inescape--;
				break;
			default:
				len += fprintf(fp, "%%%c", *e);
				break;
			}
			free(sp);
		} else {
			fputc(*e, fp);
			if (inescape <= 0) len++;
		}
		e++;
	}

	return len;
}
Пример #6
0
static char *remote_completion_function(const char *text, int state)
{
    static int len;            /* length of unquoted */
    static char *dir = NULL;   /* any initial directory in text */
    static char *unquoted = NULL; /* the unquoted filename (or beginning of it) */
    static listitem *lip = NULL;
    static rdirectory *rdir = NULL; /* the cached remote directory */
    static char merge_fmt[] = "%s/%s";

    if (!ftp_loggedin())
        return 0;

    /* this is not really true, this is for local filename completion,
     * but it works here too (sort of), and it looks nicer, since
     * the whole path is not printed by readline, ie
     * only foo is printed and not /bar/fu/foo (if cwd == /bar/fu)
     * readline appends a class character (ie /,@,*) in _local_ filenames
     */
    rl_filename_completion_desired = 1;
#ifndef HAVE_LIBEDIT
    rl_filename_quoting_desired = 1;
#endif

    if (!state) {
        dir = base_dir_xptr(text);
        if (dir) {
            stripslash(dir);
            char* e = strchr(dir, 0);
            if (e[-1]=='\"')
                e[-1] = '\0';
            unquote(dir);
            if (strcmp(dir, "/") == 0)
                strlcpy(merge_fmt, "%s%s", sizeof(merge_fmt));
            else
                strlcpy(merge_fmt, "%s/%s", sizeof(merge_fmt));
        }
#ifndef HAVE_LIBEDIT
        if(gvWaitingDots) {
            rl_insert_text("..."); /* show dots while waiting, like ncftp */
            rl_redisplay();
        }
#endif

        char* ap = ftp_path_absolute(dir);
        rdir = ftp_cache_get_directory(ap);
        const bool dir_is_cached = (rdir != 0);
        if (!rdir)
            rdir = ftp_read_directory(ap);
        free(ap);

#ifndef HAVE_LIBEDIT
        if (gvWaitingDots)
            rl_do_undo(); /* remove the dots */
#endif

        if (!dir_is_cached && ftp_get_verbosity() >= vbCommand)
            rl_forced_update_display();

        if (!rdir) {
            free(dir);
            return 0;
        }
        unquoted = dequote_filename(base_name_ptr(text), 0);
        if (!unquoted)
            unquoted = (char *)xmalloc(1);
        len = strlen(unquoted);
        lip = rdir->files->first;
    }

    while (lip) {
        rfile* fp = (rfile *)lip->data;
        lip = lip->next;

        /* 0 = not dir, 1 = dir, 2 = link (maybe dir) */
        const int isdir = ftp_maybe_isdir(fp);
        if (remote_dir_only && isdir == 0)
            continue;

        const char* name = base_name_ptr(fp->path);
        /* skip dotdirs in completion */
        if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
            continue;

        if (strncmp(name, unquoted, len) == 0) {
            char *ret;
            if (dir)
            {
                if (asprintf(&ret, merge_fmt, dir, name) == -1)
                {
                    fprintf(stderr, _("Failed to allocate memory.\n"));
                    free(unquoted);
                    free(dir);
                    return NULL;
                }
            }
            else
                ret = xstrdup(name);
            if (isdir == 1) {
                rl_completion_append_character = '/';
            } else {
                rl_completion_append_character = ' ';
            }
            return ret;
        }
    }
    free(unquoted);
    free(dir);
    return NULL;
}
Пример #7
0
static void getfiles(list *gl, unsigned int opt, const char *output)
{
    listitem *li;
    rfile *fp, *lnfp;
    const char *opath, *ofile;
    char *link = 0;

    list_sort(gl, get_sort_func, false);

    li = gl->first;
    while(li && !get_quit) {
        fp = (rfile *)li->data;

        if(!ftp_connected())
            return;

        if(gvSighupReceived) {
            if(!test(opt, GET_RESUME))
                opt |= GET_UNIQUE;
            opt |= GET_FORCE;
        }

        opath = fp->path;
        ofile = base_name_ptr(opath);

        if(strcmp(ofile, ".")==0 || strcmp(ofile, "..")==0) {
            transfer_nextfile(gl, &li, true);
            continue;
        }

        if(test(opt, GET_INTERACTIVE) && !get_batch && !gvSighupReceived) {
						char* sp = shortpath(opath, 42, ftp->homedir);
            int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
                        _("Get '%s'?"), sp);
						free(sp);
            if(a == ASKNO) {
                transfer_nextfile(gl, &li, true);
                continue;
            }
            if(a == ASKCANCEL) {
                get_quit = true;
                break;
            }
            if(a == ASKALL)
                get_batch = true;
            /* else a==ASKYES */
        }


        if(rislink(fp)) {
            link_to_link__duh:
            if(test(opt, GET_NO_DEREFERENCE)) {
                /* link the file, don't copy */
                const int r = getfile(fp, opt, output, ofile);
                transfer_nextfile(gl, &li, r == 0);
                continue;
            }

            {
                char *xcurdir = base_dir_xptr(opath);
                link = path_absolute(fp->link, xcurdir, ftp->homedir);
                stripslash(link);
                free(xcurdir);
                ftp_trace("found link: '%s' -> '%s'\n", opath, link);
            }

            lnfp = ftp_get_file(link);
            if(lnfp == 0) {
                /* couldn't dereference the link, try to RETR it */
                ftp_trace("unable to dereference link\n");
                const int r = getfile(fp, opt, output, ofile);
                transfer_nextfile(gl, &li, r == 0);
                continue;
            }

            if(strncmp(opath, lnfp->path, strlen(lnfp->path)) == 0) {
                ftp_trace("opath == '%s', lnfp->path == '%s'\n", opath,
                          lnfp->path);
								char* sp = shortpath(lnfp->path, 42, ftp->homedir);
                fprintf(stderr, _("%s: circular link -- skipping\n"), sp);
								free(sp);
                transfer_nextfile(gl, &li, true);
                continue;
            }

            fp = lnfp;

            if(rislink(fp))
                /* found a link pointing to another link
                 */
                /* forgive me father, for I have goto'ed */
                goto link_to_link__duh;
        }

        if(risdir(fp)) {
            if(test(opt, GET_RECURSIVE)) {
                char *recurs_output;
                char *recurs_mask;
                list *rgl;


                if((get_dir_glob_mask && fnmatch(get_dir_glob_mask,
                                                 base_name_ptr(fp->path),
                                                 FNM_EXTMATCH) == FNM_NOMATCH)
#ifdef HAVE_REGEX
                   || (get_dir_rx_mask_set && regexec(&get_dir_rx_mask,
                                                      base_name_ptr(fp->path),
                                                      0, 0, 0) == REG_NOMATCH)
#endif
                    )
                    {
                    } else {
                        char *q_recurs_mask;

                        bool success = true;
                        if(!test(opt, GET_PARENTS))
                            success = asprintf(&recurs_output, "%s/%s",
                                     output ? output : ".", ofile) != -1;
                        else
                            success = asprintf(&recurs_output, "%s",
                                     output ? output : ".") != -1;
                        if (!success)
                        {
                          fprintf(stderr, _("Failed to allocate memory.\n"));
                          transfer_nextfile(gl, &li, true);
			                    continue;
                        }
                        if (asprintf(&recurs_mask, "%s/*", opath) == -1)
                        {
                          free(recurs_output);
                          fprintf(stderr, _("Failed to allocate memory.\n"));
                          transfer_nextfile(gl, &li, true);
                          continue;
                        }
                        rgl = rglob_create();
                        q_recurs_mask = backslash_quote(recurs_mask);
                        rglob_glob(rgl, q_recurs_mask, true, true, get_exclude_func);
                        free(q_recurs_mask);
                        if(list_numitem(rgl) > 0)
                            getfiles(rgl, opt, recurs_output);
                        if(test(opt, GET_PRESERVE))
                            get_preserve_attribs(fp, recurs_output);
                        rglob_destroy(rgl);
                        free(recurs_output);
                    }
            } else if(test(opt, GET_VERBOSE)) {
							char* sp = shortpath(opath, 42, ftp->homedir);
							fprintf(stderr, _("%s: omitting directory\n"), sp);
							free(sp);
            }
            transfer_nextfile(gl, &li, true);
            continue;
        }
        if(!risreg(fp)) {
            if(test(opt, GET_VERBOSE)) {
							char* sp = shortpath(opath, 42, ftp->homedir);
                fprintf(stderr, _("%s: not a regular file\n"), sp);
								free(sp);
						}
            transfer_nextfile(gl, &li, true);
            continue;
        }
        const int r = getfile(fp, opt, output, ofile);

        transfer_nextfile(gl, &li, r == 0);

        if(gvInterrupted) {
            gvInterrupted = false;
            if(li && !get_quit && ftp_connected() && !gvSighupReceived)
            {
                int a = ask(ASKYES|ASKNO, ASKYES,
                            _("Continue transfer?"));
                if(a == ASKNO) {
                    get_quit = true;
                    break;
                }
                /* else a == ASKYES */
                fprintf(stderr, _("Excellent!!!\n"));
            }
        }
    }
}
Пример #8
0
bool risdotdir(const rfile *f)
{
    return (risdir(f)
            && ((strcmp(base_name_ptr(f->path), ".") == 0
                 || strcmp(base_name_ptr(f->path), "..") == 0)));
}
Пример #9
0
char *expand_prompt(const char *fmt)
{
	unsigned maxlen;
	char *ins, *e;
	bool freeins;
	char *tmp;


	if(!fmt)
		return 0;

	char* prompt = (char *)xmalloc(strlen(fmt)+1);
	char* cp = prompt;

	while(fmt && *fmt) {
		if(*fmt == '%') {
			fmt++;
			ins = 0;
			freeins = false;
			maxlen = (unsigned)-1;
			if(isdigit((int)*fmt)) {
				maxlen = (unsigned)atoi(fmt);
				while(isdigit((int)*fmt))
					fmt++;
			}
			switch(*fmt) {
			  case 'c':
				if (asprintf(&ins, "%u", list_numitem(gvFtpList)) == -1)
        {
          fprintf(stderr, _("Failed to allocate memory.\n"));
          free(prompt);
          return NULL;
        }
				freeins = true;
				break;
			  case 'C':
				if (asprintf(&ins, "%u", connection_number()) == -1)
        {
          fprintf(stderr, _("Failed to allocate memory.\n"));
          free(prompt);
          return NULL;
        }
				freeins = true;
				break;
			  case 'u': /* username */
				ins = ftp_loggedin() ? ftp->url->username : "******";
				break;
			  case 'h': /* remote hostname (as passed to cmd_open()) */
				ins = ftp_connected() ? ftp->url->hostname : "?";
				break;
			  case 'H': /* %h up to first '.' */
				if(!ftp_connected()) {
					ins = "?";
					break;
				}
				e = strchr(ftp->url->hostname, '.');
				if(e) {
					ins = xstrndup(ftp->url->hostname, e - ftp->url->hostname);
					freeins = true;
				} else
					ins = ftp->url->hostname;
				break;
			  case 'm':
				/* remote machine name (as returned from gethostbynmame) */
				ins = xstrdup(ftp_connected() ? host_getoname(ftp->host) : "?");
        freeins = true;
				break;
			  case 'M': /* %m up to first '.' */
				if(!ftp_connected()) {
					ins = "?";
					break;
				}
				e = strchr(host_getoname(ftp->host), '.');
				if(e) {
					ins = xstrndup(host_getoname(ftp->host),
								   e - host_getoname(ftp->host));
				} else
					ins = xstrdup(host_getoname(ftp->host));
        freeins = true;
				break;
			case 'n': /* remote ip number */
				ins = xstrdup(ftp_connected() ? host_getoname(ftp->host) : "?");
        freeins = true;
				break;
			case 'w': /* current remote directory */
				if(!ftp_loggedin()) {
					ins = "?";
					break;
				}
				ins = shortpath(ftp->curdir, maxlen, 0);
				freeins = true;
				break;
			case 'W': /* basename(%w) */
				if(!ftp_loggedin()) {
					ins = "?";
					break;
				}
				ins = (char *)base_name_ptr(ftp->curdir);
				break;
			case '~': /* %w but homedir replaced with '~' */
				if(!ftp_loggedin()) {
					ins = "?";
					break;
				}
				ins = shortpath(ftp->curdir, maxlen, ftp->homedir);
				freeins = true;
				break;
			case 'l': /* current local directory */
				tmp = getcwd(NULL, 0);
				ins = shortpath(tmp, maxlen, 0);
				freeins = true;
				free(tmp);
				break;
			case 'L': /* basename(%l) */
				tmp = getcwd(NULL, 0);
				ins = (char *)base_name_ptr(tmp);
				free(tmp);
				break;
			case '%': /* percent sign */
				ins = "%";
				break;
			case '#': /* local user == root ? '#' : '$' */
				ins = getuid() == 0 ? "#" : "$";
				break;
			case '{': /* begin non-printable character string */
#ifdef HAVE_LIBREADLINE
				ins = "\001\001"; /* \001 + RL_PROMPT_START_IGNORE */
#endif
				break;
			case '}': /* end non-printable character string */
#ifdef HAVE_LIBREADLINE
				ins = "\001\002"; /* \001 + RL_PROMPT_END_IGNORE */
#endif
				break;
			case 'e': /* escape (0x1B) */
				ins = "\x1B";
				break;
			default: /* illegal format specifier */
				break;
			}

			if(ins) {
        const size_t len = strlen(prompt) + strlen(ins) + strlen(fmt+1) + 1;
				char* tmp = xmalloc(len);
				strlcpy(tmp, prompt, len);
				strlcat(tmp, ins, len);
				cp = tmp + strlen(prompt) + strlen(ins);
				free(prompt);
				prompt = tmp;
				if(freeins)
					free(ins);
			}
		} else
			*cp++ = *fmt;

		fmt++;
	}
	unquote_escapes(prompt);
	return prompt;
}
Пример #10
0
/* appends rglob items in list LP matching MASK
 * EXCLUDE_FUNC (if not 0) is called for each fileinfo item found
 * and that file is excluded if EXCLUDE_FUNC returns true
 * returns 0 if successful, -1 if failure (no files found)
 *
 * if ignore_multiples is true, a file isn't added to the list
 * if it already exists in the list
 *
 * any spaces or other strange characters in MASK should be backslash-quoted
 */
int rglob_glob(list *gl, const char *mask, bool cpifnomatch,
			   bool ignore_multiples, rglobfunc exclude_func)
{
	char *path;
	char *dep, *mp;
	rdirectory *rdir;
	listitem *lip;
	rfile *fi = 0, *nfi;
	char *d;
	int found = 0;

	path = tilde_expand_home(mask, ftp->homedir);
	dep = strrchr(path, '/');
	if(!dep)
		dep = path;
	else dep++;
	mp = xstrdup(dep);
	if(mp)
		unquote(mp);
	/* note: mp might be NULL here, treat it like mp == "*" */

	/* read the directory */
	d = base_dir_xptr(path);
	if(!d) d = xstrdup(ftp->curdir);
	else unquote(d);

	rdir = ftp_get_directory(d);
	free(d);

	if(rdir) {
		lip = rdir->files->first;
		while(lip) {
			fi = (rfile *)lip->data;
			lip = lip->next;

			/* check if the mask includes this file */
			if(mp == 0 || fnmatch(mp, base_name_ptr(fi->path), 0)
			   != FNM_NOMATCH)
			{
				bool ignore_item;

				found++;

				/* call the exclude function, if any, and skip file
					if the function returns true
				*/
				if(exclude_func && exclude_func(fi))
					ignore_item = true;
				else
					ignore_item =
						(ignore_multiples &&
						 (list_search(gl, (listsearchfunc)rfile_search_path,
										  fi->path) != 0));

				if(!ignore_item) {
					nfi = rfile_clone(fi);
					list_additem(gl, (void *)nfi);
				} else
					ftp_trace("ignoring file '%s'\n", fi->path);
			}
		}
	}

	if(found == 0) {
		char *p;
		bool ignore_item;

		if(!cpifnomatch || mp == 0 || *mp == 0) {
			free(mp);
			return -1;
		}
		p = ftp_path_absolute(path);
		unquote(p);

		/* disallow multiples of the same file */
		ignore_item =
			(ignore_multiples &&
			 (list_search(gl, (listsearchfunc)rfile_search_path,
						  p)) != 0);

		if(!ignore_item) {
			nfi = rfile_create();
			rfile_fake(nfi, p);
			list_additem(gl, (void *)nfi);
		}
		free(p);
	}

	free(mp);
	free(path);
	return 0;
}
Пример #11
0
static void putfiles(list *gl, unsigned opt, const char *output)
{
	struct stat sb;
	char *path = 0;
	const char *file;
	listitem *li;

	list_sort(gl, put_sort_func, false);

	for(li=gl->first; li && !put_quit; li=li->next) {

		if(!ftp_connected())
			return;

		if(gvSighupReceived) {
			if(!test(opt, PUT_RESUME))
				opt |= PUT_UNIQUE;
			opt |= PUT_FORCE;
		}

		path = (char *)li->data;
		file = base_name_ptr(path);

		if(strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
			continue;

		if(test(opt, PUT_INTERACTIVE) && !put_batch) {
			int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
						_("Put '%s'?"),
						shortpath(path, 42, gvLocalHomeDir));
			if(a == ASKNO)
				continue;
			if(a == ASKCANCEL) {
				put_quit = true;
				break;
			}
			if(a == ASKALL)
				put_batch = true;
			/* else a==ASKYES */
		}

		if(stat(path, &sb) != 0) {
			perror(path);
			continue;
		}

		if(S_ISDIR(sb.st_mode)) {
			if(test(opt, PUT_RECURSIVE)) {
				char *recurs_output;
				char *recurs_mask;
				list *rgl;
				int r;

				if((put_dir_glob_mask
					&& fnmatch(put_dir_glob_mask,
							   base_name_ptr(path),
							   FNM_EXTMATCH) == FNM_NOMATCH)
#ifdef HAVE_REGEX
				   || (put_dir_rx_mask_set
					   && regexec(&put_dir_rx_mask,
								  base_name_ptr(path),
								  0, 0, 0) == REG_NOMATCH)
#endif
					)
					{
						/*printf("skipping %s\n", path);*/
					} else {
						if(!test(opt, PUT_PARENTS)) {
							asprintf(&recurs_output, "%s/%s",
									 output ? output : ".", file);
						} else
							recurs_output = xstrdup(output ? output : ".");

						asprintf(&recurs_mask, "%s/*", path);
						rgl = lglob_create();
						r = lglob_glob(rgl, recurs_mask, true,
									   put_exclude_func);
						free(recurs_mask);

						if(list_numitem(rgl) > 0)
							putfiles(rgl, opt, recurs_output);
						free(recurs_output);
					}
			} else
				fprintf(stderr, _("%s: omitting directory\n"),
					   shortpath(path, 42, gvLocalHomeDir));
			continue;
		}
		if(!S_ISREG(sb.st_mode)) {
			fprintf(stderr, _("%s: not a regular file\n"),
					shortpath(path, 42, gvLocalHomeDir));
			continue;
		}
		putfile(path, &sb, opt, output);

		if(gvInterrupted) {
			gvInterrupted = false;
			if(li->next && !put_quit && ftp_connected() && !gvSighupReceived)
			{
				int a = ask(ASKYES|ASKNO, ASKYES,
							_("Continue transfer?"));
				if(a == ASKNO) {
					put_quit = true;
					break;
				}
				/* else a == ASKYES */
				fprintf(stderr, _("Excellent!!!\n"));
			}
		}
	}
}
Пример #12
0
static void putfile(const char *path, struct stat *sb,
					unsigned opt, const char *output)
{
	putmode_t how = putNormal;
	bool file_exists = false;
	char *dest, *dpath;
	int r;
	bool dir_created;
	char *dest_dir, *q_dest_dir;

	if((put_glob_mask && fnmatch(put_glob_mask, base_name_ptr(path),
								 FNM_EXTMATCH) == FNM_NOMATCH)
#ifdef HAVE_REGEX
	   || (put_rx_mask_set && regexec(&put_rx_mask, base_name_ptr(path),
									  0, 0, 0) == REG_NOMATCH)
#endif
		)
		return;

	if(!output)
		output = ".";

	if(test(opt, PUT_PARENTS)) {
		char *p = base_dir_xptr(path);
		asprintf(&dest, "%s/%s/%s", output, p, base_name_ptr(path));
		free(p);
	} else if(test(opt, PUT_OUTPUT_FILE))
		dest = xstrdup(output);
	else
		asprintf(&dest, "%s/%s", output, base_name_ptr(path));

	path_collapse(dest);

	/* make sure destination directory exists */
	dpath = base_dir_xptr(dest);
	dest_dir = ftp_path_absolute(dpath);
	q_dest_dir = bash_backslash_quote(dest_dir);
	r = ftp_mkpath(q_dest_dir);
	free(q_dest_dir);
	free(dest_dir);
	if(r == -1) {
		transfer_mail_msg(_("failed to create directory %s\n"), dest_dir);
		free(dpath);
		free(dest);
		return;
	}
	dir_created = (r == 1);

	if(!dir_created && !test(opt, PUT_UNIQUE) && !test(opt, PUT_FORCE)) {
		rfile *f;
		f = ftp_get_file(dest);
		file_exists = (f != 0);
		if(f && risdir(f)) {
			/* can't overwrite a directory */
			printf(_("%s: is a directory\n"), dest);
			free(dest);
			return;
		}
	}

	if(test(opt, PUT_APPEND)) {
		how = putAppend;
	} else if(file_exists) {
		if(test(opt, PUT_SKIP_EXISTING)) {
			printf(_("Remote file '%s' exists, skipping...\n"),
				   shortpath(dest, 42, ftp->homedir));
			free(dest);
			return;
		}
		else if(test(opt, PUT_NEWER)) {
			time_t ft = ftp_filetime(dest);
			if(ft != (time_t)-1 && ft >= sb->st_mtime) {
				printf(_("Remote file '%s' is newer than local, skipping...\n"),
					   shortpath(dest, 42, ftp->homedir));
				free(dest);
				return;
			}
		}
		else if(!test(opt, PUT_RESUME)) {
			if(!put_owbatch) {
				struct tm *fan = gmtime(&sb->st_mtime);
				time_t ft;
				int a;
				rfile *f;
				char *e;

				f = ftp_get_file(dest);
				ft = ftp_filetime(f->path);
				sb->st_mtime = gmt_mktime(fan);
				e = xstrdup(ctime(&sb->st_mtime));
				a = ask(ASKYES|ASKNO|ASKUNIQUE|ASKCANCEL|ASKALL|ASKRESUME,
						ASKRESUME,
						_("Remote file '%s' exists\nLocal: %lld bytes, %sRemote: %lld bytes, %sOverwrite?"),
						shortpath(dest, 42, ftp->homedir),
						(unsigned long long) sb->st_size, e ? e : "unknown size",
						ftp_filesize(f->path), ctime(&ft));
				free(e);
				if(a == ASKCANCEL) {
					put_quit = true;
					free(dest);
					return;
				}
				else if(a == ASKNO) {
					free(dest);
					return;
				}
				else if(a == ASKUNIQUE)
					opt |= PUT_UNIQUE; /* for this file only */
				else if(a == ASKALL)
					put_owbatch = true;
				else if(a == ASKRESUME)
					opt |= PUT_RESUME; /* for this file only */
				/* else a == ASKYES */
			}
		}
	}

	if(test(opt, PUT_RESUME))
		how = putResume;
	if(test(opt, PUT_UNIQUE))
		how = putUnique;

	r = do_the_put(path, dest, how, opt);
	free(dest);
	if(r != 0)
		return;

	if(test(opt, PUT_PRESERVE)) {
		if(ftp->has_site_chmod_command)
			ftp_chmod(ftp->ti.local_name, get_mode_string(sb->st_mode));
	}

	if(test(opt, PUT_DELETE_AFTER)) {
		bool dodel = false;

		if(!test(opt, PUT_FORCE) && !put_delbatch) {
			int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
						_("Delete local file '%s'?"),
						shortpath(path, 42, gvLocalHomeDir));
			if(a == ASKALL) {
				put_delbatch = true;
				dodel = true;
			}
			else if(a == ASKCANCEL)
				put_quit = true;
			else if(a != ASKNO)
				dodel = true;
		} else
			dodel = true;

		if(dodel) {
			if(unlink(path) == 0)
				printf(_("%s: deleted\n"),
					   shortpath(path, 42, gvLocalHomeDir));
			else
				printf(_("error deleting '%s': %s\n"),
					   shortpath(path, 42, gvLocalHomeDir),
					   strerror(errno));
		}
	}
}
Пример #13
0
static int fxpfile(const rfile *fi, unsigned int opt,
					const char *output, const char *destname)
{
	fxpmode_t how = fxpNormal;
	bool file_exists = false;
	char *dest, *dpath;
	bool dir_created;
	char *dest_dir, *q_dest_dir;
	Ftp *thisftp = ftp;

	if((fxp_glob_mask
		&& fnmatch(fxp_glob_mask, base_name_ptr(fi->path), 0) == FNM_NOMATCH)
#ifdef HAVE_REGEX
	   || (fxp_rx_mask_set
		   && regexec(&fxp_rx_mask,
					  base_name_ptr(fi->path), 0, 0, 0) == REG_NOMATCH)
#endif
		)
		return 0;

	if(!output)
		output = ".";

	if(test(opt, FXP_PARENTS)) {
		char *p = base_dir_xptr(fi->path);
		if (asprintf(&dest, "%s/%s/%s", output, p, base_name_ptr(fi->path)) == -1)
    {
      fprintf(stderr, _("Failed to allocate memory.\n"));
      free(p);
      return -1;
    }
		free(p);
	} else if(test(opt, FXP_OUTPUT_FILE))
		dest = xstrdup(output);
	else
		if (asprintf(&dest, "%s/%s", output, base_name_ptr(fi->path)) == -1)
    {
      fprintf(stderr, _("Failed to allocate memory.\n"));
      return -1;
    }

	path_collapse(dest);

	ftp_use(fxp_target);

	/* make sure destination directory exists */
	dpath = base_dir_xptr(dest);
	dest_dir = ftp_path_absolute(dpath);
	q_dest_dir = backslash_quote(dest_dir);
	int r = ftp_mkpath(q_dest_dir);
	free(q_dest_dir);
	free(dest_dir);
	if(r == -1) {
		transfer_mail_msg(_("Couldn't create directory: %s\n"), dest_dir);
		free(dpath);
		free(dest);
		ftp_use(thisftp);
		return -1;
	}
	dir_created = (r == 1);

	if(!dir_created && !test(opt, FXP_UNIQUE) && !test(opt, FXP_FORCE)) {
		rfile *f;
		f = ftp_get_file(dest);
		file_exists = (f != 0);
		if(f && risdir(f)) {
			/* can't overwrite a directory */
			printf(_("%s: is a directory\n"), dest);
			free(dest);
			return 0;
		}
	}

	if(test(opt, FXP_APPEND)) {
		how = fxpAppend;
	} else if(file_exists) {
		if(test(opt, FXP_SKIP_EXISTING)) {
			char* sp = shortpath(dest, 42, ftp->homedir);
			printf(_("Remote file '%s' exists, skipping...\n"), sp);
			free(sp);
			free(dest);
			ftp_use(thisftp);
			return 0;
		}
		else if(test(opt, FXP_NEWER)) {
			time_t src_ft;
			time_t dst_ft;

			ftp_use(thisftp);
			src_ft = ftp_filetime(fi->path, test(opt, FXP_FORCE_NEWER));
			ftp_use(fxp_target);

			dst_ft = ftp_filetime(dest, test(opt, FXP_FORCE_NEWER));

			if(src_ft != (time_t)-1 && dst_ft != (time_t)-1 && dst_ft >= src_ft) {
				char* sp = shortpath(dest, 42, ftp->homedir);
				printf(_("Remote file '%s' is newer than local, skipping...\n"), sp);
				free(sp);
				free(dest);
				ftp_use(thisftp);
				return 0;
			}
		}
		else if(!test(opt, FXP_RESUME)) {
			if(!fxp_owbatch) {
				char* sp = shortpath(dest, 42, ftp->homedir);
				int a = ask(ASKYES|ASKNO|ASKUNIQUE|ASKCANCEL|ASKALL|ASKRESUME,
							ASKRESUME,
							_("File '%s' exists, overwrite?"),
							sp);
				free(sp);
				if(a == ASKCANCEL) {
					fxp_quit = true;
					free(dest);
					ftp_use(thisftp);
					return 0;
				}
				else if(a == ASKNO) {
					free(dest);
					ftp_use(thisftp);
					return 0;
				}
				else if(a == ASKUNIQUE)
					opt |= FXP_UNIQUE; /* for this file only */
				else if(a == ASKALL)
					fxp_owbatch = true;
				else if(a == ASKRESUME)
					opt |= FXP_RESUME; /* for this file only */
				/* else a == ASKYES */
			}
		}
	}

	if(test(opt, FXP_RESUME))
		how = fxpResume;
	if(test(opt, FXP_UNIQUE))
		how = fxpUnique;

	r = do_the_fxp(thisftp, fi->path, fxp_target, dest, how, opt);
	free(dest);
	if(r != 0) {
		ftp_use(thisftp);
		return -1;
	}

	if(test(opt, FXP_PRESERVE))
		fxp_preserve_attribs(fi, dest);

	if(test(opt, FXP_DELETE_AFTER)) {
		bool dodel = false;

		ftp_use(thisftp);

		if(!test(opt, FXP_FORCE)
		   && !fxp_delbatch && !gvSighupReceived)
			{
				char* sp = shortpath(fi->path, 42, ftp->homedir);
				int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
							_("Delete remote file '%s'?"),
							sp);
				free(sp);
				if(a == ASKALL) {
					fxp_delbatch = true;
					dodel = true;
				}
				else if(a == ASKCANCEL)
					fxp_quit = true;
				else if(a != ASKNO)
					dodel = true;
			} else
				dodel = true;

		if(dodel) {
			ftp_unlink(fi->path);
			char* sp = shortpath(fi->path, 42, ftp->homedir);
			if(ftp->code == ctComplete)
				fprintf(stderr, _("%s: deleted\n"), sp);
			else
				fprintf(stderr, _("error deleting '%s': %s\n"), sp,
						ftp_getreply(false));
			free(sp);
		}
	}

	ftp_use(thisftp);
	return 0;
}
Пример #14
0
int rfile_search_filename(rfile *f, const char *filename)
{
    return strcmp(base_name_ptr(f->path), filename);
}
Пример #15
0
bool lglob_exclude_dotdirs(char *f)
{
	const char *e = base_name_ptr(f);
	return f == 0 || (strcmp(e, ".") == 0 || strcmp(e, "..") == 0);
}
Пример #16
0
/* returns:
 * 0   ok, remove file from list
 * -1  failure
 */
static int getfile(const rfile *fi, unsigned int opt,
             const char *output, const char *destname)
{
    struct stat sb;
    char* dest = NULL;
    getmode_t how = getNormal;
    bool mkunique = false;
    int r, ret = -1;

    if((get_glob_mask && fnmatch(get_glob_mask, base_name_ptr(fi->path),
                                 FNM_EXTMATCH) == FNM_NOMATCH)
#ifdef HAVE_REGEX
       || (get_rx_mask_set && regexec(&get_rx_mask, base_name_ptr(fi->path),
                                      0, 0, 0) == REG_NOMATCH)
#endif
        )
    {
        return 0;
    }

    if(!output)
        output = ".";

    if(test(opt, GET_PARENTS)) {
        char *apath = base_dir_xptr(fi->path);
        if (asprintf(&dest, "%s%s/%s", output, apath, destname) == -1)
        {
          free(apath);
          fprintf(stderr, _("Failed to allocate memory.\n"));
          return -1;
        }
        free(apath);
    } else {
        /* check if -o option is given, if GET_OUTPUT_FILE is set, we only
         * transfer one file and output is set to the filename. However, if
         * the destination already exists and is a directory, we assume
         * that the user meant a directory */

        int dest_is_file = test(opt, GET_OUTPUT_FILE);

        if(stat(output, &sb) == 0) {
            if(S_ISDIR(sb.st_mode)) {
                dest_is_file = false;
            }
        }

        if(dest_is_file)
            dest = xstrdup(output);
        else
            if (asprintf(&dest, "%s/%s", output, destname) == -1)
            {
              fprintf(stderr, _("Failed to allocate memory.\n"));
              return -1;
            }
    }

    /* make sure destination directory exists */
    {
        char *destdir = base_dir_xptr(dest);
        if(destdir) {
            bool r = make_path(destdir);
            if(!r) {
                if (errno == EEXIST)
                  ftp_err("`%s' exists but is not a directory\n", destdir);
                else
                  ftp_err("%s: %s\n", destdir, strerror(errno));
                transfer_mail_msg(_("Couldn't create directory: %s\n"),
                                  destdir);
                free(destdir);
                return -1;
            }
            /* change permission and group, if requested */
            if(test(opt, GET_CHMOD)) {
                if(stat(destdir, &sb) == 0) {
                    mode_t m = sb.st_mode;
                    m = mode_adjust(m, cmod);
                    if(chmod(destdir, m) != 0)
                        perror(destdir);
                }           }
            if(test(opt, GET_CHGRP)) {
                if(chown(destdir, -1, group_change) != 0)
                    perror(dest);
            }
            free(destdir);
        }
    }

    /* check if destination file exists */
    if(stat(dest, &sb) == 0) {
        if(test(opt, GET_SKIP_EXISTING)) {
            if(test(opt, GET_VERBOSE)) {
							char* sp = shortpath(dest, 42, gvLocalHomeDir);
              fprintf(stderr, _("Local file '%s' exists, skipping...\n"),
                      sp);
							stats_file(STATS_SKIP, 0);
							free(sp);
						}
            return 0;
        }
        if(test(opt, GET_UNIQUE))
            mkunique = true;
        else if(test(opt, GET_APPEND))
            how = getAppend;
        else if(test(opt, GET_NEWER)) {
            struct tm *fan = gmtime(&sb.st_mtime);
            time_t ft = ftp_filetime(fi->path, test(opt, GET_FORCE_NEWER));
            sb.st_mtime = gmt_mktime(fan);

            ftp_trace("get -n: remote file: %s", ctime(&ft));
            ftp_trace("get -n: local file: %s\n", ctime(&sb.st_mtime));

            if(sb.st_mtime >= ft && ft != (time_t)-1) {
                if(test(opt, GET_VERBOSE)) {
									char* sp = shortpath(dest, 30, gvLocalHomeDir);
                    ftp_err(_(
                        "Local file '%s' is newer than remote, skipping...\n"),
                            sp);
									stats_file(STATS_SKIP, 0);
									free(sp);
								}
                return 0;
            }
        } else if(!test(opt, GET_RESUME)) {
            if(!get_owbatch && !gvSighupReceived) {
                struct tm *fan = gmtime(&sb.st_mtime);
                time_t ft = ftp_filetime(fi->path, test(opt, GET_FORCE_NEWER));
                int a;
                char *e;

                sb.st_mtime = gmt_mktime(fan);
                e = xstrdup(ctime(&sb.st_mtime));
								char* sp = shortpath(dest, 42, gvLocalHomeDir);
                a = ask(ASKYES|ASKNO|ASKUNIQUE|ASKCANCEL|ASKALL|ASKRESUME,
                        ASKRESUME,
                        _("Local file '%s' exists\nLocal: %lld bytes, %sRemote: %lld bytes, %sOverwrite?"),
                        sp,
                        (unsigned long long) sb.st_size, e ? e : "unknown date\n",
                        ftp_filesize(fi->path), ctime(&ft));
								free(sp);
                free(e);
                if(a == ASKCANCEL) {
                    get_quit = true;
                    return 0;
                }
                else if(a == ASKNO)
                    return 0;
                else if(a == ASKUNIQUE)
                    mkunique = true;
                else if(a == ASKALL) {
                    get_owbatch = true;
                }
                else if(a == ASKRESUME)
                    opt |= GET_RESUME; /* for this file only */
                /* else a == ASKYES */
            }
        }
        if(test(opt, GET_RESUME))
            how = getResume;
    }

    if(mkunique) {
        char* newdest = make_unique_filename(dest);
        free(dest);
        dest = newdest;
    }

    /* the file doesn't exist or we choosed to overwrite it, or changed dest */

    if(rislink(fi) && test(opt, GET_NO_DEREFERENCE)) {
        /* remove any existing destination */
        unlink(dest);
        ftp_err(_("symlinking '%s' to '%s'\n"), dest, fi->link);
        if(symlink(fi->link, dest) != 0)
            perror(dest);
        ret = 0;
    }
    else {
        r = do_the_get(fi->path, dest, how, opt);

        if(r == 0) {
			stats_file(STATS_SUCCESS, ftp->ti.total_size);
            ret = 0;
            if(test(opt, GET_PRESERVE))
                get_preserve_attribs(fi, dest);
            if(test(opt, GET_CHMOD)) {
                mode_t m = rfile_getmode(fi);
                m = mode_adjust(m, cmod);
                if(chmod(dest, m) != 0)
                    perror(dest);
            }
            if(test(opt, GET_CHGRP)) {
                if(chown(dest, -1, group_change) != 0)
                    perror(dest);
            }
            if(test(opt, GET_DELETE_AFTER)) {
                bool dodel = false;
								char* sp = shortpath(fi->path, 42, ftp->homedir);
                if(!test(opt, GET_FORCE)
                   && !get_delbatch && !gvSighupReceived)
                {
                    int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
                                _("Delete remote file '%s'?"), sp);
                    if(a == ASKALL) {
                        get_delbatch = true;
                        dodel = true;
                    }
                    else if(a == ASKCANCEL)
                        get_quit = true;
                    else if(a != ASKNO)
                        dodel = true;
                } else
                    dodel = true;

                if(dodel) {
                    ftp_unlink(fi->path);
                    if(ftp->code == ctComplete)
                        fprintf(stderr, _("%s: deleted\n"), sp);
                    else
                        fprintf(stderr, _("error deleting '%s': %s\n"),
															  sp, ftp_getreply(false));
                }
								free(sp);
            }
        } else {
			stats_file(STATS_FAIL, 0);
			ret = -1;
		}
    }

    free(dest);
    return ret;
}
Пример #17
0
static int rfile_parse_mlsd(rfile *f, char *str, const char *dirpath)
{
    char *e;
    bool isdir = false;

    if(!str)
        return -1;

    e = strchr(str, ' ');
    if(e) {
      if (asprintf(&f->path, "%s/%s",
                 strcmp(dirpath, "/") ? dirpath : "", base_name_ptr(e+1)) == -1)
      {
        f->path = NULL;
        return -1;
      }
        *e = 0;
    } else
        return -1;

    f->perm = 0;
    f->size = 0L;
    f->mtime = 0;
    f->link = 0;
    f->nhl = 0;
    f->owner = xstrdup("owner");
    f->group = xstrdup("group");
    f->date = xstrdup("Jan  0  1900");

    while((e = strqsep(&str, ';')) != 0) {
        char *factname, *value;

        factname = strqsep(&e, '=');
        value = e;

        if(!factname || !value) {
            return -1;
        }

        if(strcasecmp(factname, "size") == 0 ||
            strcasecmp(factname, "sizd") == 0)
            /* the "sizd" fact is not standardized in "Extension to
             * FTP" Internet draft, but PureFTPd uses it for some
             * reason for size of directories
             */
            f->size = strtoull(value,NULL,10);
        else if(strcasecmp(factname, "type") == 0) {
            if(strcasecmp(value, "file") == 0)
                isdir = false;
            else if(strcasecmp(value, "dir") == 0 ||
                    strcasecmp(value, "cdir") == 0 ||
                    strcasecmp(value, "pdir") == 0)
                isdir = true;
        } else if(strcasecmp(factname, "modify") == 0) {
            struct tm ts;

            sscanf(value, "%04d%02d%02d%02d%02d%02d",
                   &ts.tm_year, &ts.tm_mon, &ts.tm_mday,
                   &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
            ts.tm_year -= 1900;
            ts.tm_mon--;
            f->mtime = gmt_mktime(&ts);
            free(f->date);
            f->date = time_to_string(f->mtime);
        } else if(strcasecmp(factname, "UNIX.mode") == 0) {
            free(f->perm);
            f->perm = perm2string(strtoul(value, 0, 8));
        } else if(strcasecmp(factname, "UNIX.gid") == 0) {
            free(f->group);
            f->group = xstrdup(value);
        } else if(strcasecmp(factname, "UNIX.uid") == 0) {
            free(f->owner);
            f->owner = xstrdup(value);
        }
    }

    if(!f->perm)
        f->perm = xstrdup("-rw-r--r--");

    if(isdir)
        f->perm[0] = 'd';

    rfile_parse_colors(f);

    return 0;
}