ftp.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:67k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: ftp.c,v 1.280.2.2 1999/02/12 22:13:57 wessels Exp $
  3.  *
  4.  * DEBUG: section 9     File Transfer Protocol (FTP)
  5.  * AUTHOR: Harvest Derived
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. static const char *const crlf = "rn";
  36. static char cbuf[1024];
  37. typedef enum {
  38.     BEGIN,
  39.     SENT_USER,
  40.     SENT_PASS,
  41.     SENT_TYPE,
  42.     SENT_MDTM,
  43.     SENT_SIZE,
  44.     SENT_PORT,
  45.     SENT_PASV,
  46.     SENT_CWD,
  47.     SENT_LIST,
  48.     SENT_NLST,
  49.     SENT_REST,
  50.     SENT_RETR,
  51.     SENT_STOR,
  52.     SENT_QUIT,
  53.     READING_DATA,
  54.     WRITING_DATA,
  55.     SENT_MKDIR
  56. } ftp_state_t;
  57. struct _ftp_flags {
  58.     int isdir:1;
  59.     int pasv_supported:1;
  60.     int skip_whitespace:1;
  61.     int rest_supported:1;
  62.     int pasv_only:1;
  63.     int authenticated:1;
  64.     int http_header_sent:1;
  65.     int tried_nlst:1;
  66.     int use_base:1;
  67.     int root_dir:1;
  68.     int no_dotdot:1;
  69.     int html_header_sent:1;
  70.     int binary:1;
  71.     int try_slash_hack:1;
  72.     int put:1;
  73.     int put_mkdir:1;
  74.     int listformat_unknown:1;
  75.     int datachannel_hack:1;
  76. };
  77. typedef struct _Ftpdata {
  78.     StoreEntry *entry;
  79.     request_t *request;
  80.     char user[MAX_URL];
  81.     char password[MAX_URL];
  82.     char *reply_hdr;
  83.     int reply_hdr_state;
  84.     char *title_url;
  85.     int conn_att;
  86.     int login_att;
  87.     ftp_state_t state;
  88.     time_t mdtm;
  89.     int size;
  90.     wordlist *pathcomps;
  91.     char *filepath;
  92.     int restart_offset;
  93.     int restarted_offset;
  94.     int rest_att;
  95.     char *proxy_host;
  96.     size_t list_width;
  97.     wordlist *cwd_message;
  98.     char *old_request;
  99.     char *old_reply;
  100.     char *old_filepath;
  101.     char typecode;
  102.     struct {
  103. int fd;
  104. char *buf;
  105. size_t size;
  106. off_t offset;
  107. FREE *freefunc;
  108. wordlist *message;
  109. char *last_command;
  110. char *last_reply;
  111. int replycode;
  112.     } ctrl;
  113.     struct {
  114. int fd;
  115. char *buf;
  116. size_t size;
  117. off_t offset;
  118. FREE *freefunc;
  119. char *host;
  120. u_short port;
  121.     } data;
  122.     struct _ftp_flags flags;
  123.     FwdState *fwd;
  124. } FtpStateData;
  125. typedef struct {
  126.     char type;
  127.     int size;
  128.     char *date;
  129.     char *name;
  130.     char *showname;
  131.     char *link;
  132. } ftpListParts;
  133. typedef void (FTPSM) (FtpStateData *);
  134. #define FTP_LOGIN_ESCAPED 1
  135. #define FTP_LOGIN_NOT_ESCAPED 0
  136. /* Local functions */
  137. static CNCB ftpPasvCallback;
  138. static PF ftpDataRead;
  139. static PF ftpStateFree;
  140. static PF ftpTimeout;
  141. static PF ftpReadControlReply;
  142. static CWCB ftpWriteCommandCallback;
  143. static void ftpLoginParser(const char *, FtpStateData *, int escaped);
  144. static wordlist *ftpParseControlReply(char *, size_t, int *, int *);
  145. static int ftpRestartable(FtpStateData * ftpState);
  146. static void ftpAppendSuccessHeader(FtpStateData * ftpState);
  147. static void ftpAuthRequired(HttpReply * reply, request_t * request, const char *realm);
  148. static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState);
  149. static void ftpPutStart(FtpStateData *);
  150. static CWCB ftpPutTransferDone;
  151. static void ftpUnhack(FtpStateData * ftpState);
  152. static void ftpScheduleReadControlReply(FtpStateData *, int);
  153. static void ftpHandleControlReply(FtpStateData *);
  154. static char *ftpHtmlifyListEntry(char *line, FtpStateData * ftpState);
  155. /* State machine functions
  156.  * send == state transition
  157.  * read == wait for response, and select next state transition
  158.  * other == Transition logic
  159.  */
  160. static FTPSM ftpReadWelcome;
  161. static FTPSM ftpSendUser;
  162. static FTPSM ftpReadUser;
  163. static FTPSM ftpSendPass;
  164. static FTPSM ftpReadPass;
  165. static FTPSM ftpSendType;
  166. static FTPSM ftpReadType;
  167. static FTPSM ftpSendMdtm;
  168. static FTPSM ftpReadMdtm;
  169. static FTPSM ftpSendSize;
  170. static FTPSM ftpReadSize;
  171. static FTPSM ftpSendPort;
  172. static FTPSM ftpReadPort;
  173. static FTPSM ftpSendPasv;
  174. static FTPSM ftpReadPasv;
  175. static FTPSM ftpTraverseDirectory;
  176. static FTPSM ftpListDir;
  177. static FTPSM ftpGetFile;
  178. static FTPSM ftpSendCwd;
  179. static FTPSM ftpReadCwd;
  180. static FTPSM ftpSendList;
  181. static FTPSM ftpSendNlst;
  182. static FTPSM ftpReadList;
  183. static FTPSM ftpSendRest;
  184. static FTPSM ftpReadRest;
  185. static FTPSM ftpSendRetr;
  186. static FTPSM ftpReadRetr;
  187. static FTPSM ftpReadTransferDone;
  188. static FTPSM ftpSendQuit;
  189. static FTPSM ftpReadQuit;
  190. static FTPSM ftpFail;
  191. static FTPSM ftpDataTransferDone;
  192. static FTPSM ftpRestOrList;
  193. static FTPSM ftpSendStor;
  194. static FTPSM ftpReadStor;
  195. static FTPSM ftpSendReply;
  196. static FTPSM ftpTryMkdir;
  197. static FTPSM ftpReadMkdir;
  198. /************************************************
  199. ** State Machine Description (excluding hacks) **
  200. *************************************************
  201. From To
  202. ---------------------------------------
  203. Welcome User
  204. User Pass
  205. Pass Type
  206. Type TraverseDirectory / GetFile
  207. TraverseDirectory Cwd / GetFile / ListDir
  208. Cwd TraverseDirectory
  209. GetFile Mdtm
  210. Mdtm Size
  211. Size Pasv
  212. ListDir Pasv
  213. Pasv RestOrList
  214. RestOrList Rest / Retr / Nlst / List
  215. Rest Retr
  216. Retr / Nlst / List (ftpDataRead on datachannel)
  217. (ftpDataRead) ReadTransferDone
  218. ReadTransferDone DataTransferDone
  219. DataTransferDone Quit
  220. Quit -
  221. ************************************************/
  222. FTPSM *FTP_SM_FUNCS[] =
  223. {
  224.     ftpReadWelcome,
  225.     ftpReadUser,
  226.     ftpReadPass,
  227.     ftpReadType,
  228.     ftpReadMdtm,
  229.     ftpReadSize,
  230.     ftpReadPort,
  231.     ftpReadPasv,
  232.     ftpReadCwd,
  233.     ftpReadList, /* SENT_LIST */
  234.     ftpReadList, /* SENT_NLST */
  235.     ftpReadRest,
  236.     ftpReadRetr,
  237.     ftpReadStor,
  238.     ftpReadQuit,
  239.     ftpReadTransferDone,
  240.     ftpSendReply,
  241.     ftpReadMkdir
  242. };
  243. static void
  244. ftpStateFree(int fdnotused, void *data)
  245. {
  246.     FtpStateData *ftpState = data;
  247.     if (ftpState == NULL)
  248. return;
  249.     debug(9, 3) ("ftpStateFree: %sn", storeUrl(ftpState->entry));
  250.     storeUnregisterAbort(ftpState->entry);
  251.     storeUnlockObject(ftpState->entry);
  252.     if (ftpState->reply_hdr) {
  253. memFree(ftpState->reply_hdr, MEM_8K_BUF);
  254. ftpState->reply_hdr = NULL;
  255.     }
  256.     requestUnlink(ftpState->request);
  257.     if (ftpState->ctrl.buf)
  258. ftpState->ctrl.freefunc(ftpState->ctrl.buf);
  259.     if (ftpState->data.buf)
  260. ftpState->data.freefunc(ftpState->data.buf);
  261.     if (ftpState->pathcomps)
  262. wordlistDestroy(&ftpState->pathcomps);
  263.     if (ftpState->ctrl.message)
  264. wordlistDestroy(&ftpState->ctrl.message);
  265.     if (ftpState->cwd_message)
  266. wordlistDestroy(&ftpState->cwd_message);
  267.     safe_free(ftpState->ctrl.last_reply);
  268.     safe_free(ftpState->ctrl.last_command);
  269.     safe_free(ftpState->old_request);
  270.     safe_free(ftpState->old_reply);
  271.     safe_free(ftpState->old_filepath);
  272.     safe_free(ftpState->title_url);
  273.     safe_free(ftpState->filepath);
  274.     safe_free(ftpState->data.host);
  275.     if (ftpState->data.fd > -1) {
  276. comm_close(ftpState->data.fd);
  277. ftpState->data.fd = -1;
  278.     }
  279.     cbdataFree(ftpState);
  280. }
  281. static void
  282. ftpLoginParser(const char *login, FtpStateData * ftpState, int escaped)
  283. {
  284.     char *s = NULL;
  285.     xstrncpy(ftpState->user, login, MAX_URL);
  286.     if ((s = strchr(ftpState->user, ':'))) {
  287. *s = 0;
  288. xstrncpy(ftpState->password, s + 1, MAX_URL);
  289. if (escaped)
  290.     rfc1738_unescape(ftpState->password);
  291.     } else {
  292. xstrncpy(ftpState->password, null_string, MAX_URL);
  293.     }
  294.     if (escaped)
  295. rfc1738_unescape(ftpState->user);
  296.     if (ftpState->user[0] || ftpState->password[0])
  297. return;
  298.     xstrncpy(ftpState->user, "anonymous", MAX_URL);
  299.     xstrncpy(ftpState->password, Config.Ftp.anon_user, MAX_URL);
  300. }
  301. static void
  302. ftpTimeout(int fd, void *data)
  303. {
  304.     FtpStateData *ftpState = data;
  305.     StoreEntry *entry = ftpState->entry;
  306.     debug(9, 4) ("ftpTimeout: FD %d: '%s'n", fd, storeUrl(entry));
  307.     if (entry->store_status == STORE_PENDING) {
  308. if (entry->mem_obj->inmem_hi == 0) {
  309.     fwdFail(ftpState->fwd,
  310. errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT));
  311. }
  312.     }
  313.     if (ftpState->data.fd > -1) {
  314. comm_close(ftpState->data.fd);
  315. ftpState->data.fd = -1;
  316.     }
  317.     comm_close(ftpState->ctrl.fd);
  318.     /* don't modify ftpState here, it has been freed */
  319. }
  320. static void
  321. ftpListingStart(FtpStateData * ftpState)
  322. {
  323.     StoreEntry *e = ftpState->entry;
  324.     wordlist *w;
  325.     char *dirup;
  326.     int i, j, k;
  327.     storeBuffer(e);
  328.     storeAppendPrintf(e, "<!-- HTML listing generated by Squid %s -->n",
  329. version_string);
  330.     storeAppendPrintf(e, "<!-- %s -->n", mkrfc1123(squid_curtime));
  331.     storeAppendPrintf(e, "<HTML><HEAD><TITLE>n");
  332.     storeAppendPrintf(e, "FTP Directory: %sn",
  333. ftpState->title_url);
  334.     storeAppendPrintf(e, "</TITLE>n");
  335.     if (ftpState->flags.use_base)
  336. storeAppendPrintf(e, "<BASE HREF="%s">n",
  337.     ftpState->title_url);
  338.     storeAppendPrintf(e, "</HEAD><BODY>n");
  339.     if (ftpState->cwd_message) {
  340. storeAppendPrintf(e, "<PRE>n");
  341. for (w = ftpState->cwd_message; w; w = w->next)
  342.     storeAppendPrintf(e, "%sn", w->key);
  343. storeAppendPrintf(e, "</PRE>n");
  344. storeAppendPrintf(e, "<HR>n");
  345. wordlistDestroy(&ftpState->cwd_message);
  346.     }
  347.     storeAppendPrintf(e, "<H2>n");
  348.     storeAppendPrintf(e, "FTP Directory: ");
  349.     /* "ftp://" == 6 characters */
  350.     assert(strlen(ftpState->title_url) >= 6);
  351.     for (i = 6, j = 0; ftpState->title_url[i]; j = i) {
  352. storeAppendPrintf(e, "<A HREF="");
  353. i += strcspn(&ftpState->title_url[i], "/");
  354. if (ftpState->title_url[i] == '/')
  355.     i++;
  356. for (k = 0; k < i; k++)
  357.     storeAppendPrintf(e, "%c", ftpState->title_url[k]);
  358. storeAppendPrintf(e, "">");
  359. for (k = j; k < i - 1; k++)
  360.     storeAppendPrintf(e, "%c", ftpState->title_url[k]);
  361. if (ftpState->title_url[k] != '/')
  362.     storeAppendPrintf(e, "%c", ftpState->title_url[k++]);
  363. storeAppendPrintf(e, "</A>");
  364. if (k < i)
  365.     storeAppendPrintf(e, "%c", ftpState->title_url[k++]);
  366. if (i == j) {
  367.     /* Error guard, or "assert" */
  368.     storeAppendPrintf(e, "ERROR: Failed to parse URL: %sn",
  369. ftpState->title_url);
  370.     debug(9, 0) ("Failed to parse URL: %sn", ftpState->title_url);
  371.     break;
  372. }
  373.     }
  374.     storeAppendPrintf(e, "</H2>n");
  375.     storeAppendPrintf(e, "<PRE>n");
  376.     dirup = ftpHtmlifyListEntry("<internal-dirup>", ftpState);
  377.     storeAppend(e, dirup, strlen(dirup));
  378.     storeBufferFlush(e);
  379.     ftpState->flags.html_header_sent = 1;
  380. }
  381. static void
  382. ftpListingFinish(FtpStateData * ftpState)
  383. {
  384.     StoreEntry *e = ftpState->entry;
  385.     storeBuffer(e);
  386.     storeAppendPrintf(e, "</PRE>n");
  387.     if (ftpState->flags.listformat_unknown && !ftpState->flags.tried_nlst) {
  388. storeAppendPrintf(e, "<A HREF="./;type=d">[As plain directory]</A>n");
  389.     } else if (ftpState->typecode == 'D') {
  390. storeAppendPrintf(e, "<A HREF="./">[As extended directory]</A>n");
  391.     }
  392.     storeAppendPrintf(e, "<HR>n");
  393.     storeAppendPrintf(e, "<ADDRESS>n");
  394.     storeAppendPrintf(e, "Generated %s by %s (<a href="http://squid.nlanr.net/Squid/">%s</a>)n",
  395. mkrfc1123(squid_curtime),
  396. getMyHostname(),
  397. full_appname_string,
  398. version_string);
  399.     storeAppendPrintf(e, "</ADDRESS></BODY></HTML>n");
  400.     storeBufferFlush(e);
  401. }
  402. static const char *Month[] =
  403. {
  404.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  405.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  406. };
  407. static int
  408. is_month(const char *buf)
  409. {
  410.     int i;
  411.     for (i = 0; i < 12; i++)
  412. if (!strcasecmp(buf, Month[i]))
  413.     return 1;
  414.     return 0;
  415. }
  416. static void
  417. ftpListPartsFree(ftpListParts ** parts)
  418. {
  419.     safe_free((*parts)->date);
  420.     safe_free((*parts)->name);
  421.     safe_free((*parts)->showname);
  422.     safe_free((*parts)->link);
  423.     safe_free(*parts);
  424. }
  425. #define MAX_TOKENS 64
  426. #define SCAN_FTP1       "%[0123456789]"
  427. #define SCAN_FTP2       "%[0123456789:]"
  428. #define SCAN_FTP3       "%[0123456789]-%[0123456789]-%[0123456789]"
  429. #define SCAN_FTP4       "%[0123456789]:%[0123456789]%[AaPp]%[Mm]"
  430. static ftpListParts *
  431. ftpListParseParts(const char *buf, struct _ftp_flags flags)
  432. {
  433.     ftpListParts *p = NULL;
  434.     char *t = NULL;
  435.     const char *ct = NULL;
  436.     char *tokens[MAX_TOKENS];
  437.     int i;
  438.     int n_tokens;
  439.     static char sbuf[128];
  440.     char *xbuf = NULL;
  441.     if (buf == NULL)
  442. return NULL;
  443.     if (*buf == '')
  444. return NULL;
  445.     p = xcalloc(1, sizeof(ftpListParts));
  446.     n_tokens = 0;
  447.     for (i = 0; i < MAX_TOKENS; i++)
  448. tokens[i] = (char *) NULL;
  449.     xbuf = xstrdup(buf);
  450.     if (flags.tried_nlst) {
  451. /* Machine readable format, one name per line */
  452. p->name = xbuf;
  453. p->type = '';
  454. return p;
  455.     }
  456.     for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space))
  457. tokens[n_tokens++] = xstrdup(t);
  458.     xfree(xbuf);
  459.     /* locate the Month field */
  460.     for (i = 3; i < n_tokens - 3; i++) {
  461. if (!is_month(tokens[i])) /* Month */
  462.     continue;
  463. if (!sscanf(tokens[i - 1], SCAN_FTP1, sbuf)) /* Size */
  464.     continue;
  465. if (!sscanf(tokens[i + 1], SCAN_FTP1, sbuf)) /* Day */
  466.     continue;
  467. if (!sscanf(tokens[i + 2], SCAN_FTP2, sbuf)) /* Yr | hh:mm */
  468.     continue;
  469. p->type = *tokens[0];
  470. p->size = atoi(tokens[i - 1]);
  471. snprintf(sbuf, 128, "%s %2s %5s",
  472.     tokens[i], tokens[i + 1], tokens[i + 2]);
  473. if (!strstr(buf, sbuf))
  474.     snprintf(sbuf, 128, "%s %2s %-5s",
  475. tokens[i], tokens[i + 1], tokens[i + 2]);
  476. if ((t = strstr(buf, sbuf))) {
  477.     p->date = xstrdup(sbuf);
  478.     if (flags.skip_whitespace) {
  479. t += strlen(sbuf);
  480. while (strchr(w_space, *t))
  481.     t++;
  482.     } else {
  483. /* XXX assumes a single space between date and filename
  484.  * suggested by:  Nathan.Bailey@cc.monash.edu.au and
  485.  * Mike Battersby <mike@starbug.bofh.asn.au> */
  486. t += strlen(sbuf) + 1;
  487.     }
  488.     p->name = xstrdup(t);
  489.     if ((t = strstr(p->name, " -> "))) {
  490. *t = '';
  491. p->link = xstrdup(t + 4);
  492.     }
  493. }
  494. break;
  495.     }
  496.     /* try it as a DOS listing */
  497.     if (n_tokens > 3 && p->name == NULL &&
  498. sscanf(tokens[0], SCAN_FTP3, sbuf, sbuf, sbuf) == 3 &&
  499.     /* 04-05-70 */
  500. sscanf(tokens[1], SCAN_FTP4, sbuf, sbuf, sbuf, sbuf) == 4) {
  501. /* 09:33PM */
  502. if (!strcasecmp(tokens[2], "<dir>")) {
  503.     p->type = 'd';
  504. } else {
  505.     p->type = '-';
  506.     p->size = atoi(tokens[2]);
  507. }
  508. snprintf(sbuf, 128, "%s %s", tokens[0], tokens[1]);
  509. p->date = xstrdup(sbuf);
  510. if (p->type == 'd') {
  511.     /* Directory.. name begins with first printable after <dir> */
  512.     ct = strstr(buf, tokens[2]);
  513.     ct += strlen(tokens[2]);
  514.     while (xisspace(*ct))
  515. ct++;
  516.     if (!*ct)
  517. ct = NULL;
  518. } else {
  519.     /* A file. Name begins after size, with a space in between */
  520.     snprintf(sbuf, 128, " %s %s", tokens[2], tokens[3]);
  521.     ct = strstr(buf, sbuf);
  522.     if (ct) {
  523. ct += strlen(tokens[2]) + 2;
  524.     }
  525. }
  526. p->name = xstrdup(ct ? ct : tokens[3]);
  527.     }
  528.     /* Try EPLF format; carson@lehman.com */
  529.     if (p->name == NULL && buf[0] == '+') {
  530. ct = buf + 1;
  531. p->type = 0;
  532. while (ct && *ct) {
  533.     switch (*ct) {
  534.     case 't':
  535. sscanf(ct + 1, "%[^,]", sbuf);
  536. p->name = xstrdup(sbuf);
  537. break;
  538.     case 's':
  539. sscanf(ct + 1, "%d", &(p->size));
  540. break;
  541.     case 'm':
  542. sscanf(ct + 1, "%d", &i);
  543. p->date = xstrdup(ctime((time_t *) & i));
  544. *(strstr(p->date, "n")) = '';
  545. break;
  546.     case '/':
  547. p->type = 'd';
  548. break;
  549.     case 'r':
  550. p->type = '-';
  551. break;
  552.     case 'i':
  553. break;
  554.     default:
  555. break;
  556.     }
  557.     ct = strstr(ct, ",");
  558.     if (ct) {
  559. ct++;
  560.     }
  561. }
  562. if (p->type == 0) {
  563.     p->type = '-';
  564. }
  565.     }
  566.     for (i = 0; i < n_tokens; i++)
  567. xfree(tokens[i]);
  568.     if (p->name == NULL)
  569. ftpListPartsFree(&p);
  570.     return p;
  571. }
  572. static const char *
  573. dots_fill(size_t len)
  574. {
  575.     static char buf[256];
  576.     int i = 0;
  577.     if (len > Config.Ftp.list_width) {
  578. memset(buf, ' ', 256);
  579. buf[0] = 'n';
  580. buf[Config.Ftp.list_width + 4] = '';
  581. return buf;
  582.     }
  583.     for (i = (int) len; i < Config.Ftp.list_width; i++)
  584. buf[i - len] = (i % 2) ? '.' : ' ';
  585.     buf[i - len] = '';
  586.     return buf;
  587. }
  588. static char *
  589. ftpHtmlifyListEntry(char *line, FtpStateData * ftpState)
  590. {
  591.     LOCAL_ARRAY(char, icon, 2048);
  592.     LOCAL_ARRAY(char, href, 2048 + 40);
  593.     LOCAL_ARRAY(char, text, 2048);
  594.     LOCAL_ARRAY(char, size, 2048);
  595.     LOCAL_ARRAY(char, chdir, 2048 + 40);
  596.     LOCAL_ARRAY(char, view, 2048 + 40);
  597.     LOCAL_ARRAY(char, download, 2048 + 40);
  598.     LOCAL_ARRAY(char, link, 2048 + 40);
  599.     LOCAL_ARRAY(char, html, 8192);
  600.     size_t width = Config.Ftp.list_width;
  601.     ftpListParts *parts;
  602.     *icon = *href = *text = *size = *chdir = *view = *download = *link = *html = '';
  603.     if ((int) strlen(line) > 1024) {
  604. snprintf(html, 8192, "%sn", line);
  605. return html;
  606.     }
  607.     /* Handle builtin <dirup> */
  608.     if (!strcmp(line, "<internal-dirup>")) {
  609. /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> {link} */
  610. snprintf(icon, 2048, "<IMG BORDER=0 SRC="%s" ALT="%-6s">",
  611.     mimeGetIconURL("internal-dirup"),
  612.     "[DIRUP]");
  613. if (!ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
  614.     /* Normal directory */
  615.     strcpy(href, "../");
  616.     strcpy(text, "Parent Directory");
  617. } else if (!ftpState->flags.no_dotdot && ftpState->flags.root_dir) {
  618.     /* "Top level" directory */
  619.     strcpy(href, "%2e%2e/");
  620.     strcpy(text, "Parent Directory");
  621.     snprintf(link, 2048, "(<A HREF="%s">%s</A>)",
  622. "%2f/",
  623. "Root Directory");
  624. } else if (ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
  625.     /* Normal directory where last component is / or ..  */
  626.     strcpy(href, "%2e%2e/");
  627.     strcpy(text, "Parent Directory");
  628.     snprintf(link, 2048, "(<A HREF="%s">%s</A>)",
  629. "../",
  630. "Back");
  631. } else { /* NO_DOTDOT && ROOT_DIR */
  632.     /* "UNIX Root" directory */
  633.     strcpy(href, "../");
  634.     strcpy(text, "Home Directory");
  635. }
  636. snprintf(html, 8192, "<A HREF="%s">%s</A> <A HREF="%s">%s</A> %sn",
  637.     href, icon, href, text, link);
  638. return html;
  639.     }
  640.     if ((parts = ftpListParseParts(line, ftpState->flags)) == NULL) {
  641. char *p;
  642. snprintf(html, 8192, "%sn", line);
  643. for (p = line; *p && xisspace(*p); p++);
  644. if (*p && !xisspace(*p))
  645.     ftpState->flags.listformat_unknown = 1;
  646. return html;
  647.     }
  648.     if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
  649. *html = '';
  650. ftpListPartsFree(&parts);
  651. return html;
  652.     }
  653.     parts->size += 1023;
  654.     parts->size >>= 10;
  655.     parts->showname = xstrdup(parts->name);
  656.     if (!Config.Ftp.list_wrap) {
  657. if (strlen(parts->showname) > width - 1) {
  658.     *(parts->showname + width - 1) = '>';
  659.     *(parts->showname + width - 0) = '';
  660. }
  661.     }
  662.     /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}n  */
  663.     xstrncpy(href, rfc1738_escape(parts->name), 2048);
  664.     xstrncpy(text, parts->showname, 2048);
  665.     switch (parts->type) {
  666.     case 'd':
  667. snprintf(icon, 2048, "<IMG BORDER=0 SRC="%s" ALT="%-6s">",
  668.     mimeGetIconURL("internal-dir"),
  669.     "[DIR]");
  670. strncat(href, "/", 2048);
  671. break;
  672.     case 'l':
  673. snprintf(icon, 2048, "<IMG BORDER=0 SRC="%s" ALT="%-6s">",
  674.     mimeGetIconURL("internal-link"),
  675.     "[LINK]");
  676. /* sometimes there is an 'l' flag, but no "->" link */
  677. if (parts->link)
  678.     snprintf(link, 2048, " -> <A HREF="%s">%s</A>",
  679. rfc1738_escape(parts->link),
  680. parts->link);
  681. break;
  682.     case '':
  683. snprintf(icon, 2048, "<IMG BORDER=0 SRC="%s" ALT="%-6s">",
  684.     mimeGetIconURL(parts->name),
  685.     "[UNKNOWN]");
  686. snprintf(chdir, 2048, " <A HREF="%s/;type=d"><IMG BORDER=0 SRC="%s" "
  687.     "ALT="[DIR]"></A>",
  688.     rfc1738_escape(parts->name),
  689.     mimeGetIconURL("internal-dir"));
  690. break;
  691.     case '-':
  692.     default:
  693. snprintf(icon, 2048, "<IMG BORDER=0 SRC="%s" ALT="%-6s">",
  694.     mimeGetIconURL(parts->name),
  695.     "[FILE]");
  696. snprintf(size, 2048, " %6dk", parts->size);
  697. break;
  698.     }
  699.     if (parts->type != 'd') {
  700. if (mimeGetViewOption(parts->name)) {
  701.     snprintf(view, 2048, " <A HREF="%s;type=a"><IMG BORDER=0 SRC="%s" "
  702. "ALT="[VIEW]"></A>",
  703. href, mimeGetIconURL("internal-view"));
  704. }
  705. if (mimeGetDownloadOption(parts->name)) {
  706.     snprintf(download, 2048, " <A HREF="%s;type=i"><IMG BORDER=0 SRC="%s" "
  707. "ALT="[DOWNLOAD]"></A>",
  708. href, mimeGetIconURL("internal-download"));
  709. }
  710.     }
  711.     /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> . . . {date}{size}{chdir}{view}{download}{link}n  */
  712.     if (parts->type != '') {
  713. snprintf(html, 8192, "<A HREF="%s">%s</A> <A HREF="%s">%s</A>%s "
  714.     "%s%8s%s%s%s%sn",
  715.     href, icon, href, text, dots_fill(strlen(text)),
  716.     parts->date, size, chdir, view, download, link);
  717.     } else {
  718. /* Plain listing. {icon} {text} ... {chdir}{view}{download} */
  719. snprintf(html, 8192, "<A HREF="%s">%s</A> <A HREF="%s">%s</A>%s "
  720.     "%s%s%s%sn",
  721.     href, icon, href, text, dots_fill(strlen(text)),
  722.     chdir, view, download, link);
  723.     }
  724.     ftpListPartsFree(&parts);
  725.     return html;
  726. }
  727. static void
  728. ftpParseListing(FtpStateData * ftpState)
  729. {
  730.     char *buf = ftpState->data.buf;
  731.     char *sbuf; /* NULL-terminated copy of buf */
  732.     char *end;
  733.     char *line;
  734.     char *s;
  735.     char *t;
  736.     size_t linelen;
  737.     size_t usable;
  738.     StoreEntry *e = ftpState->entry;
  739.     int len = ftpState->data.offset;
  740.     /*
  741.      * We need a NULL-terminated buffer for scanning, ick
  742.      */
  743.     sbuf = xmalloc(len + 1);
  744.     xstrncpy(sbuf, buf, len + 1);
  745.     end = sbuf + len - 1;
  746.     while (*end != 'r' && *end != 'n' && end > sbuf)
  747. end--;
  748.     usable = end - sbuf;
  749.     debug(9, 3) ("ftpParseListing: usable = %dn", usable);
  750.     if (usable == 0) {
  751. debug(9, 3) ("ftpParseListing: didn't find end for %sn", storeUrl(e));
  752. xfree(sbuf);
  753. return;
  754.     }
  755.     debug(9, 3) ("ftpParseListing: %d bytes to play withn", len);
  756.     line = memAllocate(MEM_4K_BUF);
  757.     end++;
  758.     storeBuffer(e);
  759.     s = sbuf;
  760.     s += strspn(s, crlf);
  761.     for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
  762. debug(9, 3) ("ftpParseListing: s = {%s}n", s);
  763. linelen = strcspn(s, crlf) + 1;
  764. if (linelen < 2)
  765.     break;
  766. if (linelen > 4096)
  767.     linelen = 4096;
  768. xstrncpy(line, s, linelen);
  769. debug(9, 7) ("ftpParseListing: {%s}n", line);
  770. if (!strncmp(line, "total", 5))
  771.     continue;
  772. t = ftpHtmlifyListEntry(line, ftpState);
  773. assert(t != NULL);
  774. storeAppend(e, t, strlen(t));
  775.     }
  776.     storeBufferFlush(e);
  777.     assert(usable <= len);
  778.     if (usable < len) {
  779. /* must copy partial line to beginning of buf */
  780. linelen = len - usable;
  781. if (linelen > 4096)
  782.     linelen = 4096;
  783. xstrncpy(line, end, linelen);
  784. xstrncpy(ftpState->data.buf, line, ftpState->data.size);
  785. ftpState->data.offset = strlen(ftpState->data.buf);
  786.     }
  787.     memFree(line, MEM_4K_BUF);
  788.     xfree(sbuf);
  789. }
  790. static void
  791. ftpReadComplete(FtpStateData * ftpState)
  792. {
  793.     debug(9, 3) ("ftpReadCompleten");
  794.     /* Connection closed; retrieval done. */
  795.     if (ftpState->flags.html_header_sent)
  796. ftpListingFinish(ftpState);
  797.     if (!ftpState->flags.put) {
  798. storeTimestampsSet(ftpState->entry);
  799. fwdComplete(ftpState->fwd);
  800.     }
  801.     /* expect the "transfer complete" message on the control socket */
  802.     ftpScheduleReadControlReply(ftpState, 1);
  803. }
  804. static void
  805. ftpDataRead(int fd, void *data)
  806. {
  807.     FtpStateData *ftpState = data;
  808.     int len;
  809.     int j;
  810.     int bin;
  811.     StoreEntry *entry = ftpState->entry;
  812.     MemObject *mem = entry->mem_obj;
  813.     size_t read_sz;
  814. #if DELAY_POOLS
  815.     delay_id delay_id = delayMostBytesAllowed(mem);
  816. #endif
  817.     assert(fd == ftpState->data.fd);
  818.     if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
  819. comm_close(ftpState->ctrl.fd);
  820. return;
  821.     }
  822.     errno = 0;
  823.     read_sz = ftpState->data.size - ftpState->data.offset;
  824. #if DELAY_POOLS
  825.     read_sz = delayBytesWanted(delay_id, 1, read_sz);
  826. #endif
  827.     memset(ftpState->data.buf + ftpState->data.offset, '', read_sz);
  828.     Counter.syscalls.sock.reads++;
  829.     len = read(fd, ftpState->data.buf + ftpState->data.offset, read_sz);
  830.     if (len > 0) {
  831. fd_bytes(fd, len, FD_READ);
  832. #if DELAY_POOLS
  833. delayBytesIn(delay_id, len);
  834. #endif
  835. kb_incr(&Counter.server.all.kbytes_in, len);
  836. kb_incr(&Counter.server.ftp.kbytes_in, len);
  837. ftpState->data.offset += len;
  838.     }
  839.     debug(9, 5) ("ftpDataRead: FD %d, Read %d bytesn", fd, len);
  840.     if (len > 0) {
  841. IOStats.Ftp.reads++;
  842. for (j = len - 1, bin = 0; j; bin++)
  843.     j >>= 1;
  844. IOStats.Ftp.read_hist[bin]++;
  845.     }
  846.     if (ftpState->flags.isdir && !ftpState->flags.html_header_sent && len >= 0) {
  847. ftpListingStart(ftpState);
  848.     }
  849.     if (len < 0) {
  850. debug(50, 1) ("ftpDataRead: read error: %sn", xstrerror());
  851. if (ignoreErrno(errno)) {
  852.     commSetSelect(fd,
  853. COMM_SELECT_READ,
  854. ftpDataRead,
  855. data,
  856. Config.Timeout.read);
  857. } else {
  858.     assert(mem->inmem_hi > 0);
  859.     ftpDataTransferDone(ftpState);
  860. }
  861.     } else if (len == 0) {
  862. ftpReadComplete(ftpState);
  863.     } else {
  864. if (ftpState->flags.isdir) {
  865.     ftpParseListing(ftpState);
  866. } else {
  867.     storeAppend(entry, ftpState->data.buf, len);
  868.     ftpState->data.offset = 0;
  869. }
  870. if (ftpState->size > 0 && mem->inmem_hi >= ftpState->size + mem->reply->hdr_sz)
  871.     ftpReadComplete(ftpState);
  872. else
  873.     commSetSelect(fd,
  874. COMM_SELECT_READ,
  875. ftpDataRead,
  876. data,
  877. Config.Timeout.read);
  878.     }
  879. }
  880. /*
  881.  * ftpCheckAuth
  882.  *
  883.  * Return 1 if we have everything needed to complete this request.
  884.  * Return 0 if something is missing.
  885.  */
  886. static int
  887. ftpCheckAuth(FtpStateData * ftpState, const HttpHeader * req_hdr)
  888. {
  889.     char *orig_user;
  890.     const char *auth;
  891.     ftpLoginParser(ftpState->request->login, ftpState, FTP_LOGIN_ESCAPED);
  892.     if (ftpState->user[0] && ftpState->password[0])
  893. return 1; /* name and passwd both in URL */
  894.     if (!ftpState->user[0] && !ftpState->password[0])
  895. return 1; /* no name or passwd */
  896.     if (ftpState->password[0])
  897. return 1; /* passwd with no name? */
  898.     /* URL has name, but no passwd */
  899.     if (!(auth = httpHeaderGetAuth(req_hdr, HDR_AUTHORIZATION, "Basic")))
  900. return 0; /* need auth header */
  901.     ftpState->flags.authenticated = 1;
  902.     orig_user = xstrdup(ftpState->user);
  903.     ftpLoginParser(auth, ftpState, FTP_LOGIN_NOT_ESCAPED);
  904.     if (!strcmp(orig_user, ftpState->user)) {
  905. xfree(orig_user);
  906. return 1; /* same username */
  907.     }
  908.     strcpy(ftpState->user, orig_user);
  909.     xfree(orig_user);
  910.     return 0; /* different username */
  911. }
  912. static void
  913. ftpCheckUrlpath(FtpStateData * ftpState)
  914. {
  915.     request_t *request = ftpState->request;
  916.     int l;
  917.     const char *t;
  918.     if ((t = strRChr(request->urlpath, ';')) != NULL) {
  919. if (strncasecmp(t + 1, "type=", 5) == 0) {
  920.     ftpState->typecode = (char) toupper((int) *(t + 6));
  921.     strCutPtr(request->urlpath, t);
  922. }
  923.     }
  924.     l = strLen(request->urlpath);
  925.     ftpState->flags.use_base = 1;
  926.     /* check for null path */
  927.     if (!l) {
  928. stringReset(&request->urlpath, "/");
  929. ftpState->flags.isdir = 1;
  930. ftpState->flags.root_dir = 1;
  931.     } else if (!strCmp(request->urlpath, "/%2f/")) {
  932. /* UNIX root directory */
  933. ftpState->flags.use_base = 0;
  934. ftpState->flags.isdir = 1;
  935. ftpState->flags.root_dir = 1;
  936.     } else if ((l >= 1) && (*(strBuf(request->urlpath) + l - 1) == '/')) {
  937. /* Directory URL, ending in / */
  938. ftpState->flags.isdir = 1;
  939. ftpState->flags.use_base = 0;
  940. if (l == 1)
  941.     ftpState->flags.root_dir = 1;
  942.     }
  943. }
  944. static void
  945. ftpBuildTitleUrl(FtpStateData * ftpState)
  946. {
  947.     request_t *request = ftpState->request;
  948.     size_t len;
  949.     char *t;
  950.     len = 64
  951. + strlen(ftpState->user)
  952. + strlen(ftpState->password)
  953. + strlen(request->host)
  954. + strLen(request->urlpath);
  955.     t = ftpState->title_url = xcalloc(len, 1);
  956.     strcat(t, "ftp://");
  957.     if (strcmp(ftpState->user, "anonymous")) {
  958. strcat(t, ftpState->user);
  959. strcat(t, "@");
  960.     }
  961.     strcat(t, request->host);
  962.     if (request->port != urlDefaultPort(PROTO_FTP))
  963. snprintf(&t[strlen(t)], len - strlen(t), ":%d", request->port);
  964.     strcat(t, strBuf(request->urlpath));
  965. }
  966. void
  967. ftpStart(FwdState * fwd)
  968. {
  969.     request_t *request = fwd->request;
  970.     StoreEntry *entry = fwd->entry;
  971.     int fd = fwd->server_fd;
  972.     LOCAL_ARRAY(char, realm, 8192);
  973.     const char *url = storeUrl(entry);
  974.     FtpStateData *ftpState = xcalloc(1, sizeof(FtpStateData));
  975.     HttpReply *reply;
  976.     StoreEntry *pe = NULL;
  977.     const cache_key *key = NULL;
  978.     cbdataAdd(ftpState, cbdataXfree, 0);
  979.     debug(9, 3) ("ftpStart: '%s'n", url);
  980.     Counter.server.all.requests++;
  981.     Counter.server.ftp.requests++;
  982.     storeLockObject(entry);
  983.     ftpState->entry = entry;
  984.     ftpState->request = requestLink(request);
  985.     ftpState->ctrl.fd = fd;
  986.     ftpState->data.fd = -1;
  987.     ftpState->size = -1;
  988.     ftpState->flags.pasv_supported = 1;
  989.     ftpState->flags.rest_supported = 1;
  990.     ftpState->fwd = fwd;
  991.     comm_add_close_handler(fd, ftpStateFree, ftpState);
  992.     if (ftpState->request->method == METHOD_PUT)
  993. ftpState->flags.put = 1;
  994.     if (!ftpCheckAuth(ftpState, &request->header)) {
  995. /* This request is not fully authenticated */
  996. if (request->port == 21) {
  997.     snprintf(realm, 8192, "ftp %s", ftpState->user);
  998. } else {
  999.     snprintf(realm, 8192, "ftp %s port %d",
  1000. ftpState->user, request->port);
  1001. }
  1002. /* eject any old cached object */
  1003. key = storeKeyPublic(entry->mem_obj->url, entry->mem_obj->method);
  1004. if ((pe = storeGet(key)) != NULL)
  1005.     storeRelease(pe);
  1006. /* create reply */
  1007. reply = entry->mem_obj->reply;
  1008. assert(reply != NULL);
  1009. /* create appropriate reply */
  1010. ftpAuthRequired(reply, request, realm);
  1011. httpReplySwapOut(reply, entry);
  1012. fwdComplete(ftpState->fwd);
  1013. comm_close(fd);
  1014. return;
  1015.     }
  1016.     ftpCheckUrlpath(ftpState);
  1017.     ftpBuildTitleUrl(ftpState);
  1018.     debug(9, 5) ("ftpStart: host=%s, path=%s, user=%s, passwd=%sn",
  1019. ftpState->request->host, strBuf(ftpState->request->urlpath),
  1020. ftpState->user, ftpState->password);
  1021.     ftpState->state = BEGIN;
  1022.     ftpState->ctrl.last_command = xstrdup("Connect to server");
  1023.     ftpState->ctrl.buf = memAllocate(MEM_4K_BUF);
  1024.     ftpState->ctrl.freefunc = memFree4K;
  1025.     ftpState->ctrl.size = 4096;
  1026.     ftpState->ctrl.offset = 0;
  1027.     ftpState->data.buf = xmalloc(SQUID_TCP_SO_RCVBUF);
  1028.     ftpState->data.size = SQUID_TCP_SO_RCVBUF;
  1029.     ftpState->data.freefunc = xfree;
  1030.     ftpScheduleReadControlReply(ftpState, 0);
  1031.     commSetTimeout(fd, Config.Timeout.read, ftpTimeout, ftpState);
  1032. }
  1033. /* ====================================================================== */
  1034. static void
  1035. ftpWriteCommand(const char *buf, FtpStateData * ftpState)
  1036. {
  1037.     debug(9, 5) ("ftpWriteCommand: %sn", buf);
  1038.     safe_free(ftpState->ctrl.last_command);
  1039.     ftpState->ctrl.last_command = xstrdup(buf);
  1040.     comm_write(ftpState->ctrl.fd,
  1041. xstrdup(buf),
  1042. strlen(buf),
  1043. ftpWriteCommandCallback,
  1044. ftpState,
  1045. xfree);
  1046.     ftpScheduleReadControlReply(ftpState, 0);
  1047. }
  1048. static void
  1049. ftpWriteCommandCallback(int fd, char *bufnotused, size_t size, int errflag, void *data)
  1050. {
  1051.     FtpStateData *ftpState = data;
  1052.     StoreEntry *entry = ftpState->entry;
  1053.     ErrorState *err;
  1054.     debug(9, 7) ("ftpWriteCommandCallback: wrote %d bytesn", size);
  1055.     if (size > 0) {
  1056. fd_bytes(fd, size, FD_WRITE);
  1057. kb_incr(&Counter.server.all.kbytes_out, size);
  1058. kb_incr(&Counter.server.ftp.kbytes_out, size);
  1059.     }
  1060.     if (errflag == COMM_ERR_CLOSING)
  1061. return;
  1062.     if (errflag) {
  1063. debug(50, 1) ("ftpWriteCommandCallback: FD %d: %sn", fd, xstrerror());
  1064. if (entry->mem_obj->inmem_hi == 0) {
  1065.     err = errorCon(ERR_WRITE_ERROR, HTTP_SERVICE_UNAVAILABLE);
  1066.     err->xerrno = errno;
  1067.     err->request = requestLink(ftpState->request);
  1068.     errorAppendEntry(entry, err);
  1069. }
  1070. comm_close(ftpState->ctrl.fd);
  1071.     }
  1072. }
  1073. static wordlist *
  1074. ftpParseControlReply(char *buf, size_t len, int *codep, int *used)
  1075. {
  1076.     char *s;
  1077.     char *sbuf;
  1078.     char *end;
  1079.     int usable;
  1080.     int complete = 0;
  1081.     wordlist *head = NULL;
  1082.     wordlist *list;
  1083.     wordlist **tail = &head;
  1084.     off_t offset;
  1085.     size_t linelen;
  1086.     int code = -1;
  1087.     debug(9, 5) ("ftpParseControlReplyn");
  1088.     /*
  1089.      * We need a NULL-terminated buffer for scanning, ick
  1090.      */
  1091.     sbuf = xmalloc(len + 1);
  1092.     xstrncpy(sbuf, buf, len + 1);
  1093.     end = sbuf + len - 1;
  1094.     while (*end != 'r' && *end != 'n' && end > sbuf)
  1095. end--;
  1096.     usable = end - sbuf;
  1097.     debug(9, 3) ("ftpParseControlReply: usable = %dn", usable);
  1098.     if (usable == 0) {
  1099. debug(9, 3) ("ftpParseControlReply: didn't find end of linen");
  1100. safe_free(sbuf);
  1101. return NULL;
  1102.     }
  1103.     debug(9, 3) ("ftpParseControlReply: %d bytes to play withn", len);
  1104.     end++;
  1105.     s = sbuf;
  1106.     s += strspn(s, crlf);
  1107.     for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
  1108. if (complete)
  1109.     break;
  1110. debug(9, 3) ("ftpParseControlReply: s = {%s}n", s);
  1111. linelen = strcspn(s, crlf) + 1;
  1112. if (linelen < 2)
  1113.     break;
  1114. if (linelen > 3)
  1115.     complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
  1116. if (complete)
  1117.     code = atoi(s);
  1118. offset = 0;
  1119. if (linelen > 3)
  1120.     if (*s >= '0' && *s <= '9' && (*(s + 3) == '-' || *(s + 3) == ' '))
  1121. offset = 4;
  1122. list = memAllocate(MEM_WORDLIST);
  1123. list->key = xmalloc(linelen - offset);
  1124. xstrncpy(list->key, s + offset, linelen - offset);
  1125. debug(9, 7) ("%d %sn", code, list->key);
  1126. *tail = list;
  1127. tail = &list->next;
  1128.     }
  1129.     *used = (int) (s - sbuf);
  1130.     safe_free(sbuf);
  1131.     if (!complete)
  1132. wordlistDestroy(&head);
  1133.     if (codep)
  1134. *codep = code;
  1135.     return head;
  1136. }
  1137. static void
  1138. ftpScheduleReadControlReply(FtpStateData * ftpState, int buffered_ok)
  1139. {
  1140.     debug(9, 3) ("ftpScheduleReadControlReply: FD %dn", ftpState->ctrl.fd);
  1141.     if (buffered_ok && ftpState->ctrl.offset > 0) {
  1142. /* We've already read some reply data */
  1143. ftpHandleControlReply(ftpState);
  1144.     } else {
  1145. commSetSelect(ftpState->ctrl.fd,
  1146.     COMM_SELECT_READ,
  1147.     ftpReadControlReply,
  1148.     ftpState,
  1149.     Config.Timeout.read);
  1150.     }
  1151. }
  1152. static void
  1153. ftpReadControlReply(int fd, void *data)
  1154. {
  1155.     FtpStateData *ftpState = data;
  1156.     StoreEntry *entry = ftpState->entry;
  1157.     int len;
  1158.     ErrorState *err;
  1159.     debug(9, 5) ("ftpReadControlReplyn");
  1160.     assert(ftpState->ctrl.offset < ftpState->ctrl.size);
  1161.     Counter.syscalls.sock.reads++;
  1162.     len = read(fd,
  1163. ftpState->ctrl.buf + ftpState->ctrl.offset,
  1164. ftpState->ctrl.size - ftpState->ctrl.offset);
  1165.     if (len > 0) {
  1166. fd_bytes(fd, len, FD_READ);
  1167. kb_incr(&Counter.server.all.kbytes_in, len);
  1168. kb_incr(&Counter.server.ftp.kbytes_in, len);
  1169.     }
  1170.     debug(9, 5) ("ftpReadControlReply: FD %d, Read %d bytesn", fd, len);
  1171.     if (len < 0) {
  1172. debug(50, 1) ("ftpReadControlReply: read error: %sn", xstrerror());
  1173. if (ignoreErrno(errno)) {
  1174.     ftpScheduleReadControlReply(ftpState, 0);
  1175. } else {
  1176.     if (entry->mem_obj->inmem_hi == 0) {
  1177. err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
  1178. err->xerrno = errno;
  1179. err->request = requestLink(ftpState->request);
  1180. errorAppendEntry(entry, err);
  1181.     }
  1182.     comm_close(ftpState->ctrl.fd);
  1183. }
  1184. return;
  1185.     }
  1186.     if (len == 0) {
  1187. if (entry->store_status == STORE_PENDING) {
  1188.     storeReleaseRequest(entry);
  1189.     if (entry->mem_obj->inmem_hi == 0) {
  1190. err = errorCon(ERR_FTP_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
  1191. err->xerrno = 0;
  1192. err->request = requestLink(ftpState->request);
  1193. err->ftp_server_msg = ftpState->ctrl.message;
  1194. errorAppendEntry(entry, err);
  1195.     }
  1196. }
  1197. comm_close(ftpState->ctrl.fd);
  1198. return;
  1199.     }
  1200.     len += ftpState->ctrl.offset;
  1201.     ftpState->ctrl.offset = len;
  1202.     assert(len <= ftpState->ctrl.size);
  1203.     ftpHandleControlReply(ftpState);
  1204. }
  1205. static void
  1206. ftpHandleControlReply(FtpStateData * ftpState)
  1207. {
  1208.     char *oldbuf;
  1209.     wordlist **W, **T;
  1210.     int bytes_used = 0;
  1211.     wordlistDestroy(&ftpState->ctrl.message);
  1212.     ftpState->ctrl.message = ftpParseControlReply(ftpState->ctrl.buf,
  1213. ftpState->ctrl.offset, &ftpState->ctrl.replycode, &bytes_used);
  1214.     if (ftpState->ctrl.message == NULL) {
  1215. /* didn't get complete reply yet */
  1216. if (ftpState->ctrl.offset == ftpState->ctrl.size) {
  1217.     oldbuf = ftpState->ctrl.buf;
  1218.     ftpState->ctrl.buf = xcalloc(ftpState->ctrl.size << 1, 1);
  1219.     xmemcpy(ftpState->ctrl.buf, oldbuf, ftpState->ctrl.size);
  1220.     ftpState->ctrl.size <<= 1;
  1221.     ftpState->ctrl.freefunc(oldbuf);
  1222.     ftpState->ctrl.freefunc = xfree;
  1223. }
  1224. ftpScheduleReadControlReply(ftpState, 0);
  1225. return;
  1226.     } else if (ftpState->ctrl.offset == bytes_used) {
  1227. /* used it all up */
  1228. ftpState->ctrl.offset = 0;
  1229.     } else {
  1230. /* Got some data past the complete reply */
  1231. assert(bytes_used < ftpState->ctrl.offset);
  1232. ftpState->ctrl.offset -= bytes_used;
  1233. xmemmove(ftpState->ctrl.buf, ftpState->ctrl.buf + bytes_used,
  1234.     ftpState->ctrl.offset);
  1235.     }
  1236.     /* Extract reply message (last line) */
  1237.     for (T = NULL, W = &ftpState->ctrl.message; *W && (*W)->next; W = &(*W)->next) {
  1238. /* Skip trailing blank lines */
  1239. if (strlen((*W)->key) == 0) {
  1240.     if (T == NULL)
  1241. T = W;
  1242. } else if ((*W)->next) {
  1243.     T = NULL;
  1244. }
  1245.     }
  1246.     safe_free(ftpState->ctrl.last_reply);
  1247.     ftpState->ctrl.last_reply = (*W)->key;
  1248.     safe_free(*W);
  1249.     if (T)
  1250. wordlistDestroy(T);
  1251.     debug(9, 8) ("ftpReadControlReply: state=%d, code=%dn", ftpState->state,
  1252. ftpState->ctrl.replycode);
  1253.     FTP_SM_FUNCS[ftpState->state] (ftpState);
  1254. }
  1255. /* ====================================================================== */
  1256. static void
  1257. ftpReadWelcome(FtpStateData * ftpState)
  1258. {
  1259.     int code = ftpState->ctrl.replycode;
  1260.     debug(9, 3) ("ftpReadWelcomen");
  1261.     if (ftpState->flags.pasv_only)
  1262. ftpState->login_att++;
  1263.     if (code == 220) {
  1264. if (ftpState->ctrl.message) {
  1265.     if (strstr(ftpState->ctrl.message->key, "NetWare"))
  1266. ftpState->flags.skip_whitespace = 1;
  1267.     if (ftpState->cwd_message)
  1268. wordlistDestroy(&ftpState->cwd_message);
  1269.     ftpState->cwd_message = ftpState->ctrl.message;
  1270.     ftpState->ctrl.message = NULL;
  1271. }
  1272. ftpSendUser(ftpState);
  1273.     } else if (code == 120) {
  1274. if (NULL != ftpState->ctrl.message)
  1275.     debug(9, 3) ("FTP server is busy: %sn",
  1276. ftpState->ctrl.message->key);
  1277. return;
  1278.     } else {
  1279. ftpFail(ftpState);
  1280.     }
  1281. }
  1282. static void
  1283. ftpSendUser(FtpStateData * ftpState)
  1284. {
  1285.     if (ftpState->proxy_host != NULL)
  1286. snprintf(cbuf, 1024, "USER %s@%srn",
  1287.     ftpState->user,
  1288.     ftpState->request->host);
  1289.     else
  1290. snprintf(cbuf, 1024, "USER %srn", ftpState->user);
  1291.     ftpWriteCommand(cbuf, ftpState);
  1292.     ftpState->state = SENT_USER;
  1293. }
  1294. static void
  1295. ftpReadUser(FtpStateData * ftpState)
  1296. {
  1297.     int code = ftpState->ctrl.replycode;
  1298.     debug(9, 3) ("ftpReadUsern");
  1299.     if (code == 230) {
  1300. ftpReadPass(ftpState);
  1301.     } else if (code == 331) {
  1302. ftpSendPass(ftpState);
  1303.     } else {
  1304. ftpFail(ftpState);
  1305.     }
  1306. }
  1307. static void
  1308. ftpSendPass(FtpStateData * ftpState)
  1309. {
  1310.     snprintf(cbuf, 1024, "PASS %srn", ftpState->password);
  1311.     ftpWriteCommand(cbuf, ftpState);
  1312.     ftpState->state = SENT_PASS;
  1313. }
  1314. static void
  1315. ftpReadPass(FtpStateData * ftpState)
  1316. {
  1317.     int code = ftpState->ctrl.replycode;
  1318.     debug(9, 3) ("ftpReadPassn");
  1319.     if (ftpState->ctrl.message) {
  1320. if (ftpState->cwd_message)
  1321.     wordlistDestroy(&ftpState->cwd_message);
  1322. ftpState->cwd_message = ftpState->ctrl.message;
  1323. ftpState->ctrl.message = NULL;
  1324.     }
  1325.     if (code == 230) {
  1326. ftpSendType(ftpState);
  1327.     } else {
  1328. ftpFail(ftpState);
  1329.     }
  1330. }
  1331. static void
  1332. ftpSendType(FtpStateData * ftpState)
  1333. {
  1334.     const char *t;
  1335.     const char *filename;
  1336.     char mode;
  1337.     /*
  1338.      * Ref section 3.2.2 of RFC 1738
  1339.      */
  1340.     switch (mode = ftpState->typecode) {
  1341.     case 'D':
  1342. mode = 'A';
  1343. break;
  1344.     case 'A':
  1345.     case 'I':
  1346. break;
  1347.     default:
  1348. if (ftpState->flags.isdir) {
  1349.     mode = 'A';
  1350. } else {
  1351.     t = strRChr(ftpState->request->urlpath, '/');
  1352.     filename = t ? t + 1 : strBuf(ftpState->request->urlpath);
  1353.     mode = mimeGetTransferMode(filename);
  1354. }
  1355. break;
  1356.     }
  1357.     if (mode == 'I')
  1358. ftpState->flags.binary = 1;
  1359.     else
  1360. ftpState->flags.binary = 0;
  1361.     snprintf(cbuf, 1024, "TYPE %crn", mode);
  1362.     ftpWriteCommand(cbuf, ftpState);
  1363.     ftpState->state = SENT_TYPE;
  1364. }
  1365. static void
  1366. ftpReadType(FtpStateData * ftpState)
  1367. {
  1368.     int code = ftpState->ctrl.replycode;
  1369.     char *path;
  1370.     char *d, *p;
  1371.     debug(9, 3) ("This is ftpReadTypen");
  1372.     if (code == 200) {
  1373. p = path = xstrdup(strBuf(ftpState->request->urlpath));
  1374. if (*p == '/')
  1375.     p++;
  1376. while (*p) {
  1377.     d = p;
  1378.     p += strcspn(p, "/");
  1379.     if (*p)
  1380. *p++ = '';
  1381.     rfc1738_unescape(d);
  1382.     wordlistAdd(&ftpState->pathcomps, d);
  1383. }
  1384. xfree(path);
  1385. if (ftpState->pathcomps)
  1386.     ftpTraverseDirectory(ftpState);
  1387. else
  1388.     ftpListDir(ftpState);
  1389.     } else {
  1390. ftpFail(ftpState);
  1391.     }
  1392. }
  1393. static void
  1394. ftpTraverseDirectory(FtpStateData * ftpState)
  1395. {
  1396.     wordlist *w;
  1397.     debug(9, 4) ("ftpTraverseDirectory %sn",
  1398. ftpState->filepath ? ftpState->filepath : "<NULL>");
  1399.     safe_free(ftpState->filepath);
  1400.     /* Done? */
  1401.     if (ftpState->pathcomps == NULL) {
  1402. debug(9, 3) ("the final component was a directoryn");
  1403. ftpListDir(ftpState);
  1404. return;
  1405.     }
  1406.     /* Go to next path component */
  1407.     w = ftpState->pathcomps;
  1408.     ftpState->filepath = w->key;
  1409.     ftpState->pathcomps = w->next;
  1410.     xfree(w);
  1411.     /* Check if we are to CWD or RETR */
  1412.     if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
  1413. ftpSendCwd(ftpState);
  1414.     } else {
  1415. debug(9, 3) ("final component is probably a filen");
  1416. ftpGetFile(ftpState);
  1417. return;
  1418.     }
  1419. }
  1420. static void
  1421. ftpSendCwd(FtpStateData * ftpState)
  1422. {
  1423.     char *path = ftpState->filepath;
  1424.     debug(9, 3) ("ftpSendCwdn");
  1425.     if (!strcmp(path, "..") || !strcmp(path, "/")) {
  1426. ftpState->flags.no_dotdot = 1;
  1427.     } else {
  1428. ftpState->flags.no_dotdot = 0;
  1429.     }
  1430.     if (*path)
  1431. snprintf(cbuf, 1024, "CWD %srn", path);
  1432.     else
  1433. snprintf(cbuf, 1024, "CWDrn");
  1434.     ftpWriteCommand(cbuf, ftpState);
  1435.     ftpState->state = SENT_CWD;
  1436. }
  1437. static void
  1438. ftpReadCwd(FtpStateData * ftpState)
  1439. {
  1440.     int code = ftpState->ctrl.replycode;
  1441.     debug(9, 3) ("This is ftpReadCwdn");
  1442.     if (code >= 200 && code < 300) {
  1443. /* CWD OK */
  1444. ftpUnhack(ftpState);
  1445. if (ftpState->cwd_message)
  1446.     wordlistDestroy(&ftpState->cwd_message);
  1447. ftpState->cwd_message = ftpState->ctrl.message;
  1448. ftpState->ctrl.message = NULL;
  1449. /* Continue to traverse the path */
  1450. ftpTraverseDirectory(ftpState);
  1451.     } else {
  1452. /* CWD FAILED */
  1453. if (!ftpState->flags.put)
  1454.     ftpFail(ftpState);
  1455. else
  1456.     ftpTryMkdir(ftpState);
  1457.     }
  1458. }
  1459. static void
  1460. ftpTryMkdir(FtpStateData * ftpState)
  1461. {
  1462.     char *path = ftpState->filepath;
  1463.     debug(9, 3) ("ftpTryMkdir: with path=%sn", path);
  1464.     snprintf(cbuf, 1024, "MKD %srn", path);
  1465.     ftpWriteCommand(cbuf, ftpState);
  1466.     ftpState->state = SENT_MKDIR;
  1467. }
  1468. static void
  1469. ftpReadMkdir(FtpStateData * ftpState)
  1470. {
  1471.     char *path = ftpState->filepath;
  1472.     int code = ftpState->ctrl.replycode;
  1473.     debug(9, 3) ("ftpReadMkdir: path %s, code %dn", path, code);
  1474.     if (code == 257) { /* success */
  1475. ftpSendCwd(ftpState);
  1476.     } else if (code == 550) { /* dir exists */
  1477. if (ftpState->flags.put_mkdir) {
  1478.     ftpState->flags.put_mkdir = 1;
  1479.     ftpSendCwd(ftpState);
  1480. } else
  1481.     ftpSendReply(ftpState);
  1482.     } else
  1483. ftpSendReply(ftpState);
  1484. }
  1485. static void
  1486. ftpGetFile(FtpStateData * ftpState)
  1487. {
  1488.     assert(*ftpState->filepath != '');
  1489.     ftpState->flags.isdir = 0;
  1490.     ftpSendMdtm(ftpState);
  1491. }
  1492. static void
  1493. ftpListDir(FtpStateData * ftpState)
  1494. {
  1495.     if (!ftpState->flags.isdir) {
  1496. debug(9, 3) ("Directory path did not end in /n");
  1497. strcat(ftpState->title_url, "/");
  1498. ftpState->flags.isdir = 1;
  1499. ftpState->flags.use_base = 1;
  1500.     }
  1501.     ftpSendPasv(ftpState);
  1502. }
  1503. static void
  1504. ftpSendMdtm(FtpStateData * ftpState)
  1505. {
  1506.     assert(*ftpState->filepath != '');
  1507.     snprintf(cbuf, 1024, "MDTM %srn", ftpState->filepath);
  1508.     ftpWriteCommand(cbuf, ftpState);
  1509.     ftpState->state = SENT_MDTM;
  1510. }
  1511. static void
  1512. ftpReadMdtm(FtpStateData * ftpState)
  1513. {
  1514.     int code = ftpState->ctrl.replycode;
  1515.     debug(9, 3) ("This is ftpReadMdtmn");
  1516.     if (code == 213) {
  1517. ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply);
  1518. ftpUnhack(ftpState);
  1519.     } else if (code < 0) {
  1520. ftpFail(ftpState);
  1521.     }
  1522.     ftpSendSize(ftpState);
  1523. }
  1524. static void
  1525. ftpSendSize(FtpStateData * ftpState)
  1526. {
  1527.     /* Only send SIZE for binary transfers. The returned size
  1528.      * is useless on ASCII transfers */
  1529.     if (ftpState->flags.binary) {
  1530. assert(ftpState->filepath != NULL);
  1531. assert(*ftpState->filepath != '');
  1532. snprintf(cbuf, 1024, "SIZE %srn", ftpState->filepath);
  1533. ftpWriteCommand(cbuf, ftpState);
  1534. ftpState->state = SENT_SIZE;
  1535.     } else
  1536. /* Skip to next state no non-binary transfers */
  1537. ftpSendPasv(ftpState);
  1538. }
  1539. static void
  1540. ftpReadSize(FtpStateData * ftpState)
  1541. {
  1542.     int code = ftpState->ctrl.replycode;
  1543.     debug(9, 3) ("This is ftpReadSizen");
  1544.     if (code == 213) {
  1545. ftpUnhack(ftpState);
  1546. ftpState->size = atoi(ftpState->ctrl.last_reply);
  1547.     } else if (code < 0) {
  1548. ftpFail(ftpState);
  1549.     }
  1550.     ftpSendPasv(ftpState);
  1551. }
  1552. static void
  1553. ftpSendPasv(FtpStateData * ftpState)
  1554. {
  1555.     int fd;
  1556.     struct sockaddr_in addr;
  1557.     socklen_t addr_len;
  1558.     if (ftpState->data.fd >= 0) {
  1559. if (!ftpState->flags.datachannel_hack) {
  1560.     /* We are already connected, reuse this connection. */
  1561.     ftpRestOrList(ftpState);
  1562.     return;
  1563. } else {
  1564.     /* Close old connection */
  1565.     comm_close(ftpState->data.fd);
  1566.     ftpState->data.fd = -1;
  1567. }
  1568.     }
  1569.     if (!ftpState->flags.pasv_supported) {
  1570. ftpSendPort(ftpState);
  1571. return;
  1572.     }
  1573.     addr_len = sizeof(addr);
  1574.     if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
  1575. debug(9, 0) ("ftpSendPasv: getsockname(%d,..): %sn",
  1576.     ftpState->ctrl.fd, xstrerror());
  1577. addr.sin_addr = Config.Addrs.tcp_outgoing;
  1578.     }
  1579.     /* Open data channel with the same local address as control channel */
  1580.     fd = comm_open(SOCK_STREAM,
  1581. 0,
  1582. addr.sin_addr,
  1583. 0,
  1584. COMM_NONBLOCKING,
  1585. storeUrl(ftpState->entry));
  1586.     debug(9, 3) ("ftpSendPasv: Unconnected data socket created on FD %dn", fd);
  1587.     if (fd < 0) {
  1588. ftpFail(ftpState);
  1589. return;
  1590.     }
  1591.     /*
  1592.      * No comm_add_close_handler() here.  If we have both ctrl and
  1593.      * data FD's call ftpStateFree() upon close, then we have
  1594.      * to delete the close handler which did NOT get called
  1595.      * to prevent ftpStateFree() getting called twice.
  1596.      * Instead we'll always call comm_close() on the ctrl FD.
  1597.      */
  1598.     ftpState->data.fd = fd;
  1599.     snprintf(cbuf, 1024, "PASVrn");
  1600.     ftpWriteCommand(cbuf, ftpState);
  1601.     ftpState->state = SENT_PASV;
  1602. }
  1603. static void
  1604. ftpReadPasv(FtpStateData * ftpState)
  1605. {
  1606.     int code = ftpState->ctrl.replycode;
  1607.     int h1, h2, h3, h4;
  1608.     int p1, p2;
  1609.     int n;
  1610.     u_short port;
  1611.     int fd = ftpState->data.fd;
  1612.     char *buf = ftpState->ctrl.last_reply;
  1613.     LOCAL_ARRAY(char, junk, 1024);
  1614.     debug(9, 3) ("This is ftpReadPasvn");
  1615.     if (code != 227) {
  1616. debug(9, 3) ("PASV not supported by remote endn");
  1617. comm_close(ftpState->data.fd);
  1618. ftpState->data.fd = -1;
  1619. ftpSendPort(ftpState);
  1620. return;
  1621.     }
  1622.     if ((int) strlen(buf) > 1024) {
  1623. debug(9, 1) ("ftpReadPasv: Avoiding potential buffer overflown");
  1624. ftpSendPort(ftpState);
  1625. return;
  1626.     }
  1627.     /*  227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).  */
  1628.     /*  ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
  1629.     debug(9, 5) ("scanning: %sn", buf);
  1630.     n = sscanf(buf, "%[^0123456789]%d,%d,%d,%d,%d,%d",
  1631. junk, &h1, &h2, &h3, &h4, &p1, &p2);
  1632.     if (n != 7 || p1 < 0 || p2 < 0 || p1 > 255 || p2 > 255) {
  1633. debug(9, 3) ("Bad 227 replyn");
  1634. debug(9, 3) ("n=%d, p1=%d, p2=%dn", n, p1, p2);
  1635. ftpSendPort(ftpState);
  1636. return;
  1637.     }
  1638.     snprintf(junk, 1024, "%d.%d.%d.%d", h1, h2, h3, h4);
  1639.     if (!safe_inet_addr(junk, NULL)) {
  1640. debug(9, 1) ("unsafe address (%s)n", junk);
  1641. ftpSendPort(ftpState);
  1642. return;
  1643.     }
  1644.     port = ((p1 << 8) + p2);
  1645.     if (0 == port) {
  1646. debug(9, 1) ("ftpReadPasv: Invalid PASV reply: %sn", buf);
  1647. ftpSendPort(ftpState);
  1648. return;
  1649.     }
  1650.     debug(9, 5) ("ftpReadPasv: connecting to %s, port %dn", junk, port);
  1651.     ftpState->data.port = port;
  1652.     ftpState->data.host = xstrdup(junk);
  1653.     commConnectStart(fd, junk, port, ftpPasvCallback, ftpState);
  1654. }
  1655. static void
  1656. ftpPasvCallback(int fd, int status, void *data)
  1657. {
  1658.     FtpStateData *ftpState = data;
  1659.     request_t *request = ftpState->request;
  1660.     ErrorState *err;
  1661.     debug(9, 3) ("ftpPasvCallbackn");
  1662.     if (status != COMM_OK) {
  1663. err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
  1664. err->xerrno = errno;
  1665. err->host = xstrdup(ftpState->data.host);
  1666. err->port = ftpState->data.port;
  1667. err->request = requestLink(request);
  1668. errorAppendEntry(ftpState->entry, err);
  1669. comm_close(ftpState->ctrl.fd);
  1670. return;
  1671.     }
  1672.     ftpRestOrList(ftpState);
  1673. }
  1674. static int
  1675. ftpOpenListenSocket(FtpStateData * ftpState, int fallback)
  1676. {
  1677.     int fd;
  1678.     struct sockaddr_in addr;
  1679.     socklen_t addr_len;
  1680.     int on = 1;
  1681.     u_short port = 0;
  1682.     /*
  1683.      * Set up a listen socket on the same local address as the
  1684.      * control connection.
  1685.      */
  1686.     addr_len = sizeof(addr);
  1687.     if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
  1688. debug(9, 0) ("ftpOpenListenSocket: getsockname(%d,..): %sn",
  1689.     ftpState->ctrl.fd, xstrerror());
  1690. return -1;
  1691.     }
  1692.     /*
  1693.      * REUSEADDR is needed in fallback mode, since the same port is
  1694.      * used for both control and data.
  1695.      */
  1696.     if (fallback) {
  1697. setsockopt(ftpState->ctrl.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
  1698. port = ntohs(addr.sin_port);
  1699.     }
  1700.     fd = comm_open(SOCK_STREAM,
  1701. 0,
  1702. addr.sin_addr,
  1703. port,
  1704. COMM_NONBLOCKING | (fallback ? COMM_REUSEADDR : 0),
  1705. storeUrl(ftpState->entry));
  1706.     debug(9, 3) ("ftpOpenListenSocket: Unconnected data socket created on FD %dn", fd);
  1707.     if (fd < 0) {
  1708. debug(9, 0) ("ftpOpenListenSocket: comm_open failedn");
  1709. return -1;
  1710.     }
  1711.     if (comm_listen(fd) < 0) {
  1712. comm_close(fd);
  1713. return -1;
  1714.     }
  1715.     ftpState->data.fd = fd;
  1716.     ftpState->data.port = comm_local_port(fd);;
  1717.     ftpState->data.host = NULL;
  1718.     return fd;
  1719. }
  1720. static void
  1721. ftpSendPort(FtpStateData * ftpState)
  1722. {
  1723.     int fd;
  1724.     struct sockaddr_in addr;
  1725.     socklen_t addr_len;
  1726.     unsigned char *addrptr;
  1727.     unsigned char *portptr;
  1728.     debug(9, 3) ("This is ftpSendPortn");
  1729.     ftpState->flags.pasv_supported = 0;
  1730.     fd = ftpOpenListenSocket(ftpState, 0);
  1731.     addr_len = sizeof(addr);
  1732.     if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
  1733. debug(9, 0) ("ftpSendPort: getsockname(%d,..): %sn", fd, xstrerror());
  1734. /* XXX Need to set error message */
  1735. ftpFail(ftpState);
  1736. return;
  1737.     }
  1738.     addrptr = (unsigned char *) &addr.sin_addr.s_addr;
  1739.     portptr = (unsigned char *) &addr.sin_port;
  1740.     snprintf(cbuf, 1024, "PORT %d,%d,%d,%d,%d,%drn",
  1741. addrptr[0], addrptr[1], addrptr[2], addrptr[3],
  1742. portptr[0], portptr[1]);
  1743.     ftpWriteCommand(cbuf, ftpState);
  1744.     ftpState->state = SENT_PORT;
  1745. }
  1746. static void
  1747. ftpReadPort(FtpStateData * ftpState)
  1748. {
  1749.     int code = ftpState->ctrl.replycode;
  1750.     debug(9, 3) ("This is ftpReadPortn");
  1751.     if (code != 200) {
  1752. /* Fall back on using the same port as the control connection */
  1753. debug(9, 3) ("PORT not supported by remote endn");
  1754. comm_close(ftpState->data.fd);
  1755. ftpState->data.fd = -1;
  1756. ftpOpenListenSocket(ftpState, 1);
  1757.     }
  1758.     ftpRestOrList(ftpState);
  1759. }
  1760. /* "read" handler to accept data connection */
  1761. static void
  1762. ftpAcceptDataConnection(int fd, void *data)
  1763. {
  1764.     FtpStateData *ftpState = data;
  1765.     struct sockaddr_in peer, me;
  1766.     debug(9, 3) ("ftpAcceptDataConnectionn");
  1767.     fd = comm_accept(fd, &peer, &me);
  1768.     if (fd < 0) {
  1769. debug(9, 1) ("ftpHandleDataAccept: comm_accept(%d): %s", fd, xstrerror());
  1770. /* XXX Need to set error message */
  1771. ftpFail(ftpState);
  1772. return;
  1773.     }
  1774.     /* Replace the Listen socket with the accepted data socket */
  1775.     comm_close(ftpState->data.fd);
  1776.     debug(9, 3) ("ftpAcceptDataConnection: Connected data socket on FD %dn", fd);
  1777.     ftpState->data.fd = fd;
  1778.     ftpState->data.port = ntohs(peer.sin_port);
  1779.     ftpState->data.host = xstrdup(inet_ntoa(peer.sin_addr));
  1780.     commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1781.     commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
  1782. ftpState);
  1783.     /* XXX We should have a flag to track connect state...
  1784.      *    host NULL -> not connected, port == local port
  1785.      *    host set  -> connected, port == remote port
  1786.      */
  1787.     /* Restart state (SENT_NLST/LIST/RETR) */
  1788.     FTP_SM_FUNCS[ftpState->state] (ftpState);
  1789. }
  1790. static void
  1791. ftpRestOrList(FtpStateData * ftpState)
  1792. {
  1793.     debug(9, 3) ("This is ftpRestOrListn");
  1794.     if (ftpState->flags.put) {
  1795. debug(9, 3) ("ftpRestOrList: Sending STOR request...n");
  1796. ftpSendStor(ftpState);
  1797.     } else if (ftpState->typecode == 'D') {
  1798. /* XXX This should NOT be here */
  1799. ftpSendNlst(ftpState); /* sec 3.2.2 of RFC 1738 */
  1800. ftpState->flags.isdir = 1;
  1801. ftpState->flags.use_base = 1;
  1802.     } else if (ftpState->flags.isdir)
  1803. ftpSendList(ftpState);
  1804.     else if (ftpRestartable(ftpState))
  1805. ftpSendRest(ftpState);
  1806.     else
  1807. ftpSendRetr(ftpState);
  1808. }
  1809. static void
  1810. ftpSendStor(FtpStateData * ftpState)
  1811. {
  1812.     assert(ftpState->filepath != NULL);
  1813.     snprintf(cbuf, 1024, "STOR %srn", ftpState->filepath);
  1814.     ftpWriteCommand(cbuf, ftpState);
  1815.     ftpState->state = SENT_STOR;
  1816. }
  1817. static void
  1818. ftpReadStor(FtpStateData * ftpState)
  1819. {
  1820.     int code = ftpState->ctrl.replycode;
  1821.     debug(9, 3) ("This is ftpReadStorn");
  1822.     if (code >= 100 && code < 200) {
  1823. /*
  1824.  * Cancel the timeout on the Control socket, pumpStart will
  1825.  * establish one on the data socket.
  1826.  */
  1827. commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1828. ftpPutStart(ftpState);
  1829. debug(9, 3) ("ftpReadStor: writing data channeln");
  1830. ftpState->state = WRITING_DATA;
  1831.     } else if (code == 553) {
  1832. /* directory does not exist, have to create, sigh */
  1833. #if WORK_IN_PROGRESS
  1834. ftpTraverseDirectory(ftpState);
  1835. #endif
  1836. ftpSendReply(ftpState);
  1837.     } else {
  1838. debug(9, 3) ("ftpReadStor: that's all folksn");
  1839. ftpSendReply(ftpState);
  1840.     }
  1841. }
  1842. static void
  1843. ftpSendRest(FtpStateData * ftpState)
  1844. {
  1845.     snprintf(cbuf, 1024, "REST %drn", ftpState->restart_offset);
  1846.     ftpWriteCommand(cbuf, ftpState);
  1847.     ftpState->state = SENT_REST;
  1848. }
  1849. static int
  1850. ftpRestartable(FtpStateData * ftpState)
  1851. {
  1852.     if (ftpState->restart_offset > 0)
  1853. return 1;
  1854.     if (!ftpState->request->range)
  1855. return 0;
  1856.     if (!ftpState->flags.binary)
  1857. return 0;
  1858.     if (ftpState->size <= 0)
  1859. return 0;
  1860.     ftpState->restart_offset = httpHdrRangeLowestOffset(ftpState->request->range, (size_t) ftpState->size);
  1861.     if (ftpState->restart_offset <= 0)
  1862. return 0;
  1863.     return 1;
  1864. }
  1865. static void
  1866. ftpReadRest(FtpStateData * ftpState)
  1867. {
  1868.     int code = ftpState->ctrl.replycode;
  1869.     debug(9, 3) ("This is ftpReadRestn");
  1870.     assert(ftpState->restart_offset > 0);
  1871.     if (code == 350) {
  1872. ftpState->restarted_offset = ftpState->restart_offset;
  1873. ftpSendRetr(ftpState);
  1874.     } else if (code > 0) {
  1875. debug(9, 3) ("ftpReadRest: REST not supportedn");
  1876. ftpState->flags.rest_supported = 0;
  1877. ftpSendRetr(ftpState);
  1878.     } else {
  1879. ftpFail(ftpState);
  1880.     }
  1881. }
  1882. static void
  1883. ftpSendList(FtpStateData * ftpState)
  1884. {
  1885.     if (ftpState->filepath) {
  1886. ftpState->flags.use_base = 1;
  1887. snprintf(cbuf, 1024, "LIST %srn", ftpState->filepath);
  1888.     } else {
  1889. snprintf(cbuf, 1024, "LISTrn");
  1890.     }
  1891.     ftpWriteCommand(cbuf, ftpState);
  1892.     ftpState->state = SENT_LIST;
  1893. }
  1894. static void
  1895. ftpSendNlst(FtpStateData * ftpState)
  1896. {
  1897.     ftpState->flags.tried_nlst = 1;
  1898.     if (ftpState->filepath) {
  1899. ftpState->flags.use_base = 1;
  1900. snprintf(cbuf, 1024, "NLST %srn", ftpState->filepath);
  1901.     } else {
  1902. snprintf(cbuf, 1024, "NLSTrn");
  1903.     }
  1904.     ftpWriteCommand(cbuf, ftpState);
  1905.     ftpState->state = SENT_NLST;
  1906. }
  1907. static void
  1908. ftpReadList(FtpStateData * ftpState)
  1909. {
  1910.     int code = ftpState->ctrl.replycode;
  1911.     debug(9, 3) ("This is ftpReadListn");
  1912.     if (code == 125 || (code == 150 && ftpState->data.host)) {
  1913. /* Begin data transfer */
  1914. ftpAppendSuccessHeader(ftpState);
  1915. commSetSelect(ftpState->data.fd,
  1916.     COMM_SELECT_READ,
  1917.     ftpDataRead,
  1918.     ftpState,
  1919.     Config.Timeout.read);
  1920. commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
  1921. ftpState->state = READING_DATA;
  1922. /*
  1923.  * Cancel the timeout on the Control socket and establish one
  1924.  * on the data socket
  1925.  */
  1926. commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1927. commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
  1928. return;
  1929.     } else if (code == 150) {
  1930. /* Accept data channel */
  1931. commSetSelect(ftpState->data.fd,
  1932.     COMM_SELECT_READ,
  1933.     ftpAcceptDataConnection,
  1934.     ftpState,
  1935.     0);
  1936. /*
  1937.  * Cancel the timeout on the Control socket and establish one
  1938.  * on the data socket
  1939.  */
  1940. commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1941. commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
  1942. return;
  1943.     } else if (!ftpState->flags.tried_nlst && code > 300) {
  1944. ftpSendNlst(ftpState);
  1945.     } else {
  1946. ftpFail(ftpState);
  1947. return;
  1948.     }
  1949. }
  1950. static void
  1951. ftpSendRetr(FtpStateData * ftpState)
  1952. {
  1953.     assert(ftpState->filepath != NULL);
  1954.     snprintf(cbuf, 1024, "RETR %srn", ftpState->filepath);
  1955.     ftpWriteCommand(cbuf, ftpState);
  1956.     ftpState->state = SENT_RETR;
  1957. }
  1958. static void
  1959. ftpReadRetr(FtpStateData * ftpState)
  1960. {
  1961.     int code = ftpState->ctrl.replycode;
  1962.     debug(9, 3) ("This is ftpReadRetrn");
  1963.     if (code == 125 || (code == 150 && ftpState->data.host)) {
  1964. /* Begin data transfer */
  1965. debug(9, 3) ("ftpReadRetr: reading data channeln");
  1966. ftpAppendSuccessHeader(ftpState);
  1967. commSetSelect(ftpState->data.fd,
  1968.     COMM_SELECT_READ,
  1969.     ftpDataRead,
  1970.     ftpState,
  1971.     Config.Timeout.read);
  1972. commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
  1973. ftpState->state = READING_DATA;
  1974. /*
  1975.  * Cancel the timeout on the Control socket and establish one
  1976.  * on the data socket
  1977.  */
  1978. commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1979. commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
  1980.     ftpState);
  1981.     } else if (code == 150) {
  1982. /* Accept data channel */
  1983. commSetSelect(ftpState->data.fd,
  1984.     COMM_SELECT_READ,
  1985.     ftpAcceptDataConnection,
  1986.     ftpState,
  1987.     0);
  1988. /*
  1989.  * Cancel the timeout on the Control socket and establish one
  1990.  * on the data socket
  1991.  */
  1992. commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
  1993. commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
  1994.     ftpState);
  1995.     } else if (code >= 300) {
  1996. if (!ftpState->flags.try_slash_hack) {
  1997.     /* Try this as a directory missing trailing slash... */
  1998.     ftpHackShortcut(ftpState, ftpSendCwd);
  1999. } else {
  2000.     ftpFail(ftpState);
  2001. }
  2002.     } else {
  2003. ftpFail(ftpState);
  2004.     }
  2005. }
  2006. static void
  2007. ftpReadTransferDone(FtpStateData * ftpState)
  2008. {
  2009.     int code = ftpState->ctrl.replycode;
  2010.     debug(9, 3) ("This is ftpReadTransferDonen");
  2011.     if (code != 226) {
  2012. debug(9, 1) ("ftpReadTransferDone: Got code %d after reading datan",
  2013.     code);
  2014. debug(9, 1) ("--> releasing '%s'n", storeUrl(ftpState->entry));
  2015. storeReleaseRequest(ftpState->entry);
  2016.     }
  2017.     ftpDataTransferDone(ftpState);
  2018. }
  2019. static void
  2020. ftpDataTransferDone(FtpStateData * ftpState)
  2021. {
  2022.     debug(9, 3) ("This is ftpDataTransferDonen");
  2023.     if (ftpState->data.fd > -1) {
  2024. comm_close(ftpState->data.fd);
  2025. ftpState->data.fd = -1;
  2026.     }
  2027.     ftpSendQuit(ftpState);
  2028. }
  2029. static void
  2030. ftpSendQuit(FtpStateData * ftpState)
  2031. {
  2032.     assert(ftpState->ctrl.fd > -1);
  2033.     snprintf(cbuf, 1024, "QUITrn");
  2034.     ftpWriteCommand(cbuf, ftpState);
  2035.     ftpState->state = SENT_QUIT;
  2036. }
  2037. static void
  2038. ftpReadQuit(FtpStateData * ftpState)
  2039. {
  2040.     comm_close(ftpState->ctrl.fd);
  2041. }
  2042. static void
  2043. ftpTrySlashHack(FtpStateData * ftpState)
  2044. {
  2045.     char *path;
  2046.     ftpState->flags.try_slash_hack = 1;
  2047.     /* Free old paths */
  2048.     if (ftpState->pathcomps)
  2049. wordlistDestroy(&ftpState->pathcomps);
  2050.     safe_free(ftpState->filepath);
  2051.     /* Build the new path (urlpath begins with /) */
  2052.     path = xstrdup(strBuf(ftpState->request->urlpath));
  2053.     rfc1738_unescape(path);
  2054.     ftpState->filepath = path;
  2055.     /* And off we go */
  2056.     ftpGetFile(ftpState);
  2057. }
  2058. static void
  2059. ftpTryDatachannelHack(FtpStateData * ftpState)
  2060. {
  2061.     ftpState->flags.datachannel_hack = 1;
  2062.     /* we have to undo some of the slash hack... */
  2063.     if (ftpState->old_filepath != NULL) {
  2064. ftpState->flags.try_slash_hack = 0;
  2065. safe_free(ftpState->filepath);
  2066. ftpState->filepath = ftpState->old_filepath;
  2067. ftpState->old_filepath = NULL;
  2068.     }
  2069.     ftpState->flags.tried_nlst = 0;
  2070.     /* And off we go */
  2071.     if (ftpState->flags.isdir) {
  2072. ftpListDir(ftpState);
  2073.     } else {
  2074. ftpGetFile(ftpState);
  2075.     }
  2076.     return;
  2077. }
  2078. /* Forget hack status. Next error is shown to the user */
  2079. static void
  2080. ftpUnhack(FtpStateData * ftpState)
  2081. {
  2082.     if (ftpState->old_request != NULL) {
  2083. safe_free(ftpState->old_request);
  2084. safe_free(ftpState->old_reply);
  2085.     }
  2086. }
  2087. static void
  2088. ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState)
  2089. {
  2090.     /* Clear some unwanted state */
  2091.     ftpState->restarted_offset = 0;
  2092.     ftpState->restart_offset = 0;
  2093.     /* Save old error message & some state info */
  2094.     if (ftpState->old_request == NULL) {
  2095. ftpState->old_request = ftpState->ctrl.last_command;
  2096. ftpState->ctrl.last_command = NULL;
  2097. ftpState->old_reply = ftpState->ctrl.last_reply;
  2098. ftpState->ctrl.last_reply = NULL;
  2099. if (ftpState->pathcomps == NULL && ftpState->filepath != NULL)
  2100.     ftpState->old_filepath = xstrdup(ftpState->filepath);
  2101.     }
  2102.     /* Jump to the "hack" state */
  2103.     nextState(ftpState);
  2104. }
  2105. static void
  2106. ftpFail(FtpStateData * ftpState)
  2107. {
  2108.     ErrorState *err;
  2109.     debug(9, 3) ("ftpFailn");
  2110.     /* Try the / hack to support "Netscape" FTP URL's for retreiving files */
  2111.     if (!ftpState->flags.isdir && /* Not a directory */
  2112. !ftpState->flags.try_slash_hack && /* Not in slash hack */
  2113. ftpState->mdtm <= 0 && ftpState->size < 0 && /* Not known as a file */
  2114. strNCaseCmp(ftpState->request->urlpath, "/%2f", 4) != 0) { /* No slash encoded */
  2115. switch (ftpState->state) {
  2116. case SENT_CWD:
  2117. case SENT_RETR:
  2118.     /* Try the / hack */
  2119.     ftpHackShortcut(ftpState, ftpTrySlashHack);
  2120.     return;
  2121. default:
  2122.     break;
  2123. }
  2124.     }
  2125.     /* Try to reopen datachannel */
  2126.     if (!ftpState->flags.datachannel_hack &&
  2127. ftpState->pathcomps == NULL) {
  2128. switch (ftpState->state) {
  2129. case SENT_RETR:
  2130. case SENT_LIST:
  2131. case SENT_NLST:
  2132.     /* Try to reopen datachannel */
  2133.     ftpHackShortcut(ftpState, ftpTryDatachannelHack);
  2134.     return;
  2135. default:
  2136.     break;
  2137. }
  2138.     }
  2139.     /* Translate FTP errors into HTTP errors */
  2140.     err = NULL;
  2141.     switch (ftpState->state) {
  2142.     case SENT_USER:
  2143.     case SENT_PASS:
  2144. if (ftpState->ctrl.replycode > 500)
  2145.     err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN);
  2146. else if (ftpState->ctrl.replycode == 421)
  2147.     err = errorCon(ERR_FTP_UNAVAILABLE, HTTP_SERVICE_UNAVAILABLE);
  2148. break;
  2149.     case SENT_CWD:
  2150.     case SENT_RETR:
  2151. if (ftpState->ctrl.replycode == 550)
  2152.     err = errorCon(ERR_FTP_NOT_FOUND, HTTP_NOT_FOUND);
  2153. break;
  2154.     default:
  2155. break;
  2156.     }
  2157.     if (err == NULL)
  2158. err = errorCon(ERR_FTP_FAILURE, HTTP_BAD_GATEWAY);
  2159.     err->request = requestLink(ftpState->request);
  2160.     err->ftp_server_msg = ftpState->ctrl.message;
  2161.     if (ftpState->old_request)
  2162. err->ftp.request = ftpState->old_request;
  2163.     else
  2164. err->ftp.request = ftpState->ctrl.last_command;
  2165.     if (err->ftp.request) {
  2166. if (!strncmp(err->ftp.request, "PASS", 4))
  2167.     err->ftp.request = "PASS <yourpassword>";
  2168.     }
  2169.     if (ftpState->old_reply)
  2170. err->ftp.reply = ftpState->old_reply;
  2171.     else
  2172. err->ftp.reply = ftpState->ctrl.last_reply;
  2173.     errorAppendEntry(ftpState->entry, err);
  2174.     comm_close(ftpState->ctrl.fd);
  2175. }
  2176. static void
  2177. ftpPutStart(FtpStateData * ftpState)
  2178. {
  2179.     debug(9, 3) ("ftpPutStartn");
  2180.     pumpStart(ftpState->data.fd, ftpState->fwd, ftpPutTransferDone, ftpState);
  2181. }
  2182. static void
  2183. ftpPutTransferDone(int fd, char *bufnotused, size_t size, int errflag, void *data)
  2184. {
  2185.     FtpStateData *ftpState = data;
  2186.     if (ftpState->data.fd >= 0) {
  2187. comm_close(ftpState->data.fd);
  2188. ftpState->data.fd = -1;
  2189.     }
  2190.     ftpReadComplete(ftpState);
  2191. }
  2192. static void
  2193. ftpSendReply(FtpStateData * ftpState)
  2194. {
  2195.     ErrorState *err;
  2196.     int code = ftpState->ctrl.replycode;
  2197.     http_status http_code;
  2198.     err_type err_code = ERR_NONE;
  2199.     debug(9, 5) ("ftpSendReply: %s, code %dn",
  2200. storeUrl(ftpState->entry), code);
  2201.     if (cbdataValid(ftpState))
  2202. debug(9, 5) ("ftpSendReply: ftpState (%p) is valid!n", ftpState);
  2203.     if (code == 226) {
  2204. err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
  2205. http_code = (ftpState->mdtm > 0) ? HTTP_ACCEPTED : HTTP_CREATED;
  2206.     } else {
  2207. err_code = ERR_FTP_PUT_ERROR;
  2208. http_code = HTTP_INTERNAL_SERVER_ERROR;
  2209.     }
  2210.     err = errorCon(err_code, http_code);
  2211.     err->request = requestLink(ftpState->request);
  2212.     if (ftpState->old_request)
  2213. err->ftp.request = ftpState->old_request;
  2214.     else
  2215. err->ftp.request = ftpState->ctrl.last_command;
  2216.     if (ftpState->old_reply)
  2217. err->ftp.reply = ftpState->old_reply;
  2218.     else
  2219. err->ftp.reply = ftpState->ctrl.last_reply;
  2220.     errorAppendEntry(ftpState->entry, err);
  2221.     storeBufferFlush(ftpState->entry);
  2222.     comm_close(ftpState->ctrl.fd);
  2223. }
  2224. static void
  2225. ftpAppendSuccessHeader(FtpStateData * ftpState)
  2226. {
  2227.     char *mime_type = NULL;
  2228.     char *mime_enc = NULL;
  2229.     String urlpath = ftpState->request->urlpath;
  2230.     const char *filename = NULL;
  2231.     const char *t = NULL;
  2232.     StoreEntry *e = ftpState->entry;
  2233.     StoreEntry *pe = NULL;
  2234.     http_reply *reply = e->mem_obj->reply;
  2235.     if (ftpState->flags.http_header_sent)
  2236. return;
  2237.     ftpState->flags.http_header_sent = 1;
  2238.     assert(e->mem_obj->inmem_hi == 0);
  2239.     EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
  2240.     filename = (t = strRChr(urlpath, '/')) ? t + 1 : strBuf(urlpath);
  2241.     if (ftpState->flags.isdir) {
  2242. mime_type = "text/html";
  2243.     } else {
  2244. switch (ftpState->typecode) {
  2245. case 'I':
  2246.     mime_type = "application/octet-stream";
  2247.     mime_enc = mimeGetContentEncoding(filename);
  2248.     break;
  2249. case 'A':
  2250.     mime_type = "text/plain";
  2251.     break;
  2252. default:
  2253.     mime_type = mimeGetContentType(filename);
  2254.     mime_enc = mimeGetContentEncoding(filename);
  2255.     break;
  2256. }
  2257.     }
  2258.     storeBuffer(e);
  2259.     httpReplyReset(reply);
  2260.     /* set standard stuff */
  2261.     if (ftpState->restarted_offset) {
  2262. /* Partial reply */
  2263. HttpHdrRangeSpec range_spec;
  2264. range_spec.offset = ftpState->restarted_offset;
  2265. range_spec.length = ftpState->size - ftpState->restarted_offset;
  2266. httpReplySetHeaders(reply, 1.0, HTTP_PARTIAL_CONTENT, "Gatewaying",
  2267.     mime_type, ftpState->size - ftpState->restarted_offset, ftpState->mdtm, -2);
  2268. httpHeaderAddContRange(&reply->header, range_spec, ftpState->size);
  2269.     } else {
  2270. /* Full reply */
  2271. httpReplySetHeaders(reply, 1.0, HTTP_OK, "Gatewaying",
  2272.     mime_type, ftpState->size, ftpState->mdtm, -2);
  2273.     }
  2274.     /* additional info */
  2275.     if (mime_enc)
  2276. httpHeaderPutStr(&reply->header, HDR_CONTENT_ENCODING, mime_enc);
  2277.     httpReplySwapOut(reply, e);
  2278.     storeBufferFlush(e);
  2279.     reply->hdr_sz = e->mem_obj->inmem_hi;
  2280.     storeTimestampsSet(e);
  2281.     if (ftpState->flags.authenticated) {
  2282. /*
  2283.  * Authenticated requests can't be cached. Eject any old cached
  2284.  * object
  2285.  */
  2286. pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method);
  2287. if (pe)
  2288.     storeRelease(pe);
  2289. storeRelease(e);
  2290.     } else if (EBIT_TEST(e->flags, ENTRY_CACHABLE) && !ftpState->restarted_offset) {
  2291. storeSetPublicKey(e);
  2292.     } else {
  2293. storeRelease(e);
  2294.     }
  2295. }
  2296. static void
  2297. ftpAuthRequired(HttpReply * old_reply, request_t * request, const char *realm)
  2298. {
  2299.     ErrorState *err = errorCon(ERR_ACCESS_DENIED, HTTP_UNAUTHORIZED);
  2300.     HttpReply *rep;
  2301.     err->request = requestLink(request);
  2302.     rep = errorBuildReply(err);
  2303.     errorStateFree(err);
  2304.     /* add Authenticate header */
  2305.     httpHeaderPutAuth(&rep->header, "Basic", realm);
  2306.     /* move new reply to the old one */
  2307.     httpReplyAbsorb(old_reply, rep);
  2308. }
  2309. char *
  2310. ftpUrlWith2f(const request_t * request)
  2311. {
  2312.     LOCAL_ARRAY(char, buf, MAX_URL);
  2313.     LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
  2314.     LOCAL_ARRAY(char, portbuf, 32);
  2315.     char *t;
  2316.     portbuf[0] = '';
  2317.     if (request->protocol != PROTO_FTP)
  2318. return NULL;
  2319.     if (request->port != urlDefaultPort(request->protocol))
  2320. snprintf(portbuf, 32, ":%d", request->port);
  2321.     loginbuf[0] = '';
  2322.     if ((int) strlen(request->login) > 0) {
  2323. strcpy(loginbuf, request->login);
  2324. if ((t = strchr(loginbuf, ':')))
  2325.     *t = '';
  2326. strcat(loginbuf, "@");
  2327.     }
  2328.     snprintf(buf, MAX_URL, "%s://%s%s%s%s%s",
  2329. ProtocolStr[request->protocol],
  2330. loginbuf,
  2331. request->host,
  2332. portbuf,
  2333. "/%2f",
  2334. strBuf(request->urlpath));
  2335.     if ((t = strchr(buf, '?')))
  2336. *t = '';
  2337.     return buf;
  2338. }