www.pudn.com > FTPC.zip > FTP_DATA.C


/*--------------------------------------------------------------------- 
 * 
 *  Program: AC_FTP.EXE Asynch Ftp Client (TCP) 
 * 
 *  filename: FTP_DATA.C 
 * 
 *  copyright by Bob Quinn, 1995 
 *    
 *  Description: 
 *    Client application that uses "file transfer protocol" (ftp) 
 *    service as described by RFC 959.   
 * 
 *    This module contains the functions that deal with the FTP 
 *    data connection (for sending and receiving data between  
 *    client and server to service LIST, RECV and STOR commands).  
 * 
 *  This software is not subject to any  export  provision  of 
 *  the  United  States  Department  of  Commerce,  and may be 
 *  exported to any country or planet. 
 * 
 *  Permission is granted to anyone to use this  software  for any   
 *  purpose  on  any computer system, and to alter it and redistribute  
 *  it freely, subject to the following  restrictions: 
 * 
 *  1. The author is not responsible for the consequences of 
 *     use of this software, no matter how awful, even if they 
 *     arise from flaws in it. 
 * 
 *  2. The origin of this software must not be misrepresented, 
 *     either by explicit claim or by omission.  Since few users 
 *     ever read sources, credits must appear in the documentation. 
 * 
 *  3. Altered versions must be plainly marked as such, and 
 *     must not be misrepresented as being the original software. 
 *     Since few users ever read sources, credits must appear in 
 *     the documentation. 
 * 
 *  4. This notice may not be removed or altered. 
 *	  
 ---------------------------------------------------------------------*/ 
#include "..\wsa_xtra.h"  
#include  
#include  
#include "..\winsockx.h" 
 
#include     /* for _fmemcpy() & _fmemset() */ 
#include     /* for _ltoa() */ 
#include  
#include "resource.h" 
#include     /* for Microsoft find file structure */ 
#include "ac_ftp.h" 
 
/*-------------------------------------------------------------- 
 * Function: InitDataConn() 
 * 
 * Description: Set up a listening socket for a data connection 
 */ 
SOCKET InitDataConn(PSOCKADDR_IN lpstName, HWND hDlg, u_int nAsyncMsg) 
{ 
  int nRet; 
  SOCKET hLstnSock; 
  int nLen = SOCKADDR_LEN; 
   
  if (bDebug) { 
    wsprintf(achTempBuf,  
      "InitDataConn()   Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%d\n",  
      nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd, 
      astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState); 
    OutputDebugString (achTempBuf);     
  } 
  lByteCount = 0;  /* init byte counter */ 
   
  /* Get a TCP socket to use for data connection listen*/ 
  hLstnSock = socket (AF_INET, SOCK_STREAM, 0); 
  if (hLstnSock == INVALID_SOCKET)  { 
    WSAperror(WSAGetLastError(), "socket()", hInst); 
  } else { 
    /* Request async notification for most events */ 
    nRet = WSAAsyncSelect(hLstnSock, hDlg, nAsyncMsg,  
           (FD_ACCEPT | FD_READ | FD_WRITE | FD_CLOSE)); 
    if (nRet == SOCKET_ERROR) { 
      WSAperror(WSAGetLastError(), "WSAAsyncSelect()", hInst); 
    } else { 
                    
      /* Name the local socket with bind() */ 
      lpstName->sin_family = PF_INET; 
      lpstName->sin_port   = 0;  /* any port will do */ 
      nRet = bind(hLstnSock,(LPSOCKADDR)lpstName,SOCKADDR_LEN); 
      if (nRet == SOCKET_ERROR) { 
	    WSAperror(WSAGetLastError(), "bind()", hInst); 
      } else { 
         
        /* Get local port number assigned by bind() */ 
        nRet = getsockname(hLstnSock,(LPSOCKADDR)lpstName,  
            (int FAR *)&nLen); 
        if (nRet == SOCKET_ERROR) { 
	      WSAperror(WSAGetLastError(), "getsockname()", hInst); 
        } else { 
 
          /* Listen for incoming connection requests */ 
          nRet = listen(hLstnSock, 5); 
          if (nRet == SOCKET_ERROR) { 
	        WSAperror(WSAGetLastError(), "listen()", hInst); 
          } 
        } 
      } 
    } 
    /* If we haven't had an error but we still don't know the local  
     *  IP address, then we need to try to get it before we return */ 
    if (!lpstName->sin_addr.s_addr) { 
      lpstName->sin_addr.s_addr = GetHostID(); 
      if (!lpstName->sin_addr.s_addr) { 
        MessageBox (hDlg, "Can't get local IP address",  
          "InitDataConn() Failed", MB_OK | MB_ICONASTERISK); 
        nRet = SOCKET_ERROR; 
      } 
    } 
    /* If we had an error or we still don't know our IP address,  
     *  then we have a problem.  Clean up */ 
    if (nRet == SOCKET_ERROR) { 
	  closesocket(hLstnSock); 
	  hLstnSock = INVALID_SOCKET; 
    } 
  } 
  return (hLstnSock); 
} /* end InitDataConn() */ 
 
