-Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
+2010-03-29 Markus Gutschke <markus@shellinabox.com>
+
+ * Started working on support for WebSockets.
+
+ * Fixed some compiler warnings when compiling with -Wextra
+
+ * Thanks to Jan Jaeger's excellent bug report, made some changes
+ that should make it easier to build ShellInABox for OpenWRT.
+
2009-12-10 Markus Gutschke <markus@shellinabox.com>
* Add .note.GNU-stack to all object files so that the generated
AM_CFLAGS = -g -std=gnu99 -Wall
AM_LDFLAGS = -g
+OBJCOPY ?= objcopy
+
noinst_LTLIBRARIES = libhttp.la \
liblogging.la
noinst_DATA = $(top_srcdir)/demo/demo.js
-rm -rf GNU-stack
.css.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.gif.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.html.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.ico.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
"$<" >"$@"
.js.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.wav.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
uninstall-man uninstall-man1
+OBJCOPY ?= objcopy
+
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck
-rm -rf GNU-stack
.css.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.gif.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.html.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.ico.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h
"$<" >"$@"
.js.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.wav.o:
- @echo objcopy "$<" "$@"
- @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \
+ @echo $(OBJCOPY) "$<" "$@"
+ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
- objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \
+ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
# Tell versions [3.59,3.63) of GNU make to not export all variables.
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
+/* Define to 1 if you have support for isnan */
+#define HAVE_ISNAN 1
+
/* Define to 1 if you have the <libutil.h> header file. */
/* #undef HAVE_LIBUTIL_H */
#define STDC_HEADERS 1
/* Most recent revision number in the version control system */
-#define VCS_REVISION "200"
+#define VCS_REVISION "202"
/* Version number of package */
#define VERSION "2.10"
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
+/* Define to 1 if you have support for isnan */
+#undef HAVE_ISNAN
+
/* Define to 1 if you have the <libutil.h> header file. */
#undef HAVE_LIBUTIL_H
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-VCS_REVISION=200
+VCS_REVISION=202
cat >>confdefs.h <<_ACEOF
$as_echo "#define HAVE_SIGWAIT 1" >>confdefs.h
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <math.h>
+int
+main ()
+{
+isnan(0.0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_ISNAN 1" >>confdefs.h
+
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.10, markus@shellinabox.com)
-VCS_REVISION=200
+VCS_REVISION=202
AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[Most recent revision number in the version control system])
[AC_DEFINE(HAVE_SIGWAIT, 1,
Define to 1 if you have a working sigwait)])
+dnl Not every system has support for isnan()
+AC_TRY_LINK([#include <math.h>],
+ [isnan(0.0);],
+ [AC_DEFINE(HAVE_ISNAN, 1,
+ Define to 1 if you have support for isnan)])
+
dnl On some systems, calling /bin/login does not work. Disable the LOGIN
dnl feature, if the user tells us that it does not do the right thing.
AC_ARG_ENABLE(login,
Upstream Author: markus@shellinabox.com
-Copyright (c) 2008-2009, Markus Gutschke
+Copyright (c) 2008-2010, Markus Gutschke
All rights reserved.
This program is free software; you can redistribute it and/or modify
// VT100.js -- JavaScript based terminal emulator
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
};
VT100.prototype.about = function() {
- alert("VT100 Terminal Emulator " + "2.10 (revision 200)" +
- "\nCopyright 2008-2009 by Markus Gutschke\n" +
+ alert("VT100 Terminal Emulator " + "2.10 (revision 202)" +
+ "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com");
};
// http.h -- Library for implementing embedded custom HTTP servers
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
#define HTTP_SUSPEND 3
#define HTTP_PARTIAL_REPLY 4
+#define WS_START_OF_FRAME 0x0100
+#define WS_END_OF_FRAME 0x0200
+#define WS_CONNECTION_OPENED 0xFF00
+#define WS_CONNECTION_CLOSED 0x7F00
+
#define NO_MSG "\001"
+#define BINARY_MSG "\001%d%p"
#define NOINTR(x) ({ int i__; while ((i__ = (x)) < 0 && errno == EINTR); i__;})
void serverRegisterStreamingHttpHandler(Server *server, const char *url,
int (*handler)(HttpConnection *, void *),
void *arg);
+void serverRegisterWebSocketHandler(Server *server, const char *url,
+ int (*handler)(HttpConnection *, void *, int, const char *, int),
+ void *arg);
ServerConnection *serverAddConnection(Server *server, int fd,
int (*handleConnection)(ServerConnection *,
void *arg, short *events,
void httpSendReply(HttpConnection *http, int code,
const char *msg, const char *fmt, ...)
__attribute__((format(printf, 4, 5)));
+void httpSendWebSocketTextMsg(HttpConnection *http, int type, const char *fmt,
+ ...) __attribute__((format(printf, 3, 4)));
+void httpSendWebSocketBinaryMsg(HttpConnection *http, int type,
+ const void *buf, int len);
void httpExitLoop(HttpConnection *http, int exitAll);
Server *httpGetServer(const HttpConnection *http);
ServerConnection *httpGetServerConnection(const HttpConnection *);
// httpconnection.c -- Manage state machine for HTTP connections
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
#ifdef HAVE_STRLCAT
#define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
#endif
+#ifndef HAVE_ISNAN
+#define isnan(x) ({ typeof(x) _x = (x); _x != _x; })
+#endif
#define max(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \
_a > _b ? _a : _b; })
static int httpFinishCommand(struct HttpConnection *http) {
int rc = HTTP_DONE;
- if (http->callback && !http->done) {
- rc = http->callback(http, http->arg, NULL, 0);
+ if ((http->callback || http->websocketHandler) && !http->done) {
+ rc = http->callback ? http->callback(http, http->arg, NULL, 0)
+ : http->websocketHandler(http, http->arg, WS_CONNECTION_CLOSED, NULL,0);
check(rc != HTTP_SUSPEND);
check(rc != HTTP_PARTIAL_REPLY);
http->callback = NULL;
}
static void httpDestroyHeaders(void *arg, char *key, char *value) {
+ (void)arg;
free(key);
free(value);
}
}
static void httpSetState(struct HttpConnection *http, int state) {
- if (state == http->state) {
+ if (state == (int)http->state) {
return;
}
http->msgOffset = 0;
http->totalWritten = 0;
http->expecting = 0;
+ http->websocketType = WS_UNDEFINED;
http->callback = NULL;
+ http->websocketHandler = NULL;
http->arg = NULL;
http->private = NULL;
http->code = 200;
void destroyHttpConnection(struct HttpConnection *http) {
if (http) {
if (http->isSuspended || http->isPartialReply) {
- if (http->callback && !http->done) {
- http->callback(http, http->arg, NULL, 0);
+ if (!http->done) {
+ if (http->callback) {
+ http->callback(http, http->arg, NULL, 0);
+ } else if (http->websocketHandler) {
+ http->websocketHandler(http, http->arg, WS_CONNECTION_CLOSED,NULL,0);
+ }
}
http->callback = NULL;
http->isSuspended = 0;
static int httpHandleCommand(struct HttpConnection *http,
const struct Trie *handlers) {
debug("Handling \"%s\" \"%s\"", http->method, http->path);
- const char *contentLength = getFromHashMap(&http->header,
- "content-length");
+ const char *contentLength = getFromHashMap(&http->header,
+ "content-length");
if (contentLength != NULL && *contentLength) {
char *endptr;
- http->expecting = strtol(contentLength,
- &endptr, 10);
+ http->expecting = strtol(contentLength,
+ &endptr, 10);
if (*endptr) {
// Invalid length. Read until end of stream and then close
// connection.
- http->expecting = -1;
+ http->expecting = -1;
}
} else {
// Unknown length. Read until end of stream and then close
// connection.
- http->expecting = -1;
+ http->expecting = -1;
}
if (!strcmp(http->method, "OPTIONS")) {
- char *response = stringPrintf(NULL,
+ char *response = stringPrintf(NULL,
"HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"Allow: GET, POST, OPTIONS\r\n"
"\r\n");
httpTransfer(http, response, strlen(response));
if (http->expecting < 0) {
- http->expecting = 0;
+ http->expecting = 0;
}
return HTTP_READ_MORE;
} else if (!strcmp(http->method, "GET")) {
if (http->expecting < 0) {
- http->expecting = 0;
+ http->expecting = 0;
}
} else if (!strcmp(http->method, "POST")) {
} else if (!strcmp(http->method, "HEAD")) {
if (http->expecting < 0) {
- http->expecting = 0;
+ http->expecting = 0;
}
} else if (!strcmp(http->method, "PUT") ||
!strcmp(http->method, "DELETE") ||
httpSendReply(http, 501, "Method Not Implemented", NO_MSG);
return HTTP_DONE;
}
- const char *host = getFromHashMap(&http->header,
- "host");
+ const char *host = getFromHashMap(&http->header,
+ "host");
if (host) {
- for (char ch; (ch = *host) != '\000'; host++) {
+ for (char ch, *ptr = (char *)host; (ch = *ptr) != '\000'; ptr++) {
if (ch == ':') {
- *(char *)host = '\000';
+ *ptr = '\000';
break;
}
if (ch != '-' && ch != '.' &&
(ch < '0' ||(ch > '9' && ch < 'A') ||
- (ch > 'Z' && ch < 'a')|| ch > 'z')) {
+ (ch > 'Z' && ch < 'a')||(ch > 'z' && ch <= 0x7E))) {
httpSendReply(http, 400, "Bad Request", NO_MSG);
return HTTP_DONE;
}
}
}
+
char *diff;
struct HttpHandler *h = (struct HttpHandler *)getFromTrie(handlers,
http->path, &diff);
+
if (h) {
- check(diff);
- while (diff > http->path && diff[-1] == '/') {
- diff--;
- }
- if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') {
- check(!http->matchedPath);
- check(!http->pathInfo);
- check(!http->query);
-
- check(http->matchedPath = malloc(diff - http->path + 1));
- memcpy(http->matchedPath, http->path, diff - http->path);
- http->matchedPath[diff - http->path] = '\000';
-
- const char *query = strchr(diff, '?');
- if (*diff && *diff != '?') {
- const char *endOfInfo = query
- ? query : strrchr(diff, '\000');
- check(http->pathInfo = malloc(endOfInfo - diff + 1));
- memcpy(http->pathInfo, diff, endOfInfo - diff);
- http->pathInfo[endOfInfo - diff] = '\000';
+ if (h->websocketHandler) {
+ // Check for WebSocket handshake
+ const char *upgrade = getFromHashMap(&http->header,
+ "upgrade");
+ if (upgrade && !strcmp(upgrade, "WebSocket")) {
+ const char *connection = getFromHashMap(&http->header,
+ "connection");
+ if (connection && !strcmp(connection, "Upgrade")) {
+ const char *origin = getFromHashMap(&http->header,
+ "origin");
+ if (origin) {
+ for (const char *ptr = origin; *ptr; ptr++) {
+ if ((unsigned char)*ptr < ' ') {
+ goto bad_ws_upgrade;
+ }
+ }
+
+ const char *protocol = getFromHashMap(&http->header,
+ "websocket-protocol");
+ if (protocol) {
+ for (const char *ptr = protocol; *ptr; ptr++) {
+ if ((unsigned char)*ptr < ' ') {
+ goto bad_ws_upgrade;
+ }
+ }
+ }
+ char *port = NULL;
+ if (http->port != (http->sslHndl ? 443 : 80)) {
+ port = stringPrintf(NULL,
+ ":%d", http->port);
+ }
+ char *response = stringPrintf(NULL,
+ "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "WebSocket-Origin: %s\r\n"
+ "WebSocket-Location: %s://%s%s%s\r\n"
+ "%s%s%s"
+ "\r\n",
+ origin,
+ http->sslHndl ? "wss" : "ws", host && *host ? host : "localhost",
+ port ? port : "", http->path,
+ protocol ? "WebSocket-Protocol: " : "",
+ protocol ? protocol : "",
+ protocol ? "\r\n" : "");
+ free(port);
+ debug("Switching to WebSockets");
+ httpTransfer(http, response, strlen(response));
+ if (http->expecting < 0) {
+ http->expecting = 0;
+ }
+ http->websocketHandler = h->websocketHandler;
+ httpSetState(http, WEBSOCKET);
+ return HTTP_READ_MORE;
+ }
+ }
}
+ }
+ bad_ws_upgrade:;
- if (query) {
- check(http->query = strdup(query + 1));
+ if (h->handler) {
+ check(diff);
+ while (diff > http->path && diff[-1] == '/') {
+ diff--;
+ }
+ if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') {
+ check(!http->matchedPath);
+ check(!http->pathInfo);
+ check(!http->query);
+
+ check(http->matchedPath = malloc(diff - http->path + 1));
+ memcpy(http->matchedPath, http->path, diff - http->path);
+ http->matchedPath[diff - http->path] = '\000';
+
+ const char *query = strchr(diff, '?');
+ if (*diff && *diff != '?') {
+ const char *endOfInfo = query
+ ? query : strrchr(diff, '\000');
+ check(http->pathInfo = malloc(endOfInfo - diff + 1));
+ memcpy(http->pathInfo, diff, endOfInfo - diff);
+ http->pathInfo[endOfInfo - diff] = '\000';
+ }
+
+ if (query) {
+ check(http->query = strdup(query + 1));
+ }
+ return h->handler(http, h->arg);
}
- return h->handler(http, h->arg);
}
}
httpSendReply(http, 404, "File Not Found", NO_MSG);
serverSetTimeout(connection, CONNECTION_TIMEOUT);
}
check(!http->done);
- if (!http->expecting && http->callback) {
- rc = http->callback(http, http->arg, "", 0);
- if (rc != HTTP_READ_MORE) {
- goto retry;
+ if (!http->expecting) {
+ if (http->callback) {
+ rc = http->callback(http, http->arg, "", 0);
+ if (rc != HTTP_READ_MORE) {
+ goto retry;
+ }
+ } else if (http->websocketHandler) {
+ http->websocketHandler(http, http->arg, WS_CONNECTION_OPENED,
+ NULL, 0);
}
}
- httpSetState(http, http->expecting ? PAYLOAD : COMMAND);
+ if (http->state != WEBSOCKET) {
+ httpSetState(http, http->expecting ? PAYLOAD : COMMAND);
+ }
break;
case HTTP_SUSPEND:
http->isSuspended = 1;
return consumed;
}
+static int httpHandleWebSocket(struct HttpConnection *http, int offset,
+ const char *buf, int bytes) {
+ check(http->websocketHandler);
+ int ch = 0x00;
+ while (bytes > offset) {
+ if (http->websocketType & WS_UNDEFINED) {
+ ch = httpGetChar(http, buf, bytes, &offset);
+ check(ch >= 0);
+ if (http->websocketType & 0xFF) {
+ // Reading another byte of length information.
+ if (http->expecting > 0xFFFFFF) {
+ return 0;
+ }
+ http->expecting = (128 * http->expecting) + (ch & 0x7F);
+ if ((ch & 0x80) == 0) {
+ // Done reading length information.
+ http->websocketType &= ~WS_UNDEFINED;
+
+ // ch is used to detect when we read the terminating byte in text
+ // mode. In binary mode, it must be set to something other than 0xFF.
+ ch = 0x00;
+ }
+ } else {
+ // Reading first byte of frame.
+ http->websocketType = (ch & 0xFF) | WS_START_OF_FRAME;
+ if (ch & 0x80) {
+ // For binary data, we have to read the length before we can start
+ // processing payload.
+ http->websocketType |= WS_UNDEFINED;
+ http->expecting = 0;
+ }
+ }
+ } else if (http->websocketType & 0x80) {
+ // Binary data
+ if (http->expecting) {
+ if (offset < 0) {
+ handle_partial:
+ check(-offset <= http->partialLength);
+ int len = -offset;
+ if (len >= http->expecting) {
+ len = http->expecting;
+ http->websocketType |= WS_END_OF_FRAME;
+ }
+ if (len &&
+ http->websocketHandler(http, http->arg, http->websocketType,
+ http->partial + http->partialLength + offset,
+ len) != HTTP_DONE) {
+ return 0;
+ }
+
+ if (ch == 0xFF) {
+ // In text mode, we jump to handle_partial, when we find the
+ // terminating 0xFF byte. If so, we should try to consume it now.
+ if (len < http->partialLength) {
+ len++;
+ http->websocketType = WS_UNDEFINED;
+ }
+ }
+
+ if (len == http->partialLength) {
+ free(http->partial);
+ http->partial = NULL;
+ http->partialLength = 0;
+ } else {
+ memmove(http->partial, http->partial + len,
+ http->partialLength - len);
+ http->partialLength -= len;
+ }
+ offset += len;
+ http->expecting -= len;
+ } else {
+ handle_buffered:;
+ int len = bytes - offset;
+ if (len >= http->expecting) {
+ len = http->expecting;
+ http->websocketType |= WS_END_OF_FRAME;
+ }
+ if (len &&
+ http->websocketHandler(http, http->arg, http->websocketType,
+ buf + offset, len) != HTTP_DONE) {
+ return 0;
+ }
+
+ if (ch == 0xFF) {
+ // In text mode, we jump to handle_buffered, when we find the
+ // terminating 0xFF byte. If so, we should consume it now.
+ check(offset + len < bytes);
+ len++;
+ http->websocketType = WS_UNDEFINED;
+ }
+ offset += len;
+ http->expecting -= len;
+ }
+ http->websocketType &= ~(WS_START_OF_FRAME | WS_END_OF_FRAME);
+ } else {
+ // Read all data. Go back to looking for a new frame header.
+ http->websocketType = WS_UNDEFINED;
+ }
+ } else {
+ // Process text data until we find a 0xFF bytes.
+ int i = offset;
+
+ // If we have partial data, process that first.
+ while (i < 0) {
+ ch = httpGetChar(http, buf, bytes, &i);
+ check(ch != -1);
+
+ // Terminate when we either find the 0xFF, or we have reached the end
+ // of partial data.
+ if (ch == 0xFF || !i) {
+ // Set WS_END_OF_FRAME, iff we have found the 0xFF marker.
+ http->expecting = i - offset - (ch == 0xFF);
+ goto handle_partial;
+ }
+ }
+
+ // Read all remaining buffered bytes (i.e. positive offset).
+ while (bytes > i) {
+ ch = httpGetChar(http, buf, bytes, &i);
+ check(ch != -1);
+
+ // Terminate when we either find the 0xFF, or we have reached the end
+ // of buffered data.
+ if (ch == 0xFF || bytes == i) {
+ // Set WS_END_OF_FRAME, iff we have found the 0xFF marker.
+ http->expecting = i - offset - (ch == 0xFF);
+ goto handle_buffered;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
int httpHandleConnection(struct ServerConnection *connection, void *http_,
short *events, short revents) {
struct HttpConnection *http = (struct HttpConnection *)http_;
bytes = 0;
}
}
+
if (bytes > 0 && http->state == SNIFFING_SSL) {
// Assume that all legitimate HTTP commands start with a sequence of
// letters followed by a space character. If we don't see this pattern,
strcmp(method, "CONNECT");
http->state = COMMAND;
break;
- } else if (j >= sizeof(method)-1 ||
+ } else if (j >= (int)sizeof(method)-1 ||
ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') {
isSSL = 1;
http->state = COMMAND;
}
}
}
+
if (bytes > 0 || (eof && http->partial)) {
check(!!http->partial == !!http->partialLength);
int offset = -http->partialLength;
consumed = len;
pushBack = bytes - offset - len;
}
+ } else if (http->state == WEBSOCKET) {
+ if (!httpHandleWebSocket(http, offset, buf, bytes)) {
+ httpCloseRead(http);
+ break;
+ }
+ consumed += bytes - offset;
+ } else {
+ check(0);
}
+
if (pushBack) {
check(offset + pushBack == bytes);
if (offset >= 0) {
break;
case PAYLOAD:
case DISCARD_PAYLOAD:
+ case WEBSOCKET:
http->expecting = 0;
httpCloseRead(http);
httpSetState(http, COMMAND);
}
}
+void httpSendWebSocketTextMsg(struct HttpConnection *http, int type,
+ const char *fmt, ...) {
+ check(type >= 0 && type <= 0x7F);
+ va_list ap;
+ va_start(ap, fmt);
+ char *buf;
+ int len;
+ if (strcmp(fmt, BINARY_MSG)) {
+ // Send a printf() style text message
+ buf = vStringPrintf(NULL, fmt, ap);
+ len = strlen(buf);
+ } else {
+ // Send a binary message
+ len = va_arg(ap, int);
+ buf = va_arg(ap, char *);
+ }
+ va_end(ap);
+ check(len >= 0 && len < 0x60000000);
+
+ // We assume that all input data is directly mapped in the range 0..255
+ // (e.g. ISO-8859-1). In order to transparently send it over a web socket,
+ // we have to encode it in UTF-8.
+ int utf8Len = len + 2;
+ for (int i = 0; i < len; ++i) {
+ if (buf[i] & 0x80) {
+ ++utf8Len;
+ }
+ }
+ char *utf8;
+ check(utf8 = malloc(utf8Len));
+ utf8[0] = type;
+ for (int i = 0, j = 1; i < len; ++i) {
+ unsigned char ch = buf[i];
+ if (ch & 0x80) {
+ utf8[j++] = 0xC0 + (ch >> 6);
+ utf8[j++] = 0x80 + (ch & 0x3F);
+ } else {
+ utf8[j++] = ch;
+ }
+ check(j < utf8Len);
+ }
+ utf8[utf8Len-1] = '\xFF';
+
+ // Free our temporary buffer, if we actually did allocate one.
+ if (strcmp(fmt, BINARY_MSG)) {
+ free(buf);
+ }
+
+ // Send to browser.
+ httpTransfer(http, utf8, utf8Len);
+}
+
+void httpSendWebSocketBinaryMsg(struct HttpConnection *http, int type,
+ const void *buf, int len) {
+ check(type >= 0x80 && type <= 0xFF);
+ check(len > 0 && len < 0x7FFFFFF0);
+
+ // Allocate buffer for header and payload.
+ char *data;
+ check(data = malloc(len + 6));
+ data[0] = type;
+
+ // Convert length to base-128.
+ int i = 0;
+ int l = len;
+ do {
+ data[++i] = 0x80 + (l & 0x7F);
+ l /= 128;
+ } while (l);
+ data[i] &= 0x7F;
+
+ // Reverse digits, so that they are big-endian.
+ for (int j = 0; j < i/2; ++j) {
+ char ch = data[1+j];
+ data[1+j] = data[i-j];
+ data[i-j] = ch;
+ }
+
+ // Transmit header and payload.
+ memmove(data + i + 1, buf, len);
+ httpTransfer(http, data, len + i + 1);
+}
+
void httpExitLoop(struct HttpConnection *http, int exitAll) {
serverExitLoop(http->server, exitAll);
}
// httpconnection.h -- Manage state machine for HTTP connections
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
#define HTTP_SUSPEND 3
#define HTTP_PARTIAL_REPLY 4
+#define WS_UNDEFINED 0x1000
+#define WS_START_OF_FRAME 0x0100
+#define WS_END_OF_FRAME 0x0200
+
#define NO_MSG "\001"
struct HttpConnection {
int isSuspended;
int isPartialReply;
int done;
- enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD } state;
+ enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD,
+ WEBSOCKET } state;
char *peerName;
int peerPort;
char *url;
int msgOffset;
int totalWritten;
int expecting;
+ int websocketType;
int (*callback)(struct HttpConnection *, void *,
const char *,int);
+ int (*websocketHandler)(struct HttpConnection *, void *,
+ int, const char *, int);
void *arg;
void *private;
int code;
struct HttpHandler {
int (*handler)(struct HttpConnection *, void *);
int (*streamingHandler)(struct HttpConnection *, void *, const char *, int);
+ int (*websocketHandler)(struct HttpConnection *, void *, int,
+ const char *, int);
void *arg, *streamingArg;
};
void httpSendReply(struct HttpConnection *http, int code,
const char *msg, const char *fmt, ...)
__attribute__((format(printf, 4, 5)));
+void httpSendWebSocketTextMsg(struct HttpConnection *http, int type,
+ const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+void httpSendWebSocketBinaryMsg(struct HttpConnection *http, int type,
+ const void *buf, int len);
void httpExitLoop(struct HttpConnection *http, int exitAll);
struct Server *httpGetServer(const struct HttpConnection *http);
struct ServerConnection *httpGetServerConnection(const struct HttpConnection*);
serverGetFd
serverRegisterHttpHandler
serverRegisterStreamingHttpHandler
+serverRegisterWebSocketHandler
serverAddConnection
serverDeleteConnection
serverSetTimeout
httpGetPrivate
httpSetPrivate
httpSendReply
+httpSendWebSocketTextMsg
+httpSendWebSocketBinaryMsg
httpExitLoop
httpGetServer
httpGetServerConnection
// server.c -- Generic server that can deal with HTTP connections
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
}
static void serverDestroyHandlers(void *arg, char *value) {
+ (void)arg;
free(value);
}
h->handler = serverCollectHandler;
h->arg = h;
h->streamingHandler = handler;
+ h->websocketHandler = NULL;
h->streamingArg = arg;
addToTrie(&server->handlers, url, (char *)h);
}
check(h = malloc(sizeof(struct HttpHandler)));
h->handler = handler;
h->streamingHandler = NULL;
+ h->websocketHandler = NULL;
h->streamingArg = NULL;
h->arg = arg;
addToTrie(&server->handlers, url, (char *)h);
}
}
+void serverRegisterWebSocketHandler(struct Server *server, const char *url,
+ int (*handler)(struct HttpConnection *, void *, int, const char *, int),
+ void *arg) {
+ if (!handler) {
+ addToTrie(&server->handlers, url, NULL);
+ } else {
+ struct HttpHandler *h;
+ check(h = malloc(sizeof(struct HttpHandler)));
+ h->handler = NULL;
+ h->streamingHandler = NULL;
+ h->websocketHandler = handler;
+ h->arg = arg;
+ addToTrie(&server->handlers, url, (char *)h);
+ }
+}
+
static int serverQuitHandler(struct HttpConnection *http, void *arg) {
+ (void)arg;
httpSendReply(http, 200, "Good Bye", NO_MSG);
httpExitLoop(http, 1);
return HTTP_DONE;
// server.h -- Generic server that can deal with HTTP connections
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
int (*handler)(struct HttpConnection *, void *),
void *arg);
+void serverRegisterWebSocketHandler(struct Server *server, const char *url,
+ int (*handler)(struct HttpConnection *, void *, int, const char *, int),
+ void *arg);
struct ServerConnection *serverAddConnection(struct Server *server, int fd,
int (*handleConnection)(struct ServerConnection *c,
void *arg, short *events,
// ssl.c -- Support functions that find and load SSL support, if available
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
{ { &d2i_X509 }, "d2i_X509" },
{ { &X509_free }, "X509_free" }
};
- for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
+ for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
if (!(*symbols[i].var = loadSymbol("libssl.so", symbols[i].fn))) {
debug("Failed to load SSL support. Could not find \"%s\"",
symbols[i].fn);
- for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
+ for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
*symbols[j].var = NULL;
}
return;
check((buf = malloc(bufSize)) != NULL);
for (;;) {
check(len < bufSize - 1);
- size_t readLen = bufSize - len - 1;
+ ssize_t readLen = bufSize - len - 1;
ssize_t bytesRead = NOINTR(read(fd, buf + len, readLen));
if (bytesRead > 0) {
len += bytesRead;
return NULL;
}
unsigned char *ret;
- size_t maxSize = (((end - ptr)*6)+7)/8;
+ ssize_t maxSize = (((end - ptr)*6)+7)/8;
check((ret = malloc(maxSize)) != NULL);
unsigned char *out = ret;
unsigned bits = 0;
#ifdef HAVE_TLSEXT
static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
+ (void)al;
check(!ERR_peek_error());
const char *name = SSL_get_servername(sslHndl,
TLSEXT_NAMETYPE_host_name);
}
free(serverName);
if (context != ssl->sslContext) {
- check(SSL_set_SSL_CTX(sslHndl, context) > 0);
+ check(SSL_set_SSL_CTX(sslHndl, context));
}
check(!ERR_peek_error());
return SSL_TLSEXT_ERR_OK;
static int gethostbyname_r(const char *name, struct hostent *ret,
char *buf, size_t buflen,
struct hostent **result, int *h_errnop) {
+ (void)buf;
+ (void)buflen;
if (result) {
*result = NULL;
}
// url.c -- Object representing uniform resource locators
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
}
static void urlDestroyHashMapEntry(void *arg, char *key, char *value) {
+ (void)arg;
free(key);
free(value);
}
// launcher.c -- Launch services from a privileged process
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
{ { &pam_start }, "libpam.so", "pam_start" },
{ { &misc_conv }, "libpam_misc.so", "misc_conv" }
};
- for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
+ for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
if (!(*symbols[i].var = loadSymbol(symbols[i].lib, symbols[i].fn))) {
#if defined(HAVE_SECURITY_PAM_CLIENT_H)
if (!strcmp(symbols[i].fn, "pam_binary_handler_fn")) {
}
debug("Failed to load PAM support. Could not find \"%s\"",
symbols[i].fn);
- for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
+ for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
*symbols[j].var = NULL;
}
break;
}
struct LaunchRequest *request;
- size_t len = sizeof(struct LaunchRequest) + strlen(u) + 1;
+ ssize_t len = sizeof(struct LaunchRequest) + strlen(u) + 1;
check(request = calloc(len, 1));
request->service = service;
request->width = session->width;
}
static void destroyUtmpHashEntry(void *arg, char *key, char *value) {
+ (void)arg;
+ (void)key;
deleteUtmp((struct Utmp *)value);
}
}
static void sigAlrmHandler(int sig, siginfo_t *info, void *unused) {
+ (void)sig;
+ (void)info;
+ (void)unused;
puts("\nLogin timed out after 60 seconds.");
_exit(1);
}
}
if (restricted &&
- (service->uid != restricted || service->gid != pw->pw_gid)) {
+ (service->uid != (int)restricted || service->gid != (int)pw->pw_gid)) {
puts("\nAccess denied!");
#if defined(HAVE_SECURITY_PAM_APPL_H)
if (service->authUser != 2 /* SSH */) {
if (i == ngroups) {
groups[ngroups++] = service->gid;
break;
- } else if (groups[i] == service->gid) {
+ } else if ((int)groups[i] == service->gid) {
break;
}
}
}
static void destroyVariableHashEntry(void *arg, char *key, char *value) {
+ (void)arg;
free(key);
free(value);
}
static void execService(int width, int height, struct Service *service,
const char *peerName, char **environment,
const char *url) {
+ (void)width;
+ (void)height;
+
// Create a hash table with all the variables that we can expand. This
// includes all environment variables being passed to the child.
HashMap *vars;
pam_handle_t *pam = internalLogin(service, utmp, &environment);
#if defined(HAVE_SECURITY_PAM_APPL_H)
if (pam && !geteuid()) {
- check(pam_open_session(pam, PAM_SILENT) == PAM_SUCCESS);
+ if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) {
+ fprintf(stderr, "Access denied.\n");
+ _exit(1);
+ }
pid_t pid = fork();
switch (pid) {
case -1:
}
static void sigChildHandler(int sig, siginfo_t *info, void *unused) {
+ (void)sig;
+ (void)info;
+ (void)unused;
}
static void launcherDaemon(int fd) {
// privileges.c -- Manage process privileges
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
getresuid(&ru, &eu, &su);
// Try to switch the user-provided group.
- if ((ru && runAsGroup != rg) ||
+ if ((ru && runAsGroup != (int)rg) ||
setresgid(runAsGroup, runAsGroup, runAsGroup)) {
if (showError) {
fatal("Only privileged users can change their group memberships");
if (runAsUser >= 0) {
// Try to switch to the user-provided user id.
- if (r && runAsUser != r) {
+ if (r && runAsUser != (int)r) {
fatal("Only privileged users can change their user id");
}
check(!setresuid(runAsUser, runAsUser, -1));
if (runAsUser >= 0) {
// Try to switch to the user-provided user id.
- if ((r && runAsUser != r) ||
+ if ((r && runAsUser != (int)r) ||
setresuid(runAsUser, runAsUser, runAsUser)) {
fatal("Only privileged users can change their user id.");
}
// service.c -- Service descriptions
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
}
void destroyServiceHashEntry(void *arg, char *key, char *value) {
+ (void)arg;
+ (void)key;
+ (void)value;
}
static int enumerateServicesHelper(void *arg, const char *key, char **value) {
+ (void)arg;
+ (void)key;
+
check(services = realloc(services,
++numServices * sizeof(struct Service *)));
services[numServices-1] = *(struct Service **)value;
// service.h -- Service descriptions
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
int useHomeDir;
int authUser;
int useDefaultShell;
- int uid;
- int gid;
+ int uid; /* Can be -1 */
+ int gid; /* Can be -1 */
const char *user;
const char *group;
const char *cwd;
// session.c -- Session management for HTTP/HTTPS connections
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
}
static void destroySessionHashEntry(void *arg, char *key, char *value) {
+ (void)arg;
+ (void)key;
+
deleteSession((struct Session *)value);
}
char *ptr = sessionKey;
int count = 0;
int bits = 0;
- for (int i = 0;;) {
+ for (unsigned i = 0;;) {
bits = (bits << 8) | buf[i];
count += 8;
drain:
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
};
ShellInABox.prototype.about = function() {
- alert("Shell In A Box version " + "2.10 (revision 200)" +
- "\nCopyright 2008-2009 by Markus Gutschke\n" +
+ alert("Shell In A Box version " + "2.10 (revision 202)" +
+ "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" +
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + VERSION +
- "\nCopyright 2008-2009 by Markus Gutschke\n" +
+ "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" +
// shellinaboxd.c -- A custom web server that makes command line applications
// available as AJAX web applications.
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
static int handleSession(struct ServerConnection *connection, void *arg,
short *events, short revents) {
+ (void)events;
struct Session *session = (struct Session *)arg;
session->connection = connection;
int len = MAX_RESPONSE - session->len;
static int dataHandler(HttpConnection *http, struct Service *service,
const char *buf, int len, URL *url) {
+ (void)len;
if (!buf) {
// Somebody unexpectedly closed our http connection (e.g. because of a
// timeout). This is the last notification that we will get.
// Remember the beginning of the "[if ...]" statement
ifPtr = ptr;
}
- } else if (ifPtr && !elsePtr && eol - ptr >= strlen(tag) + 7 &&
+ } else if (ifPtr && !elsePtr && eol - ptr >= (ssize_t)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 &&
+ } else if (ifPtr && eol - ptr >= (ssize_t)strlen(tag) + 8 &&
!memcmp(ptr, "[endif ", 7) &&
!memcmp(ptr + 7, tag, strlen(tag)) &&
ptr[7 + strlen(tag)] == ']') {
}
static void destroyExternalFileHashEntry(void *arg, char *key, char *value) {
+ (void)arg;
free(key);
free(value);
}
st.st_size + 2));
char *newData = strrchr(cssStyleSheet, '\000');
*newData++ = '\n';
- if (fread(newData, 1, st.st_size, css) != st.st_size) {
+ if (fread(newData, st.st_size, 1, css) != 1) {
fatal("Failed to read style sheet \"%s\"", optarg);
}
newData[st.st_size]= '\000';
static void removeLimits() {
static int res[] = { RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_NPROC };
- for (int i = 0; i < sizeof(res)/sizeof(int); i++) {
+ for (unsigned i = 0; i < sizeof(res)/sizeof(int); i++) {
struct rlimit rl;
getrlimit(res[i], &rl);
if (rl.rlim_max < RLIM_INFINITY) {
// usercss.c -- Defines user-selectable CSS options
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
static struct HashMap *defines;
static void definesDestructor(void *arg, char *key, char *value) {
+ (void)arg;
+ (void)value;
+
free(key);
}
FILE *fp;
check(fp = fdopen(fd, "r"));
check(*style = malloc(st.st_size + 1));
- check(fread(*style, 1, st.st_size, fp) == st.st_size);
+ check(fread(*style, st.st_size, 1, fp) == 1);
(*style)[st.st_size] = '\000';
*len = st.st_size;
fclose(fp);
// VT100.js -- JavaScript based terminal emulator
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
};
VT100.prototype.about = function() {
- alert("VT100 Terminal Emulator " + "2.10 (revision 200)" +
- "\nCopyright 2008-2009 by Markus Gutschke\n" +
+ alert("VT100 Terminal Emulator " + "2.10 (revision 202)" +
+ "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com");
};
// VT100.js -- JavaScript based terminal emulator
-// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + VERSION +
- "\nCopyright 2008-2009 by Markus Gutschke\n" +
+ "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com");
};