~ubuntu-branches/ubuntu/utopic/znc/utopic

« back to all changes in this revision

Viewing changes to .pc/02-inc-port-in-cookie.diff/WebModules.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Patrick Matthäi
  • Date: 2010-08-16 21:08:17 UTC
  • Revision ID: james.westby@ubuntu.com-20100816210817-hl153y7n93f7p1ia
Tags: 0.092-2
* Merge 0.092-1~bpo50+1 changelog.
* Add upstream patch 01-out-of-range-error.diff, which fixes a DoS from
  authenticated users.
  Closes: #592064
* Add upstream patch 02-inc-port-in-cookie.diff, which adds the port number
  of the znc instance to the cookie name, to avoid cookie clashes with more
  than a single znc instance per IP and user.
* Add upstream patch 03-attach-channels-on-joining.diff, which ensures, that
  channels are always attached on joining them.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2004-2010  See the AUTHORS file for details.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 2 as published
 
6
 * by the Free Software Foundation.
 
7
 */
 
8
 
 
9
#include "WebModules.h"
 
10
#include "User.h"
 
11
#include "znc.h"
 
12
#include <sstream>
 
13
 
 
14
/// @todo Do we want to make this a configure option?
 
15
#define _SKINDIR_ _DATADIR_ "/webskins"
 
16
 
 
17
// Sessions are valid for a day, (24h, ...)
 
18
CWebSessionMap CWebSock::m_mspSessions(24 * 60 * 60 * 1000);
 
19
 
 
20
CZNCTagHandler::CZNCTagHandler(CWebSock& WebSock) : CTemplateTagHandler(), m_WebSock(WebSock) {
 
21
}
 
22
 
 
23
bool CZNCTagHandler::HandleTag(CTemplate& Tmpl, const CString& sName, const CString& sArgs, CString& sOutput) {
 
24
        if (sName.Equals("URLPARAM")) {
 
25
                //sOutput = CZNC::Get()
 
26
                sOutput = m_WebSock.GetParam(sArgs.Token(0), false);
 
27
                return true;
 
28
        }
 
29
 
 
30
        return false;
 
31
}
 
32
 
 
33
CWebSession::CWebSession(const CString& sId) : m_sId(sId) {
 
34
        m_pUser = NULL;
 
35
}
 
36
 
 
37
bool CWebSession::IsAdmin() const { return IsLoggedIn() && m_pUser->IsAdmin(); }
 
38
 
 
39
CWebAuth::CWebAuth(CWebSock* pWebSock, const CString& sUsername, const CString& sPassword)
 
40
        : CAuthBase(sUsername, sPassword, pWebSock) {
 
41
        m_pWebSock = pWebSock;
 
42
}
 
43
 
 
44
void CWebSession::ClearMessageLoops() {
 
45
        m_vsErrorMsgs.clear();
 
46
        m_vsSuccessMsgs.clear();
 
47
}
 
48
 
 
49
void CWebSession::FillMessageLoops(CTemplate& Tmpl) {
 
50
        for (unsigned int a = 0; a < m_vsErrorMsgs.size(); a++) {
 
51
                CTemplate& Row = Tmpl.AddRow("ErrorLoop");
 
52
                Row["Message"] = m_vsErrorMsgs[a];
 
53
        }
 
54
 
 
55
        for (unsigned int b = 0; b < m_vsSuccessMsgs.size(); b++) {
 
56
                CTemplate& Row = Tmpl.AddRow("SuccessLoop");
 
57
                Row["Message"] = m_vsSuccessMsgs[b];
 
58
        }
 
59
}
 
60
 
 
61
size_t CWebSession::AddError(const CString& sMessage) {
 
62
        m_vsErrorMsgs.push_back(sMessage);
 
63
        return m_vsErrorMsgs.size();
 
64
}
 
65
 
 
66
size_t CWebSession::AddSuccess(const CString& sMessage) {
 
67
        m_vsSuccessMsgs.push_back(sMessage);
 
68
        return m_vsSuccessMsgs.size();
 
69
}
 
70
 
 
71
void CWebSessionMap::FinishUserSessions(const CUser& User) {
 
72
        iterator it = m_mItems.begin();
 
73
 
 
74
        while (it != m_mItems.end()) {
 
75
                if (it->second.second->GetUser() == &User) {
 
76
                        m_mItems.erase(it++);
 
77
                } else {
 
78
                        ++it;
 
79
                }
 
80
        }
 
81
}
 