/*-------------------------------------------------------------- 
 * Function: AcceptDataConn() 
 * 
 * Description: Accept an incoming data connection 
 */ 
SOCKET AcceptDataConn(SOCKET hLstnSock, PSOCKADDR_IN pstName) 
{ 
  SOCKET hDataSock; 
  int nRet, nLen = SOCKADDR_LEN, nOptval; 
   
  if (bDebug) { 
    wsprintf(achTempBuf,  
      "AcceptDataConn() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%d\n",  
      nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd, 
      astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState); 
    OutputDebugString (achTempBuf);     
  } 
 
  hDataSock = accept (hLstnSock, (LPSOCKADDR)pstName, (LPINT)&nLen); 
  if (hDataSock == SOCKET_ERROR) { 
    int WSAErr = WSAGetLastError(); 
    if (WSAErr != WSAEWOULDBLOCK) 
      WSAperror (WSAErr, "accept", hInst); 
  } else if (bReAsync) { 
    /* This SHOULD be unnecessary, since all new sockets are supposed 
     *  to inherit properties of the listening socket (like all the 
     *  asynch events registered but some WinSocks don't do this. 
     * Request async notification for most events */ 
    nRet = WSAAsyncSelect(hDataSock, hWinMain, WSA_ASYNC+1,  
           (FD_READ | FD_WRITE | FD_CLOSE)); 
    if (nRet == SOCKET_ERROR) { 
      WSAperror(WSAGetLastError(), "WSAAsyncSelect()", hInst); 
    } 
    /* Try to get lots of buffer space */ 
    nOptval = astFtpCmd[0].nFtpCmd==STOR ? SO_SNDBUF : SO_RCVBUF; 
    GetBuf(hDataSock, INPUT_SIZE*2, nOptval); 
  } 
  return (hDataSock); 
} /* end AcceptData() */ 
 
/*-------------------------------------------------------------- 
 * Function: SendData() 
 * 
 * Description: Open data file, read and send 
 */ 
long SendData(SOCKET *hDataSock, HFILE hDataFile, int len) 
{ 
  static int cbReadFromFile;         /* bytes read from file */ 
  static int cbSentToServer;         /* number of buffered bytes sent */ 
  static HFILE hLastFile;            /* handle of last file sent */ 
  long cbTotalSent = 0;              /* total bytes sent */ 
  int nRet, WSAErr, cbBytesToSend; 
 
  /* Reset our counters when we access a new file */ 
  if (hLastFile != hDataFile) { 
    cbReadFromFile = 0; 
    cbSentToServer = 0; 
    hLastFile = hDataFile; 
  } 
     
  /* Read data from file, and send it. */ 
  do { 
    if (bIOBeep) 
      MessageBeep(0xFFFF); 
 
    /* calculate what's left to send */ 
    cbBytesToSend = cbReadFromFile - cbSentToServer;  
    if (cbBytesToSend <= 0) { 
     
      /* read data from input file, if we need it */ 
      if (!bFromNul) { 
        cbReadFromFile = _lread(hDataFile, achOutBuf, INPUT_SIZE); 
        if (cbReadFromFile == HFILE_ERROR) { 
          MessageBox (hWinMain, "Error reading data file",  
            "SendData() Failed", MB_OK | MB_ICONASTERISK); 
          break; 
        } else if (!cbReadFromFile){ 
          /* EOF: no more data to send */ 
          CloseFtpConn(hDataSock, (PSTR)0, 0, hWinMain); 
          EndData(); 
          break; 
        } else { 
          cbBytesToSend = cbReadFromFile; /* send as much as we read */ 
        }  
      } else { 
        /* just send whatever's in memory (up to our max) */ 
        if (lByteCount < MAXNULPUT) { 
          cbBytesToSend = INPUT_SIZE; 
        } else { 
          CloseFtpConn(hDataSock, (PSTR)0, 0, hWinMain); 
          EndData(); 
        } 
      } 
      cbSentToServer = 0;  /* reset tally */ 
    } 
    /* Send data to server */ 
    nRet = send (*hDataSock, &(achOutBuf[cbSentToServer]),  
                ((len < cbBytesToSend) ? len : cbBytesToSend), 0); 
 
    if (nRet == SOCKET_ERROR) { 
      WSAErr = WSAGetLastError(); 
      /* Display significant errors */ 
      if (WSAErr != WSAEWOULDBLOCK) 
        WSAperror(WSAErr, (LPSTR)"send()", hInst); 
    } else { 
      /* Update byte counter, and display. */ 
      lByteCount += nRet; 
      _ltoa(lByteCount, achTempBuf, 10); 
      SetDlgItemText(hWinMain, IDC_DATA_RATE, achTempBuf); 
      cbSentToServer += nRet;/* tally bytes sent since last file read */ 
      cbTotalSent    += nRet;/* tally total bytes sent since we started */ 
    } 
  } while (nRet != SOCKET_ERROR); 
 
  return (cbTotalSent); 
} /* end SendData() */ 
 
