Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: znc/znc
base: bdc3bfe91887
...
head fork: znc/znc
compare: 1c8e9b9355ac
  • 10 commits
  • 32 files changed
  • 0 commit comments
  • 2 contributors
Commits on Feb 15, 2014
@uu1101 uu1101 Add CString::StartsWith and CString::EndsWith 7a9ce63
@uu1101 uu1101 The Listener of a RealListener is never NULL d3ddb9b
Commits on Feb 16, 2014
@uu1101 uu1101 Add URIPrefix listener option 88c85b0
@uu1101 uu1101 Add prefix on redirects and strip it on requests d796fc8
@uu1101 uu1101 Prepend URIPrefix to web interface links
Prefix links in templates with URIPrefix and add the input field to the
Listener editor.

The URIPrefix is provided as a top-level template variable. All URIs
have been changed to have the prefix prepended.
4376b37
@uu1101 uu1101 Use absolute URIs in Redirect
Although most browsers seem to accept relative redirects, it is not
allowed by HTTP/1.1.

This commit changes relative URIs to absolute ones when redirecting.
46975cd
@uu1101 uu1101 Don't support relative redirects c0974d9
@uu1101 uu1101 Redirect to URIPrefix for paths outside the prefix f6a881e
@uu1101 uu1101 Set favicon URL to respect the prefix
When not specified, browsers tend to send favicon request to
/favicon.ico. This commit tells the browsers to look for it under the
URIPrefix.
26a9d73
Commits on Feb 17, 2014
@DarthGandalf DarthGandalf Merge pull request #485 from uu1101/uriprefix
Allow serving the web interface under a subpath
Fix #480
Fix #138
1c8e9b9
Showing with 174 additions and 81 deletions.
  1. +4 −2 include/znc/HTTPSock.h
  2. +8 −4 include/znc/Listener.h
  3. +1 −1  include/znc/WebModules.h
  4. +11 −0 include/znc/ZNCString.h
  5. +2 −1  include/znc/znc.h
  6. +2 −2 modules/data/cert/tmpl/index.tmpl
  7. +1 −1  modules/data/certauth/tmpl/index.tmpl
  8. +2 −2 modules/data/lastseen/tmpl/index.tmpl
  9. +2 −2 modules/data/notes/tmpl/index.tmpl
  10. +1 −1  modules/data/perform/tmpl/index.tmpl
  11. +1 −1  modules/data/send_raw/tmpl/index.tmpl
  12. +1 −1  modules/data/stickychan/tmpl/index.tmpl
  13. +1 −1  modules/data/webadmin/tmpl/add_edit_chan.tmpl
  14. +3 −3 modules/data/webadmin/tmpl/add_edit_network.tmpl
  15. +3 −3 modules/data/webadmin/tmpl/add_edit_user.tmpl
  16. +2 −2 modules/data/webadmin/tmpl/del_network.tmpl
  17. +2 −2 modules/data/webadmin/tmpl/del_user.tmpl
  18. +4 −4 modules/data/webadmin/tmpl/listusers.tmpl
  19. +7 −4 modules/data/webadmin/tmpl/settings.tmpl
  20. +14 −10 modules/webadmin.cpp
  21. +5 −2 src/ClientCommand.cpp
  22. +27 −8 src/HTTPSock.cpp
  23. +7 −5 src/Listener.cpp
  24. +3 −1 src/WebModules.cpp
  25. +8 −0 src/ZNCString.cpp
  26. +24 −6 src/znc.cpp
  27. +6 −1 webskins/_default_/pub/_default_.css
  28. +3 −2 webskins/_default_/tmpl/BaseHeader.tmpl
  29. +2 −2 webskins/_default_/tmpl/LoginBar.tmpl
  30. +7 −7 webskins/_default_/tmpl/Menu.tmpl
  31. +5 −0 webskins/dark-clouds/pub/dark-clouds.css
  32. +5 −0 webskins/ice/pub/ice.css
View
6 include/znc/HTTPSock.h
@@ -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 @@ class CHTTPSock : public CSocket {
const CString& GetPass() const;
const CString& GetParamString() const;
const CString& GetContentType() const;
+ const CString& GetURIPrefix() const;
bool IsPost() const;
// !Getters
@@ -121,6 +122,7 @@ class CHTTPSock : public CSocket {
bool m_bAcceptGzip;
MCString m_msRequestCookies;
MCString m_msResponseCookies;
+ CString m_sURIPrefix;
};
#endif // !_HTTPSOCK_H
View
12 include/znc/Listener.h
@@ -32,11 +32,12 @@ class CListener {
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 @@ class CListener {
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 @@ class CListener {
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 @@ class CRealListener : public CZNCSock {
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
View
2  include/znc/WebModules.h
@@ -117,7 +117,7 @@ class CWebSock : public CHTTPSock {
PAGE_DONE // all stuff has been done
};
- CWebSock();
+ CWebSock(const CString& sURIPrefix);
virtual ~CWebSock();
virtual bool ForceLogin();
View
11 include/znc/ZNCString.h
@@ -478,6 +478,17 @@ class CString : public std::string {
*/
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.
View
3  include/znc/znc.h
@@ -154,7 +154,8 @@ class CZNC {
// 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*);
View
4 modules/data/cert/tmpl/index.tmpl
@@ -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>
View
2  modules/data/certauth/tmpl/index.tmpl
@@ -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>
View
4 modules/data/lastseen/tmpl/index.tmpl
@@ -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 ?>
View
4 modules/data/notes/tmpl/index.tmpl
@@ -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>
View
2  modules/data/perform/tmpl/index.tmpl
@@ -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>
View
2  modules/data/send_raw/tmpl/index.tmpl
@@ -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">
View
2  modules/data/stickychan/tmpl/index.tmpl
@@ -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>
View
2  modules/data/webadmin/tmpl/add_edit_chan.tmpl
@@ -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" />
View
6 modules/data/webadmin/tmpl/add_edit_network.tmpl
@@ -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>
View
6 modules/data/webadmin/tmpl/add_edit_user.tmpl
@@ -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>
View
4 modules/data/webadmin/tmpl/del_network.tmpl
@@ -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>
View
4 modules/data/webadmin/tmpl/del_user.tmpl
@@ -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>
View
8 modules/data/webadmin/tmpl/listusers.tmpl
@@ -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>
View
11 modules/data/webadmin/tmpl/settings.tmpl
@@ -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" />
View
24 modules/webadmin.cpp
@@ -101,6 +101,7 @@ class CWebAdminMod : public CModule {
CString sArgs(sArgStr);
CString sPort;
CString sListenHost;
+ CString sURIPrefix;
while (sArgs.Left(1) == "-") {
CString sOpt = sArgs.Token(0);
@@ -150,7 +151,7 @@ class CWebAdminMod : public CModule {
}
// 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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
}
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
}
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 @@ class CWebAdminMod : public CModule {
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 @@ class CWebAdminMod : public CModule {
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;
}
View
7 src/ClientCommand.cpp
@@ -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;
View
35 src/HTTPSock.cpp
@@ -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 [" << sURL << "]");
- AddHeader("Location", sURL);
- PrintErrorPage(302, "Found", "The document has moved <a href=\"" + sURL.Escape_n(CString::EHTML) + "\">here</a>.");
+ 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;
+ return true;
+ }
}
void CHTTPSock::Connected() {
View
12 src/Listener.cpp
@@ -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
View
4 src/WebModules.cpp
@@ -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) {
View
8 src/ZNCString.cpp
@@ -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;
View
30 src/znc.cpp
@@ -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) {
View
7 webskins/_default_/pub/_default_.css
@@ -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;
}
View
5 webskins/_default_/tmpl/BaseHeader.tmpl
@@ -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 ?>
View
4 webskins/_default_/tmpl/LoginBar.tmpl
@@ -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" />
View
14 webskins/_default_/tmpl/Menu.tmpl
@@ -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>
View
5 webskins/dark-clouds/pub/dark-clouds.css
@@ -167,6 +167,11 @@ input.third, textarea.third,
width: 100px;
}
+input.sixth, textarea.sixth,
+.sixth input, .sixth textarea {
+ width: 50px;
+}
+
.checkboxandlabel {
white-space: nowrap;
}
View
5 webskins/ice/pub/ice.css
@@ -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;
}

No commit comments for this range

Something went wrong with that request. Please try again.