~vanhoof/+junk/znc

« back to all changes in this revision

Viewing changes to .pc/01-null-pointer-traffic.diff/znc.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Patrick Matthäi
  • Date: 2010-07-03 13:00:20 UTC
  • mfrom: (1.3.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100703130020-oa8ws14tos8izp22
Tags: 0.092-1
* New upstream release.
  - Remove 01-null-pointer-traffic.diff, merged by upstream.
* Bump Standards-Version to 3.9.0.
  - Use Breaks instead of Conflicts in some situations.
  - Fix BSD license in debian/copyright.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2004-2010  See the AUTHORS file for details.
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify it
5
 
 * under the terms of the GNU General Public License version 2 as published
6
 
 * by the Free Software Foundation.
7
 
 */
8
 
 
9
 
#include "znc.h"
10
 
#include "Chan.h"
11
 
#include "IRCSock.h"
12
 
#include "Server.h"
13
 
#include "User.h"
14
 
#include "Listener.h"
15
 
#include <pwd.h>
16
 
#include <list>
17
 
 
18
 
namespace
19
 
{ // private namespace for local things
20
 
        struct CGlobalModuleConfigLine
21
 
        {
22
 
                CString  m_sName;
23
 
                CString  m_sValue;
24
 
                CUser   *m_pUser;
25
 
                CChan   *m_pChan;
26
 
        };
27
 
}
28
 
 
29
 
CZNC::CZNC() {
30
 
        if (!InitCsocket()) {
31
 
                CUtils::PrintError("Could not initialize Csocket!");
32
 
                exit(-1);
33
 
        }
34
 
 
35
 
        m_pModules = new CGlobalModules();
36
 
        m_pISpoofLockFile = NULL;
37
 
        m_uiConnectDelay = 5;
38
 
        m_uiAnonIPLimit = 10;
39
 
        SetISpoofFormat(""); // Set ISpoofFormat to default
40
 
        m_uBytesRead = 0;
41
 
        m_uBytesWritten = 0;
42
 
        m_pConnectUserTimer = NULL;
43
 
        m_eConfigState = ECONFIG_NOTHING;
44
 
        m_TimeStarted = time(NULL);
45
 
        m_sConnectThrottle.SetTTL(30000);
46
 
}
47
 
 
48
 
CZNC::~CZNC() {
49
 
        if (m_pISpoofLockFile)
50
 
                ReleaseISpoof();
51
 
 
52
 
        m_pModules->UnloadAll();
53
 
 
54
 
        for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
55
 
                a->second->GetModules().UnloadAll();
56
 
        }
57
 
 
58
 
        for (size_t b = 0; b < m_vpListeners.size(); b++) {
59
 
                delete m_vpListeners[b];
60
 
        }
61
 
 
62
 
        for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
63
 
                a->second->SetBeingDeleted(true);
64
 
        }
65
 
 
66
 
        m_pConnectUserTimer = NULL;
67
 
        // This deletes m_pConnectUserTimer
68
 
        m_Manager.Cleanup();
69
 
        DeleteUsers();
70
 
 
71
 
        delete m_pModules;
72
 
 
73
 
        ShutdownCsocket();
74
 
        DeletePidFile();
75
 
}
76
 
 
77
 
CString CZNC::GetVersion() {
78
 
        char szBuf[128];
79
 
 
80
 
        snprintf(szBuf, sizeof(szBuf), "%1.3f"VERSION_EXTRA, VERSION);
81
 
        // If snprintf overflows (which I doubt), we want to be on the safe side
82
 
        szBuf[sizeof(szBuf) - 1] = '\0';
83
 
 
84
 
        return szBuf;
85
 
}
86
 
 
87
 
CString CZNC::GetTag(bool bIncludeVersion) {
88
 
        if (!bIncludeVersion) {
89
 
                return "ZNC - http://znc.sourceforge.net";
90
 
        }
91
 
 
92
 
        char szBuf[128];
93
 
        snprintf(szBuf, sizeof(szBuf), "ZNC %1.3f"VERSION_EXTRA" - http://znc.sourceforge.net", VERSION);
94
 
        // If snprintf overflows (which I doubt), we want to be on the safe side
95
 
        szBuf[sizeof(szBuf) - 1] = '\0';
96
 
 
97
 
        return szBuf;
98
 
}
99
 
 
100
 
CString CZNC::GetUptime() const {
101
 
        time_t now = time(NULL);
102
 
        return CString::ToTimeStr(now - TimeStarted());
103
 
}
104
 
 
105
 
bool CZNC::OnBoot() {
106
 
        if (!GetModules().OnBoot()) {
107
 
                return false;
108
 
        }
109
 
 
110
 
        for (map<CString,CUser*>::iterator it = m_msUsers.begin(); it != m_msUsers.end(); ++it) {
111
 
                if (!it->second->GetModules().OnBoot()) {
112
 
                        return false;
113
 
                }
114
 
        }
115
 
 
116
 
        return true;
117
 
}
118
 
 
119
 
bool CZNC::ConnectUser(CUser *pUser) {
120
 
        CString sSockName = "IRC::" + pUser->GetUserName();
121
 
        // Don't use pUser->GetIRCSock(), as that only returns something if the
122
 
        // CIRCSock is already connected, not when it's still connecting!
123
 
        CIRCSock* pIRCSock = (CIRCSock*) m_Manager.FindSockByName(sSockName);
124
 
 
125
 
        if (m_pISpoofLockFile != NULL) {
126
 
                return false;
127
 
        }
128
 
 
129
 
        if (!pUser->GetIRCConnectEnabled())
130
 
                return false;
131
 
 
132
 
        if (pIRCSock || !pUser->HasServers())
133
 
                return false;
134
 
 
135
 
        if (pUser->ConnectPaused())
136
 
                return false;
137
 
 
138
 
        CServer* pServer = pUser->GetNextServer();
139
 
 
140
 
        if (!pServer)
141
 
                return false;
142
 
 
143
 
        if (m_sConnectThrottle.GetItem(pServer->GetName()))
144
 
                return false;
145
 
 
146
 
        if (!WriteISpoof(pUser)) {
147
 
                DEBUG("ISpoof could not be written");
148
 
                pUser->PutStatus("ISpoof could not be written, retrying...");
149
 
                return true;
150
 
        }
151
 
 
152
 
        m_sConnectThrottle.AddItem(pServer->GetName());
153
 
 
154
 
        DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetName() << " " << pServer->GetPort() << "] ...");
155
 
        pUser->PutStatus("Attempting to connect to [" + pServer->GetName() + " " + CString(pServer->GetPort()) + "] ...");
156
 
 
157
 
        pIRCSock = new CIRCSock(pUser);
158
 
        pIRCSock->SetPass(pServer->GetPass());
159
 
 
160
 
        bool bSSL = false;
161
 
#ifdef HAVE_LIBSSL
162
 
        if (pServer->IsSSL()) {
163
 
                bSSL = true;
164
 
        }
165
 
#endif
166
 
 
167
 
        MODULECALL(OnIRCConnecting(pIRCSock), pUser, NULL,
168
 
                DEBUG("Some module aborted the connection attempt");
169
 
                pUser->PutStatus("Some module aborted the connection attempt");
170
 
                ReleaseISpoof();
171
 
                delete pIRCSock;
172
 
                return false;
173
 
        );
174
 
 
175
 
        if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, pUser->GetVHost(), pIRCSock)) {
176
 
                ReleaseISpoof();
177
 
                pUser->PutStatus("Unable to connect. (Bad host?)");
178
 
        }
179
 
 
180
 
        return true;
181
 
}
182
 
 
183
 
bool CZNC::HandleUserDeletion()
184
 
{
185
 
        map<CString, CUser*>::iterator it;
186
 
        map<CString, CUser*>::iterator end;
187
 
 
188
 
        if (m_msDelUsers.empty())
189
 
                return false;
190
 
 
191
 
        end = m_msDelUsers.end();
192
 
        for (it = m_msDelUsers.begin(); it != end; ++it) {
193
 
                CUser* pUser = it->second;
194
 
                pUser->SetBeingDeleted(true);
195
 
 
196
 
                if (GetModules().OnDeleteUser(*pUser)) {
197
 
                        pUser->SetBeingDeleted(false);
198
 
                        continue;
199
 
                }
200
 
                m_msUsers.erase(pUser->GetUserName());
201
 
 
202
 
                // Don't use pUser->GetIRCSock(), as that only returns something if the
203
 
                // CIRCSock is already connected, not when it's still connecting!
204
 
                CIRCSock* pIRCSock = (CIRCSock*) m_Manager.FindSockByName("IRC::" + pUser->GetUserName());
205
 
 
206
 
                if (pIRCSock) {
207
 
                        m_Manager.DelSockByAddr(pIRCSock);
208
 
                }
209
 
 
210
 
                pUser->DelClients();
211
 
                pUser->DelModules();
212
 
                CWebSock::FinishUserSessions(*pUser);
213
 
                AddBytesRead(pUser->BytesRead());
214
 
                AddBytesWritten(pUser->BytesWritten());
215
 
                delete pUser;
216
 
        }
217
 
 
218
 
        m_msDelUsers.clear();
219
 
 
220
 
        return true;
221
 
}
222
 
 
223
 
void CZNC::Loop() {
224
 
        while (true) {
225
 
                CString sError;
226
 
 
227
 
                switch (GetConfigState()) {
228
 
                case ECONFIG_NEED_REHASH:
229
 
                        SetConfigState(ECONFIG_NOTHING);
230
 
 
231
 
                        if (RehashConfig(sError)) {
232
 
                                Broadcast("Rehashing succeeded", true);
233
 
                        } else {
234
 
                                Broadcast("Rehashing failed: " + sError, true);
235
 
                                Broadcast("ZNC is in some possibly inconsistent state!", true);
236
 
                        }
237
 
                        break;
238
 
                case ECONFIG_NEED_WRITE:
239
 
                        SetConfigState(ECONFIG_NOTHING);
240
 
 
241
 
                        if (WriteConfig()) {
242
 
                                Broadcast("Writing the config suceeded", true);
243
 
                        } else {
244
 
                                Broadcast("Writing the config file failed", true);
245
 
                        }
246
 
                        break;
247
 
                case ECONFIG_NOTHING:
248
 
                        break;
249
 
                }
250
 
 
251
 
                // Check for users that need to be deleted
252
 
                if (HandleUserDeletion()) {
253
 
                        // Also remove those user(s) from the config file
254
 
                        WriteConfig();
255
 
                }
256
 
 
257
 
                // Csocket wants micro seconds
258
 
                // 500 msec to 600 sec
259
 
                m_Manager.DynamicSelectLoop(500 * 1000, 600 * 1000 * 1000);
260
 
        }
261
 
}
262
 
 
263
 
bool CZNC::WriteISpoof(CUser* pUser) {
264
 
        if (m_pISpoofLockFile != NULL)
265
 
                return false;
266
 
 
267
 
        if (!m_sISpoofFile.empty()) {
268
 
                m_pISpoofLockFile = new CFile;
269
 
                if (!m_pISpoofLockFile->TryExLock(m_sISpoofFile, O_RDWR | O_CREAT)) {
270
 
                        delete m_pISpoofLockFile;
271
 
                        m_pISpoofLockFile = NULL;
272
 
                        return false;
273
 
                }
274
 
 
275
 
                char buf[1024];
276
 
                memset((char*) buf, 0, 1024);
277
 
                m_pISpoofLockFile->Read(buf, 1023);
278
 
                m_sOrigISpoof = buf;
279
 
 
280
 
                if (!m_pISpoofLockFile->Seek(0) || !m_pISpoofLockFile->Truncate()) {
281
 
                        delete m_pISpoofLockFile;
282
 
                        m_pISpoofLockFile = NULL;
283
 
                        return false;
284
 
                }
285
 
 
286
 
                CString sData = pUser->ExpandString(m_sISpoofFormat);
287
 
 
288
 
                // If the format doesn't contain anything expandable, we'll
289
 
                // assume this is an "old"-style format string.
290
 
                if (sData == m_sISpoofFormat) {
291
 
                        sData.Replace("%", pUser->GetIdent());
292
 
                }
293
 
                m_pISpoofLockFile->Write(sData + "\n");
294
 
        }
295
 
        return true;
296
 
}
297
 
 
298
 
