FTP_CTRL.C
上传用户:tjfeida
上传日期:2013-03-10
资源大小:1917k
文件大小:14k
源码类别:

Ftp客户端

开发平台:

Visual C++

  1. /*---------------------------------------------------------------------
  2.  *
  3.  *  Program: AC_FTP.EXE Asynch Ftp Client (TCP)
  4.  *
  5.  *  filename: FTP_CTRL.C
  6.  *
  7.  *  copyright by Bob Quinn, 1995
  8.  *   
  9.  *  Description:
  10.  *    Client application that uses "file transfer protocol" (ftp)
  11.  *    service as described by RFC 959.  
  12.  *
  13.  *    This module contains the functions that deal with the FTP
  14.  *    control connection (for sending commands to FTP server, 
  15.  *    receiving replies from FTP server, and processing them.
  16.  *
  17.  *  This software is not subject to any  export  provision  of
  18.  *  the  United  States  Department  of  Commerce,  and may be
  19.  *  exported to any country or planet.
  20.  *
  21.  *  Permission is granted to anyone to use this  software  for any  
  22.  *  purpose  on  any computer system, and to alter it and redistribute 
  23.  *  it freely, subject to the following  restrictions:
  24.  *
  25.  *  1. The author is not responsible for the consequences of
  26.  *     use of this software, no matter how awful, even if they
  27.  *     arise from flaws in it.
  28.  *
  29.  *  2. The origin of this software must not be misrepresented,
  30.  *     either by explicit claim or by omission.  Since few users
  31.  *     ever read sources, credits must appear in the documentation.
  32.  *
  33.  *  3. Altered versions must be plainly marked as such, and
  34.  *     must not be misrepresented as being the original software.
  35.  *     Since few users ever read sources, credits must appear in
  36.  *     the documentation.
  37.  *
  38.  *  4. This notice may not be removed or altered.
  39.  *  
  40.  ---------------------------------------------------------------------*/
  41. #include "..wsa_xtra.h" 
  42. #include <windows.h>
  43. #include <windowsx.h>
  44. #include "..winsockx.h"
  45. #include <string.h>    /* for _fmemcpy() & _fmemset() */
  46. #include <winsock.h>
  47. #include "resource.h"
  48. #include <direct.h>    /* for Microsoft find file structure */
  49. #include "ac_ftp.h"
  50. /*---------------------------------------------------------------
  51.  * Function: InitCtrlConn()
  52.  *
  53.  * Description: get a TCP socket, register for async notification,
  54.  *   and then connect to Ftp server
  55.  */
  56. SOCKET InitCtrlConn(PSOCKADDR_IN pstName, HWND hDlg, u_int nAsyncMsg) 
  57. {
  58.   int nRet;
  59.   SOCKET hCtrlSock;
  60.                      
  61.   if (bDebug) 
  62.     OutputDebugString("InitCtrlConn()n");
  63.     
  64.   /* Get a TCP socket for control connection */
  65.   hCtrlSock = socket (AF_INET, SOCK_STREAM, 0);
  66.   if (hCtrlSock == INVALID_SOCKET)  {
  67.     WSAperror(WSAGetLastError(), "socket()", hInst);
  68.   } else {
  69.                  
  70.     /* Request async notification for most events */
  71.     nRet = WSAAsyncSelect(hCtrlSock, hDlg, nAsyncMsg, 
  72.            (FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE));
  73.     if (nRet == SOCKET_ERROR) {
  74.       WSAperror(WSAGetLastError(), "WSAAsyncSelect()", hInst);
  75.       closesocket(hCtrlSock);
  76.       hCtrlSock = INVALID_SOCKET;
  77.     } else {
  78.                    
  79.       /* Initiate non-blocking connect to server */
  80.       pstName->sin_family = PF_INET;
  81.       pstName->sin_port   = htons(IPPORT_FTP);
  82.       nRet = connect(hCtrlSock,(LPSOCKADDR)pstName,SOCKADDR_LEN);
  83.       if (nRet == SOCKET_ERROR) {
  84.     int WSAErr = WSAGetLastError();
  85.                
  86.     /* Anything but "would block" error is bad */
  87.     if (WSAErr != WSAEWOULDBLOCK) {
  88.       /* Report error and clean up */
  89.       WSAperror(WSAErr, "connect()", hInst);
  90.       closesocket(hCtrlSock);
  91.       hCtrlSock = INVALID_SOCKET;
  92.     }
  93.       }
  94.     }
  95.   }
  96.   return (hCtrlSock);
  97. } /* end InitCtrlConn() */
  98. /*--------------------------------------------------------------
  99.  * Function: SendFtpCmd()
  100.  *
  101.  * Description: Format and send an FTP command to the server
  102.  */
  103. int SendFtpCmd(void)
  104. {
  105.   int nRet, nLen, nBytesSent = 0;
  106.   int nFtpCmd = astFtpCmd[1].nFtpCmd;
  107.   
  108.   if (bDebug) {
  109.     wsprintf(achTempBuf, 
  110.       "SendFtpCmd()    Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%dn", 
  111.       nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd, 
  112.       astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
  113.     OutputDebugString (achTempBuf);
  114.   }
  115.   /* Create a command string (if we don't already have one) */
  116.   if (szFtpCmd[0] == 0) {
  117.     
  118.     switch (nFtpCmd) {
  119.       case PORT:
  120.         wsprintf (szFtpCmd, "PORT %d,%d,%d,%d,%d,%drn", 
  121.           stDLclName.sin_addr.S_un.S_un_b.s_b1,  /* local addr */
  122.           stDLclName.sin_addr.S_un.S_un_b.s_b2,
  123.           stDLclName.sin_addr.S_un.S_un_b.s_b3,
  124.           stDLclName.sin_addr.S_un.S_un_b.s_b4,
  125.           stDLclName.sin_port & 0xFF,            /* local port */
  126.          (stDLclName.sin_port & 0xFF00)>>8);
  127.         break;
  128.       case CWD:
  129.       case DELE:
  130.       case PASS:
  131.       case RETR:
  132.       case STOR:
  133.       case TYPE:
  134.       case USER:
  135.         /* Ftp commmand and parameter */
  136.         wsprintf (szFtpCmd, "%s %srn", 
  137.           aszFtpCmd[nFtpCmd], &(astFtpCmd[1].szFtpParm));
  138.         break;
  139.       case ABOR:
  140.       case LIST:
  141.       case PWD:
  142.       case QUIT:
  143.         /* Solitary Ftp command string (no parameter) */
  144.         wsprintf (szFtpCmd, "%srn", aszFtpCmd[nFtpCmd]);
  145.         break;
  146.       default: 
  147.         return (0);  /* we have a bogus command! */
  148.     }
  149.   }
  150.   nLen = strlen(szFtpCmd);
  151.   
  152.   if (hCtrlSock != INVALID_SOCKET) {         
  153.     /* Send the ftp command to control socket */
  154.     while (nBytesSent < nLen) {
  155.       nRet = send(hCtrlSock, (LPSTR)szFtpCmd, nLen-nBytesSent, 0);
  156.       if (nRet == SOCKET_ERROR) {
  157.         int WSAErr = WSAGetLastError();
  158.       
  159.         /* If "would block" error we'll pickup again with async
  160.          *  FD_WRITE notification, but any other error is bad news */
  161.         if (WSAErr != WSAEWOULDBLOCK) 
  162.           WSAperror(WSAErr, "SendFtpCmd()", hInst);
  163.         break;
  164.       }
  165.       nBytesSent += nRet;
  166.     }
  167.   }
  168.   /* if we sent it all, update our status and move everything up 
  169.    *  in command queue */
  170.   if (nBytesSent == nLen) {
  171.     int i;
  172.     if (nFtpCmd == PASS)                 /* hide password */
  173.       memset (szFtpCmd+5, 'x', 10);
  174.         
  175.     if (bLogFile)                         /* log command */
  176.       _lwrite (hLogFile, szFtpCmd, strlen(szFtpCmd));
  177.     GetDlgItemText (hWinMain, IDC_REPLY,  /* display command */
  178.       achRplyBuf, RPLY_SIZE-strlen(szFtpCmd));
  179.     wsprintf (achTempBuf, "%s%s", szFtpCmd, achRplyBuf);
  180.     SetDlgItemText (hWinMain, IDC_REPLY, achTempBuf);
  181.     szFtpCmd[0] = 0;  /* disable Ftp command string */
  182.     
  183.     /* move everything up in the command queue */
  184.     for (i=0; i < nQLen; i++) {
  185.       astFtpCmd[i].nFtpCmd = astFtpCmd[i+1].nFtpCmd;
  186.       astFtpCmd[i+1].nFtpCmd = 0;        /* reset old command */
  187.       if (*(astFtpCmd[i+1].szFtpParm)) {
  188.         memcpy (astFtpCmd[i].szFtpParm, astFtpCmd[i+1].szFtpParm, CMD_SIZE);
  189.         *(astFtpCmd[i+1].szFtpParm) = 0; /* terminate old string */
  190.       } else {
  191.         *(astFtpCmd[i].szFtpParm) = 0;   /* terminate unused string */
  192.       }
  193.     }
  194.     nQLen--;          /* decrement the queue length */
  195.     
  196.     switch (nFtpCmd) {
  197.       case (USER):
  198.         SetDlgItemText (hWinMain, IDC_STATUS,"Status: connecting");
  199.         break;
  200.       case (STOR):
  201.         SetDlgItemText (hWinMain, IDC_STATUS,"Status: sending a file");
  202.         break;
  203.       case (RETR):
  204.         SetDlgItemText (hWinMain, IDC_STATUS,"Status: receiving a file");
  205.         break;
  206.       case (LIST):
  207.         SetDlgItemText (hWinMain, IDC_STATUS,"Status: receiving directory");
  208.         break;
  209.       case (QUIT):
  210.         SetDlgItemText (hWinMain, IDC_SERVER, "Server: none");
  211.         SetDlgItemText (hWinMain, IDC_STATUS, "Status: not connected");
  212.         break;
  213.     }
  214.   }
  215.   return (nBytesSent);
  216. } /* end SendFtpCmd() */
  217.                      
  218. /*--------------------------------------------------------------
  219.  * Function: QueueFtpCmd()
  220.  *
  221.  * Description: Put FTP command in command queue for sending after we
  222.  *  receive responses to pending commands or now if nothing is pending
  223.  */
  224. BOOL QueueFtpCmd(int nFtpCmd, LPSTR szFtpParm) {
  225.    
  226.   if (bDebug) {
  227.     wsprintf(achTempBuf, 
  228.       "QueueFtpCmd()    Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, pend:%dn", 
  229.       nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
  230.       astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, 
  231.       recv(hCtrlSock, achTempBuf, BUF_SIZE, MSG_PEEK));
  232.     OutputDebugString (achTempBuf);    
  233.   }
  234.   if ((nFtpCmd == ABOR) || (nFtpCmd == QUIT)) {
  235.     AbortFtpCmd();
  236.     if (hCtrlSock != INVALID_SOCKET)
  237.       SetDlgItemText (hWinMain, IDC_STATUS,"Status: connected");
  238.   } else if (nQLen == MAX_CMDS) {
  239.     /* Notify user if they can't fit in the queue */
  240.     MessageBox (hWinMain, "Ftp command queue is full, try again later", 
  241.        "Can't Queue Command", MB_OK | MB_ICONASTERISK);
  242.     return (FALSE);            /* not queued */
  243.   }
  244.   nQLen++;  /* increment Ftp command counter */
  245.   /* Save command vitals */  
  246.   astFtpCmd[nQLen].nFtpCmd = nFtpCmd;
  247.   if (szFtpParm)
  248.     lstrcpy (astFtpCmd[nQLen].szFtpParm, szFtpParm);
  249.   if (!(astFtpCmd[0].nFtpCmd) && astFtpCmd[1].nFtpCmd) {
  250.     /* If nothing pending reply, then send the next command */
  251.     SendFtpCmd();
  252.   }
  253.   return (TRUE);   /* queued! */
  254. }  /* end QueueFtpCmd() */
  255. /*--------------------------------------------------------------
  256.  * Function: AbortFtpCmd()
  257.  *
  258.  * Description: Clean up routine to abort a pending FTP command and
  259.  * clear the command queue
  260.  */
  261. void AbortFtpCmd(void) {
  262.   int i;
  263.   if (hLstnSock != INVALID_SOCKET) {/* Close listen socket */
  264.     closesocket(hLstnSock);
  265.     hLstnSock = INVALID_SOCKET;
  266.   }
  267.   if (hDataSock != INVALID_SOCKET){ /* Close data socket */
  268.     CloseFtpConn(&hDataSock,  
  269.       (astFtpCmd[0].nFtpCmd != STOR) ? achInBuf : (PSTR)0, 
  270.       INPUT_SIZE, hWinMain);
  271.     EndData();
  272.   }
  273.   for (i=0;i<MAX_CMDS;i++)          /* Clear command queue */
  274.     astFtpCmd[i].nFtpCmd = 0;         
  275.   nQLen = 0;
  276. } /* end AbortFtpCmd() */
  277. /*--------------------------------------------------------------
  278.  * Function: RecvFtpRply()
  279.  *
  280.  * Description: Read the FTP reply from server (and log it)
  281.  */
  282. int RecvFtpRply(SOCKET hCtrlSock, LPSTR szFtpRply, int nLen)
  283. {
  284.   int nRet=0;
  285.   if (bDebug) {
  286.     wsprintf(achTempBuf, 
  287.       "RecvFtpRply()    Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%dn", 
  288.       nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
  289.       astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
  290.     OutputDebugString (achTempBuf);
  291.   }
  292.   if (hCtrlSock != INVALID_SOCKET) {
  293.     memset(szFtpRply,0,nLen);   /* Init receive buffer */
  294.   
  295.     /* Read as much as we can */
  296.     nRet = recv(hCtrlSock,(LPSTR)szFtpRply,nLen,0);
  297.     
  298.     if (nRet == SOCKET_ERROR) {
  299.       int WSAErr = WSAGetLastError();
  300.       if (WSAErr != WSAEWOULDBLOCK) {
  301.         WSAperror (WSAErr, "RecvFtpRply()", hInst);
  302.       }
  303.     } else if (bLogFile)   /* log reply */
  304.       _lwrite (hLogFile, szFtpRply, nRet);     
  305.   }
  306.   return (nRet);  
  307. } /* end RecvFtpReply() */
  308. /*--------------------------------------------------------------
  309.  * Function: ProcessFtpRply()
  310.  *
  311.  * Description: Figure out what happened, and what to do next.
  312.  */
  313. void ProcessFtpRply (LPSTR szRply, int nBufLen) 
  314. {
  315.   LPSTR szFtpRply;
  316.   int nPendingFtpCmd, i;
  317.   
  318.   if (bDebug) {
  319.     wsprintf(achTempBuf, 
  320.       "ProcessFtpRply() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%dn", 
  321.       nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
  322.       astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
  323.     OutputDebugString (achTempBuf);    
  324.   }
  325.   /* Skip continuation lines (denoted by a dash after reply code
  326.    *  or with a blank reply code and no dash) */
  327.   szFtpRply = szRply;
  328.   while ((*(szFtpRply+3) == '-') || 
  329.         ((*(szFtpRply)==' ')&&(*(szFtpRply+1)==' ')&&(*(szFtpRply+2)==' '))) {
  330.     /* find end of reply line */
  331.     for (i=0;*szFtpRply!=0x0a && *szFtpRply && i<nBufLen-3; szFtpRply++,i++);
  332.     szFtpRply++;       /* go to beginning of next reply */
  333.     if (!(*szFtpRply)) /* quit if end of string */
  334.       return;
  335.   }
  336.   
  337.   *szFtpCmd  = 0;                        /* Disable old command string */
  338.   nPendingFtpCmd = astFtpCmd[0].nFtpCmd; /* Save last Ftp Cmd */
  339.   if ((*szFtpRply != '1') && 
  340.       (nPendingFtpCmd != LIST) &&
  341.       (nPendingFtpCmd != STOR) &&
  342.       (nPendingFtpCmd != RETR))
  343.     /* For any but preliminary reply, clear old command */    
  344.     astFtpCmd[0].nFtpCmd = 0;
  345.   /* First digit in 3-digit Ftp reply code is the most significant */
  346.   switch (*szFtpRply) {
  347.     case ('1'):  /* Positive preliminary reply */
  348.       break;
  349.     case ('2'):  /* Positive completion reply */
  350.       switch(nPendingFtpCmd) {
  351.         case 0:  
  352.           /* Check for "220 Service ready for new user" reply, and
  353.            *  send user command to login if login message found */
  354.           if ((*(szFtpRply+1)=='2') && (*(szFtpRply+2)=='0'))
  355.             QueueFtpCmd(USER, szUser);
  356.           break;
  357.         case CWD:
  358.         case USER:
  359.         case PASS:
  360.           /* We're logged in!  Get remote working directory */
  361.           QueueFtpCmd(PWD, 0);
  362.           break;
  363.         case PWD:
  364.           /* Display remote working directory */
  365.           SetDlgItemText (hWinMain, IDC_RPWD, &szFtpRply[4]);
  366.           break;
  367.         case TYPE:
  368.         case PORT:
  369.           /* Send next command (it's already queued) */
  370.           SendFtpCmd();
  371.           break;
  372.         case ABOR:
  373.           /* Close the data socket */
  374.           if (hDataSock != INVALID_SOCKET)
  375.             CloseFtpConn(&hDataSock, (PSTR)0, 0, hWinMain);
  376.           break;
  377.         case QUIT:
  378.           /* Close the control socket */
  379.           if (hCtrlSock != INVALID_SOCKET)
  380.             CloseFtpConn(&hCtrlSock, (PSTR)0, 0, hWinMain);
  381.           break;
  382.         default:
  383.           break; /* Nothing to do after most replies */
  384.       }
  385.       break;     
  386.     case ('3'):  /* Positive intermediate reply */
  387.       if (nPendingFtpCmd == USER)
  388.         QueueFtpCmd(PASS, szPWrd);
  389.       break;
  390.     case ('4'):  /* Transient negative completion reply */
  391.     case ('5'):  /* Permenant negative completion reply */
  392.       /* If Port failed, forget about queued commands */
  393.       if (nPendingFtpCmd != ABOR)
  394.         QueueFtpCmd(ABOR, 0);
  395.       break;
  396.   }
  397. } /* end ProcessFtpRply() */