82
 
 
83
void CWebAuth::AcceptedLogin(CUser& User) {
 
84
        if (m_pWebSock) {
 
85
                CSmartPtr<CWebSession> spSession = m_pWebSock->GetSession();
 
86
 
 
87
                spSession->SetUser(&User);
 
88
 
 
89
                m_pWebSock->SetLoggedIn(true);
 
90
                m_pWebSock->UnPauseRead();
 
91
                m_pWebSock->Redirect("/?cookie_check=true");
 
92
 
 
93
                DEBUG("Successful login attempt ==> USER [" + User.GetUserName() + "] ==> SESSION [" + spSession->GetId() + "]");
 
94
        }
 
95
}
 
96
 
 
97
void CWebAuth::RefusedLogin(const CString& sReason) {
 
98
        if (m_pWebSock) {
 
99
                CSmartPtr<CWebSession> spSession = m_pWebSock->GetSession();
 
100
 
 
101
                spSession->AddError("Invalid login!");
 
102
                spSession->SetUser(NULL);
 
103
 
 
104
                m_pWebSock->SetLoggedIn(false);
 
105
                m_pWebSock->UnPauseRead();
 
106
                m_pWebSock->Redirect("/?cookie_check=true");
 
107
 
 
108
                DEBUG("UNSUCCESSFUL login attempt ==> REASON [" + sReason + "] ==> SESSION [" + spSession->GetId() + "]");
 
109
        }
 
110
}
 
111
 
 
112
void CWebAuth::Invalidate() {
 
113
        CAuthBase::Invalidate();
 
114
        m_pWebSock = NULL;
 
115
}
 
116
 
 
117
CWebSock::CWebSock(CModule* pModule) : CHTTPSock(pModule) {
 
118
        m_pModule = pModule;
 
119
        m_bPathsSet = false;
 
120
 
 
121
        m_Template.AddTagHandler(new CZNCTagHandler(*this));
 
122
}
 
123
 
 
124
CWebSock::CWebSock(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout)
 
125
                : CHTTPSock(pModule, sHostname, uPort, iTimeout) {
 
126
        m_pModule = pModule;
 
127
        m_bPathsSet = false;
 
128
 
 
129
        m_Template.AddTagHandler(new CZNCTagHandler(*this));
 
130
}
 
131
 
 
132
CWebSock::~CWebSock() {
 
133
        if (!m_spAuth.IsNull()) {
 
134
                m_spAuth->Invalidate();
 
135
        }
 
136
 
 
137
        CUser *pUser = GetSession()->GetUser();
 
138
        if (pUser) {
 
139
                pUser->AddBytesWritten(GetBytesWritten());
 
140
                pUser->AddBytesRead(GetBytesRead());
 
141
        } else {
 
142
                CZNC::Get().AddBytesWritten(GetBytesWritten());
 
143
                CZNC::Get().AddBytesRead(GetBytesRead());
 
144
        }
 
145
 
 
146
        // bytes have been accounted for, so make sure they don't get again:
 
147
        ResetBytesWritten();
 
148
        ResetBytesRead();
 
149
 
 
150
        // If the module IsFake() then it was created as a dummy and needs to be deleted
 
151
        if (m_pModule && m_pModule->IsFake()) {
 
152
                m_pModule->UnlinkSocket(this);
 
153
                delete m_pModule;
 
154
                m_pModule = NULL;
 
155
        }
 
156
}
 
157
 
 
158
void CWebSock::ParsePath() {
 
159
        // The URI looks like:
 
160
        //         /[user:][module][/page][?arg1=val1&arg2=val2...]
 
161
 
 
162
        m_sForceUser.clear();
 
163
 
 
164
        m_sPath = GetPath().TrimLeft_n("/");
 
165
 
 
166
        m_sPath.TrimPrefix("mods/");
 
167
        m_sPath.TrimPrefix("modfiles/");
 
168
 
 
169
        m_sModName = m_sPath.Token(0, false, "/");
 
170
        m_sPage = m_sPath.Token(1, true, "/");
 
171
 
 
172
        if (m_sModName.find(":") != CString::npos) {
 
173
                m_sForceUser = m_sModName.Token(0, false, ":");
 
174
                m_sModName = m_sModName.Token(1, false, ":");
 
175
        }
 
176
 
 
177
        if (m_sPage.empty()) {
 
178
                m_sPage = "index";
 
179
        }
 
180
 
 
181
        DEBUG("Path [" + m_sPath + "], User [" + m_sForceUser + "], Module [" + m_sModName + "], Page [" + m_sPage + "]");
 
182
}
 
