]>
Commit | Line | Data |
---|---|---|
a09a7e79 | 1 | /* |
b9b0fd4c | 2 | * Copyright (C) 2004-2011 See the AUTHORS file for details. |
a09a7e79 | 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 | */ | |
6dcacaa7 | 8 | |
538d3ece | 9 | #include "znc.h" |
e72c4456 | 10 | #include "Chan.h" |
3f24f287 | 11 | #include "FileUtils.h" |
538d3ece | 12 | #include "IRCSock.h" |
e72c4456 | 13 | #include "Server.h" |
14 | #include "User.h" | |
eb449190 | 15 | #include "Listener.h" |
70c77458 | 16 | #include "Config.h" |
322483ab | 17 | #include <list> |
538d3ece | 18 | |
672df0a8 A |
19 | #define CONFIG_WRITE_INTERVAL (60 * 5) |
20 | ||
11ffe9db | 21 | static inline CString FormatBindError() { |
22 | CString sError = (errno == 0 ? CString("unknown error, check the host name") : CString(strerror(errno))); | |
23 | return "Unable to bind [" + sError + "]"; | |
24 | } | |
25 | ||
538d3ece | 26 | CZNC::CZNC() { |
c03bd915 | 27 | if (!InitCsocket()) { |
4fd1b09f | 28 | CUtils::PrintError("Could not initialize Csocket!"); |
c03bd915 | 29 | exit(-1); |
30 | } | |
31 | ||
89e5079c | 32 | m_pModules = new CGlobalModules(); |
7bff1c4d | 33 | m_uiConnectDelay = 5; |
16507001 | 34 | m_uiAnonIPLimit = 10; |
b772e266 | 35 | m_uBytesRead = 0; |
36 | m_uBytesWritten = 0; | |
e62ca411 | 37 | m_uiMaxBufferSize = 500; |
a213258c | 38 | m_pConnectUserTimer = NULL; |
c1ee7248 | 39 | m_eConfigState = ECONFIG_NOTHING; |
78f8cce2 | 40 | m_TimeStarted = time(NULL); |
752f19c9 | 41 | m_sConnectThrottle.SetTTL(30000); |
a4d388b5 | 42 | m_pLockFile = NULL; |
a9ba4020 | 43 | m_bProtectWebSessions = true; |
672df0a8 | 44 | m_pConfigTimer = NULL; |
538d3ece | 45 | } |
46 | ||
47 | CZNC::~CZNC() { | |
0435ece3 | 48 | m_pModules->UnloadAll(); |
3dde793e | 49 | |
115b1708 | 50 | for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) { |
2f0d4e67 | 51 | a->second->GetModules().UnloadAll(); |
52 | } | |
2f0d4e67 | 53 | |
ca824705 | 54 | for (size_t b = 0; b < m_vpListeners.size(); b++) { |
55 | delete m_vpListeners[b]; | |
56 | } | |
57 | ||
115b1708 | 58 | for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) { |
0dd41818 | 59 | a->second->SetBeingDeleted(true); |
60 | } | |
61 | ||
c4891c5d | 62 | m_pConnectUserTimer = NULL; |
a213258c | 63 | // This deletes m_pConnectUserTimer |
538d3ece | 64 | m_Manager.Cleanup(); |
65 | DeleteUsers(); | |
f8bb245c | 66 | |
b8c4fc76 | 67 | delete m_pModules; |
ee9686e4 | 68 | delete m_pLockFile; |
9a1c3874 | 69 | |
c03bd915 | 70 | ShutdownCsocket(); |
9a1c3874 | 71 | DeletePidFile(); |
538d3ece | 72 | } |
73 | ||
d397cb8a | 74 | CString CZNC::GetVersion() { |
75 | char szBuf[128]; | |
76 | ||
77 | snprintf(szBuf, sizeof(szBuf), "%1.3f"VERSION_EXTRA, VERSION); | |
78 | // If snprintf overflows (which I doubt), we want to be on the safe side | |
79 | szBuf[sizeof(szBuf) - 1] = '\0'; | |
80 | ||
81 | return szBuf; | |
82 | } | |
83 | ||
beb5b49b | 84 | CString CZNC::GetTag(bool bIncludeVersion) { |
c2d92663 | 85 | if (!bIncludeVersion) { |
087f01e9 | 86 | return "ZNC - http://znc.in"; |
c2d92663 | 87 | } |
88 | ||
44d38b77 | 89 | char szBuf[128]; |
087f01e9 | 90 | snprintf(szBuf, sizeof(szBuf), "ZNC %1.3f"VERSION_EXTRA" - http://znc.in", VERSION); |
d397cb8a | 91 | // If snprintf overflows (which I doubt), we want to be on the safe side |
92 | szBuf[sizeof(szBuf) - 1] = '\0'; | |
c2d92663 | 93 | |
94 | return szBuf; | |
95 | } | |
96 | ||
86924339 | 97 | CString CZNC::GetUptime() const { |
bcf59c56 | 98 | time_t now = time(NULL); |
b2512e55 | 99 | return CString::ToTimeStr(now - TimeStarted()); |
bcf59c56 | 100 | } |
101 | ||
538d3ece | 102 | bool CZNC::OnBoot() { |
9ae959b8 | 103 | ALLMODULECALL(OnBoot(), return false); |
538d3ece | 104 | |
105 | return true; | |
106 | } | |
107 | ||
a213258c | 108 | bool CZNC::ConnectUser(CUser *pUser) { |
109 | CString sSockName = "IRC::" + pUser->GetUserName(); | |
b359f968 | 110 | CIRCSock* pIRCSock = pUser->GetIRCSock(); |
a213258c | 111 | |
5c7b586b | 112 | if (!pUser->GetIRCConnectEnabled()) |
113 | return false; | |
a213258c | 114 | |
115 | if (pIRCSock || !pUser->HasServers()) | |
116 | return false; | |
117 | ||
a213258c | 118 | CServer* pServer = pUser->GetNextServer(); |
119 | ||
120 | if (!pServer) | |
121 | return false; | |
122 | ||
752f19c9 | 123 | if (m_sConnectThrottle.GetItem(pServer->GetName())) |
124 | return false; | |
125 | ||
752f19c9 | 126 | m_sConnectThrottle.AddItem(pServer->GetName()); |
127 | ||
e0dda308 | 128 | DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetString(false) << "] ..."); |
129 | pUser->PutStatus("Attempting to connect to [" + pServer->GetString(false) + "] ..."); | |
a213258c | 130 | |
131 | pIRCSock = new CIRCSock(pUser); | |
132 | pIRCSock->SetPass(pServer->GetPass()); | |
133 | ||
134 | bool bSSL = false; | |
135 | #ifdef HAVE_LIBSSL | |
136 | if (pServer->IsSSL()) { | |
137 | bSSL = true; | |
138 | } | |
139 | #endif | |
140 | ||
d0a38e41 | 141 | MODULECALL(OnIRCConnecting(pIRCSock), pUser, NULL, |
142 | DEBUG("Some module aborted the connection attempt"); | |
3f493072 | 143 | pUser->PutStatus("Some module aborted the connection attempt"); |
d0a38e41 | 144 | delete pIRCSock; |
145 | return false; | |
146 | ); | |
147 | ||
341263f9 | 148 | if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, pUser->GetBindHost(), pIRCSock)) { |
a213258c | 149 | pUser->PutStatus("Unable to connect. (Bad host?)"); |
150 | } | |
151 | ||
152 | return true; | |
153 | } | |
154 | ||
45dae8a1 | 155 | bool CZNC::HandleUserDeletion() |
156 | { | |
157 | map<CString, CUser*>::iterator it; | |
158 | map<CString, CUser*>::iterator end; | |
538d3ece | 159 | |
291ad479 | 160 | if (m_msDelUsers.empty()) |
45dae8a1 | 161 | return false; |
b51302be | 162 | |
45dae8a1 | 163 | end = m_msDelUsers.end(); |
115b1708 | 164 | for (it = m_msDelUsers.begin(); it != end; ++it) { |
45dae8a1 | 165 | CUser* pUser = it->second; |
166 | pUser->SetBeingDeleted(true); | |
57fb9fc8 | 167 | |
45dae8a1 | 168 | if (GetModules().OnDeleteUser(*pUser)) { |
169 | pUser->SetBeingDeleted(false); | |
170 | continue; | |
171 | } | |
45dae8a1 | 172 | m_msUsers.erase(pUser->GetUserName()); |
c315a07d | 173 | |
b359f968 | 174 | CIRCSock* pIRCSock = pUser->GetIRCSock(); |
c315a07d | 175 | |
45dae8a1 | 176 | if (pIRCSock) { |
177 | m_Manager.DelSockByAddr(pIRCSock); | |
178 | } | |
c315a07d | 179 | |
45dae8a1 | 180 | pUser->DelClients(); |
45dae8a1 | 181 | pUser->DelModules(); |
c3728f4c | 182 | CWebSock::FinishUserSessions(*pUser); |
45dae8a1 | 183 | AddBytesRead(pUser->BytesRead()); |
184 | AddBytesWritten(pUser->BytesWritten()); | |
185 | delete pUser; | |
186 | } | |
187 | ||
188 | m_msDelUsers.clear(); | |
c315a07d | 189 | |
45dae8a1 | 190 | return true; |
191 | } | |
192 | ||
672df0a8 A |
193 | class CConfigWriteTimer : public CCron { |
194 | public: | |
195 | CConfigWriteTimer(int iSecs) : CCron() { | |
196 | SetName("Config write timer"); | |
197 | Start(iSecs); | |
198 | } | |
199 | ||
200 | protected: | |
201 | void RunJob() { | |
202 | CZNC::Get().SetConfigState(CZNC::ECONFIG_NEED_WRITE); | |
203 | ||
204 | CZNC::Get().DisableConfigTimer(); | |
205 | } | |
206 | }; | |
207 | ||
e64b2549 | 208 | void CZNC::Loop() { |
45dae8a1 | 209 | while (true) { |
e326f6eb | 210 | CString sError; |
e326f6eb | 211 | |
672df0a8 A |
212 | ConfigState eState = GetConfigState(); |
213 | switch (eState) { | |
c1ee7248 | 214 | case ECONFIG_NEED_REHASH: |
215 | SetConfigState(ECONFIG_NOTHING); | |
e326f6eb | 216 | |
803b34c6 | 217 | if (RehashConfig(sError)) { |
218 | Broadcast("Rehashing succeeded", true); | |
219 | } else { | |
220 | Broadcast("Rehashing failed: " + sError, true); | |
221 | Broadcast("ZNC is in some possibly inconsistent state!", true); | |
e326f6eb | 222 | } |
c1ee7248 | 223 | break; |
672df0a8 A |
224 | case ECONFIG_WANT_WRITE: |
225 | SetConfigState(ECONFIG_NOTHING); | |
226 | ||
227 | if (m_pConfigTimer == NULL) { | |
228 | m_pConfigTimer = new CConfigWriteTimer(CONFIG_WRITE_INTERVAL); | |
229 | GetManager().AddCron(m_pConfigTimer); | |
230 | } | |
231 | break; | |
c1ee7248 | 232 | case ECONFIG_NEED_WRITE: |
672df0a8 | 233 | case ECONFIG_NEED_VERBOSE_WRITE: |
c1ee7248 | 234 | SetConfigState(ECONFIG_NOTHING); |
235 | ||
672df0a8 | 236 | if (!WriteConfig()) { |
c1ee7248 | 237 | Broadcast("Writing the config file failed", true); |
672df0a8 A |
238 | } else if (eState == ECONFIG_NEED_VERBOSE_WRITE) { |
239 | Broadcast("Writing the config succeeded", true); | |
c1ee7248 | 240 | } |
241 | break; | |
242 | case ECONFIG_NOTHING: | |
243 | break; | |
e326f6eb | 244 | } |
245 | ||
45dae8a1 | 246 | // Check for users that need to be deleted |
247 | if (HandleUserDeletion()) { | |
248 | // Also remove those user(s) from the config file | |
29e14b42 | 249 | WriteConfig(); |
c315a07d | 250 | } |
251 | ||
a213258c | 252 | // Csocket wants micro seconds |
cf41b0ac | 253 | // 500 msec to 600 sec |
254 | m_Manager.DynamicSelectLoop(500 * 1000, 600 * 1000 * 1000); | |
538d3ece | 255 | } |
538d3ece | 256 | } |
257 | ||
e2b91a32 | 258 | CFile* CZNC::InitPidFile() { |
538d3ece | 259 | if (!m_sPidFile.empty()) { |
8a9ddda2 | 260 | CString sFile; |
261 | ||
262 | // absolute path or relative to the data dir? | |
263 | if (m_sPidFile[0] != '/') | |
264 | sFile = GetZNCPath() + "/" + m_sPidFile; | |
265 | else | |
266 | sFile = m_sPidFile; | |
267 | ||
e2b91a32 | 268 | return new CFile(sFile); |
269 | } | |
538d3ece | 270 | |
e2b91a32 | 271 | return NULL; |
272 | } | |
e4aabe6c | 273 | |
e2b91a32 | 274 | bool CZNC::WritePidFile(int iPid) { |
275 | CFile* File = InitPidFile(); | |
276 | if (File == NULL) | |
277 | return false; | |
278 | ||
279 | CUtils::PrintAction("Writing pid file [" + File->GetLongName() + "]"); | |
280 | ||
281 | bool bRet = false; | |
282 | if (File->Open(O_WRONLY | O_TRUNC | O_CREAT)) { | |
283 | File->Write(CString(iPid) + "\n"); | |
284 | File->Close(); | |
285 | bRet = true; | |
538d3ece | 286 | } |
287 | ||
e2b91a32 | 288 | delete File; |
289 | CUtils::PrintStatus(bRet); | |
290 | return bRet; | |
538d3ece | 291 | } |
292 | ||
9a1c3874 | 293 | bool CZNC::DeletePidFile() { |
e2b91a32 | 294 | CFile* File = InitPidFile(); |
295 | if (File == NULL) | |
296 | return false; | |
9a1c3874 | 297 | |
e2b91a32 | 298 | CUtils::PrintAction("Deleting pid file [" + File->GetLongName() + "]"); |
299 | ||
d735e9d8 | 300 | bool bRet = File->Delete(); |
e2b91a32 | 301 | |
302 | delete File; | |
303 | CUtils::PrintStatus(bRet); | |
304 | return bRet; | |
9a1c3874 | 305 | } |
306 | ||
b03f495b | 307 | bool CZNC::WritePemFile() { |
8f0fc2fa | 308 | #ifndef HAVE_LIBSSL |
309 | CUtils::PrintError("ZNC was not compiled with ssl support."); | |
f74ab87e | 310 | return false; |
8f0fc2fa | 311 | #else |
bf171597 | 312 | CString sPemFile = GetPemLocation(); |
bf171597 | 313 | |
314 | CUtils::PrintAction("Writing Pem file [" + sPemFile + "]"); | |
315 | FILE *f = fopen(sPemFile.c_str(), "w"); | |
316 | ||
317 | if (!f) { | |
318 | CUtils::PrintStatus(false, "Unable to open"); | |
319 | return false; | |
320 | } | |
321 | ||
dcf357b1 | 322 | CUtils::GenerateCert(f, ""); |
bf171597 | 323 | fclose(f); |
324 | ||
325 | CUtils::PrintStatus(true); | |
326 | return true; | |
8f0fc2fa | 327 | #endif |
bf171597 | 328 | } |
329 | ||
538d3ece | 330 | void CZNC::DeleteUsers() { |
115b1708 | 331 | for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) { |
0dd41818 | 332 | a->second->SetBeingDeleted(true); |
538d3ece | 333 | delete a->second; |
334 | } | |
335 | ||
336 | m_msUsers.clear(); | |
c4891c5d | 337 | DisableConnectUser(); |
538d3ece | 338 | } |
339 | ||
86924339 | 340 | bool CZNC::IsHostAllowed(const CString& sHostMask) const { |
115b1708 | 341 | for (map<CString,CUser*>::const_iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) { |
538d3ece | 342 | if (a->second->IsHostAllowed(sHostMask)) { |
343 | return true; | |
344 | } | |
345 | } | |
346 | ||
347 | return false; | |
348 | } | |
349 | ||
b25e65db | 350 | bool CZNC::AllowConnectionFrom(const CString& sIP) const { |
16507001 | 351 | if (m_uiAnonIPLimit == 0) |
352 | return true; | |
36ffa163 | 353 | return (GetManager().GetAnonConnectionCount(sIP) < m_uiAnonIPLimit); |
b25e65db | 354 | } |
355 | ||
0a622749 | 356 | void CZNC::InitDirs(const CString& sArgvPath, const CString& sDataDir) { |
538d3ece | 357 | // If the bin was not ran from the current directory, we need to add that dir onto our cwd |
beb5b49b | 358 | CString::size_type uPos = sArgvPath.rfind('/'); |
8db106bf | 359 | if (uPos == CString::npos) |
360 | m_sCurPath = "./"; | |
361 | else | |
362 | m_sCurPath = CDir::ChangeDir("./", sArgvPath.Left(uPos), ""); | |
538d3ece | 363 | |
364 | // Try to set the user's home dir, default to binpath on failure | |
f9ffe6f4 | 365 | CFile::InitHomePath(m_sCurPath); |
538d3ece | 366 | |
0a622749 | 367 | if (sDataDir.empty()) { |
f9ffe6f4 | 368 | m_sZNCPath = CFile::GetHomePath() + "/.znc"; |
0a622749 | 369 | } else { |
3b8134c3 | 370 | m_sZNCPath = sDataDir; |
0a622749 | 371 | } |
7f53cc81 | 372 | |
f82f3cf0 | 373 | m_sSSLCertFile = m_sZNCPath + "/znc.pem"; |
4b1fbab6 | 374 | } |
375 | ||
f5c2528c | 376 | CString CZNC::GetConfPath(bool bAllowMkDir) const { |
fd5dd4c3 | 377 | CString sConfPath = m_sZNCPath + "/configs"; |
f5c2528c | 378 | if (bAllowMkDir && !CFile::Exists(sConfPath)) { |
804b259f | 379 | CDir::MakeDir(sConfPath); |
380 | } | |
381 | ||
382 | return sConfPath; | |
383 | } | |
384 | ||
4b1fbab6 | 385 | CString CZNC::GetUserPath() const { |
fd5dd4c3 | 386 | CString sUserPath = m_sZNCPath + "/users"; |
4b1fbab6 | 387 | if (!CFile::Exists(sUserPath)) { |
388 | CDir::MakeDir(sUserPath); | |
389 | } | |
390 | ||
391 | return sUserPath; | |
392 | } | |
393 | ||
394 | CString CZNC::GetModPath() const { | |
fd5dd4c3 | 395 | CString sModPath = m_sZNCPath + "/modules"; |
4b1fbab6 | 396 | |
397 | if (!CFile::Exists(sModPath)) { | |
398 | CDir::MakeDir(sModPath); | |
399 | } | |
933f1cc6 | 400 | |
4b1fbab6 | 401 | return sModPath; |
538d3ece | 402 | } |
403 | ||
8e59f751 US |
404 | const CString& CZNC::GetCurPath() const { |
405 | if (!CFile::Exists(m_sCurPath)) { | |
406 | CDir::MakeDir(m_sCurPath); | |
407 | } | |
408 | return m_sCurPath; | |
409 | } | |
410 | ||
411 | const CString& CZNC::GetHomePath() const { | |
412 | return CFile::GetHomePath(); | |
413 | } | |
414 | ||
415 | const CString& CZNC::GetZNCPath() const { | |
416 | if (!CFile::Exists(m_sZNCPath)) { | |
417 | CDir::MakeDir(m_sZNCPath); | |
418 | } | |
419 | return m_sZNCPath; | |
420 | } | |
421 | ||
422 | CString CZNC::GetPemLocation() const { | |
423 | return CDir::ChangeDir("", m_sSSLCertFile); | |
424 | } | |
0013ab2c | 425 | |
f5c2528c | 426 | CString CZNC::ExpandConfigPath(const CString& sConfigFile, bool bAllowMkDir) { |
beb5b49b | 427 | CString sRetPath; |
538d3ece | 428 | |
6bd03a84 | 429 | if (sConfigFile.empty()) { |
f5c2528c | 430 | sRetPath = GetConfPath(bAllowMkDir) + "/znc.conf"; |
6bd03a84 | 431 | } else { |
078bbcf0 | 432 | if (sConfigFile.Left(2) == "./" || sConfigFile.Left(3) == "../") { |
0013ab2c | 433 | sRetPath = GetCurPath() + "/" + sConfigFile; |
078bbcf0 | 434 | } else if (sConfigFile.Left(1) != "/") { |
f5c2528c | 435 | sRetPath = GetConfPath(bAllowMkDir) + "/" + sConfigFile; |
44e1416b | 436 | } else { |
437 | sRetPath = sConfigFile; | |
0013ab2c | 438 | } |
439 | } | |
440 | ||
441 | return sRetPath; | |
442 | } | |
443 | ||
1f090c4d | 444 | bool CZNC::WriteConfig() { |
eb0d9d43 | 445 | if (GetConfigFile().empty()) { |
446 | return false; | |
447 | } | |
448 | ||
e51189ea | 449 | // We first write to a temporary file and then move it to the right place |
a4d388b5 | 450 | CFile *pFile = new CFile(GetConfigFile() + "~"); |
1f090c4d | 451 | |
a4d388b5 US |
452 | if (!pFile->Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) { |
453 | delete pFile; | |
eb0d9d43 | 454 | return false; |
455 | } | |
456 | ||
457 | // We have to "transfer" our lock on the config to the new file. | |
458 | // The old file (= inode) is going away and thus a lock on it would be | |
459 | // useless. These lock should always succeed (races, anyone?). | |
a4d388b5 US |
460 | if (!pFile->TryExLock()) { |
461 | pFile->Delete(); | |
462 | delete pFile; | |
1f090c4d | 463 | return false; |
464 | } | |
465 | ||
a4d388b5 | 466 | pFile->Write(MakeConfigHeader() + "\n"); |
2472ea7a | 467 | |
a4d388b5 US |
468 | pFile->Write("AnonIPLimit = " + CString(m_uiAnonIPLimit) + "\n"); |
469 | pFile->Write("MaxBufferSize= " + CString(m_uiMaxBufferSize) + "\n"); | |
470 | pFile->Write("SSLCertFile = " + CString(m_sSSLCertFile) + "\n"); | |
a9ba4020 | 471 | pFile->Write("ProtectWebSessions = " + CString(m_bProtectWebSessions) + "\n"); |
16507001 | 472 | |
ca824705 | 473 | for (size_t l = 0; l < m_vpListeners.size(); l++) { |
474 | CListener* pListener = m_vpListeners[l]; | |
475 | CString sHostPortion = pListener->GetBindHost(); | |
ef9d9b21 | 476 | |
ca824705 | 477 | if (!sHostPortion.empty()) { |
3188856a | 478 | sHostPortion = sHostPortion.FirstLine() + " "; |
ca824705 | 479 | } |
480 | ||
6bc45bf8 | 481 | CString sAcceptProtocol; |
482 | if(pListener->GetAcceptType() == CListener::ACCEPT_IRC) | |
483 | sAcceptProtocol = "irc_only "; | |
484 | else if(pListener->GetAcceptType() == CListener::ACCEPT_HTTP) | |
485 | sAcceptProtocol = "web_only "; | |
486 | ||
6acaebf7 | 487 | CString s6; |
488 | switch (pListener->GetAddrType()) { | |
489 | case ADDR_IPV4ONLY: | |
490 | s6 = "4"; | |
491 | break; | |
492 | case ADDR_IPV6ONLY: | |
493 | s6 = "6"; | |
494 | break; | |
495 | case ADDR_ALL: | |
496 | s6 = " "; | |
497 | break; | |
498 | } | |
ca824705 | 499 | |
a4d388b5 | 500 | pFile->Write("Listener" + s6 + " = " + sAcceptProtocol + sHostPortion + |
6bc45bf8 | 501 | CString((pListener->IsSSL()) ? "+" : "") + CString(pListener->GetPort()) + "\n"); |
ca824705 | 502 | } |
ef9d9b21 | 503 | |
a4d388b5 US |
504 | pFile->Write("ConnectDelay = " + CString(m_uiConnectDelay) + "\n"); |
505 | pFile->Write("ServerThrottle = " + CString(m_sConnectThrottle.GetTTL()/1000) + "\n"); | |
1fa9187b | 506 | |
3188856a | 507 | if (!m_sPidFile.empty()) { |
a4d388b5 | 508 | pFile->Write("PidFile = " + m_sPidFile.FirstLine() + "\n"); |
3188856a | 509 | } |
ad92c58c | 510 | |
511 | if (!m_sSkinName.empty()) { | |
a4d388b5 | 512 | pFile->Write("Skin = " + m_sSkinName.FirstLine() + "\n"); |
ad92c58c | 513 | } |
514 | ||
3188856a | 515 | if (!m_sStatusPrefix.empty()) { |
a4d388b5 | 516 | pFile->Write("StatusPrefix = " + m_sStatusPrefix.FirstLine() + "\n"); |
3188856a | 517 | } |
1f090c4d | 518 | |
d2e72850 | 519 | for (unsigned int m = 0; m < m_vsMotd.size(); m++) { |
a4d388b5 | 520 | pFile->Write("Motd = " + m_vsMotd[m].FirstLine() + "\n"); |
d2e72850 | 521 | } |
522 | ||
341263f9 | 523 | for (unsigned int v = 0; v < m_vsBindHosts.size(); v++) { |
a4d388b5 | 524 | pFile->Write("BindHost = " + m_vsBindHosts[v].FirstLine() + "\n"); |
573c4be4 | 525 | } |
526 | ||
1f090c4d | 527 | CGlobalModules& Mods = GetModules(); |
528 | ||
529 | for (unsigned int a = 0; a < Mods.size(); a++) { | |
3188856a | 530 | CString sName = Mods[a]->GetModName(); |
1f090c4d | 531 | CString sArgs = Mods[a]->GetArgs(); |
532 | ||
533 | if (!sArgs.empty()) { | |
3188856a | 534 | sArgs = " " + sArgs.FirstLine(); |
1f090c4d | 535 | } |
536 | ||
a4d388b5 | 537 | pFile->Write("LoadModule = " + sName.FirstLine() + sArgs + "\n"); |
1f090c4d | 538 | } |
1f090c4d | 539 | |
647045b7 A |
540 | pFile->Write("forceserver = " + m_forceServer + "\n"); |
541 | pFile->Write("webircpassword = " + m_webircPassword + "\n"); | |
542 | ||
115b1708 | 543 | for (map<CString,CUser*>::iterator it = m_msUsers.begin(); it != m_msUsers.end(); ++it) { |
1f090c4d | 544 | CString sErr; |
545 | ||
546 | if (!it->second->IsValid(sErr)) { | |
235b10c2 | 547 | DEBUG("** Error writing config for user [" << it->first << "] [" << sErr << "]"); |
1f090c4d | 548 | continue; |
549 | } | |
550 | ||
a4d388b5 | 551 | pFile->Write("\n"); |
29e14b42 | 552 | |
a4d388b5 | 553 | if (!it->second->WriteConfig(*pFile)) { |
235b10c2 | 554 | DEBUG("** Error writing config for user [" << it->first << "]"); |
1f090c4d | 555 | } |
556 | } | |
557 | ||
eb0d9d43 | 558 | // If Sync() fails... well, let's hope nothing important breaks.. |
a4d388b5 | 559 | pFile->Sync(); |
1f090c4d | 560 | |
a4d388b5 | 561 | if (pFile->HadError()) { |
7f3e55e7 | 562 | DEBUG("Error while writing the config, errno says: " + CString(strerror(errno))); |
a4d388b5 US |
563 | pFile->Delete(); |
564 | delete pFile; | |
7f3e55e7 US |
565 | return false; |
566 | } | |
567 | ||
e51189ea | 568 | // We wrote to a temporary name, move it to the right place |
a4d388b5 US |
569 | if (!pFile->Move(GetConfigFile(), true)) { |
570 | DEBUG("Error while replacing the config file with a new version"); | |
571 | pFile->Delete(); | |
572 | delete pFile; | |
eb0d9d43 | 573 | return false; |
a4d388b5 | 574 | } |
eb0d9d43 | 575 | |
576 | // Everything went fine, just need to update the saved path. | |
a4d388b5 US |
577 | pFile->SetFileName(GetConfigFile()); |
578 | ||
579 | // Make sure the lock is kept alive as long as we need it. | |
580 | delete m_pLockFile; | |
581 | m_pLockFile = pFile; | |
e51189ea | 582 | |
1f090c4d | 583 | return true; |
584 | } | |
585 | ||
2472ea7a | 586 | CString CZNC::MakeConfigHeader() { |
587 | return | |
588 | "// WARNING\n" | |
589 | "//\n" | |
590 | "// Do NOT edit this file while ZNC is running!\n" | |
8cadb672 | 591 | "// Use webadmin or *admin instead.\n" |
f5fe9868 AS |
592 | "//\n" |
593 | "// Buf if you feel risky, you might want to read help on /znc saveconfig and /znc rehash.\n" | |
594 | "// Also check http://en.znc.in/wiki/Configuration\n"; | |
2472ea7a | 595 | } |
596 | ||
de2f07ec | 597 | bool CZNC::WriteNewConfig(const CString& sConfigFile) { |
beb5b49b | 598 | CString sAnswer, sUser; |
cc5c7716 | 599 | VCString vsLines; |
708366d9 | 600 | |
2472ea7a | 601 | vsLines.push_back(MakeConfigHeader()); |
0013ab2c | 602 | |
ef27fee3 | 603 | m_sConfigFile = ExpandConfigPath(sConfigFile); |
cc5c7716 | 604 | CUtils::PrintMessage("Building new config"); |
878c5d5a | 605 | |
0013ab2c | 606 | CUtils::PrintMessage(""); |
7bb4ed34 | 607 | CUtils::PrintMessage("First let's start with some global settings..."); |
0013ab2c | 608 | CUtils::PrintMessage(""); |
609 | ||
ef9d9b21 | 610 | // Listen |
79512603 AS |
611 | #ifdef HAVE_IPV6 |
612 | bool b6 = true; | |
613 | #else | |
614 | bool b6 = false; | |
615 | #endif | |
11ffe9db | 616 | CString sListenHost; |
617 | CString sSSL; | |
d0f33f8b | 618 | unsigned int uListenPort = 0; |
11ffe9db | 619 | bool bSuccess; |
620 | ||
621 | do { | |
622 | bSuccess = true; | |
63a5de13 | 623 | while (!CUtils::GetNumInput("What port would you like ZNC to listen on?", uListenPort, 1025, 65535)) ; |
ca824705 | 624 | |
0013ab2c | 625 | #ifdef HAVE_LIBSSL |
11ffe9db | 626 | if (CUtils::GetBoolInput("Would you like ZNC to listen using SSL?", !sSSL.empty())) { |
627 | sSSL = "+"; | |
628 | ||
629 | CString sPemFile = GetPemLocation(); | |
630 | if (!CFile::Exists(sPemFile)) { | |
631 | CUtils::PrintError("Unable to locate pem file: [" + sPemFile + "]"); | |
632 | if (CUtils::GetBoolInput("Would you like to create a new pem file now?", | |
633 | true)) { | |
634 | WritePemFile(); | |
635 | } | |
4bd90ab9 | 636 | } |
11ffe9db | 637 | } else |
638 | sSSL = ""; | |
ca824705 | 639 | #endif |
640 | ||
ca824705 | 641 | #ifdef HAVE_IPV6 |
79512603 | 642 | b6 = CUtils::GetBoolInput("Would you like ZNC to listen using ipv6?", b6); |
0013ab2c | 643 | #endif |
644 | ||
11ffe9db | 645 | CUtils::GetInput("Listen Host", sListenHost, sListenHost, "Blank for all ips"); |
646 | ||
647 | CUtils::PrintAction("Verifying the listener"); | |
648 | CListener* pListener = new CListener(uListenPort, sListenHost, !sSSL.empty(), | |
79512603 | 649 | b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL); |
11ffe9db | 650 | if (!pListener->Listen()) { |
651 | CUtils::PrintStatus(false, FormatBindError()); | |
652 | bSuccess = false; | |
653 | } else | |
654 | CUtils::PrintStatus(true); | |
655 | delete pListener; | |
656 | } while (!bSuccess); | |
ef9d9b21 | 657 | |
658 | if (!sListenHost.empty()) { | |
ca824705 | 659 | sListenHost += " "; |
ef9d9b21 | 660 | } |
661 | ||
79512603 | 662 | vsLines.push_back("Listener" + CString(b6 ? " " : "4") + " = " + sListenHost + sSSL + CString(uListenPort)); |
ef9d9b21 | 663 | // !Listen |
0013ab2c | 664 | |
0bbab8f4 | 665 | set<CModInfo> ssGlobalMods; |
89e5079c | 666 | GetModules().GetAvailableMods(ssGlobalMods, true); |
207478d3 | 667 | size_t uNrOtherGlobalMods = FilterUncommonModules(ssGlobalMods); |
0bbab8f4 | 668 | |
291ad479 | 669 | if (!ssGlobalMods.empty()) { |
0bbab8f4 | 670 | CUtils::PrintMessage(""); |
671 | CUtils::PrintMessage("-- Global Modules --"); | |
672 | CUtils::PrintMessage(""); | |
673 | ||
92304835 AS |
674 | CTable Table; |
675 | Table.AddColumn("Name"); | |
676 | Table.AddColumn("Description"); | |
677 | set<CModInfo>::iterator it; | |
678 | ||
679 | for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) { | |
680 | const CModInfo& Info = *it; | |
681 | Table.AddRow(); | |
682 | Table.SetCell("Name", Info.GetName()); | |
683 | Table.SetCell("Description", Info.GetDescription().Ellipsize(128)); | |
684 | } | |
1c90fd9b | 685 | |
92304835 AS |
686 | unsigned int uTableIdx = 0; CString sLine; |
687 | while (Table.GetLine(uTableIdx++, sLine)) { | |
688 | CUtils::PrintMessage(sLine); | |
689 | } | |
1c90fd9b | 690 | |
92304835 AS |
691 | if (uNrOtherGlobalMods > 0) { |
692 | CUtils::PrintMessage("And " + CString(uNrOtherGlobalMods) + " other (uncommon) modules. You can enable those later."); | |
693 | } | |
207478d3 | 694 | |
92304835 | 695 | CUtils::PrintMessage(""); |
1c90fd9b | 696 | |
92304835 AS |
697 | for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) { |
698 | const CModInfo& Info = *it; | |
699 | CString sName = Info.GetName(); | |
0bbab8f4 | 700 | |
ac5c021c | 701 | if (CDebug::StdoutIsTTY()) { |
92304835 AS |
702 | if (CUtils::GetBoolInput("Load global module <\033[1m" + sName + "\033[22m>?", false)) |
703 | vsLines.push_back("LoadModule = " + sName); | |
704 | } else { | |
705 | if (CUtils::GetBoolInput("Load global module <" + sName + ">?", false)) | |
706 | vsLines.push_back("LoadModule = " + sName); | |
0bbab8f4 | 707 | } |
708 | } | |
709 | } | |
0bbab8f4 | 710 | |
0013ab2c | 711 | // User |
712 | CUtils::PrintMessage(""); | |
7bb4ed34 | 713 | CUtils::PrintMessage("Now we need to set up a user..."); |
b3d6a86f | 714 | CUtils::PrintMessage("ZNC needs one user per IRC network."); |
0013ab2c | 715 | CUtils::PrintMessage(""); |
716 | ||
ef1c8de5 | 717 | bool bFirstUser = true; |
718 | ||
0013ab2c | 719 | do { |
720 | vsLines.push_back(""); | |
beb5b49b | 721 | CString sNick; |
ecd587c5 | 722 | do { |
723 | CUtils::GetInput("Username", sUser, "", "AlphaNumeric"); | |
724 | } while (!CUser::IsValidUserName(sUser)); | |
725 | ||
726 | vsLines.push_back("<User " + sUser + ">"); | |
bf2bd397 | 727 | CString sSalt; |
728 | sAnswer = CUtils::GetSaltedHashPass(sSalt); | |
cd63bae0 | 729 | vsLines.push_back("\tPass = " + CUtils::sDefaultHash + "#" + sAnswer + "#" + sSalt + "#"); |
ef1c8de5 | 730 | |
731 | if (CUtils::GetBoolInput("Would you like this user to be an admin?", bFirstUser)) { | |
a3c71606 | 732 | vsLines.push_back("\tAdmin = true"); |
ef1c8de5 | 733 | } else { |
a3c71606 | 734 | vsLines.push_back("\tAdmin = false"); |
ef1c8de5 | 735 | } |
736 | ||
47c0cb9b | 737 | CUtils::GetInput("Nick", sNick, CUser::MakeCleanUserName(sUser)); |
738 | vsLines.push_back("\tNick = " + sNick); | |
739 | CUtils::GetInput("Alt Nick", sAnswer, sNick + "_"); | |
740 | if (!sAnswer.empty()) { | |
741 | vsLines.push_back("\tAltNick = " + sAnswer); | |
742 | } | |
743 | CUtils::GetInput("Ident", sAnswer, sNick); | |
744 | vsLines.push_back("\tIdent = " + sAnswer); | |
745 | CUtils::GetInput("Real Name", sAnswer, "Got ZNC?"); | |
746 | vsLines.push_back("\tRealName = " + sAnswer); | |
341263f9 | 747 | CUtils::GetInput("Bind Host", sAnswer, "", "optional"); |
47c0cb9b | 748 | if (!sAnswer.empty()) { |
341263f9 | 749 | vsLines.push_back("\tBindHost = " + sAnswer); |
47c0cb9b | 750 | } |
d2e72850 | 751 | // todo: Possibly add motd |
0013ab2c | 752 | |
04e1d6aa | 753 | unsigned int uBufferCount = 0; |
754 | ||
47c0cb9b | 755 | CUtils::GetNumInput("Number of lines to buffer per channel", uBufferCount, 0, ~0, 50); |
756 | if (uBufferCount) { | |
757 | vsLines.push_back("\tBuffer = " + CString(uBufferCount)); | |
758 | } | |
b475f5d6 | 759 | if (CUtils::GetBoolInput("Would you like to keep buffers after replay?", false)) { |
0013ab2c | 760 | vsLines.push_back("\tKeepBuffer = true"); |
761 | } else { | |
762 | vsLines.push_back("\tKeepBuffer = false"); | |
763 | } | |
764 | ||
765 | CUtils::GetInput("Default channel modes", sAnswer, "+stn"); | |
766 | if (!sAnswer.empty()) { | |
767 | vsLines.push_back("\tChanModes = " + sAnswer); | |
768 | } | |
769 | ||
0bbab8f4 | 770 | set<CModInfo> ssUserMods; |
89e5079c | 771 | GetModules().GetAvailableMods(ssUserMods); |
207478d3 | 772 | size_t uNrOtherUserMods = FilterUncommonModules(ssUserMods); |
e50580ab | 773 | |
0bbab8f4 | 774 | if (ssUserMods.size()) { |
e50580ab | 775 | vsLines.push_back(""); |
776 | CUtils::PrintMessage(""); | |
0bbab8f4 | 777 | CUtils::PrintMessage("-- User Modules --"); |
e50580ab | 778 | CUtils::PrintMessage(""); |
779 | ||
92304835 AS |
780 | CTable Table; |
781 | Table.AddColumn("Name"); | |
782 | Table.AddColumn("Description"); | |
783 | set<CModInfo>::iterator it; | |
1c90fd9b | 784 | |
92304835 AS |
785 | for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) { |
786 | const CModInfo& Info = *it; | |
787 | Table.AddRow(); | |
788 | Table.SetCell("Name", Info.GetName()); | |
789 | Table.SetCell("Description", Info.GetDescription().Ellipsize(128)); | |
790 | } | |
1c90fd9b | 791 | |
92304835 AS |
792 | unsigned int uTableIdx = 0; CString sLine; |
793 | while (Table.GetLine(uTableIdx++, sLine)) { | |
794 | CUtils::PrintMessage(sLine); | |
795 | } | |
796 | ||
797 | if (uNrOtherUserMods > 0) { | |
798 | CUtils::PrintMessage("And " + CString(uNrOtherUserMods) + " other (uncommon) modules. You can enable those later."); | |
799 | } | |
207478d3 | 800 | |
92304835 | 801 | CUtils::PrintMessage(""); |
1c90fd9b | 802 | |
92304835 AS |
803 | for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) { |
804 | const CModInfo& Info = *it; | |
805 | CString sName = Info.GetName(); | |
279279fe | 806 | |
ac5c021c | 807 | if (CDebug::StdoutIsTTY()) { |
92304835 AS |
808 | if (CUtils::GetBoolInput("Load module <\033[1m" + sName + "\033[22m>?", false)) |
809 | vsLines.push_back("\tLoadModule = " + sName); | |
810 | } else { | |
811 | if (CUtils::GetBoolInput("Load module <" + sName + ">?", false)) | |
812 | vsLines.push_back("\tLoadModule = " + sName); | |
e50580ab | 813 | } |
814 | } | |
815 | } | |
816 | ||
0013ab2c | 817 | vsLines.push_back(""); |
818 | CUtils::PrintMessage(""); | |
e50580ab | 819 | CUtils::PrintMessage("-- IRC Servers --"); |
35ae34fa | 820 | CUtils::PrintMessage("Only add servers from the same IRC network."); |
7bb4ed34 | 821 | CUtils::PrintMessage("If a server from the list can't be reached, another server will be used."); |
0013ab2c | 822 | CUtils::PrintMessage(""); |
823 | ||
824 | do { | |
beb5b49b | 825 | CString sHost, sPass; |
0013ab2c | 826 | bool bSSL = false; |
d0f33f8b | 827 | unsigned int uServerPort = 0; |
04e1d6aa | 828 | |
c64d7bc1 | 829 | while (!CUtils::GetInput("IRC server", sHost, "", "host only") || !CServer::IsValidHostName(sHost)) ; |
d0f33f8b | 830 | while (!CUtils::GetNumInput("[" + sHost + "] Port", uServerPort, 1, 65535, 6667)) ; |
0013ab2c | 831 | CUtils::GetInput("[" + sHost + "] Password (probably empty)", sPass); |
832 | ||
833 | #ifdef HAVE_LIBSSL | |
b3d6a86f | 834 | bSSL = CUtils::GetBoolInput("Does this server use SSL?", false); |
0013ab2c | 835 | #endif |
836 | ||
d0f33f8b | 837 | vsLines.push_back("\tServer = " + sHost + ((bSSL) ? " +" : " ") + CString(uServerPort) + " " + sPass); |
cc5c7716 | 838 | |
839 | CUtils::PrintMessage(""); | |
b3d6a86f | 840 | } while (CUtils::GetBoolInput("Would you like to add another server for this IRC network?", false)); |
0013ab2c | 841 | |
842 | vsLines.push_back(""); | |
843 | CUtils::PrintMessage(""); | |
e50580ab | 844 | CUtils::PrintMessage("-- Channels --"); |
0013ab2c | 845 | CUtils::PrintMessage(""); |
846 | ||
beb5b49b | 847 | CString sArg = "a"; |
848 | CString sPost = " for ZNC to automatically join?"; | |
ecd587c5 | 849 | bool bDefault = true; |
0013ab2c | 850 | |
ecd587c5 | 851 | while (CUtils::GetBoolInput("Would you like to add " + sArg + " channel" + sPost, bDefault)) { |
b490b120 | 852 | while (!CUtils::GetInput("Channel name", sAnswer)) ; |
0013ab2c | 853 | vsLines.push_back("\t<Chan " + sAnswer + ">"); |
854 | vsLines.push_back("\t</Chan>"); | |
855 | sArg = "another"; | |
04e1d6aa | 856 | sPost = "?"; |
ecd587c5 | 857 | bDefault = false; |
6cb31706 | 858 | } |
0013ab2c | 859 | |
860 | vsLines.push_back("</User>"); | |
861 | ||
862 | CUtils::PrintMessage(""); | |
ef1c8de5 | 863 | bFirstUser = false; |
7bb4ed34 | 864 | } while (CUtils::GetBoolInput("Would you like to set up another user (e.g. for connecting to another network)?", false)); |
0013ab2c | 865 | // !User |
866 | ||
cc5c7716 | 867 | CFile File; |
868 | bool bFileOK, bFileOpen = false; | |
869 | do { | |
de2f07ec | 870 | CUtils::PrintAction("Writing config [" + m_sConfigFile + "]"); |
878c5d5a | 871 | |
cc5c7716 | 872 | bFileOK = true; |
de2f07ec | 873 | if (CFile::Exists(m_sConfigFile)) { |
a4d388b5 | 874 | if (!File.TryExLock(m_sConfigFile)) { |
cc5c7716 | 875 | CUtils::PrintStatus(false, "ZNC is currently running on this config."); |
876 | bFileOK = false; | |
877 | } else { | |
a4d388b5 | 878 | File.Close(); |
cc5c7716 | 879 | CUtils::PrintStatus(false, "This config already exists."); |
880 | if (CUtils::GetBoolInput("Would you like to overwrite it?", false)) | |
de2f07ec | 881 | CUtils::PrintAction("Overwriting config [" + m_sConfigFile + "]"); |
cc5c7716 | 882 | else |
883 | bFileOK = false; | |
884 | } | |
885 | } | |
878c5d5a | 886 | |
cc5c7716 | 887 | if (bFileOK) { |
de2f07ec | 888 | File.SetFileName(m_sConfigFile); |
80a84ba4 | 889 | if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) { |
878c5d5a | 890 | bFileOpen = true; |
891 | } else { | |
cc5c7716 | 892 | CUtils::PrintStatus(false, "Unable to open file"); |
893 | bFileOK = false; | |
878c5d5a | 894 | } |
895 | } | |
cc5c7716 | 896 | if (!bFileOK) { |
de2f07ec | 897 | CUtils::GetInput("Please specify an alternate location (or \"stdout\" for displaying the config)", m_sConfigFile, m_sConfigFile); |
898 | if (m_sConfigFile.Equals("stdout")) | |
cc5c7716 | 899 | bFileOK = true; |
900 | else | |
de2f07ec | 901 | m_sConfigFile = ExpandConfigPath(m_sConfigFile); |
cc5c7716 | 902 | } |
903 | } while (!bFileOK); | |
878c5d5a | 904 | |
905 | if (!bFileOpen) { | |
906 | CUtils::PrintMessage(""); | |
cc5c7716 | 907 | CUtils::PrintMessage("Printing the new config to stdout:"); |
878c5d5a | 908 | CUtils::PrintMessage(""); |
909 | cout << endl << "----------------------------------------------------------------------------" << endl << endl; | |
0013ab2c | 910 | } |
911 | ||
912 | for (unsigned int a = 0; a < vsLines.size(); a++) { | |
878c5d5a | 913 | if (bFileOpen) { |
914 | File.Write(vsLines[a] + "\n"); | |
915 | } else { | |
916 | cout << vsLines[a] << endl; | |
917 | } | |
538d3ece | 918 | } |
919 | ||
878c5d5a | 920 | if (bFileOpen) { |
921 | File.Close(); | |
bff969f4 US |
922 | if (File.HadError()) |
923 | CUtils::PrintStatus(false, "There was an error while writing the config"); | |
924 | else | |
925 | CUtils::PrintStatus(true); | |
878c5d5a | 926 | } else { |
927 | cout << endl << "----------------------------------------------------------------------------" << endl << endl; | |
928 | } | |
0d0c5462 | 929 | |
bff969f4 US |
930 | if (File.HadError()) { |
931 | bFileOpen = false; | |
932 | CUtils::PrintMessage("Printing the new config to stdout instead:"); | |
933 | cout << endl << "----------------------------------------------------------------------------" << endl << endl; | |
934 | for (unsigned int a = 0; a < vsLines.size(); a++) { | |
935 | cout << vsLines[a] << endl; | |
936 | } | |
937 | cout << endl << "----------------------------------------------------------------------------" << endl << endl; | |
938 | } | |
939 | ||
16be1817 | 940 | const CString sProtocol(sSSL.empty() ? "http" : "https"); |
0d0c5462 | 941 | CUtils::PrintMessage(""); |
7bb4ed34 | 942 | CUtils::PrintMessage("To connect to this ZNC you need to connect to it as your IRC server", true); |
0d0c5462 | 943 | CUtils::PrintMessage("using the port that you supplied. You have to supply your login info", true); |
7bb4ed34 | 944 | CUtils::PrintMessage("as the IRC server password like this: user:pass.", true); |
0d0c5462 | 945 | CUtils::PrintMessage(""); |
946 | CUtils::PrintMessage("Try something like this in your IRC client...", true); | |
16be1817 | 947 | CUtils::PrintMessage("/server <znc_server_ip> " + sSSL + CString(uListenPort) + " " + sUser + ":<pass>", true); |
948 | CUtils::PrintMessage("And this in your browser...", true); | |
949 | CUtils::PrintMessage(sProtocol + "://<znc_server_ip>:" + CString(uListenPort) + "/", true); | |
0d0c5462 | 950 | CUtils::PrintMessage(""); |
951 | ||
a4d388b5 | 952 | File.UnLock(); |
7bb4ed34 | 953 | return bFileOpen && CUtils::GetBoolInput("Launch ZNC now?", true); |
0013ab2c | 954 | } |
955 | ||
207478d3 | 956 | size_t CZNC::FilterUncommonModules(set<CModInfo>& ssModules) { |
957 | const char* ns[] = { "webadmin", "admin", | |
958 | "chansaver", "keepnick", "simple_away", "partyline", | |
959 | "kickrejoin", "nickserv", "perform" }; | |
960 | const set<CString> ssNames(ns, ns + sizeof(ns) / sizeof(ns[0])); | |
961 | ||
962 | size_t uNrRemoved = 0; | |
963 | for(set<CModInfo>::iterator it = ssModules.begin(); it != ssModules.end(); ) { | |
964 | if(ssNames.count(it->GetName()) > 0) { | |
965 | it++; | |
966 | } else { | |
967 | set<CModInfo>::iterator it2 = it++; | |
968 | ssModules.erase(it2); | |
969 | uNrRemoved++; | |
970 | } | |
971 | } | |
972 | ||
973 | return uNrRemoved; | |
974 | } | |
207478d3 | 975 | |
8e33a737 | 976 | bool CZNC::ParseConfig(const CString& sConfig) |
977 | { | |
978 | CString s; | |
979 | ||
f5c2528c | 980 | m_sConfigFile = ExpandConfigPath(sConfig, false); |
0013ab2c | 981 | |
8e33a737 | 982 | return DoRehash(s); |
983 | } | |
984 | ||
985 | bool CZNC::RehashConfig(CString& sError) | |
986 | { | |
1023d868 | 987 | ALLMODULECALL(OnPreRehash(), NOTHING); |
dde7921e | 988 | |
8e33a737 | 989 | // This clears m_msDelUsers |
990 | HandleUserDeletion(); | |
991 | ||
992 | // Mark all users as going-to-be deleted | |
993 | m_msDelUsers = m_msUsers; | |
994 | m_msUsers.clear(); | |
995 | ||
996 | if (DoRehash(sError)) { | |
1023d868 | 997 | ALLMODULECALL(OnPostRehash(), NOTHING); |
cb066e24 | 998 | |
8e33a737 | 999 | return true; |
1000 | } | |
1001 | ||
1002 | // Rehashing failed, try to recover | |
1003 | CString s; | |
291ad479 | 1004 | while (!m_msDelUsers.empty()) { |
8e33a737 | 1005 | AddUser(m_msDelUsers.begin()->second, s); |
1006 | m_msDelUsers.erase(m_msDelUsers.begin()); | |
1007 | } | |
1008 | ||
1009 | return false; | |
1010 | } | |
1011 | ||
1012 | bool CZNC::DoRehash(CString& sError) | |
1013 | { | |
1014 | sError.clear(); | |
1015 | ||
1f090c4d | 1016 | CUtils::PrintAction("Opening Config [" + m_sConfigFile + "]"); |
a9d08422 | 1017 | |
1f090c4d | 1018 | if (!CFile::Exists(m_sConfigFile)) { |
8e33a737 | 1019 | sError = "No such file"; |
1020 | CUtils::PrintStatus(false, sError); | |
7bb4ed34 | 1021 | CUtils::PrintMessage("Restart ZNC with the --makeconf option if you wish to create this config."); |
878c5d5a | 1022 | return false; |
7e7bbfad | 1023 | } |
1024 | ||
1f090c4d | 1025 | if (!CFile::IsReg(m_sConfigFile)) { |
8e33a737 | 1026 | sError = "Not a file"; |
1027 | CUtils::PrintStatus(false, sError); | |
7e7bbfad | 1028 | return false; |
1029 | } | |
1030 | ||
a4d388b5 | 1031 | CFile *pFile = new CFile(m_sConfigFile); |
12734782 | 1032 | |
33ce80f4 | 1033 | // need to open the config file Read/Write for fcntl() |
1034 | // exclusive locking to work properly! | |
a4d388b5 | 1035 | if (!pFile->Open(m_sConfigFile, O_RDWR)) { |
fe361f7a | 1036 | sError = "Can not open config file"; |
1037 | CUtils::PrintStatus(false, sError); | |
a4d388b5 | 1038 | delete pFile; |
fe361f7a | 1039 | return false; |
1040 | } | |
1041 | ||
a4d388b5 | 1042 | if (!pFile->TryExLock()) { |
8e33a737 | 1043 | sError = "ZNC is already running on this config."; |
1044 | CUtils::PrintStatus(false, sError); | |
a4d388b5 | 1045 | delete pFile; |
f32fb1b3 | 1046 | return false; |
1047 | } | |
1048 | ||
a4d388b5 US |
1049 | // (re)open the config file |
1050 | delete m_pLockFile; | |
1051 | m_pLockFile = pFile; | |
1052 | CFile &File = *pFile; | |
538d3ece | 1053 | |
70c77458 US |
1054 | CConfig config; |
1055 | if (!config.Parse(File, sError)) { | |
52f755f0 | 1056 | CUtils::PrintStatus(false, sError); |
538d3ece | 1057 | return false; |
1058 | } | |
a9d08422 | 1059 | CUtils::PrintStatus(true); |
1060 | ||
341263f9 | 1061 | m_vsBindHosts.clear(); |
8e33a737 | 1062 | m_vsMotd.clear(); |
1063 | ||
1064 | // Delete all listeners | |
291ad479 | 1065 | while (!m_vpListeners.empty()) { |
8e33a737 | 1066 | delete m_vpListeners[0]; |
1067 | m_vpListeners.erase(m_vpListeners.begin()); | |
1068 | } | |
1069 | ||
99f1efc8 | 1070 | MCString msModules; // Modules are queued for later loading |
538d3ece | 1071 | |
70c77458 | 1072 | VCString vsList; |
70c77458 US |
1073 | VCString::const_iterator vit; |
1074 | config.FindStringVector("loadmodule", vsList); | |
1075 | for (vit = vsList.begin(); vit != vsList.end(); ++vit) { | |
1076 | CString sModName = vit->Token(0); | |
1077 | CString sArgs = vit->Token(1, true); | |
538d3ece | 1078 | |
70c77458 US |
1079 | if (msModules.find(sModName) != msModules.end()) { |
1080 | sError = "Module [" + sModName + | |
1081 | "] already loaded"; | |
1082 | CUtils::PrintError(sError); | |
1083 | return false; | |
538d3ece | 1084 | } |
70c77458 US |
1085 | CString sModRet; |
1086 | CModule *pOldMod; | |
538d3ece | 1087 | |
70c77458 US |
1088 | pOldMod = GetModules().FindModule(sModName); |
1089 | if (!pOldMod) { | |
1090 | CUtils::PrintAction("Loading Global Module [" + sModName + "]"); | |
538d3ece | 1091 | |
70c77458 | 1092 | bool bModRet = GetModules().LoadModule(sModName, sArgs, NULL, sModRet); |
67f4a73f | 1093 | |
70c77458 US |
1094 | CUtils::PrintStatus(bModRet, sModRet); |
1095 | if (!bModRet) { | |
1096 | sError = sModRet; | |
1097 | return false; | |
1098 | } | |
1099 | } else if (pOldMod->GetArgs() != sArgs) { | |
1100 | CUtils::PrintAction("Reloading Global Module [" + sModName + "]"); | |
8e33a737 | 1101 | |
70c77458 | 1102 | bool bModRet = GetModules().ReloadModule(sModName, sArgs, NULL, sModRet); |
8e33a737 | 1103 | |
70c77458 US |
1104 | CUtils::PrintStatus(bModRet, sModRet); |
1105 | if (!bModRet) { | |
1106 | sError = sModRet; | |
1107 | return false; | |
538d3ece | 1108 | } |
70c77458 US |
1109 | } else |
1110 | CUtils::PrintMessage("Module [" + sModName + "] already loaded."); | |
1111 | ||
1112 | msModules[sModName] = sArgs; | |
1113 | } | |
1114 | ||
90fb9e8c US |
1115 | CString sISpoofFormat, sISpoofFile; |
1116 | config.FindStringEntry("ispoofformat", sISpoofFormat); | |
1117 | config.FindStringEntry("ispooffile", sISpoofFile); | |
1118 | if (!sISpoofFormat.empty() || !sISpoofFile.empty()) { | |
1119 | CModule *pIdentFileMod = GetModules().FindModule("identfile"); | |
1120 | if (!pIdentFileMod) { | |
1121 | CUtils::PrintAction("Loading Global Module [identfile]"); | |
1122 | ||
1123 | CString sModRet; | |
1124 | bool bModRet = GetModules().LoadModule("identfile", "", NULL, sModRet); | |
1125 | ||
90fb9e8c US |
1126 | CUtils::PrintStatus(bModRet, sModRet); |
1127 | if (!bModRet) { | |
1128 | sError = sModRet; | |
1129 | return false; | |
1130 | } | |
1131 | ||
1132 | pIdentFileMod = GetModules().FindModule("identfile"); | |
1133 | msModules["identfile"] = ""; | |
1134 | } | |
1135 | ||
1136 | pIdentFileMod->SetNV("File", sISpoofFile); | |
1137 | pIdentFileMod->SetNV("Format", sISpoofFormat); | |
1138 | } | |
1139 | ||
70c77458 US |
1140 | config.FindStringVector("motd", vsList); |
1141 | for (vit = vsList.begin(); vit != vsList.end(); ++vit) { | |
1142 | AddMotd(*vit); | |
1143 | } | |
1144 | ||
1145 | config.FindStringVector("bindhost", vsList); | |
1146 | for (vit = vsList.begin(); vit != vsList.end(); ++vit) { | |
1147 | AddBindHost(*vit); | |
1148 | } | |
1149 | config.FindStringVector("vhost", vsList); | |
1150 | for (vit = vsList.begin(); vit != vsList.end(); ++vit) { | |
1151 | AddBindHost(*vit); | |
1152 | } | |
1153 | ||
1154 | CString sVal; | |
1155 | if (config.FindStringEntry("pidfile", sVal)) | |
1156 | m_sPidFile = sVal; | |
1157 | if (config.FindStringEntry("statusprefix", sVal)) | |
1158 | m_sStatusPrefix = sVal; | |
1159 | if (config.FindStringEntry("sslcertfile", sVal)) | |
1160 | m_sSSLCertFile = sVal; | |
1161 | if (config.FindStringEntry("skin", sVal)) | |
1162 | SetSkinName(sVal); | |
1163 | if (config.FindStringEntry("connectdelay", sVal)) | |
1164 | m_uiConnectDelay = sVal.ToUInt(); | |
1165 | if (config.FindStringEntry("serverthrottle", sVal)) | |
1166 | m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000); | |
1167 | if (config.FindStringEntry("anoniplimit", sVal)) | |
1168 | m_uiAnonIPLimit = sVal.ToUInt(); | |
1169 | if (config.FindStringEntry("maxbuffersize", sVal)) | |
1170 | m_uiMaxBufferSize = sVal.ToUInt(); | |
a9ba4020 ME |
1171 | if (config.FindStringEntry("protectwebsessions", sVal)) |
1172 | m_bProtectWebSessions = sVal.ToBool(); | |
a0cc3de4 A |
1173 | if (config.FindStringEntry("webircpassword", sVal)) |
1174 | m_webircPassword = sVal; | |
6cabf380 A |
1175 | if (config.FindStringEntry("forceserver", sVal)) |
1176 | m_forceServer = sVal; | |
70c77458 | 1177 | |
3fa04f8c US |
1178 | // This has to be after SSLCertFile is handled since it uses that value |
1179 | const char *szListenerEntries[] = { | |
1180 | "listen", "listen6", "listen4", | |
1181 | "listener", "listener6", "listener4" | |
1182 | }; | |
1183 | const size_t numListenerEntries = sizeof(szListenerEntries) / sizeof(szListenerEntries[0]); | |
1184 | ||
1185 | for (size_t i = 0; i < numListenerEntries; i++) { | |
1186 | config.FindStringVector(szListenerEntries[i], vsList); | |
1187 | vit = vsList.begin(); | |
1188 | ||
1189 | for (; vit != vsList.end(); ++vit) { | |
1190 | if (!AddListener(szListenerEntries[i] + CString(" ") + *vit, sError)) | |
1191 | return false; | |
1192 | } | |
1193 | } | |
1194 | ||
70c77458 US |
1195 | CConfig::SubConfig subConf; |
1196 | CConfig::SubConfig::const_iterator subIt; | |
1197 | config.FindSubConfig("user", subConf); | |
1198 | for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { | |
1199 | const CString& sUserName = subIt->first; | |
1200 | CConfig* pSubConf = subIt->second.m_pSubConfig; | |
1201 | CUser* pRealUser = NULL; | |
1202 | ||
1203 | CUtils::PrintMessage("Loading user [" + sUserName + "]"); | |
1204 | ||
1205 | // Either create a CUser* or use an existing one | |
1206 | map<CString, CUser*>::iterator it = m_msDelUsers.find(sUserName); | |
1207 | ||
1208 | if (it != m_msDelUsers.end()) { | |
1209 | pRealUser = it->second; | |
1210 | m_msDelUsers.erase(it); | |
538d3ece | 1211 | } |
1212 | ||
70c77458 | 1213 | CUser* pUser = new CUser(sUserName); |
a3ef9b2d | 1214 | |
70c77458 US |
1215 | if (!m_sStatusPrefix.empty()) { |
1216 | if (!pUser->SetStatusPrefix(m_sStatusPrefix)) { | |
1217 | sError = "Invalid StatusPrefix [" + m_sStatusPrefix + "] Must be 1-5 chars, no spaces."; | |
1218 | CUtils::PrintError(sError); | |
1219 | return false; | |
1220 | } | |
1221 | } | |
a3ef9b2d | 1222 | |
70c77458 US |
1223 | if (!pUser->ParseConfig(pSubConf, sError)) { |
1224 | CUtils::PrintError(sError); | |
1225 | delete pUser; | |
1226 | pUser = NULL; | |
1227 | return false; | |
1228 | } | |
538d3ece | 1229 | |
70c77458 US |
1230 | if (!pSubConf->empty()) { |
1231 | sError = "Unhandled lines in config for User [" + sUserName + "]!"; | |
1232 | CUtils::PrintError(sError); | |
ca824705 | 1233 | |
70c77458 US |
1234 | DumpConfig(pSubConf); |
1235 | return false; | |
1236 | } | |
538d3ece | 1237 | |
70c77458 US |
1238 | CString sErr; |
1239 | if (pRealUser) { | |
1240 | if (!pRealUser->Clone(*pUser, sErr) | |
1241 | || !AddUser(pRealUser, sErr)) { | |
1242 | sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr; | |
1243 | DEBUG("CUser::Clone() failed in rehash"); | |
538d3ece | 1244 | } |
70c77458 US |
1245 | pUser->SetBeingDeleted(true); |
1246 | delete pUser; | |
1247 | pUser = NULL; | |
1248 | } else if (!AddUser(pUser, sErr)) { | |
1249 | sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr; | |
538d3ece | 1250 | } |
1251 | ||
70c77458 | 1252 | if (!sError.empty()) { |
322483ab | 1253 | CUtils::PrintError(sError); |
70c77458 US |
1254 | if (pUser) { |
1255 | pUser->SetBeingDeleted(true); | |
1256 | delete pUser; | |
1257 | pUser = NULL; | |
1258 | } | |
322483ab | 1259 | return false; |
1260 | } | |
70c77458 US |
1261 | |
1262 | pUser = NULL; | |
1263 | pRealUser = NULL; | |
538d3ece | 1264 | } |
1265 | ||
70c77458 US |
1266 | if (!config.empty()) { |
1267 | sError = "Unhandled lines in config!"; | |
1268 | CUtils::PrintError(sError); | |
1269 | ||
1270 | DumpConfig(&config); | |
1271 | return false; | |
1272 | } | |
1273 | ||
1274 | ||
9109df92 | 1275 | // Unload modules which are no longer in the config |
8e33a737 | 1276 | set<CString> ssUnload; |
1277 | for (size_t i = 0; i < GetModules().size(); i++) { | |
1278 | CModule *pCurMod = GetModules()[i]; | |
1279 | ||
1280 | if (msModules.find(pCurMod->GetModName()) == msModules.end()) | |
1281 | ssUnload.insert(pCurMod->GetModName()); | |
1282 | } | |
1283 | ||
115b1708 | 1284 | for (set<CString>::iterator it = ssUnload.begin(); it != ssUnload.end(); ++it) { |
8e33a737 | 1285 | if (GetModules().UnloadModule(*it)) |
1286 | CUtils::PrintMessage("Unloaded Global Module [" + *it + "]"); | |
1287 | else | |
1288 | CUtils::PrintMessage("Could not unload [" + *it + "]"); | |
1289 | } | |
df1c36ca | 1290 | |
291ad479 | 1291 | if (m_msUsers.empty()) { |
8e33a737 | 1292 | sError = "You must define at least one user in your config."; |
1293 | CUtils::PrintError(sError); | |
538d3ece | 1294 | return false; |
1295 | } | |
1296 | ||
291ad479 | 1297 | if (m_vpListeners.empty()) { |
8e33a737 | 1298 | sError = "You must supply at least one Listen port in your config."; |
1299 | CUtils::PrintError(sError); | |
077b25e7 | 1300 | return false; |
1301 | } | |
1302 | ||
d08829ee | 1303 | // Make sure that users that want to connect do so and also make sure a |
1304 | // new ConnectDelay setting is applied. | |
1305 | DisableConnectUser(); | |
abeba8ae | 1306 | EnableConnectUser(); |
c20f8427 | 1307 | |
538d3ece | 1308 | return true; |
1309 | } | |
3dde793e | 1310 | |
70c77458 US |
1311 | void CZNC::DumpConfig(const CConfig* pConfig) { |
1312 | CConfig::EntryMapIterator eit = pConfig->BeginEntries(); | |
1313 | for (; eit != pConfig->EndEntries(); ++eit) { | |
1314 | const CString& sKey = eit->first; | |
1315 | const VCString& vsList = eit->second; | |
1316 | VCString::const_iterator it = vsList.begin(); | |
1317 | for (; it != vsList.end(); ++it) { | |
1318 | CUtils::PrintError(sKey + " = " + *it); | |
1319 | } | |
1320 | } | |
1321 | ||
1322 | CConfig::SubConfigMapIterator sit = pConfig->BeginSubConfigs(); | |
1323 | for (; sit != pConfig->EndSubConfigs(); ++sit) { | |
1324 | const CString& sKey = sit->first; | |
1325 | const CConfig::SubConfig& sSub = sit->second; | |
1326 | CConfig::SubConfig::const_iterator it = sSub.begin(); | |
1327 | ||
1328 | for (; it != sSub.end(); ++it) { | |
1329 | CUtils::PrintError("SubConfig [" + sKey + " " + it->first + "]:"); | |
1330 | DumpConfig(it->second.m_pSubConfig); | |
1331 | } | |
1332 | } | |
1333 | } | |
1334 | ||
341263f9 | 1335 | void CZNC::ClearBindHosts() { |
1336 | m_vsBindHosts.clear(); | |
7cb1d2f9 | 1337 | } |
1338 | ||
341263f9 | 1339 | bool CZNC::AddBindHost(const CString& sHost) { |
7cb1d2f9 | 1340 | if (sHost.empty()) { |
1341 | return false; | |
1342 | } | |
1343 | ||
341263f9 | 1344 | for (unsigned int a = 0; a < m_vsBindHosts.size(); a++) { |
1345 | if (m_vsBindHosts[a].Equals(sHost)) { | |
573c4be4 | 1346 | return false; |
1347 | } | |
1348 | } | |
1349 | ||
341263f9 | 1350 | m_vsBindHosts.push_back(sHost); |
573c4be4 | 1351 | return true; |
1352 | } | |
1353 | ||
341263f9 | 1354 | bool CZNC::RemBindHost(const CString& sHost) { |
ce31b29b | 1355 | VCString::iterator it; |
341263f9 | 1356 | for (it = m_vsBindHosts.begin(); it != m_vsBindHosts.end(); ++it) { |
ce31b29b | 1357 | if (sHost.Equals(*it)) { |
341263f9 | 1358 | m_vsBindHosts.erase(it); |
ce31b29b | 1359 | return true; |
1360 | } | |
1361 | } | |
1362 | ||
1363 | return false; | |
573c4be4 | 1364 | } |
1365 | ||
a773c13f | 1366 | void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly, |
1367 | CUser* pSkipUser, CClient *pSkipClient) { | |
115b1708 | 1368 | for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) { |
a773c13f | 1369 | if (bAdminOnly && !a->second->IsAdmin()) |
1370 | continue; | |
1371 | ||
1372 | if (a->second != pSkipUser) { | |
ef1c8de5 | 1373 | CString sMsg = sMessage; |
a773c13f | 1374 | |
b740d7cc | 1375 | MODULECALL(OnBroadcast(sMsg), a->second, NULL, continue); |
a773c13f | 1376 | a->second->PutStatusNotice("*** " + sMsg, NULL, pSkipClient); |
ef1c8de5 | 1377 | } |
1378 | } | |
1379 | } | |
1380 | ||
ad92c58c | 1381 | CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) { |
1382 | if (sUsername.empty()) { | |
1383 | return CZNC::Get().GetModules().FindModule(sModName); | |
1384 | } | |
1385 | ||
1386 | CUser* pUser = FindUser(sUsername); | |
1387 | ||
1388 | return (!pUser) ? NULL : pUser->GetModules().FindModule(sModName); | |
1389 | } | |
1390 | ||
1391 | CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) { | |
1392 | if (pUser) { | |
1393 | return pUser->GetModules().FindModule(sModName); | |
1394 | } | |
1395 | ||
1396 | return CZNC::Get().GetModules().FindModule(sModName); | |
1397 | } | |
1398 | ||
c315a07d | 1399 | CUser* CZNC::FindUser(const CString& sUsername) { |
1400 | map<CString,CUser*>::iterator it = m_msUsers.find(sUsername); | |
1401 | ||
1402 | if (it != m_msUsers.end()) { | |
1403 | return it->second; | |
1404 | } | |
1405 | ||
1406 | return NULL; | |
1407 | } | |
1408 | ||
1409 | bool CZNC::DeleteUser(const CString& sUsername) { | |
1410 | CUser* pUser = FindUser(sUsername); | |
1411 | ||
1412 | if (!pUser) { | |
1413 | return false; | |
1414 | } | |
1415 | ||
b51302be | 1416 | m_msDelUsers[pUser->GetUserName()] = pUser; |
c315a07d | 1417 | return true; |
1418 | } | |
1419 | ||
e8d176ea | 1420 | bool CZNC::AddUser(CUser* pUser, CString& sErrorRet) { |
48ee5c71 | 1421 | if (FindUser(pUser->GetUserName()) != NULL) { |
ea569a2b | 1422 | sErrorRet = "User already exists"; |
235b10c2 | 1423 | DEBUG("User [" << pUser->GetUserName() << "] - already exists"); |
48ee5c71 | 1424 | return false; |
1425 | } | |
1426 | if (!pUser->IsValid(sErrorRet)) { | |
235b10c2 | 1427 | DEBUG("Invalid user [" << pUser->GetUserName() << "] - [" |
1428 | << sErrorRet << "]"); | |
48ee5c71 | 1429 | return false; |
c315a07d | 1430 | } |
9ae959b8 | 1431 | GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), pUser, NULL, |
28022ca7 | 1432 | DEBUG("AddUser [" << pUser->GetUserName() << "] aborted by a module [" |
1433 | << sErrorRet << "]"); | |
1434 | return false; | |
9ae959b8 | 1435 | ); |
48ee5c71 | 1436 | m_msUsers[pUser->GetUserName()] = pUser; |
1437 | return true; | |
c315a07d | 1438 | } |
2a32927d | 1439 | |
cac29752 | 1440 | CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost, EAddrType eAddr) { |
1441 | vector<CListener*>::iterator it; | |
1442 | ||
1443 | for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) { | |
1444 | if ((*it)->GetPort() != uPort) | |
1445 | continue; | |
1446 | if ((*it)->GetBindHost() != sBindHost) | |
1447 | continue; | |
1448 | if ((*it)->GetAddrType() != eAddr) | |
1449 | continue; | |
1450 | return *it; | |
1451 | } | |
1452 | return NULL; | |
1453 | } | |
1454 | ||
70c77458 US |
1455 | bool CZNC::AddListener(const CString& sLine, CString& sError) { |
1456 | CString sName = sLine.Token(0); | |
1457 | CString sValue = sLine.Token(1, true); | |
1458 | ||
1459 | EAddrType eAddr = ADDR_ALL; | |
1460 | if (sName.Equals("Listen4") || sName.Equals("Listen") || sName.Equals("Listener4")) { | |
1461 | eAddr = ADDR_IPV4ONLY; | |
1462 | } | |
1463 | if (sName.Equals("Listener6")) { | |
1464 | eAddr = ADDR_IPV6ONLY; | |
1465 | } | |
1466 | ||
1467 | CListener::EAcceptType eAccept = CListener::ACCEPT_ALL; | |
1468 | if (sValue.TrimPrefix("irc_only ")) | |
1469 | eAccept = CListener::ACCEPT_IRC; | |
1470 | else if (sValue.TrimPrefix("web_only ")) | |
1471 | eAccept = CListener::ACCEPT_HTTP; | |
1472 | ||
1473 | bool bSSL = false; | |
1474 | CString sPort; | |
1475 | CString sBindHost; | |
1476 | ||
1477 | if (ADDR_IPV4ONLY == eAddr) { | |
1478 | sValue.Replace(":", " "); | |
1479 | } | |
1480 | ||
1481 | if (sValue.find(" ") != CString::npos) { | |
1482 | sBindHost = sValue.Token(0, false, " "); | |
1483 | sPort = sValue.Token(1, true, " "); | |
1484 | } else { | |
1485 | sPort = sValue; | |
1486 | } | |
1487 | ||
1488 | if (sPort.Left(1) == "+") { | |
1489 | sPort.LeftChomp(); | |
1490 | bSSL = true; | |
1491 | } | |
1492 | ||
1493 | CString sHostComment; | |
1494 | ||
1495 | if (!sBindHost.empty()) { | |
1496 | sHostComment = " on host [" + sBindHost + "]"; | |
1497 | } | |
1498 | ||
1499 | CString sIPV6Comment; | |
1500 | ||
1501 | switch (eAddr) { | |
1502 | case ADDR_ALL: | |
1503 | sIPV6Comment = ""; | |
1504 | break; | |
1505 | case ADDR_IPV4ONLY: | |
1506 | sIPV6Comment = " using ipv4"; | |
1507 | break; | |
1508 | case ADDR_IPV6ONLY: | |
1509 | sIPV6Comment = " using ipv6"; | |
1510 | } | |
1511 | ||
1512 | unsigned short uPort = sPort.ToUShort(); | |
1513 | CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") + CString(uPort) + "]" + sHostComment + sIPV6Comment); | |
1514 | ||
1515 | #ifndef HAVE_IPV6 | |
1516 | if (ADDR_IPV6ONLY == eAddr) { | |
1517 | sError = "IPV6 is not enabled"; | |
1518 | CUtils::PrintStatus(false, sError); | |
1519 | return false; | |
1520 | } | |
1521 | #endif | |
1522 | ||
1523 | #ifndef HAVE_LIBSSL | |
1524 | if (bSSL) { | |
1525 | sError = "SSL is not enabled"; | |
1526 | CUtils::PrintStatus(false, sError); | |
1527 | return false; | |
1528 | } | |
1529 | #else | |
1530 | CString sPemFile = GetPemLocation(); | |
1531 | ||
1532 | if (bSSL && !CFile::Exists(sPemFile)) { | |
1533 | sError = "Unable to locate pem file: [" + sPemFile + "]"; | |
1534 | CUtils::PrintStatus(false, sError); | |
1535 | ||
1536 | // If stdin is e.g. /dev/null and we call GetBoolInput(), | |
1537 | // we are stuck in an endless loop! | |
1538 | if (isatty(0) && CUtils::GetBoolInput("Would you like to create a new pem file?", true)) { | |
1539 | sError.clear(); | |
1540 | WritePemFile(); | |
1541 | } else { | |
1542 | return false; | |
1543 | } | |
1544 | ||
1545 | CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" + sHostComment + sIPV6Comment); | |
1546 | } | |
1547 | #endif | |
1548 | if (!uPort) { | |
1549 | sError = "Invalid port"; | |
1550 | CUtils::PrintStatus(false, sError); | |
1551 | return false; | |
1552 | } | |
1553 | ||
1554 | CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept); | |
1555 | ||
1556 | if (!pListener->Listen()) { | |
1557 | sError = FormatBindError(); | |
1558 | CUtils::PrintStatus(false, sError); | |
1559 | delete pListener; | |
1560 | return false; | |
1561 | } | |
1562 | ||
1563 | m_vpListeners.push_back(pListener); | |
1564 | CUtils::PrintStatus(true); | |
1565 | ||
1566 | return true; | |
1567 | } | |
1568 | ||
cac29752 | 1569 | bool CZNC::AddListener(CListener* pListener) { |
1570 | if (!pListener->GetRealListener()) { | |
1571 | // Listener doesnt actually listen | |
1572 | delete pListener; | |
1573 | return false; | |
1574 | } | |
1575 | ||
1576 | // We don't check if there is an identical listener already listening | |
1577 | // since one can't listen on e.g. the same port multiple times | |
1578 | ||
1579 | m_vpListeners.push_back(pListener); | |
1580 | return true; | |
1581 | } | |
1582 | ||
1583 | bool CZNC::DelListener(CListener* pListener) { | |
1584 | vector<CListener*>::iterator it; | |
1585 | ||
1586 | for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) { | |
1587 | if (*it == pListener) { | |
1588 | m_vpListeners.erase(it); | |
505bdd1e | 1589 | delete pListener; |
cac29752 | 1590 | return true; |
1591 | } | |
1592 | } | |
1593 | ||
1594 | return false; | |
1595 | } | |
1596 | ||
2a32927d | 1597 | CZNC& CZNC::Get() { |
1598 | static CZNC* pZNC = new CZNC; | |
1599 | return *pZNC; | |
1600 | } | |
1601 | ||
85dce87d | 1602 | CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair &Users, |
1603 | TrafficStatsPair &ZNC, TrafficStatsPair &Total) { | |
1604 | TrafficStatsMap ret; | |
f36ad9e6 | 1605 | unsigned long long uiUsers_in, uiUsers_out, uiZNC_in, uiZNC_out; |
1606 | const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap(); | |
1607 | ||
1608 | uiUsers_in = uiUsers_out = 0; | |
1609 | uiZNC_in = BytesRead(); | |
1610 | uiZNC_out = BytesWritten(); | |
1611 | ||
115b1708 | 1612 | for (map<CString, CUser*>::const_iterator it = msUsers.begin(); it != msUsers.end(); ++it) { |
85dce87d | 1613 | ret[it->first] = TrafficStatsPair(it->second->BytesRead(), it->second->BytesWritten()); |
f36ad9e6 | 1614 | uiUsers_in += it->second->BytesRead(); |
1615 | uiUsers_out += it->second->BytesWritten(); | |
1616 | } | |
1617 | ||
115b1708 | 1618 | for (CSockManager::const_iterator it = m_Manager.begin(); it != m_Manager.end(); ++it) { |
1fcb7b2b | 1619 | CUser *pUser = NULL; |
f36ad9e6 | 1620 | if ((*it)->GetSockName().Left(5) == "IRC::") { |
1fcb7b2b | 1621 | pUser = ((CIRCSock *) *it)->GetUser(); |
f36ad9e6 | 1622 | } else if ((*it)->GetSockName().Left(5) == "USR::") { |
1fcb7b2b | 1623 | pUser = ((CClient*) *it)->GetUser(); |
1624 | } | |
1625 | ||
1626 | if (pUser) { | |
1627 | ret[pUser->GetUserName()].first += (*it)->GetBytesRead(); | |
1628 | ret[pUser->GetUserName()].second += (*it)->GetBytesWritten(); | |
1629 | uiUsers_in += (*it)->GetBytesRead(); | |
1630 | uiUsers_out += (*it)->GetBytesWritten(); | |
f36ad9e6 | 1631 | } else { |
1632 | uiZNC_in += (*it)->GetBytesRead(); | |
1633 | uiZNC_out += (*it)->GetBytesWritten(); | |
0a0c8260 | 1634 | } |
b772e266 | 1635 | } |
f36ad9e6 | 1636 | |
85dce87d | 1637 | Users = TrafficStatsPair(uiUsers_in, uiUsers_out); |
1638 | ZNC = TrafficStatsPair(uiZNC_in, uiZNC_out); | |
1639 | Total = TrafficStatsPair(uiUsers_in + uiZNC_in, uiUsers_out + uiZNC_out); | |
f36ad9e6 | 1640 | |
1641 | return ret; | |
b772e266 | 1642 | } |
1643 | ||
0bd4927d | 1644 | void CZNC::AuthUser(CSmartPtr<CAuthBase> AuthClass) { |
0bd4927d | 1645 | // TODO unless the auth module calls it, CUser::IsHostAllowed() is not honoured |
9ae959b8 | 1646 | GLOBALMODULECALL(OnLoginAttempt(AuthClass), NULL, NULL, return); |
0bd4927d | 1647 | |
9c92d93a | 1648 | CUser* pUser = FindUser(AuthClass->GetUsername()); |
0bd4927d | 1649 | |
1650 | if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) { | |
0bd4927d | 1651 | AuthClass->RefuseLogin("Invalid Password"); |
1652 | return; | |
1653 | } | |
1654 | ||
1655 | CString sHost = AuthClass->GetRemoteIP(); | |
1656 | ||
1657 | if (!pUser->IsHostAllowed(sHost)) { | |
1658 | AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed"); | |
1659 | return; | |
1660 | } | |
1661 | ||
1662 | AuthClass->AcceptLogin(*pUser); | |
1663 | } | |
1664 | ||
a213258c | 1665 | class CConnectUserTimer : public CCron { |
1666 | public: | |
1667 | CConnectUserTimer(int iSecs) : CCron() { | |
1668 | SetName("Connect users"); | |
1669 | Start(iSecs); | |
abeba8ae | 1670 | m_uiPosNextUser = 0; |
ef31d6e2 | 1671 | // Don't wait iSecs seconds for first timer run |
1672 | m_bRunOnNextCall = true; | |
a213258c | 1673 | } |
79212882 | 1674 | virtual ~CConnectUserTimer() { |
1675 | // This is only needed when ZNC shuts down: | |
1676 | // CZNC::~CZNC() sets its CConnectUserTimer pointer to NULL and | |
1677 | // calls the manager's Cleanup() which destroys all sockets and | |
1678 | // timers. If something calls CZNC::EnableConnectUser() here | |
1679 | // (e.g. because a CIRCSock is destroyed), the socket manager | |
1680 | // deletes that timer almost immediately, but CZNC now got a | |
1681 | // dangling pointer to this timer which can crash later on. | |
1682 | // | |
1683 | // Unlikely but possible ;) | |
1684 | CZNC::Get().LeakConnectUser(this); | |
1685 | } | |
a213258c | 1686 | |
1687 | protected: | |
1688 | virtual void RunJob() { | |
d78369fe | 1689 | unsigned int uiUserCount; |
d78369fe | 1690 | bool bUsersLeft = false; |
abeba8ae | 1691 | const map<CString,CUser*>& mUsers = CZNC::Get().GetUserMap(); |
1692 | map<CString,CUser*>::const_iterator it = mUsers.begin(); | |
a213258c | 1693 | |
d78369fe | 1694 | uiUserCount = CZNC::Get().GetUserMap().size(); |
abeba8ae | 1695 | |
1696 | if (m_uiPosNextUser >= uiUserCount) { | |
1697 | m_uiPosNextUser = 0; | |
1698 | } | |
1699 | ||
1700 | for (unsigned int i = 0; i < m_uiPosNextUser; i++) { | |
1701 | it++; | |
1702 | } | |
a213258c | 1703 | |
1704 | // Try to connect each user, if this doesnt work, abort | |
d78369fe | 1705 | for (unsigned int i = 0; i < uiUserCount; i++) { |
abeba8ae | 1706 | if (it == mUsers.end()) |
1707 | it = mUsers.begin(); | |
a213258c | 1708 | |
abeba8ae | 1709 | CUser* pUser = it->second; |
1710 | it++; | |
1711 | m_uiPosNextUser = (m_uiPosNextUser + 1) % uiUserCount; | |
a213258c | 1712 | |
87a368cb | 1713 | // Is this user disconnected? |
1714 | if (pUser->GetIRCSock() != NULL) | |
1715 | continue; | |
a213258c | 1716 | |
87a368cb | 1717 | // Does this user want to connect? |
1718 | if (!pUser->GetIRCConnectEnabled()) | |
1719 | continue; | |
abeba8ae | 1720 | |
87a368cb | 1721 | // Does this user have any servers? |
1722 | if (!pUser->HasServers()) | |
1723 | continue; | |
1724 | ||
1725 | // The timer runs until it once didn't find any users to connect | |
1726 | bUsersLeft = true; | |
1727 | ||
235b10c2 | 1728 | DEBUG("Connecting user [" << pUser->GetUserName() << "]"); |
87a368cb | 1729 | |
1730 | if (CZNC::Get().ConnectUser(pUser)) | |
1731 | // User connecting, wait until next time timer fires | |
1732 | return; | |
d78369fe | 1733 | } |
a213258c | 1734 | |
abeba8ae | 1735 | if (bUsersLeft == false) { |
235b10c2 | 1736 | DEBUG("ConnectUserTimer done"); |
a213258c | 1737 | CZNC::Get().DisableConnectUser(); |
abeba8ae | 1738 | } |
a213258c | 1739 | } |
1740 | ||
1741 | private: | |
f2d7ae1a | 1742 | size_t m_uiPosNextUser; |
a213258c | 1743 | }; |
1744 | ||
0456e3f0 | 1745 | void CZNC::SetConnectDelay(unsigned int i) { |
1746 | if (m_uiConnectDelay != i && m_pConnectUserTimer != NULL) { | |
1747 | m_pConnectUserTimer->Start(i); | |
1748 | } | |
1749 | m_uiConnectDelay = i; | |
1750 | } | |
1751 | ||
a213258c | 1752 | void CZNC::EnableConnectUser() { |
1753 | if (m_pConnectUserTimer != NULL) | |
1754 | return; | |
1755 | ||
6b07726e | 1756 | m_pConnectUserTimer = new CConnectUserTimer(m_uiConnectDelay); |
a213258c | 1757 | GetManager().AddCron(m_pConnectUserTimer); |
1758 | } | |
1759 | ||
1760 | void CZNC::DisableConnectUser() { | |
1761 | if (m_pConnectUserTimer == NULL) | |
1762 | return; | |
1763 | ||
1764 | // This will kill the cron | |
1765 | m_pConnectUserTimer->Stop(); | |
1766 | m_pConnectUserTimer = NULL; | |
1767 | } | |
79212882 | 1768 | |
1769 | void CZNC::LeakConnectUser(CConnectUserTimer *pTimer) { | |
1770 | if (m_pConnectUserTimer == pTimer) | |
1771 | m_pConnectUserTimer = NULL; | |
1772 | } | |
33ce80f4 | 1773 | |
1774 | bool CZNC::WaitForChildLock() { | |
a4d388b5 | 1775 | return m_pLockFile && m_pLockFile->ExLock(); |
33ce80f4 | 1776 | } |
672df0a8 A |
1777 | |
1778 | void CZNC::DisableConfigTimer() { | |
1779 | if (m_pConfigTimer) { | |
1780 | m_pConfigTimer->Stop(); | |
1781 | m_pConfigTimer = NULL; | |
1782 | } | |
1783 | } |