📄 ftp.c
字号:
logprintf (LOG_VERBOSE, _("\Remote file no newer than local file `%s' -- not retrieving.\n"), con->target); dlthis = false; } else if (eq_size) { /* Remote file is newer or sizes cannot be matched */ logprintf (LOG_VERBOSE, _("\Remote file is newer than local file `%s' -- retrieving.\n\n"), con->target); } else { /* Sizes do not match */ logprintf (LOG_VERBOSE, _("\The sizes do not match (local %s) -- retrieving.\n\n"), number_to_static_string (local_size)); } } } /* opt.timestamping && f->type == FT_PLAINFILE */ switch (f->type) { case FT_SYMLINK: /* If opt.retr_symlinks is defined, we treat symlinks as if they were normal files. There is currently no way to distinguish whether they might be directories, and follow them. */ if (!opt.retr_symlinks) {#ifdef HAVE_SYMLINK if (!f->linkto) logputs (LOG_NOTQUIET, _("Invalid name of the symlink, skipping.\n")); else { struct_stat st; /* Check whether we already have the correct symbolic link. */ int rc = lstat (con->target, &st); if (rc == 0) { size_t len = strlen (f->linkto) + 1; if (S_ISLNK (st.st_mode)) { char *link_target = (char *)alloca (len); size_t n = readlink (con->target, link_target, len); if ((n == len - 1) && (memcmp (link_target, f->linkto, n) == 0)) { logprintf (LOG_VERBOSE, _("\Already have correct symlink %s -> %s\n\n"), con->target, escnonprint (f->linkto)); dlthis = false; break; } } } logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"), con->target, escnonprint (f->linkto)); /* Unlink before creating symlink! */ unlink (con->target); if (symlink (f->linkto, con->target) == -1) logprintf (LOG_NOTQUIET, "symlink: %s\n", strerror (errno)); logputs (LOG_VERBOSE, "\n"); } /* have f->linkto */#else /* not HAVE_SYMLINK */ logprintf (LOG_NOTQUIET, _("Symlinks not supported, skipping symlink `%s'.\n"), con->target);#endif /* not HAVE_SYMLINK */ } else /* opt.retr_symlinks */ { if (dlthis) err = ftp_loop_internal (u, f, con); } /* opt.retr_symlinks */ break; case FT_DIRECTORY: if (!opt.recursive) logprintf (LOG_NOTQUIET, _("Skipping directory `%s'.\n"), escnonprint (f->name)); break; case FT_PLAINFILE: /* Call the retrieve loop. */ if (dlthis) err = ftp_loop_internal (u, f, con); break; case FT_UNKNOWN: logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"), escnonprint (f->name)); break; } /* switch */ /* Set the time-stamp information to the local file. Symlinks are not to be stamped because it sets the stamp on the original. :( */ if (!(f->type == FT_SYMLINK && !opt.retr_symlinks) && f->tstamp != -1 && dlthis && file_exists_p (con->target)) { /* #### This code repeats in http.c and ftp.c. Move it to a function! */ const char *fl = NULL; if (opt.output_document) { if (output_stream_regular) fl = opt.output_document; } else fl = con->target; if (fl) touch (fl, f->tstamp); } else if (f->tstamp == -1) logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target); if (f->perms && f->type == FT_PLAINFILE && dlthis) { if (opt.preserve_perm) chmod (con->target, f->perms); } else DEBUGP (("Unrecognized permissions for %s.\n", con->target)); xfree (con->target); con->target = old_target; url_set_file (u, ofile); xfree (ofile); /* Break on fatals. */ if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR) break; con->cmd &= ~ (DO_CWD | DO_LOGIN); f = f->next; } /* We do not want to call ftp_retrieve_dirs here */ if (opt.recursive && !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel)) err = ftp_retrieve_dirs (u, orig, con); else if (opt.recursive) DEBUGP ((_("Will not retrieve dirs since depth is %d (max %d).\n"), depth, opt.reclevel)); --depth; return err;}/* Retrieve the directories given in a file list. This function works by simply going through the linked list and calling ftp_retrieve_glob on each directory entry. The function knows about excluded directories. */static uerr_tftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con){ char *container = NULL; int container_size = 0; for (; f; f = f->next) { int size; char *odir, *newdir; if (opt.quota && total_downloaded_bytes > opt.quota) break; if (f->type != FT_DIRECTORY) continue; /* Allocate u->dir off stack, but reallocate only if a larger string is needed. It's a pity there's no "realloca" for an item on the bottom of the stack. */ size = strlen (u->dir) + 1 + strlen (f->name) + 1; if (size > container_size) container = (char *)alloca (size); newdir = container; odir = u->dir; if (*odir == '\0' || (*odir == '/' && *(odir + 1) == '\0')) /* If ODIR is empty or just "/", simply append f->name to ODIR. (In the former case, to preserve u->dir being relative; in the latter case, to avoid double slash.) */ sprintf (newdir, "%s%s", odir, f->name); else /* Else, use a separator. */ sprintf (newdir, "%s/%s", odir, f->name); DEBUGP (("Composing new CWD relative to the initial directory.\n")); DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n", odir, f->name, newdir)); if (!accdir (newdir)) { logprintf (LOG_VERBOSE, _("\Not descending to `%s' as it is excluded/not-included.\n"), escnonprint (newdir)); continue; } con->st &= ~DONE_CWD; odir = xstrdup (u->dir); /* because url_set_dir will free u->dir. */ url_set_dir (u, newdir); ftp_retrieve_glob (u, con, GLOB_GETALL); url_set_dir (u, odir); xfree (odir); /* Set the time-stamp? */ } if (opt.quota && total_downloaded_bytes > opt.quota) return QUOTEXC; else return RETROK;}/* Return true if S has a leading '/' or contains '../' */static boolhas_insecure_name_p (const char *s){ if (*s == '/') return true; if (strstr (s, "../") != 0) return true; return false;}/* A near-top-level function to retrieve the files in a directory. The function calls ftp_get_listing, to get a linked list of files. Then it weeds out the file names that do not match the pattern. ftp_retrieve_list is called with this updated list as an argument. If the argument ACTION is GLOB_GETONE, just download the file (but first get the listing, so that the time-stamp is heeded); if it's GLOB_GLOBALL, use globbing; if it's GLOB_GETALL, download the whole directory. */static uerr_tftp_retrieve_glob (struct url *u, ccon *con, int action){ struct fileinfo *f, *start; uerr_t res; con->cmd |= LEAVE_PENDING; res = ftp_get_listing (u, con, &start); if (res != RETROK) return res; /* First: weed out that do not conform the global rules given in opt.accepts and opt.rejects. */ if (opt.accepts || opt.rejects) { f = start; while (f) { if (f->type != FT_DIRECTORY && !acceptable (f->name)) { logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), escnonprint (f->name)); f = delelement (f, &start); } else f = f->next; } } /* Remove all files with possible harmful names */ f = start; while (f) { if (has_insecure_name_p (f->name)) { logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), escnonprint (f->name)); f = delelement (f, &start); } else f = f->next; } /* Now weed out the files that do not match our globbing pattern. If we are dealing with a globbing pattern, that is. */ if (*u->file && (action == GLOB_GLOBALL || action == GLOB_GETONE)) { int (*matcher) (const char *, const char *, int) = opt.ignore_case ? fnmatch_nocase : fnmatch; int matchres = 0; f = start; while (f) { matchres = matcher (u->file, f->name, 0); if (matchres == -1) { logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno)); break; } if (matchres == FNM_NOMATCH) f = delelement (f, &start); /* delete the element from the list */ else f = f->next; /* leave the element in the list */ } if (matchres == -1) { freefileinfo (start); return RETRBADPATTERN; } } if (start) { /* Just get everything. */ ftp_retrieve_list (u, start, con); } else if (!start) { if (action == GLOB_GLOBALL) { /* No luck. */ /* #### This message SUCKS. We should see what was the reason that nothing was retrieved. */ logprintf (LOG_VERBOSE, _("No matches on pattern `%s'.\n"), escnonprint (u->file)); } else /* GLOB_GETONE or GLOB_GETALL */ { /* Let's try retrieving it anyway. */ con->st |= ON_YOUR_OWN; res = ftp_loop_internal (u, NULL, con); return res; } } freefileinfo (start); if (opt.quota && total_downloaded_bytes > opt.quota) return QUOTEXC; else /* #### Should we return `res' here? */ return RETROK;}/* The wrapper that calls an appropriate routine according to contents of URL. Inherently, its capabilities are limited on what can be encoded into a URL. */uerr_tftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob){ ccon con; /* FTP connection */ uerr_t res; *dt = 0; xzero (con); con.csock = -1; con.st = ON_YOUR_OWN; con.rs = ST_UNIX; con.id = NULL; con.proxy = proxy; /* If the file name is empty, the user probably wants a directory index. We'll provide one, properly HTML-ized. Unless opt.htmlify is 0, of course. :-) */ if (!*u->file && !recursive) { struct fileinfo *f; res = ftp_get_listing (u, &con, &f); if (res == RETROK) { if (opt.htmlify && !opt.spider) { char *filename = (opt.output_document ? xstrdup (opt.output_document) : (con.target ? xstrdup (con.target) : url_file_name (u))); res = ftp_index (filename, u, f); if (res == FTPOK && opt.verbose) { if (!opt.output_document) { struct_stat st; wgint sz; if (stat (filename, &st) == 0) sz = st.st_size; else sz = -1; logprintf (LOG_NOTQUIET, _("Wrote HTML-ized index to `%s' [%s].\n"), filename, number_to_static_string (sz)); } else logprintf (LOG_NOTQUIET, _("Wrote HTML-ized index to `%s'.\n"), filename); } xfree (filename); } freefileinfo (f); } } else { bool ispattern = false; if (glob) { /* Treat the URL as a pattern if the file name part of the URL path contains wildcards. (Don't check for u->file because it is unescaped and therefore doesn't leave users the option to escape literal '*' as %2A.) */ char *file_part = strrchr (u->path, '/'); if (!file_part) file_part = u->path; ispattern = has_wildcards_p (file_part); } if (ispattern || recursive || opt.timestamping) { /* ftp_retrieve_glob is a catch-all function that gets called if we need globbing, time-stamping or recursion. Its third argument is just what we really need. */ res = ftp_retrieve_glob (u, &con, ispattern ? GLOB_GLOBALL : GLOB_GETONE); } else res = ftp_loop_internal (u, NULL, &con); } if (res == FTPOK) res = RETROK; if (res == RETROK) *dt |= RETROKF; /* If a connection was left, quench it. */ if (con.csock != -1) fd_close (con.csock); xfree_null (con.id); con.id = NULL; xfree_null (con.target); con.target = NULL; return res;}/* Delete an element from the fileinfo linked list. Returns the address of the next element, or NULL if the list is exhausted. It can modify the start of the list. */static struct fileinfo *delelement (struct fileinfo *f, struct fileinfo **start){ struct fileinfo *prev = f->prev; struct fileinfo *next = f->next; xfree (f->name); xfree_null (f->linkto); xfree (f); if (next) next->prev = prev; if (prev) prev->next = next; else *start = next; return next;}/* Free the fileinfo linked list of files. */static voidfreefileinfo (struct fileinfo *f){ while (f) { struct fileinfo *next = f->next; xfree (f->name); if (f->linkto) xfree (f->linkto); xfree (f); f = next; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -