~ubuntu-branches/ubuntu/oneiric/znc/oneiric-backports

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