]> icculus.org git repositories - taylor/freespace2.git/blob - src/inetfile/chttpget.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / inetfile / chttpget.cpp
1 /*
2 * $Logfile: /Freespace2/code/Inetfile/Chttpget.cpp $
3 * $Revision$
4 * $Date$
5 * $Author$
6 *
7 * HTTP Client class (get only)
8 *
9 * $Log$
10 * Revision 1.2  2002/05/07 03:16:45  theoddone33
11 * The Great Newline Fix
12 *
13 * Revision 1.1.1.1  2002/05/03 03:28:09  root
14 * Initial import.
15 *
16  * 
17  * 5     8/24/99 1:49a Dave
18  * Fixed client-side afterburner stuttering. Added checkbox for no version
19  * checking on PXO join. Made button info passing more friendly between
20  * client and server.
21  * 
22  * 4     8/22/99 1:19p Dave
23  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
24  * which d3d cards are detected.
25  * 
26  * 21    8/21/99 6:33p Kevin
27  * Fixed Proxy Stuff
28  * 
29  * 20    8/21/99 6:48a Jeff
30  * Linux port
31  * 
32  * 19    8/20/99 3:01p Kevin
33  * Added support for Proxies (I hope!)
34  * 
35  * 18    8/15/99 6:38p Jeff
36  * fixed compile error
37  * 
38  * 17    8/15/99 6:26p Kevin
39  * 
40  * 16    4/14/99 1:20a Jeff
41  * fixed case mismatched #includes
42  * 
43  * 15    3/03/99 12:28a Nate
44  * sped up something or other when the connection is done
45  * 
46  * 14    2/03/99 4:20p Kevin
47  * Got multiplayer working with .mn3 files, and setup autodownloading
48  * 
49  * 13    1/27/99 5:49p Kevin
50  * 
51  * 12    1/27/99 5:38p Kevin
52  * 
53  * 11    12/30/98 12:15p Kevin
54  * Auto Mission Download system
55  * 
56  * 10    10/12/98 4:59p Kevin
57  * Added delay to thread when cancelled...
58  * 
59  * 9     10/12/98 4:49p Nate
60  * More fixes
61  * 
62  * 8     10/12/98 1:54p Nate
63  * Fixed bug
64  * 
65  * 7     10/12/98 11:30a Kevin
66  * More memory stuff
67  * 
68  * 6     10/08/98 12:59p Nate
69  * fixed cancel
70  * 
71  * 5     10/08/98 9:57a Kevin
72  * made transfer cancellable
73  * 
74  * 4     7/31/98 12:19p Nate
75  * Fixed http abort problem.
76  * 
77  * 3     7/31/98 11:57a Kevin
78  * Added new functions for getting state
79  * 
80  * 2     6/01/98 10:10a Kevin
81  * Added DLL connection interface and auto update DLL
82  * 
83  * 1     5/27/98 9:52a Kevin
84  * 
85  * 1     5/25/98 5:31p Kevin
86  * Initial version
87 *
88 * $NoKeywords: $
89 */
90
91 // #define WIN32
92
93 // #ifdef WIN32
94 #include <windows.h>
95 #include <process.h>
96 // #endif
97
98 #include <string.h>
99 #include <stdio.h>
100 #include <stdlib.h>
101
102 #include "inetgetfile.h"
103 #include "chttpget.h"
104
105 #ifdef __LINUX__
106
107 inline void Sleep(int millis)
108 {
109         struct timeval tv;
110         tv.tv_sec = 0;
111         tv.tv_usec = millis*1000;
112         select(0,NULL,NULL,NULL,&tv);
113 }
114 #endif
115
116 #define NW_AGHBN_CANCEL         1
117 #define NW_AGHBN_LOOKUP         2
118 #define NW_AGHBN_READ           3
119
120 #ifndef __LINUX__
121 void __cdecl http_gethostbynameworker(void *parm);
122 #else
123 void *http_gethostbynameworker(void *parm);
124 #endif
125
126 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
127
128 #ifndef __LINUX__
129 void HTTPObjThread( void * obj )
130 #else
131 void *HTTPObjThread( void * obj )
132 #endif
133 {
134         ((ChttpGet *)obj)->WorkerThread();
135         ((ChttpGet *)obj)->m_Aborted = true;
136         //OutputDebugString("http transfer exiting....\n");
137
138         #ifdef __LINUX__
139         return NULL;
140         #endif
141 }
142
143 void ChttpGet::AbortGet()
144 {
145 // #ifdef WIN32
146         OutputDebugString("Aborting....\n");
147 // #endif
148         m_Aborting = true;
149         while(!m_Aborted) Sleep(50); //Wait for the thread to end
150 // #ifdef WIN32
151         OutputDebugString("Aborted....\n");
152 // #endif
153 }
154
155 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
156 {
157         m_ProxyEnabled = true;
158         m_ProxyIP = proxyip;
159         m_ProxyPort = proxyport;
160         GetFile(URL,localfile);
161 }
162
163 ChttpGet::ChttpGet(char *URL,char *localfile)
164 {
165         m_ProxyEnabled = false;
166         GetFile(URL,localfile);
167 }
168
169
170 void ChttpGet::GetFile(char *URL,char *localfile)
171 {
172         m_DataSock = INVALID_SOCKET;
173         m_iBytesIn = 0;
174         m_iBytesTotal = 0;
175         m_State = HTTP_STATE_STARTUP;;
176         m_Aborting = false;
177         m_Aborted = false;
178
179         strncpy(m_URL,URL,MAX_URL_LEN-1);
180         m_URL[MAX_URL_LEN-1] = 0;
181
182         LOCALFILE = fopen(localfile,"wb");
183         if(NULL == LOCALFILE)
184         {
185                 m_State = HTTP_STATE_CANT_WRITE_FILE;
186                 return;
187         }
188         m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
189         if(INVALID_SOCKET == m_DataSock)
190         {
191                 m_State = HTTP_STATE_SOCKET_ERROR;
192                 return;
193         }
194         unsigned long arg;
195
196         arg = true;
197 #ifndef __LINUX__
198         ioctlsocket( m_DataSock, FIONBIO, &arg );
199 #else
200         ioctl( m_DataSock, FIONBIO, &arg );
201 #endif
202
203         char *pURL = URL;
204         if(strnicmp(URL,"http:",5)==0)
205         {
206                 pURL +=5;
207                 while(*pURL == '/')
208                 {
209                         pURL++;
210                 }
211         }
212         //There shouldn't be any : in this string
213         if(strchr(pURL,':'))
214         {
215                 m_State = HTTP_STATE_URL_PARSING_ERROR;
216                 return;
217         }
218         //read the filename by searching backwards for a /
219         //then keep reading until you find the first /
220         //when you found it, you have the host and dir
221         char *filestart = NULL;
222         char *dirstart = NULL;
223         for(int i = strlen(pURL);i>=0;i--)
224         {
225                 if(pURL[i]== '/')
226                 {
227                         if(!filestart)
228                         {
229                                 filestart = pURL+i+1;
230                                 dirstart = pURL+i+1;
231                                 strcpy(m_szFilename,filestart);
232                         }
233                         else
234                         {
235                                 dirstart = pURL+i+1;
236                         }
237                 }
238         }
239         if((dirstart==NULL) || (filestart==NULL))
240         {
241                 m_State = HTTP_STATE_URL_PARSING_ERROR;
242                 return;
243         }
244         else
245         {
246                 strcpy(m_szDir,dirstart);//,(filestart-dirstart));
247                 //m_szDir[(filestart-dirstart)] = NULL;
248                 strncpy(m_szHost,pURL,(dirstart-pURL));
249                 m_szHost[(dirstart-pURL)-1] = '\0';
250         }
251 // #ifdef WIN32
252         if(NULL==_beginthread(HTTPObjThread,0,this))
253         {
254                 m_State = HTTP_STATE_INTERNAL_ERROR;
255                 return;
256         }
257         /*
258 #elif defined(__LINUX__)
259         pthread_t thread;
260         if(!inet_LoadThreadLib())
261         {
262                 m_State = HTTP_STATE_INTERNAL_ERROR;
263                 return;
264         }
265         if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0)
266         {
267                 m_State = HTTP_STATE_INTERNAL_ERROR;
268                 return;
269         }
270 #endif
271         */
272 }
273
274
275 ChttpGet::~ChttpGet()
276 {
277         if(m_DataSock != INVALID_SOCKET)
278         {
279                 shutdown(m_DataSock,2);
280 #ifndef __LINUX__
281                 closesocket(m_DataSock);
282 #else
283                 close(m_DataSock);
284 #endif
285         }
286 }
287
288 int ChttpGet::GetStatus()
289 {
290         return m_State;
291 }
292
293 unsigned int ChttpGet::GetBytesIn()
294 {
295         return m_iBytesIn;
296 }
297
298 unsigned int ChttpGet::GetTotalBytes()
299 {
300         return m_iBytesTotal;
301 }
302
303
304 void ChttpGet::WorkerThread()
305 {
306         char szCommand[1000];
307         char *p;
308         int irsp = 0;
309         ConnectSocket();
310         if(m_Aborting)
311         {
312                 fclose(LOCALFILE);
313                 return;
314         }
315         if(m_State != HTTP_STATE_CONNECTED)
316         {
317                 fclose(LOCALFILE);
318                 return;
319         }
320         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);
321         send(m_DataSock,szCommand,strlen(szCommand),0);
322         p = GetHTTPLine();
323         if(strnicmp("HTTP/",p,5)==0)
324         {
325                 char *pcode;
326                 pcode = strchr(p,' ')+1;
327                 if(!pcode)
328                 {
329                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
330                         fclose(LOCALFILE);
331                         return;
332
333                 }
334                 pcode[3] = '\0';
335                 irsp = atoi(pcode);
336
337                 if(irsp == 0)
338                 {
339                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
340                         fclose(LOCALFILE);
341                         return;
342                 }
343                 if(irsp==200)
344                 {
345                         int idataready=0;
346                         do
347                         {
348                                 p = GetHTTPLine();
349                                 if(p==NULL)
350                                 {
351                                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
352                                         fclose(LOCALFILE);
353                                         return;
354                                 }
355                                 if(*p=='\0')
356                                 {
357                                         idataready = 1;
358                                         break;
359                                 }
360                                 if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
361                                 {
362                                         char *s = strchr(p,' ')+1;
363                                         p = s;
364                                         if(s)
365                                         {
366                                                 while(*s)
367                                                 {
368                                                         if(!isdigit(*s))
369                                                         {
370                                                                 *s='\0';
371                                                         }
372                                                         s++;
373                                                 };
374                                                 m_iBytesTotal = atoi(p);
375                                         }
376
377                                 }
378
379                                 Sleep(1);
380                         }while(!idataready);
381                 ReadDataChannel();
382                 return;
383                 }
384                 m_State = HTTP_STATE_FILE_NOT_FOUND;
385                 fclose(LOCALFILE);
386                 return;
387         }
388         else
389         {
390                 m_State = HTTP_STATE_UNKNOWN_ERROR;
391                 fclose(LOCALFILE);
392                 return;
393         }
394 }
395
396 int ChttpGet::ConnectSocket()
397 {
398         //HOSTENT *he;
399         unsigned int ip;
400         SERVENT *se;
401         SOCKADDR_IN hostaddr;
402         if(m_Aborting){
403                 return 0;
404         }
405         
406         ip = inet_addr((const char *)m_szHost);
407
408         int rcode = 0;
409         if(ip==INADDR_NONE)
410         {
411                 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost);          
412                 do
413                 {       
414                         if(m_Aborting)
415                         {
416                                 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost);
417                                 return 0;
418                         }
419                         rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost);
420
421                         Sleep(1);
422                 }while(rcode==0);
423         }
424         
425         if(rcode == -1)
426         {
427                 m_State = HTTP_STATE_HOST_NOT_FOUND;
428                 return 0;
429         }
430         //m_ControlSock
431         if(m_Aborting)
432                 return 0;
433         se = getservbyname("http", NULL);
434         if(m_Aborting)
435                 return 0;
436         if(se == NULL)
437         {
438                 hostaddr.sin_port = htons(80);
439         }
440         else
441         {
442                 hostaddr.sin_port = se->s_port;
443         }
444         hostaddr.sin_family = AF_INET;          
445         //ip = htonl(ip);
446         memcpy(&hostaddr.sin_addr,&ip,4);
447
448         if(m_ProxyEnabled)
449         {
450                 //This is on a proxy, so we need to make sure to connect to the proxy machine
451                 ip = inet_addr((const char *)m_ProxyIP);
452                                 
453                 if(ip==INADDR_NONE)
454                 {
455                         http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP);
456                         rcode = 0;
457                         do
458                         {       
459                                 if(m_Aborting)
460                                 {
461                                         http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP);
462                                         return 0;
463                                 }
464                                 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP);
465
466                                 Sleep(1);
467                         }while(rcode==0);
468                         
469                         
470                         if(rcode == -1)
471                         {
472                                 m_State = HTTP_STATE_HOST_NOT_FOUND;
473                                 return 0;
474                         }
475
476                 }
477                 //Use either the proxy port or 80 if none specified
478                 hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
479                 //Copy the proxy address...
480                 memcpy(&hostaddr.sin_addr,&ip,4);
481
482         }
483         //Now we will connect to the host                                       
484         fd_set  wfds;
485
486         timeval timeout;
487         timeout.tv_sec = 0;
488         timeout.tv_usec = 0;
489         int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
490         int cerr = WSAGetLastError();
491         if(serr)
492         {
493                 while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK))
494                 {
495                         FD_ZERO(&wfds);
496                         FD_SET( m_DataSock, &wfds );
497                         if(select(0,NULL,&wfds,NULL,&timeout))
498                         {
499                                 serr = 0;
500                                 break;
501                         }
502                         if(m_Aborting)
503                                 return 0;
504                         serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
505                         if(serr == 0)
506                                 break;
507                         cerr = WSAGetLastError();
508                         if(cerr==WSAEISCONN)
509                         {
510                                 serr = 0;
511                                 break;
512                         }
513
514                         Sleep(1);
515                 };
516         }
517         if(serr)
518         {
519                 m_State = HTTP_STATE_CANT_CONNECT;
520                 return 0;
521         }
522         m_State = HTTP_STATE_CONNECTED;
523         return 1;
524 }
525
526 char *ChttpGet::GetHTTPLine()
527 {
528         unsigned int iBytesRead;
529         char chunk[2];
530         unsigned int igotcrlf = 0;
531         memset(recv_buffer,0,1000);
532         do
533         {
534                 chunk[0]='\0';
535                 bool gotdata = false;
536                 do
537                 {
538                         iBytesRead = recv(m_DataSock,chunk,1,0);
539
540                         if(SOCKET_ERROR == iBytesRead)
541                         {       
542                                 int error = WSAGetLastError();
543                                 if(WSAEWOULDBLOCK==error)
544                                 {
545                                         gotdata = false;
546                                         continue;
547                                 }
548                                 else
549                                         return NULL;
550                         }
551                         else
552                         {
553                                 gotdata = true;
554                         }
555
556                         Sleep(1);
557                 }while(!gotdata);
558                 
559                 if(chunk[0]==0x0d)
560                 {
561                         //This should always read a 0x0a
562                         do
563                         {
564                                 iBytesRead = recv(m_DataSock,chunk,1,0);
565
566                                 if(SOCKET_ERROR == iBytesRead)
567                                 {       
568                                         int error = WSAGetLastError();
569                                         if(WSAEWOULDBLOCK==error)
570                                         {
571                                                 gotdata = false;
572                                                 continue;
573                                         }
574                                         else
575                                                 return NULL;
576                                 }
577                                 else
578                                 {
579                                         gotdata = true;
580                                 }
581
582                                 Sleep(1);
583                         }while(!gotdata);
584                         igotcrlf = 1;   
585                 }
586                 else
587                 {       chunk[1] = '\0';
588                         strcat(recv_buffer,chunk);
589                 }
590                 
591                 Sleep(1);
592         }while(igotcrlf==0);
593         return recv_buffer;     
594 }
595
596 unsigned int ChttpGet::ReadDataChannel()
597 {
598         char sDataBuffer[4096];         // Data-storage buffer for the data channel
599         int nBytesRecv = 0;                                             // Bytes received from the data channel
600
601         fd_set  wfds;
602
603         timeval timeout;
604         timeout.tv_sec = 0;
605         timeout.tv_usec = 500;
606
607         m_State = HTTP_STATE_RECEIVING;                 
608    do   
609    {
610                 FD_ZERO(&wfds);
611                 FD_SET( m_DataSock, &wfds );
612
613                 if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal))
614                 {
615                         break;
616                 }
617                 select(0,&wfds,NULL,NULL,&timeout);
618         if(m_Aborting)
619                 {
620                         fclose(LOCALFILE);
621                         return 0;               
622                 }
623                 nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0);
624         if(m_Aborting)
625                 {
626                         fclose(LOCALFILE);
627                         return 0;
628                 }
629                 if(SOCKET_ERROR == nBytesRecv)
630                 {       
631                         int error = WSAGetLastError();
632                         if(WSAEWOULDBLOCK==error)
633                         {
634                                 nBytesRecv = 1;
635                                 continue;
636                         }
637                 }
638                 m_iBytesIn += nBytesRecv;
639                 if (nBytesRecv > 0 )
640                 {
641                         fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
642                         //Write sDataBuffer, nBytesRecv
643         }
644                 
645                 Sleep(1);
646         }while (nBytesRecv > 0);
647         fclose(LOCALFILE);                                                      
648         // Close the file and check for error returns.
649         if (nBytesRecv == SOCKET_ERROR)
650         { 
651                 //Ok, we got a socket error -- xfer aborted?
652                 m_State = HTTP_STATE_RECV_FAILED;
653                 return 0;
654         }
655         else
656         {
657                 //OutputDebugString("HTTP File complete!\n");
658                 //done!
659                 m_State = HTTP_STATE_FILE_RECEIVED;
660                 return 1;
661         }
662 }       
663
664
665 typedef struct _async_dns_lookup
666 {
667         unsigned int ip;        //resolved host. Write only to worker thread.
668         char * host;//host name to resolve. read only to worker thread
669         bool done;      //write only to the worker thread. Signals that the operation is complete
670         bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
671         bool abort;     //read only to worker thread. If this is set, don't fill in the struct.
672 }async_dns_lookup;
673
674 async_dns_lookup httpaslu;
675 async_dns_lookup *http_lastaslu = NULL;
676
677 #ifndef __LINUX__
678 void __cdecl http_gethostbynameworker(void *parm);
679 #else
680 void *http_gethostbynameworker(void *parm);
681 #endif
682
683 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
684 {
685         
686         if(command==NW_AGHBN_LOOKUP)
687         {
688                 if(http_lastaslu)
689                         http_lastaslu->abort = true;
690
691                 async_dns_lookup *newaslu;
692                 newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup));
693                 memset(&newaslu->ip,0,sizeof(unsigned int));
694                 newaslu->host = hostname;
695                 newaslu->done = false;
696                 newaslu->error = false;
697                 newaslu->abort = false;
698                 http_lastaslu = newaslu;
699                 httpaslu.done = false;
700
701 // #ifdef WIN32
702                 _beginthread(http_gethostbynameworker,0,newaslu);
703         /*
704 #elif defined(__LINUX__)
705                 pthread_t thread;
706                 if(!inet_LoadThreadLib())
707                 {
708                         return 0;
709                 }
710
711                 df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu);
712 #endif
713                 */
714                 return 1;
715         }
716         else if(command==NW_AGHBN_CANCEL)
717         {
718                 if(http_lastaslu)
719                         http_lastaslu->abort = true;
720                 http_lastaslu = NULL;
721         }
722         else if(command==NW_AGHBN_READ)
723         {
724                 if(!http_lastaslu)
725                         return -1;
726                 if(httpaslu.done)
727                 {
728                         //free(http_lastaslu);
729                         http_lastaslu = NULL;
730                         memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
731                         return 1;
732                 }
733                 else if(httpaslu.error)
734                 {
735                         free(http_lastaslu);
736                         http_lastaslu = NULL;
737                         return -1;
738                 }
739                 else return 0;
740         }
741         return -2;
742
743 }
744
745 // This is the worker thread which does the lookup.
746 #ifndef __LINUX__
747 void __cdecl http_gethostbynameworker(void *parm)
748 #else
749 void *http_gethostbynameworker(void *parm)
750 #endif
751 {
752 #ifdef __LINUX__
753         df_pthread_detach(df_pthread_self());
754 #endif
755         async_dns_lookup *lookup = (async_dns_lookup *)parm;
756         HOSTENT *he = gethostbyname(lookup->host);
757         if(he==NULL)
758         {
759                 lookup->error = true;
760                 #ifdef __LINUX__
761                 return NULL;
762                 #else
763                 return;
764                 #endif
765         }
766         else if(!lookup->abort)
767         {
768                 memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
769                 lookup->done = true;
770                 memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));
771         }
772         free(lookup);
773
774 #ifdef __LINUX__
775         return NULL;
776 #endif
777 }
778