183
 
 
184
CModule* CWebSock::ResolveModule() {
 
185
        ParsePath();
 
186
        CModule* pModRet = NULL;
 
187
 
 
188
        // Dot means static file, not module
 
189
        if (m_sModName.find(".") != CString::npos) {
 
190
                return NULL;
 
191
        }
 
192
 
 
193
        // First look for forced user-mods
 
194
        if (!m_sForceUser.empty()) {
 
195
                CUser* pUser = CZNC::Get().FindUser(m_sForceUser);
 
196
 
 
197
                if (pUser) {
 
198
                        pModRet = pUser->GetModules().FindModule(m_sModName);
 
199
                } else {
 
200
                        DEBUG("User not found while trying to handle web request for [" + m_sPage + "]");
 
201
                }
 
202
        } else {
 
203
                // This could be user level or global level, check both
 
204
                pModRet = CZNC::Get().GetModules().FindModule(m_sModName);
 
205
 
 
206
                if (!pModRet) {
 
207
                        // It's not a loaded global module and it has no forced username so we
 
208
                        // have to force a login to try a module loaded by the current user
 
209
                        if (!ForceLogin()) {
 
210
                                return NULL;
 
211
                        }
 
212
 
 
213
                        pModRet = GetSession()->GetUser()->GetModules().FindModule(m_sModName);
 
214
                }
 
215
        }
 
216
 
 
217
        if (!pModRet) {
 
218
                DEBUG("Module not found");
 
219
        } else if (pModRet->IsFake()) {
 
220
                DEBUG("Fake module found, ignoring");
 
221
                pModRet = NULL;
 
222
        }
 
223
 
 
224
        return pModRet;
 
225
}
 
226
 
 
227
size_t CWebSock::GetAvailSkins(vector<CFile>& vRet) {
 
228
        vRet.clear();
 
229
 
 
230
        CString sRoot(GetSkinPath("_default_"));
 
231
 
 
232
        sRoot.TrimRight("/");
 
233
        sRoot.TrimRight("_default_");
 
234
        sRoot.TrimRight("/");
 
235
 
 
236
        if (!sRoot.empty()) {
 
237
                sRoot += "/";
 
238
        }
 
239
 
 
240
        if (!sRoot.empty() && CFile::IsDir(sRoot)) {
 
241
                CDir Dir(sRoot);
 
242
 
 
243
                for (unsigned int d = 0; d < Dir.size(); d++) {
 
244
                        const CFile& SubDir = *Dir[d];
 
245
 
 
246
                        if (SubDir.IsDir() && SubDir.GetShortName() == "_default_") {
 
247
                                vRet.push_back(SubDir);
 
248
                        }
 
249
                }
 
250
 
 
251
                for (unsigned int e = 0; e < Dir.size(); e++) {
 
252
                        const CFile& SubDir = *Dir[e];
 
253
 
 
254
                        if (SubDir.IsDir() && SubDir.GetShortName() != "_default_" && SubDir.GetShortName() != ".svn") {
 
255
                                vRet.push_back(SubDir);
 
256
                        }
 
257
                }
 
258
        }
 
259
 
 
260
        return vRet.size();
 
261
}
 