void CZNC::ReleaseISpoof() {
299
 
        if (m_pISpoofLockFile == NULL)
300
 
                return;
301
 
 
302
 
        if (!m_sISpoofFile.empty()) {
303
 
                if (m_pISpoofLockFile->Seek(0) && m_pISpoofLockFile->Truncate()) {
304
 
                        m_pISpoofLockFile->Write(m_sOrigISpoof);
305
 
                }
306
 
 
307
 
                m_sOrigISpoof = "";
308
 
        }
309
 
 
310
 
        delete m_pISpoofLockFile;
311
 
        m_pISpoofLockFile = NULL;
312
 
}
313
 
 
314
 
CFile* CZNC::InitPidFile() {
315
 
        if (!m_sPidFile.empty()) {
316
 
                CString sFile;
317
 
 
318
 
                // absolute path or relative to the data dir?
319
 
                if (m_sPidFile[0] != '/')
320
 
                        sFile = GetZNCPath() + "/" + m_sPidFile;
321
 
                else
322
 
                        sFile = m_sPidFile;
323
 
 
324
 
                return new CFile(sFile);
325
 
        }
326
 
 
327
 
        return NULL;
328
 
}
329
 
 
330
 
bool CZNC::WritePidFile(int iPid) {
331
 
        CFile* File = InitPidFile();
332
 
        if (File == NULL)
333
 
                return false;
334
 
 
335
 
        CUtils::PrintAction("Writing pid file [" + File->GetLongName() + "]");
336
 
 
337
 
        bool bRet = false;
338
 
        if (File->Open(O_WRONLY | O_TRUNC | O_CREAT)) {
339
 
                File->Write(CString(iPid) + "\n");
340
 
                File->Close();
341
 
                bRet = true;
342
 
        }
343
 
 
344
 
        delete File;
345
 
        CUtils::PrintStatus(bRet);
346
 
        return bRet;
347
 
}
348
 
 
349
 
bool CZNC::DeletePidFile() {
350
 
        CFile* File = InitPidFile();
351
 
        if (File == NULL)
352
 
                return false;
353
 
 
354
 
        CUtils::PrintAction("Deleting pid file [" + File->GetLongName() + "]");
355
 
 
356
 
        bool bRet = false;
357
 
        if (File->Delete())
358
 
                bRet = true;
359
 
 
360
 
        delete File;
361
 
        CUtils::PrintStatus(bRet);
362
 
        return bRet;
363
 
}
364
 
 
365
 
bool CZNC::WritePemFile() {
366
 
#ifndef HAVE_LIBSSL
367
 
        CUtils::PrintError("ZNC was not compiled with ssl support.");
368
 
        return false;
369
 
#else
370
 
        CString sPemFile = GetPemLocation();
371
 
        const char* pHostName = getenv("HOSTNAME");
372
 
        CString sHost;
373
 
 
374
 
        if (pHostName) {
375
 
                sHost = pHostName;
376
 
        }
377
 
 
378
 
        if (CFile::Exists(sPemFile)) {
379
 
                CUtils::PrintError("Pem file [" + sPemFile + "] already exists");
380
 
                return false;
381
 
        }
382
 
 
383
 
        while (!CUtils::GetInput("hostname of your shell", sHost, sHost, "including the '.com' portion")) ;
384
 
 
385
 
        CUtils::PrintAction("Writing Pem file [" + sPemFile + "]");
386
 
        FILE *f = fopen(sPemFile.c_str(), "w");
387
 
 
388
 
        if (!f) {
389
 
                CUtils::PrintStatus(false, "Unable to open");
390
 
                return false;
391
 
        }
392
 
 
393
 
        CUtils::GenerateCert(f, sHost);
394
 
        fclose(f);
395
 
 
396
 
        CUtils::PrintStatus(true);
397
 
        return true;
398
 
#endif
399
 
}
400
 
 
401
 
void CZNC::DeleteUsers() {
402
 
        for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
403
 
                a->second->SetBeingDeleted(true);
404
 
                delete a->second;
405
 
        }
406
 
 
407
 
        m_msUsers.clear();
408
 
        DisableConnectUser();
409
 
}
410
 
 
411
 
CUser* CZNC::GetUser(const CString& sUser) {
412
 
        // Todo: make this case insensitive
413
 
        map<CString,CUser*>::iterator it = m_msUsers.find(sUser);
414
 
        return (it == m_msUsers.end()) ? NULL : it->second;
415
 
}
416
 
 
417
 
Csock* CZNC::FindSockByName(const CString& sSockName) {
418
 
        return m_Manager.FindSockByName(sSockName);
419
 
}
420
 
 
421
 
bool CZNC::IsHostAllowed(const CString& sHostMask) const {
422
 
        for (map<CString,CUser*>::const_iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
423
 
                if (a->second->IsHostAllowed(sHostMask)) {
424
 
                        return true;
425
 
                }
426
 
        }
427
 
 
428
 
        return false;
429
 
}
430
 
 
431
 
bool CZNC::AllowConnectionFrom(const CString& sIP) const {
432
 
        if (m_uiAnonIPLimit == 0)
433
 
                return true;
434
 
        if (GetManager().GetAnonConnectionCount(sIP) >= m_uiAnonIPLimit)
435
 
                return false;
436
 
        return true;
437
 
}
438
 
 
439
 
void CZNC::InitDirs(const CString& sArgvPath, const CString& sDataDir) {
440
 
        char *home;
441
 
 
442
 
        // If the bin was not ran from the current directory, we need to add that dir onto our cwd
443
 
        CString::size_type uPos = sArgvPath.rfind('/');
444
 
        if (uPos == CString::npos)
445
 
                m_sCurPath = "./";
446
 
        else
447
 
                m_sCurPath = CDir::ChangeDir("./", sArgvPath.Left(uPos), "");
448
 
 
449
 
        // Try to set the user's home dir, default to binpath on failure
450
 
        home = getenv("HOME");
451
 
 
452
 
        m_sHomePath.clear();
453
 
        if (home) {
454
 
                m_sHomePath = home;
455
 
        }
456
 
 
457
 
        if (m_sHomePath.empty()) {
458
 
                struct passwd* pUserInfo = getpwuid(getuid());
459
 
 
460
 
                if (pUserInfo) {
461
 
                        m_sHomePath = pUserInfo->pw_dir;
462
 
                }
463
 
        }
464
 
 
465
 
        if (m_sHomePath.empty()) {
466
 
                m_sHomePath = m_sCurPath;
467
 
        }
468
 
 
469
 
        if (sDataDir.empty()) {
470
 
                m_sZNCPath = m_sHomePath + "/.znc";
471
 
        } else {
472
 
                m_sZNCPath = sDataDir;
473
 
        }
474
 
}
475
 
 
476
 
CString CZNC::GetConfPath(bool bAllowMkDir) const {
477
 
        CString sConfPath = m_sZNCPath + "/configs";
478
 
        if (bAllowMkDir && !CFile::Exists(sConfPath)) {
479
 
                CDir::MakeDir(sConfPath);
480
 
        }
481
 
 
482
 
        return sConfPath;
483
 
}
484
 
 
485
 
CString CZNC::GetUserPath() const {
486
 
        CString sUserPath = m_sZNCPath + "/users";
487
 
        if (!CFile::Exists(sUserPath)) {
488
 
                CDir::MakeDir(sUserPath);
489
 
        }
490
 
 
491
 
        return sUserPath;
492
 
}
493
 
 
494
 
CString CZNC::GetModPath() const {
495
 
        CString sModPath = m_sZNCPath + "/modules";
496
 
 
497
 
        if (!CFile::Exists(sModPath)) {
498
 
                CDir::MakeDir(sModPath);
499
 
        }
500
 
 
501
 
        return sModPath;
502
 
}
503
 
 
504
 
 
505
 
CString CZNC::ExpandConfigPath(const CString& sConfigFile, bool bAllowMkDir) {
506
 
        CString sRetPath;
507
 
 
508
 
        if (sConfigFile.empty()) {
509
 
                sRetPath = GetConfPath(bAllowMkDir) + "/znc.conf";
510
 
        } else {
511
 
                if (sConfigFile.Left(2) == "./" || sConfigFile.Left(3) == "../") {
512
 
                        sRetPath = GetCurPath() + "/" + sConfigFile;
513
 
                } else if (sConfigFile.Left(1) != "/") {
514
 
                        sRetPath = GetConfPath(bAllowMkDir) + "/" + sConfigFile;
515
 
                } else {
516
 
                        sRetPath = sConfigFile;
517
 
                }
518
 
        }
519
 
 
520
 
        return sRetPath;
521
 
}
522
 
 
523
 
