home *** CD-ROM | disk | FTP | other *** search
/ The Arcade BBS / arcadebbs.zip / arcadebbs / bbstools / WWIV Mods / WSFTPSRC.ZIP / WS_CON.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-12  |  23.4 KB  |  759 lines

  1. /***************************************************************************
  2.   Windows Sockets Client Application Support Module
  3.  
  4.   Written by:
  5.       John A. Junod             Internet: <junodj@gordon-emh2.army.mil>
  6.       267 Hillwood Street                 <zj8549@trotter.usma.edu>
  7.       Martinez, GA 30907      Compuserve: 72321,366 
  8.  
  9.   This program executable and all source code is released into the public
  10.   domain.  It would be nice (but is not required) to give me a little 
  11.   credit for any use of this code.  
  12.  
  13.   THE INFORMATION AND CODE PROVIDED IS PROVIDED AS IS WITHOUT WARRANTY 
  14.   OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
  15.   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  16.   PURPOSE. IN NO EVENT SHALL JOHN A. JUNOD BE LIABLE FOR ANY DAMAGES 
  17.   WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS 
  18.   OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF JOHN A. JUNOD HAS BEEN 
  19.   ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  20.  
  21. *****************************************************************************/
  22.  
  23. #include "ws_glob.h"
  24. #include "ws_ftp.h"
  25. #include <stdio.h>
  26. #include <stdarg.h>
  27. #include <ctype.h>
  28. #include <io.h>
  29. #include <fcntl.h>
  30. #include <sys\stat.h>
  31. #include <time.h>
  32.  
  33. extern int errno;
  34. extern int nHostType;
  35. extern BOOL bAborted;   // timer routine may set this
  36.  
  37. /*
  38. // send a message on the control socket, read and display the resulting
  39. // message and return the result value
  40. */
  41. int getreply(SOCKET ctrl_skt,LPSTR cmdstring)
  42. {
  43.   int iRetCode=0;
  44.  
  45.   iCode=0;
  46.   if(strncmp(cmdstring,"PASS ",5)==0)
  47.     DoAddLine("PASS xxxxxx");
  48.   else
  49.     DoAddLine(cmdstring);
  50.   if(ctrl_skt==INVALID_SOCKET) {
  51.     DoAddLine("Not connected");
  52.   } else {
  53.     if(SendPacket(ctrl_skt,cmdstring)!=-1)
  54.       iRetCode=ReadDisplayLine(ctrl_skt);
  55.   }
  56.   return(iRetCode);  // 0 - 5
  57. }
  58.  
  59. int command(SOCKET ctrl_skt, char *fmt,...)
  60. {
  61.    va_list args;
  62.    char szBuf[90];
  63.    int  iRetCode;
  64.  
  65.    va_start(args,fmt);
  66.    vsprintf(szBuf,fmt,args);
  67.    va_end(args);
  68.    return(getreply(ctrl_skt,szBuf));
  69. }
  70.  
  71. // return a string pointer to ON or OFF based on the flag
  72. char *onoff(BOOL flag)
  73. {
  74.   if(flag) return("ON"); else return("OFF");
  75. }
  76.  
  77. // process CWD
  78. int DoCWD(SOCKET ctrl_skt,LPSTR path)
  79. {
  80.   if(command(ctrl_skt,"CWD %s",path)==FTP_ERROR && iCode==500) {
  81.     command(ctrl_skt,"XCWD %s",path);
  82.   }
  83.   return(iCode/100);
  84. }
  85.  
  86. // proces PWD
  87. int DoPWD(SOCKET ctrl_skt)
  88. {
  89.   if(command(ctrl_skt,"PWD")==FTP_ERROR && iCode==500) {
  90.     command(ctrl_skt,"XPWD");
  91.   }
  92.   return(iCode/100);
  93. }
  94.  
  95. // process MKD
  96. int DoMKD(SOCKET ctrl_skt,LPSTR pathname)
  97. {
  98.   if(command(ctrl_skt,"MKD %s",pathname)==FTP_ERROR && iCode==500) {
  99.     command(ctrl_skt,"XMKD %s",pathname);
  100.   }
  101.   return(iCode/100);
  102. }
  103.  
  104. // process RMD
  105. int DoRMD(SOCKET ctrl_skt,LPSTR pathname)
  106. {
  107.   if(command(ctrl_skt,"RMD %s",pathname)==FTP_ERROR && iCode==500)
  108.     command(ctrl_skt,"XRMD %s",pathname);
  109.   return(iCode/100);
  110. }
  111.  
  112. // process DELE
  113. int DoDELE(SOCKET ctrl_skt,LPSTR pathname)
  114. {
  115.   command(ctrl_skt,"DELE %s",pathname);
  116.   return(iCode/100);
  117. }
  118.  
  119. // process user command
  120. int DoQUOTE(SOCKET ctrl_skt,LPSTR string)
  121. {
  122.   if(strncmp(string,"LIST",4)==0 ||
  123.      strncmp(string,"NLST",4)==0)
  124.     DoDirList(ctrl_skt,string);
  125.   else
  126.     command(ctrl_skt,string);
  127.   return(iCode/100);
  128. }
  129.  
  130. // process chmod
  131. int DoCHMOD(SOCKET ctrl_skt,LPSTR modes,LPSTR filename)
  132. {
  133.   return(command(ctrl_skt,"SITE CHMOD %s %s",modes,filename));
  134. }
  135.  
  136. extern BOOL bHELP;
  137.  
  138. // initial connection
  139. SOCKET DoConnect(LPSTR host)
  140. {
  141.   int iLength,iRetCode;
  142.   int iFlag=1;
  143.   SOCKET ctrl_skt;
  144.  
  145.   if(bConnected) {
  146.     DoAddLine("Already connected!");
  147.     return(INVALID_SOCKET);
  148.   }
  149.   // let other routines know that we are busy
  150.   bCmdInProgress++;
  151.  
  152.   bHELP=FALSE;
  153.  
  154.   // create a connected socket
  155.   if((ctrl_skt=connectTCP(host,"ftp"))==INVALID_SOCKET) {
  156.     // DoAddLine("connection failed");
  157.     bCmdInProgress--;
  158.     return(INVALID_SOCKET);
  159.   }
  160.   // get information about local end of the connection
  161.   iLength = sizeof (saCtrlAddr);
  162.   if (getsockname(ctrl_skt,(struct sockaddr *)&saCtrlAddr, &iLength)
  163.       ==SOCKET_ERROR)
  164.   {
  165.     ReportWSError("getsockname",WSAGetLastError());
  166.     bCmdInProgress--;
  167.     DoClose(ctrl_skt);
  168.     return(INVALID_SOCKET);
  169.   }
  170.   // show remote end address
  171.   DoPrintf("[%u] from %s port %u",ctrl_skt,
  172.            inet_ntoa(saCtrlAddr.sin_addr),
  173.            ntohs(saCtrlAddr.sin_port));
  174.   // get initial message from remote end
  175.   while((iRetCode=ReadDisplayLine(ctrl_skt))==FTP_PRELIM)
  176.     if(strstr(szMsgBuf,"(EXOS")!=NULL && nHostType==0)
  177.       nHostType=DLG_HOST_U5000-6000;
  178.   // if it succeeded
  179.   if(iRetCode==FTP_COMPLETE) {
  180.     if (setsockopt(ctrl_skt, SOL_SOCKET, SO_OOBINLINE,
  181.         (LPSTR)&iFlag, sizeof(iFlag))==SOCKET_ERROR)
  182.     {
  183.       ReportWSError("setsockopt",WSAGetLastError());
  184.     }
  185.     // have to reset this so "command" will work
  186.     bCmdInProgress--;
  187.     // send our userid
  188.     if((iRetCode=command(ctrl_skt,"USER %s",szUserID))==FTP_CONTINUE)
  189.     {
  190.       // if the remote system requires a password, send it.
  191.       iRetCode=command(ctrl_skt,"PASS %s",szPassWord);
  192.     }
  193.     // if we are successfully logged on,.....
  194.     if(iRetCode!=FTP_COMPLETE)
  195.     {
  196.       DoAddLine("logon failure, so quitting");
  197.       DoClose((SOCKET)ctrl_skt);
  198.       return(INVALID_SOCKET);
  199.     }
  200.     bConnected=1;
  201.   } else {
  202.     DoPrintf("unk open msg \"%s\" %u",szMsgBuf,iCode);
  203.     // allow other processes to work
  204.     bCmdInProgress--;
  205.     DoClose((SOCKET)ctrl_skt);
  206.     return(INVALID_SOCKET);
  207.   }
  208.   wsprintf(szString,"WS_FTP %s",szRemoteHost);
  209.   SetWindowText(hWndMain,szString);
  210.   return (ctrl_skt);
  211. }
  212.  
  213. int DoDirList(SOCKET ctrl_skt,LPSTR szCMD)
  214. {
  215.   int nRC,nBell;
  216.   nBell=bBell; bBell=0;
  217.   nRC=RetrieveFile(ctrl_skt,szCMD,szTmpFile,TYPE_A);
  218.   bBell=nBell;
  219.   return(nRC);
  220. }
  221.  
  222. int SendFile(SOCKET ctrl_skt,LPSTR szCMD,LPSTR localfile,char stype)
  223. {
  224.   int iRetCode;
  225.   int iLength;
  226.  
  227.   iCode=0;
  228.   // if we don't have a valid control socket, can't do anything
  229.   if(ctrl_skt==INVALID_SOCKET) {
  230.     DoAddLine("no ctrl_skt, ignored");
  231.     return(0);
  232.   }
  233.   // if we are doing something, don't try to do this
  234.   if(bCmdInProgress) {
  235.     DoAddLine("command in process, ignored");
  236.     return(0);
  237.   }
  238.   // if the requested type is not the same as the default type
  239.   if(cType!=stype) {
  240.     if(stype==TYPE_L)
  241.       command(ctrl_skt,"TYPE L 8");
  242.     else
  243.       command(ctrl_skt,"TYPE %c",stype);
  244.     cType=stype;
  245.   }
  246.   // create a listener socket, if it is successful
  247.   if((listen_socket=GetFTPListenSocket(ctrl_skt))!=INVALID_SOCKET) {
  248.     // send command to see the result of this all
  249.     iRetCode=command((SOCKET)ctrl_skt,szCMD);
  250.     // read the control channel (should return 1xx if it worked)
  251.     if(iRetCode==FTP_PRELIM) {
  252.       // wait for connection back to us on the listen socket
  253.       SetTimer(hWndMain,10,uiTimeOut*1000,NULL);
  254.       // get our data connection
  255.       iLength=sizeof(saSockAddr1);
  256.       data_socket=accept(listen_socket,(struct sockaddr far *)&saSockAddr1,
  257.                          (int far *)&iLength);
  258.       // turn off the timeout timer
  259.       KillTimer(hWndMain,10);
  260.       // if it failed, we have to quit this
  261.       if(data_socket==INVALID_SOCKET) {
  262.         ReportWSError("accept",WSAGetLastError());
  263.         listen_socket=DoClose(listen_socket);
  264.         iRetCode=0;
  265.       } else {
  266.         // we don't need the listener socket anymore
  267.         listen_socket=DoClose(listen_socket);
  268.         // inform user of the connection
  269.         DoPrintf("[%u] accept from %s port %u", data_socket,
  270.           inet_ntoa(saSockAddr1.sin_addr),ntohs(saSockAddr1.sin_port));
  271.         // copy the file
  272.         iRetCode=SendMass(data_socket,localfile,stype==TYPE_I);
  273.         // close the socket
  274.         data_socket=DoClose(data_socket);
  275.         // read the close control message (should return 2xx)
  276.         iRetCode=ReadDisplayLine(ctrl_skt);
  277.       }
  278.     } else {
  279.       listen_socket=DoClose(listen_socket);
  280.       iRetCode=0;
  281.       if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  282.     }
  283.   } else {
  284.     listen_socket=DoClose(listen_socket);
  285.     iRetCode=0;
  286.     if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  287.   }
  288.   return(iRetCode);
  289. }
  290.  
  291. int RetrieveFile(SOCKET ctrl_skt,LPSTR szCMD,LPSTR localfile,char rtype)
  292. {
  293.   int iRetCode;
  294.   int iLength;
  295.  
  296.   iCode=0;
  297.   // if we don't have a valid control socket, can't do anything
  298.   if(ctrl_skt==INVALID_SOCKET) {
  299.     DoAddLine("no ctrl_skt, ignored");
  300.     return(0);
  301.   }
  302.   // if we are doing something, don't try to do this
  303.   if(bCmdInProgress) {
  304.     DoAddLine("command in process, ignored");
  305.     return(0);
  306.   }
  307.   // if the requested type is not the same as the default type
  308.   if(cType!=rtype) {
  309.     if(rtype==TYPE_L)
  310.       command(ctrl_skt,"TYPE L 8");
  311.     else
  312.       command(ctrl_skt,"TYPE %c",rtype);
  313.     cType=rtype;
  314.   }
  315.   // create a listener socket, if it is successful
  316.   if((listen_socket=GetFTPListenSocket(ctrl_skt))!=INVALID_SOCKET) {
  317.     // send command to see the result of this all
  318.     iRetCode=command((SOCKET)ctrl_skt,szCMD);
  319.     // read the control channel (should return 1xx if it worked)
  320.     if(iRetCode==FTP_PRELIM) {
  321.       // wait for connection back to us on the listen socket
  322.       SetTimer(hWndMain,10,uiTimeOut*1000,NULL);
  323.       // get our data connection
  324.       iLength=sizeof(saSockAddr1);
  325.       data_socket=accept(listen_socket,(struct sockaddr far *)&saSockAddr1,
  326.                          (int far *)&iLength);
  327.       // turn off the timeout timer
  328.       KillTimer(hWndMain,10);
  329.       // if it failed, we have to quit this
  330.       if(data_socket==INVALID_SOCKET) {
  331.         ReportWSError("accept",WSAGetLastError());
  332.         listen_socket=DoClose(listen_socket);
  333.         iRetCode=0;
  334.       } else {
  335.         // we don't need the listener socket anymore
  336.         listen_socket=DoClose(listen_socket);
  337.         // inform user of the connection
  338.         DoPrintf("[%u] accept from %s port %u", data_socket,
  339.           inet_ntoa(saSockAddr1.sin_addr),ntohs(saSockAddr1.sin_port));
  340.         // copy the file
  341.         iRetCode=ReadMass(data_socket,localfile,rtype==TYPE_I);
  342.         // shut the data socket down
  343.         if(shutdown(data_socket,2)!=0)
  344.           ReportWSError("shutdown",WSAGetLastError());
  345.         // close the data socket
  346.         data_socket=DoClose(data_socket);
  347.         // read the close control message (should return 2xx)
  348.         iRetCode=ReadDisplayLine(ctrl_skt);
  349.       }
  350.     } else {
  351.       listen_socket=DoClose(listen_socket);
  352.       iRetCode=0;
  353.       if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  354.     }
  355.  
  356.   } else {
  357.     listen_socket=DoClose(listen_socket);
  358.     iRetCode=0;
  359.     if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  360.   }
  361.   return(iRetCode);
  362. }
  363.  
  364. // user close routine
  365. SOCKET DoClose(SOCKET sockfd)
  366. {
  367.   LINGER linger;
  368.  
  369.   if(sockfd!=INVALID_SOCKET) {
  370.     if(WSAIsBlocking()) {
  371.       DoPrintf("[%u] Cancelled blocking call",sockfd);
  372.       WSACancelBlockingCall();
  373.       bAborted=TRUE;
  374.     }
  375. /*
  376.     if(shutdown(sockfd,2)==SOCKET_ERROR)
  377.       ReportWSError("shutdown",WSAGetLastError());
  378. */
  379.     linger.l_onoff  = TRUE;
  380.     linger.l_linger = 0;
  381.     setsockopt(sockfd,SOL_SOCKET,SO_LINGER,
  382.                 (LPSTR)&linger,sizeof(linger) );
  383.     if(closesocket(sockfd)==SOCKET_ERROR)
  384.       ReportWSError("closesocket",WSAGetLastError());
  385.     else {
  386.       DoPrintf("[%u] Socket closed.",sockfd);
  387.       sockfd=INVALID_SOCKET;
  388.     }
  389.   }
  390.  
  391.   if(sockfd!=INVALID_SOCKET)
  392.     DoPrintf("[%u] Failed to close socket.",sockfd);
  393.  
  394.   return(sockfd);
  395. }
  396.  
  397. int SendPacket(SOCKET sockfd,LPSTR msg)
  398. {
  399.   int i;
  400.  
  401.   if(sockfd==INVALID_SOCKET) return(-1);
  402.   if(bCmdInProgress) {
  403.     DoAddLine("command in progress, ignored");
  404.     return (-1);
  405.   }
  406.   bCmdInProgress++;
  407.   i=strlen(msg);
  408.   strcpy(szSendPkt,msg);
  409.   // append a CRLF to the end of outgoing messages
  410.   szSendPkt[i++]='\r';
  411.   szSendPkt[i++]='\n';
  412.   szSendPkt[i]=0;  
  413.   i=sendstr(sockfd,szSendPkt,i);
  414.   bCmdInProgress--;
  415.   return (i);
  416. }
  417.  
  418. int iMultiLine=0;
  419. // read a reply (may be multi line) and display in debug window
  420. int ReadDisplayLine(SOCKET sockfd)
  421. {
  422.   int iRetCode;
  423.   int iContinue;
  424.   char *s;
  425.   char c;
  426.  
  427.   // can't do anything if we don't have a socket
  428.   if(sockfd==INVALID_SOCKET) return(0);
  429.   // let other routine know that we are doing something right now.
  430.   bCmdInProgress++;
  431.   // count the lines in the response
  432.   iMultiLine++;
  433.   // initialize some variables
  434.   iContinue=0;
  435.   // go read the line
  436.   iRetCode=ReadLine(sockfd);
  437.   // if it wasn't a valid value or the 4th char was a hyphen
  438.   if(iRetCode<100 || iRetCode>599 || szMsgBuf[3]=='-')
  439.     // then it is a continuation line
  440.     iContinue=1;
  441.   // send the line we read to our user/debug window
  442.   DoAddLine((LPSTR)&szMsgBuf[0]);
  443.   //  DoPrintf("iRetCode=%u, iContinue=%u",iRetCode,iContinue);
  444.   // if the timer killed it
  445.   if(bAborted) { iCode=iRetCode=421; iContinue=0; }
  446.   // we only want to set the real return code in certain situations
  447.   if((iMultiLine==1 || iCode==0) && iRetCode>99 && iRetCode<600)
  448.     iCode=iRetCode;
  449.   // handle continuation lines
  450.   if(iContinue==1 || (iCode>0 && iMultiLine>1 && iRetCode!=iCode))
  451.     ReadDisplayLine(sockfd);
  452.   // count back down our multiline reponses
  453.   iMultiLine--;
  454.   // allow other processes to run
  455.   bCmdInProgress--;
  456.   // return only the first char of return code
  457.   if(iCode>99 && iCode<600)
  458.     return (iCode/100);
  459.   else return 0;
  460. }
  461.  
  462. // read a reply line back in from the sockfd and return the
  463. // value at the beginning of the first line.
  464. int ReadLine(SOCKET sockfd)
  465. {
  466.   LPSTR szBuf;
  467.   int nIndex;
  468.   int iNumBytes,iN1,iN2,iN3;
  469.   int iBytesRead;
  470.   int iRetCode;
  471.   int i;
  472.   char *s;
  473.   char c;
  474.  
  475.   // can't do anything if we don't have a socket
  476.   if(sockfd==INVALID_SOCKET) return(0);
  477.   // let other routines know that we are doing something right now.
  478.   bCmdInProgress++;
  479.   // make sure we don't mistakenly think we timed out
  480.   KillTimer(hWndMain,10); bAborted=FALSE;
  481.   // zero our receive buffer
  482.   memset(szMsgBuf,0,4096);
  483.   // initialize some variables
  484.   szBuf=szMsgBuf; iBytesRead=0; iRetCode=0;
  485.   // set our timeout
  486.   SetTimer(hWndMain,10,uiTimeOut*1000,NULL);
  487.   // this routine is a little better as it read 80 characters at a time
  488.   // (if it works:-)  Here we PEEK at what is available, find the LF etc...
  489.   while(iBytesRead<4000 && (iNumBytes=recv(sockfd,(LPSTR)szBuf,82,MSG_PEEK))>0)
  490.   {
  491.     // Trumpet WinSock Alpha 15 always returns the len (82) from a recv
  492.     // with MSG_PEEK.  I suppose this is an error??? The spec doesn't say
  493.     // that MSG_PEEK returns something different than normal.
  494.     KillTimer(hWndMain,10);
  495.     iN1=iNumBytes;
  496.     // must terminate the string so strchr doesn't go wild.
  497.     szBuf[iNumBytes]=0;
  498.     // find a LF in the input if it exists
  499.     //
  500.     for(nIndex=0;nIndex<iNumBytes;nIndex++)
  501.       if(szBuf[nIndex]==0 || szBuf[nIndex]==0x0a) {
  502.         iNumBytes=nIndex+1;
  503.         break;
  504.       }
  505.     // have to treat the UNISYS 5000 (EXOS driver) special.  It sends a
  506.     // line with a CR at end and no LF and then follows it up with a
  507.     // separate packet that has a CR/LF.  Usually this second packet is
  508.     // not there when we peek but is when we read (answers my question
  509.     // about the second receive containing new data!!... jaj 931024)
  510.     if(iNumBytes>80 && nHostType==(DLG_HOST_U5000-6000))
  511.       for(nIndex=0;nIndex<iNumBytes;nIndex++)
  512.         if(szBuf[nIndex]==0x0d) {
  513.           iNumBytes=nIndex+2;
  514.           break;
  515.         }
  516.     iN2=iNumBytes;
  517.     // otherwise read up to the full length of what the first recv saw.
  518.     // Wonder what happens here if the second receive actually returns more
  519.     // characters than the first receive and there was a LF in the extra data?   
  520.     iNumBytes=recv(sockfd,(LPSTR)szBuf,iNumBytes,0);
  521.     // again, terminate the string
  522.     szBuf[iNumBytes]=0;
  523.     DoPrintf("[%u] readline %u - %u - %u %s",sockfd,iN1,iN2,iNumBytes,szBuf);
  524.     // bump the receive buffer pointer
  525.     szBuf+=iNumBytes;
  526.     // count the bytes that we have read so far
  527.     iBytesRead+=iNumBytes;
  528.     // if the last character read was a LF, then stop.
  529.     if(*(szBuf-1)==0x0a)
  530.       break;         // '\n') break;
  531.     // otherwise reset the timer and go read more characters
  532.     SetTimer(hWndMain,10,uiTimeOut*1000,NULL);
  533.   }
  534.   // if we are here, we have a line or an error or there was nothing to read
  535.   KillTimer(hWndMain,10);
  536.   // in any case terminate what we have
  537.   *szBuf=0;
  538.   // find the retcode at the beginning of the line
  539.   c=szMsgBuf[3]; szMsgBuf[3]=0; iRetCode=atoi(szMsgBuf); szMsgBuf[3]=c;
  540.   // if the timer killed it
  541.   if(bAborted) iRetCode=421;
  542.   // strip trailing blanks, CR's and LF's
  543.   while((i=strlen(szMsgBuf))>2 &&
  544.         (szMsgBuf[i-1]==0x0a || szMsgBuf[i-1]==0x0d || szMsgBuf[i-1]==' '))
  545.     szMsgBuf[i-1]=0;
  546.   // unmark our progress
  547.   bCmdInProgress--;
  548.  
  549.   return(iRetCode);
  550. }
  551.  
  552. // based on WINTEL (ftp.c) and BSD (ftp.c)
  553. SOCKET GetFTPListenSocket(SOCKET ctrl_skt)
  554. {
  555.     SOCKET listen_skt;
  556.     int iLength;
  557.     int iRetCode;
  558.     char *a,*p;
  559.     int iFlag=1;
  560.  
  561.     // create a data socket
  562.     if((listen_skt=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
  563.     {
  564.         ReportWSError("socket create",WSAGetLastError());
  565.         return (INVALID_SOCKET);
  566.     }
  567.     // let system pick an unused port. we tell remote end with PORT cmd
  568.     DoPrintf("[%u] going to listen %s port %u",listen_skt,
  569.       inet_ntoa(saCtrlAddr.sin_addr),ntohs(saCtrlAddr.sin_port));
  570.  
  571.     if(bSendPort) {
  572.       saCtrlAddr.sin_port=htons(0);
  573.       saCtrlAddr.sin_family=AF_INET;
  574.       saCtrlAddr.sin_addr.s_addr=0;
  575.     } else
  576.       // otherwise we attempt to reuse our ctrl_skt
  577.       if(setsockopt(listen_skt,SOL_SOCKET,SO_REUSEADDR,
  578.          (char *)&iFlag,sizeof(iFlag))==SOCKET_ERROR)
  579.       {
  580.         ReportWSError("setsockopt",WSAGetLastError());
  581.         closesocket(listen_skt);
  582.         return(INVALID_SOCKET);
  583.       }
  584.     //  Bind name to socket
  585.     if( bind((SOCKET)listen_skt,(LPSOCKADDR)&saCtrlAddr,
  586.              (int)sizeof(struct sockaddr))==SOCKET_ERROR)
  587.       {
  588.         ReportWSError("bind",WSAGetLastError());
  589.         closesocket(listen_skt);
  590.         return (INVALID_SOCKET);
  591.     }
  592.     // get the port name that we got for later transmission in PORT cmd
  593.     iLength=sizeof(saCtrlAddr);
  594.     if(getsockname(listen_skt,(struct sockaddr *)&saCtrlAddr,&iLength)<0)
  595.     {
  596.       ReportWSError("getsockname",WSAGetLastError());
  597.       closesocket(listen_skt);
  598.       return(INVALID_SOCKET);
  599.     }
  600.     // invoke listener
  601.     if(listen(listen_skt,1)!=0)
  602.     {
  603.       ReportWSError("listen",WSAGetLastError());
  604.        closesocket(listen_skt);
  605.       return(INVALID_SOCKET);
  606.     }
  607.  
  608. // inform remote end about our port that we created.
  609.     if(bSendPort)
  610.     {
  611.       struct sockaddr_in saTmpAddr;
  612.       int iLength;
  613.  
  614.       iLength = sizeof (saTmpAddr);
  615.       if (getsockname(ctrl_skt,(LPSOCKADDR)&saTmpAddr, &iLength)
  616.         ==SOCKET_ERROR)
  617.       {
  618.         ReportWSError("getsockname",WSAGetLastError());
  619.       }
  620.  
  621.       a = (char *)&saTmpAddr.sin_addr;
  622.       p = (char *)&saCtrlAddr.sin_port;
  623. #define  UC(b)  (((int)b)&0xff)
  624.       if((iRetCode=command(ctrl_skt,"PORT %d,%d,%d,%d,%d,%d",
  625.           UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
  626.           UC(p[0]), UC(p[1])))!=FTP_COMPLETE) {
  627.         DoPrintf("[%u] remote end didn't understand our port command.",listen_skt);
  628.         return(listen_skt);
  629.       }
  630.     }
  631.     DoPrintf("[%u] listener %s port %u",listen_skt,
  632.       inet_ntoa(saCtrlAddr.sin_addr),ntohs(saCtrlAddr.sin_port));
  633.     return(listen_skt);
  634. }
  635.  
  636. // send a file through the data socket
  637. int SendMass(SOCKET sockfd,LPSTR filename,BOOL binaryflag)
  638. {
  639.   int iNumBytes;
  640.   int  iRetCode;
  641.   int  iFileHandle;
  642.   long lBytesWritten;
  643.   time_t ttStart,ttStop,ttDiff;
  644.  
  645.   iRetCode=0;
  646.   // if we don't have a socket, return an error  
  647.   if(sockfd==INVALID_SOCKET || !(bConnected)) return 0;
  648.   // turn on a flag so other routines know we have a command in progress
  649.   bCmdInProgress++;
  650.   // initialize some vars
  651.   lBytesWritten=0l; iRetCode=0; 
  652.   // at the moment we are ignoring the fact that the local destination file
  653.   // may not open correctly.
  654.   if((iFileHandle=_lopen(filename,READ))== -1) {
  655.     DoPrintf("failed to open file %s (%u)",filename,errno);
  656.     if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  657.   } else {
  658.     // get the start time
  659.     ttStart=time(NULL);
  660.     // loop to send output to remote end
  661.     while((iNumBytes=_lread(iFileHandle,szMsgBuf,512))>0)
  662.     {
  663.       // this forces binary mode at the moment
  664.       iRetCode=sendstr(sockfd,szMsgBuf,iNumBytes);
  665.       // count the characters that we received
  666.       lBytesWritten+=iNumBytes;
  667.  
  668.       wsprintf(szString,"%lu",lBytesWritten);
  669.       SendMessage(hTxtLBytes,WM_SETTEXT,0,(LPARAM)szString);
  670.     }
  671.     // get the finish time
  672.     ttStop=time(NULL);
  673.     // if the output file is open, close it
  674.     _lclose(iFileHandle);
  675.     // show the user how we did
  676.     SendMessage(hTxtLBytes,WM_SETTEXT,0,(LPARAM)NULL);
  677.     ttDiff=ttStop-ttStart;
  678.     if(ttDiff==0l) ttDiff=1l;
  679.     DoPrintf("Transmitted %ld bytes in %ld secs, %ld bps, transfer %s",
  680.       lBytesWritten,(long)ttDiff,
  681.       lBytesWritten*8l/(long)(ttDiff),
  682.       (iFileHandle==-1)?"failed":"succeeded");
  683.     iRetCode=TRUE;
  684.     if(bBell) MessageBeep(MB_OK);
  685.   }
  686.   // turn off our command in progress flag
  687.   bCmdInProgress--;
  688.  
  689.   return (iRetCode);
  690. }
  691.  
  692. // read information from the data socket into a file.  
  693. int ReadMass(SOCKET sockfd,LPSTR filename,BOOL binaryflag)
  694. {
  695.   int  iNumBytes;
  696.   int  iRetCode;
  697.   int  iFileHandle;
  698.   long lBytesRead;
  699.   time_t ttStart,ttStop,ttDiff;
  700.  
  701.   // if we don't have a socket, return an error  
  702.   if(sockfd==INVALID_SOCKET || !(bConnected)) return 0;
  703.   // turn on a flag so other routines know we have a command in progress
  704.   bCmdInProgress++;
  705.   // make sure we don't mistakenly think we timed out
  706.   KillTimer(hWndMain,10); bAborted=FALSE;
  707.   // initialize some vars
  708.   lBytesRead=0l; iRetCode=0; 
  709.   // at the moment we are ignoring the fact that the local destination file
  710.   // may not open correctly.
  711.   if((iFileHandle=_lcreat(filename,0))== -1)
  712.     DoPrintf("failed to open file %s (%u)",filename,errno);
  713.   // get the start time
  714.   ttStart=time(NULL);
  715.   // loop to receive input from remote end
  716.   while(!bAborted && (iNumBytes=recv(sockfd,(LPSTR)szMsgBuf,4000,0))>0)
  717.   {
  718.     // write what we received if the file is open
  719.     if(iFileHandle!= -1)
  720.       _lwrite(iFileHandle,szMsgBuf,iNumBytes);
  721.     // count the characters that we received
  722.     lBytesRead+=iNumBytes;
  723.     // update screen
  724.     wsprintf(szString,"%lu",lBytesRead);
  725.     SendMessage(hTxtRBytes,WM_SETTEXT,0,(LPARAM)szString);
  726.   }
  727.   // get the finish time
  728.   ttStop=time(NULL);
  729.   // if the output file is open, close it
  730.   if(iFileHandle != -1)  _lclose(iFileHandle);
  731.   // if we had a recv error, let us know about it
  732.   if(iNumBytes==SOCKET_ERROR)
  733.   {
  734.     ReportWSError("recv",WSAGetLastError());
  735.     if(lBytesRead==0l)
  736.     {
  737.       if(bBell) MessageBeep(MB_ICONEXCLAMATION);
  738.       bCmdInProgress--;
  739.       return(FALSE);
  740.     }
  741.   }
  742.   SendMessage(hTxtRBytes,WM_SETTEXT,0,(LPARAM)NULL);
  743.  
  744.   ttDiff=ttStop-ttStart;
  745.   if(ttDiff==0l) ttDiff=1l;
  746.   // show the user how we did
  747.   DoPrintf("Received %ld bytes in %ld secs, %ld bps, transfer %s",
  748.     lBytesRead,(long)ttDiff,
  749.     lBytesRead*8l/(long)ttDiff,
  750.     (iFileHandle==-1 || bAborted)?"failed":"succeeded");
  751.  
  752.   if(bBell) MessageBeep(MB_OK);
  753.   // turn off our command in progress flag
  754.   bCmdInProgress--;
  755.  
  756.   return (TRUE);
  757. }
  758.  
  759.