262
 
 
263
void CWebSock::SetPaths(CModule* pModule, bool bIsTemplate) {
 
264
        m_Template.ClearPaths();
 
265
 
 
266
        CString sHomeSkinsDir(CZNC::Get().GetZNCPath() + "/webskins/");
 
267
        CString sSkinName(GetSkinName());
 
268
 
 
269
        // Module specific paths
 
270
 
 
271
        if (pModule) {
 
272
                const CString& sModName(pModule->GetModName());
 
273
 
 
274
                // 1. ~/.znc/webskins/<user_skin_setting>/mods/<mod_name>/
 
275
                //
 
276
                if (!sSkinName.empty()) {
 
277
                        m_Template.AppendPath(GetSkinPath(sSkinName) + "/mods/" + sModName + "/");
 
278
                }
 
279
 
 
280
                // 2. ~/.znc/webskins/_default_/mods/<mod_name>/
 
281
                //
 
282
                m_Template.AppendPath(GetSkinPath("_default_") + "/mods/" + sModName + "/");
 
283
 
 
284
                // 3. ./modules/<mod_name>/tmpl/
 
285
                //
 
286
                m_Template.AppendPath(pModule->GetModDataDir() + "/tmpl/");
 
287
 
 
288
                // 4. ~/.znc/webskins/<user_skin_setting>/mods/<mod_name>/
 
289
                //
 
290
                if (!sSkinName.empty()) {
 
291
                        m_Template.AppendPath(GetSkinPath(sSkinName) + "/mods/" + sModName + "/");
 
292
                }
 
293
 
 
294
                // 5. ~/.znc/webskins/_default_/mods/<mod_name>/
 
295
                //
 
296
                m_Template.AppendPath(GetSkinPath("_default_") + "/mods/" + sModName + "/");
 
297
        }
 
298
 
 
299
        // 6. ~/.znc/webskins/<user_skin_setting>/
 
300
        //
 
301
        if (!sSkinName.empty()) {
 
302
                m_Template.AppendPath(GetSkinPath(sSkinName) + CString(bIsTemplate ? "/tmpl/" : "/"), (0 && pModule != NULL));
 
303
        }
 
304
 
 
305
        // 7. ~/.znc/webskins/_default_/
 
306
        //
 
307
        m_Template.AppendPath(GetSkinPath("_default_") + CString(bIsTemplate ? "/tmpl/" : "/"), (0 && pModule != NULL));
 
308
 
 
309
        m_bPathsSet = true;
 
310
}
 
311
 
 
312
void CWebSock::SetVars() {
 
313
        m_Template["SessionUser"] = GetUser();
 
314
        m_Template["SessionIP"] = GetRemoteIP();
 
315
        m_Template["Tag"] = CZNC::GetTag(GetSession()->GetUser() != NULL);
 
316
        m_Template["SkinName"] = GetSkinName();
 
317
        m_Template["_CSRF_Check"] = GetCSRFCheck();
 
318
 
 
319
        if (GetSession()->IsAdmin()) {
 
320
                m_Template["IsAdmin"] = "true";
 
321
        }
 
322
 
 
323
        GetSession()->FillMessageLoops(m_Template);
 
324
        GetSession()->ClearMessageLoops();
 
325
 
 
326
        // Global Mods
 
327
        CGlobalModules& vgMods = CZNC::Get().GetModules();
 
328
        for (unsigned int a = 0; a < vgMods.size(); a++) {
 
329
                AddModLoop("GlobalModLoop", *vgMods[a]);
 
330
        }
 
331
 
 
332
        // User Mods
 
333
        if (IsLoggedIn()) {
 
334
                CModules& vMods = GetSession()->GetUser()->GetModules();
 
335
 
 
336
                for (unsigned int a = 0; a < vMods.size(); a++) {
 
337
                        AddModLoop("UserModLoop", *vMods[a]);
 
338
                }
 
339
        }
 
340
 
 
341
        if (IsLoggedIn()) {
 
342
                m_Template["LoggedIn"] = "true";
 
343
        }
 
344
}
 
345
 
 
346
bool CWebSock::AddModLoop(const CString& sLoopName, CModule& Module) {
 
347
        CString sTitle(Module.GetWebMenuTitle());
 
348
 
 
349
        if (!sTitle.empty() && (IsLoggedIn() || (!Module.WebRequiresLogin() && !Module.WebRequiresAdmin())) && (GetSession()->IsAdmin() || !Module.WebRequiresAdmin())) {
 
350
                CTemplate& Row = m_Template.AddRow(sLoopName);
 
351
 
 
352
                Row["ModName"] = Module.GetModName();
 
353
                Row["Title"] = sTitle;
 
354
 
 
355
                if (m_sModName == Module.GetModName()) {
 
356
                        Row["Active"] = "true";
 
357
                }
 
358
 
 
359
                if (Module.GetUser()) {
 
360
                        Row["Username"] = Module.GetUser()->GetUserName();
 
361
                }
 
362
 
 
363
                VWebSubPages& vSubPages = Module.GetSubPages();
 
364
 
 
365
                for (unsigned int a = 0; a < vSubPages.size(); a++) {
 
366
                        TWebSubPage& SubPage = vSubPages[a];
 
367
 
 
368
                        // bActive is whether or not the current url matches this subpage (params will be checked below)
 
369
                        bool bActive = (m_sModName == Module.GetModName() && m_sPage == SubPage->GetName());
 
370
 
 
371
                        if (SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) {
 
372
                                continue;  // Don't add admin-only subpages to requests from non-admin users
 
373
                        }
 
374
 
 
375
                        CTemplate& SubRow = Row.AddRow("SubPageLoop");
 
376
 
 
377
                        SubRow["ModName"] = Module.GetModName();
 
378
                        SubRow["PageName"] = SubPage->GetName();
 
379
                        SubRow["Title"] = SubPage->GetTitle().empty() ? SubPage->GetName() : SubPage->GetTitle();
 
380
 
 
381
                        CString& sParams = SubRow["Params"];
 
382
 
 
383
                        const VPair& vParams = SubPage->GetParams();
 
384
                        for (size_t b = 0; b < vParams.size(); b++) {
 
385
                                pair<CString, CString> ssNV = vParams[b];
 
386
 
 
387
                                if (!sParams.empty()) {
 
388
                                        sParams += "&";
 
389
                                }
 
390
 
 
391
                                if (!ssNV.first.empty()) {
 
392
                                        if (!ssNV.second.empty()) {
 
393
                                                sParams += ssNV.first.Escape_n(CString::EURL);
 
394
                                                sParams += "=";
 
395
                                                sParams += ssNV.second.Escape_n(CString::EURL);
 
396
                                        }
 
397
 
 
398
                                        if (bActive && GetParam(ssNV.first, false) != ssNV.second) {
 
399
                                                bActive = false;
 
400
                                        }
 
401
                                }
 
402
                        }
 
403
 
 
404
                        if (bActive) {
 
405
                                SubRow["Active"] = "true";
 
406
                        }
 
407
                }
 
408
 
 
409
                return true;
 
410
        }
 
411
 
 
412
        return false;
 
413
}
 