bool CZNC::WriteConfig() {
524
 
        if (GetConfigFile().empty()) {
525
 
                return false;
526
 
        }
527
 
 
528
 
        // Close the old handle to the config file, we are replacing that file.
529
 
        m_LockFile.Close();
530
 
 
531
 
        // We first write to a temporary file and then move it to the right place
532
 
        m_LockFile.SetFileName(GetConfigFile() + "~");
533
 
 
534
 
        if (!m_LockFile.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
535
 
                return false;
536
 
        }
537
 
 
538
 
        // We have to "transfer" our lock on the config to the new file.
539
 
        // The old file (= inode) is going away and thus a lock on it would be
540
 
        // useless. These lock should always succeed (races, anyone?).
541
 
        if (!m_LockFile.TryExLock()) {
542
 
                return false;
543
 
        }
544
 
 
545
 
        if (GetModules().OnWriteConfig(m_LockFile)) {
546
 
                return false;
547
 
        }
548
 
 
549
 
        m_LockFile.Write("AnonIPLimit  = " + CString(m_uiAnonIPLimit) + "\n");
550
 
 
551
 
        for (size_t l = 0; l < m_vpListeners.size(); l++) {
552
 
                CListener* pListener = m_vpListeners[l];
553
 
                CString sHostPortion = pListener->GetBindHost();
554
 
 
555
 
                if (!sHostPortion.empty()) {
556
 
                        sHostPortion = sHostPortion.FirstLine() + " ";
557
 
                }
558
 
 
559
 
                CString sAcceptProtocol;
560
 
                if(pListener->GetAcceptType() == CListener::ACCEPT_IRC)
561
 
                        sAcceptProtocol = "irc_only ";
562
 
                else if(pListener->GetAcceptType() == CListener::ACCEPT_HTTP)
563
 
                        sAcceptProtocol = "web_only ";
564
 
 
565
 
                CString s6;
566
 
                switch (pListener->GetAddrType()) {
567
 
                        case ADDR_IPV4ONLY:
568
 
                                s6 = "4";
569
 
                                break;
570
 
                        case ADDR_IPV6ONLY:
571
 
                                s6 = "6";
572
 
                                break;
573
 
                        case ADDR_ALL:
574
 
                                s6 = " ";
575
 
                                break;
576
 
                }
577
 
 
578
 
                m_LockFile.Write("Listener" + s6 + "    = " + sAcceptProtocol + sHostPortion +
579
 
                        CString((pListener->IsSSL()) ? "+" : "") + CString(pListener->GetPort()) + "\n");
580
 
        }
581
 
 
582
 
        m_LockFile.Write("ConnectDelay = " + CString(m_uiConnectDelay) + "\n");
583
 
        m_LockFile.Write("ServerThrottle = " + CString(m_sConnectThrottle.GetTTL()/1000) + "\n");
584
 
 
585
 
        if (!m_sISpoofFile.empty()) {
586
 
                m_LockFile.Write("ISpoofFile   = " + m_sISpoofFile.FirstLine() + "\n");
587
 
                if (!m_sISpoofFormat.empty()) {
588
 
                        m_LockFile.Write("ISpoofFormat = " + m_sISpoofFormat.FirstLine() + "\n");
589
 
                }
590
 
        }
591
 
 
592
 
        if (!m_sPidFile.empty()) {
593
 
                m_LockFile.Write("PidFile      = " + m_sPidFile.FirstLine() + "\n");
594
 
        }
595
 
 
596
 
        if (!m_sSkinName.empty()) {
597
 
                m_LockFile.Write("Skin         = " + m_sSkinName.FirstLine() + "\n");
598
 
        }
599
 
 
600
 
        if (!m_sStatusPrefix.empty()) {
601
 
                m_LockFile.Write("StatusPrefix = " + m_sStatusPrefix.FirstLine() + "\n");
602
 
        }
603
 
 
604
 
        for (unsigned int m = 0; m < m_vsMotd.size(); m++) {
605
 
                m_LockFile.Write("Motd         = " + m_vsMotd[m].FirstLine() + "\n");
606
 
        }
607
 
 
608
 
        for (unsigned int v = 0; v < m_vsVHosts.size(); v++) {
609
 
                m_LockFile.Write("VHost        = " + m_vsVHosts[v].FirstLine() + "\n");
610
 
        }
611
 
 
612
 
        CGlobalModules& Mods = GetModules();
613
 
 
614
 
        for (unsigned int a = 0; a < Mods.size(); a++) {
615
 
                CString sName = Mods[a]->GetModName();
616
 
                CString sArgs = Mods[a]->GetArgs();
617
 
 
618
 
                if (!sArgs.empty()) {
619
 
                        sArgs = " " + sArgs.FirstLine();
620
 
                }
621
 
 
622
 
                m_LockFile.Write("LoadModule   = " + sName.FirstLine() + sArgs + "\n");
623
 
        }
624
 
 
625
 
        for (map<CString,CUser*>::iterator it = m_msUsers.begin(); it != m_msUsers.end(); ++it) {
626
 
                CString sErr;
627
 
 
628
 
                if (!it->second->IsValid(sErr)) {
629
 
                        DEBUG("** Error writing config for user [" << it->first << "] [" << sErr << "]");
630
 
                        continue;
631
 
                }
632
 
 
633
 
                m_LockFile.Write("\n");
634
 
 
635
 
                if (!it->second->WriteConfig(m_LockFile)) {
636
 
                        DEBUG("** Error writing config for user [" << it->first << "]");
637
 
                }
638
 
        }
639
 
 
640
 
        // If Sync() fails... well, let's hope nothing important breaks..
641
 
        m_LockFile.Sync();
642
 
 
643
 
        // We wrote to a temporary name, move it to the right place
644
 
        if (!m_LockFile.Move(GetConfigFile(), true))
645
 
                return false;
646
 
 
647
 
        // Everything went fine, just need to update the saved path.
648
 
        m_LockFile.SetFileName(GetConfigFile());
649
 
 
650
 
        return true;
651
 
}
652
 
 
653
 
bool CZNC::WriteNewConfig(const CString& sConfigFile) {
654
 
        CString sAnswer, sUser;
655
 
        VCString vsLines;
656
 
 
657
 
        m_sConfigFile = ExpandConfigPath(sConfigFile);
658
 
        CUtils::PrintMessage("Building new config");
659
 
 
660
 
        CUtils::PrintMessage("");
661
 
        CUtils::PrintMessage("First lets start with some global settings...");
662
 
        CUtils::PrintMessage("");
663
 
 
664
 
        // Listen
665
 
        unsigned int uListenPort = 0;
666
 
        while (!CUtils::GetNumInput("What port would you like ZNC to listen on?", uListenPort, 1, 65535)) ;
667
 
 
668
 
        CString sSSL;
669
 
#ifdef HAVE_LIBSSL
670
 
        if (CUtils::GetBoolInput("Would you like ZNC to listen using SSL?", false)) {
671
 
                sSSL = "+";
672
 
 
673
 
                CString sPemFile = GetPemLocation();
674
 
                if (!CFile::Exists(sPemFile)) {
675
 
                        CUtils::PrintError("Unable to locate pem file: [" + sPemFile + "]");
676
 
                        if (CUtils::GetBoolInput("Would you like to create a new pem file now?",
677
 
                                                true)) {
678
 
                                WritePemFile();
679
 
                        }
680
 
                }
681
 
        }
682
 
#endif
683
 
 
684
 
        CString s6 = "4";
685
 
#ifdef HAVE_IPV6
686
 
        if (CUtils::GetBoolInput("Would you like ZNC to listen using ipv6?", false)) {
687
 
                s6 = " ";
688
 
        }
689
 
#endif
690
 
 
691
 
        CString sListenHost;
692
 
        CUtils::GetInput("Listen Host", sListenHost, "", "Blank for all ips");
693
 
 
694
 
        if (!sListenHost.empty()) {
695
 
                sListenHost += " ";
696
 
        }
697
 
 
698
 
        vsLines.push_back("Listener" + s6 + "  = " + sListenHost + sSSL + CString(uListenPort));
699
 
        // !Listen
700
 
 
701
 
        set<CModInfo> ssGlobalMods;
702
 
        GetModules().GetAvailableMods(ssGlobalMods, true);
703
 
        size_t uNrOtherGlobalMods = FilterUncommonModules(ssGlobalMods);
704
 
 
705
 
        if (!ssGlobalMods.empty()) {
706
 
                CUtils::PrintMessage("");
707
 
                CUtils::PrintMessage("-- Global Modules --");
708
 
                CUtils::PrintMessage("");
709
 
 
710
 
                if (CUtils::GetBoolInput("Do you want to load any global modules?")) {
711
 
                        CTable Table;
712
 
                        Table.AddColumn("Name");
713
 
                        Table.AddColumn("Description");
714
 
                        set<CModInfo>::iterator it;
715
 
 
716
 
                        for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) {
717
 
                                const CModInfo& Info = *it;
718
 
                                Table.AddRow();
719
 
                                Table.SetCell("Name", Info.GetName());
720
 
                                Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
721
 
                        }
722
 
 
723
 
                        unsigned int uTableIdx = 0; CString sLine;
724
 
                        while (Table.GetLine(uTableIdx++, sLine)) {
725
 
                                CUtils::PrintMessage(sLine);
726
 
                        }
727
 
 
728
 
                        if (uNrOtherGlobalMods > 0) {
729
 
                                CUtils::PrintMessage("And " + CString(uNrOtherGlobalMods) + " other (uncommon) modules. You can enable those later.");
730
 
                        }
731
 
 
732
 
                        CUtils::PrintMessage("");
733
 
 
734
 
                        for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) {
735
 
                                const CModInfo& Info = *it;
736
 
                                CString sName = Info.GetName();
737
 
 
738
 
                                if (CUtils::StdoutIsTTY()) {
739
 
                                        if (CUtils::GetBoolInput("Load global module <\033[1m" + sName + "\033[22m>?", false))
740
 
                                                vsLines.push_back("LoadModule = " + sName);
741
 
                                } else {
742
 
                                        if (CUtils::GetBoolInput("Load global module <" + sName + ">?", false))
743
 
                                                vsLines.push_back("LoadModule = " + sName);
744
 
                                }
745
 
                        }
746
 
                }
747
 
        }
748
 
 
749
 
        // User
750
 
        CUtils::PrintMessage("");
751
 
        CUtils::PrintMessage("Now we need to setup a user...");
752
 
        CUtils::PrintMessage("");
753
 
 
754
 
        bool bFirstUser = true;
755
 
 
756
 
        do {
757
 
                vsLines.push_back("");
758
 
                CString sNick;
759
 
                do {
760
 
                        CUtils::GetInput("Username", sUser, "", "AlphaNumeric");
761
 
                } while (!CUser::IsValidUserName(sUser));
762
 
 
763
 
                vsLines.push_back("<User " + sUser + ">");
764
 
                CString sSalt;
765
 
                sAnswer = CUtils::GetSaltedHashPass(sSalt);
766
 
                vsLines.push_back("\tPass       = " + CUtils::sDefaultHash + "#" + sAnswer + "#" + sSalt + "#");
767
 
 
768
 
                if (CUtils::GetBoolInput("Would you like this user to be an admin?", bFirstUser)) {
769
 
                        vsLines.push_back("\tAdmin      = true");
770
 
                } else {
771
 
                        vsLines.push_back("\tAdmin      = false");
772
 
                }
773
 
 
774
 
                CUtils::GetInput("Nick", sNick, CUser::MakeCleanUserName(sUser));
775
 
                vsLines.push_back("\tNick       = " + sNick);
776
 
                CUtils::GetInput("Alt Nick", sAnswer, sNick + "_");
777
 
                if (!sAnswer.empty()) {
778
 
                        vsLines.push_back("\tAltNick    = " + sAnswer);
779
 
                }
780
 
                CUtils::GetInput("Ident", sAnswer, sNick);
781
 
                vsLines.push_back("\tIdent      = " + sAnswer);
782
 
                CUtils::GetInput("Real Name", sAnswer, "Got ZNC?");
783
 
                vsLines.push_back("\tRealName   = " + sAnswer);
784
 
                CUtils::GetInput("VHost", sAnswer, "", "optional");
785
 
                if (!sAnswer.empty()) {
786
 
                        vsLines.push_back("\tVHost      = " + sAnswer);
787
 
                }
788
 
                // todo: Possibly add motd
789
 
 
790
 
                unsigned int uBufferCount = 0;
791
 
 
792
 
                CUtils::GetNumInput("Number of lines to buffer per channel", uBufferCount, 0, ~0, 50);
793
 
                if (uBufferCount) {
794
 
                        vsLines.push_back("\tBuffer     = " + CString(uBufferCount));
795
 
                }
796
 
                if (CUtils::GetBoolInput("Would you like to keep buffers after replay?", false)) {
797
 
                        vsLines.push_back("\tKeepBuffer = true");
798
 
                } else {
799
 
                        vsLines.push_back("\tKeepBuffer = false");
800
 
                }
801
 
 
802
 
                CUtils::GetInput("Default channel modes", sAnswer, "+stn");
803
 
                if (!sAnswer.empty()) {
804
 
                        vsLines.push_back("\tChanModes  = " + sAnswer);
805
 
                }
806
 
 
807
 
                set<CModInfo> ssUserMods;
808
 
                GetModules().GetAvailableMods(ssUserMods);
809
 
                size_t uNrOtherUserMods = FilterUncommonModules(ssUserMods);
810
 
 
811
 
                if (ssUserMods.size()) {
812
 
                        vsLines.push_back("");
813
 
                        CUtils::PrintMessage("");
814
 
                        CUtils::PrintMessage("-- User Modules --");
815
 
                        CUtils::PrintMessage("");
816
 
 
817
 
                        if (CUtils::GetBoolInput("Do you want to automatically load any user modules for this user?")) {
818
 
                                CTable Table;
819
 
                                Table.AddColumn("Name");
820
 
                                Table.AddColumn("Description");
821
 
                                set<CModInfo>::iterator it;
822
 
 
823
 
                                for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
824
 
                                        const CModInfo& Info = *it;
825
 
                                        Table.AddRow();
826
 
                                        Table.SetCell("Name", Info.GetName());
827
 
                                        Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
828
 
                                }
829
 
 
830
 
                                unsigned int uTableIdx = 0; CString sLine;
831
 
                                while (Table.GetLine(uTableIdx++, sLine)) {
832
 
                                        CUtils::PrintMessage(sLine);
833
 
                                }
834
 
 
835
 
                                if (uNrOtherUserMods > 0) {
836
 
                                        CUtils::PrintMessage("And " + CString(uNrOtherUserMods) + " other (uncommon) modules. You can enable those later.");
837
 
                                }
838
 
 
839
 
                                CUtils::PrintMessage("");
840
 
 
841
 
                                for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
842
 
                                        const CModInfo& Info = *it;
843
 
                                        CString sName = Info.GetName();
844
 
 
845
 
                                        if (CUtils::StdoutIsTTY()) {
846
 
                                                if (CUtils::GetBoolInput("Load module <\033[1m" + sName + "\033[22m>?", false))
847
 
                                                        vsLines.push_back("\tLoadModule = " + sName);
848
 
                                        } else {
849
 
                                                if (CUtils::GetBoolInput("Load module <" + sName + ">?", false))
850
 
                                                        vsLines.push_back("\tLoadModule = " + sName);
851
 
                                        }
852
 
                                }
853
 
                        }
