1 /***************************************************************************
2 chttp.cpp - description
3 -------------------
4 begin : Mon Jul 29 2002
5 copyright : (C) 2002-2003 by Mathias Küster
6 email : mathen@users.berlios.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "chttp.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "core/clist.h"
24 #include "core/cmanager.h"
25 #include "dcobject.h"
26
27 // for encoding of proxy user/pass
28 #include "core/cbase64.h"
29
30 /** */
CHttp()31 CHttp::CHttp()
32 {
33 m_pHttpCallback = 0;
34 m_pCallback = 0;
35 m_eUrlMethod = eumGET;
36
37 m_pMessageList = new CList<CDCMessage>();
38 }
39
40 /** */
~CHttp()41 CHttp::~CHttp()
42 {
43 if ( m_pCallback )
44 {
45 CManager::Instance()->Remove( m_pCallback );
46 delete m_pCallback;
47 m_pCallback = 0;
48 }
49
50 Disconnect();
51
52 delete m_pHttpCallback;
53 m_pHttpCallback = 0;
54
55 delete m_pMessageList;
56 }
57
58 /** */
Callback()59 int CHttp::Callback()
60 {
61 Thread();
62 return 0;
63 }
64
65 /** */
GetUrl(CString url,CString postdata)66 int CHttp::GetUrl( CString url, CString postdata )
67 {
68 int i,i1;
69 CString s(url);
70 CString sRealHost;
71 CString sRealPort;
72
73 //Disconnect();
74
75 m_nErrorCode = 0;
76 m_bData = false;
77 m_sHeader.Empty();
78 m_sLocation.Empty();
79
80 m_baData.SetSize(0);
81
82 if ( s.IsEmpty() )
83 {
84 return -1;
85 }
86
87 if ( postdata.IsEmpty() )
88 {
89 m_eUrlMethod = eumGET;
90 m_sPostData.Empty();
91 }
92 else
93 {
94 m_eUrlMethod = eumPOST;
95 m_sPostData = postdata;
96 }
97
98 CString header = s.Mid(0,7);
99
100 header = header.ToUpper();
101
102 if ( header == "HTTP://" )
103 {
104 s = s.Mid(7,s.Length()-7);
105 }
106
107 if ( (i = s.Find(':')) >= 0 )
108 {
109 m_sHost = s.Mid(0,i);
110 }
111
112 if ( (i1 = s.Find('/')) < 0 )
113 {
114 printf("CHTTP: no '/' found\n");
115 return -1;
116 }
117
118 if ( i >= 0 )
119 {
120 m_sPort = s.Mid(i+1,i1-i-1);
121 }
122 else
123 {
124 m_sHost = s.Mid(0,i1);
125 m_sPort = "80";
126 }
127
128 char *proxy = getenv("http_proxy");
129
130 if( proxy )
131 {
132 unsigned int port;
133 if ( ParseProxy(proxy,m_sProxyUser,m_sProxyPass,m_sProxy,port) )
134 {
135 m_sProxyPort = CString::number(port);
136 printf("CHTTP: PROXY: '%s':'%s'", m_sProxy.Data(),m_sProxyPort.Data());
137 if ( (m_sProxyUser.NotEmpty()) || (m_sProxyPass.NotEmpty()) )
138 {
139 printf(" '%s':'%s' UNTESTED\n", m_sProxyUser.Data(),m_sProxyPass.Data());
140 }
141 else
142 {
143 printf("\n");
144 }
145 }
146 else
147 {
148 printf("CHTTP: PROXY parse error\n");
149 }
150
151 m_sUrl = url;
152 sRealHost = m_sProxy;
153 sRealPort = m_sProxyPort;
154 }
155 else
156 {
157 /* unset proxy variables */
158 m_sProxy.Empty();
159 m_sProxyPort.Empty();
160 m_sProxyUser.Empty();
161 m_sProxyPass.Empty();
162
163 m_sUrl = s.Mid(i1,s.Length()-i1);
164 sRealHost = m_sHost;
165 sRealPort = m_sPort;
166 }
167
168 printf("CHTTP: HOST : '%s:%s'\n",m_sHost.Data(),m_sPort.Data());
169 printf("CHTTP: URL : '%s'\n",m_sUrl.Data());
170
171 if ( Connect( sRealHost, sRealPort.asINT() ) == ecsERROR )
172 {
173 return -1;
174 }
175
176 if ( !m_pCallback )
177 {
178 m_pCallback = new CCallback0<CHttp>( this, &CHttp::Callback );
179 CManager::Instance()->Add( m_pCallback );
180 }
181
182 return 0;
183 }
184
185 /** */
GetData(CByteArray * ba)186 bool CHttp::GetData( CByteArray * ba )
187 {
188 bool err = false;
189
190 if ( (m_nErrorCode == 200) && m_bData && (m_eMode == estNONE) )
191 {
192 if ( (m_nContentLength == -1) || (m_nContentLength == m_baData.Size()) )
193 {
194 if ( ba )
195 {
196 ba->SetSize(0);
197 ba->Append( m_baData.Data(), m_baData.Size() );
198 }
199
200 err = true;
201 }
202 }
203
204 return err;
205 }
206
207 /** */
CallBack_SendObject(CDCMessage * DCMessage)208 int CHttp::CallBack_SendObject( CDCMessage * DCMessage )
209 {
210 int err;
211
212 if ( m_pHttpCallback )
213 {
214 err = m_pHttpCallback->notify(DCMessage);
215 }
216 else
217 {
218 err = DC_CallBack( DCMessage );
219 }
220
221 if ( err == -1 )
222 {
223 printf("CHttp: CallBack failed (state)...\n");
224 delete DCMessage;
225 }
226
227 return err;
228 }
229
230 /** */
ConnectionState(eConnectionState state)231 void CHttp::ConnectionState( eConnectionState state )
232 {
233 CMessageConnectionState *Object;
234
235 Object = new CMessageConnectionState();
236
237 Object->m_eState = state;
238 Object->m_sMessage = GetSocketError();
239
240 if ( state == estCONNECTED )
241 {
242 m_eMode = estTRANSFERHANDSHAKE;
243 }
244 else if ( state == estDISCONNECTED )
245 {
246 m_eMode = estNONE;
247 }
248
249 // callback over notify because connection class is locked
250 m_pMessageList->Add(Object);
251 }
252
253 /** */
DataAvailable(const char * buffer,int len)254 void CHttp::DataAvailable( const char * buffer, int len )
255 {
256 int i,i1,i2;
257
258 if ( m_eMode == estTRANSFERDOWNLOAD )
259 {
260 if ( m_bData == false )
261 {
262 for(i=0;i<len;i++)
263 {
264 m_sHeader += buffer[i];
265
266 if ((m_sHeader.Length() > 4) &&
267 (m_sHeader.Data()[m_sHeader.Length()-4] == 0x0d) &&
268 (m_sHeader.Data()[m_sHeader.Length()-3] == 0x0a) &&
269 (m_sHeader.Data()[m_sHeader.Length()-2] == 0x0d) &&
270 (m_sHeader.Data()[m_sHeader.Length()-1] == 0x0a) )
271 {
272 //m_sHeader += CString().Set(buffer,i);
273 //i+=4;
274
275 // parse header ...
276 if ( (i1 = m_sHeader.Find("HTTP/1.0 ")) == -1 )
277 {
278 if ( (i1 = m_sHeader.Find("HTTP/1.1 ")) == -1 )
279 {
280 printf("wrong proto '%s'\n",m_sHeader.Data());
281 Disconnect();
282 return;
283 }
284 }
285
286 if ( (i2 = m_sHeader.Find(' ',i1+9)) == -1 )
287 {
288 printf("wrong proto '%s'\n",m_sHeader.Data());
289 Disconnect();
290 }
291 else
292 {
293 m_nErrorCode = m_sHeader.Mid(i1+9,i2-i1-9).asINT();
294
295 if ( m_nErrorCode == 200 )
296 {
297 printf("no error\n");
298 m_bData = true;
299 }
300 else if ( m_nErrorCode == 302 )
301 {
302 printf("redirect 302\n");
303 }
304 else
305 {
306 printf("http error %d\n",m_nErrorCode);
307 Disconnect();
308 }
309 }
310
311 m_nContentLength = -1;
312
313 if ( m_nErrorCode == 200 )
314 {
315 if ( (i1 = m_sHeader.Find("Content-Length: ")) != -1 )
316 {
317 if ( (i2 = m_sHeader.Find(0x0d,i1)) != -1 )
318 {
319 m_nContentLength = m_sHeader.Mid(i1+16,i2-i1-16).asLONG();
320 }
321 }
322 }
323 else if ( m_nErrorCode == 302 )
324 {
325 if ( (i1 = m_sHeader.Find("Location: ")) != -1 )
326 {
327 if ( (i2 = m_sHeader.Find(0x0d,i1)) != -1 )
328 {
329 m_sLocation = m_sHeader.Mid(i1+10,i2-i1-10);
330 }
331
332 if ( m_sLocation.IsEmpty() )
333 {
334 printf("http wrong location\n");
335 m_nErrorCode = 0;
336 }
337 }
338 }
339
340 i++;
341 break;
342 }
343 }
344
345 if ( m_bData && (m_nErrorCode != 0) )
346 {
347 AppendData( buffer+i,len-i );
348 }
349 }
350 else
351 {
352 AppendData( buffer, len );
353 }
354
355 //printf("DATA: %ld\n",baData.Size());
356 }
357 }
358
359 /** */
AppendData(const char * buffer,int len)360 void CHttp::AppendData( const char * buffer, int len )
361 {
362 CMessageTransfer * msg = new CMessageTransfer();
363
364 if ( m_nContentLength != -1 )
365 {
366 msg->m_nLength = m_nContentLength;
367 }
368
369 msg->m_nTransfered = m_baData.Size();
370
371 m_pMessageList->Add(msg);
372
373 m_baData.Append((const unsigned char*)buffer,len);
374 }
375
376 /** */
DataSend()377 void CHttp::DataSend()
378 {
379 CString header;
380
381 if ( m_eMode == estTRANSFERHANDSHAKE )
382 {
383 // send request
384 if ( m_eUrlMethod == eumGET )
385 {
386 header = "GET ";
387 }
388 else
389 {
390 header = "POST ";
391 }
392
393 header += m_sUrl;
394 header += " HTTP/1.0";
395 header += "\xd\xa";
396 header += "User-Agent: DCGUI v";
397 header += DCLIB_VERSION_STRING;
398 header += "\xd\xa";
399 header += "Referer: http://";
400 header += m_sHost;
401 header += ':';
402 header += m_sPort;
403 header += '/';
404 header += "\xd\xa";
405 if ( (m_sProxy.NotEmpty()) && (m_sProxyUser.NotEmpty()) )
406 {
407 header += "Proxy-Authorization: Basic ";
408 header += CBase64::Encode( m_sProxyUser + ":" + m_sProxyPass );
409 header += "\xd\xa";
410 }
411 header += "Host: ";
412 header += m_sHost;
413 header += "\xd\xa";
414
415 if ( m_eUrlMethod == eumPOST )
416 {
417 header += "Content-Type: text/plain";
418 header += "\xd\xa";
419 header += "Content-Length: ";
420 header += CString::number(m_sPostData.Length());
421 header += "\xd\xa";
422 }
423
424 header += "\xd\xa";
425
426 if ( m_eUrlMethod == eumPOST )
427 {
428 header += m_sPostData;
429 }
430
431 if ( Write( (const unsigned char*)header.Data(), header.Length() ) != 0 )
432 {
433 m_eMode = estNONE;
434 }
435 else
436 {
437 m_eMode = estTRANSFERDOWNLOAD;
438 }
439 }
440 }
441
442 /** */
DataTimeout()443 void CHttp::DataTimeout()
444 {
445 Disconnect(true);
446 printf("data timeout\n");
447 }
448
449 /** */
Notify()450 void CHttp::Notify()
451 {
452 CDCMessage * dcmessage = 0;
453
454 while ( (dcmessage=m_pMessageList->Next(0)) != 0 )
455 {
456 m_pMessageList->Remove(dcmessage);
457 CallBack_SendObject(dcmessage);
458 }
459 }
460
461 /** */
Encode(CString s)462 CString CHttp::Encode( CString s )
463 {
464 CString s1;
465
466 s1 = s.Replace("&","&");
467 s1 = s1.Replace(">",">");
468 s1 = s1.Replace("<","<");
469 s1 = s1.Replace("\x7f","□");
470 s1 = s1.Replace("=","=");
471 s1 = s1.Replace(" "," ");
472
473 return s1;
474 }
475
476 /** */
Decode(CString s)477 CString CHttp::Decode( CString s )
478 {
479 CString s1;
480
481 s1 = s.Replace(">",">");
482 s1 = s1.Replace("<","<");
483 s1 = s1.Replace("□","\x7f");
484 s1 = s1.Replace("=","=");
485 s1 = s1.Replace(" "," ");
486 s1 = s1.Replace("&","&");
487
488 return s1;
489 }
490
491 /** http_proxy=http://username:password@host:port/ */
ParseProxy(char * data,CString & user,CString & pass,CString & host,unsigned int & port)492 bool CHttp::ParseProxy( char * data, CString & user, CString & pass, CString & host, unsigned int & port )
493 {
494 if ( data == 0 )
495 {
496 return false;
497 }
498
499 int i;
500 CString temp;
501 CString tempuser;
502 CString temppass;
503 CString temphost;
504 unsigned int tempport = 8080;
505
506 CString datastring(data);
507 datastring = datastring.ToLower();
508
509 // check scheme
510 i = datastring.Find("://");
511 if ( i != -1 )
512 {
513 temp = datastring.Mid(0,i);
514 if ( temp != "http" )
515 {
516 printf("CHttp::ParseProxy unsupported scheme '%s'\n", temp.Data());
517 return false;
518 }
519 datastring = datastring.Mid(i+3);
520 }
521
522 // check for optional user:password
523 i = datastring.Find('@');
524 if ( i != -1 )
525 {
526 tempuser = datastring.Mid(0,i);
527 datastring = datastring.Mid(i+1);
528
529 int i2 = tempuser.Find(':');
530 if ( i2 != -1 )
531 {
532 temppass = tempuser.Mid(i2+1);
533 tempuser = tempuser.Mid(0,i2);
534 }
535 }
536
537 // remove any path
538 // what is this for, does a path in a proxy address make any sense?
539 i = datastring.Find('/');
540 if ( i < 0 )
541 {
542 //printf("CHTTP: no '/' at the end of your proxy found.\n");
543 //printf("CHTTP: Check your http_proxy environment variable !\n");
544 //printf("CHTTP: Trying without ...\n");
545 }
546 else
547 {
548 datastring = datastring.Mid(0,i);
549 }
550
551 // check for optional port
552 i = datastring.Find(':');
553 if ( i != -1 )
554 {
555 temphost = datastring.Mid(0,i);
556 tempport = datastring.Mid(i+1).asUINT();
557 }
558 else
559 {
560 temphost = datastring;
561 }
562
563 // sanity check
564 if ( (temphost.IsEmpty()) || (tempport > 65535) || (tempport <= 0) )
565 {
566 printf("CHttp::ParseProxy validation failed\n");
567 return false;
568 }
569 else
570 {
571 user = tempuser;
572 pass = temppass;
573 host = temphost;
574 port = tempport;
575 return true;
576 }
577 }
578