414
 
 
415
CWebSock::EPageReqResult CWebSock::PrintStaticFile(const CString& sPath, CString& sPageRet, CModule* pModule) {
 
416
        SetPaths(pModule);
 
417
        CString sFile = m_Template.ExpandFile(sPath.TrimLeft_n("/"));
 
418
        DEBUG("About to print [" + sFile+ "]");
 
419
        // Either PrintFile() fails and sends an error page or it suceeds and
 
420
        // sends a result. In both cases we don't have anything more to do.
 
421
        PrintFile(sFile);
 
422
        return PAGE_DONE;
 
423
}
 
424
 
 
425
CWebSock::EPageReqResult CWebSock::PrintTemplate(const CString& sPageName, CString& sPageRet, CModule* pModule) {
 
426
        SetVars();
 
427
        m_Template["PageName"] = sPageName;
 
428
 
 
429
        if (pModule) {
 
430
                CUser* pUser = pModule->GetUser();
 
431
                m_Template["ModUser"] = pUser ? pUser->GetUserName() : "";
 
432
                m_Template["ModName"] = pModule->GetModName();
 
433
 
 
434
                if (m_Template.find("Title") == m_Template.end()) {
 
435
                        m_Template["Title"] = pModule->GetWebMenuTitle();
 
436
                }
 
437
        }
 
438
 
 
439
        if (!m_bPathsSet) {
 
440
                SetPaths(pModule, true);
 
441
        }
 
442
 
 
443
        if (m_Template.GetFileName().empty() && !m_Template.SetFile(sPageName + ".tmpl")) {
 
444
                return PAGE_NOTFOUND;
 
445
        }
 
446
 
 
447
        if (m_Template.PrintString(sPageRet)) {
 
448
                return PAGE_PRINT;
 
449
        } else {
 
450
                return PAGE_NOTFOUND;
 
451
        }
 
452
}
 
453
 
 
454
CString CWebSock::GetSkinPath(const CString& sSkinName) const {
 
455
        CString sRet = CZNC::Get().GetZNCPath() + "/webskins/" + sSkinName;
 
456
 
 
457
        if (!CFile::IsDir(sRet)) {
 
458
                sRet = CZNC::Get().GetCurPath() + "/webskins/" + sSkinName;
 
459
 
 
460
                if (!CFile::IsDir(sRet)) {
 
461
                        sRet = CString(_SKINDIR_) + "/" + sSkinName;
 
462
                }
 
463
        }
 
464
 
 
465
        return sRet + "/";
 
466
}
 
467
 
 
468
bool CWebSock::ForceLogin() {
 
469
        if (GetSession()->IsLoggedIn()) {
 
470
                return true;
 
471
        }
 
472
 
 
473
        GetSession()->AddError("You must login to view that page");
 
474
        Redirect("/");
 
475
        return false;
 
476
}
 