854
 
                }
855
 
 
856
 
                vsLines.push_back("");
857
 
                CUtils::PrintMessage("");
858
 
                CUtils::PrintMessage("-- IRC Servers --");
859
 
                CUtils::PrintMessage("");
860
 
 
861
 
                do {
862
 
                        CString sHost, sPass;
863
 
                        bool bSSL = false;
864
 
                        unsigned int uServerPort = 0;
865
 
 
866
 
                        while (!CUtils::GetInput("IRC server", sHost, "", "host only") || !CServer::IsValidHostName(sHost)) ;
867
 
                        while (!CUtils::GetNumInput("[" + sHost + "] Port", uServerPort, 1, 65535, 6667)) ;
868
 
                        CUtils::GetInput("[" + sHost + "] Password (probably empty)", sPass);
869
 
 
870
 
#ifdef HAVE_LIBSSL
871
 
                        bSSL = CUtils::GetBoolInput("Does this server use SSL? (probably no)", false);
872
 
#endif
873
 
 
874
 
                        vsLines.push_back("\tServer     = " + sHost + ((bSSL) ? " +" : " ") + CString(uServerPort) + " " + sPass);
875
 
 
876
 
                        CUtils::PrintMessage("");
877
 
                } while (CUtils::GetBoolInput("Would you like to add another server?", false));
878
 
 
879
 
                vsLines.push_back("");
880
 
                CUtils::PrintMessage("");
881
 
                CUtils::PrintMessage("-- Channels --");
882
 
                CUtils::PrintMessage("");
883
 
 
884
 
                CString sArg = "a";
885
 
                CString sPost = " for ZNC to automatically join?";
886
 
                bool bDefault = true;
887
 
 
888
 
                while (CUtils::GetBoolInput("Would you like to add " + sArg + " channel" + sPost, bDefault)) {
889
 
                        while (!CUtils::GetInput("Channel name", sAnswer)) ;
890
 
                        vsLines.push_back("\t<Chan " + sAnswer + ">");
891
 
                        vsLines.push_back("\t</Chan>");
892
 
                        sArg = "another";
893
 
                        sPost = "?";
894
 
                        bDefault = false;
895
 
                }
896
 
 
897
 
                vsLines.push_back("</User>");
898
 
 
899
 
                CUtils::PrintMessage("");
900
 
                bFirstUser = false;
901
 
        } while (CUtils::GetBoolInput("Would you like to setup another user?", false));
902
 
        // !User
903
 
 
904
 
        CFile File;
905
 
        bool bFileOK, bFileOpen = false;
906
 
        do {
907
 
                CUtils::PrintAction("Writing config [" + m_sConfigFile + "]");
908
 
 
909
 
                bFileOK = true;
910
 
                if (CFile::Exists(m_sConfigFile)) {
911
 
                        if (!m_LockFile.TryExLock(m_sConfigFile)) {
912
 
                                CUtils::PrintStatus(false, "ZNC is currently running on this config.");
913
 
                                bFileOK = false;
914
 
                        } else {
915
 
                                m_LockFile.Close();
916
 
                                CUtils::PrintStatus(false, "This config already exists.");
917
 
                                if (CUtils::GetBoolInput("Would you like to overwrite it?", false))
918
 
                                        CUtils::PrintAction("Overwriting config [" + m_sConfigFile + "]");
919
 
                                else
920
 
                                        bFileOK = false;
921
 
                        }
922
 
                }
923
 
 
924
 
                if (bFileOK) {
925
 
                        File.SetFileName(m_sConfigFile);
926
 
                        if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
927
 
                                bFileOpen = true;
928
 
                        } else {
929
 
                                CUtils::PrintStatus(false, "Unable to open file");
930
 
                                bFileOK = false;
931
 
                        }
932
 
                }
933
 
                if (!bFileOK) {
934
 
                        CUtils::GetInput("Please specify an alternate location (or \"stdout\" for displaying the config)", m_sConfigFile, m_sConfigFile);
935
 
                        if (m_sConfigFile.Equals("stdout"))
936
 
                                bFileOK = true;
937
 
                        else
938
 
                                m_sConfigFile = ExpandConfigPath(m_sConfigFile);
939
 
                }
940
 
        } while (!bFileOK);
941
 
 
942
 
        if (!bFileOpen) {
943
 
                CUtils::PrintMessage("");
944
 
                CUtils::PrintMessage("Printing the new config to stdout:");
945
 
                CUtils::PrintMessage("");
946
 
                cout << endl << "----------------------------------------------------------------------------" << endl << endl;
947
 
        }
948
 
 
949
 
        for (unsigned int a = 0; a < vsLines.size(); a++) {
950
 
                if (bFileOpen) {
951
 
                        File.Write(vsLines[a] + "\n");
952
 
                } else {
953
 
                        cout << vsLines[a] << endl;
954
 
                }
955
 
        }
956
 
 
957
 
        if (bFileOpen) {
958
 
                File.Close();
959
 
                CUtils::PrintStatus(true);
960
 
        } else {
961
 
                cout << endl << "----------------------------------------------------------------------------" << endl << endl;
962
 
        }
963
 
 
964
 
        CUtils::PrintMessage("");
965
 
        CUtils::PrintMessage("To connect to this znc you need to connect to it as your irc server", true);
966
 
        CUtils::PrintMessage("using the port that you supplied.  You have to supply your login info", true);
967
 
        CUtils::PrintMessage("as the irc server password like so... user:pass.", true);
968
 
        CUtils::PrintMessage("");
969
 
        CUtils::PrintMessage("Try something like this in your IRC client...", true);
970
 
        CUtils::PrintMessage("/server <znc_server_ip> " + CString(uListenPort) + " " + sUser + ":<pass>", true);
971
 
        CUtils::PrintMessage("");
972
 
 
973
 
        m_LockFile.UnLock();
974
 
        return bFileOpen && CUtils::GetBoolInput("Launch znc now?", true);
975
 
}
976
 
 
977
 
size_t CZNC::FilterUncommonModules(set<CModInfo>& ssModules) {
978
 
        const char* ns[] = { "webadmin", "admin",
979
 
                "chansaver", "keepnick", "simple_away", "partyline",
980
 
                "kickrejoin", "nickserv", "perform" };
981
 
        const set<CString> ssNames(ns, ns + sizeof(ns) / sizeof(ns[0]));
982
 
 
983
 
        size_t uNrRemoved = 0;
984
 
        for(set<CModInfo>::iterator it = ssModules.begin(); it != ssModules.end(); ) {
985
 
                if(ssNames.count(it->GetName()) > 0) {
986
 
                        it++;
987
 
                } else {
988
 
                        set<CModInfo>::iterator it2 = it++;
989
 
                        ssModules.erase(it2);
990
 
                        uNrRemoved++;
991
 
                }
992
 
        }
993
 
 
994
 
        return uNrRemoved;
995
 
}
996
 
 
997
 
bool CZNC::ParseConfig(const CString& sConfig)
998
 
{
999
 
        CString s;
1000
 
 
1001
 
        m_sConfigFile = ExpandConfigPath(sConfig, false);
1002
 
 
1003
 
        return DoRehash(s);
1004
 
}
1005
 
 
1006
 
bool CZNC::RehashConfig(CString& sError)
1007
 
{
1008
 
        GetModules().OnPreRehash();
1009
 
        for (map<CString, CUser*>::iterator itb = m_msUsers.begin();
1010
 
                        itb != m_msUsers.end(); ++itb) {
1011
 
                itb->second->GetModules().OnPreRehash();
1012
 
        }
1013
 
 
1014
 
        // This clears m_msDelUsers
1015
 
        HandleUserDeletion();
1016
 
 
1017
 
        // Mark all users as going-to-be deleted
1018
 
        m_msDelUsers = m_msUsers;
1019
 
        m_msUsers.clear();
1020
 
 
1021
 
        if (DoRehash(sError)) {
1022
 
                GetModules().OnPostRehash();
1023
 
                for (map<CString, CUser*>::iterator it = m_msUsers.begin();
1024
 
                                it != m_msUsers.end(); ++it) {
1025
 
                        it->second->GetModules().OnPostRehash();
1026
 
                }
1027
 
 
1028
 
                return true;
1029
 
        }
1030
 
 
1031
 
        // Rehashing failed, try to recover
1032
 
        CString s;
1033
 
        while (!m_msDelUsers.empty()) {
1034
 
                AddUser(m_msDelUsers.begin()->second, s);
1035
 
                m_msDelUsers.erase(m_msDelUsers.begin());
1036
 
        }
1037
 
 
1038
 
        return false;
1039
 
}
1040
 
 
1041
 
bool CZNC::DoRehash(CString& sError)
1042
 
