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. git-svn-id: https://shellinabox.googlecode.com/svn/trunk@202 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
5a75b2f091
commit
9b0a937e35
29 changed files with 581 additions and 138 deletions
2
COPYING
2
COPYING
|
@ -1,4 +1,4 @@
|
|||
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
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
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
|
||||
|
|
38
Makefile.am
38
Makefile.am
|
@ -2,6 +2,8 @@ AM_CPPFLAGS =
|
|||
AM_CFLAGS = -g -std=gnu99 -Wall
|
||||
AM_LDFLAGS = -g
|
||||
|
||||
OBJCOPY ?= objcopy
|
||||
|
||||
noinst_LTLIBRARIES = libhttp.la \
|
||||
liblogging.la
|
||||
noinst_DATA = $(top_srcdir)/demo/demo.js
|
||||
|
@ -234,37 +236,37 @@ clean-local:
|
|||
-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
|
||||
|
||||
|
||||
|
@ -280,20 +282,20 @@ 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
|
||||
|
||||
|
||||
|
|
38
Makefile.in
38
Makefile.in
|
@ -1152,6 +1152,8 @@ uninstall-man: uninstall-man1
|
|||
uninstall-man uninstall-man1
|
||||
|
||||
|
||||
OBJCOPY ?= objcopy
|
||||
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
$(SHELL) ./config.status --recheck
|
||||
|
||||
|
@ -1247,35 +1249,35 @@ clean-local:
|
|||
-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
|
||||
|
@ -1290,19 +1292,19 @@ 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.
|
||||
|
|
5
config.h
5
config.h
|
@ -25,6 +25,9 @@
|
|||
/* 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 */
|
||||
|
||||
|
@ -141,7 +144,7 @@
|
|||
#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"
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
/* 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
|
||||
|
||||
|
|
21
configure
vendored
21
configure
vendored
|
@ -2325,7 +2325,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
|
|||
ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
|
||||
VCS_REVISION=200
|
||||
VCS_REVISION=202
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
|
@ -10682,6 +10682,25 @@ if ac_fn_c_try_link "$LINENO"; then :
|
|||
|
||||
$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
|
||||
|
|
|
@ -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.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])
|
||||
|
@ -55,6 +55,12 @@ AC_TRY_LINK([#include <pthread.h>
|
|||
[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,
|
||||
|
|
2
debian/copyright
vendored
2
debian/copyright
vendored
|
@ -5,7 +5,7 @@ It was downloaded from http://shellinabox.com/
|
|||
|
||||
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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -1955,8 +1955,8 @@ VT100.prototype.toggleBell = function() {
|
|||
};
|
||||
|
||||
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");
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -56,7 +56,13 @@
|
|||
#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__;})
|
||||
|
||||
|
@ -77,6 +83,9 @@ void serverRegisterHttpHandler(Server *server, const char *url,
|
|||
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,
|
||||
|
@ -109,6 +118,10 @@ void *httpSetPrivate(HttpConnection *http, void *private);
|
|||
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 *);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -66,6 +66,9 @@
|
|||
#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; })
|
||||
|
||||
|
@ -223,8 +226,9 @@ static char *strcasestr(const char *haystack, const char *needle) {
|
|||
|
||||
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;
|
||||
|
@ -267,6 +271,7 @@ static int httpFinishCommand(struct HttpConnection *http) {
|
|||
}
|
||||
|
||||
static void httpDestroyHeaders(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
@ -296,7 +301,7 @@ static char *getPeerName(int fd, int *port, int numericHosts) {
|
|||
}
|
||||
|
||||
static void httpSetState(struct HttpConnection *http, int state) {
|
||||
if (state == http->state) {
|
||||
if (state == (int)http->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -372,7 +377,9 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server,
|
|||
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;
|
||||
|
@ -388,8 +395,12 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server,
|
|||
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;
|
||||
|
@ -774,41 +785,41 @@ void httpTransferPartialReply(struct HttpConnection *http, char *msg, int len){
|
|||
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") ||
|
||||
|
@ -820,52 +831,116 @@ static int httpHandleCommand(struct HttpConnection *http,
|
|||
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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') {
|
||||
check(!http->matchedPath);
|
||||
check(!http->pathInfo);
|
||||
check(!http->query);
|
||||
bad_ws_upgrade:;
|
||||
|
||||
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->handler) {
|
||||
check(diff);
|
||||
while (diff > http->path && diff[-1] == '/') {
|
||||
diff--;
|
||||
}
|
||||
if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') {
|
||||
check(!http->matchedPath);
|
||||
check(!http->pathInfo);
|
||||
check(!http->query);
|
||||
|
||||
if (query) {
|
||||
check(http->query = strdup(query + 1));
|
||||
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);
|
||||
|
@ -1011,13 +1086,20 @@ static int httpParseHeaders(struct HttpConnection *http,
|
|||
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;
|
||||
|
@ -1187,6 +1269,140 @@ static int httpParsePayload(struct HttpConnection *http, int offset,
|
|||
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_;
|
||||
|
@ -1219,6 +1435,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *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,
|
||||
|
@ -1241,7 +1458,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
|
|||
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;
|
||||
|
@ -1262,6 +1479,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes > 0 || (eof && http->partial)) {
|
||||
check(!!http->partial == !!http->partialLength);
|
||||
int offset = -http->partialLength;
|
||||
|
@ -1331,7 +1549,16 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
|
|||
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) {
|
||||
|
@ -1383,6 +1610,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
|
|||
break;
|
||||
case PAYLOAD:
|
||||
case DISCARD_PAYLOAD:
|
||||
case WEBSOCKET:
|
||||
http->expecting = 0;
|
||||
httpCloseRead(http);
|
||||
httpSetState(http, COMMAND);
|
||||
|
@ -1563,6 +1791,89 @@ void httpSendReply(struct HttpConnection *http, int code,
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -60,6 +60,10 @@
|
|||
#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 {
|
||||
|
@ -71,7 +75,8 @@ 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;
|
||||
|
@ -91,8 +96,11 @@ struct HttpConnection {
|
|||
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;
|
||||
|
@ -104,6 +112,8 @@ struct HttpConnection {
|
|||
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;
|
||||
|
||||
};
|
||||
|
@ -128,6 +138,11 @@ void *httpSetPrivate(struct HttpConnection *http, void *private);
|
|||
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*);
|
||||
|
|
|
@ -5,6 +5,7 @@ serverGetListeningPort
|
|||
serverGetFd
|
||||
serverRegisterHttpHandler
|
||||
serverRegisterStreamingHttpHandler
|
||||
serverRegisterWebSocketHandler
|
||||
serverAddConnection
|
||||
serverDeleteConnection
|
||||
serverSetTimeout
|
||||
|
@ -24,6 +25,8 @@ httpSetCallback
|
|||
httpGetPrivate
|
||||
httpSetPrivate
|
||||
httpSendReply
|
||||
httpSendWebSocketTextMsg
|
||||
httpSendWebSocketBinaryMsg
|
||||
httpExitLoop
|
||||
httpGetServer
|
||||
httpGetServerConnection
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -129,6 +129,7 @@ static int serverCollectHandler(struct HttpConnection *http, void *handler_) {
|
|||
}
|
||||
|
||||
static void serverDestroyHandlers(void *arg, char *value) {
|
||||
(void)arg;
|
||||
free(value);
|
||||
}
|
||||
|
||||
|
@ -143,6 +144,7 @@ void serverRegisterHttpHandler(struct Server *server, const char *url,
|
|||
h->handler = serverCollectHandler;
|
||||
h->arg = h;
|
||||
h->streamingHandler = handler;
|
||||
h->websocketHandler = NULL;
|
||||
h->streamingArg = arg;
|
||||
addToTrie(&server->handlers, url, (char *)h);
|
||||
}
|
||||
|
@ -158,13 +160,31 @@ void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
|
|||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -93,6 +93,9 @@ void serverRegisterHttpHandler(struct Server *server, const char *url,
|
|||
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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -288,11 +288,11 @@ static void loadSSL(void) {
|
|||
{ { &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;
|
||||
|
@ -361,7 +361,7 @@ static const unsigned char *sslSecureReadASCIIFileToMem(int fd) {
|
|||
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;
|
||||
|
@ -414,7 +414,7 @@ static const unsigned char *sslPEMtoASN1(const unsigned char *pem,
|
|||
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;
|
||||
|
@ -542,6 +542,7 @@ static int sslSetCertificateFromFile(SSL_CTX *context,
|
|||
|
||||
#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);
|
||||
|
@ -603,7 +604,7 @@ static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
|
|||
}
|
||||
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;
|
||||
|
@ -616,6 +617,8 @@ static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -87,6 +87,7 @@ static char *urlUnescape(char *s) {
|
|||
}
|
||||
|
||||
static void urlDestroyHashMapEntry(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -321,7 +321,7 @@ static void loadPAM(void) {
|
|||
{ { &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")) {
|
||||
|
@ -336,7 +336,7 @@ static void loadPAM(void) {
|
|||
}
|
||||
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;
|
||||
|
@ -419,7 +419,7 @@ int launchChild(int service, struct Session *session, const char *url) {
|
|||
}
|
||||
|
||||
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;
|
||||
|
@ -538,6 +538,8 @@ void deleteUtmp(struct Utmp *utmp) {
|
|||
}
|
||||
|
||||
static void destroyUtmpHashEntry(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
deleteUtmp((struct Utmp *)value);
|
||||
}
|
||||
|
||||
|
@ -765,6 +767,9 @@ static const struct passwd *getPWEnt(uid_t uid) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -958,7 +963,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
|||
}
|
||||
|
||||
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 */) {
|
||||
|
@ -1004,7 +1009,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
|||
if (i == ngroups) {
|
||||
groups[ngroups++] = service->gid;
|
||||
break;
|
||||
} else if (groups[i] == service->gid) {
|
||||
} else if ((int)groups[i] == service->gid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1047,6 +1052,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
|||
}
|
||||
|
||||
static void destroyVariableHashEntry(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
@ -1054,6 +1060,9 @@ static void destroyVariableHashEntry(void *arg, char *key, char *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;
|
||||
|
@ -1346,7 +1355,10 @@ static void childProcess(struct Service *service, int width, int height,
|
|||
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:
|
||||
|
@ -1409,6 +1421,9 @@ static void childProcess(struct Service *service, int width, int height,
|
|||
}
|
||||
|
||||
static void sigChildHandler(int sig, siginfo_t *info, void *unused) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)unused;
|
||||
}
|
||||
|
||||
static void launcherDaemon(int fd) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -76,7 +76,7 @@ static void removeGroupPrivileges(int showError) {
|
|||
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");
|
||||
|
@ -110,7 +110,7 @@ void lowerPrivileges(void) {
|
|||
|
||||
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));
|
||||
|
@ -136,7 +136,7 @@ void dropPrivileges(void) {
|
|||
|
||||
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.");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -243,9 +243,15 @@ void deleteService(struct Service *service) {
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -55,8 +55,8 @@ struct Service {
|
|||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -153,6 +153,9 @@ void finishAllSessions(void) {
|
|||
}
|
||||
|
||||
static void destroySessionHashEntry(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
|
||||
deleteSession((struct Session *)value);
|
||||
}
|
||||
|
||||
|
@ -167,7 +170,7 @@ char *newSessionKey(void) {
|
|||
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:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -358,8 +358,8 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
|
|||
};
|
||||
|
||||
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" +
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -359,7 +359,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
|
|||
|
||||
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" +
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
|
@ -257,6 +257,7 @@ static void sessionDone(void *arg) {
|
|||
|
||||
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;
|
||||
|
@ -311,6 +312,7 @@ static int invalidatePendingHttpSession(void *arg, const char *key,
|
|||
|
||||
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.
|
||||
|
@ -514,13 +516,13 @@ static void serveStaticFile(HttpConnection *http, const char *contentType,
|
|||
// 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)] == ']') {
|
||||
|
@ -810,6 +812,7 @@ static void usage(void) {
|
|||
}
|
||||
|
||||
static void destroyExternalFileHashEntry(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
@ -938,7 +941,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
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';
|
||||
|
@ -1149,7 +1152,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
|
||||
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) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -60,6 +60,9 @@
|
|||
static struct HashMap *defines;
|
||||
|
||||
static void definesDestructor(void *arg, char *key, char *value) {
|
||||
(void)arg;
|
||||
(void)value;
|
||||
|
||||
free(key);
|
||||
}
|
||||
|
||||
|
@ -73,7 +76,7 @@ static void readStylesheet(struct UserCSS *userCSS, const char *filename,
|
|||
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);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -1955,8 +1955,8 @@ VT100.prototype.toggleBell = function() {
|
|||
};
|
||||
|
||||
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");
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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
|
||||
|
@ -1956,7 +1956,7 @@ VT100.prototype.toggleBell = function() {
|
|||
|
||||
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");
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue