1 //=============================================================================
2 //
3 //   File : KviIrcServerDataBase.cpp
4 //   Creation date : Mon Jul 10 2000 14:25:00 by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC client distribution
7 //   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 //   This program is FREE software. You can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License
11 //   as published by the Free Software Foundation; either version 2
12 //   of the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the HOPE that it will be USEFUL,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 //   See the GNU General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public License
20 //   along with this program. If not, write to the Free Software Foundation,
21 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24 
25 #include "KviIrcServerDataBase.h"
26 #include "KviConfigurationFile.h"
27 #include "KviIrcNetwork.h"
28 #include "KviIrcServer.h"
29 #include "KviLocale.h"
30 #include "KviNetUtils.h"
31 #include "KviNickServRuleSet.h"
32 
33 #include <QApplication>
34 #include <QLayout>
35 #include <QMessageBox>
36 #include <QCheckBox>
37 
KviIrcServerDataBase()38 KviIrcServerDataBase::KviIrcServerDataBase()
39 {
40 	m_pRecords = new KviPointerHashTable<QString, KviIrcNetwork>(17, false);
41 	m_pRecords->setAutoDelete(true);
42 	m_pAutoConnectOnStartupServers = nullptr;
43 	m_pAutoConnectOnStartupNetworks = nullptr;
44 }
45 
~KviIrcServerDataBase()46 KviIrcServerDataBase::~KviIrcServerDataBase()
47 {
48 	delete m_pRecords;
49 	if(m_pAutoConnectOnStartupServers)
50 		delete m_pAutoConnectOnStartupServers;
51 	if(m_pAutoConnectOnStartupNetworks)
52 		delete m_pAutoConnectOnStartupNetworks;
53 }
54 
clearAutoConnectOnStartupServers()55 void KviIrcServerDataBase::clearAutoConnectOnStartupServers()
56 {
57 	if(!m_pAutoConnectOnStartupServers)
58 		return;
59 
60 	delete m_pAutoConnectOnStartupServers;
61 	m_pAutoConnectOnStartupServers = nullptr;
62 }
63 
clearAutoConnectOnStartupNetworks()64 void KviIrcServerDataBase::clearAutoConnectOnStartupNetworks()
65 {
66 	if(!m_pAutoConnectOnStartupNetworks)
67 		return;
68 
69 	delete m_pAutoConnectOnStartupNetworks;
70 	m_pAutoConnectOnStartupNetworks = nullptr;
71 }
72 
clear()73 void KviIrcServerDataBase::clear()
74 {
75 	m_pRecords->clear();
76 	m_szCurrentNetwork = "";
77 }
78 
addNetwork(KviIrcNetwork * pNet)79 void KviIrcServerDataBase::addNetwork(KviIrcNetwork * pNet)
80 {
81 	m_pRecords->replace(pNet->name(), pNet);
82 }
83 
findNetwork(const QString & szName)84 KviIrcNetwork * KviIrcServerDataBase::findNetwork(const QString & szName)
85 {
86 	KviIrcNetwork * pNet = m_pRecords->find(szName);
87 	return pNet;
88 }
89 
networkCount() const90 unsigned int KviIrcServerDataBase::networkCount() const
91 {
92 	return m_pRecords->count();
93 }
94 
currentNetwork()95 KviIrcNetwork * KviIrcServerDataBase::currentNetwork()
96 {
97 	KviIrcNetwork * pNet = nullptr;
98 	if(!m_szCurrentNetwork.isEmpty())
99 		pNet = m_pRecords->find(m_szCurrentNetwork);
100 	if(pNet)
101 		return pNet;
102 
103 	return nullptr;
104 }
105 
makeCurrentBestServerInNetwork(const QString & szNetName,KviIrcNetwork * pNet,QString & szError)106 bool KviIrcServerDataBase::makeCurrentBestServerInNetwork(const QString & szNetName, KviIrcNetwork * pNet, QString & szError)
107 {
108 	m_szCurrentNetwork = szNetName;
109 	// find a round-robin server in that network
110 
111 	if(pNet->m_pServerList->isEmpty())
112 	{
113 		szError = __tr2qs("The specified network has no server entries");
114 		return false;
115 	}
116 
117 	for(KviIrcServer * pServ = pNet->m_pServerList->first(); pServ; pServ = pNet->m_pServerList->next())
118 	{
119 		if(pServ->description().contains("random", Qt::CaseInsensitive) || (pServ->description().contains("round", Qt::CaseInsensitive) && (pServ->description().contains("robin", Qt::CaseInsensitive))))
120 		{
121 			pNet->setCurrentServer(pServ);
122 			return true;
123 		}
124 	}
125 
126 	// no explicit round robin... try some common names
127 
128 	QString szTryAlso1, szTryAlso2, szTryAlso3;
129 
130 	szTryAlso1 = QString("irc.%1.org").arg(szNetName);
131 	szTryAlso2 = QString("irc.%1.net").arg(szNetName);
132 	szTryAlso3 = QString("irc.%1.com").arg(szNetName);
133 
134 	for(KviIrcServer * pServer = pNet->m_pServerList->first(); pServer; pServer = pNet->m_pServerList->next())
135 	{
136 		if(KviQString::equalCI(pServer->hostName(), szTryAlso1) || KviQString::equalCI(pServer->hostName(), szTryAlso2) || KviQString::equalCI(pServer->hostName(), szTryAlso3))
137 		{
138 			pNet->setCurrentServer(pServer);
139 			return true;
140 		}
141 	}
142 
143 	// a random one in this network
144 	return true;
145 }
146 
makeCurrentServer(KviIrcServerDefinition * pDef,QString & szError)147 bool KviIrcServerDataBase::makeCurrentServer(KviIrcServerDefinition * pDef, QString & szError)
148 {
149 	KviIrcServer * pServer = nullptr;
150 
151 	KviPointerHashTableIterator<QString, KviIrcNetwork> it(*m_pRecords);
152 	KviIrcNetwork * pNet = nullptr;
153 	KviIrcServer * pServ;
154 
155 	if(KviQString::equalCIN(pDef->szServer, "net:", 4))
156 	{
157 		// net:networkname form
158 		QString szNet = pDef->szServer;
159 		szNet.remove(0, 4);
160 		KviIrcNetwork * pNet = m_pRecords->find(szNet);
161 		if(pNet)
162 			return makeCurrentBestServerInNetwork(szNet, pNet, szError);
163 		szError = __tr2qs("The server specification seems to be in the net:<string> but the network couldn't be found in the database");
164 		return false;
165 	}
166 
167 	if(KviQString::equalCIN(pDef->szServer, "id:", 3))
168 	{
169 		// id:serverid form
170 		QString szId = pDef->szServer;
171 		szId.remove(0, 3);
172 
173 		while((pNet = it.current()))
174 		{
175 			for(pServ = pNet->serverList()->first(); pServ && (!pServer); pServ = pNet->serverList()->next())
176 			{
177 				if(KviQString::equalCI(pServ->id(), szId))
178 				{
179 					pServer = pServ;
180 					goto search_finished;
181 				}
182 			}
183 			++it;
184 		}
185 		szError = __tr2qs("The server specification seems to be in the id:<string> form but the identifier couldn't be found in the database");
186 		return false;
187 	}
188 
189 	it.toFirst();
190 
191 	while((pNet = it.current()))
192 	{
193 		for(pServ = pNet->serverList()->first(); pServ && (!pServer); pServ = pNet->serverList()->next())
194 		{
195 			if(KviQString::equalCI(pServ->hostName(), pDef->szServer))
196 			{
197 				if(pDef->szId.isEmpty() || KviQString::equalCI(pServ->id(), pDef->szId))
198 				{
199 					if(pDef->bIPv6 == pServ->isIPv6())
200 					{
201 						if(pDef->bSSL == pServ->useSSL())
202 						{
203 							if(pDef->bPortIsValid)
204 							{
205 								// must match the port
206 								if(pDef->uPort == pServ->port())
207 								{
208 									// port matches
209 									if(!pDef->szLinkFilter.isEmpty())
210 									{
211 										// must match the link filter
212 										if(KviQString::equalCI(pDef->szLinkFilter, pServ->linkFilter()))
213 										{
214 											// link filter matches
215 											pServer = pServ;
216 											goto search_finished;
217 										} // else link filter doesn't match
218 									}
219 									else
220 									{
221 										// no need to match the link filter
222 										pServer = pServ;
223 										goto search_finished;
224 									}
225 								} // else port doesn't match
226 							}
227 							else
228 							{
229 								// no need to match the port
230 								if(!pDef->szLinkFilter.isEmpty())
231 								{
232 									// must match the link filter
233 									if(KviQString::equalCI(pDef->szLinkFilter, pServ->linkFilter()))
234 									{
235 										// link filter matches
236 										pServer = pServ;
237 										goto search_finished;
238 									} // else link filter doesn't match
239 								}
240 								else
241 								{
242 									// no need to match the link filter
243 									pServer = pServ;
244 									goto search_finished;
245 								}
246 							}
247 						}
248 					}
249 				}
250 			}
251 		}
252 		++it;
253 	}
254 
255 search_finished:
256 
257 	if(pNet && pServer)
258 	{
259 		m_szCurrentNetwork = pNet->name();
260 		pNet->setCurrentServer(pServer);
261 		return true;
262 	}
263 
264 	// no such server: is it a valid ip address or hostname ?
265 	bool bIsValidIPv4 = KviNetUtils::isValidStringIp(pDef->szServer);
266 #ifdef COMPILE_IPV6_SUPPORT
267 	bool bIsValidIPv6 = KviNetUtils::isValidStringIPv6(pDef->szServer);
268 #else
269 	bool bIsValidIPv6 = false;
270 #endif
271 
272 	if(!(bIsValidIPv4 || bIsValidIPv6))
273 	{
274 		// is it a valid hostname ? (must contain at least one dot)
275 		if(!pDef->szServer.contains('.'))
276 		{
277 			// assume it is a network name!
278 			KviIrcNetwork * pNet = m_pRecords->find(pDef->szServer);
279 			if(pNet)
280 				return makeCurrentBestServerInNetwork(pDef->szServer, pNet, szError);
281 			// else probably not a network name
282 		}
283 	}
284 
285 	// a valid hostname or ip address, not found in list : add it and make it current
286 
287 	pNet = m_pRecords->find(__tr2qs("Standalone Servers"));
288 	if(!pNet)
289 	{
290 		pNet = new KviIrcNetwork(__tr2qs("Standalone Servers"));
291 		m_pRecords->replace(pNet->name(), pNet);
292 	}
293 
294 	KviIrcServer * pSrv = new KviIrcServer();
295 	pSrv->setHostName(pDef->szServer);
296 	if(bIsValidIPv4)
297 	{
298 		pSrv->setIp(pDef->szServer);
299 		pSrv->setCacheIp(true);
300 #ifdef COMPILE_IPV6_SUPPORT
301 	}
302 	else
303 	{
304 		if(bIsValidIPv6)
305 		{
306 			pSrv->setIp(pDef->szServer);
307 			pSrv->setCacheIp(true);
308 			pDef->bIPv6 = true;
309 		}
310 	}
311 #else
312 	}
313 #endif
314 	pSrv->setPort(pDef->bPortIsValid ? pDef->uPort : 6667);
315 	pSrv->setLinkFilter(pDef->szLinkFilter);
316 	pSrv->setPassword(pDef->szPass);
317 	pSrv->setNickName(pDef->szNick);
318 	pSrv->setInitUMode(pDef->szInitUMode);
319 	pSrv->setIPv6(pDef->bIPv6);
320 	pSrv->setUseSSL(pDef->bSSL);
321 	pSrv->setEnabledSTARTTLS(pDef->bSTARTTLS);
322 	pNet->insertServer(pSrv);
323 	m_szCurrentNetwork = pNet->name();
324 	pNet->setCurrentServer(pSrv);
325 
326 	return true;
327 }
328 
parseMircServerRecord(QString szEntry,QString & szNet,QString & szDescription,QString & szHost,QString & szPort,bool & bSsl,kvi_u32_t & uPort)329 void parseMircServerRecord(QString szEntry, QString & szNet,
330     QString & szDescription, QString & szHost, QString & szPort, bool & bSsl, kvi_u32_t & uPort)
331 {
332 	bSsl = false;
333 	int iIdx = szEntry.indexOf("SERVER:", 0, Qt::CaseSensitive);
334 	if(iIdx != -1)
335 	{
336 		szDescription = szEntry.left(iIdx);
337 		szNet = szDescription.section(':', 0, 0);
338 		szDescription = szDescription.section(':', 1, 1);
339 
340 		szEntry.remove(0, iIdx + 7);
341 		iIdx = szEntry.indexOf("GROUP:", 0, Qt::CaseSensitive);
342 		if(iIdx != -1)
343 		{
344 			szHost = szEntry.left(iIdx);
345 		}
346 		else
347 		{
348 			szHost = szEntry;
349 		}
350 
351 		szPort = szHost.section(':', 1, 1);
352 		if(szPort[0] == '+')
353 		{
354 			bSsl = true;
355 			szPort.remove(0, 1);
356 		}
357 		szHost = szHost.section(':', 0, 0);
358 
359 		bool bOk;
360 		uPort = szPort.toUInt(&bOk);
361 		if(!bOk)
362 			uPort = 6667;
363 	}
364 }
365 
importFromMircIni(const QString & szFilename,const QString & szMircIni,QStringList & recentServers)366 void KviIrcServerDataBase::importFromMircIni(const QString & szFilename, const QString & szMircIni, QStringList & recentServers)
367 {
368 	clear();
369 	recentServers.clear();
370 	QString szDefaultServer;
371 	KviConfigurationFile mircCfg(szMircIni, KviConfigurationFile::Read, true);
372 	if(mircCfg.hasGroup("mirc"))
373 	{
374 		mircCfg.setGroup("mirc");
375 		szDefaultServer = mircCfg.readEntry("host");
376 	}
377 
378 	KviConfigurationFile cfg(szFilename, KviConfigurationFile::Read, true);
379 	int i = 0;
380 
381 	QString szEntry;
382 	QString szKey;
383 	if(cfg.hasGroup("recent"))
384 	{
385 		cfg.setGroup("recent");
386 		do
387 		{
388 			szKey = QString("n%1").arg(i);
389 			szEntry = cfg.readEntry(szKey);
390 			if(!szEntry.isEmpty())
391 			{
392 				QString szNet;
393 				QString szDescription;
394 				QString szHost;
395 				QString szPort;
396 				bool bSsl = false;
397 				kvi_u32_t uPort = 0;
398 
399 				parseMircServerRecord(szEntry, szNet,
400 				    szDescription, szHost, szPort, bSsl, uPort);
401 
402 				recentServers << (bSsl ? "ircs://" : "irc://") + szHost + ":" + szPort;
403 			}
404 			i++;
405 		} while(!szEntry.isEmpty());
406 	}
407 
408 	i = 0;
409 	if(cfg.hasGroup("servers"))
410 	{
411 		cfg.setGroup("servers");
412 		do
413 		{
414 			szKey = QString("n%1").arg(i);
415 			szEntry = cfg.readEntry(szKey);
416 			if(!szEntry.isEmpty())
417 			{
418 				bool bDefault = false;
419 				QString szNet;
420 				QString szDescription;
421 				QString szHost;
422 				QString szPort;
423 				bool bSsl = false;
424 				kvi_u32_t uPort = 0;
425 				// <net>:<description>SERVER:<server:port>GROUP:<group???>
426 				if(szEntry == szDefaultServer)
427 					bDefault = true;
428 
429 				parseMircServerRecord(szEntry, szNet,
430 				    szDescription, szHost, szPort, bSsl, uPort);
431 
432 				KviIrcNetwork * pNet = findNetwork(szNet);
433 
434 				if(!pNet)
435 				{
436 					pNet = new KviIrcNetwork(szNet);
437 					addNetwork(pNet);
438 				}
439 
440 				KviIrcServer * pServ = new KviIrcServer();
441 				pServ->setHostName(szHost);
442 				pServ->setDescription(szDescription);
443 				pServ->setPort(uPort);
444 
445 				pNet->m_pServerList->append(pServ);
446 				if(bDefault)
447 				{
448 					m_szCurrentNetwork = szNet;
449 				}
450 			}
451 			i++;
452 		} while(!szEntry.isEmpty());
453 	}
454 }
455 
load(const QString & szFilename)456 void KviIrcServerDataBase::load(const QString & szFilename)
457 {
458 	clear();
459 	KviConfigurationFile cfg(szFilename, KviConfigurationFile::Read);
460 
461 	KviConfigurationFileIterator it(*(cfg.dict()));
462 
463 	QString szTmp;
464 
465 	while(it.current())
466 	{
467 		if(it.current()->count() > 0)
468 		{
469 			KviIrcNetwork * pNewNet = new KviIrcNetwork(it.currentKey());
470 			addNetwork(pNewNet);
471 			cfg.setGroup(it.currentKey());
472 			pNewNet->m_szEncoding = cfg.readEntry("Encoding");
473 			pNewNet->m_szTextEncoding = cfg.readEntry("TextEncoding");
474 			pNewNet->m_szDescription = cfg.readEntry("Description");
475 			pNewNet->m_szNickName = cfg.readEntry("NickName");
476 			pNewNet->m_szAlternativeNickName = cfg.readEntry("AlternativeNickName");
477 			pNewNet->m_szRealName = cfg.readEntry("RealName");
478 			pNewNet->m_szUserName = cfg.readEntry("UserName");
479 			pNewNet->m_szPass = cfg.readEntry("Pass");
480 			pNewNet->m_szOnConnectCommand = cfg.readEntry("OnConnectCommand");
481 			pNewNet->m_szOnLoginCommand = cfg.readEntry("OnLoginCommand");
482 			pNewNet->m_pNickServRuleSet = KviNickServRuleSet::load(&cfg, QString());
483 			pNewNet->m_bAutoConnect = cfg.readBoolEntry("AutoConnect", false);
484 			pNewNet->m_szUserIdentityId = cfg.readEntry("UserIdentityId");
485 			if(pNewNet->m_bAutoConnect)
486 			{
487 				if(!m_pAutoConnectOnStartupNetworks)
488 				{
489 					m_pAutoConnectOnStartupNetworks = new KviPointerList<KviIrcNetwork>;
490 					m_pAutoConnectOnStartupNetworks->setAutoDelete(false);
491 				}
492 				m_pAutoConnectOnStartupNetworks->append(pNewNet);
493 			}
494 			QStringList l = cfg.readStringListEntry("AutoJoinChannels", QStringList());
495 			if(l.count() > 0)
496 				pNewNet->setAutoJoinChannelList(new QStringList(l));
497 
498 			if(cfg.readBoolEntry("Current", false))
499 				m_szCurrentNetwork = it.currentKey();
500 
501 			int nServers = cfg.readIntEntry("NServers", 0);
502 			for(int i = 0; i < nServers; i++)
503 			{
504 				KviIrcServer * pServ = new KviIrcServer();
505 				szTmp = QString("%1_").arg(i);
506 				if(pServ->load(&cfg, szTmp))
507 				{
508 					pNewNet->m_pServerList->append(pServ);
509 					szTmp = QString("%1_Current").arg(i);
510 					if(cfg.readBoolEntry(szTmp, false))
511 						pNewNet->m_pCurrentServer = pServ;
512 					if(pServ->autoConnect())
513 					{
514 						if(!m_pAutoConnectOnStartupServers)
515 						{
516 							m_pAutoConnectOnStartupServers = new KviPointerList<KviIrcServer>;
517 							m_pAutoConnectOnStartupServers->setAutoDelete(false);
518 						}
519 						m_pAutoConnectOnStartupServers->append(pServ);
520 					}
521 				}
522 				else
523 					delete pServ;
524 			}
525 			if(!pNewNet->m_pCurrentServer)
526 				pNewNet->m_pCurrentServer = pNewNet->m_pServerList->first();
527 		}
528 		++it;
529 	}
530 }
531 
save(const QString & szFilename)532 void KviIrcServerDataBase::save(const QString & szFilename)
533 {
534 	KviConfigurationFile cfg(szFilename, KviConfigurationFile::Write);
535 
536 	cfg.clear(); // clear any old entry
537 
538 	KviPointerHashTableIterator<QString, KviIrcNetwork> it(*m_pRecords);
539 
540 	QString szTmp;
541 
542 	while(KviIrcNetwork * pNetwork = it.current())
543 	{
544 		cfg.setGroup(pNetwork->name());
545 		cfg.writeEntry("NServers", pNetwork->m_pServerList->count());
546 		if(pNetwork->m_bAutoConnect)
547 			cfg.writeEntry("AutoConnect", true);
548 		if(!pNetwork->m_szEncoding.isEmpty())
549 			cfg.writeEntry("Encoding", pNetwork->m_szEncoding);
550 		if(!pNetwork->m_szTextEncoding.isEmpty())
551 			cfg.writeEntry("TextEncoding", pNetwork->m_szTextEncoding);
552 		if(!pNetwork->m_szDescription.isEmpty())
553 			cfg.writeEntry("Description", pNetwork->m_szDescription);
554 		if(!pNetwork->m_szNickName.isEmpty())
555 			cfg.writeEntry("NickName", pNetwork->m_szNickName);
556 		if(!pNetwork->m_szAlternativeNickName.isEmpty())
557 			cfg.writeEntry("AlternativeNickName", pNetwork->m_szAlternativeNickName);
558 		if(!pNetwork->m_szRealName.isEmpty())
559 			cfg.writeEntry("RealName", pNetwork->m_szRealName);
560 		if(!pNetwork->m_szUserName.isEmpty())
561 			cfg.writeEntry("UserName", pNetwork->m_szUserName);
562 		if(!pNetwork->m_szPass.isEmpty())
563 			cfg.writeEntry("Pass", pNetwork->m_szPass);
564 		if(!pNetwork->m_szOnConnectCommand.isEmpty())
565 			cfg.writeEntry("OnConnectCommand", pNetwork->m_szOnConnectCommand);
566 		if(!pNetwork->m_szOnLoginCommand.isEmpty())
567 			cfg.writeEntry("OnLoginCommand", pNetwork->m_szOnLoginCommand);
568 		if(pNetwork->m_pNickServRuleSet)
569 			pNetwork->m_pNickServRuleSet->save(&cfg, QString());
570 		if(pNetwork->autoJoinChannelList())
571 			cfg.writeEntry("AutoJoinChannels", *(pNetwork->autoJoinChannelList()));
572 		if(pNetwork->m_szName == m_szCurrentNetwork)
573 			cfg.writeEntry("Current", true);
574 		if(!pNetwork->m_szUserIdentityId.isEmpty())
575 			cfg.writeEntry("UserIdentityId", pNetwork->m_szUserIdentityId);
576 		int i = 0;
577 		for(KviIrcServer * pServ = pNetwork->m_pServerList->first(); pServ; pServ = pNetwork->m_pServerList->next())
578 		{
579 			szTmp = QString("%1_").arg(i);
580 			pServ->save(&cfg, szTmp);
581 
582 			if(pServ == pNetwork->m_pCurrentServer)
583 			{
584 				szTmp = QString("%1_Current").arg(i);
585 				cfg.writeEntry(szTmp, true);
586 			}
587 
588 			i++;
589 		}
590 		++it;
591 	}
592 }
593