{
1043
 
        sError.clear();
1044
 
 
1045
 
        CUtils::PrintAction("Opening Config [" + m_sConfigFile + "]");
1046
 
 
1047
 
        if (!CFile::Exists(m_sConfigFile)) {
1048
 
                sError = "No such file";
1049
 
                CUtils::PrintStatus(false, sError);
1050
 
                CUtils::PrintMessage("Restart znc with the --makeconf option if you wish to create this config.");
1051
 
                return false;
1052
 
        }
1053
 
 
1054
 
        if (!CFile::IsReg(m_sConfigFile)) {
1055
 
                sError = "Not a file";
1056
 
                CUtils::PrintStatus(false, sError);
1057
 
                return false;
1058
 
        }
1059
 
 
1060
 
        // (re)open the config file
1061
 
        if (m_LockFile.IsOpen())
1062
 
                m_LockFile.Close();
1063
 
 
1064
 
        if (!m_LockFile.Open(m_sConfigFile)) {
1065
 
                sError = "Can not open config file";
1066
 
                CUtils::PrintStatus(false, sError);
1067
 
                return false;
1068
 
        }
1069
 
 
1070
 
        if (!m_LockFile.TryExLock()) {
1071
 
                sError = "ZNC is already running on this config.";
1072
 
                CUtils::PrintStatus(false, sError);
1073
 
                return false;
1074
 
        }
1075
 
 
1076
 
        CFile &File = m_LockFile;
1077
 
 
1078
 
        // This fd is re-used for rehashing, so we must seek back to the beginning!
1079
 
        if (!File.Seek(0)) {
1080
 
                sError = "Could not seek to the beginning of the config.";
1081
 
                CUtils::PrintStatus(false, sError);
1082
 
                return false;
1083
 
        }
1084
 
 
1085
 
        CUtils::PrintStatus(true);
1086
 
 
1087
 
        m_vsVHosts.clear();
1088
 
        m_vsMotd.clear();
1089
 
 
1090
 
        // Delete all listeners
1091
 
        while (!m_vpListeners.empty()) {
1092
 
                delete m_vpListeners[0];
1093
 
                m_vpListeners.erase(m_vpListeners.begin());
1094
 
        }
1095
 
 
1096
 
        CString sLine;
1097
 
        bool bCommented = false;     // support for /**/ style comments
1098
 
        CUser* pUser = NULL;         // Used to keep track of which user block we are in
1099
 
        CUser* pRealUser = NULL;     // If we rehash a user, this is the real one
1100
 
        CChan* pChan = NULL;         // Used to keep track of which chan block we are in
1101
 
        unsigned int uLineNum = 0;
1102
 
        MCString msModules;          // Modules are queued for later loading
1103
 
 
1104
 
        std::list<CGlobalModuleConfigLine> lGlobalModuleConfigLine;
1105
 
 
1106
 
        while (File.ReadLine(sLine)) {
1107
 
                uLineNum++;
1108
 
 
1109
 
                // Remove all leading / trailing spaces and line endings
1110
 
                sLine.Trim();
1111
 
 
1112
 
                if ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) {
1113
 
                        continue;
1114
 
                }
1115
 
 
1116
 
                if (sLine.Left(2) == "/*") {
1117
 
                        if (sLine.Right(2) != "*/") {
1118
 
                                bCommented = true;
1119
 
                        }
1120
 
 
1121
 
                        continue;
1122
 
                }
1123
 
 
1124
 
                if (bCommented) {
1125
 
                        if (sLine.Right(2) == "*/") {
1126
 
                                bCommented = false;
1127
 
                        }
1128
 
 
1129
 
                        continue;
1130
 
                }
1131
 
 
1132
 
                if ((sLine.Left(1) == "<") && (sLine.Right(1) == ">")) {
1133
 
                        sLine.LeftChomp();
1134
 
                        sLine.RightChomp();
1135
 
                        sLine.Trim();
1136
 
 
1137
 
                        CString sTag = sLine.substr(0, sLine.find_first_of(" \t\r\n"));
1138
 
                        CString sValue = (sTag.size() < sLine.size()) ? sLine.substr(sTag.size() +1) : "";
1139
 
 
1140
 
                        sTag.Trim();
1141
 
                        sValue.Trim();
1142
 
 
1143
 
                        if (sLine.Left(1) == "/") {
1144
 
                                sTag = sTag.substr(1);
1145
 
 
1146
 
                                if (pUser) {
1147
 
                                        if (pChan) {
1148
 
                                                if (sTag.Equals("Chan")) {
1149
 
                                                        // Save the channel name, because AddChan
1150
 
                                                        // deletes the CChannel*, if adding fails
1151
 
                                                        sError = pChan->GetName();
1152
 
                                                        if (!pUser->AddChan(pChan)) {
1153
 
                                                                sError = "Channel [" + sError + "] defined more than once";
1154
 
                                                                CUtils::PrintError(sError);
1155
 
                                                                return false;
1156
 
                                                        }
1157
 
                                                        sError.clear();
1158
 
                                                        pChan = NULL;
1159
 
                                                        continue;
1160
 
                                                }
1161
 
                                        } else if (sTag.Equals("User")) {
1162
 
                                                CString sErr;
1163
 
 
1164
 
                                                if (pRealUser) {
1165
 
                                                        if (!pRealUser->Clone(*pUser, sErr)
1166
 
                                                                        || !AddUser(pRealUser, sErr)) {
1167
 
                                                                sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
1168
 
                                                                DEBUG("CUser::Clone() failed in rehash");
1169
 
                                                        }
1170
 
                                                        pUser->SetBeingDeleted(true);
1171
 
                                                        delete pUser;
1172
 
                                                        pUser = NULL;
1173
 
                                                } else if (!AddUser(pUser, sErr)) {
1174
 
                                                        sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
1175
 
                                                }
1176
 
 
1177
 
                                                if (!sError.empty()) {
1178
 
                                                        CUtils::PrintError(sError);
1179
 
                                                        if (pUser) {
1180
 
                                                                pUser->SetBeingDeleted(true);
1181
 
                                                                delete pUser;
1182
 
                                                                pUser = NULL;
1183
 
                                                        }
1184
 
                                                        return false;
1185
 
                                                }
1186
 
 
1187
 
                                                pUser = NULL;
1188
 
                                                pRealUser = NULL;
1189
 
                                                continue;
1190
 
                                        }
1191
 
                                }
1192
 
                        } else if (sTag.Equals("User")) {
1193
 
                                if (pUser) {
1194
 
                                        sError = "You may not nest <User> tags inside of other <User> tags.";
1195
 
                                        CUtils::PrintError(sError);
1196
 
                                        return false;
1197
 
                                }
1198
 
 
1199
 
                                if (sValue.empty()) {
1200
 
                                        sError = "You must supply a username in the <User> tag.";
1201
 
                                        CUtils::PrintError(sError);
1202
 
                                        return false;
1203
 
                                }
1204
 
 
1205
 
                                if (m_msUsers.find(sValue) != m_msUsers.end()) {
1206
 
                                        sError = "User [" + sValue + "] defined more than once.";
1207
 
                                        CUtils::PrintError(sError);
1208
 
                                        return false;
1209
 
                                }
1210
 
 
1211
 
                                CUtils::PrintMessage("Loading user [" + sValue + "]");
1212
 
 
1213
 
                                // Either create a CUser* or use an existing one
1214
 
                                map<CString, CUser*>::iterator it = m_msDelUsers.find(sValue);
1215
 
 
1216
 
                                if (it != m_msDelUsers.end()) {
1217
 
                                        pRealUser = it->second;
1218
 
                                        m_msDelUsers.erase(it);
1219
 
                                } else
1220
 
                                        pRealUser = NULL;
1221
 
 
1222
 
                                pUser = new CUser(sValue);
1223
 
 
1224
 
                                if (!m_sStatusPrefix.empty()) {
1225
 
                                        if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
1226
 
                                                sError = "Invalid StatusPrefix [" + m_sStatusPrefix + "] Must be 1-5 chars, no spaces.";
1227
 
                                                CUtils::PrintError(sError);
1228
 
                                                return false;
1229
 
                                        }
1230
 
                                }
1231
 
 
1232
 
                                continue;
1233
 
                        } else if (sTag.Equals("Chan")) {
1234
 
                                if (!pUser) {
1235
 
                                        sError = "<Chan> tags must be nested inside of a <User> tag.";
1236
 
                                        CUtils::PrintError(sError);
1237
 
                                        return false;
1238
 
                                }
1239
 
 
1240
 
                                if (pChan) {
1241
 
                                        sError = "You may not nest <Chan> tags inside of other <Chan> tags.";
1242
 
                                        CUtils::PrintError(sError);
1243
 
                                        return false;
1244
 
                                }
1245
 
 
1246
 
                                pChan = new CChan(sValue, pUser, true);
1247
 
                                continue;
1248
 
                        }
1249
 
                }
1250
 
 
1251
 
                // If we have a regular line, figure out where it goes
1252
 
                CString sName = sLine.Token(0, false, "=");
1253
 
                CString sValue = sLine.Token(1, true, "=");
1254
 
                sName.Trim();
1255
 
                sValue.Trim();
