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