2 * $Logfile: /Freespace2/code/Inetfile/CFtp.cpp $
7 * FTP Client class (get only)
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 3 5/04/99 7:34p Dave
15 * Fixed slow HTTP get problem.
17 * 2 4/20/99 6:39p Dave
18 * Almost done with artillery targeting. Added support for downloading
19 * images on the PXO screen.
21 * 1 4/20/99 4:37p Dave
33 void FTPObjThread( void * obj )
35 ((CFtpGet *)obj)->WorkerThread();
38 void CFtpGet::AbortGet()
41 while(!m_Aborted) ; //Wait for the thread to end
45 CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password)
47 SOCKADDR_IN listensockaddr;
48 m_State = FTP_STATE_STARTUP;
50 m_ListenSock = INVALID_SOCKET;
51 m_DataSock = INVALID_SOCKET;
52 m_ControlSock = INVALID_SOCKET;
58 LOCALFILE = fopen(localfile,"wb");
61 m_State = FTP_STATE_CANT_WRITE_FILE;
67 strcpy(m_szUserName,Username);
71 strcpy(m_szUserName,"anonymous");
75 strcpy(m_szPassword,Password);
79 strcpy(m_szPassword,"pxouser@pxo.net");
81 m_ListenSock = socket(AF_INET, SOCK_STREAM, 0);
82 if(INVALID_SOCKET == m_ListenSock)
84 // vint iWinsockErr = WSAGetLastError();
85 m_State = FTP_STATE_SOCKET_ERROR;
90 listensockaddr.sin_family = AF_INET;
91 listensockaddr.sin_port = 0;
92 listensockaddr.sin_addr.s_addr = INADDR_ANY;
94 // Bind the listen socket
95 if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR)))
97 //Couldn't bind the socket
98 // int iWinsockErr = WSAGetLastError();
99 m_State = FTP_STATE_SOCKET_ERROR;
103 // Listen for the server connection
104 if (listen(m_ListenSock, 1))
106 //Couldn't listen on the socket
107 // int iWinsockErr = WSAGetLastError();
108 m_State = FTP_STATE_SOCKET_ERROR;
112 m_ControlSock = socket(AF_INET, SOCK_STREAM, 0);
113 if(INVALID_SOCKET == m_ControlSock)
115 m_State = FTP_STATE_SOCKET_ERROR;
119 //Get rid of any extra ftp:// stuff
121 if(_strnicmp(URL,"ftp:",4)==0)
129 //There shouldn't be any : in this string
132 m_State = FTP_STATE_URL_PARSING_ERROR;
135 //read the filename by searching backwards for a /
136 //then keep reading until you find the first /
137 //when you found it, you have the host and dir
138 char *filestart = NULL;
139 char *dirstart = NULL;
140 for(int i = strlen(pURL);i>=0;i--)
146 filestart = pURL+i+1;
148 strcpy(m_szFilename,filestart);
156 if((dirstart==NULL) || (filestart==NULL))
158 m_State = FTP_STATE_URL_PARSING_ERROR;
163 strncpy(m_szDir,dirstart,(filestart-dirstart));
164 m_szDir[(filestart-dirstart)] = NULL;
165 strncpy(m_szHost,pURL,(dirstart-pURL));
166 m_szHost[(dirstart-pURL)-1] = NULL;
168 //At this point we should have a nice host,dir and filename
170 //if(NULL==CreateThread(NULL,0,ObjThread,this,0,&m_dwThreadId))
171 if(NULL==_beginthread(FTPObjThread,0,this))
173 m_State = FTP_STATE_INTERNAL_ERROR;
176 m_State = FTP_STATE_CONNECTING;
183 if(m_ListenSock != INVALID_SOCKET)
185 shutdown(m_ListenSock,2);
186 closesocket(m_ListenSock);
188 if(m_DataSock != INVALID_SOCKET)
190 shutdown(m_DataSock,2);
191 closesocket(m_DataSock);
193 if(m_ControlSock != INVALID_SOCKET)
195 shutdown(m_ControlSock,2);
196 closesocket(m_ControlSock);
202 //Returns a value to specify the status (ie. connecting/connected/transferring/done)
203 int CFtpGet::GetStatus()
208 unsigned int CFtpGet::GetBytesIn()
213 unsigned int CFtpGet::GetTotalBytes()
216 return m_iBytesTotal;
219 //This function does all the work -- connects on a blocking socket
220 //then sends the appropriate user and password commands
221 //and then the cwd command, the port command then get and finally the quit
222 void CFtpGet::WorkerThread()
224 ConnectControlSocket();
225 if(m_State != FTP_STATE_LOGGING_IN)
230 if(m_State != FTP_STATE_LOGGED_IN)
236 //We are all done now, and state has the current state.
242 unsigned int CFtpGet::GetFile()
244 //Start off by changing into the proper dir.
245 char szCommandString[200];
248 sprintf(szCommandString,"TYPE I\r\n");
249 rcode = SendFTPCommand(szCommandString);
252 m_State = FTP_STATE_UNKNOWN_ERROR;
259 sprintf(szCommandString,"CWD %s\r\n",m_szDir);
260 rcode = SendFTPCommand(szCommandString);
263 m_State = FTP_STATE_DIRECTORY_INVALID;
271 m_State = FTP_STATE_UNKNOWN_ERROR;
276 sprintf(szCommandString,"RETR %s\r\n",m_szFilename);
277 rcode = SendFTPCommand(szCommandString);
280 m_State = FTP_STATE_FILE_NOT_FOUND;
285 //Now we will try to determine the file size...
287 p = strchr(recv_buffer,'(');
293 m_iBytesTotal = atoi(p);
298 m_DataSock = accept(m_ListenSock, NULL,NULL);//(SOCKADDR *)&sockaddr,&iAddrLength);
299 // Close the listen socket
300 closesocket(m_ListenSock);
301 if (m_DataSock == INVALID_SOCKET)
303 // int iWinsockErr = WSAGetLastError();
304 m_State = FTP_STATE_SOCKET_ERROR;
311 m_State = FTP_STATE_FILE_RECEIVED;
315 unsigned int CFtpGet::IssuePort()
318 char szCommandString[200];
319 SOCKADDR_IN listenaddr; // Socket address structure
320 int iLength; // Length of the address structure
321 UINT nLocalPort; // Local port for listening
322 UINT nReplyCode; // FTP server reply code
325 // Get the address for the hListenSocket
326 iLength = sizeof(listenaddr);
327 if (getsockname(m_ListenSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
329 // int iWinsockErr = WSAGetLastError();
330 m_State = FTP_STATE_SOCKET_ERROR;
334 // Extract the local port from the hListenSocket
335 nLocalPort = listenaddr.sin_port;
337 // Now, reuse the socket address structure to
338 // get the IP address from the control socket.
339 if (getsockname(m_ControlSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
341 // int iWinsockErr = WSAGetLastError();
342 m_State = FTP_STATE_SOCKET_ERROR;
346 // Format the PORT command with the correct numbers.
347 sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
348 listenaddr.sin_addr.S_un.S_un_b.s_b1,
349 listenaddr.sin_addr.S_un.S_un_b.s_b2,
350 listenaddr.sin_addr.S_un.S_un_b.s_b3,
351 listenaddr.sin_addr.S_un.S_un_b.s_b4,
355 // Tell the server which port to use for data.
356 nReplyCode = SendFTPCommand(szCommandString);
357 if (nReplyCode != 200)
359 // int iWinsockErr = WSAGetLastError();
360 m_State = FTP_STATE_SOCKET_ERROR;
366 int CFtpGet::ConnectControlSocket()
370 SOCKADDR_IN hostaddr;
371 he = gethostbyname(m_szHost);
374 m_State = FTP_STATE_HOST_NOT_FOUND;
380 se = getservbyname("ftp", NULL);
384 hostaddr.sin_port = htons(21);
388 hostaddr.sin_port = se->s_port;
390 hostaddr.sin_family = AF_INET;
391 memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4);
394 //Now we will connect to the host
395 if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)))
397 // int iWinsockErr = WSAGetLastError();
398 m_State = FTP_STATE_CANT_CONNECT;
401 m_State = FTP_STATE_LOGGING_IN;
406 int CFtpGet::LoginHost()
408 char szLoginString[200];
411 sprintf(szLoginString,"USER %s\r\n",m_szUserName);
412 rcode = SendFTPCommand(szLoginString);
415 m_State = FTP_STATE_LOGIN_ERROR;
418 sprintf(szLoginString,"PASS %s\r\n",m_szPassword);
419 rcode = SendFTPCommand(szLoginString);
422 m_State = FTP_STATE_LOGIN_ERROR;
426 m_State = FTP_STATE_LOGGED_IN;
431 unsigned int CFtpGet::SendFTPCommand(char *command)
434 FlushControlChannel();
435 // Send the FTP command
436 if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0)))
438 // int iWinsockErr = WSAGetLastError();
439 // Return 999 to indicate an error has occurred
443 // Read the server's reply and return the reply code as an integer
444 return(ReadFTPServerReply());
449 unsigned int CFtpGet::ReadFTPServerReply()
452 unsigned int iBytesRead;
455 unsigned int igotcrlf = 0;
456 memset(recv_buffer,0,1000);
460 iBytesRead = recv(m_ControlSock,chunk,1,0);
462 if (iBytesRead == SOCKET_ERROR)
464 // int iWinsockErr = WSAGetLastError();
465 // Return 999 to indicate an error has occurred
469 if((chunk[0]==0x0a) || (chunk[0]==0x0d))
471 if(recv_buffer[0]!=NULL)
478 strcat(recv_buffer,chunk);
484 if(recv_buffer[3] == '-')
486 //Hack -- must be a MOTD
487 return ReadFTPServerReply();
489 if(recv_buffer[3] != ' ')
491 //We should have 3 numbers then a space
494 memcpy(szcode,recv_buffer,3);
496 rcode = atoi(szcode);
497 // Extract the reply code from the server reply and return as an integer
502 unsigned int CFtpGet::ReadDataChannel()
504 char sDataBuffer[4096]; // Data-storage buffer for the data channel
505 int nBytesRecv; // Bytes received from the data channel
506 m_State = FTP_STATE_RECEIVING;
513 nBytesRecv = recv(m_DataSock, (LPSTR)&sDataBuffer,sizeof(sDataBuffer), 0);
515 m_iBytesIn += nBytesRecv;
518 fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
519 //Write sDataBuffer, nBytesRecv
523 }while (nBytesRecv > 0);
525 // Close the file and check for error returns.
526 if (nBytesRecv == SOCKET_ERROR)
528 //Ok, we got a socket error -- xfer aborted?
529 m_State = FTP_STATE_RECV_FAILED;
535 m_State = FTP_STATE_FILE_RECEIVED;
541 void CFtpGet::FlushControlChannel()
551 FD_SET(m_ControlSock,&read_fds);
553 while(select(0,&read_fds,NULL,NULL,&timeout))
555 recv(m_ControlSock,flushbuff,1,0);