1256
 
 
1257
 
                if ((!sName.empty()) && (!sValue.empty())) {
1258
 
                        if (pUser) {
1259
 
                                if (pChan) {
1260
 
                                        if (sName.Equals("Buffer")) {
1261
 
                                                pChan->SetBufferCount(sValue.ToUInt());
1262
 
                                                continue;
1263
 
                                        } else if (sName.Equals("KeepBuffer")) {
1264
 
                                                pChan->SetKeepBuffer(sValue.Equals("true"));
1265
 
                                                continue;
1266
 
                                        } else if (sName.Equals("Detached")) {
1267
 
                                                pChan->SetDetached(sValue.Equals("true"));
1268
 
                                                continue;
1269
 
                                        } else if (sName.Equals("AutoCycle")) {
1270
 
                                                if (sValue.Equals("true")) {
1271
 
                                                        CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle " + pChan->GetName());
1272
 
                                                }
1273
 
                                                continue;
1274
 
                                        } else if (sName.Equals("Key")) {
1275
 
                                                pChan->SetKey(sValue);
1276
 
                                                continue;
1277
 
                                        } else if (sName.Equals("Modes")) {
1278
 
                                                pChan->SetDefaultModes(sValue);
1279
 
                                                continue;
1280
 
                                        }
1281
 
                                } else {
1282
 
                                        if (sName.Equals("Buffer")) {
1283
 
                                                pUser->SetBufferCount(sValue.ToUInt());
1284
 
                                                continue;
1285
 
                                        } else if (sName.Equals("KeepBuffer")) {
1286
 
                                                pUser->SetKeepBuffer(sValue.Equals("true"));
1287
 
                                                continue;
1288
 
                                        } else if (sName.Equals("Nick")) {
1289
 
                                                pUser->SetNick(sValue);
1290
 
                                                continue;
1291
 
                                        } else if (sName.Equals("CTCPReply")) {
1292
 
                                                pUser->AddCTCPReply(sValue.Token(0), sValue.Token(1, true));
1293
 
                                                continue;
1294
 
                                        } else if (sName.Equals("QuitMsg")) {
1295
 
                                                pUser->SetQuitMsg(sValue);
1296
 
                                                continue;
1297
 
                                        } else if (sName.Equals("AltNick")) {
1298
 
                                                pUser->SetAltNick(sValue);
1299
 
                                                continue;
1300
 
                                        } else if (sName.Equals("AwaySuffix")) {
1301
 
                                                CUtils::PrintMessage("WARNING: AwaySuffix has been depricated, instead try -> LoadModule = awaynick %nick%_" + sValue);
1302
 
                                                continue;
1303
 
                                        } else if (sName.Equals("AutoCycle")) {
1304
 
                                                if (sValue.Equals("true")) {
1305
 
                                                        CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle");
1306
 
                                                }
1307
 
                                                continue;
1308
 
                                        } else if (sName.Equals("Pass")) {
1309
 
                                                // There are different formats for this available:
1310
 
                                                // Pass = <plain text>
1311
 
                                                // Pass = <md5 hash> -
1312
 
                                                // Pass = plain#<plain text>
1313
 
                                                // Pass = <hash name>#<hash>
1314
 
                                                // Pass = <hash name>#<salted hash>#<salt>#
1315
 
                                                // 'Salted hash' means hash of 'password' + 'salt'
1316
 
                                                // Possible hashes are md5 and sha256
1317
 
                                                if (sValue.Right(1) == "-") {
1318
 
                                                        sValue.RightChomp();
1319
 
                                                        sValue.Trim();
1320
 
                                                        pUser->SetPass(sValue, CUser::HASH_MD5);
1321
 
                                                } else {
1322
 
                                                        CString sMethod = sValue.Token(0, false, "#");
1323
 
                                                        CString sPass = sValue.Token(1, true, "#");
1324
 
                                                        if (sMethod == "md5" || sMethod == "sha256") {
1325
 
                                                                CUser::eHashType type = CUser::HASH_MD5;
1326
 
                                                                if (sMethod == "sha256")
1327
 
                                                                        type = CUser::HASH_SHA256;
1328
 
 
1329
 
                                                                CString sSalt = sPass.Token(1, false, "#");
1330
 
                                                                sPass = sPass.Token(0, false, "#");
1331
 
                                                                pUser->SetPass(sPass, type, sSalt);
1332
 
                                                        } else if (sMethod == "plain") {
1333
 
                                                                pUser->SetPass(sPass, CUser::HASH_NONE);
1334
 
                                                        } else {
1335
 
                                                                pUser->SetPass(sValue, CUser::HASH_NONE);
1336
 
                                                        }
1337
 
                                                }
1338
 
 
1339
 
                                                continue;
1340
 
                                        } else if (sName.Equals("MultiClients")) {
1341
 
                                                pUser->SetMultiClients(sValue.Equals("true"));
1342
 
                                                continue;
1343
 
                                        } else if (sName.Equals("BounceDCCs")) {
1344
 
                                                pUser->SetBounceDCCs(sValue.Equals("true"));
1345
 
                                                continue;
1346
 
                                        } else if (sName.Equals("Ident")) {
1347
 
                                                pUser->SetIdent(sValue);
1348
 
                                                continue;
1349
 
                                        } else if (sName.Equals("DenyLoadMod")) {
1350
 
                                                pUser->SetDenyLoadMod(sValue.Equals("true"));
1351
 
                                                continue;
1352
 
                                        } else if (sName.Equals("Admin")) {
1353
 
                                                pUser->SetAdmin(sValue.Equals("true"));
1354
 
                                                continue;
1355
 
                                        } else if (sName.Equals("DenySetVHost")) {
1356
 
                                                pUser->SetDenySetVHost(sValue.Equals("true"));
1357
 
                                                continue;
1358
 
                                        } else if (sName.Equals("StatusPrefix")) {
1359
 
                                                if (!pUser->SetStatusPrefix(sValue)) {
1360
 
                                                        sError = "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces.";
1361
 
                                                        CUtils::PrintError(sError);
1362
 
                                                        return false;
1363
 
                                                }
1364
 
                                                continue;
1365
 
                                        } else if (sName.Equals("DCCLookupMethod")) {
1366
 
                                                pUser->SetUseClientIP(sValue.Equals("Client"));
1367
 
                                                continue;
1368
 
                                        } else if (sName.Equals("RealName")) {
1369
 
                                                pUser->SetRealName(sValue);
1370
 
                                                continue;
1371
 
                                        } else if (sName.Equals("KeepNick")) {
1372
 
                                                if (sValue.Equals("true")) {
1373
 
                                                        CUtils::PrintError("WARNING: KeepNick has been deprecated, instead try -> LoadModule = keepnick");
1374
 
                                                }
1375
 
                                                continue;
1376
 
                                        } else if (sName.Equals("ChanModes")) {
1377
 
                                                pUser->SetDefaultChanModes(sValue);
1378
 
                                                continue;
1379
 
                                        } else if (sName.Equals("VHost")) {
1380
 
                                                pUser->SetVHost(sValue);
1381
 
                                                continue;
1382
 
                                        } else if (sName.Equals("DCCVHost")) {
1383
 
                                                pUser->SetDCCVHost(sValue);
1384
 
                                                continue;
1385
 
                                        } else if (sName.Equals("Allow")) {
1386
 
                                                pUser->AddAllowedHost(sValue);
1387
 
                                                continue;
1388
 
                                        } else if (sName.Equals("Server")) {
1389
 
                                                CUtils::PrintAction("Adding Server [" + sValue + "]");
1390
 
                                                CUtils::PrintStatus(pUser->AddServer(sValue));
1391
 
                                                continue;
1392
 
                                        } else if (sName.Equals("Chan")) {
1393
 
                                                pUser->AddChan(sValue, true);
1394
 
                                                continue;
1395
 
                                        } else if (sName.Equals("TimestampFormat")) {
1396
 
                                                pUser->SetTimestampFormat(sValue);
1397
 
                                                continue;
1398
 
                                        } else if (sName.Equals("AppendTimestamp")) {
1399
 
                                                pUser->SetTimestampAppend(sValue.ToBool());
1400
 
                                                continue;
1401
 
                                        } else if (sName.Equals("PrependTimestamp")) {
1402
 
                                                pUser->SetTimestampPrepend(sValue.ToBool());
1403
 
                                                continue;
1404
 
                                        } else if (sName.Equals("IRCConnectEnabled")) {
1405
 
                                                pUser->SetIRCConnectEnabled(sValue.ToBool());
1406
 
                                                continue;
1407
 
                                        } else if (sName.Equals("Timestamp")) {
1408
 
                                                if (!sValue.Trim_n().Equals("true")) {
1409
 
                                                        if (sValue.Trim_n().Equals("append")) {
1410
 
                                                                pUser->SetTimestampAppend(true);
1411
 
                                                                pUser->SetTimestampPrepend(false);
1412
 
                                                        } else if (sValue.Trim_n().Equals("prepend")) {
1413
 
                                                                pUser->SetTimestampAppend(false);
1414
 
                                                                pUser->SetTimestampPrepend(true);
1415
 
                                                        } else if (sValue.Trim_n().Equals("false")) {
1416
 
                                                                pUser->SetTimestampAppend(false);
1417
 
                                                                pUser->SetTimestampPrepend(false);
1418
 
                                                        } else {
1419
 
                                                                pUser->SetTimestampFormat(sValue);
1420
 
                                                        }
1421
 
                                                }
1422
 
                                                continue;
1423
 
                                        } else if (sName.Equals("TimezoneOffset")) {
1424
 
                                                pUser->SetTimezoneOffset(sValue.ToDouble()); // there is no ToFloat()
1425
 
                                                continue;
1426
 
                                        } else if (sName.Equals("JoinTries")) {
1427
 
                                                pUser->SetJoinTries(sValue.ToUInt());
1428
 
                                                continue;
1429
 
                                        } else if (sName.Equals("MaxJoins")) {
1430
 
                                                pUser->SetMaxJoins(sValue.ToUInt());
1431
 
                                                continue;
1432
 
                                        } else if (sName.Equals("Skin")) {
1433
 
                                                pUser->SetSkinName(sValue);
1434
 
                                                continue;
1435
 
                                        } else if (sName.Equals("LoadModule")) {
1436
 
                                                CString sModName = sValue.Token(0);
1437
 
 
1438
 
                                                // XXX Legacy crap, added in znc 0.089
1439
 
                                                if (sModName == "discon_kick") {
1440
 
                                                        CUtils::PrintMessage("NOTICE: [discon_kick] was renamed, loading [disconkick] instead");
1441
 
                                                        sModName = "disconkick";
1442
 
                                                }
1443
 
 
1444
 
                                                CUtils::PrintAction("Loading Module [" + sModName + "]");
1445
 
                                                CString sModRet;
1446
 
                                                CString sArgs = sValue.Token(1, true);
1447
 
 
1448
 
                                                bool bModRet = pUser->GetModules().LoadModule(sModName, sArgs, pUser, sModRet);
1449
 
 
1450
 
                                                // If the module was loaded, sModRet contains
1451
 
                                                // "Loaded Module [name] ..." and we strip away this beginning.
1452
 
                                                if (bModRet)
1453
 
                                                        sModRet = sModRet.Token(1, true, sModName + "] ");
1454
 
 
1455
 
                                                CUtils::PrintStatus(bModRet, sModRet);
1456
 
                                                if (!bModRet) {
1457
 
                                                        sError = sModRet;
1458
 
                                                        return false;
1459
 
                                                }
1460
 
                                                continue;
1461
 
                                        }
1462
 
                                }
1463
 
                        } else {
1464
 
                                if (sName.Equals("Listen") || sName.Equals("Listen6") || sName.Equals("Listen4")
1465
 
                                                || sName.Equals("Listener") || sName.Equals("Listener6") || sName.Equals("Listener4")) {
1466
 
                                        EAddrType eAddr = ADDR_ALL;
1467
 
                                        if (sName.Equals("Listen4") || sName.Equals("Listen") || sName.Equals("Listener4")) {
1468
 
                                                eAddr = ADDR_IPV4ONLY;
1469
 
                                        }
1470
 
                                        if (sName.Equals("Listener6")) {
1471
 
                                                eAddr = ADDR_IPV6ONLY;
1472
 
                                        }
1473
 
 
1474
 
                                        CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
1475
 
                                        if (sValue.TrimPrefix("irc_only "))
1476
 
                                                eAccept = CListener::ACCEPT_IRC;
1477
 
                                        else if (sValue.TrimPrefix("web_only "))
1478
 
                                                eAccept = CListener::ACCEPT_HTTP;
1479
 
 
1480
 
                                        bool bSSL = false;
1481
 
                                        CString sPort;
1482
 
                                        CString sBindHost;
1483
 
 
1484
 
                                        if (ADDR_IPV4ONLY == eAddr) {
1485
 
                                                sValue.Replace(":", " ");
1486
 
                                        }
1487
 
 
1488
 
                                        if (sValue.find(" ") != CString::npos) {
1489
 
                                                sBindHost = sValue.Token(0, false, " ");
1490
 
                                                sPort = sValue.Token(1, true, " ");
1491
 
                                        } else {
1492
 
                                                sPort = sValue;
1493
 
                                        }
1494
 
 
1495
 
                                        if (sPort.Left(1) == "+") {
1496
 
                                                sPort.LeftChomp();
1497
 
                                                bSSL = true;
1498
 
                                        }
1499
 
 
1500
 
                                        CString sHostComment;
1501
 
 
1502
 
                                        if (!sBindHost.empty()) {
1503
 
                                                sHostComment = " on host [" + sBindHost + "]";
1504
 
                                        }
1505
 
 
1506
 
                                        CString sIPV6Comment;
1507
 
 
1508
 
                                        switch (eAddr) {
1509
 
                                                case ADDR_ALL:
1510
 
                                                        sIPV6Comment = "";
1511
 
                                                        break;
1512
 
                                                case ADDR_IPV4ONLY:
1513
 
                                                        sIPV6Comment = " using ipv4";
1514
 
                                                        break;
1515
 
                                                case ADDR_IPV6ONLY:
1516
 
                                                        sIPV6Comment = " using ipv6";
1517
 
                                        }
1518
 
 
1519
 
                                        unsigned short uPort = sPort.ToUShort();
1520
 
                                        CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") + CString(uPort) + "]" + sHostComment + sIPV6Comment);
1521
 
 
1522
 
#ifndef HAVE_IPV6
1523
 
                                        if (ADDR_IPV6ONLY == eAddr) {
1524
 
                                                sError = "IPV6 is not enabled";
1525
 
                                                CUtils::PrintStatus(false, sError);
1526
 
                                                return false;
1527
 
                                        }
1528
 
#endif
1529
 
 
1530
 
#ifndef HAVE_LIBSSL
1531
 
                                        if (bSSL) {
1532
 
                                                sError = "SSL is not enabled";
1533
 
                                                CUtils::PrintStatus(false, sError);
1534
 
                                                return false;
1535
 
                                        }
1536
 
#else
1537
 
                                        CString sPemFile = GetPemLocation();
1538
 
 
1539
 
                                        if (bSSL && !CFile::Exists(sPemFile)) {
1540
 
                                                sError = "Unable to locate pem file: [" + sPemFile + "]";
1541
 
                                                CUtils::PrintStatus(false, sError);
1542
 
 
1543
 
                                                // If stdin is e.g. /dev/null and we call GetBoolInput(),
1544
 
                                                // we are stuck in an endless loop!
1545
 
                                                if (isatty(0) && CUtils::GetBoolInput("Would you like to create a new pem file?", true)) {
1546
 
                                                        sError.clear();
1547
 
                                                        WritePemFile();
1548
 
                                                } else {
1549
 
                                                        return false;
1550
 
                                                }
1551
 
 
1552
 
                                                CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" + sHostComment + sIPV6Comment);
1553
 
                                        }
1554
 
#endif
1555
 
                                        if (!uPort) {
1556
 
                                                sError = "Invalid port";
1557
 
                                                CUtils::PrintStatus(false, sError);
1558
 
                                                return false;
1559
 
                                        }
1560
 
 
1561
 
                                        CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
1562
 
 
1563
 
                                        if (!pListener->Listen()) {
1564
 
                                                sError = (errno == 0 ? CString("unknown error, check the host name") : CString(strerror(errno)));
1565
 
                                                sError = "Unable to bind [" + sError + "]";
1566
 
                                                CUtils::PrintStatus(false, sError);
1567
 
                                                delete pListener;
1568
 
                                                return false;
1569
 
                                        }
1570
 
 
1571
 
                                        m_vpListeners.push_back(pListener);
1572
 
                                        CUtils::PrintStatus(true);
1573
 
 
1574
 
                                        continue;
1575
 
                                } else if (sName.Equals("LoadModule")) {
1576
 
                                        CString sModName = sValue.Token(0);
1577
 
                                        CString sArgs = sValue.Token(1, true);
1578
 
 
1579
 
                                        if (msModules.find(sModName) != msModules.end()) {
1580
 
                                                sError = "Module [" + sModName +
1581
 
                                                        "] already loaded";
1582
 
                                                CUtils::PrintError(sError);
1583
 
                                                return false;
1584
 
                                        }
1585
 
                                        msModules[sModName] = sArgs;
1586
 
                                        continue;
1587
 
                                } else if (sName.Equals("ISpoofFormat")) {
1588
 
                                        m_sISpoofFormat = sValue;
1589
 
                                        continue;
1590
 
                                } else if (sName.Equals("ISpoofFile")) {
1591
 
                                        if (sValue.Left(2) == "~/") {
1592
 
                                                sValue.LeftChomp(2);
1593
 
                                                sValue = GetHomePath() + "/" + sValue;
1594
 
                                        }
1595
 
                                        m_sISpoofFile = sValue;
1596
 
                                        continue;
1597
 
                                } else if (sName.Equals("MOTD")) {
1598
 
                                        AddMotd(sValue);
1599
 
                                        continue;
1600
 
                                } else if (sName.Equals("VHost")) {
1601
 
                                        AddVHost(sValue);
1602
 
                                        continue;
1603
 
                                } else if (sName.Equals("PidFile")) {
1604
 
                                        m_sPidFile = sValue;
1605
 
                                        continue;
1606
 
                                } else if (sName.Equals("Skin")) {
1607
 
                                        SetSkinName(sValue);
1608
 
                                        continue;
1609
 
                                } else if (sName.Equals("StatusPrefix")) {
1610
 
                                        m_sStatusPrefix = sValue;
1611
 
                                        continue;
1612
 
                                } else if (sName.Equals("ConnectDelay")) {
1613
 
                                        m_uiConnectDelay = sValue.ToUInt();
1614
 
                                        continue;
1615
 
                                } else if (sName.Equals("ServerThrottle")) {
1616
 
                                        m_sConnectThrottle.SetTTL(sValue.ToUInt()*1000);
1617
 
                                        continue;
1618
 
                                } else if (sName.Equals("AnonIPLimit")) {
1619
 
                                        m_uiAnonIPLimit = sValue.ToUInt();
1620
 
                                        continue;
1621
 
                                }
1622
 
                        }
