FS2_Open
Open source remastering of the Freespace 2 engine
cftp.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include <stdio.h>
13 
14 #include "globalincs/pstypes.h"
15 
16 #ifdef WIN32
17 #include <windows.h>
18 #include <process.h>
19 #else
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <sys/select.h>
25 #include <errno.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 
29 #define WSAGetLastError() (errno)
30 #endif
31 
32 #include "inetfile/cftp.h"
33 
34 
35 #ifdef WIN32
36 void FTPObjThread( void * obj )
37 #else
38 int FTPObjThread( void *obj )
39 #endif
40 {
41  ((CFtpGet *)obj)->WorkerThread();
42 
43 #ifdef SCP_UNIX
44  return 0;
45 #endif
46 }
47 
49 {
50  m_Aborting = true;
51 
52  while(!m_Aborted) ; //Wait for the thread to end
53 
54  fclose(LOCALFILE);
55 }
56 
57 CFtpGet::CFtpGet(char *URL, char *localfile, char *Username, char *Password)
58 {
59  SOCKADDR_IN listensockaddr;
61 
65  m_iBytesIn = 0;
66  m_iBytesTotal = 0;
67  m_Aborting = false;
68  m_Aborted = false;
69 #ifdef SCP_UNIX
70  thread_id = NULL;
71 #endif
72 
73  LOCALFILE = fopen(localfile, "wb");
74 
75  if (NULL == LOCALFILE) {
77  return;
78  }
79 
80  if(Username)
81  {
82  strcpy_s(m_szUserName,Username);
83  }
84  else
85  {
86  strcpy_s(m_szUserName,"anonymous");
87  }
88  if(Password)
89  {
90  strcpy_s(m_szPassword,Password);
91  }
92  else
93  {
94  strcpy_s(m_szPassword,"pxouser@pxo.net");
95  }
96  m_ListenSock = socket(AF_INET, SOCK_STREAM, 0);
98  {
99  // vint iWinsockErr = WSAGetLastError();
101  return;
102  }
103  else
104  {
105  memset(&listensockaddr, 0, sizeof(SOCKADDR_IN));
106  listensockaddr.sin_family = AF_INET;
107  listensockaddr.sin_port = 0;
108  listensockaddr.sin_addr.s_addr = INADDR_ANY;
109 
110  // Bind the listen socket
111  if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR)))
112  {
113  //Couldn't bind the socket
114  // int iWinsockErr = WSAGetLastError();
116  return;
117  }
118 
119  // Listen for the server connection
120  if (listen(m_ListenSock, 1))
121  {
122  //Couldn't listen on the socket
123  // int iWinsockErr = WSAGetLastError();
125  return;
126  }
127  }
128  m_ControlSock = socket(AF_INET, SOCK_STREAM, 0);
130  {
132  return;
133  }
134  //Parse the URL
135  //Get rid of any extra ftp:// stuff
136  char *pURL = URL;
137  if(_strnicmp(URL,"ftp:",4)==0)
138  {
139  pURL +=4;
140  while(*pURL == '/')
141  {
142  pURL++;
143  }
144  }
145  //There shouldn't be any : in this string
146  if(strchr(pURL,':'))
147  {
149  return;
150  }
151  //read the filename by searching backwards for a /
152  //then keep reading until you find the first /
153  //when you found it, you have the host and dir
154  char *filestart = NULL;
155  char *dirstart = NULL;
156  for(int i = strlen(pURL);i>=0;i--)
157  {
158  if(pURL[i]== '/')
159  {
160  if(!filestart)
161  {
162  filestart = pURL+i+1;
163  dirstart = pURL+i+1;
164  strcpy_s(m_szFilename,filestart);
165  }
166  else
167  {
168  dirstart = pURL+i+1;
169  }
170  }
171  }
172  if((dirstart==NULL) || (filestart==NULL))
173  {
175  return;
176  }
177  else
178  {
179  strncpy(m_szDir,dirstart,(filestart-dirstart));
180  m_szDir[(filestart-dirstart)] = '\0';
181  strncpy(m_szHost,pURL,(dirstart-pURL));
182  m_szHost[(dirstart-pURL)-1] = '\0';
183  }
184  //At this point we should have a nice host,dir and filename
185 
186  //if(NULL==CreateThread(NULL,0,ObjThread,this,0,&m_dwThreadId))
187 #ifdef WIN32
188  if ( _beginthread(FTPObjThread,0,this) == NULL )
189 #else
190  if ( (thread_id = SDL_CreateThread(FTPObjThread, this)) == NULL )
191 #endif
192  {
194  return;
195  }
196 
198 }
199 
200 
201 
203 {
204 #ifdef WIN32
205  _endthread();
206 #else
207  if (thread_id)
208  SDL_WaitThread(thread_id, NULL);
209 #endif
210 
211  fclose(LOCALFILE);
212 
214  {
215  shutdown(m_ListenSock,2);
217  }
219  {
220  shutdown(m_DataSock,2);
222  }
224  {
225  shutdown(m_ControlSock,2);
227  }
228 
229 
230 }
231 
232 //Returns a value to specify the status (ie. connecting/connected/transferring/done)
234 {
235  return m_State;
236 }
237 
239 {
240  return m_iBytesIn;
241 }
242 
244 {
245 
246  return m_iBytesTotal;
247 }
248 
249 //This function does all the work -- connects on a blocking socket
250 //then sends the appropriate user and password commands
251 //and then the cwd command, the port command then get and finally the quit
253 {
256  {
257  return;
258  }
259  LoginHost();
261  {
262  return;
263  }
264  GetFile();
265 
266  //We are all done now, and state has the current state.
267  m_Aborted = true;
268 
269 
270 }
271 
273 {
274  //Start off by changing into the proper dir.
275  char szCommandString[200];
276  int rcode;
277 
278  sprintf(szCommandString,"TYPE I\r\n");
279  rcode = SendFTPCommand(szCommandString);
280  if(rcode >=400)
281  {
283  return 0;
284  }
285  if(m_Aborting)
286  return 0;
287  if(m_szDir[0])
288  {
289  sprintf(szCommandString,"CWD %s\r\n",m_szDir);
290  rcode = SendFTPCommand(szCommandString);
291  if(rcode >=400)
292  {
294  return 0;
295  }
296  }
297  if(m_Aborting)
298  return 0;
299  if(!IssuePort())
300  {
302  return 0;
303  }
304  if(m_Aborting)
305  return 0;
306  sprintf(szCommandString,"RETR %s\r\n",m_szFilename);
307  rcode = SendFTPCommand(szCommandString);
308  if(rcode >=400)
309  {
311  return 0;
312  }
313  if(m_Aborting)
314  return 0;
315  //Now we will try to determine the file size...
316  char *p,*s;
317  p = strchr(recv_buffer,'(');
318  p++;
319  if(p)
320  {
321  s = strchr(p,' ');
322  *s = '\0';
323  m_iBytesTotal = atoi(p);
324  }
325  if(m_Aborting)
326  return 0;
327 
328  m_DataSock = accept(m_ListenSock, NULL,NULL);//(SOCKADDR *)&sockaddr,&iAddrLength);
329  // Close the listen socket
331  if (m_DataSock == INVALID_SOCKET)
332  {
333  // int iWinsockErr = WSAGetLastError();
335  return 0;
336  }
337  if(m_Aborting)
338  return 0;
339  ReadDataChannel();
340 
342  return 1;
343 }
344 
346 {
347 
348  char szCommandString[200];
349  SOCKADDR_IN listenaddr; // Socket address structure
350 #ifdef SCP_UNIX
351  socklen_t iLength; // Length of the address structure
352 #else
353  int iLength; // Length of the address structure
354 #endif
355  UINT nLocalPort; // Local port for listening
356  UINT nReplyCode; // FTP server reply code
357 
358 
359  // Get the address for the hListenSocket
360  iLength = sizeof(listenaddr);
361  if (getsockname(m_ListenSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
362  {
363  // int iWinsockErr = WSAGetLastError();
365  return 0;
366  }
367 
368  // Extract the local port from the hListenSocket
369  nLocalPort = listenaddr.sin_port;
370 
371  // Now, reuse the socket address structure to
372  // get the IP address from the control socket.
373  if (getsockname(m_ControlSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
374  {
375  // int iWinsockErr = WSAGetLastError();
377  return 0;
378  }
379 
380  // Format the PORT command with the correct numbers.
381 #ifdef WINDOWS
382  sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
383  listenaddr.sin_addr.S_un.S_un_b.s_b1,
384  listenaddr.sin_addr.S_un.S_un_b.s_b2,
385  listenaddr.sin_addr.S_un.S_un_b.s_b3,
386  listenaddr.sin_addr.S_un.S_un_b.s_b4,
387  nLocalPort & 0xFF,
388  nLocalPort >> 8);
389 #else
390  sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
391  (listenaddr.sin_addr.s_addr & 0xff000000) >> 24,
392  (listenaddr.sin_addr.s_addr & 0x00ff0000) >> 16,
393  (listenaddr.sin_addr.s_addr & 0x0000ff00) >> 8,
394  (listenaddr.sin_addr.s_addr & 0x000000ff),
395  nLocalPort & 0xFF,
396  nLocalPort >> 8);
397 #endif
398 
399  // Tell the server which port to use for data.
400  nReplyCode = SendFTPCommand(szCommandString);
401  if (nReplyCode != 200)
402  {
403  // int iWinsockErr = WSAGetLastError();
405  return 0;
406  }
407  return 1;
408 }
409 
411 {
412  HOSTENT *he;
413  SERVENT *se;
414  SOCKADDR_IN hostaddr;
415  he = gethostbyname(m_szHost);
416  if(he == NULL)
417  {
419  return 0;
420  }
421  //m_ControlSock
422  if(m_Aborting)
423  return 0;
424 
425  memset(&hostaddr, 0, sizeof(SOCKADDR_IN));
426 
427  se = getservbyname("ftp", NULL);
428 
429  if(se == NULL)
430  {
431  hostaddr.sin_port = htons(21);
432  }
433  else
434  {
435  hostaddr.sin_port = se->s_port;
436  }
437  hostaddr.sin_family = AF_INET;
438  memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4);
439  if(m_Aborting)
440  return 0;
441  //Now we will connect to the host
442  if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)))
443  {
444  // int iWinsockErr = WSAGetLastError();
446  return 0;
447  }
449  return 1;
450 }
451 
452 
454 {
455  char szLoginString[200];
456  int rcode;
457 
458  sprintf(szLoginString,"USER %s\r\n",m_szUserName);
459  rcode = SendFTPCommand(szLoginString);
460  if(rcode >=400)
461  {
463  return 0;
464  }
465  sprintf(szLoginString,"PASS %s\r\n",m_szPassword);
466  rcode = SendFTPCommand(szLoginString);
467  if(rcode >=400)
468  {
470  return 0;
471  }
472 
474  return 1;
475 }
476 
477 
479 {
480 
482  // Send the FTP command
483  if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0)))
484  {
485  // int iWinsockErr = WSAGetLastError();
486  // Return 999 to indicate an error has occurred
487  return(999);
488  }
489 
490  // Read the server's reply and return the reply code as an integer
491  return(ReadFTPServerReply());
492 }
493 
494 
495 
497 {
498  uint rcode;
499  int iBytesRead;
500  char chunk[2];
501  char szcode[4];
502  uint igotcrlf = 0;
503  memset(recv_buffer,0,1000);
504  do
505  {
506  chunk[0] = '\0';
507  iBytesRead = recv(m_ControlSock,chunk,1,0);
508 
509  if (iBytesRead == SOCKET_ERROR)
510  {
511  // int iWinsockErr = WSAGetLastError();
512  // Return 999 to indicate an error has occurred
513  return(999);
514  }
515 
516  if((chunk[0]==0x0a) || (chunk[0]==0x0d))
517  {
518  if(recv_buffer[0] != '\0')
519  {
520  igotcrlf = 1;
521  }
522  }
523  else
524  { chunk[1] = '\0';
525  strcat_s(recv_buffer,chunk);
526  }
527 
528  Sleep(1);
529  }while(igotcrlf==0);
530 
531  if(recv_buffer[3] == '-')
532  {
533  //Hack -- must be a MOTD
534  return ReadFTPServerReply();
535  }
536  if(recv_buffer[3] != ' ')
537  {
538  //We should have 3 numbers then a space
539  return 999;
540  }
541  memcpy(szcode,recv_buffer,3);
542  szcode[3] = '\0';
543  rcode = atoi(szcode);
544  // Extract the reply code from the server reply and return as an integer
545  return(rcode);
546 }
547 
548 
550 {
551  char sDataBuffer[4096]; // Data-storage buffer for the data channel
552  int nBytesRecv; // Bytes received from the data channel
554  if(m_Aborting)
555  return 0;
556  do
557  {
558  if(m_Aborting)
559  return 0;
560  nBytesRecv = recv(m_DataSock, (LPSTR)&sDataBuffer,sizeof(sDataBuffer), 0);
561 
562  m_iBytesIn += nBytesRecv;
563  if (nBytesRecv > 0 )
564  {
565  fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
566  //Write sDataBuffer, nBytesRecv
567  }
568 
569  Sleep(1);
570  }while (nBytesRecv > 0);
571  fclose(LOCALFILE);
572  // Close the file and check for error returns.
573  if (nBytesRecv == SOCKET_ERROR)
574  {
575  //Ok, we got a socket error -- xfer aborted?
577  return 0;
578  }
579  else
580  {
581  //done!
583  return 1;
584  }
585 }
586 
587 
589 {
590  fd_set read_fds;
591  TIMEVAL timeout;
592  char flushbuff[3];
593 
594  timeout.tv_sec = 0;
595  timeout.tv_usec = 0;
596 
597  FD_ZERO(&read_fds);
598  FD_SET(m_ControlSock, &read_fds);
599 
600 #ifdef WIN32
601  while ( select(0, &read_fds, NULL, NULL, &timeout) )
602 #else
603  while ( select(m_ControlSock+1, &read_fds, NULL, NULL, &timeout) )
604 #endif
605  {
606  recv(m_ControlSock,flushbuff,1,0);
607 
608  Sleep(1);
609  }
610 }
int i
Definition: multi_pxo.cpp:466
#define FTP_STATE_DIRECTORY_INVALID
Definition: cftp.h:32
int FTPObjThread(void *obj)
Definition: cftp.cpp:38
#define FTP_STATE_CANT_WRITE_FILE
Definition: cftp.h:38
uint m_State
Definition: cftp.h:74
#define FTP_STATE_URL_PARSING_ERROR
Definition: cftp.h:25
bool m_Aborted
Definition: cftp.h:77
char m_szDir[200]
Definition: cftp.h:82
int LoginHost()
Definition: cftp.cpp:453
#define FTP_STATE_CONNECTING
Definition: cftp.h:26
uint ReadFTPServerReply()
Definition: cftp.cpp:496
#define SOCKADDR_IN
Definition: config.h:124
#define SOCKADDR
Definition: config.h:123
#define SOCKET_ERROR
Definition: config.h:138
#define INVALID_SOCKET
Definition: psnet2.h:53
void AbortGet()
Definition: cftp.cpp:48
unsigned int UINT
Definition: config.h:82
uint GetBytesIn()
Definition: cftp.cpp:238
uint GetTotalBytes()
Definition: cftp.cpp:243
void WorkerThread()
Definition: cftp.cpp:252
#define FTP_STATE_SOCKET_ERROR
Definition: cftp.h:24
#define SERVENT
Definition: config.h:127
#define HOSTENT
Definition: config.h:126
char * LPSTR
Definition: config.h:107
unsigned int uint
Definition: pstypes.h:64
SOCKET m_ControlSock
Definition: cftp.h:89
uint IssuePort()
Definition: cftp.cpp:345
#define FTP_STATE_FILE_RECEIVED
Definition: cftp.h:35
uint GetFile()
Definition: cftp.cpp:272
uint ReadDataChannel()
Definition: cftp.cpp:549
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
#define FTP_STATE_LOGGING_IN
Definition: cftp.h:29
GLdouble s
Definition: Glext.h:5321
#define FTP_STATE_STARTUP
Definition: cftp.h:39
int GetStatus()
Definition: cftp.cpp:233
char m_szUserName[100]
Definition: cftp.h:79
timeval TIMEVAL
Definition: config.h:216
void FlushControlChannel()
Definition: cftp.cpp:588
uint m_iBytesIn
Definition: cftp.h:72
GLbitfield GLuint64 timeout
Definition: Glext.h:6725
Definition: cftp.h:48
#define FTP_STATE_FILE_NOT_FOUND
Definition: cftp.h:33
#define FTP_STATE_LOGIN_ERROR
Definition: cftp.h:30
#define _strnicmp(s1, s2, n)
Definition: config.h:273
#define FTP_STATE_UNKNOWN_ERROR
Definition: cftp.h:36
#define FTP_STATE_CANT_CONNECT
Definition: cftp.h:28
#define closesocket(x)
Definition: config.h:128
#define strcat_s(...)
Definition: safe_strings.h:68
#define _endthread()
Definition: config.h:211
SOCKET m_DataSock
Definition: cftp.h:88
char recv_buffer[1000]
Definition: cftp.h:85
CFtpGet(char *URL, char *localfile, char *Username=NULL, char *Password=NULL)
Definition: cftp.cpp:57
#define FTP_STATE_HOST_NOT_FOUND
Definition: cftp.h:27
char m_szPassword[100]
Definition: cftp.h:80
GLfloat GLfloat p
Definition: Glext.h:8373
void Sleep(int mili)
uint m_iBytesTotal
Definition: cftp.h:73
GLsizei GLsizei GLuint * obj
Definition: Glext.h:5619
#define FTP_STATE_INTERNAL_ERROR
Definition: cftp.h:23
int ConnectControlSocket()
Definition: cftp.cpp:410
SOCKET m_ListenSock
Definition: cftp.h:87
bool m_Aborting
Definition: cftp.h:76
#define FTP_STATE_RECV_FAILED
Definition: cftp.h:37
FILE * LOCALFILE
Definition: cftp.h:91
~CFtpGet()
Definition: cftp.cpp:202
#define FTP_STATE_LOGGED_IN
Definition: cftp.h:31
#define LPSOCKADDR
Definition: config.h:125
char m_szFilename[100]
Definition: cftp.h:83
int socklen_t
Definition: tcp_socket.cpp:39
char m_szHost[200]
Definition: cftp.h:81
#define _beginthread(x, y, z)
Definition: config.h:210
uint SendFTPCommand(char *command)
Definition: cftp.cpp:478
#define FTP_STATE_RECEIVING
Definition: cftp.h:34
#define strcpy_s(...)
Definition: safe_strings.h:67