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("&","&amp;");
467 	s1 = s1.Replace(">","&gt;");
468 	s1 = s1.Replace("<","&lt;");
469 	s1 = s1.Replace("\x7f","&#x25a1;");
470 	s1 = s1.Replace("=","&#61;");
471 	s1 = s1.Replace(" ","&#32;");
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("&gt;",">");
482 	s1 = s1.Replace("&lt;","<");
483 	s1 = s1.Replace("&#x25a1;","\x7f");
484 	s1 = s1.Replace("&#61;","=");
485 	s1 = s1.Replace("&#32;"," ");
486 	s1 = s1.Replace("&amp;","&");
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