1623
 
 
1624
 
                }
1625
 
 
1626
 
                if (sName.Equals("GM:", false, 3))
1627
 
                { // GM: prefix is a pass through to config lines for global modules
1628
 
                        CGlobalModuleConfigLine cTmp;
1629
 
                        cTmp.m_sName = sName.substr(3, CString::npos);
1630
 
                        cTmp.m_sValue = sValue;
1631
 
                        cTmp.m_pChan = pChan;
1632
 
                        cTmp.m_pUser = pUser;
1633
 
                        lGlobalModuleConfigLine.push_back(cTmp);
1634
 
                }
1635
 
                else
1636
 
                {
1637
 
                        sError = "Unhandled line " + CString(uLineNum) + " in config: [" + sLine + "]";
1638
 
                        CUtils::PrintError(sError);
1639
 
                        return false;
1640
 
                }
1641
 
        }
1642
 
 
1643
 
        // First step: Load and reload new modules or modules with new arguments
1644
 
        for (MCString::iterator it = msModules.begin(); it != msModules.end(); ++it) {
1645
 
                CString sModName = it->first;
1646
 
                CString sArgs = it->second;
1647
 
                CString sModRet;
1648
 
                CModule *pOldMod;
1649
 
 
1650
 
                pOldMod = GetModules().FindModule(sModName);
1651
 
                if (!pOldMod) {
1652
 
                        CUtils::PrintAction("Loading Global Module [" + sModName + "]");
1653
 
 
1654
 
                        bool bModRet = GetModules().LoadModule(sModName, sArgs, NULL, sModRet);
1655
 
 
1656
 
                        // If the module was loaded, sModRet contains
1657
 
                        // "Loaded Module [name] ..." and we strip away this beginning.
1658
 
                        if (bModRet)
1659
 
                                sModRet = sModRet.Token(1, true, sModName + "] ");
1660
 
 
1661
 
                        CUtils::PrintStatus(bModRet, sModRet);
1662
 
                        if (!bModRet) {
1663
 
                                sError = sModRet;
1664
 
                                return false;
1665
 
                        }
1666
 
                } else if (pOldMod->GetArgs() != sArgs) {
1667
 
                        CUtils::PrintAction("Reloading Global Module [" + sModName + "]");
1668
 
 
1669
 
                        bool bModRet = GetModules().ReloadModule(sModName, sArgs, NULL, sModRet);
1670
 
 
1671
 
                        // If the module was loaded, sModRet contains
1672
 
                        // "Loaded Module [name] ..." and we strip away this beginning.
1673
 
                        if (bModRet)
1674
 
                                sModRet = sModRet.Token(1, true, sModName + "] ");
1675
 
 
1676
 
                        CUtils::PrintStatus(bModRet, sModRet);
1677
 
                        if (!bModRet) {
1678
 
                                sError = sModRet;
1679
 
                                return false;
1680
 
                        }
1681
 
                } else
1682
 
                        CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
1683
 
        }
1684
 
 
1685
 
        // Second step: Unload modules which are no longer in the config
1686
 
        set<CString> ssUnload;
1687
 
        for (size_t i = 0; i < GetModules().size(); i++) {
1688
 
                CModule *pCurMod = GetModules()[i];
1689
 
 
1690
 
                if (msModules.find(pCurMod->GetModName()) == msModules.end())
1691
 
                        ssUnload.insert(pCurMod->GetModName());
1692
 
        }
1693
 
 
1694
 
        for (set<CString>::iterator it = ssUnload.begin(); it != ssUnload.end(); ++it) {
1695
 
                if (GetModules().UnloadModule(*it))
1696
 
                        CUtils::PrintMessage("Unloaded Global Module [" + *it + "]");
1697
 
                else
1698
 
                        CUtils::PrintMessage("Could not unload [" + *it + "]");
1699
 
        }
1700
 
 
1701
 
        // last step, throw unhandled config items at global config
1702
 
        for (std::list<CGlobalModuleConfigLine>::iterator it = lGlobalModuleConfigLine.begin(); it != lGlobalModuleConfigLine.end(); ++it)
1703
 
        {
1704
 
                if ((pChan && pChan == it->m_pChan) || (pUser && pUser == it->m_pUser))
1705
 
                        continue; // skip unclosed user or chan
1706
 
                bool bHandled = false;
1707
 
                if (it->m_pUser) {
1708
 
                        MODULECALL(OnConfigLine(it->m_sName, it->m_sValue, it->m_pUser, it->m_pChan), it->m_pUser, NULL, bHandled = true);
1709
 
                } else {
1710
 
                        bHandled = GetModules().OnConfigLine(it->m_sName, it->m_sValue, it->m_pUser, it->m_pChan);
1711
 
                }
1712
 
                if (!bHandled) {
1713
 
                        CUtils::PrintMessage("unhandled global module config line [GM:" + it->m_sName + "] = [" + it->m_sValue + "]");
1714
 
                }
1715
 
        }
1716
 
 
1717
 
        if (pChan) {
1718
 
                // TODO last <Chan> not closed
1719
 
                delete pChan;
1720
 
        }
1721
 
 
1722
 
        if (pUser) {
1723
 
                // TODO last <User> not closed
1724
 
                delete pUser;
1725
 
        }
1726
 
 
1727
 
        if (m_msUsers.empty()) {
1728
 
                sError = "You must define at least one user in your config.";
1729
 
                CUtils::PrintError(sError);
1730
 
                return false;
1731
 
        }
1732
 
 
1733
 
        if (m_vpListeners.empty()) {
1734
 
                sError = "You must supply at least one Listen port in your config.";
1735
 
                CUtils::PrintError(sError);
1736
 
                return false;
1737
 
        }
1738
 
 
1739
 
        // Make sure that users that want to connect do so and also make sure a
1740
 
        // new ConnectDelay setting is applied.
1741
 
        DisableConnectUser();
1742
 
        EnableConnectUser();
1743
 
 
1744
 
        return true;
1745
 
}
1746
 
 
1747
 
void CZNC::ClearVHosts() {
1748
 
        m_vsVHosts.clear();
1749
 
}
1750
 
 
1751
 
bool CZNC::AddVHost(const CString& sHost) {
1752
 
        if (sHost.empty()) {
1753
 
                return false;
1754
 
        }
1755
 
 
1756
 
        for (unsigned int a = 0; a < m_vsVHosts.size(); a++) {
1757
 
                if (m_vsVHosts[a].Equals(sHost)) {
1758
 
                        return false;
1759
 
                }
1760
 
        }
1761
 
 
1762
 
        m_vsVHosts.push_back(sHost);
1763
 
        return true;
1764
 
}
1765
 
 
1766
 
bool CZNC::RemVHost(const CString& sHost) {
1767
 
        VCString::iterator it;
1768
 
        for (it = m_vsVHosts.begin(); it != m_vsVHosts.end(); ++it) {
1769
 
                if (sHost.Equals(*it)) {
1770
 
                        m_vsVHosts.erase(it);
1771
 
                        return true;
1772
 
                }
1773
 
        }
1774
 
 
1775
 
        return false;
1776
 
}
1777
 
 
1778
 
void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly,
1779
 
                CUser* pSkipUser, CClient *pSkipClient) {
1780
 
        for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
1781
 
                if (bAdminOnly && !a->second->IsAdmin())
1782
 
                        continue;
1783
 
 
1784
 
                if (a->second != pSkipUser) {
1785
 
                        CString sMsg = sMessage;
1786
 
 
1787
 
                        MODULECALL(OnBroadcast(sMsg), a->second, NULL, continue);
1788
 
                        a->second->PutStatusNotice("*** " + sMsg, NULL, pSkipClient);
1789
 
                }
1790
 
        }
1791
 
}
1792
 
 
1793
 
CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) {
1794
 
        if (sUsername.empty()) {
1795
 
                return CZNC::Get().GetModules().FindModule(sModName);
1796
 
        }
1797
 
 
1798
 
        CUser* pUser = FindUser(sUsername);
1799
 
 
1800
 
        return (!pUser) ? NULL : pUser->GetModules().FindModule(sModName);
1801
 
}
1802
 
 
1803
 
CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
1804
 
        if (pUser) {
1805
 
                return pUser->GetModules().FindModule(sModName);
1806
 
        }
1807
 
 
1808
 
        return CZNC::Get().GetModules().FindModule(sModName);
1809
 
}
1810
 
 
1811
 
CUser* CZNC::FindUser(const CString& sUsername) {
1812
 
        map<CString,CUser*>::iterator it = m_msUsers.find(sUsername);
1813
 
 
1814
 
        if (it != m_msUsers.end()) {
1815
 
                return it->second;
1816
 
        }
1817
 
 
1818
 
        return NULL;
1819
 
}
1820
 
 
1821
 
bool CZNC::DeleteUser(const CString& sUsername) {
1822
 
        CUser* pUser = FindUser(sUsername);
1823
 
 
1824
 
        if (!pUser) {
1825
 
                return false;
1826
 
        }
1827
 
 
1828
 
        m_msDelUsers[pUser->GetUserName()] = pUser;
1829
 
        return true;
1830
 
}
1831
 
 
1832
 
bool CZNC::AddUser(CUser* pUser, CString& sErrorRet) {
1833
 
        if (FindUser(pUser->GetUserName()) != NULL) {
1834
 
                sErrorRet = "User already exists";
1835
 
                DEBUG("User [" << pUser->GetUserName() << "] - already exists");
1836
 
                return false;
1837
 
        }
1838
 
        if (!pUser->IsValid(sErrorRet)) {
1839
 
                DEBUG("Invalid user [" << pUser->GetUserName() << "] - ["
1840
 
                                << sErrorRet << "]");
1841
 
                return false;
1842
 
        }
1843
 
        if (GetModules().OnAddUser(*pUser, sErrorRet)) {
1844
 
                DEBUG("AddUser [" << pUser->GetUserName() << "] aborted by a module ["
1845
 
                        << sErrorRet << "]");
1846
 
                return false;
1847
 
        }
1848
 
        m_msUsers[pUser->GetUserName()] = pUser;
1849
 
        return true;
1850
 
}
1851
 
 
1852
 
CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost, EAddrType eAddr) {
1853
 
        vector<CListener*>::iterator it;
1854
 
 
1855
 
        for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) {
1856
 
                if ((*it)->GetPort() != uPort)
1857
 
                        continue;
1858
 
                if ((*it)->GetBindHost() != sBindHost)
1859
 
                        continue;
1860
 
                if ((*it)->GetAddrType() != eAddr)
1861
 
                        continue;
1862
 
                return *it;
1863
 
        }
1864
 
        return NULL;
1865
 
}
1866
 
 
1867
 
bool CZNC::AddListener(CListener* pListener) {
1868
 
        if (!pListener->GetRealListener()) {
1869
 
                // Listener doesnt actually listen
1870
 
                delete pListener;
1871
 
                return false;
1872
 
        }
1873
 
 
1874
 
        // We don't check if there is an identical listener already listening
1875
 
        // since one can't listen on e.g. the same port multiple times
1876
 
 
1877
 
        m_vpListeners.push_back(pListener);
1878
 
        return true;
1879
 
}
1880
 
 
1881
 
bool CZNC::DelListener(CListener* pListener) {
1882
 
        vector<CListener*>::iterator it;
1883
 
 
1884
 
        for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) {
1885
 
                if (*it == pListener) {
1886
 
                        m_vpListeners.erase(it);
1887
 
                        delete pListener;
1888
 
                        return true;
1889
 
                }
1890
 
        }
1891
 
 
1892
 
        return false;
1893
 
}
1894
 
 
1895
 
CZNC& CZNC::Get() {
1896
 
        static CZNC* pZNC = new CZNC;
1897
 
        return *pZNC;
1898
 
}
1899
 
 
1900
 
CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair &Users,
1901
 
                        TrafficStatsPair &ZNC, TrafficStatsPair &Total) {
1902
 
        TrafficStatsMap ret;
1903
 
        unsigned long long uiUsers_in, uiUsers_out, uiZNC_in, uiZNC_out;
1904
 
        const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
1905
 
 
1906
 
        uiUsers_in = uiUsers_out = 0;
1907
 
        uiZNC_in  = BytesRead();
1908
 
        uiZNC_out = BytesWritten();
1909
 
 
1910
 
        for (map<CString, CUser*>::const_iterator it = msUsers.begin(); it != msUsers.end(); ++it) {
1911
 
                ret[it->first] = TrafficStatsPair(it->second->BytesRead(), it->second->BytesWritten());
1912
 
                uiUsers_in  += it->second->BytesRead();
1913
 
                uiUsers_out += it->second->BytesWritten();
1914
 
        }
1915
 
 
1916
 
        for (CSockManager::const_iterator it = m_Manager.begin(); it != m_Manager.end(); ++it) {
1917
 
                if ((*it)->GetSockName().Left(5) == "IRC::") {
1918
 
                        CIRCSock *p = (CIRCSock *) *it;
1919
 
                        ret[p->GetUser()->GetUserName()].first  += p->GetBytesRead();
1920
 
                        ret[p->GetUser()->GetUserName()].second += p->GetBytesWritten();
1921
 
                        uiUsers_in  += p->GetBytesRead();
1922
 
                        uiUsers_out += p->GetBytesWritten();
1923
 
                } else if ((*it)->GetSockName().Left(5) == "USR::") {
1924
 
                        CClient *p = (CClient *) *it;
1925
 
                        ret[p->GetUser()->GetUserName()].first  += p->GetBytesRead();
1926
 
                        ret[p->GetUser()->GetUserName()].second += p->GetBytesWritten();
1927
 
                        uiUsers_in  += p->GetBytesRead();
1928
 
                        uiUsers_out += p->GetBytesWritten();
1929
 
                } else {
1930
 
                        uiZNC_in  += (*it)->GetBytesRead();
1931
 
                        uiZNC_out += (*it)->GetBytesWritten();
1932
 
                }
1933
 
        }
1934
 
 
1935
 
        Users = TrafficStatsPair(uiUsers_in, uiUsers_out);
1936
 
        ZNC   = TrafficStatsPair(uiZNC_in, uiZNC_out);
1937
 
        Total = TrafficStatsPair(uiUsers_in + uiZNC_in, uiUsers_out + uiZNC_out);
1938
 
 
1939
 
        return ret;
1940
 
}
1941
 
 
1942
 
void CZNC::AuthUser(CSmartPtr<CAuthBase> AuthClass) {
1943
 
        // TODO unless the auth module calls it, CUser::IsHostAllowed() is not honoured
1944
 
        if (GetModules().OnLoginAttempt(AuthClass)) {
1945
 
                return;
1946
 
        }
1947
 
 
1948
 
        CUser* pUser = GetUser(AuthClass->GetUsername());
1949
 
 
1950
 
        if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) {
1951
 
                AuthClass->RefuseLogin("Invalid Password");
1952
 
                return;
1953
 
        }
1954
 
 
1955
 
        CString sHost = AuthClass->GetRemoteIP();
1956
 
 
1957
 
        if (!pUser->IsHostAllowed(sHost)) {
1958
 
                AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed");
1959
 
                return;
1960
 
        }
1961
 
 
1962
 
        AuthClass->AcceptLogin(*pUser);
1963
 
}
1964
 
 
1965
 
class CConnectUserTimer : public CCron {
1966
 
public:
1967
 
        CConnectUserTimer(int iSecs) : CCron() {
1968
 
                SetName("Connect users");
1969
 
                Start(iSecs);
1970
 
                m_uiPosNextUser = 0;
1971
 
                // Don't wait iSecs seconds for first timer run
1972
 
                m_bRunOnNextCall = true;
1973
 
        }
1974
 
        virtual ~CConnectUserTimer() {
1975
 
                // This is only needed when ZNC shuts down:
1976
 
                // CZNC::~CZNC() sets its CConnectUserTimer pointer to NULL and
1977
 
                // calls the manager's Cleanup() which destroys all sockets and
1978
 
                // timers. If something calls CZNC::EnableConnectUser() here
1979
 
                // (e.g. because a CIRCSock is destroyed), the socket manager
1980
 
                // deletes that timer almost immediately, but CZNC now got a
1981
 
                // dangling pointer to this timer which can crash later on.
1982
 
                //
1983
 
                // Unlikely but possible ;)
1984
 
                CZNC::Get().LeakConnectUser(this);
1985
 
        }
1986
 
 
1987
 
protected:
1988
 
        virtual void RunJob() {
1989
 
                unsigned int uiUserCount;
1990
 
                bool bUsersLeft = false;
1991
 
                const map<CString,CUser*>& mUsers = CZNC::Get().GetUserMap();
1992
 
                map<CString,CUser*>::const_iterator it = mUsers.begin();
1993
 
 
1994
 
                uiUserCount = CZNC::Get().GetUserMap().size();
1995
 
 
1996
 
                if (m_uiPosNextUser >= uiUserCount) {
1997
 
                        m_uiPosNextUser = 0;
1998
 
                }
1999
 
 
2000
 
                for (unsigned int i = 0; i < m_uiPosNextUser; i++) {
2001
 
                        it++;
2002
 
                }
2003
 
 
2004
 
                // Try to connect each user, if this doesnt work, abort
2005
 
                for (unsigned int i = 0; i < uiUserCount; i++) {
2006
 
                        if (it == mUsers.end())
2007
 
                                it = mUsers.begin();
2008
 
 
2009
 
                        CUser* pUser = it->second;
2010
 
                        it++;
2011
 
                        m_uiPosNextUser = (m_uiPosNextUser + 1) % uiUserCount;
2012
 
 
2013
 
                        // Is this user disconnected?
2014
 
                        if (pUser->GetIRCSock() != NULL)
2015
 
                                continue;
2016
 
 
2017
 
                        // Does this user want to connect?
2018
 
                        if (!pUser->GetIRCConnectEnabled())
2019
 
                                continue;
2020
 
 
2021
 
                        // Does this user have any servers?
2022
 
                        if (!pUser->HasServers())
2023
 
                                continue;
2024
 
 
2025
 
                        // The timer runs until it once didn't find any users to connect
2026
 
                        bUsersLeft = true;
2027
 
 
2028
 
                        DEBUG("Connecting user [" << pUser->GetUserName() << "]");
2029
 
 
2030
 
                        if (CZNC::Get().ConnectUser(pUser))
2031
 
                                // User connecting, wait until next time timer fires
2032
 
                                return;
2033
 
                }
2034
 
 
2035
 
                if (bUsersLeft == false) {
2036
 
                        DEBUG("ConnectUserTimer done");
2037
 
                        CZNC::Get().DisableConnectUser();
2038
 
                }
2039
 
        }
2040
 
 
2041
 
private:
2042
 
        size_t m_uiPosNextUser;
2043
 
};
2044
 
 
2045
 
void CZNC::EnableConnectUser() {
2046
 
        if (m_pConnectUserTimer != NULL)
2047
 
                return;
2048
 
 
2049
 
        m_pConnectUserTimer = new CConnectUserTimer(m_uiConnectDelay);
2050
 
        GetManager().AddCron(m_pConnectUserTimer);
2051
 
}
2052
 
 
2053
 
void CZNC::DisableConnectUser() {
2054
 
        if (m_pConnectUserTimer == NULL)
2055
 
                return;
2056
 
 
2057
 
        // This will kill the cron
2058
 
        m_pConnectUserTimer->Stop();
2059
 
        m_pConnectUserTimer = NULL;
2060
 
}
2061
 
 
2062
 
void CZNC::LeakConnectUser(CConnectUserTimer *pTimer) {
2063
 
        if (m_pConnectUserTimer == pTimer)
2064
 
                m_pConnectUserTimer = NULL;
2065
 
}