Merge pull request #485 from uu1101/uriprefix

Allow serving the web interface under a subpath
Fix #480
Fix #138
This commit is contained in:
Alexey Sokolov
2014-02-17 00:44:37 +00:00
32 changed files with 175 additions and 82 deletions
+4 -2
View File
@@ -25,8 +25,8 @@ class CModule;
class CHTTPSock : public CSocket {
public:
CHTTPSock(CModule *pMod);
CHTTPSock(CModule *pMod, const CString& sHostname, unsigned short uPort, int iTimeout = 60);
CHTTPSock(CModule *pMod, const CString& sURIPrefix);
CHTTPSock(CModule *pMod, const CString& sURIPrefix, const CString& sHostname, unsigned short uPort, int iTimeout = 60);
virtual ~CHTTPSock();
// Csocket derived members
@@ -76,6 +76,7 @@ public:
const CString& GetPass() const;
const CString& GetParamString() const;
const CString& GetContentType() const;
const CString& GetURIPrefix() const;
bool IsPost() const;
// !Getters
@@ -121,6 +122,7 @@ protected:
bool m_bAcceptGzip;
MCString m_msRequestCookies;
MCString m_msResponseCookies;
CString m_sURIPrefix;
};
#endif // !_HTTPSOCK_H
+8 -4
View File
@@ -32,11 +32,12 @@ public:
ACCEPT_ALL
} EAcceptType;
CListener(unsigned short uPort, const CString& sBindHost, bool bSSL, EAddrType eAddr, EAcceptType eAccept) {
CListener(unsigned short uPort, const CString& sBindHost, const CString& sURIPrefix, bool bSSL, EAddrType eAddr, EAcceptType eAccept) {
m_uPort = uPort;
m_sBindHost = sBindHost;
m_bSSL = bSSL;
m_eAddr = eAddr;
m_sURIPrefix = sURIPrefix;
m_pListener = NULL;
m_eAcceptType = eAccept;
}
@@ -49,6 +50,7 @@ public:
unsigned short GetPort() const { return m_uPort; }
const CString& GetBindHost() const { return m_sBindHost; }
CRealListener* GetRealListener() const { return m_pListener; }
const CString& GetURIPrefix() const { return m_sURIPrefix; }
EAcceptType GetAcceptType() const { return m_eAcceptType; }
// !Getters
@@ -65,13 +67,14 @@ protected:
EAddrType m_eAddr;
unsigned short m_uPort;
CString m_sBindHost;
CString m_sURIPrefix;
CRealListener* m_pListener;
EAcceptType m_eAcceptType;
};
class CRealListener : public CZNCSock {
public:
CRealListener(CListener *pParent) : CZNCSock(), m_pParent(pParent) {}
CRealListener(CListener& listener) : CZNCSock(), m_Listener(listener) {}
virtual ~CRealListener();
virtual bool ConnectionFrom(const CString& sHost, unsigned short uPort);
@@ -79,18 +82,19 @@ public:
virtual void SockError(int iErrno, const CString& sDescription);
private:
CListener* m_pParent;
CListener& m_Listener;
};
class CIncomingConnection : public CZNCSock {
public:
CIncomingConnection(const CString& sHostname, unsigned short uPort, CListener::EAcceptType eAcceptType);
CIncomingConnection(const CString& sHostname, unsigned short uPort, CListener::EAcceptType eAcceptType, const CString& sURIPrefix);
virtual ~CIncomingConnection() {}
virtual void ReadLine(const CString& sData);
virtual void ReachedMaxBuffer();
private:
CListener::EAcceptType m_eAcceptType;
const CString m_sURIPrefix;
};
#endif // !_LISTENER_H
+1 -1
View File
@@ -117,7 +117,7 @@ public:
PAGE_DONE // all stuff has been done
};
CWebSock();
CWebSock(const CString& sURIPrefix);
virtual ~CWebSock();
virtual bool ForceLogin();
+11
View File
@@ -478,6 +478,17 @@ public:
*/
CString TrimSuffix_n(const CString& sSuffix) const;
/** Check whether the string starts with a given prefix.
* @param sPrefix The prefix.
* @return True if the string starts with prefix, false otherwise.
*/
bool StartsWith(const CString& sPrefix) const;
/** Check whether the string ends with a given suffix.
* @param sSuffix The suffix.
* @return True if the string ends with suffix, false otherwise.
*/
bool EndsWith(const CString& sSuffix) const;
/** Remove characters from the beginning of this string.
* @param uLen The number of characters to remove.
* @return true if this string was modified.
+2 -1
View File
@@ -154,7 +154,8 @@ public:
// Listener yummy
CListener* FindListener(u_short uPort, const CString& BindHost, EAddrType eAddr);
bool AddListener(CListener*);
bool AddListener(unsigned short uPort, const CString& sBindHost, bool bSSL,
bool AddListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL,
EAddrType eAddr, CListener::EAcceptType eAccept, CString& sError);
bool DelListener(CListener*);
+2 -2
View File
@@ -1,12 +1,12 @@
<? INC Header.tmpl ?>
<? IF Cert ?>
<p>You already have a certificate set, use the form below to overwrite the current certificate. Alternatively click <a href="<? VAR ModPath ?>delete">here</a> to <a href="<? VAR ModPath ?>delete">delete</a> your certificate.</p>
<p>You already have a certificate set, use the form below to overwrite the current certificate. Alternatively click <a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?>delete">here</a> to <a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?>delete">delete</a> your certificate.</p>
<? ELSE ?>
<p>You do not have a cert.</p>
<? ENDIF ?>
<form method="post" action="<? VAR ModPath ?>update">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>update">
<? INC _csrf_check.tmpl ?>
<div class="section">
<h3>Certificate</h3>
+1 -1
View File
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form method="post" action="<? VAR ModPath ?>add">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>add">
<? INC _csrf_check.tmpl ?>
<div class="section">
<h3>Add A Note</h3>
+2 -2
View File
@@ -17,8 +17,8 @@
<td><? VAR LastSeen DEFAULT="- unknown -" ?></td>
<td><? VAR Info ?></td>
<? IF WebAdminLoaded TOP ?><td><span class="nowrap">
[<a href="/mods/global/webadmin/edituser?user=<?VAR Username ESC=URL?>">Edit</a>]
<? IF !IsSelf ?>[<a href="/mods/global/webadmin/deluser?user=<?VAR Username ESC=URL?>">Delete</a>]<? ENDIF ?>
[<a href="<? VAR URIPrefix TOP ?>/mods/global/webadmin/edituser?user=<?VAR Username ESC=URL?>">Edit</a>]
<? IF !IsSelf ?>[<a href="<? VAR URIPrefix TOP ?>/mods/global/webadmin/deluser?user=<?VAR Username ESC=URL?>">Delete</a>]<? ENDIF ?>
</span></td><? ENDIF ?>
</tr>
<? ENDLOOP ?>
+2 -2
View File
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form method="post" action="<? VAR ModPath ?>addnote">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>addnote">
<? INC _csrf_check.tmpl ?>
<div class="section">
<h3>Add A Note</h3>
@@ -37,7 +37,7 @@
<tbody>
<? LOOP NotesLoop ?>
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
<td><a href="<? VAR ModPath TOP ?>delnote?key=<? VAR Key ESC=URL,HTML ?>"><img src="<? VAR ModFilesPath TOP ?>trash.gif" alt="[del]" /></a></td>
<td><a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>delnote?key=<? VAR Key ESC=URL,HTML ?>"><img src="<? VAR URIPrefix TOP ?><? VAR ModFilesPath TOP ?>trash.gif" alt="[del]" /></a></td>
<td><? VAR Key ?></td>
<td><? VAR Note ?></td>
</tr>
+1 -1
View File
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form method="post" action="<? VAR ModPath ?>">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>">
<? INC _csrf_check.tmpl ?>
<div class="section">
<h3>Perform</h3>
+1 -1
View File
@@ -1,7 +1,7 @@
<? ADDROW JSLoop HREF=/modfiles/user/send_raw/select.js ?>
<? INC Header.tmpl ?>
<form method="post" action="<? VAR ModPath ?>">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>">
<? INC _csrf_check.tmpl ?>
<div class="section">
+1 -1
View File
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form action="" method="post">
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>">
<? INC _csrf_check.tmpl ?>
<table class="data">
<thead>
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form action="<? IF Edit ?>editchan<? ELSE ?>addchan<? ENDIF ?>" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?><? IF Edit ?>editchan<? ELSE ?>addchan<? ENDIF ?>" method="post">
<? INC _csrf_check.tmpl ?>
<div class="section">
<input type="hidden" name="submitted" value="1" />
@@ -5,7 +5,7 @@
<p>To connect to this network from your IRC client, you can set the server password field as follows: <code><? VAR Username ?>/<? IF Edit ?><? VAR Name ?><? ELSE ?>&lt;network&gt;<? ENDIF ?>:&lt;password&gt;</code> or username field as <code><? VAR Username ?>/<? IF Edit ?><? VAR Name ?><? ELSE ?>&lt;network&gt;<? ENDIF ?></code></p>
</div>
<form action="<? IF Edit ?>editnetwork<? ELSE ?>addnetwork<? ENDIF ?>" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?><? IF Edit ?>editnetwork<? ELSE ?>addnetwork<? ENDIF ?>" method="post">
<? INC _csrf_check.tmpl ?>
<div class="section">
<input type="hidden" name="submitted" value="1" />
@@ -116,7 +116,7 @@
<table>
<thead>
<tr>
<td>[<a href="addchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Name ESC=URL ?>">Add</a>]</td>
<td>[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>addchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Name ESC=URL ?>">Add</a>]</td>
<? IF ChannelLoop ?>
<td>Save</td>
<td>Name</td>
@@ -135,7 +135,7 @@
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
<td>
<input type="hidden" name="channel" value="<? VAR Name ?>" />
[<a href="editchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Network ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="delchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Network ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Del</a>]
[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>editchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Network ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>delchan?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Network ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Del</a>]
</td>
<td><input type="checkbox" name="save_<? VAR Name ?>"<? IF InConfig ?> checked="checked"<? ENDIF ?> /></td>
<td><? VAR Name ?></td>
@@ -1,6 +1,6 @@
<? INC Header.tmpl ?>
<form action="<? IF Edit ?>edituser<? ELSE ?>adduser<? ENDIF ?>" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?><? IF Edit ?>edituser<? ELSE ?>adduser<? ENDIF ?>" method="post">
<? INC _csrf_check.tmpl ?>
<div class="section">
<input type="hidden" name="submitted" value="1" />
@@ -128,7 +128,7 @@
<table>
<thead>
<tr>
<td>[<a href="addnetwork?user=<? VAR Username ESC=URL ?>">Add</a>]</td>
<td>[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>addnetwork?user=<? VAR Username ESC=URL ?>">Add</a>]</td>
<? IF NetworkLoop ?>
<td>Name</td>
<td>Clients</td>
@@ -145,7 +145,7 @@
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
<td>
<input type="hidden" name="network" value="<? VAR Name ?>" />
[<a href="editnetwork?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="delnetwork?user=<? VAR Username ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Del</a>]
[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>editnetwork?user=<? VAR Username ESC=URL ?>&amp;network=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>delnetwork?user=<? VAR Username ESC=URL ?>&amp;name=<? VAR Name ESC=URL ?>">Del</a>]
</td>
<td><? VAR Name ?></td>
<td><? VAR Clients ?></td>
+2 -2
View File
@@ -6,13 +6,13 @@
<div class="sectionbody">
<div class="subsection">
Are you sure you want to delete "<? VAR Username ?>/<? VAR Network ?>"?
<form action="delnetwork" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>delnetwork" method="post">
<? INC _csrf_check.tmpl ?>
<input type="hidden" name="user" value="<? VAR Username ?>" />
<input type="hidden" name="name" value="<? VAR Network ?>" />
<div class="yesnobutton"><input type="submit" value="Yes" /></div>
</form>
<form action="listusers" method="get">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>listusers" method="get">
<div class="yesnobutton"><input type="submit" value="No" /></div>
</form>
</div>
+2 -2
View File
@@ -6,13 +6,13 @@
<div class="sectionbody">
<div class="subsection">
Are you sure you want to delete "<? VAR Username ?>"?
<form action="deluser" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>deluser" method="post">
<? INC _csrf_check.tmpl ?>
<input type="hidden" name="submitted" value="1" />
<input type="hidden" name="user" value="<? VAR Username ?>" />
<div class="yesnobutton"><input type="submit" value="Yes" /></div>
</form>
<form action="listusers" method="get">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>listusers" method="get">
<div class="yesnobutton"><input type="submit" value="No" /></div>
</form>
</div>
+4 -4
View File
@@ -2,7 +2,7 @@
<?IF !UserLoop?>
<div class="textsection">
There are no users defined. Click <a href="adduser">here</a> if you would like to add one.
There are no users defined. Click <a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>adduser">here</a> if you would like to add one.
</div>
<?ELSE?>
<div class="toptable">
@@ -21,9 +21,9 @@
<tr class="<?IF __EVEN__?>evenrow<?ELSE?>oddrow<?ENDIF?>">
<td>
<span class="nowrap">
[<a href="edituser?user=<?VAR Username ESC=URL?>">Edit</a>]
[<a href="adduser?clone=<? VAR Username ESC=URL ?>" alt="Clone <? VAR Username ESC=URL ?>">Clone</a>]
<? IF !IsSelf ?>[<a href="deluser?user=<?VAR Username ESC=URL?>">Delete</a>]<? ENDIF ?>
[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>edituser?user=<?VAR Username ESC=URL?>">Edit</a>]
[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>adduser?clone=<? VAR Username ESC=URL ?>" alt="Clone <? VAR Username ESC=URL ?>">Clone</a>]
<? IF !IsSelf ?>[<a href="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>deluser?user=<?VAR Username ESC=URL?>">Delete</a>]<? ENDIF ?>
</span>
</td>
<td><? VAR Username ?></td>
+7 -4
View File
@@ -16,6 +16,7 @@
<td>IPv6</td>
<td>IRC</td>
<td>Web</td>
<td>URIPrefix</td>
</tr>
</thead>
@@ -38,9 +39,10 @@
<td>
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsWeb ?>checked="checked"<? ENDIF ?>/></div>
</td>
<td><? VAR URIPrefix ?></td>
<td>
<? IF SuggestDeletion ?>
<form action="del_listener" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>del_listener" method="post">
<? INC _csrf_check.tmpl ?>
<input name="host" type="hidden" value="<? VAR BindHost ?>"/>
<input name="port" type="hidden" value="<? VAR Port ?>"/>
@@ -53,15 +55,16 @@
</tr>
<? ENDLOOP ?>
<tr>
<form action="add_listener" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>add_listener" method="post">
<? INC _csrf_check.tmpl ?>
<td><input name="port" type="number" min="1" max="65535" class="number"/></td>
<td><input name="host" type="text" value="*" class="third"/></td>
<td><input name="host" type="text" value="*" class="sixth"/></td>
<td><div class="checkbox"><input name="ssl" type="checkbox"/></div></td>
<td><div class="checkbox"><input name="ipv4" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="ipv6" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="irc" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="web" type="checkbox" checked="checked"/></div></td>
<td><input name="uriprefix" type="text" value="" class="sixth"/></td>
<td><input type="submit" value="Add"/></td>
</form>
</tr>
@@ -71,7 +74,7 @@
</div>
</div>
<form action="settings" method="post">
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>settings" method="post">
<? INC _csrf_check.tmpl ?>
<input type="hidden" name="submitted" value="1" />
+14 -10
View File
@@ -101,6 +101,7 @@ public:
CString sArgs(sArgStr);
CString sPort;
CString sListenHost;
CString sURIPrefix;
while (sArgs.Left(1) == "-") {
CString sOpt = sArgs.Token(0);
@@ -150,7 +151,7 @@ public:
}
// Now turn that into a listener instance
CListener *pListener = new CListener(uPort, sListenHost, bSSL,
CListener *pListener = new CListener(uPort, sListenHost, sURIPrefix, bSSL,
(!bIPv6 ? ADDR_IPV4ONLY : ADDR_ALL), CListener::ACCEPT_HTTP);
if (!pListener->Listen()) {
@@ -542,7 +543,7 @@ public:
WebSock.PrintErrorPage("Please don't delete yourself, suicide is not the answer!");
return true;
} else if (CZNC::Get().DeleteUser(sUser)) {
WebSock.Redirect("listusers");
WebSock.Redirect(GetWebPath() + "listusers");
return true;
}
@@ -710,7 +711,7 @@ public:
return true;
}
WebSock.Redirect("editnetwork?user=" + pUser->GetUserName().Escape_n(CString::EURL) + "&network=" + pNetwork->GetName().Escape_n(CString::EURL));
WebSock.Redirect(GetWebPath() + "editnetwork?user=" + pUser->GetUserName().Escape_n(CString::EURL) + "&network=" + pNetwork->GetName().Escape_n(CString::EURL));
return true;
}
@@ -984,7 +985,7 @@ public:
return true;
}
WebSock.Redirect("edituser?user=" + pUser->GetUserName().Escape_n(CString::EURL));
WebSock.Redirect(GetWebPath() + "edituser?user=" + pUser->GetUserName().Escape_n(CString::EURL));
return true;
}
@@ -1020,7 +1021,7 @@ public:
return true;
}
WebSock.Redirect("edituser?user=" + pUser->GetUserName().Escape_n(CString::EURL));
WebSock.Redirect(GetWebPath() + "edituser?user=" + pUser->GetUserName().Escape_n(CString::EURL));
return false;
}
@@ -1040,7 +1041,7 @@ public:
return true;
}
WebSock.Redirect("editnetwork?user=" + pNetwork->GetUser()->GetUserName().Escape_n(CString::EURL) + "&network=" + pNetwork->GetName().Escape_n(CString::EURL));
WebSock.Redirect(GetWebPath() + "editnetwork?user=" + pNetwork->GetUser()->GetUserName().Escape_n(CString::EURL) + "&network=" + pNetwork->GetName().Escape_n(CString::EURL));
return false;
}
@@ -1320,9 +1321,9 @@ public:
}
if (!spSession->IsAdmin()) {
WebSock.Redirect("edituser");
WebSock.Redirect(GetWebPath() + "edituser");
} else {
WebSock.Redirect("listusers");
WebSock.Redirect(GetWebPath() + "listusers");
}
/* we don't want the template to be printed while we redirect */
@@ -1420,6 +1421,7 @@ public:
bool AddListener(CWebSock& WebSock, CTemplate& Tmpl) {
unsigned short uPort = WebSock.GetParam("port").ToUShort();
CString sHost = WebSock.GetParam("host");
CString sURIPrefix = WebSock.GetParam("uriprefix");
if (sHost == "*") sHost = "";
bool bSSL = WebSock.GetParam("ssl").ToBool();
bool bIPv4 = WebSock.GetParam("ipv4").ToBool();
@@ -1460,7 +1462,7 @@ public:
}
CString sMessage;
if (CZNC::Get().AddListener(uPort, sHost, bSSL, eAddr, eAccept, sMessage)) {
if (CZNC::Get().AddListener(uPort, sHost, sURIPrefix, bSSL, eAddr, eAccept, sMessage)) {
if (!sMessage.empty()) {
WebSock.GetSession()->AddSuccess(sMessage);
}
@@ -1545,6 +1547,8 @@ public:
l["IsWeb"] = CString(pListener->GetAcceptType() != CListener::ACCEPT_IRC);
l["IsIRC"] = CString(pListener->GetAcceptType() != CListener::ACCEPT_HTTP);
l["URIPrefix"] = pListener->GetURIPrefix() + "/";
// simple protection for user from shooting his own foot
// TODO check also for hosts/families
// such check is only here, user still can forge HTTP request to delete web port
@@ -1688,7 +1692,7 @@ public:
WebSock.GetSession()->AddError("Settings changed, but config was not written");
}
WebSock.Redirect("settings");
WebSock.Redirect(GetWebPath() + "settings");
/* we don't want the template to be printed while we redirect */
return false;
}
+5 -2
View File
@@ -1430,6 +1430,7 @@ void CClient::UserPortCommand(CString& sLine) {
Table.AddColumn("SSL");
Table.AddColumn("Proto");
Table.AddColumn("IRC/Web");
Table.AddColumn("URIPrefix");
vector<CListener*>::const_iterator it;
const vector<CListener*>& vpListeners = CZNC::Get().GetListeners();
@@ -1445,6 +1446,7 @@ void CClient::UserPortCommand(CString& sLine) {
CListener::EAcceptType eAccept = (*it)->GetAcceptType();
Table.SetCell("IRC/Web", (eAccept == CListener::ACCEPT_ALL ? "All" : (eAccept == CListener::ACCEPT_IRC ? "IRC" : "Web")));
Table.SetCell("URIPrefix", (*it)->GetURIPrefix() + "/");
}
PutStatus(Table);
@@ -1483,12 +1485,13 @@ void CClient::UserPortCommand(CString& sLine) {
}
if (sPort.empty() || sAddr.empty() || sAccept.empty()) {
PutStatus("Usage: AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> [bindhost]");
PutStatus("Usage: AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> [bindhost [uriprefix]]");
} else {
bool bSSL = (sPort.Left(1).Equals("+"));
const CString sBindHost = sLine.Token(4);
const CString sURIPrefix = sLine.Token(5);
CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
CListener* pListener = new CListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
if (!pListener->Listen()) {
delete pListener;
+28 -9
View File
@@ -28,11 +28,11 @@ using std::set;
#define MAX_POST_SIZE 1024 * 1024
CHTTPSock::CHTTPSock(CModule *pMod) : CSocket(pMod) {
CHTTPSock::CHTTPSock(CModule *pMod, const CString& sURIPrefix) : CSocket(pMod), m_sURIPrefix(sURIPrefix) {
Init();
}
CHTTPSock::CHTTPSock(CModule *pMod, const CString& sHostname, unsigned short uPort, int iTimeout) : CSocket(pMod, sHostname, uPort, iTimeout) {
CHTTPSock::CHTTPSock(CModule *pMod, const CString& sURIPrefix, const CString& sHostname, unsigned short uPort, int iTimeout) : CSocket(pMod, sHostname, uPort, iTimeout), m_sURIPrefix(sURIPrefix) {
Init();
}
@@ -214,7 +214,16 @@ CString CHTTPSock::GetDate(time_t stamp) {
void CHTTPSock::GetPage() {
DEBUG("Page Request [" << m_sURI << "] ");
OnPageRequest(m_sURI);
// Check that the requested path starts with the prefix. Strip it if so.
if (!m_sURI.TrimPrefix(m_sURIPrefix)) {
DEBUG("INVALID path => Does not start with prefix [" + m_sURIPrefix + "]");
DEBUG("Expected prefix: " << m_sURIPrefix);
DEBUG("Requested path: " << m_sURI);
Redirect(m_sURI);
} else {
OnPageRequest(m_sURI);
}
}
#ifdef HAVE_ZLIB
@@ -495,6 +504,10 @@ const CString& CHTTPSock::GetParamString() const {
return m_sPostData;
}
const CString& CHTTPSock::GetURIPrefix() const {
return m_sURIPrefix;
}
bool CHTTPSock::HasParam(const CString& sName, bool bPost) const {
if (bPost)
return (m_msvsPOSTParams.find(sName) != m_msvsPOSTParams.end());
@@ -712,13 +725,19 @@ bool CHTTPSock::Redirect(const CString& sURL) {
if (SentHeader()) {
DEBUG("Redirect() - Header was already sent");
return false;
} else if(!sURL.StartsWith("/")) {
// HTTP/1.1 only admits absolute URIs for the Location header.
DEBUG("Redirect to relative URI [" + sURL + "] is not allowed.");
return false;
} else {
CString location = m_sURIPrefix + sURL;
DEBUG("- Redirect to [" << location << "] with prefix [" + m_sURIPrefix + "]");
AddHeader("Location", location);
PrintErrorPage(302, "Found", "The document has moved <a href=\"" + location.Escape_n(CString::EHTML) + "\">here</a>.");
return true;
}
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;
}
void CHTTPSock::Connected() {
+7 -5
View File
@@ -27,7 +27,7 @@ bool CListener::Listen() {
return false;
}
m_pListener = new CRealListener(this);
m_pListener = new CRealListener(*this);
bool bSSL = false;
#ifdef HAVE_LIBSSL
@@ -50,7 +50,7 @@ void CListener::ResetRealListener() {
}
CRealListener::~CRealListener() {
m_pParent->ResetRealListener();
m_Listener.ResetRealListener();
}
bool CRealListener::ConnectionFrom(const CString& sHost, unsigned short uPort) {
@@ -60,7 +60,9 @@ bool CRealListener::ConnectionFrom(const CString& sHost, unsigned short uPort) {
}
Csock* CRealListener::GetSockObj(const CString& sHost, unsigned short uPort) {
CIncomingConnection *pClient = new CIncomingConnection(sHost, uPort, m_pParent->GetAcceptType());
CIncomingConnection *pClient = new CIncomingConnection(sHost, uPort,
m_Listener.GetAcceptType(),
m_Listener.GetURIPrefix());
if (CZNC::Get().AllowConnectionFrom(sHost)) {
GLOBALMODULECALL(OnClientConnect(pClient, sHost, uPort), NOTHING);
} else {
@@ -83,7 +85,7 @@ void CRealListener::SockError(int iErrno, const CString& sDescription) {
}
}
CIncomingConnection::CIncomingConnection(const CString& sHostname, unsigned short uPort, CListener::EAcceptType eAcceptType) : CZNCSock(sHostname, uPort) {
CIncomingConnection::CIncomingConnection(const CString& sHostname, unsigned short uPort, CListener::EAcceptType eAcceptType, const CString& sURIPrefix) : CZNCSock(sHostname, uPort), m_sURIPrefix(sURIPrefix) {
m_eAcceptType = eAcceptType;
// The socket will time out in 120 secs, no matter what.
// This has to be fixed up later, if desired.
@@ -141,7 +143,7 @@ void CIncomingConnection::ReadLine(const CString& sLine) {
return;
}
pSock = new CWebSock();
pSock = new CWebSock(m_sURIPrefix);
CZNC::Get().GetManager().SwapSockByAddr(pSock, this);
// And don't forget to give it some sane name / timeout
+3 -1
View File
@@ -185,7 +185,7 @@ void CWebAuth::Invalidate() {
m_pWebSock = NULL;
}
CWebSock::CWebSock() : CHTTPSock(NULL) {
CWebSock::CWebSock(const CString& sURIPrefix) : CHTTPSock(NULL, sURIPrefix) {
m_bPathsSet = false;
m_Template.AddTagHandler(new CZNCTagHandler(*this));
@@ -325,6 +325,7 @@ void CWebSock::SetVars() {
m_Template["Version"] = CZNC::GetVersion();
m_Template["SkinName"] = GetSkinName();
m_Template["_CSRF_Check"] = GetCSRFCheck();
m_Template["URIPrefix"] = GetURIPrefix();
if (GetSession()->IsAdmin()) {
m_Template["IsAdmin"] = "true";
@@ -473,6 +474,7 @@ CWebSock::EPageReqResult CWebSock::PrintStaticFile(const CString& sPath, CString
CWebSock::EPageReqResult CWebSock::PrintTemplate(const CString& sPageName, CString& sPageRet, CModule* pModule) {
SetVars();
m_Template["PageName"] = sPageName;
if (pModule) {
+8
View File
@@ -1092,6 +1092,14 @@ bool CString::TrimSuffix(const CString& sSuffix) {
}
}
bool CString::StartsWith(const CString& sPrefix) const {
return Left(sPrefix.length()).Equals(sPrefix);
}
bool CString::EndsWith(const CString& sSuffix) const {
return Right(sSuffix.length()).Equals(sSuffix);
}
CString CString::TrimPrefix_n(const CString& sPrefix) const {
CString sRet = *this;
+24 -6
View File
@@ -450,6 +450,7 @@ bool CZNC::WriteConfig() {
CConfig listenerConfig;
listenerConfig.AddKeyValuePair("Host", pListener->GetBindHost());
listenerConfig.AddKeyValuePair("URIPrefix", pListener->GetURIPrefix() + "/");
listenerConfig.AddKeyValuePair("Port", CString(pListener->GetPort()));
listenerConfig.AddKeyValuePair("IPv4", CString(pListener->GetAddrType() != ADDR_IPV6ONLY));
@@ -578,6 +579,7 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) {
bool b6 = false;
#endif
CString sListenHost;
CString sURIPrefix;
bool bListenSSL = false;
unsigned int uListenPort = 0;
bool bSuccess;
@@ -606,7 +608,7 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) {
CUtils::GetInput("Listen Host", sListenHost, sListenHost, "Blank for all ips");
CUtils::PrintAction("Verifying the listener");
CListener* pListener = new CListener((unsigned short int)uListenPort, sListenHost, bListenSSL,
CListener* pListener = new CListener((unsigned short int)uListenPort, sListenHost, sURIPrefix, bListenSSL,
b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL);
if (!pListener->Listen()) {
CUtils::PrintStatus(false, FormatBindError());
@@ -1689,12 +1691,15 @@ bool CZNC::AddListener(const CString& sLine, CString& sError) {
bSSL = true;
}
// No support for URIPrefix for old-style configs.
CString sURIPrefix;
unsigned short uPort = sPort.ToUShort();
return AddListener(uPort, sBindHost, bSSL, eAddr, eAccept, sError);
return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept, sError);
}
bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost, bool bSSL,
EAddrType eAddr, CListener::EAcceptType eAccept, CString& sError) {
bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefixRaw, bool bSSL,
EAddrType eAddr, CListener::EAcceptType eAccept, CString& sError) {
CString sHostComment;
if (!sBindHost.empty()) {
@@ -1755,7 +1760,18 @@ bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost, bool bSSL
return false;
}
CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
// URIPrefix must start with a slash and end without one.
CString sURIPrefix = CString(sURIPrefixRaw);
if(!sURIPrefix.empty()) {
if (!sURIPrefix.StartsWith("/")) {
sURIPrefix = "/" + sURIPrefix;
}
if (sURIPrefix.EndsWith("/")) {
sURIPrefix.TrimRight("/");
}
}
CListener* pListener = new CListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
if (!pListener->Listen()) {
sError = FormatBindError();
@@ -1772,6 +1788,7 @@ bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost, bool bSSL
bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
CString sBindHost;
CString sURIPrefix;
bool bSSL;
bool b4;
#ifdef HAVE_IPV6
@@ -1793,6 +1810,7 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
pConfig->FindBoolEntry("ipv6", b6, b6);
pConfig->FindBoolEntry("allowirc", bIRC, true);
pConfig->FindBoolEntry("allowweb", bWeb, true);
pConfig->FindStringEntry("uriprefix", sURIPrefix);
EAddrType eAddr;
if (b4 && b6) {
@@ -1820,7 +1838,7 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
return false;
}
return AddListener(uPort, sBindHost, bSSL, eAddr, eAccept, sError);
return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept, sError);
}
bool CZNC::AddListener(CListener* pListener) {
+6 -1
View File
@@ -247,6 +247,11 @@ input.third, textarea.third,
width: 150px;
}
input.sixth, textarea.sixth,
.sixth input, .sixth textarea {
width: 75px;
}
input.number {
width: 40px;
}
@@ -299,7 +304,7 @@ tr.evenrow td {
.subsection input, .subsection select, .subsection textarea {
margin: 5px 0 5px 0;
min-width: 100px;
min-width: 75px;
vertical-align: middle;
}
+3 -2
View File
@@ -5,12 +5,13 @@
<head>
<meta charset="UTF-8" />
<title>ZNC - <? VAR Title DEFAULT="Web Frontend" ?></title>
<link rel="shortcut icon" href="<? VAR URIPrefix TOP ?>/favicon.ico" type="image/x-icon" />
<? LOOP CSSLoop ?>
<?IF HREF?><link rel="stylesheet" type="text/css" href="<? VAR HREF ?>" /><?ENDIF?>
<?IF HREF?><link rel="stylesheet" type="text/css" href="<? VAR URIPrefix TOP ?><? VAR HREF ?>" /><?ENDIF?>
<?ENDLOOP?>
<? LOOP JSLoop ?>
<?IF HREF?><script type="text/javascript" src="<? VAR HREF ?>"></script><?ENDIF?>
<?IF HREF?><script type="text/javascript" src="<? VAR URIPrefix TOP ?><? VAR HREF ?>"></script><?ENDIF?>
<?ENDLOOP?>
<? INC ExtraHeader.tmpl ?>
+2 -2
View File
@@ -1,9 +1,9 @@
<? IF LoggedIn ?>
<span class="logoutbox"><a href="/logout">Logout</a></span>
<span class="logoutbox"><a href="<? VAR URIPrefix TOP ?>/logout">Logout</a></span>
<? ELSE IF !ModName && PageName == "login" ?>
<span class="logoutbox">&nbsp;</span>
<? ELSE ?>
<form action="/login" method="post">
<form action="<? VAR URIPrefix TOP ?>/login" method="post">
<div class="loginbox">
<input type="hidden" name="submitted" value="1" />
User:<input type="text" name="user" />
+7 -7
View File
@@ -1,14 +1,14 @@
<div id="menu">
<ul class="nav">
<li class="topitem<? IF !ModName && PageName == "index" ?> active<? ENDIF ?>"><a href="/">Home</a></li>
<li class="topitem<? IF !ModName && PageName == "index" ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?>/">Home</a></li>
<? IF GlobalModLoop ?>
<li class="topitem parent"><span class="title">Global Modules</span>
<ul>
<? LOOP GlobalModLoop SORTASC=ModName ?>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?>"><? VAR Title ?></a></li>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?>"><? VAR Title ?></a></li>
<? LOOP SubPageLoop ?>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<? ENDLOOP ?>
<? ENDLOOP ?>
</ul>
@@ -19,9 +19,9 @@
<li class="topitem parent"><span class="title">User Modules</span>
<ul>
<? LOOP UserModLoop SORTASC=ModName ?>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?>"><? VAR Title ?></a></li>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?>"><? VAR Title ?></a></li>
<? LOOP SubPageLoop ?>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<? ENDLOOP ?>
<? ENDLOOP ?>
</ul>
@@ -34,9 +34,9 @@
<li class="topitem parent"><span class="title">Network Modules (<? VAR NetworkName ?>)</span>
<ul>
<? LOOP ModLoop SORTASC=ModName ?>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?>"><? VAR Title ?></a></li>
<li class="modtitle<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?>"><? VAR Title ?></a></li>
<? LOOP SubPageLoop ?>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<li class="subitem<? IF Active ?> active<? ENDIF ?>"><a href="<? VAR URIPrefix TOP ?><? VAR ModPath ?><? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? VAR Title ?></a></li>
<? ENDLOOP ?>
<? ENDLOOP ?>
</ul>
+5
View File
@@ -167,6 +167,11 @@ input.third, textarea.third,
width: 100px;
}
input.sixth, textarea.sixth,
.sixth input, .sixth textarea {
width: 50px;
}
.checkboxandlabel {
white-space: nowrap;
}
+5
View File
@@ -141,6 +141,11 @@ input.third, textarea.third,
width: 125px;
}
input.sixth, textarea.sixth,
.sixth input, .sixth textarea {
width: 62px;
}
.subsection div.checkbox {
padding: 9px 0 0 3px;
}