2 * $Logfile: /Freespace2/code/Inetfile/CFtp.cpp $
7 * FTP Client class (get only)
10 * Revision 1.2 2002/05/07 03:16:45 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:09 root
17 * 3 5/04/99 7:34p Dave
18 * Fixed slow HTTP get problem.
20 * 2 4/20/99 6:39p Dave
21 * Almost done with artillery targeting. Added support for downloading
22 * images on the PXO screen.
24 * 1 4/20/99 4:37p Dave
36 void FTPObjThread( void * obj )
38 ((CFtpGet *)obj)->WorkerThread();
41 void CFtpGet::AbortGet()
44 while(!m_Aborted) ; //Wait for the thread to end
48 CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password)
50 SOCKADDR_IN listensockaddr;
51 m_State = FTP_STATE_STARTUP;
53 m_ListenSock = INVALID_SOCKET;
54 m_DataSock = INVALID_SOCKET;
55 m_ControlSock = INVALID_SOCKET;
61 LOCALFILE = fopen(localfile,"wb");
64 m_State = FTP_STATE_CANT_WRITE_FILE;
70 strcpy(m_szUserName,Username);
74 strcpy(m_szUserName,"anonymous");
78 strcpy(m_szPassword,Password);
82 strcpy(m_szPassword,"pxouser@pxo.net");
84 m_ListenSock = socket(AF_INET, SOCK_STREAM, 0);
85 if(INVALID_SOCKET == m_ListenSock)
87 // vint iWinsockErr = WSAGetLastError();
88 m_State = FTP_STATE_SOCKET_ERROR;
93 listensockaddr.sin_family = AF_INET;
94 listensockaddr.sin_port = 0;
95 listensockaddr.sin_addr.s_addr = INADDR_ANY;
97 // Bind the listen socket
98 if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR)))
100 //Couldn't bind the socket
101 // int iWinsockErr = WSAGetLastError();
102 m_State = FTP_STATE_SOCKET_ERROR;
106 // Listen for the server connection
107 if (listen(m_ListenSock, 1))
109 //Couldn't listen on the socket
110 // int iWinsockErr = WSAGetLastError();
111 m_State = FTP_STATE_SOCKET_ERROR;
115 m_ControlSock = socket(AF_INET, SOCK_STREAM, 0);
116 if(INVALID_SOCKET == m_ControlSock)
118 m_State = FTP_STATE_SOCKET_ERROR;
122 //Get rid of any extra ftp:// stuff
124 if(_strnicmp(URL,"ftp:",4)==0)
132 //There shouldn't be any : in this string
135 m_State = FTP_STATE_URL_PARSING_ERROR;
138 //read the filename by searching backwards for a /
139 //then keep reading until you find the first /
140 //when you found it, you have the host and dir
141 char *filestart = NULL;
142 char *dirstart = NULL;
143 for(int i = strlen(pURL);i>=0;i--)
149 filestart = pURL+i+1;
151 strcpy(m_szFilename,filestart);
159 if((dirstart==NULL) || (filestart==NULL))
161 m_State = FTP_STATE_URL_PARSING_ERROR;
166 strncpy(m_szDir,dirstart,(filestart-dirstart));
167 m_szDir[(filestart-dirstart)] = NULL;
168 strncpy(m_szHost,pURL,(dirstart-pURL));
169 m_szHost[(dirstart-pURL)-1] = NULL;
171 //At this point we should have a nice host,dir and filename
173 //if(NULL==CreateThread(NULL,0,ObjThread,this,0,&m_dwThreadId))
174 if(NULL==_beginthread(FTPObjThread,0,this))
176 m_State = FTP_STATE_INTERNAL_ERROR;
179 m_State = FTP_STATE_CONNECTING;
186 if(m_ListenSock != INVALID_SOCKET)
188 shutdown(m_ListenSock,2);
189 closesocket(m_ListenSock);
191 if(m_DataSock != INVALID_SOCKET)
193 shutdown(m_DataSock,2);
194 closesocket(m_DataSock);
196 if(m_ControlSock != INVALID_SOCKET)
198 shutdown(m_ControlSock,2);
199 closesocket(m_ControlSock);
205 //Returns a value to specify the status (ie. connecting/connected/transferring/done)
206 int CFtpGet::GetStatus()
211 unsigned int CFtpGet::GetBytesIn()
216 unsigned int CFtpGet::GetTotalBytes()
219 return m_iBytesTotal;
222 //This function does all the work -- connects on a blocking socket
223 //then sends the appropriate user and password commands
224 //and then the cwd command, the port command then get and finally the quit
225 void CFtpGet::WorkerThread()
227 ConnectControlSocket();
228 if(m_State != FTP_STATE_LOGGING_IN)
233 if(m_State != FTP_STATE_LOGGED_IN)
239 //We are all done now, and state has the current state.
245 unsigned int CFtpGet::GetFile()
247 //Start off by changing into the proper dir.
248 char szCommandString[200];
251 sprintf(szCommandString,"TYPE I\r\n");
252 rcode = SendFTPCommand(szCommandString);
255 m_State = FTP_STATE_UNKNOWN_ERROR;
262 sprintf(szCommandString,"CWD %s\r\n",m_szDir);
263 rcode = SendFTPCommand(szCommandString);
266 m_State = FTP_STATE_DIRECTORY_INVALID;
274 m_State = FTP_STATE_UNKNOWN_ERROR;
279 sprintf(szCommandString,"RETR %s\r\n",m_szFilename);
280 rcode = SendFTPCommand(szCommandString);
283 m_State = FTP_STATE_FILE_NOT_FOUND;
288 //Now we will try to determine the file size...
290 p = strchr(recv_buffer,'(');
296 m_iBytesTotal = atoi(p);
301 m_DataSock = accept(m_ListenSock, NULL,NULL);//(SOCKADDR *)&sockaddr,&iAddrLength);
302 // Close the listen socket
303 closesocket(m_ListenSock);
304 if (m_DataSock == INVALID_SOCKET)
306 // int iWinsockErr = WSAGetLastError();
307 m_State = FTP_STATE_SOCKET_ERROR;
314 m_State = FTP_STATE_FILE_RECEIVED;
318 unsigned int CFtpGet::IssuePort()
321 char szCommandString[200];
322 SOCKADDR_IN listenaddr; // Socket address structure
323 int iLength; // Length of the address structure
324 UINT nLocalPort; // Local port for listening
325 UINT nReplyCode; // FTP server reply code
328 // Get the address for the hListenSocket
329 iLength = sizeof(listenaddr);
330 if (getsockname(m_ListenSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
332 // int iWinsockErr = WSAGetLastError();
333 m_State = FTP_STATE_SOCKET_ERROR;
337 // Extract the local port from the hListenSocket
338 nLocalPort = listenaddr.sin_port;
340 // Now, reuse the socket address structure to
341 // get the IP address from the control socket.
342 if (getsockname(m_ControlSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
344 // int iWinsockErr = WSAGetLastError();
345 m_State = FTP_STATE_SOCKET_ERROR;
349 // Format the PORT command with the correct numbers.
350 sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
351 listenaddr.sin_addr.S_un.S_un_b.s_b1,
352 listenaddr.sin_addr.S_un.S_un_b.s_b2,
353 listenaddr.sin_addr.S_un.S_un_b.s_b3,
354 listenaddr.sin_addr.S_un.S_un_b.s_b4,
358 // Tell the server which port to use for data.
359 nReplyCode = SendFTPCommand(szCommandString);
360 if (nReplyCode != 200)
362 // int iWinsockErr = WSAGetLastError();
363 m_State = FTP_STATE_SOCKET_ERROR;
369 int CFtpGet::ConnectControlSocket()
373 SOCKADDR_IN hostaddr;
374 he = gethostbyname(m_szHost);
377 m_State = FTP_STATE_HOST_NOT_FOUND;
383 se = getservbyname("ftp", NULL);
387 hostaddr.sin_port = htons(21);
391 hostaddr.sin_port = se->s_port;
393 hostaddr.sin_family = AF_INET;
394 memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4);
397 //Now we will connect to the host
398 if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)))
400 // int iWinsockErr = WSAGetLastError();
401 m_State = FTP_STATE_CANT_CONNECT;
404 m_State = FTP_STATE_LOGGING_IN;
409 int CFtpGet::LoginHost()
411 char szLoginString[200];
414 sprintf(szLoginString,"USER %s\r\n",m_szUserName);
415 rcode = SendFTPCommand(szLoginString);
418 m_State = FTP_STATE_LOGIN_ERROR;
421 sprintf(szLoginString,"PASS %s\r\n",m_szPassword);
422 rcode = SendFTPCommand(szLoginString);
425 m_State = FTP_STATE_LOGIN_ERROR;
429 m_State = FTP_STATE_LOGGED_IN;
434 unsigned int CFtpGet::SendFTPCommand(char *command)
437 FlushControlChannel();
438 // Send the FTP command
439 if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0)))
441 // int iWinsockErr = WSAGetLastError();
442 // Return 999 to indicate an error has occurred
446 // Read the server's reply and return the reply code as an integer
447 return(ReadFTPServerReply());
452 unsigned int CFtpGet::ReadFTPServerReply()
455 unsigned int iBytesRead;
458 unsigned int igotcrlf = 0;
459 memset(recv_buffer,0,1000);
463 iBytesRead = recv(m_ControlSock,chunk,1,0);
465 if (iBytesRead == SOCKET_ERROR)
467 // int iWinsockErr = WSAGetLastError();
468 // Return 999 to indicate an error has occurred
472 if((chunk[0]==0x0a) || (chunk[0]==0x0d))
474 if(recv_buffer[0]!=NULL)
481 strcat(recv_buffer,chunk);
487 if(recv_buffer[3] == '-')
489 //Hack -- must be a MOTD
490 return ReadFTPServerReply();
492 if(recv_buffer[3] != ' ')
494 //We should have 3 numbers then a space
497 memcpy(szcode,recv_buffer,3);
499 rcode = atoi(szcode);
500 // Extract the reply code from the server reply and return as an integer
505 unsigned int CFtpGet::ReadDataChannel()
507 char sDataBuffer[4096]; // Data-storage buffer for the data channel
508 int nBytesRecv; // Bytes received from the data channel
509 m_State = FTP_STATE_RECEIVING;
516 nBytesRecv = recv(m_DataSock, (LPSTR)&sDataBuffer,sizeof(sDataBuffer), 0);
518 m_iBytesIn += nBytesRecv;
521 fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
522 //Write sDataBuffer, nBytesRecv
526 }while (nBytesRecv > 0);
528 // Close the file and check for error returns.
529 if (nBytesRecv == SOCKET_ERROR)
531 //Ok, we got a socket error -- xfer aborted?
532 m_State = FTP_STATE_RECV_FAILED;
538 m_State = FTP_STATE_FILE_RECEIVED;
544 void CFtpGet::FlushControlChannel()
554 FD_SET(m_ControlSock,&read_fds);
556 while(select(0,&read_fds,NULL,NULL,&timeout))
558 recv(m_ControlSock,flushbuff,1,0);