FS2_Open
Open source remastering of the Freespace 2 engine
chttpget.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 
13 #include "globalincs/pstypes.h"
14 
15 #ifdef _WIN32
16 #include <windows.h>
17 #include <process.h>
18 #else
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <sys/select.h>
24 #include <errno.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #include <sys/ioctl.h>
28 
29 #define WSAGetLastError() (errno)
30 #endif
31 
32 #include <string.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <errno.h>
37 
38 #include "inetfile/inetgetfile.h"
39 #include "inetfile/chttpget.h"
40 
41 
42 
43 #define NW_AGHBN_CANCEL 1
44 #define NW_AGHBN_LOOKUP 2
45 #define NW_AGHBN_READ 3
46 
47 #ifdef WIN32
48 void __cdecl http_gethostbynameworker(void *parm);
49 #else
50 int http_gethostbynameworker(void *parm);
51 #endif
52 
53 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
54 
55 #ifdef WIN32
56 void HTTPObjThread( void *obj )
57 #else
58 int HTTPObjThread( void *obj )
59 #endif
60 {
61  ((ChttpGet *)obj)->WorkerThread();
62  ((ChttpGet *)obj)->m_Aborted = true;
63  //OutputDebugString("http transfer exiting....\n");
64 
65 #ifdef SCP_UNIX
66  return 0;
67 #endif
68 }
69 
71 {
72 #ifdef WIN32
73  OutputDebugString("Aborting....\n");
74 #endif
75 
76  m_Aborting = true;
77  while(!m_Aborted) Sleep(50); //Wait for the thread to end
78 
79 #ifdef WIN32
80  OutputDebugString("Aborted....\n");
81 #endif
82 }
83 
84 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
85 {
86  m_ProxyEnabled = true;
87  m_ProxyIP = proxyip;
88  m_ProxyPort = proxyport;
89  GetFile(URL,localfile);
90 }
91 
92 ChttpGet::ChttpGet(char *URL,char *localfile)
93 {
94  m_ProxyEnabled = false;
95  GetFile(URL,localfile);
96 }
97 
98 
99 void ChttpGet::GetFile(char *URL,char *localfile)
100 {
102  m_iBytesIn = 0;
103  m_iBytesTotal = 0;
105  m_Aborting = false;
106  m_Aborted = false;
107 #ifdef SCP_UNIX
108  thread_id = NULL;
109 #endif
110 
111  strncpy(m_URL,URL,MAX_URL_LEN-1);
112  m_URL[MAX_URL_LEN-1] = 0;
113 
114  LOCALFILE = fopen(localfile,"wb");
115  if(NULL == LOCALFILE)
116  {
118  return;
119  }
120  m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
122  {
124  return;
125  }
126 
127 // uint arg = 1;
128 // ioctlsocket( m_DataSock, FIONBIO, &arg );
129 
130  char *pURL = URL;
131  if(strnicmp(URL,"http:",5)==0)
132  {
133  pURL +=5;
134  while(*pURL == '/')
135  {
136  pURL++;
137  }
138  }
139  //There shouldn't be any : in this string
140  if(strchr(pURL,':'))
141  {
143  return;
144  }
145  //read the filename by searching backwards for a /
146  //then keep reading until you find the first /
147  //when you found it, you have the host and dir
148  char *filestart = NULL;
149  char *dirstart = NULL;
150  for(int i = strlen(pURL);i>=0;i--)
151  {
152  if(pURL[i]== '/')
153  {
154  if(!filestart)
155  {
156  filestart = pURL+i+1;
157  dirstart = pURL+i+1;
158  strcpy_s(m_szFilename,filestart);
159  }
160  else
161  {
162  dirstart = pURL+i+1;
163  }
164  }
165  }
166  if((dirstart==NULL) || (filestart==NULL))
167  {
169  return;
170  }
171  else
172  {
173  strcpy_s(m_szDir,dirstart);//,(filestart-dirstart));
174  //m_szDir[(filestart-dirstart)] = NULL;
175  strncpy(m_szHost,pURL,(dirstart-pURL));
176  m_szHost[(dirstart-pURL)-1] = '\0';
177  }
178 
179 #ifdef WIN32
180  if ( _beginthread(HTTPObjThread,0,this) == NULL )
181 #else
182  if ( (thread_id = SDL_CreateThread(HTTPObjThread, this)) == NULL )
183 #endif
184  {
186  return;
187  }
188 }
189 
190 
192 {
193 #ifdef WIN32
194  _endthread();
195 #else
196  if (thread_id)
197  SDL_WaitThread(thread_id, NULL);
198 #endif
199 
200  fclose(LOCALFILE);
201 
202  if (m_DataSock != INVALID_SOCKET) {
203  shutdown(m_DataSock, 2);
205  }
206 }
207 
209 {
210  return m_State;
211 }
212 
213 unsigned int ChttpGet::GetBytesIn()
214 {
215  return m_iBytesIn;
216 }
217 
219 {
220  return m_iBytesTotal;
221 }
222 
223 
225 {
226  char szCommand[1000];
227  char *p;
228  int irsp = 0;
229  ConnectSocket();
230  if(m_Aborting)
231  {
232  fclose(LOCALFILE);
233  return;
234  }
236  {
237  fclose(LOCALFILE);
238  return;
239  }
240  sprintf(szCommand,"GET %s%s HTTP/1.1\nAccept: */*\nAccept-Encoding: deflate\nHost: %s\n\n\n",m_ProxyEnabled?"":"/",m_ProxyEnabled?m_URL:m_szDir,m_szHost);
241  send(m_DataSock,szCommand,strlen(szCommand),0);
242  p = GetHTTPLine();
243 if (!p) return;
244  if(strnicmp("HTTP/",p,5)==0)
245  {
246  char *pcode;
247  pcode = strchr(p,' ')+1;
248  if(!pcode)
249  {
251  fclose(LOCALFILE);
252  return;
253 
254  }
255  pcode[3] = '\0';
256  irsp = atoi(pcode);
257 
258  if(irsp == 0)
259  {
261  fclose(LOCALFILE);
262  return;
263  }
264  if(irsp==200)
265  {
266  int idataready=0;
267  do
268  {
269  p = GetHTTPLine();
270  if(p==NULL)
271  {
273  fclose(LOCALFILE);
274  return;
275  }
276  if(*p=='\0')
277  {
278  idataready = 1;
279  break;
280  }
281  if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
282  {
283  char *s = strchr(p,' ')+1;
284  p = s;
285  if(s)
286  {
287  while(*s)
288  {
289  if(!isdigit(*s))
290  {
291  *s='\0';
292  }
293  s++;
294  };
295  m_iBytesTotal = atoi(p);
296  }
297 
298  }
299 
300  Sleep(1);
301  }while(!idataready);
302  ReadDataChannel();
303  return;
304  }
306  fclose(LOCALFILE);
307  return;
308  }
309  else
310  {
312  fclose(LOCALFILE);
313  return;
314  }
315 }
316 
318 {
319  //HOSTENT *he;
320  unsigned int ip;
321  SERVENT *se;
322  SOCKADDR_IN hostaddr;
323  if(m_Aborting){
324  return 0;
325  }
326 
327  ip = inet_addr((const char *)m_szHost);
328 
329  int rcode = 0;
330  if(ip==INADDR_NONE)
331  {
333  do
334  {
335  if(m_Aborting)
336  {
338  return 0;
339  }
341 
342  Sleep(1);
343  }while(rcode==0);
344  }
345 
346  if(rcode == -1)
347  {
349  return 0;
350  }
351  //m_ControlSock
352  if(m_Aborting)
353  return 0;
354  se = getservbyname("http", NULL);
355  if(m_Aborting)
356  return 0;
357  if(se == NULL)
358  {
359  hostaddr.sin_port = htons(80);
360  }
361  else
362  {
363  hostaddr.sin_port = se->s_port;
364  }
365  hostaddr.sin_family = AF_INET;
366  //ip = htonl(ip);
367  memcpy(&hostaddr.sin_addr, &ip, 4);
368 
369  if(m_ProxyEnabled)
370  {
371  //This is on a proxy, so we need to make sure to connect to the proxy machine
372  ip = inet_addr((const char *)m_ProxyIP);
373 
374  if(ip==INADDR_NONE)
375  {
377  rcode = 0;
378  do
379  {
380  if(m_Aborting)
381  {
383  return 0;
384  }
386 
387  Sleep(1);
388  }while(rcode==0);
389 
390 
391  if(rcode == -1)
392  {
394  return 0;
395  }
396 
397  }
398  //Use either the proxy port or 80 if none specified
399  hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
400  //Copy the proxy address...
401  memcpy(&hostaddr.sin_addr,&ip,4);
402 
403  }
404 
405  memset(&hostaddr.sin_zero, 0, sizeof(hostaddr.sin_zero));
406 
407  //Now we will connect to the host
408  fd_set wfds;
409 
410  timeval timeout;
411  timeout.tv_sec = 0;
412  timeout.tv_usec = 0;
413 
414  int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
415  int cerr = WSAGetLastError();
416 //printf("cerr: %s\n", strerror(cerr));
417  if (serr) {
418  while ( (cerr == WSAEALREADY) || (cerr == WSAEINVAL) || (cerr == WSAEWOULDBLOCK) ) {
419  FD_ZERO(&wfds);
420  FD_SET( m_DataSock, &wfds );
421 
422 #ifdef WIN32
423  if ( select(0, NULL, &wfds, NULL, &timeout) )
424 #else
425  if ( select(m_DataSock+1, NULL, &wfds, NULL, &timeout) )
426 #endif
427  {
428  serr = 0;
429  break;
430  }
431 
432  if (m_Aborting)
433  return 0;
434 
435  serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
436 
437  if (serr == 0)
438  break;
439 
440  cerr = WSAGetLastError();
441 
442  if (cerr == WSAEISCONN) {
443  serr = 0;
444  break;
445  }
446 
447  Sleep(1);
448  }
449  }
450 
451  if (serr) {
452 //printf("1-serr: %i\n", serr);
454  return 0;
455  }
456 
458 
459  return 1;
460 }
461 
463 {
464  int iBytesRead;
465  char chunk[2];
466  uint igotcrlf = 0;
467  memset(recv_buffer,0,1000);
468  do
469  {
470  chunk[0]='\0';
471  bool gotdata = false;
472  do
473  {
474  iBytesRead = recv(m_DataSock,chunk,1,0);
475 
476  if(SOCKET_ERROR == iBytesRead)
477  {
478  int error = WSAGetLastError();
479 //printf("0 - iBytesRead: %i ==> (%i) %s\n", iBytesRead, error, strerror(error));
480  if(WSAEWOULDBLOCK==error)
481  {
482  gotdata = false;
483  continue;
484  }
485  else
486  return NULL;
487  }
488  else
489  {
490  gotdata = true;
491  }
492 
493  Sleep(1);
494  }while(!gotdata);
495 
496  if(chunk[0]==0x0d)
497  {
498  //This should always read a 0x0a
499  do
500  {
501  iBytesRead = recv(m_DataSock,chunk,1,0);
502 
503  if(SOCKET_ERROR == iBytesRead)
504  {
505  int error = WSAGetLastError();
506  if(WSAEWOULDBLOCK==error)
507  {
508 //printf("1 - iBytesRead: %i ==> %s\n", iBytesRead, strerror(error));
509  gotdata = false;
510  continue;
511  }
512  else
513  return NULL;
514  }
515  else
516  {
517  gotdata = true;
518  }
519 
520  Sleep(1);
521  }while(!gotdata);
522  igotcrlf = 1;
523  }
524  else
525  { chunk[1] = '\0';
526  strcat_s(recv_buffer,chunk);
527  }
528 
529  Sleep(1);
530  }while(igotcrlf==0);
531  return recv_buffer;
532 }
533 
535 {
536  char sDataBuffer[4096]; // Data-storage buffer for the data channel
537  int nBytesRecv = 0; // Bytes received from the data channel
538 
539  fd_set wfds;
540 
541  timeval timeout;
542  timeout.tv_sec = 0;
543  timeout.tv_usec = 500;
544 
546 
547  do {
548  FD_ZERO(&wfds);
549  FD_SET( m_DataSock, &wfds );
550 
551  if ( (m_iBytesTotal) && (m_iBytesIn == m_iBytesTotal) )
552  break;
553 
554 #ifdef WIN32
555  select(0, &wfds, NULL, NULL, &timeout);
556 #else
557  select(m_DataSock+1, &wfds, NULL, NULL, &timeout);
558 #endif
559 
560  if (m_Aborting) {
561  fclose(LOCALFILE);
562  return 0;
563  }
564 
565  nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer, sizeof(sDataBuffer), 0);
566 
567  if (m_Aborting) {
568  fclose(LOCALFILE);
569  return 0;
570  }
571 
572  if (SOCKET_ERROR == nBytesRecv) {
573  int error = WSAGetLastError();
574 
575  if (error == WSAEWOULDBLOCK) {
576  nBytesRecv = 1;
577  continue;
578  }
579  }
580 
581  m_iBytesIn += nBytesRecv;
582 
583  if (nBytesRecv > 0 ) {
584  fwrite(sDataBuffer, nBytesRecv, 1, LOCALFILE);
585  //Write sDataBuffer, nBytesRecv
586  }
587 
588  Sleep(1);
589  } while (nBytesRecv > 0);
590 
591  fclose(LOCALFILE);
592 
593  // Close the file and check for error returns.
594  if (nBytesRecv == SOCKET_ERROR) {
595  //Ok, we got a socket error -- xfer aborted?
597  return 0;
598  } else {
599  //OutputDebugString("HTTP File complete!\n");
600  //done!
602  return 1;
603  }
604 }
605 
606 
607 typedef struct _async_dns_lookup
608 {
609  unsigned int ip; //resolved host. Write only to worker thread.
610  char * host;//host name to resolve. read only to worker thread
611  bool done; //write only to the worker thread. Signals that the operation is complete
612  bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
613  bool abort; //read only to worker thread. If this is set, don't fill in the struct.
615 
618 
619 #ifdef WIN32
620 void __cdecl http_gethostbynameworker(void *parm);
621 #else
622 int http_gethostbynameworker(void *parm);
623 #endif
624 
625 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
626 {
627 
628  if(command==NW_AGHBN_LOOKUP)
629  {
630  if(http_lastaslu)
631  http_lastaslu->abort = true;
632 
633  async_dns_lookup *newaslu;
634  newaslu = (async_dns_lookup *)vm_malloc(sizeof(async_dns_lookup));
635  memset(&newaslu->ip,0,sizeof(unsigned int));
636  newaslu->host = hostname;
637  newaslu->done = false;
638  newaslu->error = false;
639  newaslu->abort = false;
640  http_lastaslu = newaslu;
641  httpaslu.done = false;
642 
643 #ifdef WIN32
645 #else
646  HOSTENT *he = gethostbyname(hostname);
647 
648  if (he == NULL) {
649  newaslu->error = true;
650  } else if ( !newaslu->abort ) {
651  memcpy( &newaslu->ip, he->h_addr_list[0], sizeof(uint) );
652  newaslu->done = true;
653  memcpy( &httpaslu,newaslu, sizeof(async_dns_lookup) );
654  }
655  // struct hostent *he;
656  // he = gethostbyname(hostname);
657  // newaslu->ip = (uint)((in_addr *)(he->h_addr))->s_addr;
658  // newaslu->done = true;
659  // httpaslu.done = true;
660  // thread_id
661 #endif
662 
663  return 1;
664  }
665  else if(command==NW_AGHBN_CANCEL)
666  {
667  if(http_lastaslu)
668  http_lastaslu->abort = true;
669  http_lastaslu = NULL;
670  }
671  else if(command==NW_AGHBN_READ)
672  {
673  if(!http_lastaslu)
674  return -1;
675  if(httpaslu.done)
676  {
677  //vm_free(http_lastaslu);
678  http_lastaslu = NULL;
679  memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
680  return 1;
681  }
682  else if(httpaslu.error)
683  {
684  vm_free(http_lastaslu);
685  http_lastaslu = NULL;
686  return -1;
687  }
688  else return 0;
689  }
690  return -2;
691 
692 }
693 
694 // This is the worker thread which does the lookup.
695 #ifdef WIN32
696 void __cdecl http_gethostbynameworker(void *parm)
697 #else
699 #endif
700 {
701 #ifdef SCP_UNIX
702 // df_pthread_detach(df_pthread_self());
703 #endif
704  async_dns_lookup *lookup = (async_dns_lookup *)parm;
705  HOSTENT *he = gethostbyname(lookup->host);
706  if(he==NULL)
707  {
708  lookup->error = true;
709 #ifdef WIN32
710  return;
711 #else
712  return 0;
713 #endif
714  }
715  else if(!lookup->abort)
716  {
717  memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
718  lookup->done = true;
719  memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));
720  }
721  vm_free(lookup);
722 
723 #ifdef SCP_UNIX
724  return 0;
725 #endif
726 }
SOCKET m_DataSock
Definition: chttpget.h:67
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
#define HTTP_STATE_FILE_NOT_FOUND
Definition: chttpget.h:22
#define WSAEWOULDBLOCK
Definition: config.h:131
char m_szHost[200]
Definition: chttpget.h:60
#define NW_AGHBN_LOOKUP
Definition: chttpget.cpp:44
#define __cdecl
Definition: config.h:72
char * GetHTTPLine()
Definition: chttpget.cpp:462
uint m_iBytesTotal
Definition: chttpget.h:51
#define SOCKADDR_IN
Definition: config.h:124
#define SOCKADDR
Definition: config.h:123
FILE * LOCALFILE
Definition: chttpget.h:69
#define SOCKET_ERROR
Definition: config.h:138
#define HTTP_STATE_SOCKET_ERROR
Definition: chttpget.h:16
async_dns_lookup httpaslu
Definition: chttpget.cpp:616
#define INVALID_SOCKET
Definition: psnet2.h:53
void WorkerThread()
Definition: chttpget.cpp:224
#define WSAEINVAL
Definition: config.h:130
#define SERVENT
Definition: config.h:127
#define HTTP_STATE_RECV_FAILED
Definition: chttpget.h:26
#define HTTP_STATE_FILE_RECEIVED
Definition: chttpget.h:24
#define HOSTENT
Definition: config.h:126
ushort m_ProxyPort
Definition: chttpget.h:56
int ConnectSocket()
Definition: chttpget.cpp:317
#define WSAEISCONN
Definition: config.h:132
unsigned int uint
Definition: pstypes.h:64
#define strnicmp(s1, s2, n)
Definition: config.h:272
char m_szDir[200]
Definition: chttpget.h:61
#define WSAEALREADY
Definition: config.h:129
bool m_Aborting
Definition: chttpget.h:64
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
#define HTTP_STATE_CANT_WRITE_FILE
Definition: chttpget.h:27
GLdouble s
Definition: Glext.h:5321
void AbortGet()
Definition: chttpget.cpp:70
int GetStatus()
Definition: chttpget.cpp:208
uint GetTotalBytes()
Definition: chttpget.cpp:218
#define HTTP_STATE_UNKNOWN_ERROR
Definition: chttpget.h:25
#define HTTP_STATE_CONNECTED
Definition: chttpget.h:21
#define HTTP_STATE_RECEIVING
Definition: chttpget.h:23
char * m_ProxyIP
Definition: chttpget.h:54
#define NW_AGHBN_CANCEL
Definition: chttpget.cpp:43
GLbitfield GLuint64 timeout
Definition: Glext.h:6725
void GetFile(char *URL, char *localfile)
Definition: chttpget.cpp:99
uint m_iBytesIn
Definition: chttpget.h:50
char m_URL[MAX_URL_LEN]
Definition: chttpget.h:55
char recv_buffer[1000]
Definition: chttpget.h:70
#define vm_malloc(size)
Definition: pstypes.h:547
#define HTTP_STATE_URL_PARSING_ERROR
Definition: chttpget.h:17
#define WSAGetLastError()
Definition: chttpget.cpp:29
#define HTTP_STATE_STARTUP
Definition: chttpget.h:28
#define MAX_URL_LEN
Definition: chttpget.h:30
#define closesocket(x)
Definition: config.h:128
int HTTPObjThread(void *obj)
Definition: chttpget.cpp:58
#define strcat_s(...)
Definition: safe_strings.h:68
#define _endthread()
Definition: config.h:211
#define HTTP_STATE_INTERNAL_ERROR
Definition: chttpget.h:15
uint ReadDataChannel()
Definition: chttpget.cpp:534
char m_szFilename[100]
Definition: chttpget.h:62
unsigned short ushort
Definition: pstypes.h:63
int http_gethostbynameworker(void *parm)
Definition: chttpget.cpp:698
GLfloat GLfloat p
Definition: Glext.h:8373
void Sleep(int mili)
#define NW_AGHBN_READ
Definition: chttpget.cpp:45
GLsizei GLsizei GLuint * obj
Definition: Glext.h:5619
int http_Asyncgethostbyname(unsigned int *ip, int command, char *hostname)
Definition: chttpget.cpp:625
bool m_ProxyEnabled
Definition: chttpget.h:53
struct _async_dns_lookup async_dns_lookup
ChttpGet(char *URL, char *localfile)
Definition: chttpget.cpp:92
unsigned int ip
Definition: chttpget.cpp:609
bool m_Aborted
Definition: chttpget.h:44
uint GetBytesIn()
Definition: chttpget.cpp:213
uint m_State
Definition: chttpget.h:52
#define HTTP_STATE_CANT_CONNECT
Definition: chttpget.h:20
#define _beginthread(x, y, z)
Definition: config.h:210
async_dns_lookup * http_lastaslu
Definition: chttpget.cpp:617
#define HTTP_STATE_HOST_NOT_FOUND
Definition: chttpget.h:19
#define strcpy_s(...)
Definition: safe_strings.h:67