477
 
 
478
CString CWebSock::GetRequestCookie(const CString& sKey) const {
 
479
        CString sRet;
 
480
 
 
481
        if (!m_sModName.empty()) {
 
482
                sRet = CHTTPSock::GetRequestCookie("Mod::" + m_sModName + "::" + sKey);
 
483
        }
 
484
 
 
485
        if (sRet.empty()) {
 
486
                return CHTTPSock::GetRequestCookie(sKey);
 
487
        }
 
488
        return sRet;
 
489
}
 
490
 
 
491
bool CWebSock::SendCookie(const CString& sKey, const CString& sValue) {
 
492
        if (!m_sModName.empty()) {
 
493
                return CHTTPSock::SendCookie("Mod::" + m_sModName + "::" + sKey, sValue);
 
494
        }
 
495
 
 
496
        return CHTTPSock::SendCookie(sKey, sValue);
 
497
}
 
498
 
 
499
void CWebSock::OnPageRequest(const CString& sURI) {
 
500
        CString sPageRet;
 
501
        EPageReqResult eRet = OnPageRequestInternal(sURI, sPageRet);
 
502
        switch (eRet) {
 
503
        case PAGE_PRINT:
 
504
                PrintPage(sPageRet);
 
505
                break;
 
506
        case PAGE_DEFERRED:
 
507
                // Something else will later call Close()
 
508
                break;
 
509
        case PAGE_DONE:
 
510
                // Redirect or something like that, it's done, just make sure
 
511
                // the connection will be closed
 
512
                Close(CLT_AFTERWRITE);
 
513
                break;
 
514
        default:
 
515
                PrintNotFound();
 
516
                break;
 
517
        }
 
518
}
 
519
 
 
520
CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI, CString& sPageRet) {
 
521
        // Check that they really POSTed from one our forms by checking if they
 
522
        // know the "secret" CSRF check value. Don't do this for login since
 
523
        // CSRF against the login form makes no sense and the login form does a
 
524
        // cookies-enabled check which would break otherwise.
 
525
        if (IsPost() && GetParam("_CSRF_Check") != GetCSRFCheck() && sURI != "/login") {
 
526
                sPageRet = GetErrorPage(403, "Access denied", "POST requests need to send "
 
527
                                "a secret token to prevent cross-site request forgery attacks.");
 
528
                return PAGE_PRINT;
 
529
        }
 
530
 
 
531
        SendCookie("SessionId", GetSession()->GetId());
 
532
 
 
533
        if (GetSession()->IsLoggedIn()) {
 
534
                m_sUser = GetSession()->GetUser()->GetUserName();
 
535
                m_bLoggedIn = true;
 
536
        }
 
537
 
 
538
        // Handle the static pages that don't require a login
 
539
        if (sURI == "/") {
 
540
                if(!m_bLoggedIn && GetParam("cookie_check", false).ToBool() && GetRequestCookie("SessionId").empty()) {
 
541
                        GetSession()->AddError("Your browser does not have cookies enabled for this site!");
 
542
                }
 
543
                return PrintTemplate("index", sPageRet);
 
544
        } else if (sURI == "/favicon.ico") {
 
545
                return PrintStaticFile("/pub/favicon.ico", sPageRet);
 
546
        } else if (sURI == "/robots.txt") {
 
547
                return PrintStaticFile("/pub/robots.txt", sPageRet);
 
548
        } else if (sURI == "/logout") {
 
549
                GetSession()->SetUser(NULL);
 
550
                SetLoggedIn(false);
 
551
                Redirect("/");
 
552
 
 
553
                // We already sent a reply
 
554
                return PAGE_DONE;
 
555
        } else if (sURI == "/login") {
 
556
                if (GetParam("submitted").ToBool()) {
 
557
                        m_sUser = GetParam("user");
 
558
                        m_sPass = GetParam("pass");
 
559
                        m_bLoggedIn = OnLogin(m_sUser, m_sPass);
 
560
 
 
561
                        // AcceptedLogin()/RefusedLogin() will call Redirect()
 
562
                        return PAGE_DEFERRED;
 
563
                }
 
564
 
 
565
                Redirect("/"); // the login form is here
 
566
                return PAGE_DONE;
 
567
        } else if (sURI.Left(5) == "/pub/") {
 
568
                return PrintStaticFile(sURI, sPageRet);
 
569
        } else if (sURI.Left(11) == "/skinfiles/") {
 
570
                CString sSkinName = sURI.substr(11);
 
571
                CString::size_type uPathStart = sSkinName.find("/");
 
572
                if (uPathStart != CString::npos) {
 
573
                        CString sFilePath = sSkinName.substr(uPathStart + 1);
 
574
                        sSkinName.erase(uPathStart);
 
575
 
 
576
                        m_Template.ClearPaths();
 
577
                        m_Template.AppendPath(GetSkinPath(sSkinName) + "pub");
 
578
 
 
579
                        if (PrintFile(m_Template.ExpandFile(sFilePath))) {
 
580
                                return PAGE_DONE;
 
581
                        } else {
 
582
                                return PAGE_NOTFOUND;
 
583
                        }
 
584
                }
 
585
                return PAGE_NOTFOUND;
 
586
        } else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") {
 
587
                ParsePath();
 
588
                // Make sure modules are treated as directories
 
589
                if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) {
 
590
                        Redirect(sURI + "/");
 
591
                        return PAGE_DONE;
 
592
                }
 