/*-------------------------------------------------------------- 
 * Function: RecvData() 
 * 
 * Description: Receive data from net and write to open data file  
 */ 
int RecvData(SOCKET hDataSock, HFILE hDataFile, LPSTR achInBuf, int len) 
{ 
  static HFILE hLastFile;          /* handle of last file sent */ 
  static int cbBytesBuffered;      /* total bytes received */ 
  int cbBytesRcvd = 0; 
  int nRet=0, WSAErr; 
 
   if (hDataFile != hLastFile) { 
      hLastFile = hDataFile; 
      cbBytesBuffered = 0;  
   } 
                                                        
  /* Read as much as we can from server */ 
    while (cbBytesBuffered < len) { 
 
      nRet = recv (hDataSock,&(achInBuf[cbBytesBuffered]),  
        len-cbBytesBuffered, 0); 
 
      if (nRet == SOCKET_ERROR) { 
        WSAErr = WSAGetLastError(); 
        /* Display significant errors */ 
        if (WSAErr != WSAEWOULDBLOCK) 
          WSAperror(WSAErr, (LPSTR)"recv()", hInst); 
        /* exit recv() loop on any error */ 
          goto recv_end; 
      } else if (nRet == 0) { /* Other side closed socket */ 
        /* quit if server closed connection */ 
        goto recv_end; 
           
      } else { 
        /* Update byte counter, and display */ 
        lByteCount += nRet; 
        _ltoa(lByteCount, achTempBuf, 10); 
        SetDlgItemText(hWinMain, IDC_DATA_RATE, achTempBuf); 
        cbBytesRcvd += nRet;     /* tally bytes read */ 
        cbBytesBuffered += nRet; 
      } 
    } 
recv_end: 
  if (!bToNul &&  
      ((cbBytesBuffered > (len-MTU_SIZE)) ||  
       ((nRet == SOCKET_ERROR) && WSAGetLastError() != WSAEWOULDBLOCK) ||  
       (nRet == 0))) { 
    /* If we have a lot buffered, write to data file */ 
    nRet = _lwrite(hDataFile, achInBuf, cbBytesBuffered); 
    if (nRet == HFILE_ERROR) 
      MessageBox (hWinMain, "Can't write to local file",  
        "RecvData() Failed", MB_OK | MB_ICONASTERISK); 
    cbBytesBuffered = 0; 
  } else if (bToNul) 
    cbBytesBuffered = 0; 
  return (cbBytesRcvd); 
} /* end RecvData() */ 
 
/*-------------------------------------------------------------- 
 * Function: EndData() 
 * 
 * Description: Close up the data connection 
 */ 
void EndData (void) { 
  LONG dByteRate; 
  LONG lMSecs; 
 
  /* Calculate data transfer rate, and display */ 
  lMSecs = (LONG) GetTickCount() - lStartTime; 
  if (lMSecs <= 55) 
    lMSecs = 27;  /* about half of 55Msec PC clock resolution */ 
               
  /* Socket Check should not be necessary, but some WinSocks             
   *  mistakenly post FD_CLOSE to listen socket after close */ 
  nAppState &= ~(DATACONNECTED); 
  SetDlgItemText (hWinMain, IDC_STATUS, "Status: connected"); 
             
  if (lByteCount > 0L) { 
    dByteRate = (lByteCount/lMSecs); /* data rate (bytes/Msec) */ 
    wsprintf (achTempBuf,"%ld bytes %s in %ld.%ld seconds (%ld.%ld Kbytes/sec)", 
      lByteCount,  
      ((astFtpCmd[0].nFtpCmd==STOR) ? "sent":"received"), 
      lMSecs/1000, lMSecs%1000,  
      (dByteRate*1000)/1024, (dByteRate*1000)%1024); 
    SetDlgItemText (hWinMain, IDC_DATA_RATE, achTempBuf); 
    if (hLogFile != HFILE_ERROR) 
      _lwrite (hLogFile, achTempBuf, strlen(achTempBuf)); 
  } 
  lStartTime = 0L; 
  if (hDataFile != HFILE_ERROR) { 
    _lclose (hDataFile); 
    hDataFile = HFILE_ERROR; 
    if (astFtpCmd[0].nFtpCmd == LIST) { 
      wsprintf (achTempBuf, "notepad %s", szTempFile); 
      WinExec (achTempBuf, SW_SHOW); 
    } 
  } 
  astFtpCmd[0].nFtpCmd = 0;  /* reset pending command */ 
} /* end EndData() */