📄 ftp.c
字号:
ftpListParts *p = NULL; char *t = NULL; const char *ct = NULL; char *tokens[MAX_TOKENS]; int i; int n_tokens; static char sbuf[128]; char *xbuf = NULL; if (buf == NULL) return NULL; if (*buf == '\0') return NULL; p = xcalloc(1, sizeof(ftpListParts)); n_tokens = 0; for (i = 0; i < MAX_TOKENS; i++) tokens[i] = (char *) NULL; xbuf = xstrdup(buf); if (flags.tried_nlst) { /* Machine readable format, one name per line */ p->name = xbuf; p->type = '\0'; return p; } for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space)) tokens[n_tokens++] = xstrdup(t); xfree(xbuf); /* locate the Month field */ for (i = 3; i < n_tokens - 2; i++) { if (!is_month(tokens[i])) /* Month */ continue; if (!sscanf(tokens[i - 1], SCAN_FTP1, sbuf)) /* Size */ continue; if (!sscanf(tokens[i + 1], SCAN_FTP1, sbuf)) /* Day */ continue; if (!sscanf(tokens[i + 2], SCAN_FTP2, sbuf)) /* Yr | hh:mm */ continue; p->type = *tokens[0]; p->size = atoi(tokens[i - 1]); snprintf(sbuf, 128, "%s %2s %5s", tokens[i], tokens[i + 1], tokens[i + 2]); if (!strstr(buf, sbuf)) snprintf(sbuf, 128, "%s %2s %-5s", tokens[i], tokens[i + 1], tokens[i + 2]); if ((t = strstr(buf, sbuf))) { p->date = xstrdup(sbuf); if (flags.skip_whitespace) { t += strlen(sbuf); while (strchr(w_space, *t)) t++; } else { /* XXX assumes a single space between date and filename * suggested by: Nathan.Bailey@cc.monash.edu.au and * Mike Battersby <mike@starbug.bofh.asn.au> */ t += strlen(sbuf) + 1; } p->name = xstrdup(t); if ((t = strstr(p->name, " -> "))) { *t = '\0'; p->link = xstrdup(t + 4); } } break; } /* try it as a DOS listing */ if (n_tokens > 3 && p->name == NULL && sscanf(tokens[0], SCAN_FTP3, sbuf, sbuf, sbuf) == 3 && /* 04-05-70 */ sscanf(tokens[1], SCAN_FTP4, sbuf, sbuf, sbuf, sbuf) == 4) { /* 09:33PM */ if (!strcasecmp(tokens[2], "<dir>")) { p->type = 'd'; } else { p->type = '-'; p->size = atoi(tokens[2]); } snprintf(sbuf, 128, "%s %s", tokens[0], tokens[1]); p->date = xstrdup(sbuf); if (p->type == 'd') { /* Directory.. name begins with first printable after <dir> */ ct = strstr(buf, tokens[2]); ct += strlen(tokens[2]); while (xisspace(*ct)) ct++; if (!*ct) ct = NULL; } else { /* A file. Name begins after size, with a space in between */ snprintf(sbuf, 128, " %s %s", tokens[2], tokens[3]); ct = strstr(buf, sbuf); if (ct) { ct += strlen(tokens[2]) + 2; } } p->name = xstrdup(ct ? ct : tokens[3]); } /* Try EPLF format; carson@lehman.com */ if (p->name == NULL && buf[0] == '+') { ct = buf + 1; p->type = 0; while (ct && *ct) { switch (*ct) { case '\t': sscanf(ct + 1, "%[^,]", sbuf); p->name = xstrdup(sbuf); break; case 's': sscanf(ct + 1, "%d", &(p->size)); break; case 'm': sscanf(ct + 1, "%d", &i); p->date = xstrdup(ctime((time_t *) & i)); *(strstr(p->date, "\n")) = '\0'; break; case '/': p->type = 'd'; break; case 'r': p->type = '-'; break; case 'i': break; default: break; } ct = strstr(ct, ","); if (ct) { ct++; } } if (p->type == 0) { p->type = '-'; } } for (i = 0; i < n_tokens; i++) xfree(tokens[i]); if (p->name == NULL) ftpListPartsFree(&p); return p;}static const char *dots_fill(size_t len){ static char buf[256]; int i = 0; if (len > Config.Ftp.list_width) { memset(buf, ' ', 256); buf[0] = '\n'; buf[Config.Ftp.list_width + 4] = '\0'; return buf; } for (i = (int) len; i < Config.Ftp.list_width; i++) buf[i - len] = (i % 2) ? '.' : ' '; buf[i - len] = '\0'; return buf;}static char *ftpHtmlifyListEntry(char *line, FtpStateData * ftpState){ LOCAL_ARRAY(char, icon, 2048); LOCAL_ARRAY(char, href, 2048 + 40); LOCAL_ARRAY(char, text, 2048); LOCAL_ARRAY(char, size, 2048); LOCAL_ARRAY(char, chdir, 2048 + 40); LOCAL_ARRAY(char, view, 2048 + 40); LOCAL_ARRAY(char, download, 2048 + 40); LOCAL_ARRAY(char, link, 2048 + 40); LOCAL_ARRAY(char, html, 8192); size_t width = Config.Ftp.list_width; ftpListParts *parts; *icon = *href = *text = *size = *chdir = *view = *download = *link = *html = '\0'; if ((int) strlen(line) > 1024) { snprintf(html, 8192, "%s\n", line); return html; } /* Handle builtin <dirup> */ if (!strcmp(line, "<internal-dirup>")) { /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> {link} */ snprintf(icon, 2048, "<IMG BORDER=0 SRC=\"%s\" ALT=\"%-6s\">", mimeGetIconURL("internal-dirup"), "[DIRUP]"); if (!ftpState->flags.no_dotdot && !ftpState->flags.root_dir) { /* Normal directory */ strcpy(href, "../"); strcpy(text, "Parent Directory"); } else if (!ftpState->flags.no_dotdot && ftpState->flags.root_dir) { /* "Top level" directory */ strcpy(href, "%2e%2e/"); strcpy(text, "Parent Directory"); snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)", "%2f/", "Root Directory"); } else if (ftpState->flags.no_dotdot && !ftpState->flags.root_dir) { /* Normal directory where last component is / or .. */ strcpy(href, "%2e%2e/"); strcpy(text, "Parent Directory"); snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)", "../", "Back"); } else { /* NO_DOTDOT && ROOT_DIR */ /* "UNIX Root" directory */ strcpy(href, "../"); strcpy(text, "Home Directory"); } snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A> %s\n", href, icon, href, text, link); return html; } if ((parts = ftpListParseParts(line, ftpState->flags)) == NULL) { char *p; snprintf(html, 8192, "%s\n", line); for (p = line; *p && xisspace(*p); p++); if (*p && !xisspace(*p)) ftpState->flags.listformat_unknown = 1; return html; } if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) { *html = '\0'; ftpListPartsFree(&parts); return html; } parts->size += 1023; parts->size >>= 10; parts->showname = xstrdup(parts->name); if (!Config.Ftp.list_wrap) { if (strlen(parts->showname) > width - 1) { *(parts->showname + width - 1) = '>'; *(parts->showname + width - 0) = '\0'; } } /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */ xstrncpy(href, rfc1738_escape(parts->name), 2048); xstrncpy(text, parts->showname, 2048); switch (parts->type) { case 'd': snprintf(icon, 2048, "<IMG BORDER=0 SRC=\"%s\" ALT=\"%-6s\">", mimeGetIconURL("internal-dir"), "[DIR]"); strncat(href, "/", 2048); break; case 'l': snprintf(icon, 2048, "<IMG BORDER=0 SRC=\"%s\" ALT=\"%-6s\">", mimeGetIconURL("internal-link"), "[LINK]"); /* sometimes there is an 'l' flag, but no "->" link */ if (parts->link) snprintf(link, 2048, " -> <A HREF=\"%s\">%s</A>", rfc1738_escape(parts->link), parts->link); break; case '\0': snprintf(icon, 2048, "<IMG BORDER=0 SRC=\"%s\" ALT=\"%-6s\">", mimeGetIconURL(parts->name), "[UNKNOWN]"); snprintf(chdir, 2048, " <A HREF=\"%s/;type=d\"><IMG BORDER=0 SRC=\"%s\" " "ALT=\"[DIR]\"></A>", rfc1738_escape(parts->name), mimeGetIconURL("internal-dir")); break; case '-': default: snprintf(icon, 2048, "<IMG BORDER=0 SRC=\"%s\" ALT=\"%-6s\">", mimeGetIconURL(parts->name), "[FILE]"); snprintf(size, 2048, " %6dk", parts->size); break; } if (parts->type != 'd') { if (mimeGetViewOption(parts->name)) { snprintf(view, 2048, " <A HREF=\"%s;type=a\"><IMG BORDER=0 SRC=\"%s\" " "ALT=\"[VIEW]\"></A>", href, mimeGetIconURL("internal-view")); } if (mimeGetDownloadOption(parts->name)) { snprintf(download, 2048, " <A HREF=\"%s;type=i\"><IMG BORDER=0 SRC=\"%s\" " "ALT=\"[DOWNLOAD]\"></A>", href, mimeGetIconURL("internal-download")); } } /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> . . . {date}{size}{chdir}{view}{download}{link}\n */ if (parts->type != '\0') { snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s " "%s%8s%s%s%s%s\n", href, icon, href, text, dots_fill(strlen(text)), parts->date, size, chdir, view, download, link); } else { /* Plain listing. {icon} {text} ... {chdir}{view}{download} */ snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s " "%s%s%s%s\n", href, icon, href, text, dots_fill(strlen(text)), chdir, view, download, link); } ftpListPartsFree(&parts); return html;}static voidftpParseListing(FtpStateData * ftpState){ char *buf = ftpState->data.buf; char *sbuf; /* NULL-terminated copy of buf */ char *end; char *line; char *s; char *t; size_t linelen; size_t usable; StoreEntry *e = ftpState->entry; int len = ftpState->data.offset; /* * We need a NULL-terminated buffer for scanning, ick */ sbuf = xmalloc(len + 1); xstrncpy(sbuf, buf, len + 1); end = sbuf + len - 1; while (*end != '\r' && *end != '\n' && end > sbuf) end--; usable = end - sbuf; debug(9, 3) ("ftpParseListing: usable = %d\n", usable); if (usable == 0) { debug(9, 3) ("ftpParseListing: didn't find end for %s\n", storeUrl(e)); xfree(sbuf); return; } debug(9, 3) ("ftpParseListing: %d bytes to play with\n", len); line = memAllocate(MEM_4K_BUF); end++; storeBuffer(e); s = sbuf; s += strspn(s, crlf); for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) { debug(9, 3) ("ftpParseListing: s = {%s}\n", s); linelen = strcspn(s, crlf) + 1; if (linelen < 2) break; if (linelen > 4096) linelen = 4096; xstrncpy(line, s, linelen); debug(9, 7) ("ftpParseListing: {%s}\n", line); if (!strncmp(line, "total", 5)) continue; t = ftpHtmlifyListEntry(line, ftpState); assert(t != NULL); storeAppend(e, t, strlen(t)); } storeBufferFlush(e); assert(usable <= len); if (usable < len) { /* must copy partial line to beginning of buf */ linelen = len - usable; if (linelen > 4096) linelen = 4096; xstrncpy(line, end, linelen); xstrncpy(ftpState->data.buf, line, ftpState->data.size); ftpState->data.offset = strlen(ftpState->data.buf); } memFree(line, MEM_4K_BUF); xfree(sbuf);}static voidftpReadComplete(FtpStateData * ftpState){ debug(9, 3) ("ftpReadComplete\n"); /* Connection closed; retrieval done. */ if (ftpState->flags.html_header_sent) ftpListingFinish(ftpState); if (!ftpState->flags.put) { storeTimestampsSet(ftpState->entry); fwdComplete(ftpState->fwd); } /* expect the "transfer complete" message on the control socket */ ftpScheduleReadControlReply(ftpState, 1);}static voidftpDataRead(int fd, void *data){ FtpStateData *ftpState = data; int len; int j; int bin; StoreEntry *entry = ftpState->entry; MemObject *mem = entry->mem_obj; size_t read_sz;#if DELAY_POOLS delay_id delay_id = delayMostBytesAllowed(mem);#endif assert(fd == ftpState->data.fd); if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { comm_close(ftpState->ctrl.fd); return; } errno = 0; read_sz = ftpState->data.size - ftpState->data.offset;#if DELAY_POOLS read_sz = delayBytesWanted(delay_id, 1, read_sz);#endif memset(ftpState->data.buf + ftpState->data.offset, '\0', read_sz); Counter.syscalls.sock.reads++; len = read(fd, ftpState->data.buf + ftpState->data.offset, read_sz); if (len > 0) { fd_bytes(fd, len, FD_READ);#if DELAY_POOLS delayBytesIn(delay_id, len);#endif kb_incr(&Counter.server.all.kbytes_in, len); kb_incr(&Counter.server.ftp.kbytes_in, len); ftpState->data.offset += len; } debug(9, 5) ("ftpDataRead: FD %d, Read %d bytes\n", fd, len); if (len > 0) { IOStats.Ftp.reads++; for (j = len - 1, bin = 0; j; bin++) j >>= 1; IOStats.Ftp.read_hist[bin]++; } if (ftpState->flags.isdir && !ftpState->flags.html_header_sent && len >= 0) { ftpListingStart(ftpState); } if (len < 0) { debug(50, 1) ("ftpDataRead: read error: %s\n", xstrerror()); if (ignoreErrno(errno)) { commSetSelect(fd, COMM_SELECT_READ, ftpDataRead, data, Config.Timeout.read); } else { assert(mem->inmem_hi > 0); ftpDataTransferDone(ftpState); } } else if (len == 0) { ftpReadComplete(ftpState); } else { if (ftpState->flags.isdir) { ftpParseListing(ftpState); } else { storeAppend(entry, ftpState->data.buf, len); ftpState->data.offset = 0; } if (ftpState->size > 0 && mem->inmem_hi >= ftpState->size + mem->reply->hdr_sz) ftpReadComplete(ftpState); else commSetSelect(fd, COMM_SELECT_READ, ftpDataRead, data, Config.Timeout.read); }}/* * ftpCheckAuth * * Return 1 if we have everything needed to complete this request. * Return 0 if something is missing. */static intftpCheckAuth(FtpStateData * ftpState, const HttpHeader * req_hdr){ char *orig_user; const char *auth; ftpLoginParser(ftpState->request->login, ftpState, FTP_LOGIN_ESCAPED); if (ftpState->user[0] && ftpState->password[0]) return 1; /* name and passwd both in URL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -