ftpLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:53k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* ftpLib.c - File Transfer Protocol (FTP) library */
  2. /* Copyright 1984 - 2002 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 02q,26jun02,elr  Correction for passive mode STOR and RETR command in 
  8.                      ftpXfer() (SPR #79108)
  9. 02p,06jun02,elr  Documentation (SPR #78008)
  10. 02o,23may02,elr  Added ftplPasvModeDisable flag
  11. 02n,22may02,elr  added PASV mode to ftpDataConnInit() (SPR #77169) to
  12.                      correct long delays during frequent reboots
  13.                  changed doc of ftpReplyGet() and 
  14.                      ftpReplyGetEnhanced() (SPR #76317)
  15.                  changed ftpLibDebugLevelSet() to ftpLibDebugOptionsSet()
  16. 02m,10may02,kbw  making man page edits
  17. 02l,22mar02,elr  Cleanup compiler warnings
  18. 02k,14mar02,elr  Define host response return codes (SPR #68838)
  19.                  Host response FTP_PRELIM is ok during ftpXfer (SPR #71954)
  20.                  Limit FTP_TRANSIENT response retries during ftpXfer() 
  21.                      ftpPrelimConfig() (SPR #70907) (SPR #6259) (SPR #33119)
  22.                  Added ftpLibDebugLevelSet() and added debugging messages 
  23.                  Added documentation about stack memory allocation 
  24.                     and tuning (SPR #64220)
  25.                  Moved some error message to FTPLDEBUG  (SPR #71496)
  26.                     and removed ftpErrorSuppress in favor ftplDebug
  27.                  Added documentation concerning ftpHookup() and ftpDataConnInit()
  28.                     socket connections (SPR #62289) (SPR #30556)
  29. 02j,15oct01,rae  merge from truestack ver 02n, base 02i (SPR #67644)
  30. 02i,22sep99,cno  change FD_ISSET control & data connection tests (SPR27234)
  31. 02h,15mar99,elg  change erroneous example code in ftpXfer() (SPR 9989).
  32. 02g,12mar99,p_m  Fixed SPR# 9022 by publishing ftpLs().
  33. 02f,05oct98,jmp  doc: cleanup.
  34. 02e,23jan98,spm  fixed ftpXfer to expect correct return codes for commands
  35.                  which do not involve data transfer (SPR #20017)
  36. 02d,05jun95,jag  Changed ftpXfer to handle error 425 at boot time.
  37. 02c,30aug93,jag  Changed ftpCommand to issue a single write (SPR #2492)
  38. 02b,11aug93,jmm  Changed ioctl.h and socket.h to sys/ioctl.h and sys/socket.h
  39. 02a,23feb93,jdi  doc: changed ftpCommand() examples to fixed no. of args.
  40. 01z,20jan93,jdi  documentation cleanup for 5.1.
  41. 01y,20sep92,kdl  added ftpLs; moved ftpErrorSuppress to funcBind.c.
  42. 01x,11sep92,jmm  added ftpErrorSuppress for lsOld() (SPR #1257)
  43. 01w,19aug92,smb  Changed systime.h to sys/times.h.
  44. 01v,18jul92,smb  Changed errno.h to errnoLib.h.
  45. 01u,26may92,rrr  the tree shuffle
  46.   -changed includes to have absolute path from h/
  47. 01t,19nov91,rrr  shut up some ansi warnings.
  48. 01s,04oct91,rrr  passed through the ansification filter
  49.                   -changed functions to ansi style
  50.   -changed copyright notice
  51. 01r,05apr91,jdi  documentation -- removed header parens and x-ref numbers;
  52.  doc review by dnw.
  53. 01q,12feb91,jaa  documentation.
  54. 01p,02oct90,hjb  added a call to htons() where needed.
  55. 01o,07may90,hjb  added some documentation to ftpXfer routine to issue a "QUIT"
  56.  at the end of the file transfer session via FTP.
  57. 01n,22feb90,jdi  documentation cleanup.
  58. 01m,07jun89,gae  changed SOCKADDR back to "struct sockaddr".
  59. 01l,23sep88,gae  documentation touchup.
  60. 01k,30may88,dnw  changed to v4 names.
  61. 01j,28may88,dnw  changed fioStdErr call to STD_ERR.
  62. 01i,05apr88,gae  changed fprintf() call to fdprintf()
  63. 01h,17nov87,ecs  lint: added include of inetLib.h.
  64. 01g,11nov87,jlf  documentation
  65. 01f,06nov87,dnw  fixed bug in use of setsockopt().
  66. 01e,01nov87,llk  changed remInetAddr() to UNIX compatible inet_addr().
  67. 01d,01apr87,ecs  hushed lint in ftpGetReply.
  68.  changed "VARARGS 2" to "VARARGS2" in ftpCommand
  69.  removed extraneous 4th arg from calls to bind, socket, accept,
  70.     & connect.
  71. 01c,19mar87,dnw  documentation
  72.  prepended FTP_ to ftp reply codes.
  73. 01b,14feb87,dnw  changed to use getsockname() instead of using privileged port.
  74. 01a,07nov86,dnw  written
  75. */
  76. /*
  77. DESCRIPTION
  78. This library provides facilities for transferring files to and from a host
  79. via File Transfer Protocol (FTP).  This library implements only the
  80. "client" side of the FTP facilities.
  81. FTP IN VXWORKS
  82. For most purposes, you should access the services of ftpLib by means of 
  83. netDrv, a VxWorks I/O driver that supports transparent access to remote 
  84. files by means of standard I/O system calls.  Before attempting to access 
  85. ftpLib services directly, you should check whether netDrv already provides 
  86. the same access for less trouble.
  87. HIGH-LEVEL INTERFACE
  88. The routines ftpXfer() and ftpReplyGet() provide the highest level of
  89. direct interface to FTP.  The routine ftpXfer() connects to a specified
  90. remote FTP server, logs in under a specified user name, and initiates a
  91. specified data transfer command.  The routine ftpReplyGet() receives
  92. control reply messages sent by the remote FTP server in response to the
  93. commands sent.
  94. LOW-LEVEL INTERFACE
  95. The routines ftpHookup(), ftpLogin(), ftpDataConnInit(), ftpDataConnGet(),
  96. ftpCommand(), ftpCommandEnhanced()  provide the primitives necessary to 
  97. create and use control and data connections to remote FTP servers.  The 
  98. following example shows how to use these low-level routines.  It implements 
  99. roughly the same function as ftpXfer().
  100. .CS
  101. char *host, *user, *passwd, *acct, *dirname, *filename;
  102. int ctrlSock = ERROR; /@ This is the control socket file descriptor @/
  103. int dataSock = ERROR; /@ This is the data path socket file descriptor @/
  104. if (((ctrlSock = ftpHookup (host)) == ERROR)                       ||
  105.     (ftpLogin (ctrlSock, user, passwd, acct) == ERROR)                       ||
  106.     (ftpCommand (ctrlSock, "TYPE I", 0, 0, 0, 0, 0, 0) != FTP_COMPLETE)       ||
  107.     (ftpCommand (ctrlSock, "CWD %s", dirname, 0, 0, 0, 0, 0) != FTP_COMPLETE) ||
  108.     ((dataSock = ftpDataConnInit (ctrlSock)) == ERROR)                       ||
  109.     (ftpCommand (ctrlSock, "RETR %s", filename, 0, 0, 0, 0, 0) != FTP_PRELIM) ||
  110.     ((dataSock = ftpDataConnGet (dataSock)) == ERROR))
  111.     {
  112.     /@ an error occurred; close any open sockets and return @/
  113.     if (ctrlSock != ERROR)
  114. close (ctrlSock);
  115.     if (dataSock != ERROR)
  116. close (dataSock);
  117.     return (ERROR);
  118.     }
  119. .CE
  120. For even lower-level access,  please note that the sockets provided by 
  121. ftpHookup() and ftpDataConnInit() are standard TCP/IP sockets.  Developers may 
  122. implement read(), write() and select() calls using these sockets for maximum 
  123. flexibility.
  124. To use this feature, include the following component:
  125. INCLUDE_FTP
  126. TUNING FOR MULTIPLE FILE ACCESS: 
  127. Please note that accessing multiple files simultaneously may require 
  128. increasing the memory available to the network stack.  You can examine 
  129. memory requirements by using netStackSysPoolShow() and netStackDataPoolShow()
  130. before opening and after closing files.
  131. You may need to modify the following macro definitions according to your 
  132. specific memory requirements:
  133.  NUM_64
  134.  NUM_128
  135.  NUM_256
  136.  NUM_512
  137.  NUM_1024
  138.  NUM_2048
  139.  NUM_SYS_64
  140.  NUM_SYS_128
  141.  NUM_SYS_256
  142.  NUM_SYS_512
  143.  NUM_SYS_1024
  144.  NUM_SYS_2048
  145. Please also note that each concurrent file access requires three file 
  146. descriptors (File, Control and Socket).  The following macro definition may 
  147. need modification per your application:
  148. NUM_FILES
  149. Developers are encouraged to enable the error reporting facility during 
  150. debugging using the function ftpLibDebugOptionsSet().  The output is displayed 
  151. via the logging facility.
  152. INCLUDE FILES: ftpLib.h
  153. SEE ALSO: netDrv, logLib
  154. */
  155. #include "vxWorks.h"
  156. #include "ctype.h"
  157. #include "sys/socket.h"
  158. #include "netinet/in.h"
  159. #include "ftpLib.h"
  160. #include "inetLib.h"
  161. #include "sys/times.h"
  162. #include "hostLib.h"
  163. #include "sockLib.h"
  164. #include "stdio.h"
  165. #include "string.h"
  166. #include "unistd.h"
  167. #include "errnoLib.h"
  168. #include "iosLib.h"
  169. #include "remLib.h"
  170. #include "selectLib.h"
  171. #include "taskLib.h"
  172. #include "vwModNum.h"
  173. #include "private/ftpLibP.h"
  174. #include "private/funcBindP.h"
  175. typedef struct sockaddr_in SOCKADDR_IN;
  176. #define UCA(n)     (((int)(((char *)&dataAddr.sin_addr)[n])) & 0xff)
  177. #define UCP(n)     (((int)(((char *)&dataAddr.sin_port)[n])) & 0xff)
  178. #define FTPLDEBUG(string, debugLevel, param1, param2, param3, param4, param5, param6)
  179.    {
  180.    if ((_func_logMsg != NULL) && (ftplDebug & debugLevel))
  181.       (* _func_logMsg) (string, param1, param2, param3, param4, param5, param6);
  182.    }
  183. #define FTP_PORT 21
  184. #define PASV_REPLY_STRING_LENGTH 256
  185. /* XXX ftplPasvModeDisable - XXX this will be removed in the next release XXX */
  186. BOOL ftplPasvModeDisable = FALSE;
  187. LOCAL STATUS ftpPasvReplyParse (char *, UINT32 *, UINT32 *, UINT32 *,  
  188.                                 UINT32 *, UINT32 *, UINT32 *);
  189. LOCAL BOOL ftpTransientFatal (UINT32 reply);
  190. LOCAL char pasvReplyString[PASV_REPLY_STRING_LENGTH];
  191. UINT32  ftplTransientMaxRetryCount = 1;   /* Retry just once for now */
  192. UINT32  ftplTransientRetryInterval = 0;   /* Default with no delay */
  193. FUNCPTR _func_ftpTransientFatal    = ftpTransientFatal;
  194. /*******************************************************************************
  195. *
  196. * ftpCommand - send an FTP command and get the reply 
  197. *
  198. * This command has been superceded by ftpCommandEnhanced() 
  199. *
  200. * This routine sends the specified command on the specified socket, which
  201. * should be a control connection to a remote FTP server.
  202. * The command is specified as a string in printf() format with up
  203. * to six arguments.
  204. *
  205. * After the command is sent, ftpCommand() waits for the reply from the
  206. * remote server.  The FTP reply code is returned in the same way as in
  207. * ftpReplyGet().
  208. *
  209. * EXAMPLE
  210. * .CS
  211. * ftpCommand (ctrlSock, "TYPE I", 0, 0, 0, 0, 0, 0);     /@ image-type xfer @/
  212. * ftpCommand (ctrlSock, "STOR %s", file, 0, 0, 0, 0, 0); /@ init file write @/
  213. * .CE
  214. *
  215. * RETURNS:
  216. *
  217. *  1 = FTP_PRELIM (positive preliminary)
  218. *  2 = FTP_COMPLETE (positive completion)
  219. *  3 = FTP_CONTINUE (positive intermediate)
  220. *  4 = FTP_TRANSIENT (transient negative completion)
  221. *  5 = FTP_ERROR (permanent negative completion)
  222. *
  223. * ERROR if there is a read/write error or an unexpected EOF.
  224. *
  225. * SEE ALSO: ftpReplyGet()
  226. *
  227. * VARARGS2
  228. */
  229. int ftpCommand
  230.     (
  231.     int ctrlSock,       /* fd of control connection socket */
  232.     char *fmt,          /* format string of command to send */
  233.     int arg1,           /* first of six args to format string */
  234.     int arg2,
  235.     int arg3,
  236.     int arg4,
  237.     int arg5,
  238.     int arg6
  239.     )
  240.     {
  241.         /* return most significant digit of the reply */
  242.         return (ftpCommandEnhanced (ctrlSock, fmt, arg1, arg2, 
  243.                                     arg3, arg4, arg5, arg6, NULL, 0) / 100);
  244.     }
  245. /*******************************************************************************
  246. *
  247. * ftpCommandEnhanced - send an FTP command and get the complete RFC reply code
  248. *
  249. * This command supercedes ftpCommand() 
  250. *
  251. * This routine sends the specified command on the specified socket, which
  252. * should be a control connection to a remote FTP server.
  253. * The command is specified as a string in printf() format with up
  254. * to six arguments.
  255. *
  256. * After the command is sent, ftpCommand() waits for the reply from the
  257. * remote server.  The FTP reply code is returned in the same way as in
  258. * ftpReplyGetEnhanced().
  259. *
  260. * EXAMPLE
  261. * .CS
  262. * ftpCommandEnhanced (ctrlSock, "TYPE I", 0, 0, 0, 0, 0, 0, 0, 0);     /@ image-type xfer @/
  263. * ftpCommandEnhanced (ctrlSock, "STOR %s", file, 0, 0, 0, 0, 0, 0, 0); /@ init file write @/
  264. * ftpCommandEnhanced (ctrlSock, "PASV", file, 0, 0, 0, 0, 0, reply, rplyLen); /@ Get port @/
  265. * .CE
  266. *
  267. * RETURNS:
  268. *  The complete FTP response code (see RFC #959)
  269. *
  270. * ERROR if there is a read/write error or an unexpected EOF.
  271. *
  272. * SEE ALSO: ftpReplyGetEnhanced(), ftpReplyGet()
  273. *
  274. * VARARGS2
  275. */
  276. int ftpCommandEnhanced
  277.     (
  278.     int ctrlSock,       /* fd of control connection socket */
  279.     char *fmt,          /* format string of command to send */
  280.     int arg1,           /* first of six args to format string */
  281.     int arg2,
  282.     int arg3,
  283.     int arg4,
  284.     int arg5,
  285.     int arg6,
  286.     char *replyString, /* storage for the last line of the server response or NULL */
  287.     int replyStringLength  /* Maximum character length of the replyString */
  288.     )
  289.     {
  290.     char buffer [128];
  291.     int len;
  292.     if (ftplDebug & FTPL_DEBUG_OUTGOING)
  293.         {
  294.         printErr ("---> ");
  295.         printErr (fmt, arg1, arg2, arg3, arg4, arg5, arg6);
  296.         printErr ("n");
  297.         }
  298.     /* Format Command to send to FTP server */
  299.     /* The next line will generate a warning with gcc 2.96+, this is O.K. */
  300.     sprintf (buffer, fmt, arg1, arg2, arg3, arg4, arg5, arg6);
  301.     len = strlen(buffer);
  302.     /* Append CR LF to format copy to force a single write to TCP */
  303.     sprintf(&buffer[len],"%s","rn");
  304.     len = strlen(buffer);
  305.     if (write(ctrlSock, buffer, len) < len)
  306.         {
  307.         FTPLDEBUG ("ftpCommandEnhanced: error during write to control socket - errno:0x%08x",  
  308.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  309.         return(ERROR);
  310.         }
  311.     return (ftpReplyGetEnhanced (ctrlSock, 
  312.                                  !strcmp (fmt, "QUIT"), 
  313.                                  replyString, 
  314.                                  replyStringLength));
  315.     }
  316. /*******************************************************************************
  317. *
  318. * ftpXfer - initiate a transfer via FTP
  319. *
  320. * This routine initiates a transfer via a remote FTP server
  321. * in the following order:
  322. * .IP (1) 4
  323. * Establishes a connection to the FTP server on the specified host.
  324. * .IP (2)
  325. * Logs in with the specified user name, password, and account,
  326. * as necessary for the particular host.
  327. * .IP (3)
  328. * Sets the transfer type to image by sending the command "TYPE I".
  329. * .IP (4)
  330. * Changes to the specified directory by sending
  331. * the command "CWD <dirname>".
  332. * .IP (5)
  333. * Sends the specified transfer command
  334. * with the specified filename as an argument, and establishes a data connection.
  335. * Typical transfer commands are "STOR %s", to write to a remote file,
  336. * or "RETR %s", to read a remote file.
  337. * .LP
  338. * The resulting control and data connection file descriptors are returned
  339. * via <pCtrlSock> and <pDataSock>, respectively.
  340. *
  341. * After calling this routine, the data can be read or written to the remote
  342. * server by reading or writing on the file descriptor returned in
  343. * <pDataSock>.  When all incoming data has been read (as indicated by 
  344. * an EOF when reading the data socket) and/or all outgoing data has been
  345. * written, the data socket fd should be closed.  The routine ftpReplyGet()
  346. * should then be called to receive the final reply on the control socket,
  347. * after which the control socket should be closed.
  348. *
  349. * If the FTP command does not involve data transfer, <pDataSock> should be 
  350. * NULL, in which case no data connection will be established. The only 
  351. * FTP commands supported for this case are DELE, RMD, and MKD.
  352. *
  353. * EXAMPLE
  354. * The following code fragment reads the file "/usr/fred/myfile" from the
  355. * host "server", logged in as user "fred", with password "magic"
  356. * and no account name.
  357. *
  358. * .CS
  359. *     #include "vxWorks.h"
  360. *     #include "ftpLib.h"
  361. *
  362. *     int ctrlSock;
  363. *     int dataSock;
  364. *     char buf [512];
  365. *     int nBytes;
  366. *     STATUS status;
  367. *
  368. *     if (ftpXfer ("server", "fred", "magic", "",
  369. *                  "RETR %s", "/usr/fred", "myfile",
  370. *                  &ctrlSock, &dataSock) == ERROR)
  371. *         return (ERROR);
  372. *
  373. *     while ((nBytes = read (dataSock, buf, sizeof (buf))) > 0)
  374. *         {
  375. *         ...
  376. *         }
  377. *
  378. *     close (dataSock);
  379. *
  380. *     if (nBytes < 0)             /@ read error? @/
  381. *         status = ERROR;
  382. *
  383. *     if (ftpReplyGet (ctrlSock, TRUE) != FTP_COMPLETE)
  384. *         status = ERROR;
  385. *
  386. *     if (ftpCommand (ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) != FTP_COMPLETE)
  387. *         status = ERROR;
  388. *
  389. *     close (ctrlSock);
  390. * .CE
  391. *
  392. * RETURNS:
  393. * OK, or ERROR if any socket cannot be created or if a connection cannot be
  394. * made.
  395. *
  396. * SEE ALSO: ftpReplyGet()
  397. */
  398. STATUS ftpXfer
  399.     (
  400.     char *host,         /* name of server host */
  401.     char *user,         /* user name for host login */
  402.     char *passwd,       /* password for host login */
  403.     char *acct,         /* account for host login */
  404.     char *cmd,          /* command to send to host */
  405.     char *dirname,      /* directory to 'cd' to before sending command */
  406.     char *filename,     /* filename to send with command */
  407.     int *pCtrlSock,     /* where to return control socket fd */
  408.     int *pDataSock      /* where to return data socket fd, */
  409.                         /* (NULL == don't open data connection) */
  410.     )
  411.     {
  412.     FAST int ctrlSock = ERROR;
  413.     FAST int dataSock = ERROR;
  414.     UINT32 ftpReply   = 0;
  415.     UINT32 retryCount = 0;
  416.     int    cmdResult;      
  417.     struct fd_set readFds; /* Used by select for PORT method */
  418.     int    width;          /* Used by select for PORT method */
  419.     BOOL   dataSockPassive = TRUE;
  420.     FTPLDEBUG ("ftpXfer: host:%s user:%s passwd:%s acct:%s cmd:%s dir:%sn",  
  421.                FTPL_DEBUG_ERRORS,host,user,passwd,acct,cmd,dirname);
  422.     if (((ctrlSock = ftpHookup (host)) == ERROR)    ||
  423.         (ftpLogin (ctrlSock, user, passwd, acct) != OK)    ||
  424.         (ftpCommand (ctrlSock, "TYPE I",0,0,0,0,0,0) != FTP_COMPLETE)  ||
  425.         ((dirname[0] != EOS) && (ftpCommand (ctrlSock, "CWD %s", 
  426.         (int)dirname,0,0,0,0,0) != FTP_COMPLETE)))
  427.         {
  428.         /* Detected an error during command establishment */
  429.         FTPLDEBUG ("ftpXfer: Detected an error during command establishment errno:0x%08x",  
  430.                     FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  431.         close (ctrlSock);
  432.         return (ERROR);
  433.         }
  434.     /*
  435.      * This is a special when an FTP command does not need to establish a
  436.      * data connection.
  437.      */
  438.     if (pDataSock == NULL)
  439.         {
  440.         if (ftpCommand (ctrlSock, cmd, (int)filename, 0, 0, 0, 0, 0) 
  441.                         != FTP_COMPLETE)
  442.             {
  443.             /* FTP command error. */
  444.             FTPLDEBUG ("ftpXfer: error during command.  errno:0x%08x",  
  445.             FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  446.             close (ctrlSock);
  447.             return (ERROR);
  448.             }
  449.         }
  450.        
  451.     /* 
  452.      * At this point we are trying to establish the data socket.
  453.      * We will first try using the modern, client-initiated PASV command.   
  454.      * If PASV fails,  then we will fall back to the PORT command. 
  455.      */
  456.     /*  Set up local data port and send the PORT command */
  457.     do 
  458.         {
  459.         if ((dataSock = ftpDataConnInitPassiveMode (ctrlSock)) != ERROR)
  460.             {
  461.             FTPLDEBUG ("ftpXfer: notice - mode succeeded.n ", 
  462.                 FTPL_DEBUG_ERRORS, 0, 1, 2, 3, 4, 5);
  463.             dataSockPassive = TRUE; /* We do not need to listen() on the socket */
  464.             }
  465.         else
  466.             {
  467.             FTPLDEBUG ("ftpXfer: notice - PASV mode failed. Now trying older PORT connect method", 
  468.                 FTPL_DEBUG_ERRORS, 0, 1, 2, 3, 4, 5);
  469.             if ((dataSock = ftpDataConnInit (ctrlSock)) == ERROR)
  470.                 {
  471.                 FTPLDEBUG ("ftpXfer:  ftpDataConnInit - error during trying of another port. errno:0x%08x",  
  472.                     FTPL_DEBUG_ERRORS, errno, 1, 2, 3, 4, 5);
  473.                 close (ctrlSock);
  474.                 return (ERROR);
  475.                 } 
  476.             else
  477.                 dataSockPassive = FALSE; /* We will need to listen() on the socket */
  478.             }
  479.         /* Send the FTP command.  */
  480.         cmdResult = ftpCommandEnhanced (ctrlSock, 
  481.                                         cmd, 
  482.                                         (int)filename,
  483.                                         0, 0, 0, 0, 0, NULL, 0);
  484.         if ((cmdResult/100) != FTP_PRELIM)
  485.             {
  486.             /* 
  487.              * The command has failed.  Close the data socket and decode the error.
  488.              */
  489.             close (dataSock);
  490.             /* Check if something really bad happened: File not found, etc. */
  491.             if ((cmdResult/100) == FTP_ERROR)
  492.                 {
  493.                 FTPLDEBUG ("ftpXfer:  response 0x%08x - aborting transfer.n", 
  494.                     FTPL_DEBUG_ERRORS, cmdResult, 1, 2, 3, 4, 5);
  495.                 close (ctrlSock);
  496.                 return (ERROR);
  497.                 }
  498.             if ((cmdResult/100) == FTP_TRANSIENT && _func_ftpTransientFatal != NULL)
  499.                 {
  500.                 FTPLDEBUG ("ftpXfer:  calling user-supplied applette to see if 0x%08x FTP_TRANSIENT is fatal for this command.n", 
  501.                     FTPL_DEBUG_ERRORS, cmdResult, 1, 2, 3, 4, 5);
  502.                 if ((* _func_ftpTransientFatal) (cmdResult) == TRUE)
  503.                     {
  504.                     FTPLDEBUG ("ftpXfer:  user-supplied applette says 0x%08x FTP_TRANSIENT  ** IS ** fatal for this command.n", 
  505.                     FTPL_DEBUG_ERRORS, cmdResult, 1, 2, 3, 4, 5);
  506.                     close (ctrlSock);
  507.                     errno = S_ftpLib_FATAL_TRANSIENT_RESPONSE;
  508.                     return (ERROR);
  509.                     }
  510.                 FTPLDEBUG ("ftpXfer:  user-supplied applette says 0x%08x is  ** NOT ** fatal for this command.n", 
  511.                     FTPL_DEBUG_ERRORS, cmdResult, 1, 2, 3, 4, 5);
  512.                 }
  513.             if ((ftpReply = (cmdResult/100)) == FTP_TRANSIENT)
  514.                 {
  515.                 /*
  516.                  * If the error was due to transient error (e.x. the data port
  517.                  * was not available) retry the command 
  518.                  * ftplTransientMaxRetryCount times.  
  519.                  */
  520.                 if (retryCount < ftplTransientMaxRetryCount)
  521.                     {
  522.                     ++retryCount;
  523.                     FTPLDEBUG ("ftpXfer: warning - reply was %d(decimal) - FTP_PRELIM - #%d attempt in %d ticks.n", 
  524.                                 FTPL_DEBUG_ERRORS,cmdResult,retryCount,
  525.                                 ftplTransientRetryInterval, 5, 6, 7);
  526.                     if (ftplTransientRetryInterval)
  527.                         taskDelay (ftplTransientRetryInterval);
  528.                     continue; /* try another port */
  529.                 }
  530.                 else
  531.                 {
  532.                 /* Too many retries,  close socket and return failure */
  533.                 FTPLDEBUG ("ftpXfer: error - reply was %d(decimal) - FTP_PRELIM - attempt limit (%d) exceeded.n", 
  534.                            FTPL_DEBUG_ERRORS,
  535.                            cmdResult,
  536.                            ftplTransientMaxRetryCount,
  537.                            5, 6, 7, 8);
  538.                 close (ctrlSock);
  539.                 errno = S_ftpLib_TRANSIENT_RETRY_LIMIT_EXCEEDED;
  540.                 return (ERROR);
  541.                 }
  542.                 /* Exit for any other error */
  543.                 FTPLDEBUG ("ftpXfer: error - ftpCommand != FTP_PRELIM.  errno:0x%08xn", 
  544.                            FTPL_DEBUG_ERRORS, errno, 1, 2, 3, 4, 5);
  545.                 close (ctrlSock);
  546.                 return (ERROR);
  547.                 }
  548.             }
  549.         if ( dataSockPassive == FALSE)
  550.             {
  551.             /* At this point do a select on the data & control socket */
  552.             FTPLDEBUG ("ftpXfer: notice - cmdResult:%d dataSock:%d ctrlSock:%d  errno:0x%08xn", 
  553.                 FTPL_DEBUG_ERRORS, cmdResult, dataSock, ctrlSock, errno, 4, 5);
  554.             FD_ZERO (&readFds);
  555.             FD_SET  (ctrlSock, &readFds);
  556.             FD_SET  (dataSock, &readFds);
  557.             width = (dataSock > ctrlSock) ? dataSock : ctrlSock;
  558.             width++;
  559.             if (select (width, &readFds, NULL, NULL, NULL) == ERROR)
  560.                 {
  561.                 FTPLDEBUG ("ftpXfer: error - select()==ERROR. errno:0x%08xn", 
  562.                            FTPL_DEBUG_ERRORS,errno, 1, 2, 3, 4, 5);
  563.                 close (dataSock);
  564.                 close (ctrlSock);
  565.                 return (ERROR);
  566.                 }
  567.             /* If the control socket is ready process it and take a decision,
  568.              * try again or return error. If the data socket is ready call
  569.              * ftpDataConnGet next.
  570.              */
  571.             if (FD_ISSET (ctrlSock, &readFds) && ! FD_ISSET (dataSock, &readFds))
  572.                 {
  573.                 close (dataSock);
  574.                 FTPLDEBUG ("ftpXfer: warning - control socket ready but data socket not readyn", 
  575.                            FTPL_DEBUG_ERRORS,0, 1, 2, 3, 4, 5);
  576.                 if ((ftpReply = ftpReplyGet (ctrlSock, FALSE)) == FTP_TRANSIENT)
  577.                     continue; /* Try another port */
  578.                 /* Regardless of response close sockets */
  579.                 FTPLDEBUG ("ftpXfer:  error - sending QUIT command to host. errno:0x%08xn", 
  580.                             FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  581.                 (void) ftpCommand (ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0);
  582.                 close (ctrlSock);
  583.                 return (ERROR);
  584.                 }
  585.             } /* PORT method requires checking for data socket connection */
  586.         } while ( ftpReply == FTP_TRANSIENT); /* Try again, we might need a different port */
  587.     /* If we used PASV mode,  then the socket is ready for use */
  588.     if (!dataSockPassive)
  589.         {
  590.         /* 
  591.          * We used the PORT method to establish a connection.
  592.          * The data socket connection is configured. Wait for the FTP server to connect
  593.          * to us.
  594.          */
  595.         if ((dataSock = ftpDataConnGet (dataSock)) == ERROR)
  596.             {
  597.             FTPLDEBUG ("ftpXfer: error - ftpDataConnGet()==ERROR. errno:0x%08xn", 
  598.                 FTPL_DEBUG_ERRORS,errno, 1, 2, 3, 4, 5);
  599.             close (ctrlSock);
  600.             return (ERROR);
  601.             }
  602.         }
  603.     /* Store the control and data sockets */
  604.     if (pCtrlSock != NULL)
  605.         *pCtrlSock = ctrlSock;
  606.     if (pDataSock != NULL)
  607.         *pDataSock = dataSock;
  608.     return (OK);
  609.     }
  610. /*******************************************************************************
  611. *
  612. * ftpReplyGet - get an FTP command reply
  613. *
  614. * This routine has been superceded by ftpReplyGetEnhanced()
  615. *
  616. * This routine gets a command reply on the specified control socket.
  617. *
  618. * The three-digit reply code from the first line is saved and interpreted.
  619. * The left-most digit of the reply code identifies the type of code
  620. * (see RETURNS below).
  621. *
  622. * The caller's error status is always set to the complete three-digit reply code
  623. * regardless of the actual reply value (see the manual entry for errnoGet()).
  624. * If the reply code indicates an error, the entire reply
  625. * is printed if the ftp error printing is enabled (see the manual
  626. * entry for ftpLibDebugOptionsSet()).
  627. *
  628. * If an EOF is encountered on the specified control socket, but no EOF was
  629. * expected (<expecteof> == FALSE), then ERROR is returned.
  630. *
  631. * RETURNS:
  632. *  1 = FTP_PRELIM (positive preliminary)
  633. *  2 = FTP_COMPLETE (positive completion)
  634. *  3 = FTP_CONTINUE (positive intermediate)
  635. *  4 = FTP_TRANSIENT (transient negative completion)
  636. *  5 = FTP_ERROR (permanent negative completion)
  637. *
  638. * ERROR if there is a read/write error or an unexpected EOF.
  639. */
  640. int ftpReplyGet
  641.     (
  642.     int ctrlSock,       /* control socket fd of FTP connection */
  643.     BOOL expecteof      /* TRUE = EOF expected, FALSE = EOF is error */
  644.     )
  645.     {
  646.     /* return most significant digit of reply */
  647.     return (ftpReplyGetEnhanced (ctrlSock, expecteof, NULL, 0) / 100);
  648.     }
  649. /*******************************************************************************
  650. *
  651. * ftpReplyGetEnhanced - get an FTP command reply
  652. *
  653. * This routine supercedes ftpReplyGet()
  654. *
  655. * This routine gets a command reply on the specified control socket.
  656. *
  657. * The three-digit reply code from the first line is saved and interpreted.
  658. * The left-most digit of the reply code identifies the type of code
  659. * (see RETURNS below).
  660. *
  661. * The caller's error status is always set to the complete three-digit reply code
  662. * (see the manual entry for errnoGet()).
  663. * If the reply code indicates an error, the entire reply
  664. * is printed if the ftp error printing is enabled (see the manual
  665. * entry for ftpLibDebugOptionsSet()).
  666. *
  667. * The last line of text retrieved from the servers response is stored
  668. *     in the location specified by replyString.   If replyString is NULL
  669. *     the parameter is ignored.
  670. *
  671. * If an EOF is encountered on the specified control socket, but no EOF was
  672. * expected (<expecteof> == FALSE), then ERROR is returned.
  673. *
  674. * RETURNS:
  675. *  The complete FTP response code (see RFC #959)
  676. *
  677. * ERROR if there is a read/write error or an unexpected EOF.
  678. */
  679. int ftpReplyGetEnhanced
  680.     (
  681.     int ctrlSock,       /* control socket fd of FTP connection */
  682.     BOOL expecteof,     /* TRUE = EOF expected, FALSE = EOF is error */
  683.     char *replyString,  /* Location to store text of reply, or NULL */
  684.     int  stringLengthMax /* Maximum length of reply (not including NULL) */ 
  685.     )
  686.     {
  687.     char c;
  688.     FAST int codeType;
  689.     FAST int code;
  690.     FAST int dig;
  691.     int continuation;
  692.     int origCode;
  693.     int stringIndex;
  694.     BOOL eof;
  695.     /* read all lines of a reply:
  696.      *    do
  697.      *       while not eof and not eol
  698.      *           process char
  699.      *    while not eof and not last line of reply
  700.      */
  701.     origCode = 0;
  702.     codeType = 0;
  703.     do
  704.         {
  705.         /* read all characters of a line */
  706.         dig  = 0;
  707.         code = 0;
  708.         stringIndex = 0;
  709.         continuation = FALSE;
  710.         while (!(eof = (read (ctrlSock, &c, 1) == 0)) && (c != 'n'))
  711.             {
  712.             /* Store the reply */
  713.             if (replyString != NULL)
  714.                 {
  715.                 if (stringIndex < stringLengthMax)
  716.                     {
  717.                     replyString[stringIndex] = c;
  718.                     stringIndex++;
  719.                     }
  720.                 }
  721.             dig++;
  722.             
  723.             if (dig == 1)    /* char 1 is code type */
  724.                 codeType = c - '0';
  725.             if (dig <= 3)    /* chars 1-3 are code */
  726.                 {
  727.                 if (!isdigit ((int)c))
  728.                     code = -1;
  729.                 else
  730.                 if (code != -1)
  731.                     code = code * 10 + (c - '0');
  732.                 }
  733.             if (dig == 4)    /* char 4 is continuation marker */
  734.                 continuation = (c == '-');
  735.             if ((c != 'r') &&
  736.                 (((ftplDebug & FTPL_DEBUG_INCOMING) && (dig > 4))  ||
  737.                 (ftplDebug  && (codeType == FTP_ERROR)))
  738.                )
  739.                 {
  740.                 write (STD_ERR, &c, 1);
  741.                 }
  742.             }
  743.         /* terminate the reply string */
  744.         if (replyString != NULL)
  745.             replyString[stringIndex] = c;
  746.         /* print newline if we've been printing this reply */
  747.         if ((ftplDebug & FTPL_DEBUG_INCOMING)  || 
  748.             ((codeType == FTP_ERROR) && ftplDebug))
  749.             printErr ("n");
  750.         /* save the original reply code */
  751.         if (origCode == 0)
  752.             origCode = code;
  753.         }
  754.     /* while not eof and not last line of reply */
  755.     while (!eof && !((dig >= 3) && (code == origCode) && !continuation));
  756.     /* set status to entire reply code */
  757.     errno = (M_ftpLib | origCode);
  758.     /* return error if unexpected eof encountered */
  759.     if (eof & !expecteof)
  760.         {
  761.         FTPLDEBUG ("ftpReplyGet:  error - experienced eof - errno:0x%08xn", 
  762.             FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  763.         return (ERROR);
  764.         }
  765.     else
  766.         return (origCode);    /* Return the complete code */
  767.     }
  768. /*******************************************************************************
  769. *
  770. * ftpHookup - get a control connection to the FTP server on a specified host
  771. *
  772. * This routine establishes a control connection to the FTP server on the
  773. * specified host.  This is the first step in interacting with a remote FTP
  774. * server at the lowest level.  (For a higher-level interaction with a remote
  775. * FTP server, see the manual entry for ftpXfer().)
  776. *
  777. * RETURNS:
  778. * The file descriptor of the control socket, or ERROR if the Internet
  779. * address or the host name is invalid, if a socket could not be created, or
  780. * if a connection could not be made.
  781. *
  782. * SEE ALSO: ftpLogin(), ftpXfer()
  783. */
  784. int ftpHookup
  785.     (
  786.     char *host          /* server host name or inet address */
  787.     )
  788.     {
  789.     FAST int ctrlSock;
  790.     FAST int inetAddr;
  791.     SOCKADDR_IN ctrlAddr;
  792.     if (((inetAddr = (int) inet_addr (host)) == ERROR) &&
  793.         ((inetAddr = hostGetByName (host)) == ERROR))
  794.         {
  795.         return (ERROR);
  796.         }
  797.     /* make our control socket */
  798.     ctrlSock = socket (AF_INET, SOCK_STREAM, 0);
  799.     if (ctrlSock < 0)
  800.         {
  801.         FTPLDEBUG ("ftpHookup: error - failure to get socket. errno:0x%08xn", 
  802.             FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  803.         return (ERROR);
  804.         }
  805.     /* bind a name with no inet address and let system pick port;
  806.      * this is just so we can find our socket address later */
  807.     ctrlAddr.sin_family      = AF_INET;
  808.     ctrlAddr.sin_addr.s_addr = INADDR_ANY;
  809.     ctrlAddr.sin_port        = htons (0);
  810.     if (bind (ctrlSock, (struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr)) < 0)
  811.         {
  812.         FTPLDEBUG ("ftpHookup: error - failure to bind socket. errno:0x%08xn", 
  813.             FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  814.         close (ctrlSock);
  815.         return (ERROR);
  816.         }
  817.     /* connect to other side */
  818.     ctrlAddr.sin_addr.s_addr = inetAddr;
  819.     ctrlAddr.sin_port        = htons (FTP_PORT);
  820.     if (connect (ctrlSock, (struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr)) < 0)
  821.         { 
  822.         FTPLDEBUG ("ftpHookup: error - failure to connect socket. errno:0x%08xn", 
  823.             FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  824.         close (ctrlSock);
  825.         return (ERROR);
  826.         }
  827.     ftpReplyGet (ctrlSock, FALSE);    /* read startup message from server */
  828.     return (ctrlSock);
  829.     }
  830. /*******************************************************************************
  831. *
  832. * ftpLogin - log in to a remote FTP server
  833. *
  834. * This routine logs in to a remote server with the specified user name,
  835. * password, and account name, as required by the specific remote host.  This
  836. * is typically the next step after calling ftpHookup() in interacting with a
  837. * remote FTP server at the lowest level.  (For a higher-level interaction
  838. * with a remote FTP server, see the manual entry for ftpXfer()).
  839. *
  840. * RETURNS:
  841. * OK, or ERROR if the routine is unable to log in.
  842. *
  843. * SEE ALSO: ftpHookup(), ftpXfer()
  844. */
  845. STATUS ftpLogin
  846.     (
  847.     FAST int ctrlSock,  /* fd of login control socket */
  848.     char *user,         /* user name for host login */
  849.     char *passwd,       /* password for host login */
  850.     char *account       /* account for host login */
  851.     )
  852.     {
  853.     FAST int n;
  854.     n = ftpCommand (ctrlSock, "USER %s", (int)user, 0, 0, 0, 0, 0);
  855.     if (n == FTP_CONTINUE)
  856.         n = ftpCommand (ctrlSock, "PASS %s", (int)passwd, 0, 0, 0, 0, 0);
  857.     if (n == FTP_CONTINUE)
  858.         n = ftpCommand (ctrlSock, "ACCT %s", (int)account, 0, 0, 0, 0, 0);
  859.     if (n != FTP_COMPLETE)
  860.         {
  861.         FTPLDEBUG ("ftpLogin: error - failure to get complete login. errno:0x%08xn",
  862.                     FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  863.         return (ERROR);
  864.         }
  865.     return (OK);
  866.     }
  867. /*******************************************************************************
  868. *
  869. * ftpDataConnInitPassiveMode - initialize an FTP data connection using PASV mode
  870. *
  871. * This routine sets up the client side of a data connection for the
  872. * specified control connection.  It issues a PASV command and attempts to connect
  873. * to the host-specified port.  If the host responds that it can not process the
  874. * PASV command (command not supported) or fails to recognize the command, it will 
  875. * return ERROR.
  876. *
  877. * This routine must be called f2beforefP the data-transfer command is sent;
  878. * otherwise, the server's connect may fail.
  879. *
  880. * This routine is called after ftpHookup() and ftpLogin() to establish a
  881. * connection with a remote FTP server a low level.  (For a
  882. * higher-level interaction with a remote FTP server, see ftpXfer().)
  883. *
  884. * This function is preferred over ftpDataConnInit() because 
  885. * the remote system must preserve old port connection pairs even if the target 
  886. * system suffers from a reboot (2MSL). Using PASV we encourage the host's 
  887. * selection of a fresh port.
  888. *
  889. * RETURNS: The file descriptor of the data socket created, or ERROR.
  890. *
  891. * SEE ALSO: ftpHookup(), ftpLogin(), ftpCommandEnhanced(), ftpXfer(), ftpConnInit()
  892. *
  893. */
  894. int ftpDataConnInitPassiveMode
  895.     (
  896.     int ctrlSock        /* fd of associated control socket */
  897.     )
  898.     {
  899.     FAST int dataSock;
  900.     int result;
  901.     int len;
  902.     int portMsb;
  903.     int portLsb;
  904.     int hostDataPort;
  905.     SOCKADDR_IN ctrlAddr;
  906.     SOCKADDR_IN dataAddr;
  907.     /* If configured to disable PASV mode, then just return ERROR */
  908.     if (ftplPasvModeDisable)
  909.         return (ERROR);
  910.     /* find out our inet address */
  911.     len = sizeof (ctrlAddr);
  912.     if (getsockname (ctrlSock, (struct sockaddr *)&ctrlAddr, &len) < 0)
  913.         {
  914.         FTPLDEBUG ("ftpDataConnInitPassiveMode:  getsockname() failure. errno:0x%08x", 
  915.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  916.         return (ERROR);
  917.         }
  918.     result = ftpCommandEnhanced (ctrlSock, "PASV", 
  919.                                  0,0,0,0,0,0, /* XXX - These arguments not needed */
  920.                                  pasvReplyString,
  921.                                  PASV_REPLY_STRING_LENGTH-1);
  922.     
  923.     if (result == FTP_PASSIVE_REPLY)  /* The remote FTP server supports PASSIVE mode */
  924.         {
  925.         /* Parse the last line of the reply */
  926.         ftpPasvReplyParse (pasvReplyString, 0, 0, 0, 0, &portMsb, &portLsb);
  927.         /* Convert port number */
  928.         hostDataPort = portMsb * 256 + portLsb;
  929.         /* make our data socket */
  930.         dataSock = socket (AF_INET, SOCK_STREAM, 0);
  931.         if (dataSock < 0)
  932.             {
  933.             FTPLDEBUG ("ftpDataConnInitPassiveMode: error - failure to get socket. errno:0x%08xn", 
  934.                 FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  935.             return (ERROR);
  936.             }
  937.         bzero ((char *) &dataAddr, sizeof (SOCKADDR));
  938.         /* Use the port given to us in the reply of our PASV command */
  939.         dataAddr.sin_port        = htons (hostDataPort); 
  940.         dataAddr.sin_family      = AF_INET;
  941.         len = sizeof (SOCKADDR_IN);
  942.         if (getpeername (ctrlSock, (struct sockaddr *)&ctrlAddr, &len) < 0)
  943.             {
  944.             FTPLDEBUG ("ftpDataConnInitPassiveMode:  getpeername() failure. errno:0x%08x", 
  945.                        FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  946.             close (dataSock);
  947.             return (ERROR);
  948.             }
  949.         dataAddr.sin_addr.s_addr = ctrlAddr.sin_addr.s_addr; 
  950.         /* connect to the host */
  951.         if (connect (dataSock, (struct sockaddr *)&dataAddr, sizeof (dataAddr)) < 0)
  952.             { 
  953.             FTPLDEBUG ("ftpDataConnInitPassiveMode: failure to connect. sock:%d sockMsb:%d sockLsb:%d errno:0x%08xn", 
  954.                 FTPL_DEBUG_ERRORS, hostDataPort, portMsb, portLsb, errno, 5, 6);
  955.             close (dataSock);
  956.             return (ERROR);
  957.             }
  958.         else 
  959.             {
  960.             FTPLDEBUG ("ftpDataConnInitPassiveMode: passive ftp connect established to host:%#x port:%d sock:%dn", 
  961.                 FTPL_DEBUG_ERRORS, dataAddr.sin_addr.s_addr, hostDataPort, dataSock,4,5,6);
  962.             return (dataSock);
  963.             }
  964.         }
  965.     else /* We have failed PASV mode */
  966.         {
  967.         FTPLDEBUG ("ftpDataConnInitPassiveMode: host failed to respond correctly to PASV command. errno:0x%08xn", 
  968.             FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  969.         return (ERROR);
  970.         }
  971.     }
  972. /*******************************************************************************
  973. *
  974. * ftpDataConnInit - initialize an FTP data connection using PORT mode
  975. *
  976. * This routine sets up the client side of a data connection for the
  977. * specified control connection using the PORT command.  
  978. * It creates the data port, informs the
  979. * remote FTP server of the data port address, and listens
  980. * on that data port.  The server will then connect to this data port
  981. * in response to a subsequent data-transfer command sent on the
  982. * control connection (see the manual entry for ftpCommand()).
  983. *
  984. * This routine must be called f2beforefP the data-transfer command is sent;
  985. * otherwise, the server's connect may fail.
  986. *
  987. * This routine is called after ftpHookup() and ftpLogin() to establish a
  988. * connection with a remote FTP server at the lowest level.  (For a
  989. * higher-level interaction with a remote FTP server, see ftpXfer().)
  990. *
  991. * Please note that ftpDataConnInitPassiveMode() is recommended instead
  992. * of ftpDataConnInit().
  993. *
  994. * RETURNS: The file descriptor of the data socket created, or ERROR.
  995. *
  996. * SEE ALSO: ftpDataConnInitPassiveMode(), ftpHookup(), ftpLogin(), 
  997. *           ftpCommand(), ftpXfer()
  998. */
  999. int ftpDataConnInit
  1000.     (
  1001.     int ctrlSock        /* fd of associated control socket */
  1002.     )
  1003.     {
  1004.     FAST int dataSock;
  1005.     int result;
  1006.     int len;
  1007.     int optval;
  1008.     SOCKADDR_IN ctrlAddr;
  1009.     SOCKADDR_IN dataAddr;
  1010.     /* find out our inet address */
  1011.     len = sizeof (ctrlAddr);
  1012.     if (getsockname (ctrlSock, (struct sockaddr *)&ctrlAddr, &len) < 0)
  1013.         {
  1014.         FTPLDEBUG ("ftpDataConnInit:  getsockname() failure. errno:0x%08x", 
  1015.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1016.         return (ERROR);
  1017.         }
  1018.     /* first try - try to send port */
  1019.     dataSock = socket (AF_INET, SOCK_STREAM, 0);
  1020.     if (dataSock < 0)
  1021.         {
  1022.         FTPLDEBUG ("ftpDataConnInit:  socket() failure. errno:0x%08x", 
  1023.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1024.         return (ERROR);
  1025.         }
  1026.     dataAddr = ctrlAddr;    /* set our inet address */
  1027.     dataAddr.sin_port = htons (0);    /* let system pick port num */
  1028.     if (bind (dataSock, (struct sockaddr *)&dataAddr, sizeof (dataAddr)) < 0)
  1029.         {
  1030.         FTPLDEBUG ("ftpDataConnInit:  bind() failure. errno:0x%08x", 
  1031.                     FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1032.         close (dataSock);
  1033.         return (ERROR);
  1034.         }
  1035.     if (listen (dataSock, 1) < 0)
  1036.         {
  1037.         FTPLDEBUG ("ftpDataConnInit:  listen() failure. errno:0x%08x", 
  1038.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1039.         close (dataSock);
  1040.         return (ERROR);
  1041.         }
  1042.     /* try to send socket address to other side */
  1043.     len = sizeof (dataAddr);
  1044.     if (getsockname (dataSock, (struct sockaddr *)&dataAddr, &len) < 0)
  1045.         {
  1046.         FTPLDEBUG ("ftpDataConnInit:  getsockname() failure. errno:0x%08x", 
  1047.                    FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1048.         close (dataSock);
  1049.         return (ERROR);
  1050.         }
  1051. #define UCA(n)     (((int)(((char *)&dataAddr.sin_addr)[n])) & 0xff)
  1052. #define UCP(n)     (((int)(((char *)&dataAddr.sin_port)[n])) & 0xff)
  1053.     result = ftpCommand (ctrlSock, "PORT %d,%d,%d,%d,%d,%d",
  1054.                          UCA(0), UCA(1), UCA(2), UCA(3), UCP(0), UCP(1));
  1055.     if (result != FTP_ERROR)
  1056.         {
  1057.         if (result == FTP_PRELIM)
  1058.             {
  1059.             FTPLDEBUG ("ftpDataConnInit:warning got FTP_PRELIM. ", 
  1060.                        FTPL_DEBUG_ERRORS,0,1,2,3,4,5);
  1061.             }
  1062.         if (result != FTP_COMPLETE && result != FTP_PRELIM)
  1063.             {
  1064.             FTPLDEBUG ("ftpDataConnInit: error - reply was %d(decimal) - not FTP_COMPLETE or FTP_PRELIM.n",
  1065.                        FTPL_DEBUG_ERRORS,result,1,2,3,4,5);
  1066.             close (dataSock);
  1067.             return (ERROR);
  1068.             }
  1069.         else
  1070.             {
  1071.             return (dataSock);
  1072.             }
  1073.         }
  1074.     /* second try - try to get port # correct by default */
  1075.     close (dataSock);
  1076.     dataSock = socket (AF_INET, SOCK_STREAM, 0);
  1077.     if (dataSock < 0)
  1078.         {
  1079.         FTPLDEBUG ("ftpDataConnInit: error - 2nd try to get port number failed on socket(). errno:0x%08xn",
  1080.                     FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1081.         return (ERROR);
  1082.         }
  1083.     optval = 1;
  1084.     if ((setsockopt (dataSock, SOL_SOCKET, SO_REUSEADDR,
  1085.             (caddr_t) &optval, sizeof (optval)) < 0) ||
  1086.             (bind (dataSock, (struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr)) < 0))
  1087.         {
  1088.         FTPLDEBUG ("ftpDataConnInit: error -  bind failed on 2nd attempt  errno :0x%08xn", 
  1089.                     FTPL_DEBUG_ERRORS, errno,1,2,3,4,5);
  1090.         close (dataSock);
  1091.         return (ERROR);
  1092.         }
  1093.     if (listen (dataSock, 1) < 0)
  1094.         {
  1095.         FTPLDEBUG ("ftpDataConnInit: error -  listen failed on 2nd attempt  errno :0x%08xn", 
  1096.                     FTPL_DEBUG_ERRORS,errno,1,2,3,4,5);
  1097.         close (dataSock);
  1098.         return (ERROR);
  1099.         }
  1100.     return (dataSock);
  1101.     }
  1102. /*******************************************************************************
  1103. *
  1104. * ftpDataConnGet - get a completed FTP data connection
  1105. *
  1106. * This routine completes a data connection initiated by a call to
  1107. * ftpDataConnInit().  It waits for a connection on the specified socket from
  1108. * the remote FTP server.  The specified socket should be the one returned by
  1109. * ftpDataConnInit().  The connection is established on a new socket, whose
  1110. * file descriptor is returned as the result of this function.  The original 
  1111. * socket, specified in the argument to this routine, is closed.
  1112. *
  1113. * Usually this routine is called after ftpDataConnInit() and ftpCommand() to
  1114. * initiate a data transfer from/to the remote FTP server.
  1115. *
  1116. * RETURNS:
  1117. * The file descriptor of the new data socket, or ERROR if the connection
  1118. * failed.
  1119. *
  1120. * SEE ALSO: ftpDataConnInit(), ftpCommand()
  1121. */
  1122. int ftpDataConnGet
  1123.     (
  1124.     int dataSock        /* fd of data socket on which to await connection */
  1125.     )
  1126.     {
  1127.     int newDataSock;
  1128.     SOCKADDR_IN from;
  1129.     int fromlen = sizeof (from);
  1130.     newDataSock = accept (dataSock, (struct sockaddr *) &from, &fromlen);
  1131.     close (dataSock);
  1132.     return (newDataSock);
  1133.     }
  1134. /*******************************************************************************
  1135. *
  1136. * ftpLs - list directory contents via FTP
  1137. *
  1138. * This routine lists the contents of a directory.  The content list
  1139. * is obtained via an NLST FTP transaction.
  1140. *
  1141. * The local device name must be the same as the remote host name
  1142. * with a colon ":" as a suffix.  (For example "wrs:" is the device
  1143. * name for the "wrs" host.)
  1144. *
  1145. * RETURNS : OK, or ERROR if could not open directory.
  1146. */
  1147. STATUS ftpLs
  1148.     (
  1149.     char * dirName /* name of directory to list */
  1150.     )
  1151.     {
  1152.     DEV_HDR * pDevHdr;
  1153.     char    fullFileName [MAX_FILENAME_LENGTH];
  1154.     char    hostName [MAXHOSTNAMELEN];
  1155.     int     hostLength;
  1156.     char    usr [MAX_IDENTITY_LEN];
  1157.     char    passwd [MAX_IDENTITY_LEN];
  1158.     int     dataSock;
  1159.     int     cntrlSock;
  1160.     char    buffer [BUFSIZ];
  1161.     int     nChars;
  1162.     /* Get device header and complete filename */
  1163.     ioFullFileNameGet (dirName, &pDevHdr, fullFileName);
  1164.     /* Get the host name, and remove the trailing ":" */
  1165.     strcpy (hostName, pDevHdr->name);
  1166.     hostLength = strlen (hostName);
  1167.     hostName [hostLength - 1] = EOS;
  1168.     /* Get user ID information */
  1169.     remCurIdGet (usr, passwd);
  1170.     if (ftpXfer (hostName, usr, passwd, "", "NLST", fullFileName, "",
  1171.         &cntrlSock, &dataSock) != OK)
  1172.         {
  1173.             FTPLDEBUG ("Can't open directory "%s"n", 
  1174.                FTPL_DEBUG_ERRORS,dirName,1,2,3,4,5);
  1175.             return (ERROR);
  1176.         }
  1177.     /* Write out the listing */
  1178.     while ((nChars = read (dataSock, (char *) buffer, BUFSIZ)) > 0)
  1179.         write (STD_OUT, (char *) buffer, nChars);
  1180.     
  1181.     /* Close the sockets opened by ftpXfer */
  1182.     close (cntrlSock);
  1183.     close (dataSock);
  1184.     return (OK);
  1185.     }
  1186. /*******************************************************************************
  1187. *
  1188. * ftpLibDebugOptionSet - set the debug level of the ftp library routines
  1189. *
  1190. * This routine enables the debugging of ftp transactions using the ftp library.
  1191. *
  1192. * ts
  1193. * Debugging Level     | Meaning
  1194. * ----------------------------------------------------------------
  1195. * FTPL_DEBUG_OFF      | No debugging messages. 
  1196. * FTPL_DEBUG_INCOMING | Display all incoming responses. 
  1197. * FTPL_DEBUG_OUTGOING | Display all outgoing commands. 
  1198. * FTPL_DEBUG_ERRORS   | Display warnings and errors
  1199. * te
  1200. * EXAMPLE
  1201. * .CS
  1202. * ftpLibDebugOptionsSet (FTPL_DEBUG_ERRORS);    /@ Display any runtime errors @/
  1203. * ftpLibDebugOptionsSet (FTPL_DEBUG_OUTGOING);  /@ Display outgoing commands @/
  1204. * ftpLibDebugOptionsSet (FTPL_DEBUG_INCOMING);  /@ Display incoming replies @/
  1205. * ftpLibDebugOptionsSet (FTPL_DEBUG_INCOMING |  /@ Display both commands and @/ 
  1206. *                        FTPL_DEBUG_OUTGOING);  /@         replies @/
  1207. * .CE
  1208. *
  1209. */
  1210. void ftpLibDebugOptionsSet
  1211.     (
  1212.     UINT32 debugLevel 
  1213.     )
  1214.     {
  1215.         ftplDebug = debugLevel;
  1216.     }
  1217. /*******************************************************************************
  1218. *
  1219. * ftpTransientConfigSet - set parameters for host FTP_TRANSIENT responses 
  1220. *
  1221. * This routine adjusts the delay between retries in response to receiving 
  1222. * FTP_PRELIM and the maximum retry count permitted before failing.
  1223. *
  1224. * RETURNS : OK
  1225. */
  1226. STATUS ftpTransientConfigSet
  1227.     (
  1228.     UINT32 maxRetryCount, /* The maximum number of attempts to retry */
  1229.     UINT32 retryInterval  /* time (in system clock ticks) between retries */
  1230.     )
  1231.     {
  1232.     /* Set the values */
  1233.     ftplTransientMaxRetryCount = maxRetryCount;
  1234.     ftplTransientRetryInterval = retryInterval;
  1235.     return (OK);
  1236.     }
  1237. /*******************************************************************************
  1238. *
  1239. * ftpTransientConfigGet - get parameters for host FTP_TRANSIENT responses 
  1240. *
  1241. * This routine retrieves the delay between retries in response to receiving 
  1242. * FTP_TRANSIENT and the maximum retry count permitted before failing.
  1243. *
  1244. * RETURNS : OK
  1245. *
  1246. * SEE ALSO : ftpTransientConfigSet, tickLib
  1247. */
  1248. STATUS ftpTransientConfigGet
  1249.     (
  1250.     UINT32 *maxRetryCount, /* The maximum number of attempts to retry */
  1251.     UINT32 *retryInterval  /* time (in system clock ticks) between retries */
  1252.     )
  1253.     {
  1254.     /* return the values */
  1255.     if (maxRetryCount != NULL)
  1256.         *maxRetryCount = ftplTransientMaxRetryCount;
  1257.     if (retryInterval != NULL)
  1258.        *retryInterval = ftplTransientRetryInterval;
  1259.     return (OK);
  1260.     }
  1261. /*******************************************************************************
  1262. *
  1263. * ftpTransientFatal - applette to terminate FTP transient host responses
  1264. *
  1265. * ftpXfer will normally retry a command if the host responds with a 4xx
  1266. * reply.   If this applette is installed,  it can immediately terminate
  1267. * the retry sequence.
  1268. *
  1269. *
  1270. * RETURNS 
  1271. *
  1272. * TRUE, terminate retry attempts; FALSE, continue retry attempts.
  1273. *
  1274. * INTERNAL 
  1275. * This is the default routine if the customer does not install
  1276. * an applette.
  1277. *
  1278. * SEE ALSO : ftpTransientFatalInstall(), ftpTransientConfigSet()
  1279. *
  1280. */
  1281. LOCAL BOOL ftpTransientFatal 
  1282.     (
  1283.     UINT32 reply /* Three digit code defined in RFC #959 */
  1284.     )
  1285.     {
  1286.     switch (reply)
  1287.         {
  1288.         case (421): /* Service not available */
  1289.         case (450): /* File unavailable */
  1290.         case (451): /* error in processing */
  1291.         case (452): /* insufficient storage */
  1292.             { 
  1293.             /* yes, these are actually non-recoverable replies */
  1294.             return (TRUE); 
  1295.             break;
  1296.             }
  1297.             /* attempt to retry the last command */
  1298.         default:
  1299.         return (FALSE); 
  1300.         }
  1301.     }
  1302. /*******************************************************************************
  1303. *
  1304. * ftpTransientFatalInstall - set applette to stop FTP transient host responses
  1305. *
  1306. * The routine installs a function which will determine if a transient response 
  1307. * should be fatal.
  1308. * Some ftp servers incorrectly use 'transient' responses instead of 
  1309. * 'error' to describe conditions such as 'disk full'.
  1310. *
  1311. * RETURNS 
  1312. * OK if the installation is successful, or ERROR if the installation fails.
  1313. *
  1314. * SEE ALSO 
  1315. *
  1316. * ftpTransientConfigSet(), ftpTransientFatal() in 
  1317. * target/config/comps/src/net/usrFtp.c
  1318. *
  1319. */
  1320. STATUS ftpTransientFatalInstall 
  1321.     (
  1322.     FUNCPTR pApplette  /* function that returns TRUE or FALSE */
  1323.     )
  1324.     {
  1325.     /* At least prevent this from being a disaster */
  1326.     if (pApplette == NULL)
  1327.         return (ERROR); 
  1328.     _func_ftpTransientFatal = pApplette;
  1329.     return (OK); /* attempt to retry the last command */
  1330.     }
  1331. /*******************************************************************************
  1332. *
  1333. * ftpPasvReplyParse - Parse the reply of a PASV command
  1334. *
  1335. * ftpPasvReplyParse expects to receive a string generated as a response from
  1336. * a PASV command.   The string will begin with '227' and end with a series
  1337. * of six integer values encapsulated in parentheses and separated by commas.
  1338. * EXAMPLE STRING:
  1339. *
  1340. * 227 Entering passive mode (147,11,1,23,1027,1028)
  1341. *              
  1342. * RETURNS : OK - Sucessfull parse
  1343. *           ERROR - Error in parse
  1344. *
  1345. * SEE ALSO : ftpCommandEnhanced(), ftpReplyGetEnhanced()
  1346. *
  1347. * NOMANUAL
  1348. */
  1349. LOCAL STATUS ftpPasvReplyParse 
  1350.     (
  1351.     char *responseString, /* NULL terminated string */
  1352.     UINT32 *argument1,    /* First argument */
  1353.     UINT32 *argument2,    /* Second argument */
  1354.     UINT32 *argument3,    /* Third argument */
  1355.     UINT32 *argument4,    /* Fourth argument */
  1356.     UINT32 *argument5,    /* Fifth argument */
  1357.     UINT32 *argument6     /* Six argument */
  1358.     )
  1359.     {
  1360.     char *index;
  1361.     UINT32 tmpArg1;
  1362.     UINT32 tmpArg2;
  1363.     UINT32 tmpArg3;
  1364.     UINT32 tmpArg4;
  1365.     UINT32 tmpArg5;
  1366.     UINT32 tmpArg6;
  1367.     if (responseString == NULL)
  1368.         return (ERROR);
  1369.     /* Sanity check: Check for '227' at the beginning of the reply */
  1370.     if (strstr (responseString, "227") == NULL)
  1371.         {
  1372.         FTPLDEBUG ("ftpPasvReplyParse: error - '227' not part of PASV responsen", 
  1373.             FTPL_DEBUG_ERRORS, 0,1,2,3,4,5);
  1374.         return (ERROR);
  1375.         }
  1376.  
  1377.     index = strstr(responseString, "(");
  1378.     if (index == NULL)
  1379.         {
  1380.         FTPLDEBUG ("ftpPasvReplyParse: error - '(' not part of PASV responsen", 
  1381.             FTPL_DEBUG_ERRORS, 0,1,2,3,4,5);
  1382.         return (ERROR);
  1383.         }
  1384.     /* scan in the arguments */
  1385.     sscanf (index+1, "%d,%d,%d,%d,%d,%d", 
  1386.             &tmpArg1,
  1387.             &tmpArg2,
  1388.             &tmpArg3,
  1389.             &tmpArg4,
  1390.             &tmpArg5,
  1391.             &tmpArg6);
  1392.     /* Store arguments as neccesary */
  1393.     if (argument1)
  1394.         *argument1 = tmpArg1;
  1395.     if (argument2)
  1396.         *argument2 = tmpArg2;
  1397.     if (argument3)
  1398.         *argument3 = tmpArg3;
  1399.     if (argument4)
  1400.         *argument4 = tmpArg4;
  1401.     if (argument5)
  1402.         *argument5 = tmpArg5;
  1403.     if (argument6)
  1404.         *argument6 = tmpArg6;
  1405.     return OK;
  1406.     }