1 /***************************************************************************
2                           chublistmanager.cpp  -  description
3                              -------------------
4     begin                : Don Mai 16 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 "chublistmanager.h"
19 
20 #include "cconfig.h"
21 #include "core/cmanager.h"
22 #include "dcobject.h"
23 #include "core/cbytearray.h"
24 #include "chttp.h"
25 #include "core/cbz.h"
26 #include "core/cxml.h"
27 #include "core/ccallback.h"
28 #include "core/ciconv.h"
29 
30 #ifndef WIN32
31 #include <unistd.h>
32 #include <stdlib.h>
33 #endif
34 
35 #include <stdio.h>
36 
37 /** */
CHubListManager()38 CHubListManager::CHubListManager()
39 {
40 	m_pCallback = new CCallback0<CHubListManager>( this, &CHubListManager::Callback );
41 	CManager::Instance()->Add( m_pCallback );
42 
43 	if ( CConfig::Instance()->GetReloadHubListTime() != 0 )
44 	{
45 		m_nReloadHubListTimeout = time(0) + CConfig::Instance()->GetReloadHubListTime()*60*60;
46 	}
47 	else
48 	{
49 		m_nReloadHubListTimeout = 0;
50 	}
51 
52 	// get hublist stuff
53 	m_pHttp           = 0;
54 	m_pHubListUrlList = 0;
55 	m_pHubListUrl     = 0;
56 	m_pHubListData    = 0;
57 
58 	m_bGetHubListDone = false;
59 
60 	m_pXml = 0;
61 	m_pXmlHubs = new CList<DCConfigHubItem>;
62 }
63 
64 /** */
~CHubListManager()65 CHubListManager::~CHubListManager()
66 {
67 	m_Thread.Stop();
68 
69 	SetInstance(0);
70 
71 	CManager::Instance()->Remove( m_pCallback );
72 
73 	delete m_pCallback;
74 	m_pCallback = 0;
75 
76 	delete m_pXml;
77 	m_pXml = 0;
78 
79 	delete m_pXmlHubs;
80 	m_pXmlHubs = 0;
81 }
82 
83 /** thread callbackfunction */
Callback()84 int CHubListManager::Callback()
85 {
86 	m_Thread.Lock();
87 
88 	// check reload hublist timeout
89 	if ( CConfig::Instance() )
90 	{
91 		if ( CConfig::Instance()->GetReloadHubListTime() != 0 )
92 		{
93 			if ( m_nReloadHubListTimeout != 0 )
94 			{
95 				if ( time(0) >= m_nReloadHubListTimeout )
96 				{
97 					GetPublicHubList();
98 
99 					m_nReloadHubListTimeout = time(0) + CConfig::Instance()->GetReloadHubListTime()*60*60;
100 				}
101 			}
102 			else // change from config
103 			{
104 				m_nReloadHubListTimeout = time(0) + CConfig::Instance()->GetReloadHubListTime()*60*60;
105 			}
106 		}
107 		else
108 		{
109 			m_nReloadHubListTimeout = 0;
110 		}
111 	}
112 
113 	// cleanup all objects
114 	if ( m_bGetHubListDone )
115 	{
116 		delete m_pHttp;
117 		m_pHttp = 0;
118 
119 		delete m_pHubListUrlList;
120 		m_pHubListUrlList = 0;
121 
122 		delete m_pHubListData;
123 		m_pHubListData = 0;
124 
125 		if ( m_pXmlHubs )
126 		{
127 			m_pXmlHubs->Clear();
128 		}
129 
130 		m_pHubListUrl = 0;
131 
132 		DCMessageGetHubList * msg = new DCMessageGetHubList();
133 
134 		msg->m_bRun = false;
135 
136 		if ( DC_CallBack(msg) == -1 )
137 		{
138 			delete msg;
139 		}
140 
141 		m_bGetHubListDone = false;
142 	}
143 
144 	m_Thread.UnLock();
145 
146 	return 0;
147 }
148 
149 /** http callback function */
HttpCallBack(CDCMessage * dcmsg)150 int CHubListManager::HttpCallBack( CDCMessage * dcmsg )
151 {
152 	CByteArray in;
153 
154 	switch ( dcmsg->m_eType )
155 	{
156 		case DC_MESSAGE_CONNECTION_STATE:
157 		{
158 			CMessageConnectionState *msg = (CMessageConnectionState*)dcmsg;
159 
160 			if ( msg->m_eState == estDISCONNECTED )
161 			{
162 				if ( (m_pHttp->GetHttpError() == 200) && m_pHttp->GetData(&in) )
163 				{
164 					HandleHubListData( m_pHttp->GetUrl(), &in );
165 				}
166 
167 				// redirect
168 				if ( m_pHttp->GetHttpError() == 302 )
169 				{
170 					m_pHttp->GetUrl(m_pHttp->GetLocation());
171 				}
172 				else if ( NextHubListUrl() == false )
173 				{
174 					// parse public hub list in own thread
175 					m_Thread.SetThreadCallBackFunction( new CCallback0<CHubListManager>( this, &CHubListManager::ParsePublicHubList ) );
176 					m_Thread.Start();
177 				}
178 			}
179 
180 			break;
181 		}
182 
183 		case DC_MESSAGE_TRANSFER:
184 		{
185 			if ( DC_CallBack( (CMessageTransfer*)dcmsg ) != -1 )
186 			{
187 				dcmsg = 0;
188 			}
189 
190 			break;
191 		}
192 
193 		default:
194 		{
195 			break;
196 		}
197 	}
198 
199 	if ( dcmsg )
200 	{
201 		delete dcmsg;
202 	}
203 
204 	return 0;
205 }
206 
207 /** */
HandleHubListData(const CString & url,CByteArray * in)208 void CHubListManager::HandleHubListData( const CString & url, CByteArray * in )
209 {
210 	CByteArray out;
211 
212 	if ( url.Right(4).ToLower() == ".bz2" )
213 	{
214 		if ( CBZ::Decompress( in, &out ) )
215 		{
216 			if ( url.Right(8).ToLower() == ".xml.bz2" )
217 			{
218 				if ( m_pXml == 0 )
219 				{
220 					m_pXml = new CXml();
221 				}
222 
223 				if ( m_pXml->ParseFixMemory(&out) && m_pXml->DocFirstChild() )
224 				{
225 					ParseXmlPublicHubList();
226 				}
227 				else
228 				{
229 					printf("Failed to parse XML hublist.\n");
230 				}
231 
232 				delete m_pXml;
233 				m_pXml = 0;
234 			}
235 			else
236 			{
237 				m_pHubListData->Append(out.Data(),out.Size());
238 				m_pHubListData->Append("\xD\xA",2);
239 			}
240 		}
241 		else
242 		{
243 			printf("bz2 decompress failed\n");
244 		}
245 	}
246 	else
247 	{
248 		if ( url.Right(4).ToLower() == ".xml" )
249 		{
250 			if ( m_pXml == 0 )
251 			{
252 				m_pXml = new CXml();
253 			}
254 
255 			if ( m_pXml->ParseFixMemory(in) && m_pXml->DocFirstChild() )
256 			{
257 				ParseXmlPublicHubList();
258 			}
259 			else
260 			{
261 				printf("Failed to parse XML hublist.\n");
262 			}
263 
264 			delete m_pXml;
265 			m_pXml = 0;
266 		}
267 		else
268 		{
269 			m_pHubListData->Append(in->Data(),in->Size());
270 			m_pHubListData->Append("\xD\xA",2);
271 		}
272 	}
273 }
274 
275 /** */
GetPublicHubList()276 bool CHubListManager::GetPublicHubList()
277 {
278 	bool res = false;
279 
280 	if ( m_pHttp != 0 )
281 	{
282 		// get hublist allready running
283 		return res;
284 	}
285 
286 	m_pHubListUrlList = new CList<DCConfigHubListUrl>();
287 
288 	// get all hublist urls
289 	CConfig::Instance()->GetHubListUrlList(m_pHubListUrlList);
290 
291 	// check hublist url count
292 	if ( m_pHubListUrlList->Count() == 0 )
293 	{
294 		delete m_pHubListUrlList;
295 		m_pHubListUrlList = 0;
296 		return res;
297 	}
298 
299 	// init first url
300 	m_pHubListUrl = 0;
301 
302 	m_pHubListData = new CByteArray();
303 	m_pHttp = new CHttp();
304 	m_pHttp->SetCallBackFunction( new CCallback1<CHubListManager, CDCMessage*>( this, &CHubListManager::HttpCallBack ) );
305 
306 	res = NextHubListUrl();
307 
308 	if ( !res )
309 	{
310 		m_bGetHubListDone = true;
311 	}
312 	else
313 	{
314 		DCMessageGetHubList * msg = new DCMessageGetHubList();
315 
316 		msg->m_bRun = true;
317 
318 		if ( DC_CallBack(msg) == -1 )
319 		{
320 			delete msg;
321 		}
322 	}
323 
324 	return res;
325 }
326 
327 /** */
ParsePublicHubList()328 int CHubListManager::ParsePublicHubList()
329 {
330 	CString line;
331 	CString s;
332 	CString s1,s2,s3,s4;
333 	long i=0,i1=0;
334 
335 	DCConfigHubItem * hubitem = 0;
336 
337 	if ( (m_pHubListData->Size() == 0) && (m_pXmlHubs->Count() == 0) )
338 	{
339 		m_Thread.Stop(false);
340 		m_Thread.SetThreadCallBackFunction(0);
341 		m_bGetHubListDone = true;
342 
343 		return 0;
344 	}
345 
346 	while ( (hubitem = m_pXmlHubs->Next(hubitem)) != 0 )
347 	{
348 		// name, host, description, usercount, country, shared, minshare, extra
349 		CConfig::Instance()->AddPublicHub(
350 			hubitem->m_sName,
351 			hubitem->m_sHost,
352 			hubitem->m_sDescription,
353 			hubitem->m_nUserCount,
354 			hubitem->m_sCountry,
355 			hubitem->m_nShared,
356 			hubitem->m_nMinShare,
357 			hubitem->m_sExtra
358 		);
359 	}
360 
361 	if ( m_pHubListData->Size() > 0 )
362 	{
363 
364 	s.Set((const char*)m_pHubListData->Data(),m_pHubListData->Size());
365 
366 	CIconv * pIconv = new CIconv( CConfig::Instance()->GetRemoteEncoding(), CConfig::Instance()->GetLocalEncoding() );
367 
368 	while( (i = s.Find(0x0d,i)) != -1 )
369 	{
370 		line = s.Mid(i1,i-i1);
371 
372 		if ( !line.IsEmpty() )
373 		{
374 			s1 = line.Section( '|', 0, 0 );
375 			s2 = line.Section( '|', 1, 1 );
376 			s3 = line.Section( '|', 2, 2 );
377 			s4 = line.Section( '|', 3, 3 );
378 
379 			// replace all spaces
380 			s2 = s2.Replace(" ","");
381 
382 			// remove line ends
383 			s1 = s1.Replace("\n", "");
384 
385 			// name, host, description, usercount
386 			CConfig::Instance()->AddPublicHub( pIconv->encode(s1), pIconv->encode(s2), pIconv->encode(s3), s4.asULL() );
387 		}
388 
389 		i1 = i+2;
390 		i += 2;
391 	}
392 
393 	delete pIconv;
394 
395 	}
396 
397 	// store the list
398 	if ( CConfig::Instance()->GetHubListStoreLocal() )
399 	{
400 		CConfig::Instance()->SaveDCHub();
401 	}
402 
403 	m_Thread.Stop(false);
404 	m_Thread.SetThreadCallBackFunction(0);
405 	m_bGetHubListDone = true;
406 
407 	return 0;
408 }
409 
410 /** */
NextHubListUrl()411 bool CHubListManager::NextHubListUrl()
412 {
413 	bool res = false;
414 
415 	while( (m_pHubListUrl=m_pHubListUrlList->Next(m_pHubListUrl)) != 0 )
416 	{
417 		if ( m_pHubListUrl->bEnabled )
418 		{
419 			if ( m_pHubListUrl->sUrl.Left(7) == "file://" )
420 			{
421 				CByteArray * data = new CByteArray();
422 				if ( data->LoadFromFile(m_pHubListUrl->sUrl.Mid(7)) )
423 				{
424 					HandleHubListData( m_pHubListUrl->sUrl, data );
425 				}
426 				delete data;
427 
428 				if ( NextHubListUrl() == false )
429 				{
430 					// parse public hub list in own thread
431 					m_Thread.SetThreadCallBackFunction( new CCallback0<CHubListManager>( this, &CHubListManager::ParsePublicHubList ) );
432 					m_Thread.Start();
433 				}
434 
435 				res = true;
436 				break;
437 			}
438 			else if ( m_pHubListUrl->sUrl.NotEmpty() )
439 			{
440 				m_pHttp->GetUrl(m_pHubListUrl->sUrl);
441 
442 				res = true;
443 				break;
444 			}
445 		}
446 	}
447 
448 	return res;
449 }
450 
451 /** */
ParseXmlPublicHubList()452 int CHubListManager::ParseXmlPublicHubList()
453 {
454 	int count = 0;
455 
456 	printf("Parse XML hub list...\n");
457 
458 	do
459 	{
460 		if ( (m_pXml->Name() == "Hublist") && m_pXml->FirstChild() )
461 		{
462 			CList<CXmlColumn> * cols = FindAndParseXmlColumns();
463 
464 			if ( !cols )
465 			{
466 				/*
467 				 * The column headings are only needed so that the Extra
468 				 * field is filled in.
469 				 */
470 				printf("ParseXmlPublicHubList: no column headings found, trying with defaults\n");
471 
472 				cols = new CList<CXmlColumn>;
473 
474 				CXmlColumn * xmlcol = new CXmlColumn();
475 				xmlcol->m_sName = "Name";
476 				xmlcol->m_sType = "string";
477 				cols->Add(xmlcol);
478 
479 				xmlcol = new CXmlColumn();
480 				xmlcol->m_sName = "Address";
481 				xmlcol->m_sType = "string";
482 				cols->Add(xmlcol);
483 
484 				xmlcol = new CXmlColumn();
485 				xmlcol->m_sName = "Description";
486 				xmlcol->m_sType = "string";
487 				cols->Add(xmlcol);
488 
489 				xmlcol = new CXmlColumn();
490 				xmlcol->m_sName = "Port";
491 				xmlcol->m_sType = "int";
492 				cols->Add(xmlcol);
493 
494 				xmlcol = new CXmlColumn();
495 				xmlcol->m_sName = "Users";
496 				xmlcol->m_sType = "int";
497 				cols->Add(xmlcol);
498 			}
499 
500 			/* go back to start */
501 			m_pXml->DocFirstChild();
502 			m_pXml->FirstChild();
503 
504 			do
505 			{
506 				if ( (m_pXml->Name() == "Hubs") && m_pXml->FirstChild() )
507 				{
508 					count += ParseXmlHubs( cols );
509 					m_pXml->Parent();
510 				}
511 			}
512 			while ( m_pXml->NextNode() );
513 			m_pXml->Parent();
514 
515 			cols->Clear();
516 			delete cols;
517 		}
518 	}
519 	while ( m_pXml->NextNode() );
520 
521 	printf("XML hublist: %d hubs\n", count);
522 
523 	return count;
524 }
525 
526 /** */
ParseXmlHubs(CList<CXmlColumn> * cols)527 int CHubListManager::ParseXmlHubs( CList<CXmlColumn> * cols )
528 {
529 	int count = 0;
530 
531 	do
532 	{
533 		if ( (m_pXml->Name() == "Hub") )
534 		{
535 			ParseXmlHub(cols);
536 			count++;
537 		}
538 	}
539 	while ( m_pXml->NextNode() );
540 
541 	return count;
542 }
543 
544 /** */
FindAndParseXmlColumns()545 CList<CXmlColumn> * CHubListManager::FindAndParseXmlColumns()
546 {
547 	CList<CXmlColumn> * cols = 0;
548 
549 	do
550 	{
551 		if ( (m_pXml->Name() == "Columns") && m_pXml->FirstChild() )
552 		{
553 			cols = new CList<CXmlColumn>;
554 			do
555 			{
556 				if ( m_pXml->Name() == "Column" )
557 				{
558 					CXmlColumn * col = new CXmlColumn();
559 					col->m_sName = m_pXml->Prop("Name");
560 					col->m_sType = m_pXml->Prop("Type");
561 					cols->Add(col);
562 				}
563 			}
564 			while ( m_pXml->NextNode() );
565 			break;
566 		}
567 
568 		if ( m_pXml->FirstChild() )
569 		{
570 			do
571 			{
572 				if ( (m_pXml->Name() == "Columns") && m_pXml->FirstChild() )
573 				{
574 					cols = new CList<CXmlColumn>;
575 					do
576 					{
577 						if ( m_pXml->Name() == "Column" )
578 						{
579 							CXmlColumn * col = new CXmlColumn();
580 							col->m_sName = m_pXml->Prop("Name");
581 							col->m_sType = m_pXml->Prop("Type");
582 							cols->Add(col);
583 						}
584 					}
585 					while ( m_pXml->NextNode() );
586 					break;
587 				}
588 			}
589 			while ( m_pXml->NextNode() );
590 
591 			if ( cols )
592 			{
593 				break;
594 			}
595 		}
596 	}
597 	while ( m_pXml->NextNode() );
598 
599 	/* no CXml::Parent() called since ParseXmlPublicHubList() just goes back to the start */
600 
601 	return cols;
602 }
603 
604 /** */
ParseXmlHub(CList<CXmlColumn> * cols)605 void CHubListManager::ParseXmlHub( CList<CXmlColumn> * cols )
606 {
607 	CXmlColumn * col = 0;
608 	DCConfigHubItem * hubitem = new DCConfigHubItem();
609 	CString port,namelc;
610 
611 	while ( (col = cols->Next(col)) != 0 )
612 	{
613 		col->m_sValue = m_pXml->Prop(col->m_sName);
614 
615 		namelc = col->m_sName.ToLower();
616 		if (namelc == "name")
617 		{
618 			hubitem->m_sName = col->m_sValue;
619 		}
620 		else if (namelc == "address")
621 		{
622 			hubitem->m_sHost = col->m_sValue;
623 		}
624 		else if (namelc == "description")
625 		{
626 			hubitem->m_sDescription = col->m_sValue;
627 		}
628 		else if (namelc == "users")
629 		{
630 			hubitem->m_nUserCount = col->m_sValue.asULL();
631 		}
632 		else if (namelc == "port")
633 		{
634 			port = col->m_sValue;
635 		}
636 		else if (namelc == "country")
637 		{
638 			hubitem->m_sCountry = col->m_sValue;
639 		}
640 		else if (namelc == "minshare")
641 		{
642 			hubitem->m_nMinShare = col->m_sValue.asULL();
643 		}
644 		else if (namelc == "shared")
645 		{
646 			hubitem->m_nShared = col->m_sValue.asULL();
647 		}
648 		else
649 		{
650 			hubitem->m_sExtra += col->m_sName;
651 			hubitem->m_sExtra += '=';
652 			hubitem->m_sExtra += col->m_sValue;
653 			hubitem->m_sExtra += ' ';
654 		}
655 
656 		//printf("Name=%s Type=%s Value=%s\n", col->m_sName.Data(), col->m_sType.Data(), col->m_sValue.Data());
657 	}
658 
659 	if ( (hubitem->m_sHost.Find(':') < 0) && (port.NotEmpty()) )
660 	{
661 		hubitem->m_sHost += ':';
662 		hubitem->m_sHost += port;
663 	}
664 
665 	if ( hubitem->m_sName.IsEmpty() || hubitem->m_sHost.IsEmpty() )
666 	{
667 		delete hubitem;
668 	}
669 	else
670 	{
671 		m_pXmlHubs->Add(hubitem);
672 	}
673 }
674