593
 
 
594
                if (m_sModName.empty()) {
 
595
                        return PrintTemplate("modlist", sPageRet);
 
596
                }
 
597
 
 
598
                DEBUG("FindModule(" + m_sModName + ", " + m_sForceUser + ")");
 
599
                CModule* pModule = CZNC::Get().FindModule(m_sModName, m_sForceUser);
 
600
 
 
601
                if (!pModule && m_sForceUser.empty()) {
 
602
                        if (!ForceLogin()) {
 
603
                                return PAGE_DONE;
 
604
                        }
 
605
 
 
606
                        pModule = CZNC::Get().FindModule(m_sModName, GetSession()->GetUser());
 
607
                }
 
608
 
 
609
                if (!pModule) {
 
610
                        return PAGE_NOTFOUND;
 
611
                } else if (pModule->WebRequiresLogin() && !ForceLogin()) {
 
612
                        return PAGE_PRINT;
 
613
                } else if (pModule->WebRequiresAdmin() && !GetSession()->IsAdmin()) {
 
614
                        sPageRet = GetErrorPage(403, "Forbidden", "You need to be an admin to access this module");
 
615
                        return PAGE_PRINT;
 
616
                } else if (!pModule->IsGlobal() && pModule->GetUser() != GetSession()->GetUser()) {
 
617
                        sPageRet = GetErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page");
 
618
                        return PAGE_PRINT;
 
619
                } else if (pModule->OnWebPreRequest(*this, m_sPage)) {
 
620
                        return PAGE_DEFERRED;
 
621
                }
 
622
 
 
623
                VWebSubPages& vSubPages = pModule->GetSubPages();
 
624
 
 
625
                for (unsigned int a = 0; a < vSubPages.size(); a++) {
 
626
                        TWebSubPage& SubPage = vSubPages[a];
 
627
 
 
628
                        bool bActive = (m_sModName == pModule->GetModName() && m_sPage == SubPage->GetName());
 
629
 
 
630
                        if (bActive && SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) {
 
631
                                sPageRet = GetErrorPage(403, "Forbidden", "You need to be an admin to access this page");
 
632
                                return PAGE_PRINT;
 
633
                        }
 
634
                }
 
635
 
 
636
                if (pModule && !pModule->IsGlobal() && (!IsLoggedIn() || pModule->GetUser() != GetSession()->GetUser())) {
 
637
                        AddModLoop("UserModLoop", *pModule);
 
638
                }
 
639
 
 
640
                if (sURI.Left(10) == "/modfiles/") {
 
641
                        m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/");
 
642
                        m_Template.AppendPath(pModule->GetModDataDir() + "/files/");
 
643
 
 
644
                        if (PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")))) {
 
645
                                return PAGE_PRINT;
 
646
                        } else {
 
647
                                return PAGE_NOTFOUND;
 
648
                        }
 
649
                } else {
 
650
                        SetPaths(pModule, true);
 
651
 
 
652
                        /* if a module returns false from OnWebRequest, it does not
 
653
                           want the template to be printed, usually because it did a redirect. */
 
654
                        if (pModule->OnWebRequest(*this, m_sPage, m_Template)) {
 
655
                                // If they already sent a reply, let's assume
 
656
                                // they did what they wanted to do.
 
657
                                if (SentHeader()) {
 
658
                                        return PAGE_DONE;
 
659
                                }
 
660
                                return PrintTemplate(m_sPage, sPageRet, pModule);
 
661
                        }
 
662
 
 
663
                        if (!SentHeader()) {
 
664
                                sPageRet = GetErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests");
 
665
                                return PAGE_PRINT;
 
666
                        } else {
 
667
                                return PAGE_DONE;
 
668
                        }
 
669
                }
 
