"Fossies" - the Fresh Open Source Software Archive

Member "delegate9.9.13/src/ftp.c" (23 Jul 2014, 226655 Bytes) of package /linux/misc/old/delegate9.9.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ftp.c" see the Fossies "Dox" file reference documentation.

    1 /*////////////////////////////////////////////////////////////////////////
    2 Copyright (c) 1994-2000 Yutaka Sato and ETL,AIST,MITI
    3 Copyright (c) 2001-2007 National Institute of Advanced Industrial Science and Technology (AIST)
    4 AIST-Product-ID: 2000-ETL-198715-01, H14PRO-049, H15PRO-165, H18PRO-443
    5 
    6 Permission to use this material for noncommercial and/or evaluation
    7 purpose, copy this material for your own use, and distribute the copies
    8 via publicly accessible on-line media, without fee, is hereby granted
    9 provided that the above copyright notice and this permission notice
   10 appear in all copies.
   11 AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
   12 MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
   13 OR IMPLIED WARRANTIES.
   14 /////////////////////////////////////////////////////////////////////////
   15 Content-Type:   program/C; charset=US-ASCII
   16 Program:    ftp.c (ftp/DeleGate)
   17 Author:     Yutaka Sato <ysato@etl.go.jp>
   18 Description:
   19 History:
   20     940305  created
   21     940321  added data client protocol
   22     950107  let service_ftp be half-duplex (single process)
   23     950107  introduced MODE XDC (Data transfer on Control connection)
   24     950504  Exit if server dead when waiting client's request
   25 ToDo:
   26     How about "PORT 0,0,0,0,N,M" instead of "MODE XDC" ?
   27 //////////////////////////////////////////////////////////////////////#*/
   28 #include <ctype.h>
   29 #include "ystring.h"
   30 #include "vsignal.h"
   31 #include "vsocket.h"
   32 #include "delegate.h"
   33 #include "filter.h"
   34 #include "fpoll.h"
   35 #include "file.h"
   36 #include "auth.h"
   37 #include "proc.h"
   38 
   39 int ShutdownSocket(int sock);
   40 int connectToSftpX(Connection *Conn,PCStr(host),int port,PCStr(user),int toC);
   41 int connectToSftpXX(Connection *Conn,PCStr(host),int port,PCStr(user),int toC,int fromC);
   42 int connect_ftp_data(Connection*Conn,PCStr(port),int cntrlsock,PCStr(lhost),int lport);
   43 void putFileInHTTP(FILE *tc,PCStr(path),PCStr(file));
   44 FILE *dirtar_fopen(PCStr(path));
   45 int bind_ftp_data(Connection*Conn,PVStr(mport),PCStr(server),int iport,int cntrlsock,int PASV,PCStr(lhost),int lport);
   46 
   47 char *scan_ls_l(PCStr(lsl),PVStr(mode),int *linkp,PVStr(owner),PVStr(group),FileSize *sizep,PVStr(date),PVStr(name),PVStr(sname));
   48 FILE *CTX_creat_ftpcache(Connection *Conn,PCStr(user),PCStr(host),int port,PCStr(path),PCStr(ext),PVStr(cpath),PVStr(xcpath));
   49 int ftp_EXPIRE(Connection *Conn);
   50 FILE *fopen_ftpcache0(Connection *Conn,int expire,PCStr(host),int port,PCStr(path),PCStr(ext),PVStr(cpath),int *isdirp,FileSize *sizep,int *mtimep);
   51 /*
   52 void ftp_xferlog(int start,PCStr(chost),int size,PCStr(path),int bin,int in,int anon,PCStr(user),PCStr(auser),PCStr(cstat));
   53 */
   54 void ftp_xferlog(int start,PCStr(chost),FileSize rest,FileSize size,PCStr(md5),PCStr(path),int bin,int in,int anon,PCStr(user),PCStr(auser),PCStr(cstat));
   55 FILE *fopen_ICP(Connection *Conn,PCStr(url),FileSize *sizep,int *datep);
   56 int stat_lpr(PCStr(lphost),int lpport,PCStr(queue),PCStr(opt),PCStr(fname),PVStr(stat));
   57 int rmjob_lpr(Connection*Conn,PCStr(lphost),int lpport,PCStr(queue),PCStr(user),PCStr(jobname),PVStr(stat));
   58 int send_lpr(Connection*Conn,PCStr(lphost),int lpport,PCStr(queue),FILE *dfp,int dlen,PCStr(user),PCStr(fname),PVStr(stat));
   59 
   60 extern int FTP_ACCEPT_TIMEOUT;
   61 extern int CON_TIMEOUT;
   62 extern int IO_TIMEOUT;
   63 
   64 enum _FtpBounce {
   65     FB_TH = 0x01, /* don't care (backward compatible) */
   66     FB_NO = 0x02, /* reject unconditionally (default) */
   67     FB_DO = 0x04, /* allow unconditionally */
   68     FB_CB = 0x08, /* callback to the client addr. */
   69     FB_RL = 0x10, /* reject conditionally by REJECT list */
   70 } FtpBounce;
   71 
   72 typedef struct {
   73     int fc_init;
   74   const char   *fc_proxy;   /* {user,open,site,path} */
   75   const char   *fc_usdelim; /* delimiter instead of '@' in user@site */
   76     int fc_nullPWD; /* allow 257 "" response for the PWD command */
   77     int fc_swMaster;    /* do server switch by user@site in MASTER */
   78     int fc_nodata;  /* don't do anything for data connection */
   79     int fc_chokedata;
   80     int fc_noxdcSV; /* don't use XDC with server */
   81     int fc_noxdcCL;
   82     int fc_doeprtSV;    /* do EPRT with server */
   83     int fc_doepsvCL;
   84     int fc_doepsvSV;    /* do EPSV with server */
   85     int fc_noepsvCL;
   86     int fc_nopasvSV;    /* don't use PASV with server */
   87     int fc_nopasvCL;
   88     int fc_noportSV;
   89     int fc_noportCL;
   90     int fc_bounce;  /* allow PORT directed not for the client */
   91     int fc_immportCL;   /* don't postpone PORT with the client */
   92     int fc_rawxdcSV;
   93     int fc_rawxdcCL;
   94     int fc_nostatSV;
   95     int fc_statinfo;    /* insert DeleGate status into reply text */
   96     int fc_forcexdcSV;
   97     int fc_noMLSxCL;    /* don't support MLSx */
   98     int fc_lpr_nowait;
   99     int fc_ftp_on_http;
  100     int fc_nounesc; /* dont unescape %XX to be forwarded to server */
  101     int fc_thruesc;
  102   const char   *fc_dfltuser;
  103     int fc_hideserv;
  104     int fc_maxreload;   /* max. cachefile size reloaded without verify */
  105     int fc_pasvdebug;
  106     int fc_debug;
  107   const char   *fc_uno;     /* maintain UNO */
  108     int fc_ccx;
  109     int fc_authproxy;
  110     int fc_waitSSL; /* wait SSL before returning 150 reponse */
  111 } FtpConf;
  112 
  113 #define CCX_ANYTYPE 0x01    /* CCX even for TYPE IMAGE */
  114 #define CCX_AUTOSVCC    0x02    /* CHARCODE=svcc:tosv automatically */
  115 #define CCX_COMMAND 0x04    /* CCX for command */
  116 #define CCX_RESPONSE    0x08    /* CCX for response */
  117 #define CCX_ANY 0xFF
  118 
  119 #define PXA_USERHOSTMAP 0x01    /* auth. USER u@s by AUTHORIZER=user{u}:*:s */
  120 #define PXA_HOSTMAP 0x02    /* auth. USER u@s by AUTHORIZER=user{u@s}:*:s */
  121 #define PXA_USERGEN 0x04    /* forward USER v by AUTHORIZER=user{u}(v) */
  122 #define PXA_AUTHGEN 0x08    /* apply MYAUTH */
  123 #define PXA_MAPANDGEN   (PXA_USERHOSTMAP|PXA_USERGEN|PXA_AUTHGEN)
  124 
  125 #define NUE_USER    0x0001
  126 #define NUE_PASS    0x0002
  127 #define NUE_PATH    0x0004
  128 #define NUE_ANY     0xFFFF
  129 
  130 #define fddup(x)    dup(x)
  131 
  132 #define comeq(com1,com2)    (strcasecmp(com1,com2) == 0)
  133 #define comUPLOAD(com) \
  134         (  comeq(com,"STOR") || comeq(com,"STOU") || comeq(com,"APPE") )
  135 #define comCHANGE(com) \
  136         (  comeq(com,"STOR") || comeq(com,"STOU") || comeq(com,"APPE") \
  137         || comeq(com,"RNFR") || comeq(com,"RNTO") || comeq(com,"DELE") \
  138         || comeq(com,"MKD")  || comeq(com,"RMD") )
  139 #define remote_path(path)   (strncmp(path,"//",2) == 0)
  140 
  141 #define DIRCOMS(com) \
  142         (  comeq(com,"CWD")  || comeq(com,"CDUP") \
  143         || comeq(com,"MLSD") \
  144         || comeq(com,"STAT") || comeq(com,"LIST") || comeq(com,"NLST") )
  145 
  146 #define RETRCOMS(com)    ( comeq(com,"NLST") || comeq(com,"LIST") \
  147             || comeq(com,"MLSD") \
  148             || comeq(com,"RETR") || comUPLOAD(com) )
  149 
  150 #define PATHCOMS(com)    ( RETRCOMS(com) \
  151             || comCHANGE(com) \
  152             || comeq(com,"STAT") \
  153             || comeq(com,"MLST") \
  154             || comeq(com,"MLSD") \
  155             || comeq(com,"SIZE") || comeq(com,"MDTM") )
  156 
  157 /* commands either with/without an argument */
  158 #define ARG0COMS(com)    ( comeq(com,"STAT") \
  159             || comeq(com,"NLST") || comeq(com,"LIST") \
  160             || comeq(com,"MLSD") || comeq(com,"MLST") \
  161              )
  162 
  163 /* PATHCOMS with status response only */
  164 #define STATCOMS(com)    ( comeq(com,"STAT") \
  165             || comeq(com,"CWD") \
  166             || comeq(com,"MKD") \
  167             || comeq(com,"RMD") \
  168             || comeq(com,"DELE") \
  169             || comeq(com,"RNFR") \
  170             || comeq(com,"RNTO") \
  171             || comeq(com,"MDTM") \
  172             || comeq(com,"SIZE") )
  173 
  174 #define fgetsFromST(b,s,f)  fgetsTimeout(b,s,f,FTP_FROMSERV_TIMEOUT)
  175 #define fgetsFromCT(b,s,f)  fgetsTimeout(b,s,f,FTP_FROMCLNT_TIMEOUT)
  176 
  177 #define XDC_OPENING "220-extended FTP [MODE XDC]"
  178 #define XDC_OPENING_B64 "220-extended FTP [MODE XDC][XDC/BASE64]"
  179 #define XDC_OPENING_NONE    "220-extended FTP "
  180 #define XDC_STAT    "999"
  181 #define XDC_PORT_TEMP   "999 PORT %s"
  182 #define XDC_PASV_PORT   "0,0,0,0,0,0"
  183 static const char *DFLT_MLS_FMT = \
  184  "Type=%xY;Size=%S;Modify=%xTD;Perm=%xP;Unique=%xU; %N";
  185 static const char *FEAT_MLS= \
  186  "Type*;Size*;Modify*;Perm*;Unique*;";
  187 
  188 typedef struct {
  189     int xdc_ON;     /* use MODE XDC */
  190   const char   *xdc_encode; /* use MODE XDC/BASE64 */
  191 } XDCmode;
  192 
  193 typedef struct {
  194     int fs_IAMCC; /* with a cached connection for the host:port */
  195     Connection *fs_Conn;
  196     Connection  fs_dataConn;
  197 
  198   const char   *fs_opening;
  199     MStr(   fs_proto,64);
  200     MStr(   fs_host,128);
  201     int fs_port;
  202     MStr(   fs_logindir,1024);
  203     int fs_logindir_isset;
  204     int fs_nocache;
  205     double  fs_timeout;
  206 
  207   const char   *fs_changesv;
  208     int fs_onLoginDir; /* start at the LoginDir(not the virtual root) */
  209     int fs_login1st;
  210     MStr(   fs_loginroot,256);
  211 
  212     MStr(   fs_myhost,128);
  213     MStr(   fs_myaddr,64);
  214     int fs_myport;
  215     int fs_imProxy;
  216     int fs_anonymous;
  217     int fs_anonymousOK;
  218     int fs_serverWithPASV;
  219     int fs_serverWithXDC;
  220 
  221     int fs_relaying_data;
  222     int fs_dsvsock;
  223     int fs_psvsock;
  224     int fs_dclsock;
  225     int fs_pclsock;
  226     MStr(   fs_dataError,32);
  227 
  228     XDCmode fs_XDC_SV;
  229     XDCmode fs_XDC_CL;
  230 
  231     int fs_PASVforPORT;
  232     int fs_PORTforPASV;
  233 
  234   const char   *fs_cstat;
  235     MStr(   fs_dport,128);
  236     MStr(   fs_mport,128);
  237     MStr(   fs_CWD,1024);
  238     int fs_islocal; /* CWD is on the local "file://localhost/" */
  239     MStr(   fs_prevVWD,1024); /* previous directory as a virtual URL */
  240     int fs_auth;
  241     MStr(   fs_auser,128);
  242     AuthInfo fs_proxyauth;
  243     AuthInfo fs_aub;
  244     MStr(   fs_acct,128);
  245 
  246     MStr(   fs_curcom,32);
  247     MStr(   fs_curarg,1024); /* the original argument not rewritten */
  248     MStr(   fs_opts,32);
  249        FileSize fs_REST;
  250        FileSize fs_RESTed;
  251     MStr(   fs_RNFR,1024);
  252     /*
  253     MStr(   fs_USER,128);
  254     */
  255     MStr(   fs_OUSER,128);  /* original USER argument */
  256   const char   *fs_rcUSER;
  257     MStr(   fs_PASS,128);
  258   const char   *fs_rcPASS;
  259     MStr(   fs_TYPE,128);
  260   const char   *fs_rcSYST;  /* SYST response line cache */
  261     MStr(   fs_qcTYPE,32);  /* TYPE request argument cache */
  262   const char   *fs_rcTYPE;  /* TYPE response line cache */
  263 
  264     AuthInfo fs_Ident; /* replaces fs_USER, fs_host and fs_port */
  265     int fs_authERR;
  266 
  267     FILE   *fs_ts;
  268     FILE   *fs_fs;
  269 
  270        FileSize fs_dsize; /* local file size or size in 150 ... (%u bytes) */
  271     int fs_dsrc;
  272     int fs_ddst;
  273     int fs_ABOR;
  274     int fs_peekError;
  275     int fs_dstwcc;
  276     MStr(   fs_ccxtosvb,64); /* CCX for path arg. over control conn. */
  277     MStr(   fs_ccxtomeb,64); /* CCX for path arg. over control conn. */
  278 } FtpStat;
  279 #define FSCCX_TOSV  (CCXP)FS->fs_ccxtosvb
  280 #define FSCCX_TOME  (CCXP)FS->fs_ccxtomeb
  281 
  282 #define IS_LOCAL    1
  283 #define IS_STAB     2
  284 #define IS_PXLOCAL  4
  285 
  286 #define fs_USER fs_Ident.i_user
  287 #define fs_host fs_Ident.i_Host
  288 #define fs_port fs_Ident.i_Port
  289 
  290 #define FS_NOAUTH   2 /* password authentication unnecessary */
  291 
  292 #define fs_XDCforSV fs_XDC_SV.xdc_ON
  293 #define fs_XDCencSV fs_XDC_SV.xdc_encode
  294 #define fs_XDCforCL fs_XDC_CL.xdc_ON
  295 #define fs_XDCencCL fs_XDC_CL.xdc_encode
  296 
  297 typedef struct {
  298     MStr(   fc_swcom,32);
  299     MStr(   fc_swopt,32);
  300     MStr(   fc_swarg,128);
  301     MStr(   fc_user,128);
  302     MStr(   fc_pass,128);
  303     MStr(   fc_acct,128);
  304     MStr(   fc_Path,128);
  305     MStr(   fc_type,64);
  306     MStr(   fc_opts,32);
  307     int fc_SUCcode;
  308     int fc_ERRcode;
  309     FILE   *fc_fc;
  310     FILE   *fc_tc;
  311     FtpStat *fc_callersFS;
  312 } FtpConn;
  313 
  314 int CC_TIMEOUT_FTP = 120;
  315 
  316 typedef struct {
  317     int  fe_FTP_FROMSERV_TIMEOUT;
  318     int  fe_FTP_FROMCLNT_TIMEOUT;
  319     int  fe_FTP_DELAY_REJECT_P;
  320     int  fe_CON_TIMEOUT_DATA;
  321     int  fe_MAX_FTPDATA_RETRY;
  322   const char    *fe_FTP_LIST_COM;
  323   const char    *fe_FTP_LIST_OPT;
  324   const char    *fe_FTP_LIST_OPT_ORIGIN;
  325     FtpConf  fe_FCF;
  326     FtpConn *fe_PFC;
  327     FtpStat *fe_PFS;
  328   const char    *fe_ftp_env_name;
  329     jmp_buf  fe_ftp_env;
  330     int  fe_relayingDATA;
  331 } FtpEnv;
  332 static FtpEnv *ftpEnv;
  333 #define FTP_FROMSERV_TIMEOUT    ftpEnv->fe_FTP_FROMSERV_TIMEOUT
  334 #define FTP_FROMCLNT_TIMEOUT    ftpEnv->fe_FTP_FROMCLNT_TIMEOUT
  335 #define FTP_DELAY_REJECT_P  ftpEnv->fe_FTP_DELAY_REJECT_P
  336 #define CON_TIMEOUT_DATA    ftpEnv->fe_CON_TIMEOUT_DATA
  337 #define MAX_FTPDATA_RETRY   ftpEnv->fe_MAX_FTPDATA_RETRY
  338 #define FTP_LIST_COM        ftpEnv->fe_FTP_LIST_COM
  339 #define FTP_LIST_OPT        ftpEnv->fe_FTP_LIST_OPT
  340 #define FTP_LIST_OPT_ORIGIN ftpEnv->fe_FTP_LIST_OPT_ORIGIN
  341 #define FCF     ftpEnv->fe_FCF
  342 #define PFC     ftpEnv->fe_PFC
  343 #define PFS     ftpEnv->fe_PFS
  344 #define ftp_env_name    ftpEnv->fe_ftp_env_name
  345 #define ftp_env     ftpEnv->fe_ftp_env
  346 #define relayingDATA    ftpEnv->fe_relayingDATA
  347 #define ftpDEBUG(f) (FCF.fc_debug & f)
  348 
  349 int scanFTPxHTTP(Connection *Conn,PCStr(cmdopt),PVStr(xcmd),PVStr(xopt),PVStr(xctype));
  350 static int ftpxhttpSIZE(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg));
  351 static int ftpxhttpSTAT(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg));
  352 static int ftpxhttpCHANGE(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg));
  353 int ftpxhttpSTOR(FtpStat *FS,FILE *tc,FILE *fc,PCStr(com),PCStr(arg),PVStr(path));
  354 int ftpxhttpLIST(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),PVStr(path),PVStr(head));
  355 int ftpxhttpRETR(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),PVStr(path));
  356 static int ftpxhttpAUTH(FtpStat *FS,PCStr(com),PCStr(arg));
  357 
  358 
  359 static int lookaside_cache(Connection *Conn,FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),int remote);
  360 static int put_get(FILE *ts,FILE *fs,PVStr(resp),int rsize,PCStr(fmt),...);
  361 void minit_ftp()
  362 {
  363     if( ftpEnv == 0 ){
  364         ftpEnv = NewStruct(FtpEnv);
  365         FTP_FROMSERV_TIMEOUT = 300;
  366         FTP_FROMCLNT_TIMEOUT = 900;
  367         FTP_DELAY_REJECT_P = 30;
  368         MAX_FTPDATA_RETRY = 1;
  369         FTP_LIST_COM = "LIST";
  370         FTP_LIST_OPT = "-lL";
  371         FTP_LIST_OPT_ORIGIN = "-lL";
  372         ftp_env_name = "FTP";
  373         FCF.fc_maxreload = 8*1024;
  374         FCF.fc_usdelim = "*%#";
  375     }
  376 }
  377 
  378 #define TYPE_IMAGE(FS) (toupper(FS->fs_qcTYPE[0])=='I')
  379 #define TYPE_ASCII(FS) (FS->fs_qcTYPE[0]==0 || toupper(FS->fs_qcTYPE[0])=='A')
  380 #define MODE(FS)    ((FS)->fs_anonymous ? PS_ANON : 0)
  381 
  382 static scanListFunc conf1(PCStr(conf))
  383 {   CStr(what,32);
  384     CStr(val,256);
  385 
  386     fieldScan(conf,what,val);
  387     if( strcaseeq(what,"DEFAULT") ){
  388         bzero(&FCF,sizeof(FCF));
  389         FCF.fc_maxreload = 8*1024;
  390         FCF.fc_usdelim = "*%#";
  391     }else
  392     if( strcaseeq(what,"PROXY") ){
  393         FCF.fc_proxy = stralloc(val);
  394     }else
  395     if( strcaseeq(what,"PROXYAUTH") ){
  396         if( *val == 0 )
  397             FCF.fc_authproxy |= PXA_MAPANDGEN;
  398         if( isinList(val,"userhostmap") )
  399             FCF.fc_authproxy |= PXA_USERHOSTMAP;
  400         if( isinList(val,"hostmap") )
  401             FCF.fc_authproxy |= PXA_HOSTMAP;
  402         if( isinList(val,"usergen") )
  403             FCF.fc_authproxy |= PXA_USERGEN;
  404         if( isinList(val,"authgen") )
  405             FCF.fc_authproxy |= PXA_AUTHGEN;
  406     }else
  407     if( strcaseeq(what,"USDELIM") ){
  408         FCF.fc_usdelim = stralloc(val);
  409     }else
  410     if( strcaseeq(what,"NULLPWD") ){
  411         FCF.fc_nullPWD = 1;
  412     }else
  413     if( strcaseeq(what,"SWMASTER") ){
  414         FCF.fc_swMaster = 1;
  415     }else
  416     if( strcaseeq(what,"NODATA") ){
  417         FCF.fc_nodata = 1;
  418     }else
  419     if( strcaseeq(what,"CHOKEDATA") ){
  420         FCF.fc_chokedata = atoi(val);
  421     }else
  422     if( strcaseeq(what,"DOEPRT") ){
  423         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_doeprtSV = 1;
  424     }else
  425     if( strcaseeq(what,"DOEPSV") ){
  426         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_doepsvCL = 1;
  427         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_doepsvSV = 1;
  428     }else
  429     if( strcaseeq(what,"NOEPSV") ){
  430         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_noepsvCL = 1;
  431     }else
  432     if( strcaseeq(what,"NOPASV") ){
  433         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_nopasvSV = 1;
  434         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_nopasvCL = 1;
  435     }else
  436     if( strcaseeq(what,"NOPORT") ){
  437         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_noportSV = 1;
  438         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_noportCL = 1;
  439     }else
  440     if( strcaseeq(what,"BOUNCE") ){
  441         if( strcaseeq(val,"no") ) FCF.fc_bounce = FB_NO;
  442         if( strcaseeq(val,"do") ) FCF.fc_bounce = FB_DO;
  443         if( strcaseeq(val,"th") ) FCF.fc_bounce = FB_TH;
  444         if( strcaseeq(val,"cb") ) FCF.fc_bounce = FB_CB;
  445         if( strcaseeq(val,"rl") ) FCF.fc_bounce = FB_RL;
  446     }else
  447     if( strcaseeq(what,"IMMPORT") ){
  448         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_immportCL = 1;
  449     }else
  450     if( strcaseeq(what,"WAITSSL") ){
  451         FCF.fc_waitSSL = 1;
  452     }else
  453     if( strcaseeq(what,"FORCEXDC") ){
  454         FCF.fc_forcexdcSV = 1;
  455     }else
  456     if( strcaseeq(what,"NOXDC") ){
  457         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_noxdcSV = 1;
  458         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_noxdcCL = 1;
  459     }else
  460     if( strcaseeq(what,"RAWXDC") ){
  461         if( *val == 0 || strcaseeq(val,"sv") ) FCF.fc_rawxdcSV = 1;
  462         if( *val == 0 || strcaseeq(val,"cl") ) FCF.fc_rawxdcCL = 1;
  463     }else
  464     if( strcaseeq(what,"TIMEOUT") ){
  465         FTP_FROMSERV_TIMEOUT = atoi(val);
  466         FTP_FROMCLNT_TIMEOUT = atoi(val);
  467     }else
  468     if( strcaseeq(what,"NOSTAT") ){
  469         FCF.fc_nostatSV = 1;
  470     }else
  471     if( strcaseeq(what,"NOMLSX") ){
  472         FCF.fc_noMLSxCL = 1;
  473     }else
  474     if( strcaseeq(what,"LPR_NOWAIT") ){
  475         FCF.fc_lpr_nowait = 1;
  476     }else
  477     if( strcaseeq(what,"FTP_ON_HTTP") ){
  478         FCF.fc_ftp_on_http = 1;
  479     }else
  480     if( strcaseeq(what,"UNO") ){
  481         FCF.fc_uno = stralloc(val);
  482     }else
  483     if( strcaseeq(what,"DFLTUSER") ){
  484         FCF.fc_dfltuser = stralloc(val);
  485     }else
  486     if( strcaseeq(what,"THRUESC") ){
  487         if( *val == 0 ){
  488             FCF.fc_nounesc = 1;
  489             FCF.fc_thruesc |= NUE_ANY;
  490         }
  491         if( isinListX(val,"user","c") )
  492             FCF.fc_thruesc |= NUE_USER;
  493         if( isinListX(val,"pass","c") )
  494             FCF.fc_thruesc |= NUE_PASS;
  495         if( isinListX(val,"path","c") ){
  496             FCF.fc_nounesc = 1;
  497             FCF.fc_thruesc |= NUE_PATH;
  498         }
  499         if( FCF.fc_thruesc & (NUE_USER|NUE_PASS) ){
  500             if( strchr(FCF.fc_usdelim,'%') ){
  501                 strcpy(val,FCF.fc_usdelim);
  502                 strsubst(AVStr(val),"%","");
  503                 FCF.fc_usdelim = stralloc(val);
  504             }
  505         }
  506     }else
  507     if( strcaseeq(what,"NOUNESC") ){
  508         FCF.fc_nounesc = 1;
  509     }else
  510     if( strcaseeq(what,"CCX") ){
  511         if( isinListX(val,"any",     "c") ) FCF.fc_ccx |= CCX_ANY;
  512         if( isinListX(val,"anytype", "c") ) FCF.fc_ccx |= CCX_ANYTYPE;
  513         if( isinListX(val,"autosvcc","c") ) FCF.fc_ccx |= CCX_AUTOSVCC;
  514         if( isinListX(val,"command", "c") ) FCF.fc_ccx |= CCX_COMMAND;
  515         if( isinListX(val,"response","c") ) FCF.fc_ccx |= CCX_RESPONSE;
  516     }else
  517     if( strcaseeq(what,"HIDESERV") ){
  518         FCF.fc_hideserv = 1;
  519     }else
  520     if( strcaseeq(what,"PASVDEBUG") ){
  521         FCF.fc_pasvdebug = 1;
  522     }else
  523     if( strcaseeq(what,"DEBUG") ){
  524         if( strneq(val,"0x",2) )
  525             sscanf(val+2,"%x",&FCF.fc_debug);
  526         else    FCF.fc_debug = atoi(val);
  527     }else
  528     if( strcaseeq(what,"MAXRELOAD") ){
  529         FCF.fc_maxreload = atoi(val);
  530     }else{
  531         sv1log("ERROR: unknown FTPCONF=%s\n",conf);
  532     }
  533     return 0;
  534 }
  535 static void init_conf()
  536 {   
  537     if( FCF.fc_init )
  538         return;
  539     FCF.fc_init = 1;
  540     if( getenv("NOPASV")    ) conf1("NOPASV");
  541     if( getenv("FORCEXDC")  ) conf1("FORCEXDC");
  542     if( getenv("NOXDC")     ) conf1("NOXDC");
  543     if( getenv("NOSTAT")    ) conf1("NOSTAT");
  544     if( getenv("LPR_NOWAIT")) conf1("LPR_NOWAIT");
  545     if( getenv("FTP_ON_HTTP") ) conf1("FTP_ON_HTTP");
  546 }
  547 void scan_FTPCONF(Connection *Conn,PCStr(conf))
  548 {
  549     init_conf();
  550     scan_commaListL(conf,0,scanListCall conf1);
  551 }
  552 
  553 static int rewrite_CWD(FtpStat *FS,PVStr(req),PVStr(arg),FILE *tc);
  554 static int changesv(Connection *Conn,FtpStat *FS,PCStr(wh),PVStr(cserv)){
  555     IStr(url,256);
  556 
  557     if( FS->fs_changesv && streq(FS->fs_changesv,wh) ){
  558         strcpy(url,"/");
  559         rewrite_CWD(FS,VStrNULL,AVStr(url),NULL);
  560         if( !FS->fs_islocal && strneq(url,"//",2) ){
  561             strcpy(cserv,url+2);
  562             return 1;
  563         }
  564     }
  565     return 0;
  566 }
  567 static int FTP_getMountOpts(PCStr(wh),Connection *Conn,FtpStat *FS,PCStr(opts)){
  568     IStr(opt1,128);
  569 
  570     if( opts == 0 ){
  571         return 0;
  572     }
  573     if( getOpt1(opts,"servon",AVStr(opt1)) ){
  574         FS->fs_changesv = stralloc(opt1);
  575     }
  576     if( getOpt1(opts,"timeout",AVStr(opt1)) ){
  577         FS->fs_timeout = Scan_period(opt1,'s',(double)0);
  578     }
  579     return 1;
  580 }
  581 
  582 #include "param.h"
  583 void scan_FTPCONFm(Connection *Conn,PCStr(conf)){
  584     init_conf();
  585     Verbose("%s=%s (MOUNT)\n",P_FTPCONF,conf);
  586     scan_commaListL(conf,0,scanListCall conf1);
  587 }
  588 static void setupFCF(Connection *Conn){
  589     IStr(conf,1024);
  590     int ci;
  591 
  592     for( ci=0; 0 <= (ci=find_CMAPi(Conn,P_FTPCONF,ci,AVStr(conf))); ci++ ){
  593         Verbose("%s=%s (CMAP)\n",P_FTPCONF,conf);
  594         scan_FTPCONF(Conn,conf);
  595     }
  596     if( MountOptions ){
  597         const char *mo,*confv;
  598         for( mo = MountOptions; *mo; ){
  599             mo = scan_ListElem1(mo,',',AVStr(conf));
  600             if( confv = parameq(conf,P_FTPCONF) ){
  601                 Verbose("%s=%s (MOUNT)\n",P_FTPCONF,confv);
  602                 scan_FTPCONF(Conn,confv);
  603             }
  604         }
  605     }
  606 }
  607 
  608 static void CCXreq(Connection *Conn,FtpStat *FS,PCStr(in),PVStr(qb),int qz){
  609     CCXP ccx;
  610 
  611     if( FCF.fc_ccx & CCX_COMMAND ){
  612         if( CCXactive(FSCCX_TOSV) || CCXactive(CCX_TOSV) ){
  613             if( CCXactive(CCX_TOSV) )
  614                 ccx = CCX_TOSV;
  615             else    ccx = FSCCX_TOSV;
  616             CCXexec(ccx,in,strlen(in),AVStr(qb),qz);
  617         }
  618         if( isWindowsCE() )
  619         if( CCXactive(FSCCX_TOME) ){
  620             CCXexec(FSCCX_TOME,in,strlen(in),AVStr(qb),qz);
  621         }
  622     }
  623 }
  624 static void CCXresp(Connection *Conn,FtpStat *FS,PCStr(in),PVStr(rb),int rz){
  625     CCXP ccx;
  626     if( FCF.fc_ccx & CCX_RESPONSE ){
  627         if( CCXactive(CCX_TOCL) ){
  628             ccx = CCX_TOCL;
  629             CCXexec(ccx,in,strlen(in),AVStr(rb),rz);
  630         }
  631     }
  632 }
  633 
  634 static void sigPIPE(int sig){
  635     if( relayingDATA ){
  636         svlog("FTP got SIG%s: data relay aborted\n",sigsym(sig));
  637         signal(SIGPIPE,sigPIPE);
  638         relayingDATA |= 2;
  639         return;
  640     }
  641     svlog("FTP got SIG%s: longjump to %s\n",sigsym(sig),ftp_env_name);
  642     signal(SIGPIPE,SIG_IGN);
  643     longjmp(ftp_env,sig);
  644 }
  645 static int _sigXFSZ;
  646 static void sigXFSZ(int sig){
  647     _sigXFSZ++;
  648     daemonlog("E","caught SIGZFSZ\n");
  649 }
  650 
  651 static void command_log(PCStr(fmt),...)
  652 {
  653     VARGS(8,fmt);
  654     Verbose(fmt,VA8);
  655 }
  656 
  657 static void init_FS(FtpStat *FS,Connection *Conn)
  658 {
  659     bzero(FS,sizeof(FtpStat));
  660     FS->fs_dsvsock = -1;
  661     FS->fs_psvsock = -1;
  662     FS->fs_dclsock = -1;
  663     FS->fs_pclsock = -1;
  664     FS->fs_XDCencCL = "";
  665     FS->fs_XDCencSV = "";
  666 
  667     if( FCF.fc_nopasvSV )
  668         FS->fs_serverWithPASV = -1;
  669 
  670     FS->fs_Conn = Conn;
  671     FS->fs_dataConn = *Conn;
  672     FS->fs_dataConn.xf_filters = 0;
  673     strcpy(FS->fs_dataConn.sv.p_proto,"ftp-data"); /* REAL_PROTO */
  674 
  675     if( CCXactive(CCX_TOCL) || CCXactive(CCX_TOSV) ){
  676         FCF.fc_noxdcSV = 1;
  677         FCF.fc_noxdcCL = 1;
  678     }
  679 }
  680 static void save_FS(FtpStat *FS)
  681 {
  682     if( PFS ){
  683         PFS->fs_XDCforCL = FS->fs_XDCforCL;
  684         PFS->fs_XDCencCL = FS->fs_XDCencCL;
  685         PFS->fs_dclsock = FS->fs_dclsock;
  686         PFS->fs_pclsock = FS->fs_pclsock;
  687         PFS->fs_dsvsock = FS->fs_dsvsock;
  688         PFS->fs_psvsock = FS->fs_psvsock;
  689         strcpy(PFS->fs_dport,FS->fs_dport);
  690         strcpy(PFS->fs_mport,FS->fs_mport);
  691         PFS->fs_REST = FS->fs_REST;
  692     }
  693 }
  694 static void set_client_mode(FtpStat *FS,PCStr(mode))
  695 {
  696     if( strncasecmp(mode,"XDC",3) == 0 ){
  697         if( strncasecmp(mode+3,"/BASE64",7) == 0 )
  698             FS->fs_XDCencCL = "/BASE64";
  699         else    FS->fs_XDCencCL = "";
  700         FS->fs_XDCforCL = 1;
  701     }else{
  702         FS->fs_XDCforCL = 0;
  703         FS->fs_XDCencCL = "";
  704     }
  705 }
  706 
  707 static int ServSock(Connection *Conn,FILE *ts,PCStr(where))
  708 {   int toS;
  709 
  710     if( ConnType == 'y' && 0 <= ServerSockX ){
  711         toS = ServerSockX;
  712     }else
  713     if( (Conn->xf_filters & XF_SERVER) && 0 <= ToSX ){
  714         sv1log("## viaCFI [%s]: fileno(ts)=%d ToSX=%d\n",
  715             where,fileno(ts),ToSX);
  716         toS = ToSX;
  717     }else   toS = fileno(ts);
  718     return toS;
  719 }
  720 
  721 int is_anonymous(PCStr(user))
  722 {
  723     if( strcaseeq(user,"ftp") || strcaseeq(user,"anonymous") )
  724         return 1;
  725     if( strncmp(user,"ftp@",4)==0 || strncmp(user,"anonymous@",9)==0 )
  726         return 1;
  727     return 0;
  728 }
  729 
  730 static int com_scode(PCStr(swcom),PCStr(passw),int *scodep,int *ecodep)
  731 {   int scode,ecode;
  732 
  733     scode = 220;
  734     ecode = 500;
  735     if( strcaseeq(swcom,"PASS") ){ scode = 230; ecode = 530; }else
  736     if( strcaseeq(swcom,"OPEN") ){ scode = 220; ecode = 421; }else
  737     if( strcaseeq(swcom,"USER") ){
  738         if( passw[0] )       { scode = 230; ecode = 530; }
  739         else             { scode = 331; ecode = 530; }
  740     }else
  741     if( strcaseeq(swcom,"CWD")  ){ scode = 250; ecode = 550; }
  742     else
  743     if( strcaseeq(swcom,"LIST") ){ scode = 150; ecode = 550; }else
  744     if( strcaseeq(swcom,"NLST") ){ scode = 150; ecode = 550; }else
  745     if( strcaseeq(swcom,"RETR") ){ scode = 150; ecode = 550; }else
  746     if( comUPLOAD(swcom)        ){ scode = 150; ecode = 550; }else
  747     if( comCHANGE(swcom)        ){ scode = 250; ecode = 550; }else
  748     if( strcaseeq(swcom,"STAT") ){ scode = 211; ecode = 550; }else
  749     if( strcaseeq(swcom,"SIZE") ){ scode = 213; ecode = 550; }else
  750     if( strcaseeq(swcom,"MDTM") ){ scode = 213; ecode = 550; }
  751 
  752     *scodep = scode;
  753     *ecodep = ecode;
  754     return 0;
  755 }
  756 
  757 static void putXcomplete(Connection *Conn,FtpStat *FS,FILE *tc,FileSize wcc){
  758     if( FS->fs_ABOR )
  759         fprintf(tc,"226 Abort successful\r\n");
  760     else    fprintf(tc,"226 Transfer complete (%lld bytes)\r\n",wcc);
  761 }
  762 
  763 static int check_server(PCStr(server),PCStr(swcom),FILE *tc)
  764 {
  765     return 0;
  766 }
  767 static int user_permitted(Connection *Conn,FtpStat *FS,FILE *tc,PCStr(serv),int port,PCStr(user))
  768 {   CStr(clnt,128);
  769 
  770     if( strcaseeq(FS->fs_proto,"sftp") ){
  771         /* permission should be checked with "sftp" */
  772     }
  773     set_SERVER(Conn,"ftp",serv,port);
  774     wordScan(user,REAL_USER);
  775     getClientHostPort(Conn,AVStr(clnt));
  776     if( FS->fs_proxyauth.i_user[0] ){
  777         AuthInfo sident;
  778         sident = ClientAuth;
  779         ClientAuth = FS->fs_proxyauth;
  780         if( service_permitted2(Conn,DST_PROTO,1) ){
  781             Xsprintf(TVStr(clnt),"<%s@%s>",ClientAuthUser,ClientAuthHost);
  782         }else{
  783             ClientAuth = sident;
  784         }
  785     }
  786 
  787     if( service_permitted(Conn,DST_PROTO) ){
  788         sv1log("FTP LOGIN FROM %s TO %s@%s\n",clnt,user,serv);
  789         fputLog(Conn,"Login","FTP; from=%s; to=%s@%s\n",clnt,user,serv);
  790         return 1;
  791     }else{
  792         sv1log("--U 553 Permission denied by DeleGate.\r\n");
  793         fprintf(tc,"553 Permission denied by DeleGate.\r\n");
  794         fflush(tc);
  795         sv1log("FTP LOGIN FROM %s TO %s@%s REJECTED\n",clnt,user,serv);
  796         return 0;
  797     }
  798 }
  799 
  800 static void replace_atmark(PCStr(com),char arg[])
  801 {   const char *xp;
  802 
  803     if( strchr(arg,'@') )
  804         return;
  805 
  806     if( *FCF.fc_usdelim )
  807     if( xp = strrpbrk(arg,FCF.fc_usdelim) ){
  808         sv1log("%s [replace %c with @] %s\n",com,*xp,arg);
  809         *(char*)xp = '@';
  810     }
  811 }
  812 
  813 /*
  814  * 9.2.2 after an explicit switching to a server is done as
  815  * "CWD //server" or "USER user@server", and if there is no explicit
  816  * MOUNT to the server, there is an implicit MOUNT introduced as
  817  *   MOUNT="/* ftp://server/*"
  818  * which overrides the default -stab- MOUNT on the virtual root.
  819  */
  820 static void clearAsProxy(Connection *Conn,PCStr(where)){
  821     if( ClientFlags & PF_AS_PROXY ){
  822         Verbose("clearAsProxy [%s]\n",where);
  823         ClientFlags &= ~PF_AS_PROXY;
  824     }
  825 }
  826 /*
  827 static void setAsProxy(Connection *Conn,PCStr(com),PCStr(arg)){
  828 */
  829 static void setAsProxy(Connection *Conn,FtpStat *FS,PCStr(com),PCStr(arg)){
  830     if( IsMounted ){
  831         if( ClientFlags & PF_AS_PROXY ){
  832             clearAsProxy(Conn,"change_server");
  833         }
  834         return;
  835     }
  836     if( comeq(com,"CWD") ){
  837         if( remote_path(arg) ){
  838             ClientFlags |= PF_AS_PROXY;
  839         }
  840     }else
  841     if( comeq(com,"USER") ){
  842         if( strchr(arg,'@') ){
  843             ClientFlags |= PF_AS_PROXY;
  844         }
  845     }else
  846     if( comeq(com,"PASS") ){
  847         if( strchr(FS->fs_OUSER,'@')
  848          || *FCF.fc_usdelim && strpbrk(FS->fs_OUSER,FCF.fc_usdelim)
  849         ){
  850             ClientFlags |= PF_AS_PROXY;
  851         }
  852     }else
  853     if( comeq(com,"SITE") || comeq(com,"OPEN") ){
  854         ClientFlags |= PF_AS_PROXY;
  855     }
  856     /*
  857      * switching without MOUNTed, then it should be AS_PROXY, without
  858      * any other conditions checked like above... but maybe
  859      * IsMounted is not allways set and cleared (maybe) ...
  860      */
  861     /* should use isMounted() function ? */
  862 }
  863 
  864 static int stripArgOpts(PVStr(arg),PVStr(opt));
  865 static char *getVUpathX(FtpStat *FS,PCStr(cpath),PVStr(vpath),int remote);
  866 static int change_server(Connection *Conn,FtpStat *FS,FILE *fc,FILE *tc,PCStr(swcom),PCStr(server),char cuser[],PCStr(cpass),PCStr(ctype))
  867 {   CStr(serv,128);
  868     const char *dp;
  869     const char *xp;
  870     CStr(dpass,256);
  871     const char *ppass;
  872     CStr(duser,256);
  873     AuthInfo ident;
  874     int port;
  875     int shops;
  876     int svsock;
  877     int rcode = 0;
  878     FtpConn *PFCsav;
  879 
  880     SERVREQ_SERNO = 0;
  881 
  882     PFCsav = PFC;
  883     if( PFC == NULL )
  884         PFC = (FtpConn*)calloc(sizeof(FtpConn),1);
  885     /*
  886     setAsProxy(Conn,FS->fs_curcom,FS->fs_curarg);
  887     */
  888     setAsProxy(Conn,FS,FS->fs_curcom,FS->fs_curarg);
  889     PFC->fc_callersFS = FS;
  890     PFC->fc_fc = fc;
  891     PFC->fc_tc = tc;
  892 
  893     if( PATHCOMS(swcom) ){
  894         getVUpathX(FS,FS->fs_CWD,AVStr(FS->fs_prevVWD),0);
  895     }
  896 
  897     if( !IsMounted )
  898     if( !PATHCOMS(swcom) && !do_RELAY(Conn,RELAY_PROXY)
  899      ||  PATHCOMS(swcom) && !do_RELAY(Conn,RELAY_DELEGATE)
  900     ){
  901         int ok,no;
  902         com_scode(swcom,cpass,&ok,&no);
  903         fprintf(tc,"%d Forbidden by DeleGate.\r\n",no);
  904         return -1;
  905     }
  906     IsMounted = 0;
  907 
  908 
  909     svsock = -1;
  910     if( strrchr(server,'@') ){
  911         xp = scan_userpassX(server,&ident);
  912         if( *xp == '@' ){
  913             if( *ident.i_user ) cuser = ident.i_user;
  914             if( *ident.i_pass ) cpass = ident.i_pass;
  915             if( (FCF.fc_thruesc & NUE_PASS) == 0 ){
  916             nonxalpha_unescape(cpass,AVStr(dpass),1);
  917             if( strcmp(cpass,dpass) != 0 ){
  918                 sv1log("unescaped password for %s\n",cuser);
  919                 cpass = dpass;
  920             }
  921             }
  922             server = xp+1;
  923         }
  924     }
  925     if( (FCF.fc_thruesc & NUE_USER) == 0 )
  926     if( nonxalpha_unescape(cuser,AVStr(duser),1) )
  927         cuser = duser;
  928     replace_atmark(swcom,cuser);
  929 
  930     strcpy(PFC->fc_swcom,swcom);
  931     strcpy(PFC->fc_swarg,FS->fs_curarg);
  932     stripArgOpts(AVStr(PFC->fc_swarg),AVStr(PFC->fc_swopt));
  933     com_scode(swcom,cpass,&PFC->fc_SUCcode,&PFC->fc_ERRcode);
  934     strcpy(PFC->fc_user,cuser);
  935     strcpy(PFC->fc_pass,cpass);
  936     strcpy(PFC->fc_type,ctype);
  937     wordScan(FS->fs_opts,PFC->fc_opts);
  938     wordScan(FS->fs_acct,PFC->fc_acct);
  939 
  940     wordScan(server,serv);
  941     if( dp = strchr(serv,'/') )
  942         truncVStr(dp);
  943     if( dp = strchr(server,'/') ){
  944         if( strncmp(dp,"//",2) != 0 )
  945             dp++;
  946         wordScan(dp,PFC->fc_Path);
  947 /*
  948         if( !FCF.fc_nounesc )
  949             nonxalpha_unescape(PFC->fc_Path,PFC->fc_Path,1);
  950 */
  951     }else   PFC->fc_Path[0] = 0;
  952 
  953     if( xp = strrchr(serv,':') ){
  954         truncVStr(xp);
  955         xp++;
  956         port = atoi(xp);
  957     }else
  958     if( strcaseeq(FS->fs_proto,"sftp") ){
  959         port = 22;
  960     }else
  961     if( ServerFlags & PF_SSL_IMPLICIT ){ /* set by ftps://serv/ */
  962         port = 990;
  963     }else   port = serviceport("ftp");/*DFLT_PORT;*/
  964 
  965     if( 0 <= svsock ){
  966         Verbose("USER %s\n",cuser);
  967         port = getpeerNAME(svsock,AVStr(serv));
  968     }else{
  969         if( is_anonymous(cuser) )
  970             ppass = cpass;
  971         else    ppass = "****";
  972         Verbose("CWD //[%s:%s]@%s:%d\n",cuser,ppass,serv,port);
  973     }
  974     if( serv[0] == 0 )
  975         strcpy(serv,"localhost");
  976 
  977     if( user_permitted(Conn,FS,tc,serv,port,cuser) ){
  978         shops = D_FTPHOPS;
  979         if( strcaseeq(FS->fs_proto,"sftp") ){
  980             sv1log("-SFTPGW:change_server REAL_PROTO=sftp <-FS\n");
  981             strcpy(REAL_PROTO,FS->fs_proto);
  982         }
  983         Conn->co_setup = 0; /* reset CONNECT env. in repetition */
  984         execSpecialist(Conn,FromC,tc,svsock);
  985         D_FTPHOPS = shops;
  986         rcode = 0;
  987     }else{
  988         rcode = -1;
  989     }
  990 
  991     if( PFCsav == NULL )
  992         free(PFC);
  993     PFC = PFCsav;
  994     return rcode;
  995 }
  996 
  997 int saveSTLS(PCStr(wh),Connection *Conn,int SF[4]);
  998 int restoreSTLS(PCStr(wh),Connection *Conn,int SF[4]);
  999 static int makeDataConn(Connection *Conn,PCStr(dport),int cntrlsock,int pasv)
 1000 {   int dsock;
 1001     CStr(host,64);
 1002     CStr(hostport,64);
 1003     int port,a0,a1,a2,a3,p0,p1;
 1004     int xtry;
 1005     CStr(xproto,64);
 1006     CStr(myhost,MaxHostNameLen);
 1007     CStr(dshost,MaxHostNameLen);
 1008     int myport,dsport;
 1009     const char *proto;
 1010     IStr(xhost,MaxHostNameLen);
 1011     int xport;
 1012     int SF[4] = {0};
 1013 
 1014     if( ServerFlags & (PF_SSL_ON|PF_STLS_ON) ){
 1015         /* 9.9.7 CONNECT ftp-data via SOCKS/ssl */
 1016         /* this should be done with dataConn with clear ServerFlags */
 1017         saveSTLS("makeDataConn",Conn,SF);
 1018         clearSTLS(Conn);
 1019     }
 1020 /*
 1021     if( !isREACHABLE("ftp",hostport) ){
 1022         Verbose("DataConn to unreachable host [%s]\n",hostport);
 1023         return -1;
 1024     }
 1025 */
 1026 
 1027     xtry = 0;
 1028     xport = REAL_PORT;
 1029     strcpy(xhost,REAL_HOST);
 1030     strcpy(xproto,REAL_PROTO);
 1031     strcpy(REAL_PROTO,"ftp-data"); /* getViaSocks() requires it */
 1032 
 1033     dsport = getpeerAddr(cntrlsock,AVStr(dshost));
 1034     if( dsport <= 0 )
 1035         strcpy(dshost,"0.0.0.0");
 1036     myhost[0] = 0;
 1037     myport = 0;
 1038     proto = pasv?"ftp-data-pasv-src":"ftp-data-port-src";
 1039     if( !SRCIFfor(Conn,proto,dshost,dsport,AVStr(myhost),&myport) )
 1040     if( !SRCIFfor(Conn,"ftp-data-src",dshost,dsport,AVStr(myhost),&myport) )
 1041     SRCIFfor(Conn,"ftp-data",dshost,dsport,AVStr(myhost),&myport);
 1042 
 1043     for(;;){
 1044     dsock = connect_ftp_data(Conn,dport,cntrlsock,myhost,myport);
 1045         if( 0 <= dsock )
 1046             break;
 1047         if( MAX_FTPDATA_RETRY <= ++xtry )
 1048             break;
 1049         sv1log("FTP data connection failed (%d), retrying...\n",xtry);
 1050         msleep(200);
 1051     }
 1052     REAL_PORT = xport;
 1053     strcpy(REAL_HOST,xhost);
 1054     strcpy(REAL_PROTO,xproto);
 1055     /* It should be connect_to_server() to tcprelay://a.b.c.d:ef/
 1056      * so that it can be controlled and relayed with DeleGate's routing.
 1057      */
 1058 
 1059     set_keepalive(dsock,1);
 1060     restoreSTLS("makeDataConn",Conn,SF);
 1061     return dsock;
 1062 }
 1063 
 1064 static void insert_scode(PCStr(str),FILE *dst,int scode)
 1065 {   const char *sp;
 1066     const char *np;
 1067     char ch;
 1068     char pch = 0;
 1069 
 1070     for( sp = str; sp && *sp; sp = np ){
 1071         fprintf(dst,"%d- ",scode);
 1072         for( np = sp; ch = *np; np++ ){
 1073             if( ch == '\n' && pch != '\r' ){
 1074                 putc('\r',dst);
 1075             }
 1076             putc(ch,dst);
 1077             pch = ch;
 1078             if( ch == '\n' ){
 1079                 np++;
 1080                 break;
 1081             }
 1082         }
 1083     }
 1084 }
 1085 /*
 1086 static void escape_scode(PVStr(str),FILE *dst)
 1087 */
 1088 #define escape_scode(s,d)   escape_scodeX(scode,s,d)
 1089 static void escape_scodeX(int iscode,PVStr(str),FILE *dst)
 1090 {   refQStr(sp,str); /**/
 1091     const char *np;
 1092     char ch;
 1093     int len;
 1094 
 1095     for( cpyQStr(sp,str); sp && *sp; sp = (char*)np ){
 1096         if( !ftpDEBUG(0x800) && isdigit(sp[0]) && atoi(sp) != iscode ){
 1097             Strins(AVStr(sp)," ");
 1098         }else
 1099         if( isdigit(sp[0]) && isdigit(sp[1]) && isdigit(sp[2]) ){
 1100             sp += 3;
 1101             ch = *sp;
 1102             if( ch==' ' || ch=='\t' || ch=='\r' || ch=='\n' ){
 1103                 assertVStr(str,sp+strlen(sp)+1);
 1104                 for( len = strlen(sp); 0 <= len; len-- )
 1105                     setVStrElem(sp,len+1,sp[len]);
 1106                 setVStrElem(sp,0,'-');
 1107             }
 1108         }
 1109         if( np = strchr(sp,'\n') )
 1110             np++;
 1111     }
 1112     if( dst != NULL )
 1113         fputs(str,dst);
 1114 }
 1115 
 1116 /*
 1117  *  get virtual URL path of given MOUNTed URL
 1118  */
 1119 static char *getVUpath(FtpStat *FS,PCStr(cpath),PVStr(vpath))
 1120 {
 1121     return getVUpathX(FS,cpath,BVStr(vpath),0);
 1122 }
 1123 static char *getVUpathX(FtpStat *FS,PCStr(cpath),PVStr(vpath),int remote)
 1124 {   const char *proto;
 1125     CStr(hostport,MaxHostNameLen);
 1126     const char *path;
 1127     CStr(vurl,1024);
 1128     CStr(xpath,1024);
 1129     const char *np;
 1130 
 1131     /*
 1132      * should return NULL if cpath is not mounted URL...
 1133      * in case such as MOUNT="/xxx/* /*" with CWD="/"
 1134      * 9.2.2 return a stab virtual URL for non-MOUNTed path.
 1135      */
 1136     if( FS->fs_islocal == IS_STAB ){
 1137         if( strneq(cpath,"/-stab-",7) ){
 1138             strcpy(vpath,cpath+7);
 1139             return (char*)vpath;
 1140         }
 1141     }
 1142 
 1143     /* 9.2.3 "fs_islocal" can be set by restoreCWD() meaning that
 1144      * the current working directroy (prevVWD) is left on the local
 1145      * even after it connected to a remote server "fs_host" when the
 1146      * server switch is invoked by a non CWD command.
 1147      * But the path to be virtualized here can be a remote one of the
 1148      * remote server like the one in the MLST reponse.
 1149     if( FS->fs_islocal || FS->fs_host[0] == 0 ){
 1150     */
 1151     if( !remote && FS->fs_islocal || FS->fs_host[0] == 0 ){
 1152         proto = "file";
 1153         sprintf(hostport,"%s:%d","localhost",0);
 1154     }else{
 1155         proto = "ftp";
 1156         sprintf(hostport,"%s:%d",FS->fs_host,FS->fs_port);
 1157     }
 1158     if( cpath[0] == '/' )
 1159         path = cpath + 1;
 1160     else    path = cpath;
 1161 
 1162 domatch:
 1163     if( CTX_mount_url_fromL(FS->fs_Conn,AVStr(vurl),proto,hostport,path,NULL,"ftp","-.-") ){
 1164         setVStrElem(vpath,0,'/');
 1165         decomp_absurl(vurl,VStrNULL,VStrNULL,QVStr(vpath+1,vpath),1024-1);
 1166         return (char*)vpath;
 1167     }else{
 1168         if( *path && strtailchr(path) != '/' ){
 1169             sprintf(xpath,"%s/",path);
 1170             path = xpath;
 1171             goto domatch;
 1172         }
 1173         strcpy(vpath,path);
 1174         return NULL;
 1175     }
 1176 }
 1177 static int get_VPWD(FtpStat *FS,PVStr(cwd),PVStr(npath))
 1178 {   const char *tp;
 1179 
 1180 /*
 1181     if( FS->fs_CWD[0] )
 1182         strcpy(cwd,FS->fs_CWD);
 1183     else    strcpy(cwd,FS->fs_logindir);
 1184 */
 1185     strcpy(cwd,FS->fs_CWD);
 1186 
 1187     if( getVUpath(FS,cwd,AVStr(npath)) == NULL )
 1188         return -1;
 1189 
 1190     if( tp = strrchr(npath,'/') )
 1191     if( tp != npath && tp[1] == 0 )
 1192         truncVStr(tp);
 1193     return 1;
 1194 }
 1195 static const char *isLocal(FtpStat *FS,PCStr(method),PCStr(rpath),int isdir,PVStr(mpath))
 1196 {   const char *proto;
 1197     CStr(hostport,MaxHostNameLen);  
 1198     refQStr(cpath,mpath); /**/
 1199     CStr(vurl,1024);
 1200     CStr(vup,1024);
 1201     const char *opts;
 1202     CStr(delegate,MaxHostNameLen);
 1203 
 1204     /* relative path from non-local directory is non-local too ... */
 1205     if( rpath[0] != '/' )
 1206     if( !FS->fs_islocal )
 1207         return 0;
 1208 
 1209     if( ftpDEBUG(0x100) == 0 )
 1210     if( rpath[0] != '/' && FS->fs_islocal ){
 1211         CStr(xrpath,1024);
 1212         getVUpath(FS,FS->fs_CWD,AVStr(xrpath));
 1213         if( xrpath[0] != '/' )
 1214             Strins(AVStr(xrpath),"/");
 1215         chdir_cwd(AVStr(xrpath),rpath,1);
 1216         sv1log("isLocal? [%s][%s] => %s\n",FS->fs_CWD,rpath,xrpath);
 1217         rpath = xrpath;
 1218     }
 1219 
 1220     if( rpath[0] == '/' ){
 1221         strcpy(vurl,rpath);
 1222         if( opts = CTX_mount_url_to(FS->fs_Conn,NULL,method,AVStr(vurl)) ){
 1223             if( strncaseeq(vurl,"ftp://",6) ){
 1224                 /* 9.9.1 maybe "fs_islocal" is set by
 1225                  * restoreCWD(1) to indicate staying yet on
 1226                  * the virtual root without doing CWD
 1227                  */
 1228                 sv1log("#NOT islocal(%s)%d(%s)\n",
 1229                     rpath,FS->fs_islocal,vurl);
 1230                 return 0;
 1231             }
 1232             setVStrElem(mpath,0,'/');
 1233             decomp_absurl(vurl,VStrNULL,VStrNULL,QVStr(mpath+1,mpath),1024-1);
 1234             if( isWindows() ){
 1235                 if( mpath[0] == '/' && isFullpath(mpath+1) ){
 1236                     ovstrcpy((char*)mpath,mpath+1);
 1237                 }
 1238             }
 1239             PageCountUpURL(FS->fs_Conn,CNT_ACCESS|CNT_INCREMENT,rpath,NULL);
 1240             return opts;
 1241         }
 1242     }
 1243 
 1244     strcpy(mpath,FS->fs_CWD);
 1245     chdir_cwd(AVStr(mpath),rpath,1);
 1246     if( !isdir )
 1247         isdir |= fileIsdir(mpath);
 1248     if( mpath[0] == '/' )
 1249         cpath = (char*)mpath + 1;
 1250     else    cpath = (char*)mpath;
 1251 
 1252     /*
 1253      *  cover MOUNT="/X*  /dir*"   also
 1254      *  by    MOUNT="/X/* /dir/*"
 1255      */
 1256     if( isdir && strtailchr(cpath) != '/' )
 1257         strcat(cpath,"/");
 1258 
 1259     proto = "file";
 1260     sprintf(hostport,"%s:%d","localhost",0);
 1261     ClientIF_HP(FS->fs_Conn,AVStr(delegate));
 1262 
 1263     opts = CTX_mount_url_fromL(FS->fs_Conn,AVStr(vurl),proto,hostport,cpath,
 1264         NULL,"ftp",delegate);
 1265     if( opts ){
 1266         eval_mountOptions(FS->fs_Conn,opts);
 1267         sv1log("MOUNTED LOCAL [%s] = [%s] opt=%s\n",vurl,mpath,opts);
 1268 
 1269         decomp_absurl(vurl,VStrNULL,VStrNULL,AVStr(vup),sizeof(vup));
 1270         PageCountUpURL(FS->fs_Conn,CNT_ACCESS|CNT_INCREMENT,vup,NULL);
 1271         return opts;
 1272     }else{
 1273         return 0;
 1274     }
 1275 }
 1276 
 1277 static const char *mount_ftparg(FtpStat *FS,PCStr(com),PCStr(arg),PVStr(vurl))
 1278 {   const char *opts;
 1279     int remtail;
 1280 
 1281     if( arg[0] == '/' )
 1282         strcpy(vurl,arg);
 1283     else{
 1284         getVUpath(FS,FS->fs_CWD,AVStr(vurl));
 1285         if( vurl[0] == 0 )
 1286             strcpy(vurl,"/");
 1287         chdir_cwd(AVStr(vurl),arg,1);
 1288     }
 1289 
 1290     /* when two MOUNTs like follows specified:
 1291      *   MOUNT="/d1/* ..."     (1)
 1292      *   MOUNT="/d1/d2/* ..."  (2)
 1293      * select (2) for "CWD /d1/d2" though it lacks trailing "/"
 1294      */
 1295     remtail = 0;
 1296     if( DIRCOMS(com) && strtailchr(vurl) != '/' ){
 1297         strcat(vurl,"/");
 1298         remtail = 1;
 1299     }
 1300     if( opts = CTX_mount_url_to(FS->fs_Conn,NULL,"GET",AVStr(vurl)) ){
 1301         if( remtail )
 1302         if( strtailchr(vurl) == '/' )
 1303             setVStrEnd(vurl,strlen(vurl)-1);
 1304         Verbose("mount_ftparg(%s)#A# [%s]->[%s]\n",com,arg,vurl);
 1305         return opts;
 1306     }
 1307 
 1308     if( remtail == 0 )
 1309         return NULL;
 1310     setVStrEnd(vurl,strlen(vurl)-1);
 1311     if( opts = CTX_mount_url_to(FS->fs_Conn,NULL,"GET",AVStr(vurl)) ){
 1312         Verbose("mount_ftparg(%s)#B# [%s]->[%s]\n",com,arg,vurl);
 1313         return opts;
 1314     }
 1315     return NULL;
 1316 }
 1317 
 1318 /*
 1319  * relative path which will be passed as an argument to the target FTP server
 1320  * (maybe it can unconditionally be absolute path as "LoginDir/UrlPath"...)
 1321  */
 1322 static void relative_path(FtpStat *FS,PCStr(upath),PVStr(rpath))
 1323 {   const char *cwd;
 1324     const char *cp;
 1325     const char *up;
 1326     int nmatch;
 1327 
 1328     cwd = FS->fs_CWD;
 1329     cp = cwd;
 1330     up = upath;
 1331     while( *cp == '/' ) cp++;
 1332     while( *up == '/' ) up++;
 1333     while( *cp != 0 && *cp == *up ){
 1334         cp++;
 1335         up++;
 1336     }
 1337     while( *cp == '/' ) cp++;
 1338 
 1339     if( *cwd == 0 && *upath != '/' || *cp == 0 && *up == '/' ){
 1340     /* v9.9.9 fix-140603a -- only if relative from cur. dir. (v6.0.0) */
 1341     sv1log("--- relative [%s][%s] -> [%s][%s]\n",cwd,upath,cp,up);
 1342 
 1343     while( *up == '/' ) up++;
 1344 
 1345     /*
 1346     if( *cp == 0 )
 1347     */
 1348         strcpy(rpath,up);
 1349     }
 1350     else{
 1351         /* absolute path in the target server: LoginDir/UrlPath */
 1352         strcpy(rpath,FS->fs_logindir);
 1353         if( strtailchr(rpath) != '/' )
 1354             strcat(rpath,"/");
 1355         strcat(rpath,upath);
 1356     }
 1357 }
 1358 static int sameident(FtpStat *FS,AuthInfo *ident)
 1359 { 
 1360     if( FS->fs_host[0] && hostcmp(FS->fs_host,ident->i_Host) == 0 )
 1361     if( FS->fs_port == ident->i_Port )
 1362     if( strcmp(FS->fs_USER,ident->i_user) == 0
 1363      || is_anonymous(FS->fs_USER) && is_anonymous(ident->i_user)
 1364     ){
 1365         return 1;
 1366     }
 1367     return 0;
 1368 }
 1369 static void get_dfltuser(PVStr(user),int size)
 1370 {
 1371     if( FCF.fc_dfltuser )
 1372         wordscanX(FCF.fc_dfltuser,AVStr(user),size);
 1373     else    strcpy(user,"anonymous");
 1374 }
 1375 static int decomp_ftpsite(FtpStat *FS,PVStr(site),AuthInfo *ident)
 1376 {   int port = 0;
 1377 
 1378     if( streq(FS->fs_proto,"sftp") ){
 1379         decomp_siteX("sftp",site,ident);
 1380     }else
 1381     decomp_siteX("ftp",site,ident);
 1382     if( ident->i_user[0] == 0 )
 1383     {
 1384         if( FS
 1385          && hostcmp(FS->fs_host,ident->i_Host) == 0
 1386          && FS->fs_port == ident->i_Port
 1387          && FS->fs_USER[0] != 0
 1388         ){
 1389             wordScan(FS->fs_USER,ident->i_user);
 1390         }else
 1391         if( FCF.fc_dfltuser ){
 1392             wordScan(FCF.fc_dfltuser,ident->i_user);
 1393         }else
 1394         if( FS
 1395          && FS->fs_host[0] == 0
 1396          && FS->fs_USER[0] != 0
 1397         ){
 1398             /* not so confident ... but it shold be used if a
 1399              * user name is given from the client and if it
 1400              * has not been used for any login to server
 1401              */
 1402             wordScan(FS->fs_USER,ident->i_user);
 1403         }else{
 1404             get_dfltuser(AVStr(ident->i_user),sizeof(ident->i_user));
 1405         }
 1406     }
 1407     return port;
 1408 }
 1409 
 1410 static int authOK(Connection *Conn,FtpStat *FS,PCStr(mopt),PCStr(user),PCStr(pass)){
 1411     CStr(asv,256);
 1412     const char *mp;
 1413     const char *us;
 1414 /*
 1415 sv1vlog("#### authOK ? opt[%s][%s] up[%s][%s]\n",
 1416 mopt?mopt:"",MountOptions?MountOptions:"",user,pass);
 1417 */
 1418     if( mopt == NULL || *mopt == 0 )
 1419         mopt = MountOptions;
 1420     if( mopt == NULL || *mopt == 0 )
 1421         return 0;
 1422     if( getOpt1(mopt,"AUTHORIZER",AVStr(asv)) == 0 )
 1423         return 0;
 1424 
 1425     us = is_anonymous(user)?pass:user;
 1426     if( (mp = MountVbase(mopt)) == 0 )
 1427         mp = "";
 1428     /*
 1429     if( 0 <= AuthenticateX(Conn,asv,FS->fs_USER,FS->fs_PASS,"",NULL)
 1430      || 0 <= AuthenticateX(Conn,asv,user,pass,"",NULL) ){
 1431     */
 1432     if( 0 <= AuthenticateX(Conn,asv,FS->fs_USER,FS->fs_PASS,"",&FS->fs_aub)
 1433      || 0 <= AuthenticateX(Conn,asv,user,pass,"",&FS->fs_aub) ){
 1434         sv1log("#### %s MOUNT AUTHORIZER[%s][%s][%s][%s]%s\n","OK",
 1435             asv,us,Client_Host,mp,FS->fs_CWD);
 1436         return 1;
 1437     }else{
 1438         sv1log("#### %s MOUNT AUTHORIZER[%s][%s][%s][%s]%s\n","ERR",
 1439             asv,us,Client_Host,mp,FS->fs_CWD);
 1440         return -1;
 1441     }
 1442 }
 1443 
 1444 static int swRemote(FtpStat *FS,PCStr(com),xPVStr(arg),PVStr(xserv),int *remp)
 1445 {   CStr(proto,256);
 1446     CStr(vurl,1024);
 1447     CStr(site,MaxHostNameLen);
 1448     CStr(path,1024);
 1449     const char *opts;
 1450     const char *iarg;
 1451     AuthInfo ident;
 1452     Connection *Conn = FS->fs_Conn;
 1453 
 1454     if( remp )
 1455         *remp = 0;
 1456 
 1457     iarg = arg;
 1458     if( arg[0] == '-' ){
 1459         for( arg++; *arg; arg++ ){
 1460             if( *arg == ' ' || *arg == '\t' ){
 1461                 arg++;
 1462                 break;
 1463             }
 1464         }
 1465     }
 1466 
 1467     /* even empty argument might points to a directory on remote site
 1468      * at least when (proxy) FTP-DeleGate is running without bound
 1469      * to any FTP-server...
 1470     if( *arg == 0 )
 1471         return 0;
 1472      */
 1473 
 1474     IsMounted = 0;
 1475     if( remote_path(arg) ){
 1476         sprintf(vurl,"ftp:%s",arg);
 1477         sv1log("direct access to remote: %s\n",vurl);
 1478         opts = 0;
 1479     }else{
 1480         opts = mount_ftparg(FS,com,arg,AVStr(vurl));
 1481         if( opts == 0 )
 1482             return 0;
 1483         IsMounted = 1;
 1484     }
 1485     FS->fs_authERR = 0;
 1486     if( authOK(Conn,FS,opts,FS->fs_USER,FS->fs_PASS) < 0 ){
 1487         FS->fs_authERR = 1;
 1488         return 0;
 1489     }
 1490 
 1491     decomp_absurl(vurl,AVStr(proto),AVStr(site),AVStr(path),sizeof(path));
 1492     decomp_ftpsite(FS,AVStr(site),&ident);
 1493 
 1494     if( isFTPxHTTP(proto) ){
 1495         return 0;
 1496     }
 1497     if( strcaseeq(proto,"file") || strcaseeq(proto,"lpr") )
 1498         return 0;
 1499     if( strcaseeq(proto,"https") )
 1500         return 0;
 1501     if( ident.i_Port <= 0 )
 1502         return 0;
 1503 
 1504     if( FS->fs_Conn && 0 <= FS->fs_Conn->sv.p_wfd ) /* connected */
 1505     if( sameident(FS,&ident) ){
 1506         CStr(oarg,1024);
 1507         CStr(narg,1024);
 1508         strcpy(oarg,iarg);
 1509         relative_path(FS,path,AVStr(narg));
 1510         if( arg != iarg && *arg == 0 && *narg != 0 ){
 1511             /* with -Opts without Path like "NLST -l" */
 1512             setVStrPtrInc(arg,' ');
 1513         }
 1514         strcpy(arg,narg);
 1515         sv1log("MOUNTED REMOTE [%s] -> [%s][%s]\n",oarg,vurl,iarg);
 1516         if( remp )
 1517             *remp = 1;
 1518         clearAsProxy(Conn,"swRemote-2");
 1519         return 0;
 1520     }
 1521 
 1522     if( *path == 0 )
 1523         strcpy(path,".");
 1524     if( opts ){
 1525         eval_mountOptions(FS->fs_Conn,opts);
 1526         FTP_getMountOpts("swRemote",Conn,FS,opts);
 1527     }
 1528     sprintf(xserv,"%s/%s",site,path);
 1529     sv1log("MOUNTED REMOTE [%s@%s:%d] %s %s\n",
 1530         ident.i_user,ident.i_Host,ident.i_Port,com,path);
 1531     clearAsProxy(Conn,"swRemote-1");
 1532 
 1533     FS->fs_Ident = ident;
 1534     return 1;
 1535 }
 1536 
 1537 static const char *isGateway(FtpStat *FS,int isdir,PCStr(rpath),PVStr(path),PVStr(scheme),PVStr(lphost),int *lpportp,PVStr(lpq),PVStr(fname))
 1538 {   CStr(vpath,1024);
 1539     CStr(lprserv,64);
 1540     CStr(upath,1024);
 1541     const char *opt;
 1542     int tail_added;
 1543 
 1544     /*
 1545     if( lpr: is not in MOUNTs )
 1546         return 0;
 1547     */
 1548 
 1549 /*
 1550     strcpy(vpath,FS->fs_CWD);
 1551 vpath must be in virtual path
 1552 */
 1553     getVUpath(FS,FS->fs_CWD,AVStr(vpath));
 1554 /*
 1555     if( vpath[0] == 0 )
 1556         strcpy(vpath,"/");
 1557 */
 1558     if( vpath[0] != '/' )
 1559         Strins(AVStr(vpath),"/");
 1560     chdir_cwd(AVStr(vpath),rpath,1);
 1561     if( isdir && strtailchr(vpath) != '/' )
 1562         strcat(vpath,"/");
 1563     strcpy(path,vpath);
 1564 /*
 1565  * vpath must be virtual path (by getVUpath()?)
 1566  */
 1567 
 1568     if( opt = CTX_mount_url_to(FS->fs_Conn,NULL,"PUT",AVStr(path)) ){
 1569         decomp_absurl(path,AVStr(scheme),AVStr(lprserv),AVStr(upath),sizeof(upath));
 1570         if( strcasecmp(scheme,"lpr") == 0 ){
 1571             setVStrEnd(lphost,0);
 1572             *lpportp = 0;
 1573             Xsscanf(lprserv,"%[^:]:%d",AVStr(lphost),lpportp);
 1574             setVStrEnd(lpq,0);
 1575             strcpy(fname,"LPR/FTP-GateWay");
 1576             Xsscanf(upath,"%[^/]/%s",AVStr(lpq),AVStr(fname));
 1577             sv1log("LPR: //%s /%s\n",lprserv,lpq);
 1578             return opt;
 1579         }
 1580         if( strcasecmp(scheme,"https") == 0 ){
 1581             sv1log("HTTPS/FTP-GateWay\n");
 1582             return opt;
 1583         }
 1584     }
 1585     return 0;
 1586 }
 1587 
 1588 static int stripArgOpts(PVStr(arg),PVStr(opt)){
 1589     const char *ap;
 1590     IStr(optb,128);
 1591 
 1592     if( *arg == '-' ){
 1593         ap = wordScan(arg,optb);
 1594         strcpy(opt,optb);
 1595         if( isspace(*ap) )
 1596             ap++;
 1597         ovstrcpy((char*)arg,ap);
 1598         sv1log("#stripArgOpts[%s][%s]\n",opt,arg);
 1599         return 1;
 1600     }else{
 1601         clearVStr(opt);
 1602         return 0;
 1603     }
 1604 }
 1605 static char *scanLISTarg(PCStr(com),PCStr(arg),PVStr(fopt))
 1606 {   const char *file;
 1607 
 1608     if( *arg == '-' )
 1609         file = wordscanX(arg,AVStr(fopt),128);
 1610     else{
 1611         if( strcaseeq(com,"LIST") || strcaseeq(com,"STAT") )
 1612             strcpy(fopt,FTP_LIST_OPT_ORIGIN);
 1613         else    setVStrEnd(fopt,0);
 1614         file = arg;
 1615     }
 1616     /*
 1617     while( *file == ' ' || *file == '\t' )
 1618     */
 1619     if( arg < file )
 1620     if( *file == ' ' || *file == '\t' )
 1621         file++;
 1622     return (char*)file;
 1623 }
 1624 static void putlist(FtpStat *FS,FILE *fp,PCStr(com),PCStr(fopt),PCStr(path),PCStr(file))
 1625 {   const char *rexp;
 1626     CStr(xfopt,1024);
 1627     refQStr(vbase,xfopt); /**/
 1628     const char *vopt;
 1629     int len;
 1630 
 1631     rexp = 0;
 1632     if( strneq(path,"/-stab-",7) ){
 1633         /* should return the list of intermediate path in vURL of
 1634          * MOUNT which has "/path" as the substring of the vURL.
 1635          */
 1636         fprintf(fp,"dummy\r\n");
 1637     }else
 1638     if( File_is(path) || (rexp = strpbrk(path,"*?[")) ){
 1639         strcpy(xfopt,fopt);
 1640         fopt = xfopt;
 1641         if( vopt = strchr(xfopt,'v') ){ /* -v for test */
 1642             if( strcmp(xfopt,"-v") == 0 )
 1643                 *xfopt = 0;
 1644             else    ovstrcpy((char*)vopt,vopt+1);
 1645         }
 1646         if( rexp )
 1647             strcat(xfopt,"*");
 1648         strcat(xfopt,"V"); /* must be before "/" for NLST */
 1649 
 1650         /*
 1651          * reverse MOUNT for wild card output should be supported...
 1652          */
 1653         if( vopt || strcaseeq(com,"NLST") ){
 1654             strcat(xfopt,"/");
 1655             vbase = xfopt + strlen(xfopt);
 1656             if( getVUpath(FS,path,AVStr(vbase)) != NULL ){
 1657                 CStr(pcwd,1024);
 1658                 CStr(vcwd,1024);
 1659                 get_VPWD(FS,AVStr(pcwd),AVStr(vcwd));
 1660                 len = strlen(vcwd);
 1661                 if( strncmp(vbase,vcwd,len) == 0 ){
 1662                     if( vbase[len] == '/' )
 1663                         len++;
 1664                     ovstrcpy((char*)vbase,vbase+len);
 1665                 }
 1666             }
 1667         }
 1668         if( comeq(com,"MLST") || comeq(com,"MLSD") ){
 1669             CStr(fmt,1024);
 1670             sprintf(fmt,"%s%s",comeq(com,"MLST")?" ":"",
 1671                 DFLT_MLS_FMT);
 1672             strcat(xfopt,"L");
 1673             if( comeq(com,"MLST") ){
 1674                 strcat(xfopt,"d");
 1675             }
 1676             ls_unix(fp,xfopt,AVStr(fmt),path,NULL);
 1677         }else
 1678         ls_unix(fp,xfopt,CVStr(NULL),path,NULL);
 1679         sv1log("FTP LOCAL %s [%s][%s]\n",com,xfopt,file);
 1680     }else{
 1681         fprintf(fp,"unknown: %s\r\n",path);
 1682     }
 1683 }
 1684 void fputs_CRLF(PCStr(str),FILE *fp)
 1685 {   const char *sp;
 1686     char sc;
 1687     for( sp = str; sc = *sp; sp++ ){
 1688         if( sc == '\n' )
 1689         if( sp == str || sp[-1] != '\r' )
 1690             putc('\r',fp);
 1691         putc(sc,fp);
 1692     }
 1693 }
 1694 static FILE *localLIST(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),PVStr(path))
 1695 {   FILE *fp;
 1696     const char *file;
 1697     CStr(fopt,1024);
 1698     CStr(scheme,64);
 1699     CStr(lphost,64);
 1700     CStr(lpq,64);
 1701     CStr(fname,1024);
 1702     CStr(stat,4096);
 1703     int lpport;
 1704 
 1705     file = scanLISTarg(com,arg,AVStr(fopt));
 1706 
 1707     scheme[0] = 0;
 1708     if( isGateway(FS,1,file,AVStr(path),AVStr(scheme),AVStr(lphost),&lpport,AVStr(lpq),AVStr(fname)) )
 1709     if( strcaseeq(scheme,"lpr") ){
 1710         stat_lpr(lphost,lpport,lpq,fopt,fname,AVStr(stat));
 1711         fp = TMPFILE("FTP-LPRstat");
 1712         if( TYPE_ASCII(FS) ){
 1713             fputs_CRLF(stat,fp);
 1714         }else{
 1715             fputs(stat,fp);
 1716         }
 1717         fflush(fp);
 1718         fseek(fp,0,0);
 1719         return fp;
 1720     }
 1721 
 1722     if( isLocal(FS,"GET",file,1,AVStr(path)) ){
 1723         if( path[0] == '/' ) /* not DOS drive: */
 1724         if( 2 <= strlen(path) && strtailchr(path) == '/' )
 1725             setVStrEnd(path,strlen(path)-1);
 1726 
 1727         fp = TMPFILE("FTP-localLIST");
 1728 
 1729 /* for clients which seems to expect -l by default ... */
 1730 if( strchr(fopt,'L') && strchr(fopt,'l') == 0 ){
 1731     sv1log("#### ADDED -l to [%s]\n",fopt);
 1732     strcat(fopt,"l");
 1733 }
 1734         putlist(FS,fp,com,fopt,path,file);
 1735         fflush(fp);
 1736         fseek(fp,0,0);
 1737         return fp;
 1738     }
 1739     return NULL;
 1740 }
 1741 static FILE *localRETR(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),PVStr(path))
 1742 {   FILE *fp;
 1743     Connection *Conn = FS->fs_Conn;
 1744     CStr(proto,64);
 1745 
 1746     if( FCF.fc_ftp_on_http )
 1747     if( strcaseeq(com,"RETR") ){
 1748         CStr(url,2048);
 1749         strcpy(path,FS->fs_CWD);
 1750         if( path[0] == '/' )
 1751             ovstrcpy((char*)path,path+1);
 1752         if( arg[0] ){
 1753             if( path[0] ) strcat(path,"/");
 1754             strcat(path,arg);
 1755         }
 1756         sprintf(url,"ftp://%s:%d/%s",FS->fs_host,FS->fs_port,path);
 1757         if( fp = URLget(url,0,NULL) )
 1758         if( 0 < file_size(fileno(fp)) ){
 1759  sv1log("#### %x [%s] %d/%d [%s]\n\n",
 1760  p2i(fp),url,iftell(fp),file_size(fileno(fp)),path);
 1761             fseek(fp,0,1);
 1762             return fp;
 1763         }
 1764         fclose(fp);
 1765     }
 1766 
 1767     if( isLocal(FS,"GET",arg,0,AVStr(path)) ){
 1768         if( fileIsdir(path) )
 1769             return NULL;
 1770         fp = fopen(path,"r");
 1771         if( fp == NULL ){
 1772             strcpy(proto,DFLT_PROTO);
 1773             strcpy(DFLT_PROTO,"tar");
 1774             if( service_permitted2(Conn,"tar",1) )
 1775                 fp = dirtar_fopen(path);
 1776             strcpy(DFLT_PROTO,proto);
 1777         }
 1778         if( fp != NULL )
 1779         if( FS->fs_REST ){
 1780             FileSize off;
 1781             fseek(fp,FS->fs_REST,0);
 1782             /*
 1783             Lseek(fileno(fp),FS->fs_REST,0);
 1784             */
 1785             off = Lseek(fileno(fp),FS->fs_REST,0);
 1786             if( off != FS->fs_REST ){
 1787                 sv1log("REST ERROR (lseek %lld -> %lld)\n",
 1788                     FS->fs_REST,off);
 1789             }
 1790             FS->fs_RESTed = FS->fs_REST;
 1791             FS->fs_REST = 0;
 1792         }
 1793         return fp;
 1794     }
 1795     return NULL;
 1796 }
 1797 
 1798 static int localCHANGE(FtpStat *FS,FILE *tc,PCStr(com),PCStr(vpath)){
 1799     IStr(path,1024);
 1800     const char *opts;
 1801     int code;
 1802 
 1803     opts = isLocal(FS,"PUT",vpath,0,AVStr(path));
 1804     if( opts == 0 )
 1805         return 0;
 1806     if( strstr(opts,"rw") == NULL ){
 1807         fprintf(tc,"553 Forbidden.\r\n");
 1808         return 1;
 1809     }
 1810     sv1log("## %s %s\n",com,path);
 1811     if( strcaseeq(com,"RNFR") ){
 1812         if( File_is(path) ){
 1813             code = 350;
 1814             strcpy(FS->fs_RNFR,path);
 1815             fprintf(tc,"%d OK\r\n",code);
 1816         }else{
 1817             code = 550;
 1818             fprintf(tc,"%d no such file\r\n",code);
 1819         }
 1820         return 1;
 1821     }
 1822     if( strcaseeq(com,"RNTO") ){
 1823         if( rename(FS->fs_RNFR,path) == 0 ){
 1824             code = 250;
 1825             fprintf(tc,"%d renamed\r\n",code);
 1826         }else{
 1827             code = 550;
 1828             fprintf(tc,"%d cannot rename\r\n",code);
 1829         }
 1830         return 1;
 1831     }
 1832     if( strcaseeq(com,"DELE") ){
 1833         if( unlink(path) == 0 ){
 1834             code = 250;
 1835             fprintf(tc,"%d removed\r\n",code);
 1836         }else{
 1837             code = 550;
 1838             fprintf(tc,"%d cannot remove\r\n",code);
 1839         }
 1840         return 1;
 1841     }
 1842     if( strcaseeq(com,"MKD") ){
 1843         if( mkdir(path,0770) == 0 ){
 1844             code = 250;
 1845             fprintf(tc,"%d created\r\n",code);
 1846         }else{
 1847             code = 550;
 1848             fprintf(tc,"%d cannot create\r\n",code);
 1849         }
 1850         return 1;
 1851     }
 1852     if( strcaseeq(com,"RMD") ){
 1853         if( rmdir(path) == 0 ){
 1854             code = 250;
 1855             fprintf(tc,"%d removed\r\n",code);
 1856         }else{
 1857             code = 550;
 1858             fprintf(tc,"%d cannot remove\r\n",code);
 1859         }
 1860         return 1;
 1861     }
 1862     return 0;
 1863 }
 1864 static int localDELE(FtpStat *FS,FILE *tc,PCStr(com),PCStr(vpath),PVStr(path),Connection *Conn,PCStr(user))
 1865 {   const char *opt;
 1866     CStr(scheme,64);
 1867     CStr(lphost,64);
 1868     CStr(lpq,64);
 1869     CStr(fname,1024);
 1870     CStr(stat,4096);
 1871     int lpport;
 1872     int code;
 1873 
 1874     scheme[0] = 0;
 1875     if( opt = isGateway(FS,0,vpath,AVStr(path),AVStr(scheme),AVStr(lphost),&lpport,AVStr(lpq),AVStr(fname)) )
 1876     if( strcaseeq(scheme,"lpr") ){
 1877         if( rmjob_lpr(Conn,lphost,lpport,lpq,user,vpath,AVStr(stat)) == 0 ){
 1878             code = 250;
 1879             fprintf(tc,"%d- removed [%s]\r\n",code,vpath);
 1880         }else{
 1881             code = 250;
 1882             fprintf(tc,"%d- no such job [%s]\r\n",code,vpath);
 1883         }
 1884         fputs(stat,tc);
 1885         fprintf(tc,"%d \r\n",code);
 1886         return 1;
 1887     }
 1888     if( localCHANGE(FS,tc,com,vpath) ){
 1889         return 1;
 1890     }
 1891     return 0;
 1892 }
 1893 static int connect_data(PCStr(where),FtpStat *FS,PVStr(port),int cntrlsock);
 1894 static FileSize XDCrelayServ(FtpStat *FS,int STOR,FILE *ts,FILE *fs,FILE *tc,FILE *fc,int dsock,PCStr(port), FILE *cachefp,PVStr(resp),int rsize);
 1895 int ftp_https_server(Connection *Conn,FtpStat *FS,FILE *ctc,FILE *cfc,int clsock,PCStr(com),PCStr(path));
 1896 
 1897 int FTP_dataSTLS_FCL(Connection *Conn,Connection *dataConn,int cldata);
 1898 static int XinsertFCL(FtpStat *FS,int cdsock){
 1899     Connection *ctrlConn = FS->fs_Conn;
 1900     Connection *Conn = &FS->fs_dataConn;
 1901     int gwf;
 1902     int xsrc;
 1903     int ncdsock = cdsock;
 1904 
 1905     gwf = GatewayFlags;
 1906     GatewayFlags |= GW_SYN_SSLSTART;
 1907     if( 0 <= (xsrc = insertFCL(Conn,ncdsock)) ){
 1908         ncdsock = xsrc;
 1909     }
 1910     if( 0 <= (xsrc = FTP_dataSTLS_FCL(ctrlConn,Conn,ncdsock)) ){
 1911         ncdsock = xsrc;
 1912     }
 1913     GatewayFlags = gwf;
 1914     return ncdsock;
 1915 }
 1916 
 1917 void putXferlog(Connection *Conn,FtpStat *FS,PCStr(com),PCStr(arg),FileSize start,FileSize xc,PCStr(cstat));
 1918 static int localSTOR(FtpStat *FS,FILE *tc,FILE *fc,PCStr(com),PCStr(vpath),PVStr(path),Connection *Conn,PCStr(user))
 1919 {   FILE *fp;
 1920     const char *opts;
 1921     int cdsock;
 1922     /* v9.9.11 fix-140722c
 1923     int wcc = -9;
 1924     */
 1925     FileSize wcc = -9;
 1926     int start = time(0);
 1927     int xcdsock = -1;
 1928     Connection *dataConn = &FS->fs_dataConn;
 1929     CStr(port,256);
 1930     FILE *ifp;
 1931     CStr(scheme,64);
 1932     CStr(lphost,64);
 1933     CStr(lpq,64);
 1934     CStr(fname,1024);
 1935     CStr(stat,4096);
 1936     int lpport;
 1937 
 1938     fp = NULL;
 1939     scheme[0] = 0;
 1940     if( opts = isGateway(FS,0,vpath,AVStr(path),AVStr(scheme),AVStr(lphost),&lpport,AVStr(lpq),AVStr(fname)) )
 1941         goto STOR;
 1942 
 1943     opts = isLocal(FS,"PUT",vpath,0,AVStr(path));
 1944     if( opts == NULL )
 1945         return 0;
 1946 
 1947     if( strstr(opts,"rw") == NULL && strstr(opts,"writeonly") == NULL ){
 1948         fprintf(tc,"553 Writing disabled.\r\n");
 1949         return 1;
 1950     }
 1951     if( comeq(com,"APPE") )
 1952     fp = fopen(path,"a");
 1953     else
 1954     fp = fopen(path,"w");
 1955     if( fp == NULL ){
 1956         fprintf(tc,"553 %s: Write permission denied.\r\n",vpath);
 1957         return 1;
 1958     }
 1959 STOR:
 1960     if( FS->fs_XDCforCL ){
 1961         cdsock = 0;
 1962         strcpy(port,FS->fs_dport);
 1963     }else
 1964     cdsock = connect_data("FTP-LOCAL",FS,AVStr(port),ClientSock);
 1965     xcdsock = cdsock;
 1966     if( FCF.fc_waitSSL ){
 1967         cdsock = XinsertFCL(FS,cdsock);
 1968     }
 1969     if( cdsock < 0 )
 1970         fprintf(tc,"550 cannot connect with you.\r\n");
 1971     else
 1972     if( strcaseeq(scheme,"https") ){
 1973         fprintf(tc,"150 Data connection for %s (%s)\r\n",vpath,port);
 1974         fflush(tc);
 1975         if( !FCF.fc_waitSSL ){
 1976             cdsock = XinsertFCL(FS,cdsock);
 1977         }
 1978         wcc = ftp_https_server(FS->fs_Conn,FS,tc,fc,cdsock,com,path);
 1979         fprintf(tc,"226 Transfer complete (%lld bytes)\r\n",wcc);
 1980     }
 1981     else{
 1982         fprintf(tc,"150 Data connection for %s (%s)\r\n",vpath,port);
 1983         fflush(tc);
 1984         if( !FCF.fc_waitSSL ){
 1985             cdsock = XinsertFCL(FS,cdsock);
 1986         }
 1987 
 1988         ifp = NULL;
 1989         if( strcaseeq(scheme,"lpr") ){
 1990             if( FS->fs_XDCforCL ){
 1991                 FILE *tmp;
 1992                 tmp = TMPFILE("XDC-localSTOR");
 1993                 cdsock = fddup(fileno(tmp));
 1994                 fclose(tmp);
 1995                 wcc = XDCrelayServ(FS,1,NULL,NULL,tc,fc,
 1996                     fddup(cdsock),port,NULL,VStrNULL,0);
 1997                 Lseek(cdsock,0,0);
 1998             }
 1999             ifp = fdopen(cdsock,"r");
 2000 
 2001             if( INHERENT_fork() && FCF.fc_lpr_nowait ){
 2002                 FILE *tmp;
 2003                 int isize;
 2004                 tmp = TMPFILE("LPR/FTP");
 2005                 isize = copyfile1(ifp,tmp);
 2006                 fflush(tmp);
 2007                 fseek(tmp,0,0);
 2008                 if( Fork("LPR/FTP") == 0 ){
 2009                 wcc = send_lpr(Conn,lphost,lpport,lpq,tmp,isize,
 2010                     user,fname,AVStr(stat));
 2011                     Exit(0,"");
 2012                 }
 2013                 fclose(tmp);
 2014                 strcpy(stat,"sending to LPR ...\r\n");
 2015             }else
 2016             wcc = send_lpr(Conn,lphost,lpport,lpq,ifp,0,
 2017                 user,fname,AVStr(stat));
 2018             fprintf(tc,"226- LPR response:\r\n");
 2019             fputs(stat,tc);
 2020             fprintf(tc,"226 \r\n");
 2021         }else{
 2022             if( FS->fs_XDCforCL ){
 2023                 wcc = XDCrelayServ(FS,1,NULL,NULL,tc,fc,
 2024                     fileno(fp),port,NULL,VStrNULL,0);
 2025             }else{
 2026                 ifp = fdopen(cdsock,"r");
 2027             wcc = copyfile1(ifp,fp);
 2028             }
 2029             fprintf(tc,"226 Transfer complete (%lld bytes)\r\n",wcc);
 2030             sv1log("ftp-data [%s] uploaded %lld\n",com,wcc);
 2031         }
 2032         if( ifp )
 2033         fclose(ifp);
 2034     }
 2035     if( fp )
 2036         fclose(fp);
 2037 
 2038     if( 0 <= xcdsock && xcdsock != cdsock ){
 2039         Connection *Conn = dataConn;
 2040         sv1log("localSTOR (%lld) CF=%X %X\n",wcc,ClientFlags,ServerFlags);
 2041         waitFilterThread(dataConn,300,XF_ALL);
 2042         clearSTLS(dataConn);
 2043     }
 2044 
 2045     FS->fs_cstat = "L";
 2046     putXferlog(Conn,FS,com,vpath,start,wcc,""); /* v9.9.11 fix-140722b */
 2047     return 1;
 2048 }
 2049 
 2050 int mkdatafile(PCStr(data),int leng)
 2051 {   int fd;
 2052     FILE *tmp;
 2053 
 2054     tmp = TMPFILE("mkdatafile");
 2055     fwrite(data,1,leng,tmp);
 2056     fflush(tmp);
 2057     fseek(tmp,0,0);
 2058     fd = fddup(fileno(tmp));
 2059     fclose(tmp);
 2060     return fd;
 2061 }
 2062 /*
 2063  * DeleGate <- XDC <- DeleGate <- PASV <- client
 2064  *  Even XDC-client DeleGate passes through PASV command from
 2065  *  it's client since client-side-PASV to server-side-XDC conversion
 2066  *  is not supported (in setupPASV()).
 2067  * DeleGate <- XDC <- DeleGate <- PORT <- client
 2068  *  But a PORT command from XDC-client is dummy PORT containing
 2069  *  client's PORT (forwarded in setupPORT())
 2070  *  and must not be used as PORT of the client-DeleGate
 2071  *
 2072 if( FS->fs_dport[0] != 0 || 0 < FS->fs_pclsock )
 2073  */
 2074 static int usemodeXDCtoCL(FtpStat *FS)
 2075 {
 2076     if( 0 < FS->fs_pclsock )
 2077         return 0;
 2078     else    return FS->fs_XDCforCL;
 2079 }
 2080 
 2081 void xdatamsg(FtpStat *FS,PVStr(msg),int datafd,PCStr(com),PCStr(path),FileSize dsize);
 2082 FileSize FTP_data_relay(Connection *Conn,FtpStat *FS,int src,int dst,FILE *cachefp,int tosv);
 2083 
 2084 static FileSize putToClient(Connection *Conn,FtpStat *FS,FILE *tc,PCStr(com),PCStr(stat),int datafd,PCStr(data),int leng,PCStr(path))
 2085 {   FileSize wcc;
 2086     CStr(msg,256);
 2087     int cdsock;
 2088     CStr(port,256);
 2089     int modeXDC;
 2090 
 2091     modeXDC = usemodeXDCtoCL(FS);
 2092 
 2093     if( datafd < 0 && data == NULL ){
 2094         fprintf(tc,"550 No such file\r\n");
 2095         return -1;
 2096     }
 2097 
 2098     if( FCF.fc_immportCL && 0 <= FS->fs_dclsock ){
 2099         cdsock = FS->fs_dclsock;
 2100         FS->fs_dclsock = -1;
 2101         sv1log("#### DBG use IMMPORT %d\n",cdsock);
 2102     }else
 2103     if( modeXDC ){
 2104         cdsock = 0;
 2105         strcpy(port,FS->fs_dport);
 2106     }else
 2107     cdsock = connect_data("FTP-LOCAL",FS,AVStr(port),ClientSock);
 2108     if( cdsock < 0 ){
 2109         fprintf(tc,"550 cannot connect with you.\r\n");
 2110         return -1;
 2111     }
 2112 
 2113     if( stat ){
 2114         fprintf(tc,"150- Ok\r\n");
 2115         fprintf(tc,"%s",stat);
 2116     }
 2117     xdatamsg(FS,AVStr(msg),datafd,com,path,-1);
 2118     fprintf(tc,"150 %s\r\n",msg);
 2119     fflush(tc);
 2120 
 2121     if( modeXDC ){
 2122         CStr(resp,256);
 2123         if( datafd < 0 )
 2124             datafd = mkdatafile(data,leng);
 2125         else    datafd = fddup(datafd);
 2126         wcc = XDCrelayServ(FS,0,NULL,NULL,tc,NULL,datafd,port,
 2127             NULL,AVStr(resp),sizeof(resp));
 2128     }else{
 2129     if( 0 <= datafd )
 2130         wcc = FTP_data_relay(Conn,FS,datafd,cdsock,NULL,0);
 2131     else    wcc = write(cdsock,data,leng);
 2132     close(cdsock);
 2133     }
 2134 
 2135     putXcomplete(Conn,FS,tc,wcc);
 2136 
 2137     return wcc;
 2138 }
 2139 static void get_help(PVStr(data))
 2140 {   refQStr(dp,data); /**/
 2141 
 2142     dp = Sprintf(QVStr(dp,data),"150-  @ @  \r\n");
 2143     dp = Sprintf(QVStr(dp,data),"150- ( - ) { %s }\r\n",DELEGATE_version());
 2144     dp = Sprintf(QVStr(dp,data),"150- Enter cd //server/path\r\n");
 2145     dp = Sprintf(QVStr(dp,data),"150-          to go `path' on FTP `server'\r\n");
 2146     dp = Sprintf(QVStr(dp,data),"150- This (proxy) service is maintained by '%s'\r\n",
 2147         DELEGATE_ADMIN);
 2148 }
 2149 static void putSTAT(Connection *Conn,FILE *tc)
 2150 {   CStr(myhost,MaxHostNameLen);
 2151 
 2152     ClientIF_name(Conn,FromC,AVStr(myhost));
 2153     fprintf(tc,"211-%s FTP server status:\r\n",myhost);
 2154     fprintf(tc,"    Version: FTP/%s\r\n",DELEGATE_version());
 2155     fprintf(tc,"211 end of status\r\n");
 2156 }
 2157 
 2158 /*
 2159  * from "SP listOfFacts; /realpath"
 2160  *   to "SP listOfFacts; /vpath"
 2161  */
 2162 static void putMLST1(FtpStat *FS,int remote,PCStr(resp),FILE *tc){
 2163     CStr(line,1024);
 2164     refQStr(lp,line);
 2165     CStr(fname,1024);
 2166     CStr(vpath,1024);
 2167 
 2168     if( *resp != ' ' ){
 2169         fputs(resp,tc);
 2170         return;
 2171     }
 2172     strcpy(line,resp);
 2173     if( lp = strrchr(line,';') )
 2174         lp++;
 2175     else    lp = line;
 2176     if( *lp == ' ')
 2177         lp++;
 2178     lineScan(lp,fname);
 2179     truncVStr(lp);
 2180 
 2181     getVUpathX(FS,fname,AVStr(vpath),remote);
 2182     fprintf(tc,"%s%s\r\n",line,vpath);
 2183 }
 2184 static void relayMLST(FtpStat *FS,PCStr(resp),FILE *tc){
 2185     CStr(line,1024);
 2186     const char *rp;
 2187 
 2188     for( rp = resp; rp && *rp; ){
 2189         if( rp = wordscanY(rp,AVStr(line),sizeof(line),"^\n") ){
 2190             if( *rp == '\n' ){ strcat(line,"\n"); rp++; }
 2191         }
 2192         putMLST1(FS,1,line,tc);
 2193     }
 2194 }
 2195 static void fputsMLST(FtpStat *FS,int remote,FILE *tmp,FILE *fp,PVStr(stat)){
 2196     CStr(line,1024);
 2197     refQStr(rp,stat);
 2198 
 2199     while( fgets(line,sizeof(line),tmp) != NULL ){
 2200         putMLST1(FS,remote,line,fp);
 2201         if( stat != NULL ){
 2202             strcpy(rp,line);
 2203             rp += strlen(rp);
 2204         }
 2205     }
 2206 }
 2207 static void putlistV(FtpStat *FS,FILE *fp,PCStr(com),PCStr(fopt),PCStr(path),PCStr(file)){
 2208     FILE *tmp;
 2209 
 2210     tmp = TMPFILE("putlistV");
 2211     putlist(FS,tmp,com,fopt,path,file);
 2212     fflush(tmp);
 2213     fseek(tmp,0,0);
 2214     fputsMLST(FS,0,tmp,fp,VStrNULL);
 2215     fclose(tmp);
 2216 }
 2217 
 2218 static int localSTAT(FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),int islocal)
 2219 {   const char *file;
 2220     CStr(fopt,128);
 2221     CStr(path,1024);
 2222     int body;
 2223 
 2224     if( islocal && comeq(com,"MLST") && arg[0] == 0 ){
 2225         arg = ".";
 2226     }
 2227     if( islocal && arg[0] == 0 ){
 2228         putSTAT(FS->fs_Conn,tc);
 2229         return 1;
 2230     }
 2231 
 2232     file = scanLISTarg(com,arg,AVStr(fopt));
 2233     if( strncasecmp(fopt,"-HTTP",5) == 0 ){
 2234         *fopt = 0;
 2235         body = 1;
 2236     }else   body = 0;
 2237 
 2238     if( isLocal(FS,"GET",file,0,AVStr(path)) ){
 2239     /*
 2240         if( arg[0] == 0 ){
 2241     */
 2242         if( !comeq(com,"MLST") && arg[0] == 0 ){
 2243         putSTAT(FS->fs_Conn,tc);
 2244         }else{
 2245         fprintf(tc,"211-status of %s [%s][%s]:\r\n",arg,fopt,file);
 2246         if( path[0] == '/' && isFullpath(path+1) ){ /* DOS drive: */
 2247             ovstrcpy(path,path+1);
 2248         }
 2249         if( body ){
 2250             putFileInHTTP(tc,path,file);
 2251             fputs("\r\n--\r\n",tc);
 2252         }else
 2253         if( comeq(com,"MLST") ){
 2254             putlistV(FS,tc,com,fopt,path,file);
 2255         }else   putlist(FS,tc,com,fopt,path,file);
 2256         fprintf(tc,"211 end of status\r\n");
 2257         }
 2258         return 1;
 2259     }else{
 2260         if( islocal ){
 2261         fprintf(tc,"211-status of %s [%s][%s]:\r\n",arg,fopt,file);
 2262         fprintf(tc,"%s not found\r\n",file);
 2263         fprintf(tc,"211 end of status\r\n");
 2264         }
 2265         return 0;
 2266     }
 2267 }
 2268 
 2269 static int setupPORT(Connection *Conn,FtpStat *FS,FILE *ts,FILE *fs,FILE *tc,PCStr(arg));
 2270 static int setupPASV(Connection *Conn,FtpStat *FS,FILE *ts,FILE *fs,FILE *tc,PCStr(arg));
 2271 
 2272 static int AsServer0(Connection *Conn,FtpStat *FS,FILE *tc,PCStr(com),PCStr(arg),PCStr(user))
 2273 {   int done = 1;
 2274 
 2275     if( strcasecmp(com,"XECHO") == 0 ){
 2276         fprintf(tc,"%s\r\n",arg);
 2277     }else
 2278     if( strcasecmp(com,"NOOP") == 0 ){
 2279         fprintf(tc,"200 NOOP command successful.\r\n");
 2280     }else
 2281     if( strcasecmp(com,"SYST") == 0 ){
 2282         /*
 2283         fprintf(tc,"500 SYST command is not supported.\r\n");
 2284         */
 2285         fprintf(tc,"215 UNIX Type: L8\r\n");
 2286     }else
 2287     if( strcasecmp(com,"MODE") == 0 ){
 2288         set_client_mode(FS,arg);
 2289         fprintf(tc,"200 MODE %s Ok.\r\n",arg);
 2290     }else
 2291     if( strcasecmp(com,"PORT") == 0 ){
 2292         setupPORT(Conn,FS,NULL,NULL,tc,arg);
 2293     }else
 2294     if( strcasecmp(com,"EPRT") == 0 ){
 2295         setupPORT(Conn,FS,NULL,NULL,tc,arg);
 2296     }else
 2297     if( strcasecmp(com,"PASV") == 0 ){
 2298         setupPASV(Conn,FS,NULL,NULL,tc,arg);
 2299     }else
 2300     if( strcasecmp(com,"EPSV") == 0 ){
 2301         setupPASV(Conn,FS,NULL,NULL,tc,arg);
 2302     }else
 2303     if( strcasecmp(com,"TYPE") == 0 ){
 2304         wordScan(arg,FS->fs_TYPE);
 2305         fprintf(tc,"200 Type set to %s\r\n",FS->fs_TYPE);
 2306     }else
 2307     if( strcasecmp(com,"REST") == 0 ){
 2308         Xsscanf(arg,"%lld",&FS->fs_REST);
 2309         if( 0 < FS->fs_REST )
 2310             fprintf(tc,"350 Restarting at %lld.\r\n",FS->fs_REST);
 2311         else{
 2312             fprintf(tc,"500 bad offset %lld.\r\n",FS->fs_REST);
 2313             FS->fs_REST = 0;
 2314         }
 2315     }else{
 2316         done = 0;
 2317     }
 2318     return done;
 2319 }
 2320 
 2321 /*
 2322  * it can be always TRUE after LIST / MLSD conversion is implemented.
 2323  */
 2324 static int withMLSD(Connection *Conn,FtpStat *FS){
 2325     if( FS->fs_islocal == IS_LOCAL ){
 2326         return 1;
 2327     }
 2328     return 0;
 2329 }
 2330 
 2331 int FTP_putSTLS_FEAT(Connection *Conn,FILE *tc,int wrap);
 2332 static void putFEAT(Connection *Conn,FtpStat *FS,FILE *tc,int ckssl){
 2333     fprintf(tc,"211-Extensions supported\r\n");
 2334     fprintf(tc," MDTM\r\n");
 2335     if( !FCF.fc_noMLSxCL )
 2336     if( withMLSD(Conn,FS)
 2337      || isinFTPxHTTP(Conn)
 2338      || isFTPxHTTP(FS->fs_proto) /* not assured to be set ? */
 2339     ){
 2340         fprintf(tc," MLSD %s\r\n",FEAT_MLS);
 2341         fprintf(tc," MLST %s\r\n",FEAT_MLS);
 2342     }
 2343     fprintf(tc," SIZE\r\n");
 2344     if( !FCF.fc_nopasvCL || FCF.fc_doepsvCL )
 2345     if( !FCF.fc_noepsvCL )
 2346         fprintf(tc," EPSV\r\n");
 2347     if( !FCF.fc_noportCL )
 2348         fprintf(tc," EPRT\r\n");
 2349 
 2350     if( ckssl ){
 2351         FTP_putSTLS_FEAT(Conn,tc,0);
 2352     }
 2353     fprintf(tc,"211 END\r\n");
 2354 }
 2355 
 2356 static int AsServer(Connection *Conn,FtpStat *FS,FILE *tc,FILE *fc,PCStr(com),PCStr(arg),PCStr(user))
 2357 {   CStr(data,4096);
 2358     CStr(path,1024);
 2359     FILE *fp;
 2360     int done = 1;
 2361     int authok = 0;
 2362 
 2363     if( FS->fs_anonymousOK == 0
 2364      && FS->fs_authERR == 0
 2365      && FS->fs_aub.i_Host[0] != 0 /* implies MOUNT AUTHORIZER */
 2366     ){
 2367         authok = 1;
 2368     }
 2369     if( isFTPxHTTP(FS->fs_proto) ){
 2370         if( authok == 0 ){
 2371             authok = 2;
 2372         }
 2373     }
 2374 
 2375     if( AsServer0(Conn,FS,tc,com,arg,user) ){
 2376     }else
 2377     if( !FS->fs_anonymousOK && (FS->fs_islocal&FS_NOAUTH)==0 && !authok ){
 2378         /*
 2379          * only anonymous login is allowed in the current implementation.
 2380          * opts = isLocal()
 2381          * check "user=..." in opts
 2382          */
 2383         done = 0;
 2384     }else
 2385     if( strcasecmp(com,"FEAT") == 0 ){
 2386         putFEAT(Conn,FS,tc,1);
 2387     }else
 2388     if( strcasecmp(com,"MDTM") == 0 || strcasecmp(com,"SIZE") == 0 ){
 2389         CStr(paht,1024);
 2390         CStr(stime,128);
 2391         if( ftpxhttpSIZE(FS,tc,com,arg) ){
 2392         }else
 2393         if( isLocal(FS,"GET",arg,0,AVStr(path)) && File_is(path) ){
 2394             if( fileIsdir(path)){
 2395                 fprintf(tc,"550 %s: Not a plain file\r\n",arg);
 2396             }else
 2397             if( strcaseeq(com,"MDTM") ){
 2398                 StrftimeGMT(AVStr(stime),sizeof(stime),"%Y%m%d%H%M%S",File_mtime(path),0);
 2399                 fprintf(tc,"213 %s\r\n",stime);
 2400             }else
 2401             if( strcaseeq(com,"SIZE") ){
 2402                 fprintf(tc,"213 %lld\r\n",File_sizeX(path));
 2403             }
 2404         }else   fprintf(tc,"550 %s: No such file\r\n",arg);
 2405     }else
 2406     /*
 2407     if( strcasecmp(com,"STAT") == 0 ){
 2408     */
 2409     if( comeq(com,"STAT") || !FCF.fc_noMLSxCL && comeq(com,"MLST") ){
 2410         if( ftpxhttpSTAT(FS,tc,com,arg) ){
 2411         }else
 2412         localSTAT(FS,tc,com,arg,1);
 2413     }else
 2414     if( comUPLOAD(com) ){
 2415         if( ftpxhttpSTOR(FS,tc,fc,com,arg,AVStr(path)) ){
 2416             return 2;
 2417         }else
 2418         if( localSTOR(FS,tc,fc,com,arg,AVStr(path),Conn,user) )
 2419             return 1;
 2420         return 0;
 2421     }else
 2422     if( strcasecmp(com,"DELE")==0 ){
 2423         if( ftpxhttpCHANGE(FS,tc,com,arg) ){
 2424             return 2;
 2425         }else
 2426         if( localDELE(FS,tc,com,arg,AVStr(path),Conn,user) )
 2427             return 1;
 2428         else    return 0;
 2429     }else
 2430     if( comCHANGE(com) ){
 2431         if( ftpxhttpCHANGE(FS,tc,com,arg) ){
 2432             return 2;
 2433         }else
 2434         if( localCHANGE(FS,tc,com,arg) )
 2435             return 1;
 2436         else    return 0;
 2437     }else
 2438     /*
 2439     if( strcasecmp(com,"LIST")==0 || strcasecmp(com,"NLST")==0 ){
 2440     */
 2441     if( comeq(com,"LIST") || comeq(com,"NLST")
 2442      || !FCF.fc_noMLSxCL && comeq(com,"MLSD")
 2443     ){
 2444         if( ftpxhttpLIST(FS,tc,com,arg,AVStr(path),VStrNULL) ){
 2445         }else
 2446         if( fp = localLIST(FS,tc,com,arg,AVStr(path)) ){
 2447             putToClient(Conn,FS,tc,com,NULL,fileno(fp),NULL,0,path);
 2448             fclose(fp);
 2449         }else{
 2450             get_help(AVStr(data));
 2451             putToClient(Conn,FS,tc,com,data,-1,"",0,"(init)");
 2452         }
 2453     }else
 2454     if( strcasecmp(com,"RETR") == 0 ){
 2455         int start;
 2456         int leng;
 2457         FileSize xc;
 2458 
 2459         start = time(0);
 2460         if( ftpxhttpRETR(FS,tc,com,arg,AVStr(path)) ){
 2461         }else
 2462         if( fp = localRETR(FS,tc,com,arg,AVStr(path)) ){
 2463             xc = putToClient(Conn,FS,tc,com,NULL,fileno(fp),NULL,0,path);
 2464             fclose(fp);
 2465             FS->fs_cstat = "L";
 2466             putXferlog(Conn,FS,com,arg,start,xc,"");
 2467         }else   putToClient(Conn,FS,tc,com,NULL,-1,NULL,0,"(error)");
 2468     }else{
 2469         /*fprintf(tc,"500 Unknown command\r\n");*/
 2470         done = 0;
 2471     }
 2472 
 2473     if( !strcaseeq(com,"RETR") && !comUPLOAD(com) )
 2474     if( !strcaseeq(com,"REST") && 0 < FS->fs_REST ){
 2475         sv1log("## %s: cleared REST %lld\n",com,FS->fs_REST);
 2476         FS->fs_REST = 0;
 2477     }
 2478     return done;
 2479 }
 2480 
 2481 static int rewrite_CWD(FtpStat *FS,PVStr(req),PVStr(arg),FILE *tc)
 2482 {   CStr(mdir,1024);
 2483     CStr(vdir,1024);
 2484     CStr(npath,1024);
 2485     const char *ncwd;
 2486     /*
 2487     CStr(rmsg,1024);
 2488      */
 2489     IStr(rmsg,1024); /* fix-140506a */
 2490     const char *opts;
 2491     Connection *Conn = FS->fs_Conn;
 2492     CStr(uproto,32);
 2493     CStr(usite,64);
 2494     CStr(upath,256);
 2495     CStr(uhost,64);
 2496     int uport;
 2497     int sv_IsMounted;
 2498     int sv_fs_islocal;
 2499     CStr(sv_fs_CWD,1024);
 2500     CStr(sv_fs_prevVWD,1024);
 2501 
 2502     /* if( with MOUNT with AUTHORIZER MountOption ) */
 2503     if( 1 ){ /* 9.2.2 save the context to restore on authERR */
 2504         sv_IsMounted = IsMounted;
 2505         sv_fs_islocal = FS->fs_islocal;
 2506         strcpy(sv_fs_CWD,FS->fs_CWD);
 2507         strcpy(sv_fs_prevVWD,FS->fs_prevVWD);
 2508     }
 2509 
 2510     getVUpath(FS,FS->fs_CWD,AVStr(mdir));
 2511     if( mdir[0] == 0 )
 2512         strcpy(mdir,"/");
 2513 
 2514     if( isFTPxHTTP(FS->fs_proto) ){
 2515         if( mdir[0] != '/' ){
 2516             /* should be relative to LoginDir ? */
 2517             Strins(AVStr(mdir),"/");
 2518         }
 2519     }
 2520     strcpy(FS->fs_prevVWD,mdir);
 2521 
 2522     chdir_cwd(AVStr(mdir),arg,1);
 2523     strcpy(vdir,mdir);
 2524 
 2525     if( strncmp(FS->fs_CWD,"//",2) == 0 && strncmp(mdir,"//",2) != 0 ){
 2526         refQStr(dp,mdir); /**/
 2527         strcpy(mdir,FS->fs_CWD);
 2528         if( dp = strchr(mdir+2,'/') )
 2529             chdir_cwd(AVStr(dp),arg,1);
 2530     }
 2531 
 2532     IsMounted = 0;
 2533     FS->fs_islocal = 0;
 2534 
 2535     if( strtailchr(mdir) != '/' )
 2536         strcat(mdir,"/");
 2537 
 2538     opts = CTX_mount_url_to(FS->fs_Conn,NULL,"GET",AVStr(mdir));
 2539     if( opts == 0 ){
 2540         strcpy(vdir,FS->fs_CWD);
 2541         if( mdir[0] == 0 )
 2542             strcpy(vdir,"/");
 2543         else
 2544         if( strtailchr(vdir) != '/' )
 2545             strcat(vdir,"/");
 2546         strcpy(mdir,vdir);
 2547         if( CTX_mount_url_to(FS->fs_Conn,NULL,"GET",AVStr(mdir)) )
 2548         if( strncmp(mdir,"lpr:",4) == 0 ){
 2549             strcpy(mdir,vdir);
 2550             chdir_cwd(AVStr(mdir),arg,1);
 2551             strcpy(FS->fs_CWD,mdir);
 2552             sv1log("LEAVE-MOUNTED-LPR: %s => %s\n",vdir,mdir);
 2553             sprintf(rmsg,"250 CWD command successful.\r\n");
 2554             goto EXIT;
 2555         }
 2556         return 0;
 2557     }
 2558 
 2559     if( opts ){
 2560         eval_mountOptions(FS->fs_Conn,opts);
 2561     }
 2562     if( authOK(Conn,FS,opts,FS->fs_USER,FS->fs_PASS) < 0 ){
 2563         IsMounted = sv_IsMounted;
 2564         FS->fs_islocal = sv_fs_islocal;
 2565         strcpy(FS->fs_CWD,sv_fs_CWD);
 2566         strcpy(FS->fs_prevVWD,sv_fs_prevVWD);
 2567 
 2568         if( FS->fs_aub.i_realm[0] ){
 2569             sprintf(rmsg,"530 %s\r\n",FS->fs_aub.i_realm);
 2570         }else
 2571         sprintf(rmsg,"530 not Authorized\r\n");
 2572         goto EXIT;
 2573     }
 2574 
 2575     if( strncmp(mdir,"lpr://",6) == 0 ){
 2576         sv1log("MOUNTED-TO-LPR: %s => %s\n",vdir,mdir);
 2577         strcpy(FS->fs_CWD,vdir);
 2578         sprintf(rmsg,"250 CWD command successful.\r\n");
 2579         goto EXIT;
 2580     }
 2581 
 2582     decomp_absurl(mdir,AVStr(uproto),AVStr(usite),AVStr(upath),sizeof(upath));
 2583     if( streq(uproto,"http")
 2584      || streq(uproto,"pop")
 2585      || streq(uproto,"nntp")
 2586      || streq(uproto,"news")
 2587     ){
 2588         strcpy(FS->fs_proto,uproto);
 2589         FS->fs_port = scan_hostport(uproto,usite,AVStr(FS->fs_host));
 2590         strcpy(FS->fs_loginroot,upath);
 2591         FS->fs_login1st = 1;
 2592         sv1log("MOUNTED-TO-%s: %s => %s\n",uproto,vdir,mdir);
 2593         strcpy(FS->fs_CWD,vdir);
 2594         sprintf(rmsg,"250 CWD command successful.\r\n");
 2595         goto EXIT;
 2596     }
 2597     if( isFTPxHTTP(uproto) ){
 2598         IStr(ocwd,1024);
 2599         IStr(dstproto,64);
 2600         int hcode;
 2601 
 2602         IsMounted = 1;
 2603         strcpy(FS->fs_proto,uproto);
 2604         FS->fs_port = scan_hostport(uproto,usite,AVStr(FS->fs_host));
 2605         strcpy(FS->fs_loginroot,upath);
 2606         sv1log("MOUNTED-TO-%s: %s => %s\n",uproto,vdir,mdir);
 2607         strcpy(ocwd,FS->fs_CWD);
 2608         strcpy(FS->fs_CWD,vdir);
 2609         if( ftpxhttpCHANGE(FS,tc,"CWD",vdir) < 0 ){
 2610             strcpy(FS->fs_CWD,ocwd);
 2611         }
 2612         /* response message(rmsg) is sent already in ftpxhttpCHANGE() */
 2613         goto EXIT;
 2614         /* the followings are in ftpxhttpCHANGE() */
 2615         if( streq(vdir,"/") ){
 2616             hcode = 300;
 2617         }else{
 2618             hcode = ftpxhttpSIZE(FS,0,"CWD",vdir);
 2619         }
 2620         /* testing "vdir" by HEAD to resp. 301 (MovedTo "vdir/") */
 2621         if( 300 <= hcode && hcode <= 303 ){
 2622             sprintf(rmsg,"250 CWD command successful\r\n");
 2623         }else{
 2624             sprintf(rmsg,"550 no such directory\r\n");
 2625             strcpy(FS->fs_CWD,ocwd);
 2626         }
 2627         goto EXIT;
 2628     }
 2629 
 2630     if( strncaseeq(mdir,"ftps://",7) ){
 2631         /* default port shuold be 990 ... */
 2632         Strrplc(AVStr(mdir),4,"ftp");
 2633         ServerFlags |=  PF_SSL_IMPLICIT;
 2634     }else{
 2635         ServerFlags &= ~PF_SSL_IMPLICIT;
 2636     }
 2637 
 2638     /*
 2639     if( strncmp(mdir,"ftp://",6) == 0 ){
 2640     */
 2641     /*
 2642     if( strncasecmp(mdir,"ftp://",6) == 0 ){
 2643     */
 2644     if( strncaseeq(mdir,"ftp://",6) || strncaseeq(mdir,"sftp://",7) ){
 2645         /*
 2646         strcpy(arg,mdir+4);
 2647         */
 2648         strcpy(arg,strstr(mdir,"//"));
 2649         if( req != NULL )
 2650             sprintf(req,"CWD %s\r\n",arg);
 2651         sv1log("MOUNTED-TO: %s\n",arg);
 2652         if( streq(uproto,"sftp") ){
 2653             strcpy(FS->fs_proto,"sftp");
 2654             FS->fs_port = scan_hostport(uproto,usite,AVStr(FS->fs_host));
 2655             strcpy(FS->fs_loginroot,upath);
 2656             sv1log("-SFTPGW:CWD set FS->fs_proto=sftp\n");
 2657         }else{
 2658             /* fs_proto might have to be cleared */
 2659         }
 2660         IsMounted = 1;
 2661         if( streq(CLNT_PROTO,"ftps") && !streq(DST_PROTO,"ftp") ){
 2662             /* 9.9.2 to permit MOUNTed ftp/ftps gw. by default */
 2663             Conn->no_dstcheck_proto = serviceport("ftp");
 2664         }
 2665         setMountOptions(FL_ARG,Conn,opts);
 2666         FTP_getMountOpts("rewrite_CWD",Conn,FS,opts);
 2667         /*
 2668         MountOptions = opts;
 2669         */
 2670         clearAsProxy(Conn,"rewrite_CWD");
 2671         return 0;
 2672     }
 2673 
 2674     if( strncmp(mdir,"file://localhost/",17) != 0 )
 2675         return 0;
 2676 
 2677     FS->fs_islocal = 1;
 2678     if( NoAuth ) FS->fs_islocal |= FS_NOAUTH;
 2679     ncwd = mdir + 16;
 2680     if( ncwd[1] != '/' && isFullpath(ncwd+1) ) /* DOS drive: */
 2681         ncwd++;
 2682 
 2683     if( strncmp(ncwd,"/-stab-",7) == 0 ){
 2684         /* if it's the substring of MOUNTs left hands vURLs ... */
 2685         sv1log("MOUNTED-TO-STAB: %s\n",mdir);
 2686         strcpy(FS->fs_CWD,ncwd);
 2687         sprintf(rmsg,"250 CWD command successful.\r\n");
 2688         FS->fs_islocal = IS_STAB;
 2689     }else
 2690     if( fileIsdir(ncwd) ){
 2691         sv1log("MOUNTED-TO-LOCAL: %s\n",mdir);
 2692         strcpy(FS->fs_CWD,ncwd);
 2693         sprintf(rmsg,"250 CWD command successful.\r\n");
 2694     }else{
 2695         sv1log("MOUNTED-TO-LOCAL#UNKNOWN: %s\n",mdir);
 2696         sprintf(rmsg,"550 %s: No such directory.\r\n",vdir);
 2697     }
 2698 EXIT:
 2699     if( tc != NULL ){
 2700         fputs(rmsg,tc);
 2701         fflush(tc);
 2702     }
 2703     return 1;
 2704 }
 2705 static int scanPWD(PCStr(resp),PVStr(path),xPVStr(rem))
 2706 {   CStr(rembuf,1024);
 2707 
 2708     if( rem == NULL )
 2709         setPStr(rem,rembuf,sizeof(rembuf));
 2710     setVStrEnd(rem,0);
 2711     setVStrEnd(path,0);
 2712     return Xsscanf(resp,"257 \"%[^\"]\"%[^\r\n]",AVStr(path),AVStr(rem));
 2713 }
 2714 static int rewrite_PWD(FtpStat *FS,PCStr(req),PCStr(arg),FILE *tc)
 2715 {   CStr(cwd,1024);
 2716     CStr(npath,1024);
 2717     CStr(resp,1024);
 2718 
 2719     if( get_VPWD(FS,AVStr(cwd),AVStr(npath)) < 0 )
 2720         return 0;
 2721 
 2722     sv1log("local echo for PWD: ftp://%s:%d/%s\n",
 2723         FS->fs_host,FS->fs_port,cwd);
 2724 
 2725     if( streq(npath,FS->fs_logindir) )
 2726     {
 2727         if( !FCF.fc_nounesc
 2728          && strncaseeq(cwd,"%2F",3) ){
 2729             /* 9.9.1 with MOUNT="/* ftp://server/%2F* */
 2730             /* logindir is not the root directory */
 2731         }else
 2732         strcpy(npath,"");
 2733     }
 2734     if( npath[0] == 0 ){
 2735         /* 9.2.5 returning "" breaks MOUNT of client-side DeleGate */
 2736         if( FCF.fc_nullPWD ){
 2737         }else{
 2738             strcpy(npath,"/");
 2739         }
 2740     }
 2741 
 2742     sprintf(resp,"257 \"%s\" is current directory.\r\n",npath);
 2743     sv1log("I-SAY: %s",resp);
 2744     CCXresp(FS->fs_Conn,FS,resp,AVStr(resp),sizeof(resp));
 2745     fputs(resp,tc);
 2746     fflush(tc);
 2747     return 1;
 2748 }
 2749 static int getFTPWD(PCStr(wh),FtpStat *FS,PVStr(resp));
 2750 static void getPWD(FtpStat *FS,PVStr(pwd),int siz)
 2751 {   CStr(resp,1024);
 2752 
 2753     if( getFTPWD("getPWD",FS,AVStr(resp)) ){
 2754         /* new-110521d to suppress PWD to save dir. before tmp. CWD */
 2755         scanPWD(resp,BVStr(pwd),VStrNULL);
 2756         chdir_cwd(BVStr(pwd),FS->fs_CWD,0);
 2757         sv1log("##FTPWD/getPWD[%s][%s][%s]\n",resp,FS->fs_CWD,pwd);
 2758         return;
 2759     }
 2760     setVStrEnd(pwd,0);
 2761     if( put_get(FS->fs_ts,FS->fs_fs,AVStr(resp),sizeof(resp),"PWD\r\n") != EOF )
 2762         scanPWD(resp,AVStr(pwd),VStrNULL);
 2763 }
 2764 static void setLoginPWD0(FtpStat *FS,PCStr(resp))
 2765 {   CStr(path,1024);
 2766 
 2767     scanPWD(resp,AVStr(path),VStrNULL);
 2768     if( path[0] == 0 ){
 2769         /* 9.2.5 setting LoginPWD "" breaks MOUNT of this DeleGate */
 2770         if( FCF.fc_nullPWD ){
 2771         }else{
 2772             strcpy(path,"/");
 2773         }
 2774     }
 2775     strcpy(FS->fs_logindir,path);
 2776     sv1log("LoginPWD: \"%s\"\n",path);
 2777 }
 2778 static int get_resp(FILE *fs,FILE *tc,PVStr(resps),int rsize);
 2779 static int setLoginPWD(FtpStat *FS,FILE *ts,FILE *fs)
 2780 {   CStr(resp,1024);
 2781 
 2782     if( getFTPWD("setLoginPWD",FS,AVStr(resp)) ){
 2783         /* new-110521d to suppress PWD to see the login dir. */
 2784         setLoginPWD0(FS,resp);
 2785         return 0;
 2786     }
 2787     fputs("PWD\r\n",ts);
 2788     fflush(ts);
 2789     if( get_resp(fs,NULL,AVStr(resp),sizeof(resp)) == EOF )
 2790         return -1;
 2791 
 2792     setLoginPWD0(FS,resp);
 2793     return 0;
 2794 }
 2795 /* new-110521d to suppress generating PWD to a FTP server */
 2796 /* Usage Example: CMAP="/:FTPWD:ftp:*:*" */
 2797 static int getFTPWD(PCStr(wh),FtpStat *FS,PVStr(resp)){
 2798     IStr(ftpwd,128);
 2799 
 2800     if( 0 <= find_CMAP(FS->fs_Conn,"FTPWD",TVStr(ftpwd)) ){
 2801         sprintf(resp,"257 \"%s\"",ftpwd);
 2802         return 1;
 2803     }
 2804     return 0;
 2805 }
 2806 int CTX_checkAnonftpAuth(Connection *Conn,PVStr(user),PCStr(pass)) 
 2807 {   refQStr(host,user); /**/
 2808     int smtp_vrfy,checkuser;
 2809 
 2810     if( CTX_auth_anonftp(Conn,"*",user,pass) )
 2811         return 0;
 2812 
 2813     if( CTX_auth_anonftp(Conn,"smtp-vrfy",user,pass) ){ 
 2814         smtp_vrfy = 1;
 2815         checkuser = 1;
 2816     }else
 2817     if( CTX_auth_anonftp(Conn,"smtp-vrfy",user,"-@*") ){
 2818         smtp_vrfy = 1;
 2819         checkuser = 0;
 2820     }else{
 2821         smtp_vrfy = 0;
 2822         checkuser = 1;
 2823     }
 2824 
 2825     if( smtp_vrfy ) 
 2826     if( validateEmailAddr(user,checkuser) == 0 ){
 2827         if( host = strchr(user,'@') ){
 2828             host++;
 2829             if( strchr(host,'.') == 0 && !isinetAddr(host) ){
 2830                 getFQDN(host,AVStr(host));
 2831                 sv1log("anonftp PASS rewritten with FQDN: %s\n",
 2832                     user);
 2833             }
 2834         }
 2835         return 0;
 2836     }
 2837 
 2838     return -1;
 2839 }
 2840 static int anonPASS(Connection *Conn,FILE *tc,PCStr(user),PVStr(pass))
 2841 {   CStr(pass1,256);
 2842     CStr(pass2,256);
 2843 
 2844     RFC822_addresspartX(pass,AVStr(pass1),sizeof(pass1));
 2845     strcpy(pass2,pass1);
 2846 
 2847     if( CTX_checkAnonftpAuth(Conn,AVStr(pass2),pass2) == 0 ){
 2848         if( strcmp(pass1,pass2) != 0 )
 2849             strcpy(pass,pass2);/* host part rewriten by smtp-vrfy */
 2850         return 0;
 2851     }
 2852 
 2853     if( tc != NULL ){
 2854         CStr(clnt,MaxHostNameLen);
 2855         getClientHostPort(Conn,AVStr(clnt));
 2856         sv1log("Bad anonymous login:[%s][%s]<-(%s)\n",user,pass,clnt);
 2857         fprintf(tc,"530 Invalid/Forbidden Email address '%s'.",pass);
 2858         if( streq(pass,"mozilla@") ){
 2859             CStr(me,MaxHostNameLen);
 2860             const char *dp;
 2861             ClientIF_HPname(Conn,AVStr(me));
 2862             if( dp = strtailstr(me,":21") )
 2863                 truncVStr(dp);
 2864             fprintf(tc," Try URL ftp://ftp@%s/ to indicate your Email address as a password.",me);
 2865         }
 2866         if( streq(pass,"IEUser@")
 2867          || streq(pass,"IE30User@")
 2868          || streq(pass,"IE40User@")
 2869         ){
 2870             fprintf(tc," Find %s in your registory and repair it...",pass);
 2871         }
 2872         fprintf(tc,"\r\n");
 2873         fflush(tc);
 2874     }
 2875     return -1;
 2876 }
 2877 
 2878 static void ftp_banner(Connection *Conn,FILE *tc)
 2879 {   const char *aurl;
 2880     CStr(rurl,256);
 2881     CStr(msg,2048);
 2882     CStr(buf,0x4000);
 2883     FILE *tmp;
 2884     int rcc;
 2885 
 2886     aurl = "/-/builtin/mssgs/file/ftp-banner.dhtml";
 2887     getBuiltinData(Conn,"FTP-banner",aurl,AVStr(msg),sizeof(msg),AVStr(rurl));
 2888 
 2889     tmp = TMPFILE("FTPbanner");
 2890     put_eval_dhtml(Conn,rurl,tmp,msg);
 2891     fflush(tmp);
 2892     fseek(tmp,0,0);
 2893     if( 0 <= (rcc = fread(buf,1,sizeof(buf)-1,tmp)) )
 2894         setVStrEnd(buf,rcc);
 2895     else    setVStrEnd(buf,0);
 2896     insert_scode(buf,tc,220);
 2897 
 2898     if( !FCF.fc_noxdcCL )
 2899         fprintf(tc,"%s\r\n",XDC_OPENING_B64);
 2900 
 2901     fprintf(tc,"220  \r\n");
 2902     fclose(tmp);
 2903 }
 2904 
 2905 double STLS_fsvim_wait(double ws);
 2906 int FTP_STARTTLS_withSV(Connection *Conn,FILE *ts,FILE *fs);
 2907 int FTP_STARTTLS_withCL(Connection *Conn,FILE *tc,FILE *fc,PCStr(com),PCStr(arg));
 2908 int FTP_dataSTLS_FSV(Connection *Conn,Connection *dataConn,int svdata);
 2909 int FTP_dataSTLS_FCL(Connection *Conn,Connection *dataConn,int cldata);
 2910 
 2911 static void url_login(Connection *oConn,FtpStat *FS,FILE *fc,FILE *tc,PCStr(user),PCStr(pass))
 2912 {   CStr(url,1024);
 2913     FILE *hfp;
 2914     Connection ConnBuf, *Conn = &ConnBuf;
 2915     const char *msg;
 2916 
 2917     ConnInit(Conn);
 2918     ToS = ToSX = -1;
 2919     Conn->from_myself = 1;
 2920     wordScan(user,ClientAuth.i_user);
 2921     lineScan(pass,ClientAuth.i_pass);
 2922     /*
 2923     Conn->cl_auth.i_stat = AUTH_SET;
 2924     */
 2925     Conn->cl_auth.i_stat = AUTH_FORW;
 2926 
 2927     sprintf(url,"%s://%s:%d/%s",FS->fs_proto,FS->fs_host,FS->fs_port,
 2928         FS->fs_loginroot);
 2929     Conn->no_dstcheck_proto = serviceport(FS->fs_proto);
 2930     hfp = CTX_URLget(Conn,1,url,1,NULL);
 2931     if( hfp && !feof(hfp) )
 2932         msg = "230 logged in";
 2933     else    msg = "530 cannot login";
 2934     sv1log("## %s -> %s\n",url,msg);
 2935     fprintf(tc,"%s\r\n",msg);
 2936     if( hfp )
 2937         fclose(hfp);
 2938 }
 2939 static int controlCWD(FtpStat *FS,FILE *tc,PCStr(dir));
 2940 
 2941 static int px_doAUTH(FtpStat *FS,Connection *Conn,FILE *tc,PVStr(auser),PVStr(apass),PVStr(ahost),PVStr(cuser),PVStr(cpass),PVStr(cserv)){
 2942     int ok;
 2943     const char *proto = "ftp";
 2944     const char *host = "-";
 2945     int port = 0;
 2946     IStr(up,256);
 2947     refQStr(hostp,up);
 2948     refQStr(passp,up);
 2949     IStr(hostb,MaxHostNameLen);
 2950     IStr(svup,256);
 2951     IStr(userb,128);
 2952     AuthInfo ident;
 2953     IStr(iuser,128); /* u of USER u@h */
 2954     IStr(ihost,128); /* h of USER u@h */
 2955 
 2956     sprintf(up,"%s:%s",auser,apass);
 2957 
 2958     if( FCF.fc_authproxy ){
 2959         if( strrchr(auser,'@') ){
 2960             Xsscanf(auser,"%[^@]@%s",AVStr(iuser),AVStr(ihost));
 2961         }
 2962     }
 2963     if( FCF.fc_authproxy & (PXA_HOSTMAP|PXA_USERHOSTMAP) ){
 2964         /* user@host to be matched with user at host */
 2965         if( ihost[0] ){
 2966             port = scan_hostport(proto,ihost,AVStr(hostb));
 2967             host = hostb;
 2968             if( FCF.fc_authproxy & PXA_USERHOSTMAP ){
 2969                 sprintf(up,"%s:%s",iuser,apass);
 2970             }
 2971         }
 2972     }
 2973 
 2974     bzero(&ident,sizeof(ident));
 2975     ok = doAUTH(Conn,0,tc,proto,host,port,AVStr(up),BVStr(ahost),0,&ident);
 2976 
 2977     if( ident.i_stat & AUTH_FORW )
 2978     if( ident.i_stype == (AUTH_AORIGIN | AUTH_APROXY) ){
 2979         if( FCF.fc_proxy == NULL || isinList(FCF.fc_proxy,"user") ){
 2980             strcpy(cuser,ident.i_user);
 2981             strcpy(cpass,ident.i_pass);
 2982             strcpy(cserv,ident.i_Host);
 2983         }
 2984     }
 2985     if( FCF.fc_authproxy & PXA_USERGEN ){
 2986         /* if with maped-username with AUTHORIZER=asv(mapped-user) */
 2987         if( ident.i_stat & AUTH_MAPPED ){
 2988             Xsscanf(auser,"%[^:]",AVStr(userb));
 2989             sv1log("USER %s <- %s\n",ident.i_user,userb);
 2990             strsubst(AVStr(auser),userb,ident.i_user);
 2991             FS->fs_proxyauth.i_stat |= AUTH_SET;
 2992         }
 2993     }
 2994     if( FCF.fc_authproxy & (PXA_AUTHGEN|PXA_USERHOSTMAP) ){
 2995         /* MYAUTH=u:p:ftp:host */
 2996         if( get_MYAUTH(Conn,AVStr(svup),proto,host,port) ){
 2997             strsubst(AVStr(svup),"%h",ihost);
 2998             strsubst(AVStr(svup),"%U",auser);
 2999             strsubst(AVStr(svup),"%P",apass);
 3000             Xsscanf(svup,"%[^:]:%s",AVStr(cuser),AVStr(cpass));
 3001         }
 3002     }
 3003     return ok;
 3004 }
 3005 
 3006 static void proxyFTP(Connection *Conn)
 3007 {   FILE *tc,*fc;
 3008     CStr(req,1024);
 3009     const char *dp;
 3010     CStr(com,128);
 3011     CStr(arg,1024);
 3012     const char *chost;
 3013     CStr(cuser,256);
 3014     CStr(cpass,256);
 3015     CStr(cserv,1024);
 3016     CStr(usermbox,MaxHostNameLen);
 3017     int anonymous = 0;
 3018     int islocal;
 3019     int csock;
 3020     FtpStat FSbuf, *FS = &FSbuf;
 3021     int timeout;
 3022     int proxyLoggedin;
 3023     FILE *xtc;
 3024     CStr(pxuser,128);
 3025     CStr(pxpass,128);
 3026     CStr(pxuserpass,256);
 3027     CStr(pxacct,128);
 3028     CStr(pxhost,128);
 3029     CStr(xhost,MaxHostNameLen);
 3030     CStr(host,MaxHostNameLen);
 3031     const char *xp;
 3032     int port;
 3033     AuthInfo ident;
 3034     int cerror,error,nerror;
 3035     int noanon = 0;
 3036 
 3037     fc = tc = xtc = 0;
 3038     if( FCF.fc_ccx & CCX_COMMAND ){
 3039         if( !CCXactive(CCX_TOSV) ){
 3040             scan_CCXTOSV(Conn);
 3041         }
 3042     }
 3043     PFS = FS;
 3044     init_FS(FS,Conn);
 3045 
 3046     if( isWindowsCE() )
 3047     if( CCXactive(CCX_TOCL) ){
 3048         CCXcreate("*","utf-8",FSCCX_TOME);
 3049         FCF.fc_ccx |= CCX_COMMAND;
 3050     }
 3051     tc = fdopen(fddup(ToC),"w");
 3052 
 3053     if( CTX_auth(Conn,NULL,NULL) == 0 ) /* no AUTHORIZER, host based auth. only  */
 3054     if( !source_permitted(Conn) ){
 3055         getClientUserMbox(Conn,AVStr(usermbox));
 3056         sv1log("FTP LOGIN FROM %s REJECTED\n",usermbox);
 3057         service_permitted(Conn,"ftp"); /* delay */
 3058         fprintf(tc,"421 forbidden\r\n");
 3059         fflush(tc);
 3060         return;
 3061     }
 3062     strcpy(arg,"/");
 3063     rewrite_CWD(FS,VStrNULL,AVStr(arg),NULL);
 3064     if( MountOptions && isinList(MountOptions,"noanon") ){
 3065         noanon = 1;
 3066     }
 3067     if( changesv(Conn,FS,"init",AVStr(cserv)) ){
 3068         fc = fdopen(fddup(FromC),"r");
 3069         cuser[0] = 0;
 3070         change_server(Conn,FS,fc,tc,"","",cuser,"","");
 3071         fclose(tc);
 3072         fclose(fc);
 3073         return;
 3074     }
 3075 /*
 3076 this is introduced at 3.0.42.
 3077 - should be repealed for server switching by non-CWD command (one-time switch)
 3078 - should be repealed, it might do login in vain to server MOUNTed on root ("/")
 3079 - might be necessary to avoid repetitive one-time switchs for command on "/"
 3080 - might be necessary to avoid useless invalid USER+PASS in vain
 3081     if( strncmp(arg,"//",2) == 0 ){
 3082         change_server(Conn,FS,fc,tc,"OPEN",arg+2,"","","");
 3083         return;
 3084     }
 3085 - automatic login to the server MOUNTed at / may not be good if there
 3086   are multiple MOUNT points for multiple destination servers
 3087 */
 3088     strcpy(FS->fs_logindir,FS->fs_CWD);
 3089     fc = fdopen(fddup(FromC),"r");
 3090     ftp_banner(Conn,tc);
 3091     fflush(tc);
 3092 
 3093     usermbox[0] = 0;
 3094     getClientUserMbox(Conn,AVStr(usermbox));
 3095 
 3096     cuser[0] = cpass[0] = 0;
 3097     cserv[0] = 0;
 3098     FS->fs_TYPE[0] = 0;
 3099     FS->fs_serverWithPASV = 1;
 3100     islocal = FS->fs_islocal;
 3101 
 3102     FS->fs_myport = ClientIF_name(Conn,FromC,AVStr(FS->fs_myhost));
 3103     ClientIF_addr(Conn,FromC,AVStr(FS->fs_myaddr));
 3104 
 3105     xtc = TMPFILE("proxy-auth");
 3106     if( doAUTH(Conn,NULL,xtc,"ftp","-",0,CVStr("user-xxxx:pass-xxxx"),CVStr("host-xxxx"),NULL,NULL) == EOF ){
 3107         pxuser[0] = pxpass[0] = pxacct[0] = pxhost[0] = 0;
 3108         chost = 0;
 3109         proxyLoggedin = 0;
 3110     }else   proxyLoggedin = -1;
 3111 
 3112     nerror = cerror = error = 0;
 3113     for(;;){
 3114         FS->fs_islocal = islocal;
 3115 
 3116         if( ConnError & CO_TIMEOUT )
 3117             break;
 3118         if( ConnError & CO_CLOSED )
 3119             break;
 3120 
 3121         fflush(tc);
 3122         if( FS->fs_timeout ){
 3123             timeout = (int)(FS->fs_timeout * 1000);
 3124         }else
 3125         if( FS->fs_CWD[0] == 0 )
 3126             timeout = LOGIN_TIMEOUT * 1000;
 3127         else    timeout = FTP_FROMCLNT_TIMEOUT * 1000;
 3128         if( fPollIn(fc,timeout) <= 0 ){
 3129             if( feof(fc) || ferror(tc) ){
 3130                 sv1log("disconnected from the client: %d %d\n",
 3131                     feof(fc),ferror(tc));
 3132                 break;
 3133             }
 3134             fprintf(tc,"421 ---- PROXY-FTP login: TIMEOUT(%d)\r\n",
 3135                 LOGIN_TIMEOUT);
 3136             fflush(tc);
 3137             break;
 3138         }
 3139         if( fgetsFromCT(AVStr(req),sizeof(req),fc) == NULL ){
 3140             sv1log("proxyFTP got EOF from the client.\n");
 3141             break;
 3142             /* Exit(0); Login LOG should be flushed.  QUIT command
 3143                from the user should be remembered as a normal EOF
 3144              */
 3145         }
 3146         incRequestSerno(Conn);
 3147         CCXreq(Conn,FS,req,AVStr(req),sizeof(req));
 3148 
 3149         if( strncasecmp(req,"PASS",4) == 0 && anonymous == 0 )
 3150             command_log("CLIENT-SAYS: PASS ********\n");
 3151         else    command_log("CLIENT-SAYS: %s",req);
 3152 
 3153         dp = wordScan(req,com);
 3154         if( *dp == ' ' ){
 3155             textScan(dp+1,arg);
 3156         }else
 3157         lineScan(dp,arg);
 3158         strcpy(FS->fs_curcom,com);
 3159         strcpy(FS->fs_curarg,arg);
 3160         if( streq(com,"USER") )
 3161             strcpy(FS->fs_OUSER,arg);
 3162 
 3163         Conn->no_dstcheck = 1; /* DST_HOST is not set yet */
 3164         if( !method_permitted(Conn,"ftp",com,1) ){
 3165             fprintf(tc,"500 forbidden command: %s\r\n",com);
 3166             fflush(tc);
 3167             continue;
 3168         }
 3169         Conn->no_dstcheck = 0;
 3170 
 3171         if( proxyLoggedin == 0 )
 3172         if( strcaseeq(com,"USER") || strcaseeq(com,"PASS") ){
 3173             if( strcaseeq(com,"USER") ){
 3174                 lineScan(arg,pxuser);
 3175                 if( dp = strstr(pxuser,"//") ){
 3176                     truncVStr(dp);
 3177                     strcpy(FS->fs_USER,dp+2);
 3178                     if( dp = strchr(FS->fs_USER,'@') ){
 3179                         truncVStr(dp);
 3180                         wordScan(dp+1,xhost);
 3181                     }else{
 3182                         wordScan(FS->fs_USER,xhost);
 3183                         strcpy(FS->fs_USER,"ftp");
 3184                     }
 3185                     chost = xhost;
 3186                     wordScan(FS->fs_USER,cuser);
 3187                 }
 3188             }else
 3189             if( strcaseeq(com,"PASS") )
 3190                 lineScan(arg,pxpass);
 3191             else    lineScan(arg,pxacct);
 3192 
 3193             sprintf(pxuserpass,"%s:%s",pxuser,pxpass);
 3194             if( px_doAUTH(FS,Conn,xtc,AVStr(pxuser),AVStr(pxpass),
 3195                AVStr(pxhost),AVStr(cuser),AVStr(cpass),AVStr(cserv)) == EOF ){
 3196                 if( strcaseeq(com,"USER") ){
 3197  fprintf(tc,"331 [Proxy] Password required for %s.\r\n",pxuser);
 3198                 }else{
 3199                     sv1log("login ERROR (%s)\n",pxuser);
 3200  fprintf(tc,"530 [Proxy] Login failed.\r\n");
 3201                 }
 3202             }else{
 3203             if( cuser[0] && cserv[0] ){
 3204                 change_server(Conn,FS,fc,tc,com,cserv,
 3205                     cuser,cpass,FS->fs_TYPE);
 3206                 clearVStr(cuser);
 3207                 clearVStr(cpass);
 3208                 clearVStr(cserv);
 3209                 continue;
 3210             }else
 3211 FS->fs_anonymousOK = 1; /* temporary */
 3212 
 3213                 /* tentative ... this should be treated more
 3214                  * generally (based on server, generating
 3215                  * arbitrary user:pass, etc.)
 3216                  */
 3217                 if( get_MYAUTH(Conn,AVStr(pxuserpass),"ftp","-",0) ){
 3218                     if( streq(pxuserpass,"%U:%P") ){
 3219                         lineScan(pxuser,cuser);
 3220                         lineScan(pxpass,cpass);
 3221                     }
 3222                 }
 3223 
 3224                 lineScan(pxuser,FS->fs_proxyauth.i_user);
 3225                 lineScan(pxhost,FS->fs_proxyauth.i_Host);
 3226                 proxyLoggedin = 1;
 3227                 sv1log("proxy-login OK (%s)\n",pxuser);
 3228                 if( chost ){
 3229  fprintf(tc,"332 Password requird for target %s@%s.\r\n",cuser,chost);
 3230                 }else{
 3231  fprintf(tc,"230-[Proxy] User %s logged in.\r\n",pxuser);
 3232  fprintf(tc,"230 Now you can login a target FTP server with USER user@host\r\n");
 3233 /* should do chdir(HOMEofUSER) */
 3234                 }
 3235             }
 3236             continue;
 3237         }
 3238         else
 3239         if( comeq(com,"QUIT")
 3240          || comeq(com,"AUTH")
 3241          || comeq(com,"HELP")
 3242         ){
 3243         }else{
 3244             fprintf(tc,"530 [Proxy] Login required.\r\n");
 3245             continue;
 3246         }
 3247         if( comeq(com,"AUTH") ){
 3248             if( streq(arg,"GSSAPI")
 3249              || streq(arg,"KERBEROS_V4")
 3250             ){
 3251                 sv1log("### not supported (%s %s)\n",com,arg);
 3252                 fprintf(tc,"500 not supported (yet)\r\n");
 3253                 continue;
 3254             }
 3255         }
 3256 
 3257         if( 0 < proxyLoggedin && chost && strcaseeq(com,"ACCT") ){
 3258             strcpy(cpass,arg);
 3259             change_server(Conn,FS,fc,tc,com,chost,cuser,cpass,
 3260                 FS->fs_TYPE);
 3261             continue;
 3262         }
 3263 
 3264         if( strcaseeq(com,"USER") ){
 3265             int nondefaultMounted();
 3266             replace_atmark("USER",arg);
 3267             if( !nondefaultMounted() ) /* working as a pure proxy */
 3268             if( strchr(arg,'@')==0 )/* USER without @host extension */
 3269             if( FCF.fc_proxy != NULL ){
 3270                 if( isinList(FCF.fc_proxy,"user") )
 3271                 if( !isinList(FCF.fc_proxy,"path") )
 3272                 {
 3273                     fprintf(tc,"530 login user@host\r\n");
 3274                     continue;
 3275                 }
 3276             }
 3277 
 3278             if( unescape_user_at_host(AVStr(arg)) )
 3279                 sprintf(req,"%s %s\r\n",com,arg);
 3280             if( streq(arg,"(none)") || arg[0] == 0 ){
 3281                 fprintf(tc,"530 bad user: %s\r\n",arg);
 3282                 continue;
 3283             }
 3284         }
 3285         if( isFTPxHTTP(FS->fs_proto) ){
 3286             if( strcaseeq(com,"USER") || strcaseeq(com,"PASS") ){
 3287             int hcode;
 3288             hcode = ftpxhttpAUTH(FS,com,arg);
 3289             if( hcode == 401 ){
 3290                 if( strcaseeq(com,"USER") )
 3291                     fprintf(tc,"331 password required\r\n");
 3292                 else    fprintf(tc,"530 bad auth.\r\n");
 3293                 continue;
 3294             }
 3295             }
 3296         }
 3297 
 3298         if( PATHCOMS(com) ){
 3299             int hit;
 3300             AuthInfo sident;
 3301             sident = FS->fs_Ident;
 3302             if( swRemote(FS,com,AVStr(arg),AVStr(cserv),NULL) ){
 3303                 /* options like "-l" for LIST must be forwarded too... */
 3304                 if( *arg == '-' ){
 3305                     wordScan(arg,FS->fs_opts);
 3306                 }
 3307 
 3308                 hit = RETRCOMS(com)
 3309                  && lookaside_cache(Conn,FS,tc,com,arg,1);
 3310                 FS->fs_Ident = sident;
 3311                 if( hit ){
 3312                     continue;
 3313                 }
 3314 
 3315                 change_server(Conn,FS,fc,tc,com,cserv,
 3316                     cuser,cpass,FS->fs_TYPE);
 3317                 cserv[0] = 0;
 3318                 continue;
 3319             }
 3320             if( FS->fs_authERR ){
 3321                 fprintf(tc,"530 not authorized\r\n");
 3322                 FS->fs_authERR = 0;
 3323                 continue;
 3324             }
 3325         }
 3326 
 3327         if( FTP_STARTTLS_withCL(Conn,tc,fc,com,arg) ){
 3328             continue;
 3329         }else
 3330         if( AsServer(Conn,FS,tc,fc,com,arg,cuser) ){
 3331             continue;
 3332         }else
 3333         if( Mounted() )
 3334         /* if mounted */{
 3335             if( strcasecmp(com,"USER") == 0 && strchr(arg,'@') ){
 3336               /* escape "/" in user-name to "%2F" while leaving
 3337                * original "%2F" and other "%XX" as is.
 3338                */
 3339               url_escapeX(arg,AVStr(arg),sizeof(arg),"%%/?",":@");
 3340 
 3341                 port = decomp_ftpsite(FS,AVStr(arg),&ident);
 3342                 wordScan(ident.i_user,cuser);
 3343                 textScan(ident.i_pass,cpass);
 3344                 wordScan(ident.i_Host,host);
 3345                 if( *host && *cuser ){
 3346                 if( !user_permitted(Conn,FS,tc,host,port,cuser) )
 3347                     continue;
 3348                 anonymous = is_anonymous(cuser);
 3349                 strcpy(com,"CWD");
 3350                 Strins(AVStr(arg),"//");
 3351                 sprintf(req,"%s %s\r\n",com,arg);
 3352                 sv1log("rewritten to: %s",req);
 3353                 }
 3354             }
 3355             if( strcaseeq(com,"CDUP") ){
 3356                 strcpy(com,"CWD");
 3357                 strcpy(arg,"..");
 3358                 sprintf(req,"%s %s\r\n",com,arg);
 3359             }
 3360             if( strcasecmp(com,"CWD") == 0 )
 3361                 if( rewrite_CWD(FS,AVStr(req),AVStr(arg),tc) ){
 3362                     islocal = FS->fs_islocal;
 3363                     continue;
 3364                 }
 3365                 islocal = FS->fs_islocal;
 3366                 /* is this really here ? or for "CWD" only? */
 3367         }
 3368         if( strcasecmp(com,"USER") == 0 ){
 3369             url_escapeX(arg,AVStr(arg),sizeof(arg),"/?",":@");
 3370             xp = scan_userpassX(arg,&ident);
 3371             wordScan(ident.i_user,cuser);
 3372             textScan(ident.i_pass,cpass);
 3373             nonxalpha_unescape(cuser,AVStr(cuser),1);
 3374             if( changesv(Conn,FS,"user",AVStr(cserv)) ){
 3375                 change_server(Conn,FS,fc,tc,com,cserv,cuser,cpass,FS->fs_TYPE);
 3376                 continue;
 3377             }
 3378             if( *xp == '@' ){
 3379                 xp++;
 3380                 change_server(Conn,FS,fc,tc,com,xp,cuser,cpass,FS->fs_TYPE);
 3381                 continue;
 3382             }
 3383             strcpy(FS->fs_USER,cuser);
 3384             anonymous = is_anonymous(cuser);
 3385             if( anonymous && noanon ){
 3386  fprintf(tc,"530 No anonymous\r\n");
 3387             }else
 3388             if( anonymous ){
 3389                 if( usermbox[0] ){
 3390  fprintf(tc,"331- Guest login ok, enter your E-mail address as password.\r\n");
 3391  fprintf(tc,"331  Default value is: %s\r\n",usermbox);
 3392                 }else{
 3393  fprintf(tc,"331 Guest login ok, enter your E-mail address as password.\r\n");
 3394                 }
 3395             }else{
 3396  fprintf(tc,"331 Password required for %s.\r\n",cuser);
 3397             }
 3398         }else
 3399         if( strcasecmp(com,"PASS") == 0 ){
 3400             textScan(arg,cpass);
 3401             /*
 3402             lineScan(arg,cpass);
 3403             */
 3404             if( anonymous ){
 3405                 replace_atmark("ANON-PASS",cpass);
 3406                 FS->fs_anonymous = 1;
 3407                 FS->fs_anonymousOK = 0;
 3408                 if( *cpass == 0 && usermbox[0] != 0 )
 3409                     strcpy(cpass,usermbox);
 3410                 if( anonPASS(Conn,tc,cuser,AVStr(cpass)) != 0 ){
 3411                     cpass[0] = 0;
 3412                     continue;
 3413                 }
 3414                 FS->fs_anonymousOK = 1;
 3415                 strcpy(FS->fs_PASS,cpass);
 3416             }else{
 3417                 FS->fs_anonymous = 0;
 3418                 FS->fs_anonymousOK = 0;
 3419                 if( 1 ){
 3420                 /* with AUTHORIZER in MOUNT or with ConnMap */
 3421                     strcpy(FS->fs_PASS,cpass);
 3422                 }
 3423             }
 3424             if( FS->fs_login1st ){
 3425                 url_login(Conn,FS,fc,tc,cuser,cpass);
 3426                 continue;
 3427             }
 3428 
 3429             if( changesv(Conn,FS,"pass",AVStr(cserv)) ){
 3430                 /* cserv is set */
 3431             }
 3432             /*
 3433             if( cserv[0] ){
 3434             */
 3435             if( cserv[0] || strcaseeq(FS->fs_proto,"sftp") ){
 3436                 if( cserv[0] == 0 ){
 3437                     sprintf(cserv,"%s:%d",FS->fs_host,FS->fs_port);
 3438                     if( FS->fs_loginroot[0] ){
 3439                         Xsprintf(TVStr(cserv),"/%s",
 3440                             FS->fs_loginroot);
 3441                     }
 3442                     sv1log("SFTPGW: SW sftp://%s\n",cserv);
 3443                 }
 3444                 change_server(Conn,FS,fc,tc,com,cserv,cuser,cpass,FS->fs_TYPE);
 3445                 if( strcaseeq(FS->fs_proto,"sftp") )
 3446                 /* 9.6.2 to disconnect the STLS=fcl thread */ {
 3447                     sv1log("#### %s://%s PASS RETURN\n",
 3448                         FS->fs_proto,cserv);
 3449                     fclose(fc);
 3450                     fclose(tc);
 3451                 }
 3452                 cserv[0] = 0;
 3453                 return;
 3454             }else{
 3455  if( anonymous )
 3456  fprintf(tc,"230- Guest login ok, your E-mail address is <%s>\r\n",cpass);
 3457  else
 3458  fprintf(tc,"230- User %s logged in.\r\n",cuser);
 3459  fprintf(tc,"230  Now you can select a FTP SERVER by cd //SERVER\r\n");
 3460             }
 3461         }else
 3462         if( strcaseeq(com,"CWD") && strncmp(arg,"//",2) == 0 ){
 3463             if( proxyLoggedin == 0 ){
 3464  fprintf(tc,"530 [Proxy] Login required.\r\n");
 3465                 continue;
 3466             }
 3467 
 3468             xp = scan_userpassX(arg+2,&ident);
 3469             if( *xp == '@' ){
 3470                 if( streq(ident.i_user,cuser)
 3471                  && ident.i_pass[0] == 0 ){
 3472                     /* reuse the previous password, used for onetime
 3473                      * LIST or so, for CWD for the same user
 3474                      */
 3475                 }else{
 3476                 wordScan(ident.i_user,cuser);
 3477                 textScan(ident.i_pass,cpass);
 3478                 }
 3479             }
 3480             if( cpass[0] == 0 ){
 3481                 strcpy(cserv,arg+2);
 3482  fprintf(tc,"331 Password required for %s.\r\n",cuser);
 3483                 continue;
 3484             }
 3485             change_server(Conn,FS,fc,tc,com,req+6,cuser,cpass,FS->fs_TYPE);
 3486         }else
 3487         if( strcaseeq(com,"CWD") && controlCWD(FS,tc,arg) ){
 3488             continue;
 3489         }else
 3490         if( strcasecmp(com,"SITE")==0 || strcasecmp(com,"OPEN")==0 ){
 3491             if( FCF.fc_proxy == 0
 3492              || !isinListX(FCF.fc_proxy,com,"c")
 3493             ){
 3494  fprintf(tc,"500 '%s': command not understood.\r\n",com);
 3495                 continue;
 3496             }
 3497             change_server(Conn,FS,fc,tc,com,req+5,cuser,cpass,FS->fs_TYPE);
 3498         }else
 3499         if( strcasecmp(com,"MACB") == 0 ){
 3500  fprintf(tc,"500 MACB? nani sore ?_?\r\n");
 3501         }else
 3502         if( strcasecmp(com,"QUIT") == 0 ){
 3503  fprintf(tc,"221 Goodbye.\r\n");
 3504             break;
 3505         }else
 3506         if( strcasecmp(com,"CDUP")==0 ){
 3507  fprintf(tc,"250 CWD command successful.\r\n");
 3508         }else
 3509         if( strcasecmp(com,"CWD")==0 ){
 3510             if( rewrite_CWD(FS,AVStr(req),AVStr(arg),tc) ){
 3511             }else
 3512             {
 3513 /* 9.2.2 the specified directory is not MOUNTed, so 550 shuold be
 3514  returned.  Old versions returned 250 (success) to cope with CWD
 3515  to non-MOUNTed intermediate path of a MOUNTed path, for example
 3516  CWD "/x" + CWD "y" for MOUNT="/x/y/* ftp://server/*"
 3517  From now on, such intermediate path is MOUNTed as "/-stab-/x".
 3518  fprintf(tc,"250 CWD command successful.\r\n");
 3519  */
 3520  fprintf(tc,"550 No such directory.\r\n");
 3521             }
 3522             islocal = FS->fs_islocal;
 3523         }else
 3524         if( strcasecmp(com,"PWD") == 0 ){
 3525             if( !rewrite_PWD(FS,req,arg,tc) ){
 3526                 const char *cwd;
 3527                 if( FS->fs_CWD[0] )
 3528                     cwd = FS->fs_CWD;
 3529                 else    cwd = "/";
 3530  fprintf(tc,"257 \"%s\" is current directory.\r\n",cwd);
 3531             }
 3532         }else
 3533         if( strcasecmp(com,"HELP") == 0 ){
 3534  fprintf(tc,"214-\r\n"); /* WS_FTP(6.7) freezes if HELP returns error */
 3535  fprintf(tc,"214 \r\n");
 3536         }else
 3537         if( comeq(com,"FEAT") && (ClientFlags & PF_STLS_DO) ){
 3538             /*
 3539             FTP_putSTLS_FEAT(Conn,tc,1);
 3540             */
 3541             if( FTP_putSTLS_FEAT(Conn,tc,1) == 0 ){
 3542                 /* 9.6.2 can be in SSL already */
 3543                 putFEAT(Conn,FS,tc,0);
 3544             }
 3545             /* should be announce another features ? */
 3546         }else{
 3547  fprintf(tc,"500-%s",req);
 3548  fprintf(tc,"500 only USER,PASS,TYPE,QUIT and CWD are available.\r\n");
 3549             sv1log("Unknown request: %s",req);
 3550             error = 1;
 3551         }
 3552 
 3553         if( error ){
 3554             nerror++;
 3555             cerror++;
 3556         }else{
 3557             cerror = 0;
 3558         }
 3559         error = 0;
 3560         if( 1 < cerror ){
 3561             int delay;
 3562             if( cerror < 10 )
 3563                 delay = cerror;
 3564             else    delay = 10;
 3565             sv1log("## delaying %ds on continuous error * %d/%d\n",
 3566                 delay,cerror,nerror);
 3567             sleep(delay);
 3568         }
 3569 
 3570     }
 3571     fflush(tc);
 3572     if( xtc ){
 3573         fclose(xtc);
 3574     }
 3575     if( GatewayFlags & GW_SERV_THREAD ){ /* FTP server as a thread */
 3576         fclose(tc);
 3577         fclose(fc);
 3578     }else
 3579     if( lSINGLEP() ){
 3580         fclose(tc);
 3581         fclose(fc);
 3582     }else
 3583     if( (ClientFlags & PF_SSL_ON) ){
 3584         /* 9.7.0 should close these to shutdown SSL normally from this
 3585          * side (server side), and should close even without SSL but
 3586          * it was like this from the origin ... (DeleGate/2.0.6)
 3587          */
 3588         fclose(tc);
 3589         fclose(fc);
 3590     }
 3591 }
 3592 
 3593 static int get_resp(FILE *fs,FILE *tc,PVStr(resps),int rsize)
 3594 {   refQStr(rp,resps); /**/
 3595     CStr(resp,1024);
 3596     CStr(rcode,4);
 3597     int lines;
 3598     int remlen;
 3599     int leng;
 3600     int tleng = 0;
 3601     refQStr(tp,resps);
 3602 
 3603     if( fs == NULL )
 3604         return 0;
 3605 
 3606     if( resps ){
 3607         remlen = rsize - 1;
 3608         cpyQStr(rp,resps);
 3609         cpyQStr(tp,resps);
 3610         setVStrEnd(resps,0);
 3611     }
 3612 
 3613     rcode[0] = 0;
 3614     for(lines = 0;;lines++){
 3615         if( fgetsFromST(AVStr(resp),sizeof(resp),fs) == 0 ){
 3616         /*
 3617         sv1log("FTP: connection timedout or closed by the server\n");
 3618         */
 3619             sprintf(resp,"421 %s\r\n",
 3620                  feof(fs) ? "connection closed by server":
 3621                     "server response timedout"
 3622             );
 3623             sv1log("FTP-SERVER: %s",resp);
 3624             if( tc != NULL ){
 3625                 fputs(resp,tc);
 3626                 fflush(tc);
 3627             }
 3628             return EOF;
 3629         }
 3630         if( tc != NULL ){
 3631             fputs(resp,tc);
 3632             fflush(tc);
 3633         }
 3634 
 3635         if( lines == 0 )
 3636             command_log("FTP-SERVER-SAYS: %s",resp);
 3637 
 3638         leng = strlen(resp);
 3639         tleng += leng;
 3640         if( resps && leng < remlen ){
 3641             tp = rp;
 3642             /*
 3643             Xstrcpy(HVStr(rsize,resps) rp,resp);
 3644             */
 3645             strcpy(rp,resp);
 3646             rp += leng;
 3647             remlen -= leng;
 3648         }
 3649 
 3650         if( resp[3] == '-' ){
 3651             if( rcode[0] == 0 )
 3652                 strncpy(rcode,resp,3);
 3653         }else{
 3654             if( rcode[0] == 0 || strncmp(resp,rcode,3) == 0 )
 3655                 break;
 3656         }
 3657     }
 3658 
 3659     if( !isdigit(resp[0]) )
 3660         return EOF;
 3661     if( resp[0] == '4' )
 3662         return EOF;
 3663     if( resp[0] == '5' )
 3664         return EOF;
 3665 
 3666     if( isdigit(tp[0])&&isdigit(tp[1])&&isdigit(tp[2]) && tp[3] == '-' ){
 3667         sv1log("## trunc resp. %d/%d/%d\n",istrlen(resps),rsize,tleng);
 3668         setVStrElem(tp,3,' ');
 3669     }
 3670     return 0;
 3671 }
 3672 
 3673 #define PS_ANON 1
 3674 #define PS_BUFF 2
 3675 
 3676 static int put_serv(int mode,FILE *ts,PCStr(fmt),...)
 3677 {   CStr(req,1024);
 3678     int rcode;
 3679     VARGS(8,fmt);
 3680 
 3681     rcode = fprintf(ts,fmt,VA8);
 3682     if( rcode == EOF )
 3683         return EOF;
 3684 
 3685     if( (mode & PS_BUFF) == 0 )
 3686         if( fflush(ts) == EOF ){
 3687             sv1log("put_serv: EOF\n");
 3688             return EOF;
 3689         }
 3690 
 3691     sprintf(req,fmt,VA8);
 3692     if( strncasecmp(req,"PASS",4) == 0 && (mode & PS_ANON) == 0 )
 3693         command_log("I-SAY: PASS ********\n");
 3694     else    command_log("I-SAY: %s",req);
 3695     return rcode;
 3696 }
 3697 static int _put_get(int mode,FILE *ts,FILE *fs,PVStr(resp),int rsize,PCStr(fmt),...)
 3698 {
 3699     if( fmt ){
 3700         VARGS(8,fmt);
 3701         if( put_serv(mode,ts,fmt,VA8) == EOF )
 3702             return EOF;
 3703     }
 3704 
 3705     if( get_resp(fs,NULL,AVStr(resp),rsize) == EOF )
 3706         return EOF;
 3707 
 3708     return 0;
 3709 }
 3710 static int put_get(FILE *ts,FILE *fs,PVStr(resp),int rsize,PCStr(fmt),...)
 3711 {
 3712     VARGS(8,fmt);
 3713     return _put_get(0,ts,fs,AVStr(resp),rsize,fmt,VA8);
 3714 }
 3715 static int PutGet(FILE *ts,FILE *fs,FILE *stfp,PCStr(req),PVStr(resp),int rsize){
 3716     int rcode = EOF;
 3717     if( put_serv(0,ts,"%s\r\n",req) == EOF ){
 3718         return EOF;
 3719     }
 3720     if( get_resp(fs,stfp,AVStr(resp),rsize) == EOF ){
 3721         return EOF;
 3722     }
 3723     fflush(stfp);
 3724     fseek(stfp,0,0);
 3725     rcode = atoi(resp);
 3726     return rcode;
 3727 }
 3728 
 3729 const char *searchPortSpec(PCStr(resp))
 3730 {   const char *rp;
 3731     int p;
 3732 
 3733     if( strncmp(resp,"229",3) == 0 ){
 3734         if( (rp = strstr(resp,"(|")) != 0 )
 3735             return rp+1;
 3736     }
 3737     for( rp = resp; *rp; rp++ ){
 3738         if( isdigit(*rp) )
 3739             if( sscanf(rp,"%*d,%*d,%*d,%*d,%*d,%d",&p) == 1 )
 3740                 return rp;
 3741     }
 3742     return NULL;
 3743 }
 3744 
 3745 static int insdataFSV(Connection *ctrlConn,PCStr(where),FtpStat *FS,int svdata,int cldata)
 3746 {   int fsvdata;
 3747     Connection *Conn = &FS->fs_dataConn;
 3748 
 3749     if( Conn->xf_filters & XF_FSV )
 3750         return 0;
 3751 
 3752     if( 0 <= FTP_dataSTLS_FSV(ctrlConn,Conn,svdata) )
 3753         return 1;
 3754 
 3755     if( REAL_HOST[0] == 0 )
 3756         strcpy(Conn->sv.p_host,"-"); /* for DST_PROTO */
 3757 /*
 3758     else    strcpy(Conn->sv.p_host,REAL_HOST);
 3759 copying to itself
 3760 */
 3761 
 3762     /*
 3763      * the thread for FSV=sslway will not become ready at this point
 3764      * (where==PASV) until the peer SSLway (SSL at the server-side)
 3765      * will be started later by RETR or LIST command
 3766      */
 3767     if( streq(where,"PASV") ){
 3768         int gwf = GatewayFlags;
 3769         GatewayFlags |= GW_SYN_SSLSTART;
 3770         fsvdata = insertFSV(Conn,-1,svdata);
 3771         GatewayFlags = gwf;
 3772     }else
 3773     fsvdata = insertFSV(Conn,-1,svdata);
 3774     if( 0 <= fsvdata ){
 3775         sv1log("inserted FSV[%s] %d -> %d\n",where,svdata,fsvdata);
 3776         dup2(fsvdata,svdata);
 3777         close(fsvdata);
 3778         return 1;
 3779     }
 3780     return 0;
 3781 }
 3782 static int connectPASV(Connection *Conn,FtpStat *FS,FILE *ts,FILE *fs,PVStr(resp),int rsize)
 3783 {   const char *rp;
 3784     int psock;
 3785     int timeout,stimeout;
 3786 
 3787     if( FCF.fc_doepsvSV ){
 3788         /* 9.9.8 force EPSV to server */
 3789         put_serv(0,ts,"EPSV\r\n");
 3790     }else
 3791     if( sock_isv6(ServSock(Conn,ts,"mkPASV")) ){
 3792         put_serv(0,ts,"EPSV\r\n");
 3793     }else
 3794     put_serv(0,ts,"PASV\r\n");
 3795     if( get_resp(fs,NULL,AVStr(resp),rsize) == EOF || resp[0] != '2' ){
 3796         sv1log("PASV ... %s",resp);
 3797         return -1;
 3798     }
 3799 
 3800     rp = searchPortSpec(resp);
 3801     if( rp ){
 3802         stimeout = CON_TIMEOUT;
 3803         timeout = CON_TIMEOUT_DATA;
 3804         psock = makeDataConn(Conn,rp,ServSock(Conn,ts,"mkPASV"),1);
 3805         if( 0 <= psock && FS != NULL )
 3806             insdataFSV(Conn,"PASV",FS,psock,-1);
 3807         CON_TIMEOUT = stimeout;
 3808     }else{
 3809         sv1log("UNKNOWN PASV RESPONSE: %s",resp);
 3810         AbortLog();
 3811         psock = -1;
 3812     }
 3813     return psock;
 3814 }
 3815 static int make_ftp_data(Connection *Conn,PVStr(mport),PCStr(shost),int sport,int csock,int direct,int pasv)
 3816 {   CStr(lhost,256);
 3817     int dsock,lport;
 3818     const char *proto;
 3819     int SF[4] = {0};
 3820 
 3821     if( ServerFlags & (PF_SSL_ON|PF_STLS_ON) ){
 3822         /* 9.9.7 BIND+ACCEPT ftp-data via SOCKS/ssl */
 3823         /* this should be done with dataConn with clear ServerFlags */
 3824         saveSTLS("make-ftp-data",Conn,SF);
 3825         clearSTLS(Conn);
 3826     }
 3827     lhost[0] = 0;
 3828     lport = 0;
 3829     proto = pasv?"ftp-data-pasv":"ftp-data-port";
 3830     if( SRCIFfor(Conn,proto,shost,sport,AVStr(lhost),&lport) ){
 3831     }else
 3832     SRCIFfor(Conn,"ftp-data",shost,sport,AVStr(lhost),&lport);
 3833     dsock = bind_ftp_data(Conn,AVStr(mport),shost,sport,csock,pasv,lhost,lport);
 3834     restoreSTLS("make-ftp-data",Conn,SF);
 3835     return dsock;
 3836 }
 3837 
 3838 static int mkdsockPORT(Connection *Conn,PCStr(host),FILE *ts,FILE *fs)
 3839 {   int dsock;
 3840     CStr(mport,256);
 3841     CStr(resp,1024);
 3842 
 3843     if( FCF.fc_noportSV ){
 3844         sv1log("#{ver9.9.4} PORT to server disabled (%s)\n",host);
 3845         return -1;
 3846     }
 3847 
 3848     dsock = make_ftp_data(Conn,AVStr(mport),host,serviceport("ftp"),ServSock(Conn,ts,"mkPORT"),0,0);
 3849     if( dsock < 0 )
 3850         return dsock;
 3851 
 3852     if( FCF.fc_doeprtSV && mport[0] != '|' ){
 3853         /* 9.9.8 force EPRT to server */
 3854         VSAddr addr;
 3855         VSA_ftptosa(&addr,mport);
 3856         sprintf(mport,"|||%d|",VSA_port(&addr));
 3857         sv1log("EPRT %s\n",mport);
 3858     }
 3859     if( mport[0] == '|' ){
 3860         put_serv(0,ts,"EPRT %s\r\n",mport);
 3861     }else
 3862     put_serv(0,ts,"PORT %s\r\n",mport);
 3863     if( fs != NULL )
 3864     {
 3865         get_resp(fs,NULL,AVStr(resp),sizeof(resp));
 3866         if( 400 <= atoi(resp) ){
 3867             /* error to be cared ... */
 3868             sv1log("##ERROR mkdsockPORT[%s]: %s",host,resp);
 3869         }
 3870     }
 3871     return dsock;
 3872 }
 3873 FileSize Xatoi(PCStr(snum)){
 3874     FileSize num = 0;
 3875     const char *sp;
 3876     char sc;
 3877     int neg = 0;
 3878 
 3879     for( sp = snum; sc = *sp; sp++ ){
 3880         if( sc != ' ' ){
 3881             break;
 3882         }
 3883     }
 3884     if( *sp == '-' ){
 3885         neg = 1;
 3886         sp++;
 3887     }
 3888     for( ; sc = *sp; sp++ ){
 3889         if( isdigit(sc) ){
 3890             num = num*10 + (sc-'0');
 3891         }else
 3892         if( sc == ',' ){
 3893         }else{
 3894             break;
 3895         }
 3896     }
 3897     if( neg ){
 3898         if( 0 < num && num <= 0x7FFFFFFF ){
 3899             /* might be broken expression for 2GB <= num < 4GB */
 3900             num = (unsigned int)-num;
 3901         }else{
 3902             num = -num;
 3903         }
 3904     }
 3905     return num;
 3906 }
 3907 typedef struct {
 3908     Connection *fg_Conn;
 3909     FILE   *fg_statfp; /* to receive a list in response status */
 3910     MStr(   fg_cmd,128);
 3911     MStr(   fg_opt,128);
 3912 } FtpGw;
 3913 static int listretr(FtpGw *Fg,FILE *ts,FILE *fs,PCStr(path),int *isdirp,PVStr(resp),int rsize)
 3914 {   const char *dp;
 3915     CStr(xpath,1024);
 3916     CStr(apath,1024);
 3917     CStr(comm,1024);
 3918     int isdir;
 3919     int didcwd = 0;
 3920     Connection *Conn = Fg->fg_Conn;
 3921 
 3922     isdir = 0;
 3923     if( *path == 0 )
 3924         isdir = 1;
 3925     else
 3926     if( (dp = strrchr(path,'/')) && dp != path && dp[1] == 0 ){
 3927         path = strcpy(xpath,path);
 3928         *(char*)strrchr(path,'/') = 0;
 3929         isdir = 1;
 3930     }
 3931     if( isinFTPxHTTP(Conn) ){
 3932         if( RequestFlags & QF_FTP_COMLIST ){
 3933             isdir = 1;
 3934         }
 3935         if( RequestFlags & QF_FTP_COMRETR ){
 3936             /* should not try CWD */
 3937             isdir = 0;
 3938         }
 3939     }
 3940 
 3941     if( !isdir && put_get(ts,fs,AVStr(resp),rsize,"CWD %s\r\n",path) == EOF ){
 3942         strcpy(xpath,path);
 3943         if( dp = strrchr(xpath,'/') ){
 3944             truncVStr(dp);
 3945             if( put_get(ts,fs,AVStr(resp),rsize,"CWD %s\r\n",xpath) != EOF )
 3946                 path = dp + 1;
 3947         }
 3948         if( atoi(resp) == 550 && strstr(resp,"No such file or dir") ){
 3949             return -1;
 3950         }
 3951         sprintf(comm,"RETR %s",path);
 3952         isdir = 0;
 3953     }else{
 3954         if( isdir && path[0] )
 3955         if( put_get(ts,fs,AVStr(resp),rsize,"CWD %s\r\n",path) == EOF )
 3956             return -1;
 3957 
 3958         sprintf(comm,"%s %s",FTP_LIST_COM,FTP_LIST_OPT);
 3959         isdir = 1;
 3960         didcwd = 1;
 3961     }
 3962     if( Fg->fg_cmd[0] ){
 3963         sprintf(comm,"%s",Fg->fg_cmd);
 3964         if( Fg->fg_opt[0] ){
 3965             Xsprintf(TVStr(comm)," %s",Fg->fg_opt);
 3966         }
 3967         if( didcwd ){ /* already succeeded "CWD path" */
 3968             if( ARG0COMS(Fg->fg_cmd) ){
 3969             }else{
 3970                 sprintf(resp,"550 not a plain file\r\n");
 3971                 return -1;
 3972             }
 3973         }else{
 3974             if( *path ){
 3975                 Xsprintf(TVStr(comm)," %s",path);
 3976             }
 3977         }
 3978     }
 3979 
 3980     if( !isdir ){
 3981         CStr(size,1024);
 3982         sprintf(size,"SIZE %s",path);
 3983         if( put_get(ts,fs,AVStr(resp),rsize,"%s\r\n",size) != EOF ){
 3984             /*
 3985             Xsscanf(resp,"213 %lld",&Conn->sv.p_range[2]);
 3986             */
 3987             if( strneq(resp,"213 ",4) ){
 3988                 Conn->sv.p_range[2] = Xatoi(resp+4);
 3989                 if( isinFTPxHTTP(Conn) ){
 3990                 /*
 3991                  * SIZE -> Content-Length
 3992                  * MDTM -> Last-Modified
 3993                  */
 3994                 }
 3995             }
 3996         }
 3997     }
 3998     if( 0 < reqPART_FROM ){
 3999         CStr(rest,128);
 4000         sprintf(rest,"REST %lld",reqPART_FROM);
 4001         if( put_get(ts,fs,AVStr(resp),rsize,"%s\r\n",rest) != EOF ){
 4002             gotPART_FROM = reqPART_FROM;
 4003         }else{
 4004             gotPART_FROM = -1;
 4005         }
 4006     }
 4007 
 4008     if( Fg->fg_statfp ){
 4009         int rcode;
 4010         rcode = PutGet(ts,fs,Fg->fg_statfp,comm,AVStr(resp),rsize);
 4011         if( rcode == EOF ){
 4012             return -1;
 4013         }
 4014     }else
 4015     if( put_get(ts,fs,AVStr(resp),rsize,"%s\r\n",comm) == EOF )
 4016         return -1;
 4017 
 4018     if( isdirp )
 4019         *isdirp = isdir;
 4020     return 0;
 4021 }
 4022 static int stor(FtpGw *Fg,FILE *ts,FILE *fs,PCStr(path),PVStr(resp),int rsize)
 4023 {   const char *dp;
 4024     CStr(dir,102);
 4025 
 4026     if( strrchr(path,'/') ){
 4027         strcpy(dir,path);
 4028         path = strrchr(dir,'/');
 4029         *(char*)path++ = 0; /* not "const" but fixed */
 4030         if( put_get(ts,fs,AVStr(resp),rsize,"CWD %s\r\n",dir) == EOF )
 4031             return -1;
 4032     }
 4033     if( Fg->fg_cmd[0] ){
 4034         if( put_get(ts,fs,AVStr(resp),rsize,"%s %s\r\n",Fg->fg_cmd,path) == EOF ){
 4035             return -1;
 4036         }
 4037         return 0;
 4038     }
 4039     if( put_get(ts,fs,AVStr(resp),rsize,"STOR %s\r\n",path) == EOF )
 4040         return -1;
 4041     return 0;
 4042 }
 4043 
 4044 int ACCEPTdc(int dsvsock,int asServer)
 4045 {   CStr(host,128);
 4046     int port,dsock;
 4047 
 4048     if( 1 < peerPort(dsvsock) ){
 4049         if( ViaVSAPassociator(dsvsock) ){
 4050             CStr(sockname,256);
 4051             CStr(peername,256);
 4052             dsock = VSAPaccept(MainConn(),FTP_ACCEPT_TIMEOUT,dsvsock,0,AVStr(sockname),AVStr(peername));
 4053             if( 0 <= dsock )
 4054                 dsock = fddup(dsvsock);
 4055         }else
 4056         if( acceptViaSocks(dsvsock,AVStr(host),&port) == 0 )
 4057             dsock = fddup(dsvsock);
 4058         else    dsock = -1;
 4059     }else{
 4060         dsock = ACCEPT(dsvsock,asServer,-1,FTP_ACCEPT_TIMEOUT);
 4061         if( dsock < 0 )
 4062             sv1log("FTP ACCEPT_TIMEOUT %d\n",FTP_ACCEPT_TIMEOUT);
 4063     }
 4064     return dsock;
 4065 }
 4066 static int ACCEPT_SVPORT(int dsvsock,int asServer)
 4067 {   int dsock;
 4068     CStr(shost,128);
 4069     int sport;
 4070 
 4071     Verbose("Start accept on port for PORT from server[%d]\n",dsvsock);
 4072     dsock = ACCEPTdc(dsvsock,asServer);
 4073     if( 0 < dsock ){
 4074         sport = getpeerNAME(dsock,AVStr(shost));
 4075         Verbose("Accepted the data port: %s:%d\n",shost,sport);
 4076     }
 4077     return dsock;
 4078 }
 4079 
 4080 typedef struct {
 4081     Connection *a_Conn;
 4082     FtpStat    *a_FS;
 4083     FILE       *a_dfs;  /* data file source */
 4084     FILE       *a_fc;   /* from client */
 4085     FILE       *a_fs;   /* from server */
 4086     FILE       *a_tc;   /* to client */
 4087 } RelayArg;
 4088 static const char *relay1(int ser,PCStr(buff),int leng,FILE *tcfp,PCStr(a));
 4089 static int data_open(FtpGw *Fg,int put,int PASV,int dsock,PCStr(host),FILE *ts,FILE *fs,PCStr(path),int *isdirp,PVStr(resp),int rsize)
 4090 {   int psock = -1;
 4091     CStr(xpath,1024);
 4092     RelayArg Ra;
 4093     Connection *Conn = Fg->fg_Conn;
 4094 
 4095     /* data-connection should be suppressed for STAT and MLST ... */
 4096     if( Fg->fg_cmd[0] != 0 )
 4097     if( Fg->fg_statfp != NULL )
 4098     if( STATCOMS(Fg->fg_cmd) ){
 4099         FILE *statfp = Fg->fg_statfp;
 4100         if( strncaseeq(Fg->fg_opt,"RNFR/",5) ){
 4101             put_serv(0,ts,"RNFR %s\r\n",Fg->fg_opt+5);
 4102             if( get_resp(fs,statfp,AVStr(resp),rsize) != EOF ){
 4103             }
 4104         }
 4105         if( path[0] == 0 && strcaseeq(Fg->fg_cmd,"CWD") ){
 4106             /* maybe "CWD /" at the FTPxHTTP client */
 4107             strcpy(resp,"250 empty CWD.\r\n");
 4108             fprintf(statfp,"%s",resp);
 4109         }else
 4110         if( path[0] == 0 && !strcaseeq(Fg->fg_cmd,"STAT") ){
 4111             fprintf(statfp,"501 empty arg.\r\n");
 4112             strcpy(resp,"501 empty arg.\r\n");
 4113         }else{
 4114             put_serv(0,ts,"%s %s\r\n",Fg->fg_cmd,path); /* with opt ? */
 4115             if( get_resp(fs,statfp,AVStr(resp),rsize) != EOF ){
 4116             }
 4117         }
 4118         fflush(statfp);
 4119         fseek(statfp,0,0);
 4120         return -1;
 4121     }
 4122 
 4123     if( PASV )
 4124         psock = connectPASV(Conn,NULL,ts,fs,AVStr(resp),rsize);
 4125 
 4126     if( psock < 0 && toTunnel(Conn) ){
 4127         int mode,fd;
 4128         FILE *tc;
 4129         put_serv(0,ts,"PORT 0,0,0,0,0,0\r\n");
 4130         if( get_resp(fs,NULL,AVStr(resp),rsize) != EOF )
 4131         if( listretr(Fg,ts,fs,path,isdirp,AVStr(resp),rsize) == 0 )
 4132         if( get_resp(fs,NULL,AVStr(resp),rsize) != EOF ){
 4133             tc = TMPFILE("XDC");
 4134             /*
 4135             getMessageFX(fs,NULL,FTP_FROMSERV_TIMEOUT,relay1,tc,"","");
 4136             */
 4137             Ra.a_Conn = Conn;
 4138             Ra.a_FS = 0;
 4139             Ra.a_dfs = fs;
 4140             Ra.a_fc = 0;
 4141             Ra.a_fs = fs;
 4142             Ra.a_tc = 0;
 4143             getMessageFX(fs,NULL,FTP_FROMSERV_TIMEOUT,relay1,tc,(char*)&Ra,"");
 4144             get_resp(fs,NULL,AVStr(resp),rsize);
 4145             fd = fddup(fileno(tc));
 4146             fclose(tc);
 4147             Lseek(fd,0,0);
 4148             return fd;
 4149         }
 4150     }
 4151 
 4152     if( psock < 0 && dsock < 0 )
 4153         dsock = mkdsockPORT(Conn,host,ts,fs);
 4154 
 4155     if( path != 0 ){
 4156         if( put ){
 4157         if( stor(Fg,ts,fs,path,AVStr(resp),rsize) < 0 ){
 4158             if( 0 <= psock ){
 4159                 close(psock);
 4160                 psock = -1;
 4161             }
 4162             goto EXIT;
 4163         }
 4164         }else{
 4165         if( listretr(Fg,ts,fs,path,isdirp,AVStr(resp),rsize) < 0 ){
 4166             if( 0 <= psock ){
 4167                 close(psock);
 4168                 psock = -1;
 4169             }
 4170             goto EXIT;
 4171         }
 4172         }
 4173     }
 4174 
 4175     if( psock < 0 ){
 4176         psock = ACCEPTdc(dsock,0);
 4177         if( psock < 0 ){
 4178             CStr(myhost,MaxHostNameLen);
 4179             gethostname(myhost,sizeof(myhost));
 4180             sprintf(resp,
 4181                 "500 Data connection accept timedout (%s).\r\n",
 4182                 myhost);
 4183         }
 4184     }
 4185 EXIT:
 4186     if( 0 <= dsock ){
 4187         close(dsock);
 4188         dsock = -1;
 4189         /*sv1log("FTP DATA-PORT: %s = sock[%d]\n",mport,psock);*/
 4190     }
 4191     return psock;
 4192 }
 4193 
 4194 int ShutdownSocket(int sock);
 4195 static int relayABORT(Connection *Conn,FtpStat *FS,int ab);
 4196 
 4197 static const char *relay1(int ser,PCStr(buff),int leng,FILE *tcfp,PCStr(a))
 4198 {   int wcc;
 4199     RelayArg *Ra = (RelayArg*)a;
 4200     Connection *Conn;
 4201     int ifd;
 4202 
 4203 /*
 4204  * transmitter: should check ABOR command from client and abort...
 4205  * receiver: should forward ABOR command from client to sever...
 4206  */
 4207     Conn = Ra->a_Conn;
 4208     ifd = Ra->a_dfs ? fileno(Ra->a_dfs) : -1;
 4209     if( ifd == ClientSock || ifd == FromC ){
 4210         /* XDC + STOR ... no ABOR */
 4211     }else
 4212     if( Ra->a_fc == Ra->a_dfs ){
 4213         /* XDC + STOR ... no ABOR */
 4214     }else
 4215     if( inputReady(ClientSock,NULL) ){
 4216         if( Ra->a_FS != NULL ){
 4217             int ra;
 4218             ra = relayABORT(Conn,Ra->a_FS,0);
 4219             if( 0 < ra ){
 4220                 fflush(Ra->a_fs);
 4221                 sv1log("## ABORT XDC\n");
 4222                 return "ABORT";
 4223             }
 4224         }
 4225     }
 4226 
 4227     Verbose("@%d - %d relay1.\n",ser,leng);
 4228     if( 0 < ser )
 4229     {
 4230         EmiUpdateMD5(Conn,buff,leng);
 4231         wcc = fwriteTIMEOUT(buff,1,leng,tcfp);
 4232     }
 4233     return "";
 4234 }
 4235 
 4236 static int gotABOR(void *vRa,PVStr(buff),int leng){
 4237     RelayArg *Ra = (RelayArg*)vRa;
 4238     Connection *Conn;
 4239     int ab;
 4240 
 4241     Conn = Ra->a_Conn;
 4242     if( inputReady(ClientSock,NULL) ){
 4243         ab = relayABORT(Conn,Ra->a_FS,0);
 4244         sv1log("## XDC gotABOR\n");
 4245         return -1;
 4246     }
 4247     return leng;
 4248 }
 4249 
 4250 /* PORT to/from XDC relay */
 4251 static FileSize XDCrelayServ(FtpStat *FS,int STOR,FILE *ts,FILE *fs,FILE *tc,FILE *fc,int dsock,PCStr(port), FILE *cachefp,PVStr(resp),int rsize)
 4252 {   FILE *dfp;
 4253     FileSize xc;
 4254     const char *encode;
 4255     const char *what;
 4256     int timeout;
 4257     RelayArg Ra;
 4258 
 4259     if( STOR ){
 4260         what = "STOR";
 4261         encode = FS->fs_XDCencSV;
 4262     }else{
 4263         what = "RECV";
 4264         encode = FS->fs_XDCencCL;
 4265     }
 4266     sv1log("--- XDC%s data_relay SERVER (%s).\n",encode,what);
 4267 
 4268     if( dsock < 0 ){
 4269         sv1log("FATAL: bad data socket(%d)\n",dsock);
 4270         fprintf(tc,"426 bad data socket\r\n");
 4271         fflush(tc);
 4272         return -1;
 4273     }
 4274 
 4275     fprintf(tc,XDC_PORT_TEMP,port);
 4276     fprintf(tc,"\r\n");
 4277     fflush(tc);
 4278 
 4279     Ra.a_Conn = FS->fs_Conn;
 4280     Ra.a_FS = FS;
 4281     Ra.a_fs = fs;
 4282 
 4283     if( STOR ){
 4284         dfp = fdopen(dsock,"w");
 4285         timeout = FTP_FROMSERV_TIMEOUT;
 4286         /*
 4287         xc = getMessageFX(fc,cachefp,timeout,relay1,dfp,"",encode);
 4288         */
 4289         Ra.a_dfs = fc;
 4290         Ra.a_fc = fc;
 4291         Ra.a_tc = tc;
 4292         xc = getMessageFX(fc,cachefp,timeout,relay1,dfp,(char*)&Ra,encode);
 4293         fclose(dfp);
 4294         if( fs != NULL ) /* not to local file */
 4295         get_resp(fs,NULL,AVStr(resp),rsize);
 4296     }else{
 4297         dfp = fdopen(dsock,"r");
 4298         /*
 4299         xc = putMessageFX(dfp,tc,cachefp,encode);
 4300         */
 4301         xc = putMessageFX_CB(dfp,tc,cachefp,encode,gotABOR,&Ra);
 4302         fclose(dfp);
 4303         if( fs == NULL ) /* from local file or cache */
 4304         sprintf(resp,"226 Transfer complete (%lld bytes).\r\n",xc);
 4305         else
 4306         get_resp(fs,NULL,AVStr(resp),rsize);
 4307         putPostStatus(tc,resp);
 4308     }
 4309     if( fs != NULL ){
 4310     fputs(resp,tc);
 4311     fflush(tc);
 4312     }
 4313     return xc;
 4314 }
 4315 static FileSize XDCrelayClnt(Connection *Conn,FtpStat *FS,int STOR,FILE *ts,FILE *fs,FILE *tc,FILE *fc, FILE *cachefp,PVStr(resp),int rsize)
 4316 {   CStr(port,128);
 4317     int dsock;
 4318     FILE *dfp;
 4319     FileSize xc;
 4320     const char *encode;
 4321     const char *what;
 4322     int timeout;
 4323     RelayArg Ra;
 4324 
 4325     if( STOR ){
 4326         what = "STOR";
 4327         encode = FS->fs_XDCencSV;
 4328     }else{
 4329         what = "RECV";
 4330         encode = FS->fs_XDCencCL;
 4331     }
 4332     sv1log("---- XDC%s data_relay CLIENT (%s).\n",encode,what);
 4333     get_resp(fs,NULL,AVStr(resp),rsize);
 4334 
 4335     if( sscanf(resp,XDC_PORT_TEMP,port) <= 0 ){
 4336         fputs(resp,tc);
 4337         fflush(tc);
 4338         return 0;
 4339     }
 4340 
 4341     Verbose("XDC %s",resp);
 4342     if( strcmp(port,XDC_PASV_PORT) == 0 ){
 4343         if( 0 < FS->fs_pclsock )
 4344             dsock = ACCEPTdc(FS->fs_pclsock,1);
 4345         else{
 4346             dsock = -1;
 4347         }
 4348     }else
 4349     dsock = makeDataConn(Conn,port,ClientSock,0);
 4350 
 4351 /*
 4352  * should peek fc to sense ABOR command from the client...
 4353  */
 4354 /*
 4355     if( 0 < PollIn(fc,1) ){
 4356         CStr(req,128);
 4357         fgetsTimeout(req,sizeof(req),fc);
 4358         if( strncasecmp(req,"ABOR",4) == 0 ){
 4359             fprintf(tc,"225 ABOR command successful.\r\n");
 4360             return 0;
 4361         }
 4362     }
 4363