📄 ftp.c
字号:
ftpRestOrList(FtpStateData * ftpState){ debug(9, 3) ("This is ftpRestOrList\n"); if (ftpState->flags.put) { debug(9, 3) ("ftpRestOrList: Sending STOR request...\n"); ftpSendStor(ftpState); } else if (ftpState->typecode == 'D') { /* XXX This should NOT be here */ ftpSendNlst(ftpState); /* sec 3.2.2 of RFC 1738 */ ftpState->flags.isdir = 1; ftpState->flags.use_base = 1; } else if (ftpState->flags.isdir) ftpSendList(ftpState); else if (ftpRestartable(ftpState)) ftpSendRest(ftpState); else ftpSendRetr(ftpState);}static voidftpSendStor(FtpStateData * ftpState){ assert(ftpState->filepath != NULL); snprintf(cbuf, 1024, "STOR %s\r\n", ftpState->filepath); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_STOR;}static voidftpReadStor(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadStor\n"); if (code >= 100 && code < 200) { /* * Cancel the timeout on the Control socket, pumpStart will * establish one on the data socket. */ commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); ftpPutStart(ftpState); debug(9, 3) ("ftpReadStor: writing data channel\n"); ftpState->state = WRITING_DATA; } else if (code == 553) { /* directory does not exist, have to create, sigh */#if WORK_IN_PROGRESS ftpTraverseDirectory(ftpState);#endif ftpSendReply(ftpState); } else { debug(9, 3) ("ftpReadStor: that's all folks\n"); ftpSendReply(ftpState); }}static voidftpSendRest(FtpStateData * ftpState){ snprintf(cbuf, 1024, "REST %d\r\n", ftpState->restart_offset); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_REST;}static intftpRestartable(FtpStateData * ftpState){ if (ftpState->restart_offset > 0) return 1; if (!ftpState->request->range) return 0; if (!ftpState->flags.binary) return 0; if (ftpState->size <= 0) return 0; ftpState->restart_offset = httpHdrRangeLowestOffset(ftpState->request->range, (size_t) ftpState->size); if (ftpState->restart_offset <= 0) return 0; return 1;}static voidftpReadRest(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadRest\n"); assert(ftpState->restart_offset > 0); if (code == 350) { ftpState->restarted_offset = ftpState->restart_offset; ftpSendRetr(ftpState); } else if (code > 0) { debug(9, 3) ("ftpReadRest: REST not supported\n"); ftpState->flags.rest_supported = 0; ftpSendRetr(ftpState); } else { ftpFail(ftpState); }}static voidftpSendList(FtpStateData * ftpState){ if (ftpState->filepath) { ftpState->flags.use_base = 1; snprintf(cbuf, 1024, "LIST %s\r\n", ftpState->filepath); } else { snprintf(cbuf, 1024, "LIST\r\n"); } ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_LIST;}static voidftpSendNlst(FtpStateData * ftpState){ ftpState->flags.tried_nlst = 1; if (ftpState->filepath) { ftpState->flags.use_base = 1; snprintf(cbuf, 1024, "NLST %s\r\n", ftpState->filepath); } else { snprintf(cbuf, 1024, "NLST\r\n"); } ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_NLST;}static voidftpReadList(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadList\n"); if (code == 125 || (code == 150 && ftpState->data.host)) { /* Begin data transfer */ ftpAppendSuccessHeader(ftpState); commSetSelect(ftpState->data.fd, COMM_SELECT_READ, ftpDataRead, ftpState, Config.Timeout.read); commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry); ftpState->state = READING_DATA; /* * Cancel the timeout on the Control socket and establish one * on the data socket */ commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); return; } else if (code == 150) { /* Accept data channel */ commSetSelect(ftpState->data.fd, COMM_SELECT_READ, ftpAcceptDataConnection, ftpState, 0); /* * Cancel the timeout on the Control socket and establish one * on the data socket */ commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); return; } else if (!ftpState->flags.tried_nlst && code > 300) { ftpSendNlst(ftpState); } else { ftpFail(ftpState); return; }}static voidftpSendRetr(FtpStateData * ftpState){ assert(ftpState->filepath != NULL); snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_RETR;}static voidftpReadRetr(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadRetr\n"); if (code == 125 || (code == 150 && ftpState->data.host)) { /* Begin data transfer */ debug(9, 3) ("ftpReadRetr: reading data channel\n"); ftpAppendSuccessHeader(ftpState); commSetSelect(ftpState->data.fd, COMM_SELECT_READ, ftpDataRead, ftpState, Config.Timeout.read); commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry); ftpState->state = READING_DATA; /* * Cancel the timeout on the Control socket and establish one * on the data socket */ commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); } else if (code == 150) { /* Accept data channel */ commSetSelect(ftpState->data.fd, COMM_SELECT_READ, ftpAcceptDataConnection, ftpState, 0); /* * Cancel the timeout on the Control socket and establish one * on the data socket */ commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); } else if (code >= 300) { if (!ftpState->flags.try_slash_hack) { /* Try this as a directory missing trailing slash... */ ftpHackShortcut(ftpState, ftpSendCwd); } else { ftpFail(ftpState); } } else { ftpFail(ftpState); }}static voidftpReadTransferDone(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadTransferDone\n"); if (code != 226) { debug(9, 1) ("ftpReadTransferDone: Got code %d after reading data\n", code); debug(9, 1) ("--> releasing '%s'\n", storeUrl(ftpState->entry)); storeReleaseRequest(ftpState->entry); } ftpDataTransferDone(ftpState);}static voidftpDataTransferDone(FtpStateData * ftpState){ debug(9, 3) ("This is ftpDataTransferDone\n"); if (ftpState->data.fd > -1) { comm_close(ftpState->data.fd); ftpState->data.fd = -1; } ftpSendQuit(ftpState);}static voidftpSendQuit(FtpStateData * ftpState){ assert(ftpState->ctrl.fd > -1); snprintf(cbuf, 1024, "QUIT\r\n"); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_QUIT;}static voidftpReadQuit(FtpStateData * ftpState){ comm_close(ftpState->ctrl.fd);}static voidftpTrySlashHack(FtpStateData * ftpState){ char *path; ftpState->flags.try_slash_hack = 1; /* Free old paths */ if (ftpState->pathcomps) wordlistDestroy(&ftpState->pathcomps); safe_free(ftpState->filepath); /* Build the new path (urlpath begins with /) */ path = xstrdup(strBuf(ftpState->request->urlpath)); rfc1738_unescape(path); ftpState->filepath = path; /* And off we go */ ftpGetFile(ftpState);}static voidftpTryDatachannelHack(FtpStateData * ftpState){ ftpState->flags.datachannel_hack = 1; /* we have to undo some of the slash hack... */ if (ftpState->old_filepath != NULL) { ftpState->flags.try_slash_hack = 0; safe_free(ftpState->filepath); ftpState->filepath = ftpState->old_filepath; ftpState->old_filepath = NULL; } ftpState->flags.tried_nlst = 0; /* And off we go */ if (ftpState->flags.isdir) { ftpListDir(ftpState); } else { ftpGetFile(ftpState); } return;}/* Forget hack status. Next error is shown to the user */static voidftpUnhack(FtpStateData * ftpState){ if (ftpState->old_request != NULL) { safe_free(ftpState->old_request); safe_free(ftpState->old_reply); }}static voidftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState){ /* Clear some unwanted state */ ftpState->restarted_offset = 0; ftpState->restart_offset = 0; /* Save old error message & some state info */ if (ftpState->old_request == NULL) { ftpState->old_request = ftpState->ctrl.last_command; ftpState->ctrl.last_command = NULL; ftpState->old_reply = ftpState->ctrl.last_reply; ftpState->ctrl.last_reply = NULL; if (ftpState->pathcomps == NULL && ftpState->filepath != NULL) ftpState->old_filepath = xstrdup(ftpState->filepath); } /* Jump to the "hack" state */ nextState(ftpState);}static voidftpFail(FtpStateData * ftpState){ ErrorState *err; debug(9, 3) ("ftpFail\n"); /* Try the / hack to support "Netscape" FTP URL's for retreiving files */ if (!ftpState->flags.isdir && /* Not a directory */ !ftpState->flags.try_slash_hack && /* Not in slash hack */ ftpState->mdtm <= 0 && ftpState->size < 0 && /* Not known as a file */ strNCaseCmp(ftpState->request->urlpath, "/%2f", 4) != 0) { /* No slash encoded */ switch (ftpState->state) { case SENT_CWD: case SENT_RETR: /* Try the / hack */ ftpHackShortcut(ftpState, ftpTrySlashHack); return; default: break; } } /* Try to reopen datachannel */ if (!ftpState->flags.datachannel_hack && ftpState->pathcomps == NULL) { switch (ftpState->state) { case SENT_RETR: case SENT_LIST: case SENT_NLST: /* Try to reopen datachannel */ ftpHackShortcut(ftpState, ftpTryDatachannelHack); return; default: break; } } /* Translate FTP errors into HTTP errors */ err = NULL; switch (ftpState->state) { case SENT_USER: case SENT_PASS: if (ftpState->ctrl.replycode > 500) err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN); else if (ftpState->ctrl.replycode == 421) err = errorCon(ERR_FTP_UNAVAILABLE, HTTP_SERVICE_UNAVAILABLE); break; case SENT_CWD: case SENT_RETR: if (ftpState->ctrl.replycode == 550) err = errorCon(ERR_FTP_NOT_FOUND, HTTP_NOT_FOUND); break; default: break; } if (err == NULL) err = errorCon(ERR_FTP_FAILURE, HTTP_BAD_GATEWAY); err->request = requestLink(ftpState->request); err->ftp_server_msg = ftpState->ctrl.message; if (ftpState->old_request) err->ftp.request = ftpState->old_request; else err->ftp.request = ftpState->ctrl.last_command; if (err->ftp.request) { if (!strncmp(err->ftp.request, "PASS", 4)) err->ftp.request = "PASS <yourpassword>"; } if (ftpState->old_reply) err->ftp.reply = ftpState->old_reply; else err->ftp.reply = ftpState->ctrl.last_reply; errorAppendEntry(ftpState->entry, err); comm_close(ftpState->ctrl.fd);}voidftpPumpClosedData(int data_fd, void *data){ FtpStateData *ftpState = data; assert(data_fd == ftpState->data.fd); /* * Ugly pump module closed our server-side. Deal with it. * The data FD is already closed, so just set it to -1. */ ftpState->data.fd = -1; /* * Currently, thats all we have to do. Because the upload failed, * storeAbort() will be called on the reply entry. That will * call fwdAbort, which closes ftpState->ctrl.fd and then * ftpStateFree gets called. */}static voidftpPutStart(FtpStateData * ftpState){ debug(9, 3) ("ftpPutStart\n"); /* * sigh, we need this gross hack to detect when ugly pump module * aborts and wants to close the server-side. */ comm_add_close_handler(ftpState->data.fd, ftpPumpClosedData, ftpState); pumpStart(ftpState->data.fd, ftpState->fwd, ftpPutTransferDone, ftpState);}static voidftpPutTransferDone(int fd, char *bufnotused, size_t size, int errflag, void *data){ FtpStateData *ftpState = data; if (ftpState->data.fd >= 0) { comm_remove_close_handler(fd, ftpPumpClosedData, ftpState); comm_close(ftpState->data.fd); ftpState->data.fd = -1; } ftpReadComplete(ftpState);}static voidftpSendReply(FtpStateData * ftpState){ ErrorState *err; int code = ftpState->ctrl.replycode; http_status http_code; err_type err_code = ERR_NONE; debug(9, 5) ("ftpSendReply: %s, code %d\n", storeUrl(ftpState->entry), code); if (cbdataValid(ftpState)) debug(9, 5) ("ftpSendReply: ftpState (%p) is valid!\n", ftpState); if (code == 226) { err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED; http_code = (ftpState->mdtm > 0) ? HTTP_ACCEPTED : HTTP_CREATED; } else { err_code = ERR_FTP_PUT_ERROR; http_code = HTTP_INTERNAL_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -