diff --git a/Makefile.am b/Makefile.am index 31a9c43..83b1c02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,7 +153,7 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css @rm -f "$@" - ln "$<" "$@" + sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$<" >"$@" ${top_srcdir}/demo/usercss-0.css: ${top_srcdir}/shellinabox/white-on-black.css @rm -f "$@" diff --git a/Makefile.in b/Makefile.in index 9602b5b..88bff5f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1086,7 +1086,7 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css @rm -f "$@" - ln "$<" "$@" + sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$<" >"$@" ${top_srcdir}/demo/usercss-0.css: ${top_srcdir}/shellinabox/white-on-black.css @rm -f "$@" diff --git a/config.h b/config.h index ec31435..895a918 100644 --- a/config.h +++ b/config.h @@ -138,7 +138,7 @@ #define STDC_HEADERS 1 /* Most recent revision number in the version control system */ -#define VCS_REVISION "171" +#define VCS_REVISION "172" /* Version number of package */ #define VERSION "2.9" diff --git a/configure b/configure index 932a58b..b399573 100755 --- a/configure +++ b/configure @@ -2317,7 +2317,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu -VCS_REVISION=171 +VCS_REVISION=172 cat >>confdefs.h <<_ACEOF diff --git a/configure.ac b/configure.ac index d54462c..b803160 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ(2.57) dnl This is the one location where the authoritative version number is stored AC_INIT(shellinabox, 2.9, markus@shellinabox.com) -VCS_REVISION=171 +VCS_REVISION=172 AC_SUBST(VCS_REVISION) AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", [Most recent revision number in the version control system]) diff --git a/demo/styles.css b/demo/styles.css index 6e465a1..b1b5b6f 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -135,39 +135,6 @@ #vt100 #scrollable.inverted { color: #ffffff; background-color: #000000; } -#vt100 .ansi0 { } -#vt100 .ansi1 { color: #cd0000; } -#vt100 .ansi2 { color: #00cd00; } -#vt100 .ansi3 { color: #cdcd00; } -#vt100 .ansi4 { color: #0000ee; } -#vt100 .ansi5 { color: #cd00cd; } -#vt100 .ansi6 { color: #00cdcd; } -#vt100 .ansi7 { color: #e5e5e5; } -#vt100 .ansi8 { color: #7f7f7f; } -#vt100 .ansi9 { color: #ff0000; } -#vt100 .ansi10 { color: #00ff00; } -#vt100 .ansi11 { color: #e8e800; } -#vt100 .ansi12 { color: #5c5cff; } -#vt100 .ansi13 { color: #ff00ff; } -#vt100 .ansi14 { color: #00ffff; } -#vt100 .ansi15 { color: #ffffff; } - -#vt100 .bgAnsi0 { background-color: #000000; } -#vt100 .bgAnsi1 { background-color: #cd0000; } -#vt100 .bgAnsi2 { background-color: #00cd00; } -#vt100 .bgAnsi3 { background-color: #cdcd00; } -#vt100 .bgAnsi4 { background-color: #0000ee; } -#vt100 .bgAnsi5 { background-color: #cd00cd; } -#vt100 .bgAnsi6 { background-color: #00cdcd; } -#vt100 .bgAnsi7 { background-color: #e5e5e5; } -#vt100 .bgAnsi8 { background-color: #7f7f7f; } -#vt100 .bgAnsi9 { background-color: #ff0000; } -#vt100 .bgAnsi10 { background-color: #00ff00; } -#vt100 .bgAnsi11 { background-color: #e8e800; } -#vt100 .bgAnsi12 { background-color: #5c5cff; } -#vt100 .bgAnsi13 { background-color: #ff00ff; } -#vt100 .bgAnsi14 { background-color: #00ffff; } -#vt100 .bgAnsi15 { } @media print { #vt100 .scrollback { diff --git a/demo/usercss-2.css b/demo/usercss-2.css index b5c60a5..9863105 100644 --- a/demo/usercss-2.css +++ b/demo/usercss-2.css @@ -13,17 +13,17 @@ #vt100 .ansi13 { color: inherit; } #vt100 .ansi14 { color: inherit; } -#vt100 .bgAnsi1 { background-color: inherit; } -#vt100 .bgAnsi2 { background-color: inherit; } -#vt100 .bgAnsi3 { background-color: inherit; } -#vt100 .bgAnsi4 { background-color: inherit; } -#vt100 .bgAnsi5 { background-color: inherit; } -#vt100 .bgAnsi6 { background-color: inherit; } -#vt100 .bgAnsi7 { background-color: inherit; } -#vt100 .bgAnsi8 { background-color: inherit; } -#vt100 .bgAnsi9 { background-color: inherit; } -#vt100 .bgAnsi10 { background-color: inherit; } -#vt100 .bgAnsi11 { background-color: inherit; } -#vt100 .bgAnsi12 { background-color: inherit; } -#vt100 .bgAnsi13 { background-color: inherit; } -#vt100 .bgAnsi14 { background-color: inherit; } +#vt100 .bgAnsi1 { background-color: transparent; } +#vt100 .bgAnsi2 { background-color: transparent; } +#vt100 .bgAnsi3 { background-color: transparent; } +#vt100 .bgAnsi4 { background-color: transparent; } +#vt100 .bgAnsi5 { background-color: transparent; } +#vt100 .bgAnsi6 { background-color: transparent; } +#vt100 .bgAnsi7 { background-color: transparent; } +#vt100 .bgAnsi8 { background-color: transparent; } +#vt100 .bgAnsi9 { background-color: transparent; } +#vt100 .bgAnsi10 { background-color: transparent; } +#vt100 .bgAnsi11 { background-color: transparent; } +#vt100 .bgAnsi12 { background-color: transparent; } +#vt100 .bgAnsi13 { background-color: transparent; } +#vt100 .bgAnsi14 { background-color: transparent; } diff --git a/demo/usercss-3.css b/demo/usercss-3.css index e69de29..16a1afc 100644 --- a/demo/usercss-3.css +++ b/demo/usercss-3.css @@ -0,0 +1,34 @@ +/* DEFINES_COLORS */ +#vt100 .ansi0 { } +#vt100 .ansi1 { color: #cd0000; } +#vt100 .ansi2 { color: #00cd00; } +#vt100 .ansi3 { color: #cdcd00; } +#vt100 .ansi4 { color: #0000ee; } +#vt100 .ansi5 { color: #cd00cd; } +#vt100 .ansi6 { color: #00cdcd; } +#vt100 .ansi7 { color: #e5e5e5; } +#vt100 .ansi8 { color: #7f7f7f; } +#vt100 .ansi9 { color: #ff0000; } +#vt100 .ansi10 { color: #00ff00; } +#vt100 .ansi11 { color: #e8e800; } +#vt100 .ansi12 { color: #5c5cff; } +#vt100 .ansi13 { color: #ff00ff; } +#vt100 .ansi14 { color: #00ffff; } +#vt100 .ansi15 { color: #ffffff; } + +#vt100 .bgAnsi0 { background-color: #000000; } +#vt100 .bgAnsi1 { background-color: #cd0000; } +#vt100 .bgAnsi2 { background-color: #00cd00; } +#vt100 .bgAnsi3 { background-color: #cdcd00; } +#vt100 .bgAnsi4 { background-color: #0000ee; } +#vt100 .bgAnsi5 { background-color: #cd00cd; } +#vt100 .bgAnsi6 { background-color: #00cdcd; } +#vt100 .bgAnsi7 { background-color: #e5e5e5; } +#vt100 .bgAnsi8 { background-color: #7f7f7f; } +#vt100 .bgAnsi9 { background-color: #ff0000; } +#vt100 .bgAnsi10 { background-color: #00ff00; } +#vt100 .bgAnsi11 { background-color: #e8e800; } +#vt100 .bgAnsi12 { background-color: #5c5cff; } +#vt100 .bgAnsi13 { background-color: #ff00ff; } +#vt100 .bgAnsi14 { background-color: #00ffff; } +#vt100 .bgAnsi15 { } diff --git a/demo/vt100.js b/demo/vt100.js index bf06485..9f8cc89 100644 --- a/demo/vt100.js +++ b/demo/vt100.js @@ -1892,7 +1892,7 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.9 (revision 171)" + + alert("VT100 Terminal Emulator " + "2.9 (revision 172)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; diff --git a/libhttp/httpconnection.c b/libhttp/httpconnection.c index 972a58c..c8e69f6 100644 --- a/libhttp/httpconnection.c +++ b/libhttp/httpconnection.c @@ -546,13 +546,12 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) { check(msg); check(len >= 0); - // Internet Explorer prior to version 7 seems to have difficulties with - // compressed data. It also has difficulties with SSL connections that - // are being proxied. + // Internet Explorer seems to have difficulties with compressed data. It + // also has difficulties with SSL connections that are being proxied. int ieBug = 0; const char *userAgent = getFromHashMap(&http->header, "user-agent"); const char *msie = userAgent ? strstr(userAgent, "MSIE ") : NULL; - if (msie && msie[5] >= '4' && msie[5] <= '6') { + if (msie) { ieBug++; } @@ -637,8 +636,8 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) { z_stream strm = { .zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL, - .avail_in = l - 2, - .next_in = (unsigned char *)line + 2, + .avail_in = l, + .next_in = (unsigned char *)line, .avail_out = len, .next_out = (unsigned char *)compressed }; diff --git a/shellinabox/color.css b/shellinabox/color.css index e69de29..16a1afc 100644 --- a/shellinabox/color.css +++ b/shellinabox/color.css @@ -0,0 +1,34 @@ +/* DEFINES_COLORS */ +#vt100 .ansi0 { } +#vt100 .ansi1 { color: #cd0000; } +#vt100 .ansi2 { color: #00cd00; } +#vt100 .ansi3 { color: #cdcd00; } +#vt100 .ansi4 { color: #0000ee; } +#vt100 .ansi5 { color: #cd00cd; } +#vt100 .ansi6 { color: #00cdcd; } +#vt100 .ansi7 { color: #e5e5e5; } +#vt100 .ansi8 { color: #7f7f7f; } +#vt100 .ansi9 { color: #ff0000; } +#vt100 .ansi10 { color: #00ff00; } +#vt100 .ansi11 { color: #e8e800; } +#vt100 .ansi12 { color: #5c5cff; } +#vt100 .ansi13 { color: #ff00ff; } +#vt100 .ansi14 { color: #00ffff; } +#vt100 .ansi15 { color: #ffffff; } + +#vt100 .bgAnsi0 { background-color: #000000; } +#vt100 .bgAnsi1 { background-color: #cd0000; } +#vt100 .bgAnsi2 { background-color: #00cd00; } +#vt100 .bgAnsi3 { background-color: #cdcd00; } +#vt100 .bgAnsi4 { background-color: #0000ee; } +#vt100 .bgAnsi5 { background-color: #cd00cd; } +#vt100 .bgAnsi6 { background-color: #00cdcd; } +#vt100 .bgAnsi7 { background-color: #e5e5e5; } +#vt100 .bgAnsi8 { background-color: #7f7f7f; } +#vt100 .bgAnsi9 { background-color: #ff0000; } +#vt100 .bgAnsi10 { background-color: #00ff00; } +#vt100 .bgAnsi11 { background-color: #e8e800; } +#vt100 .bgAnsi12 { background-color: #5c5cff; } +#vt100 .bgAnsi13 { background-color: #ff00ff; } +#vt100 .bgAnsi14 { background-color: #00ffff; } +#vt100 .bgAnsi15 { } diff --git a/shellinabox/monochrome.css b/shellinabox/monochrome.css index b5c60a5..9863105 100644 --- a/shellinabox/monochrome.css +++ b/shellinabox/monochrome.css @@ -13,17 +13,17 @@ #vt100 .ansi13 { color: inherit; } #vt100 .ansi14 { color: inherit; } -#vt100 .bgAnsi1 { background-color: inherit; } -#vt100 .bgAnsi2 { background-color: inherit; } -#vt100 .bgAnsi3 { background-color: inherit; } -#vt100 .bgAnsi4 { background-color: inherit; } -#vt100 .bgAnsi5 { background-color: inherit; } -#vt100 .bgAnsi6 { background-color: inherit; } -#vt100 .bgAnsi7 { background-color: inherit; } -#vt100 .bgAnsi8 { background-color: inherit; } -#vt100 .bgAnsi9 { background-color: inherit; } -#vt100 .bgAnsi10 { background-color: inherit; } -#vt100 .bgAnsi11 { background-color: inherit; } -#vt100 .bgAnsi12 { background-color: inherit; } -#vt100 .bgAnsi13 { background-color: inherit; } -#vt100 .bgAnsi14 { background-color: inherit; } +#vt100 .bgAnsi1 { background-color: transparent; } +#vt100 .bgAnsi2 { background-color: transparent; } +#vt100 .bgAnsi3 { background-color: transparent; } +#vt100 .bgAnsi4 { background-color: transparent; } +#vt100 .bgAnsi5 { background-color: transparent; } +#vt100 .bgAnsi6 { background-color: transparent; } +#vt100 .bgAnsi7 { background-color: transparent; } +#vt100 .bgAnsi8 { background-color: transparent; } +#vt100 .bgAnsi9 { background-color: transparent; } +#vt100 .bgAnsi10 { background-color: transparent; } +#vt100 .bgAnsi11 { background-color: transparent; } +#vt100 .bgAnsi12 { background-color: transparent; } +#vt100 .bgAnsi13 { background-color: transparent; } +#vt100 .bgAnsi14 { background-color: transparent; } diff --git a/shellinabox/shell_in_a_box.js b/shellinabox/shell_in_a_box.js index 7818703..ab52210 100644 --- a/shellinabox/shell_in_a_box.js +++ b/shellinabox/shell_in_a_box.js @@ -355,7 +355,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) { }; ShellInABox.prototype.about = function() { - alert("Shell In A Box version " + "2.9 (revision 171)" + + alert("Shell In A Box version " + "2.9 (revision 172)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com" + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index e2d6034..caa1e58 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -445,18 +445,129 @@ static int dataHandler(HttpConnection *http, struct Service *service, static void serveStaticFile(HttpConnection *http, const char *contentType, const char *start, const char *end) { + char *body = (char *)start; + char *bodyEnd = (char *)end; + + // Unfortunately, there are still some browsers that are so buggy that they + // need special conditional code. In anything that has a "text" MIME type, + // we allow simple conditionals. Nested conditionals are not supported. + if (!memcmp(contentType, "text/", 5)) { + char *tag = NULL; + int condTrue = -1; + char *ifPtr = NULL; + char *elsePtr = NULL; + for (char *ptr = body; bodyEnd - ptr >= 6; ) { + char *eol = ptr; + eol = memchr(eol, '\n', bodyEnd - eol); + if (eol == NULL) { + eol = bodyEnd; + } else { + ++eol; + } + if (!memcmp(ptr, "[if ", 4)) { + char *bracket = memchr(ptr + 4, ']', eol - ptr - 4); + if (bracket != NULL && bracket > ptr + 4) { + check(tag = malloc(bracket - ptr - 3)); + memcpy(tag, ptr + 4, bracket - ptr - 4); + tag[bracket - ptr - 4] = '\000'; + condTrue = 0; + const char *userAgent = getFromHashMap(httpGetHeaders(http), + "user-agent"); + if (userAgent) { + // Allow multiple comma separated conditions + for (char *tagPtr = tag; *tagPtr; ) { + char *e = strchr(tagPtr, ','); + if (!e) { + e = strchr(tag, '\000'); + } else { + *e++ = '\000'; + } + condTrue = userCSSGetDefine(tagPtr) || + strstr(userAgent, tagPtr) != NULL; + if (*e) { + e[-1] = ','; + } + if (condTrue) { + break; + } + tagPtr = e; + } + } + + // If we find any conditionals, then we need to make a copy of + // the text document. We do this lazily, as presumably the majority + // of text documents won't have conditionals. + if (body == start) { + check(body = malloc(end - start)); + memcpy(body, start, end - start); + bodyEnd += body - start; + ptr += body - start; + eol += body - start; + } + + // Remember the beginning of the "[if ...]" statement + ifPtr = ptr; + } + } else if (ifPtr && !elsePtr && eol - ptr >= strlen(tag) + 7 && + !memcmp(ptr, "[else ", 6) && + !memcmp(ptr + 6, tag, strlen(tag)) && + ptr[6 + strlen(tag)] == ']') { + // Found an "[else ...]" statement. Remember where it started. + elsePtr = ptr; + } else if (ifPtr && eol - ptr >= strlen(tag) + 8 && + !memcmp(ptr, "[endif ", 7) && + !memcmp(ptr + 7, tag, strlen(tag)) && + ptr[7 + strlen(tag)] == ']') { + // Found the closing "[endif ...]" statement. Now we can remove those + // parts of the conditionals that do not apply to this user agent. + char *s, *e; + if (condTrue) { + s = strchr(ifPtr, '\n') + 1; + e = elsePtr ? elsePtr : ptr; + } else { + if (elsePtr) { + s = strchr(elsePtr, '\n') + 1; + e = ptr; + } else { + s = ifPtr; + e = ifPtr; + } + } + memmove(ifPtr, s, e - s); + memmove(ifPtr + (e - s), eol, bodyEnd - eol); + bodyEnd -= (s - ifPtr) + (eol - e); + eol = ifPtr + (e - s); + ifPtr = NULL; + elsePtr = NULL; + free(tag); + tag = NULL; + } + ptr = eol; + } + free(tag); + } + char *response = stringPrintf(NULL, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" - "\r\n", - contentType, (long)(end - start)); + "%s\r\n", + contentType, (long)(bodyEnd - body), + body == start ? "" : + "Cache-Control: no-cache\r\n"); int len = strlen(response); if (strcmp(httpGetMethod(http), "HEAD")) { - check(response = realloc(response, len + (end - start))); - memcpy(response + len, start, end - start); - len += end - start; + check(response = realloc(response, len + (bodyEnd - body))); + memcpy(response + len, body, bodyEnd - body); + len += bodyEnd - body; } + + // If we expanded conditionals, we had to create a temporary copy. Delete + // it now. + if (body != start) { + free(body); + } + httpTransfer(http, response, len); } diff --git a/shellinabox/styles.css b/shellinabox/styles.css index 6e465a1..3c3886f 100644 --- a/shellinabox/styles.css +++ b/shellinabox/styles.css @@ -135,6 +135,11 @@ #vt100 #scrollable.inverted { color: #ffffff; background-color: #000000; } +[if DEFINES_COLORS] +/* IE cannot properly handle "inherit" properties. So, the monochrome.css + * style sheet cannot work, if we define colors by default. + */ +[else DEFINES_COLORS] #vt100 .ansi0 { } #vt100 .ansi1 { color: #cd0000; } #vt100 .ansi2 { color: #00cd00; } @@ -168,6 +173,7 @@ #vt100 .bgAnsi13 { background-color: #ff00ff; } #vt100 .bgAnsi14 { background-color: #00ffff; } #vt100 .bgAnsi15 { } +[endif DEFINES_COLORS] @media print { #vt100 .scrollback { diff --git a/shellinabox/usercss.c b/shellinabox/usercss.c index 5ab733a..e94be74 100644 --- a/shellinabox/usercss.c +++ b/shellinabox/usercss.c @@ -55,21 +55,41 @@ #include "logging/logging.h" #include "shellinabox/usercss.h" +#include "libhttp/hashmap.h" +static struct HashMap *defines; -static void readStylesheet(const char *filename, char **style, size_t *len) { - int fd = open(filename, O_RDONLY); +static void definesDestructor(void *arg, char *key, char *value) { + free(key); +} + +static void readStylesheet(struct UserCSS *userCSS, const char *filename, + char **style, size_t *len) { + int fd = open(filename, O_RDONLY); struct stat st; if (fd < 0 || fstat(fd, &st)) { fatal("Cannot access style sheet \"%s\"", filename); } FILE *fp; - check(fp = fdopen(fd, "r")); - check(*style = malloc(st.st_size + 1)); + check(fp = fdopen(fd, "r")); + check(*style = malloc(st.st_size + 1)); check(fread(*style, 1, st.st_size, fp) == st.st_size); - (*style)[st.st_size] = '\000'; - *len = st.st_size; + (*style)[st.st_size] = '\000'; + *len = st.st_size; fclose(fp); + if (!memcmp(*style, "/* DEFINES_", 11)) { + char *e = strchr(*style + 11, ' '); + if (e) { + if (!defines) { + defines = newHashMap(definesDestructor, NULL); + } + char *def; + check(def = malloc(e - *style - 2)); + memcpy(def, *style + 3, e - *style - 3); + def[e - *style - 3] = '\000'; + addToHashMap(defines, def, (char *)userCSS); + } + } } void initUserCSS(struct UserCSS *userCSS, const char *arg) { @@ -130,7 +150,8 @@ void initUserCSS(struct UserCSS *userCSS, const char *arg) { "active by default"); } - readStylesheet(filename + 1, (char **)&userCSS->style, &userCSS->styleLen); + readStylesheet(userCSS, filename + 1, (char **)&userCSS->style, + &userCSS->styleLen); free(filename); arg = colon + 1 + filenameLen; @@ -208,3 +229,10 @@ char *getUserCSSString(struct UserCSS *userCSS) { } return stringPrintf(s, " ]"); } + +struct UserCSS *userCSSGetDefine(const char *def) { + if (!defines) { + return NULL; + } + return (struct UserCSS *)getFromHashMap(defines, def); +} diff --git a/shellinabox/usercss.h b/shellinabox/usercss.h index 5747530..73019eb 100644 --- a/shellinabox/usercss.h +++ b/shellinabox/usercss.h @@ -61,5 +61,6 @@ void parseUserCSS(struct UserCSS **userCSSList, const char *arg); void destroyUserCSS(struct UserCSS *userCSS); void deleteUserCSS(struct UserCSS *userCSS); char *getUserCSSString(struct UserCSS *userCSS); +struct UserCSS *userCSSGetDefine(const char *def); #endif diff --git a/shellinabox/vt100.js b/shellinabox/vt100.js index bf06485..9f8cc89 100644 --- a/shellinabox/vt100.js +++ b/shellinabox/vt100.js @@ -1892,7 +1892,7 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.9 (revision 171)" + + alert("VT100 Terminal Emulator " + "2.9 (revision 172)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); };