bool CHTTPSock::PrintErrorPage(unsigned int uStatusId, const CString& sStatusMsg, const CString& sMessage) { if (SentHeader()) { DEBUG("PrintErrorPage(): Header was already sent"); return false; } CString sPage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" "<!DOCTYPE html>\r\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\r\n" "<head>\r\n" "<meta charset=\"UTF-8\"/>\r\n" "<title>" + CString(uStatusId) + " " + sStatusMsg.Escape_n(CString::EHTML) + "</title>\r\n" "</head>\r\n" "<body>\r\n" "<h1>" + sStatusMsg.Escape_n(CString::EHTML) + "</h1>\r\n" "<p>" + sMessage.Escape_n(CString::EHTML) + "</p>\r\n" "<hr/>\r\n" "<address>" + CZNC::GetTag(false, /* bHTML = */ true) + " at " + GetLocalIP().Escape_n(CString::EHTML) + " Port " + CString(GetLocalPort()) + "</address>\r\n" "</body>\r\n" "</html>\r\n"; PrintHeader(sPage.length(), "text/html; charset=utf-8", uStatusId, sStatusMsg); Write(sPage); Close(Csock::CLT_AFTERWRITE); return true; }
void CHTTPSock::PrintPage(const CString& sPage) { if (!SentHeader()) { PrintHeader(sPage.length()); } else { DEBUG("PrintPage(): Header was already sent"); } Write(sPage); Close(Csock::CLT_AFTERWRITE); }
bool CHTTPSock::Redirect(const CString& sURL) { if (SentHeader()) { DEBUG("Redirect() - Header was already sent"); return false; } DEBUG("- Redirect to [" << sURL << "]"); AddHeader("Location", sURL); PrintErrorPage(302, "Found", "The document has moved <a href=\"" + sURL.Escape_n(CString::EHTML) + "\">here</a>."); return true; }
bool CHTTPSock::ForceLogin() { if (m_bLoggedIn) { return true; } if (SentHeader()) { DEBUG("ForceLogin(): Header was already sent!"); return false; } AddHeader("WWW-Authenticate", "Basic realm=\"" + CZNC::GetTag(false) + "\""); PrintErrorPage(401, "Unauthorized", "You need to login to view this page."); return false; }
bool CHTTPSock::PrintHeader(off_t uContentLength, const CString& sContentType, unsigned int uStatusId, const CString& sStatusMsg) { if (SentHeader()) { DEBUG("PrintHeader(): Header was already sent!"); return false; } if (!sContentType.empty()) { m_sContentType = sContentType; } if (m_sContentType.empty()) { m_sContentType = "text/html; charset=utf-8"; } DEBUG("- " << uStatusId << " (" << sStatusMsg << ") [" << m_sContentType << "]"); Write("HTTP/" + CString(m_bHTTP10Client ? "1.0 " : "1.1 ") + CString(uStatusId) + " " + sStatusMsg + "\r\n"); Write("Date: " + GetDate() + "\r\n"); Write("Server: " + CZNC::GetTag(false) + "\r\n"); if (uContentLength > 0) { Write("Content-Length: " + CString(uContentLength) + "\r\n"); } Write("Content-Type: " + m_sContentType + "\r\n"); MCString::iterator it; for (it = m_msResponseCookies.begin(); it != m_msResponseCookies.end(); ++it) { Write("Set-Cookie: " + it->first.Escape_n(CString::EURL) + "=" + it->second.Escape_n(CString::EURL) + "; path=/;\r\n"); } for (it = m_msHeaders.begin(); it != m_msHeaders.end(); ++it) { Write(it->first + ": " + it->second + "\r\n"); } Write("Connection: Close\r\n"); Write("\r\n"); m_bSentHeader = true; return true; }
CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI, CString& sPageRet) { if (CZNC::Get().GetProtectWebSessions() && GetSession()->GetIP() != GetRemoteIP()) { DEBUG("Expected IP: " << GetSession()->GetIP()); DEBUG("Remote IP: " << GetRemoteIP()); PrintErrorPage(403, "Access denied", "This session does not belong to your IP."); return PAGE_DONE; } // Check that they really POSTed from one our forms by checking if they // know the "secret" CSRF check value. Don't do this for login since // CSRF against the login form makes no sense and the login form does a // cookies-enabled check which would break otherwise. if (IsPost() && GetParam("_CSRF_Check") != GetCSRFCheck() && sURI != "/login") { DEBUG("Expected _CSRF_Check: " << GetCSRFCheck()); DEBUG("Actual _CSRF_Check: " << GetParam("_CSRF_Check")); PrintErrorPage(403, "Access denied", "POST requests need to send " "a secret token to prevent cross-site request forgery attacks."); return PAGE_DONE; } SendCookie("SessionId", GetSession()->GetId()); if (GetSession()->IsLoggedIn()) { m_sUser = GetSession()->GetUser()->GetUserName(); m_bLoggedIn = true; } // Handle the static pages that don't require a login if (sURI == "/") { if(!m_bLoggedIn && GetParam("cookie_check", false).ToBool() && GetRequestCookie("SessionId").empty()) { GetSession()->AddError("Your browser does not have cookies enabled for this site!"); } return PrintTemplate("index", sPageRet); } else if (sURI == "/favicon.ico") { return PrintStaticFile("/pub/favicon.ico", sPageRet); } else if (sURI == "/robots.txt") { return PrintStaticFile("/pub/robots.txt", sPageRet); } else if (sURI == "/logout") { GetSession()->SetUser(NULL); SetLoggedIn(false); Redirect("/"); // We already sent a reply return PAGE_DONE; } else if (sURI == "/login") { if (GetParam("submitted").ToBool()) { m_sUser = GetParam("user"); m_sPass = GetParam("pass"); m_bLoggedIn = OnLogin(m_sUser, m_sPass); // AcceptedLogin()/RefusedLogin() will call Redirect() return PAGE_DEFERRED; } Redirect("/"); // the login form is here return PAGE_DONE; } else if (sURI.Left(5) == "/pub/") { return PrintStaticFile(sURI, sPageRet); } else if (sURI.Left(11) == "/skinfiles/") { CString sSkinName = sURI.substr(11); CString::size_type uPathStart = sSkinName.find("/"); if (uPathStart != CString::npos) { CString sFilePath = sSkinName.substr(uPathStart + 1); sSkinName.erase(uPathStart); m_Template.ClearPaths(); m_Template.AppendPath(GetSkinPath(sSkinName) + "pub"); if (PrintFile(m_Template.ExpandFile(sFilePath))) { return PAGE_DONE; } else { return PAGE_NOTFOUND; } } return PAGE_NOTFOUND; } else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") { ParsePath(); // Make sure modules are treated as directories if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) { Redirect(sURI + "/"); return PAGE_DONE; } CModule *pModule = CZNC::Get().GetModules().FindModule(m_sModName); if (!pModule) { // Check if GetSession()->GetUser() is NULL and display // an error in that case if (!ForceLogin()) return PAGE_DONE; pModule = GetSession()->GetUser()->GetModules().FindModule(m_sModName); } if (!pModule) { return PAGE_NOTFOUND; } else if (pModule->WebRequiresLogin() && !ForceLogin()) { return PAGE_PRINT; } else if (pModule->WebRequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this module"); return PAGE_DONE; } else if (!pModule->IsGlobal() && pModule->GetUser() != GetSession()->GetUser()) { PrintErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page"); return PAGE_DONE; } else if (pModule->OnWebPreRequest(*this, m_sPage)) { return PAGE_DEFERRED; } VWebSubPages& vSubPages = pModule->GetSubPages(); for (unsigned int a = 0; a < vSubPages.size(); a++) { TWebSubPage& SubPage = vSubPages[a]; bool bActive = (m_sModName == pModule->GetModName() && m_sPage == SubPage->GetName()); if (bActive && SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this page"); return PAGE_DONE; } } if (pModule && !pModule->IsGlobal() && (!IsLoggedIn() || pModule->GetUser() != GetSession()->GetUser())) { AddModLoop("UserModLoop", *pModule); } if (sURI.Left(10) == "/modfiles/") { m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/"); m_Template.AppendPath(pModule->GetModDataDir() + "/files/"); if (PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")))) { return PAGE_PRINT; } else { return PAGE_NOTFOUND; } } else { SetPaths(pModule, true); /* if a module returns false from OnWebRequest, it does not want the template to be printed, usually because it did a redirect. */ if (pModule->OnWebRequest(*this, m_sPage, m_Template)) { // If they already sent a reply, let's assume // they did what they wanted to do. if (SentHeader()) { return PAGE_DONE; } return PrintTemplate(m_sPage, sPageRet, pModule); } if (!SentHeader()) { PrintErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests"); } return PAGE_DONE; } } else { CString sPage(sURI.Trim_n("/")); if (sPage.length() < 32) { for (unsigned int a = 0; a < sPage.length(); a++) { unsigned char c = sPage[a]; if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') { return PAGE_NOTFOUND; } } return PrintTemplate(sPage, sPageRet); } } return PAGE_NOTFOUND; }