670
        } else {
 
671
                CString sPage(sURI.Trim_n("/"));
 
672
                if (sPage.length() < 32) {
 
673
                        for (unsigned int a = 0; a < sPage.length(); a++) {
 
674
                                unsigned char c = sPage[a];
 
675
 
 
676
                                if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') {
 
677
                                        return PAGE_NOTFOUND;
 
678
                                }
 
679
                        }
 
680
 
 
681
                        return PrintTemplate(sPage, sPageRet);
 
682
                }
 
683
        }
 
684
 
 
685
        return PAGE_NOTFOUND;
 
686
}
 
687
 
 
688
void CWebSock::PrintErrorPage(const CString& sMessage) {
 
689
        m_Template.SetFile("Error.tmpl");
 
690
 
 
691
        m_Template["Action"] = "error";
 
692
        m_Template["Title"] = "Error";
 
693
        m_Template["Error"] = sMessage;
 
694
}
 
695
 
 
696
CSmartPtr<CWebSession> CWebSock::GetSession() {
 
697
        if (!m_spSession.IsNull()) {
 
698
                return m_spSession;
 
699
        }
 
700
 
 
701
        const CString sCookieSessionId = GetRequestCookie("SessionId");
 
702
        CSmartPtr<CWebSession> *pSession = m_mspSessions.GetItem(sCookieSessionId);
 
703
 
 
704
        if (pSession != NULL) {
 
705
                // Refresh the timeout
 
706
                m_mspSessions.AddItem((*pSession)->GetId(), *pSession);
 
707
                m_spSession = *pSession;
 
708
                DEBUG("Found existing session from cookie: [" + sCookieSessionId + "] IsLoggedIn(" + CString((*pSession)->IsLoggedIn() ? "true" : "false") + ")");
 
709
                return *pSession;
 
710
        }
 
711
 
 
712
        CString sSessionID;
 
713
        do {
 
714
                sSessionID = CString::RandomString(32);
 
715
                sSessionID += ":" + GetRemoteIP() + ":" + CString(GetRemotePort());
 
716
                sSessionID += ":" + GetLocalIP()  + ":" + CString(GetLocalPort());
 
717
                sSessionID += ":" + CString(time(NULL));
 
718
                sSessionID = sSessionID.SHA256();
 
719
 
 
720
                DEBUG("Auto generated session: [" + sSessionID + "]");
 
721
        } while (m_mspSessions.HasItem(sSessionID));
 
722
 
 
723
        CSmartPtr<CWebSession> spSession(new CWebSession(sSessionID));
 
724
        m_mspSessions.AddItem(spSession->GetId(), spSession);
 
725
 
 
726
        m_spSession = spSession;
 
727
 
 
728
        return spSession;
 
729
}
 
730
 
 
731
CString CWebSock::GetCSRFCheck() {
 
732
        CSmartPtr<CWebSession> pSession = GetSession();
 
733
        return pSession->GetId().MD5();
 
734
}
 
735
 
 
736
bool CWebSock::OnLogin(const CString& sUser, const CString& sPass) {
 
737
        DEBUG("=================== CWebSock::OnLogin()");
 
738
        m_spAuth = new CWebAuth(this, sUser, sPass);
 
739
 
 
740
        // Some authentication module could need some time, block this socket
 
741
        // until then. CWebAuth will UnPauseRead().
 
742
        PauseRead();
 
743
        CZNC::Get().AuthUser(m_spAuth);
 
744
 
 
745
        // If CWebAuth already set this, don't change it.
 
746
        return IsLoggedIn();
 
747
}
 
748
 
 
749
Csock* CWebSock::GetSockObj(const CString& sHost, unsigned short uPort) {
 
750
        CWebSock* pSock = new CWebSock(GetModule(), sHost, uPort);
 
751
        pSock->SetSockName("Web::Client");
 
752
        pSock->SetTimeout(120);
 
753
 
 
754
        return pSock;
 
755
}
 
756
 
 
757
CString CWebSock::GetSkinName() {
 
758
        CSmartPtr<CWebSession> spSession = GetSession();
 
759
 
 
760
        if (spSession->IsLoggedIn() && !spSession->GetUser()->GetSkinName().empty()) {
 
761
                return spSession->GetUser()->GetSkinName();
 
762
        }
 
763
 
 
764
        return CZNC::Get().GetSkinName();
 
765
}
 
766