commit 5c65f44299826c55b268501d35bffae5ecfb7d99
parent d61d2bc59df349564f399318a10fe7345b9c7a2c
Author: Remy Noulin <loader2x@gmail.com>
Date: Sun, 12 Nov 2017 21:14:48 +0100
initial commit
.gitignore | 2 +
index.html | 16 ++
inoty.h | 10 ++
live-server.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++
package.yml | 20 +++
page2.html | 14 ++
scott.jpg | Bin 0 -> 23251 bytes
server.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++
server.log | 8 +
utilities.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
utilities.h | 74 +++++++++
11 files changed, 1522 insertions(+)
Diffstat:
| M | .gitignore | | | 2 | ++ |
| A | index.html | | | 16 | ++++++++++++++++ |
| A | inoty.h | | | 10 | ++++++++++ |
| A | live-server.c | | | 448 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | package.yml | | | 21 | +++++++++++++++++++++ |
| A | page2.html | | | 14 | ++++++++++++++ |
| A | scott.jpg | | | 0 | |
| A | server.c | | | 448 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | server.log | | | 8 | ++++++++ |
| A | utilities.c | | | 482 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | utilities.h | | | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
11 files changed, 1523 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,3 +1,5 @@
+a
+
# Prerequisites
*.d
diff --git a/index.html b/index.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Oscar's web server example</title>
+</head>
+<BODY BGCOLOR="grey">
+ <center>
+ <h1>Welcome to the default homepage!</h1>
+ <img src="scott.jpg" />
+ <p>Here's an image of scott pilgrim... he's really cool and you can learn about him <a href="http://scottpilgrim.com">here</a>.</p>
+ </center>
+ <p>
+ Just some basic default page. For another page... go <a href="page2.html">here</a>.
+ </p>
+</body>
+</html>
diff --git a/inoty.h b/inoty.h
@@ -0,0 +1,10 @@
+
+#include <sys/inotify.h>
+
+#define EVENT_SIZE ( sizeof (struct inotify_event) )
+#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
+
+#define inotiFy_init inotify_init
+#define inotiFy_add_watch inotify_add_watch
+#define inotiFy_event inotify_event
+#define inotiFy_rm_watch inotify_rm_watch
diff --git a/live-server.c b/live-server.c
@@ -0,0 +1,448 @@
+#! /usr/bin/env sheepy
+
+/*
+ * project: miniweb
+ * author: Oscar Sanchez (oms1005@gmail.com)
+ * HTTP Server
+ * WORKS ON BROWSERS TOO!
+ * Inspired by IBM's nweb
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "inoty.h"
+
+#include "libsheepyObject.h"
+#include "utilities.h"
+
+#define BUFSIZE 8096
+#define ERROR 42
+#define SORRY 43
+#define LOG 44
+
+i64 waitTime = 0;
+
+smallArrayt *dirList;
+
+const char injected[] = "<!-- Code injected by live-server -->\n\
+<script type=\"text/javascript\">\n\
+ // <![CDATA[ <-- For SVG support\n\
+ if ('WebSocket' in window) {\n\
+ (function() {\n\
+ function refreshCSS() {\n\
+ var sheets = [].slice.call(document.getElementsByTagName(\"link\"));\n\
+ var head = document.getElementsByTagName(\"head\")[0];\n\
+ for (var i = 0; i < sheets.length; ++i) {\n\
+ var elem = sheets[i];\n\
+ head.removeChild(elem);\n\
+ var rel = elem.rel;\n\
+ if (elem.href && typeof rel != \"string\" || rel.length == 0 || rel.toLowerCase() == \"stylesheet\") {\n\
+ var url = elem.href.replace(/(&|\\?)_cacheOverride=\\d+/, '');\n\
+ elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());\n\
+ }\n\
+ head.appendChild(elem);\n\
+ }\n\
+ }\n\
+ var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';\n\
+ var address = protocol + window.location.host + window.location.pathname + '/ws';\n\
+ var socket = new WebSocket(address);\n\
+ socket.onmessage = function(msg) {\n\
+ if (msg.data == 'reload') window.location.reload();\n\
+ else if (msg.data == 'refreshcss') refreshCSS();\n\
+ };\n\
+ console.log('Live reload enabled.');\n\
+ })();\n\
+ }\n\
+ // ]]>\n\
+</script>\n\
+";
+
+struct {
+ char *ext;
+ char *filetype;
+} extensions [] = {
+ {"gif", "image/gif" },
+ {"jpg", "image/jpeg"},
+ {"jpeg","image/jpeg"},
+ {"png", "image/png" },
+ {"zip", "image/zip" },
+ {"gz", "image/gz" },
+ {"tar", "image/tar" },
+ {"htm", "text/html" },
+ {"html","text/html" },
+ {"php", "image/php" },
+ {"cgi", "text/cgi" },
+ {"asp", "text/asp" },
+ {"jsp", "image/jsp" },
+ {"xml", "text/xml" },
+ {"js", "text/js" },
+ {"css", "text/css" },
+ {"//ws", "websocket"},
+
+ {0,0} };
+
+#define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+#define WEBSOCK_PACKAGE_NAME "s"
+#define WEBSOCK_PACKAGE_VERSION "0"
+
+void slog(int type, char *s1, char *s2, int num)
+{
+ int fd ;
+ char logbuffer[BUFSIZE*2];
+
+ switch (type) {
+ case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break;
+ case SORRY:
+ (void)sprintf(logbuffer, "<HTML><BODY><H1>Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2);
+ (void)write(num,logbuffer,strlen(logbuffer));
+ (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2);
+ break;
+ case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break;
+ }
+
+ if((fd = open("server.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) {
+ (void)write(fd,logbuffer,strlen(logbuffer));
+ (void)write(fd,"\n",1);
+ (void)close(fd);
+ }
+ if(type == ERROR || type == SORRY) exit(3);
+}
+
+void web(int fd, int hit)
+{
+ int j, file_fd, buflen, len;
+ long i, ret;
+ char * fstr;
+ static char buffer[BUFSIZE+1];
+
+ ret =read(fd,buffer,BUFSIZE);
+ if(ret == 0 || ret == -1) {
+ slog(SORRY,"failed to read browser request","",fd);
+ }
+ if(ret > 0 && ret < BUFSIZE)
+ buffer[ret]=0;
+ else buffer[0]=0;
+
+ smallStringt *reqS = allocG(buffer);
+ smallArrayt *req = splitG(reqS, "\r\n");
+ char *clientKey = NULL;
+ char *origin = NULL;
+ char *protocol = NULL; // protocol not parsed, firefox 52 doesnt send it
+ forEachSmallArray(req, S) {
+ castS(s,S);
+ if (startsWithG(s, "Sec-WebSocket-Key:")) {
+ clientKey = ssGet(s) + 19;
+ }
+ if(startsWithSG(s, "Origin:")) {
+ origin = ssGet(s) + 8;
+ }
+ finishG(s);
+ }
+
+ for(i=0;i<ret;i++)
+ if(buffer[i] == '\r' || buffer[i] == '\n')
+ buffer[i]='*';
+ slog(LOG,"request",buffer,hit);
+
+ if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) )
+ slog(SORRY,"Only simple GET operation supported",buffer,fd);
+
+ for(i=4;i<BUFSIZE;i++) {
+ if(buffer[i] == ' ') {
+ buffer[i] = 0;
+ break;
+ }
+ }
+
+ for(j=0;j<i-1;j++)
+ if(buffer[j] == '.' && buffer[j+1] == '.')
+ slog(SORRY,"Parent directory (..) path names not supported",buffer,fd);
+
+ if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) )
+ (void)strcpy(buffer,"GET /index.html");
+
+ bool isHTML = false;
+ bool isWebsocket = false;
+
+ buflen=strlen(buffer);
+ fstr = (char *)0;
+ for(i=0;extensions[i].ext != 0;i++) {
+ len = strlen(extensions[i].ext);
+ if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
+ fstr =extensions[i].filetype;
+ if (startsWithG(extensions[i].ext, "htm")) isHTML = true;
+ else if (eqS(extensions[i].ext, "//ws")) isWebsocket = true;
+ break;
+ }
+ }
+ if(fstr == 0) slog(SORRY,"file extension type not supported",buffer,fd);
+
+ if (isWebsocket) {
+ // WEBSOCKET HANDLING
+
+ slog(LOG,"websocket","detected",hit);
+ slog(LOG,"key",clientKey,hit);
+
+ SHA1Context shactx;
+ SHA1Reset(&shactx);
+
+ char buf[1024];
+ sprintf(buf, "%s%s", clientKey, WEBSOCKET_SERVER_RESPONSE);
+
+ SHA1Input(&shactx, (unsigned char *) buf, strlen(buf));
+ SHA1Result(&shactx);
+
+ char sha1buf[45];
+ unsigned char sha1mac[20];
+
+ sprintf(sha1buf, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0],
+ shactx.Message_Digest[1], shactx.Message_Digest[2],
+ shactx.Message_Digest[3], shactx.Message_Digest[4]);
+
+ for (i16 n = 0; n < (strlen(sha1buf) / 2); n++) {
+ sscanf(sha1buf + (n * 2), "%02hhx", sha1mac + n);
+ }
+
+ char base64buf[256];
+ base64_encode(sha1mac, 20, base64buf, 256);
+ memset(buf, 0, 1024);
+ snprintf(buf, 1024, "HTTP/1.1 101 Switching Protocols\r\n"
+ "Server: %s/%s\r\n"
+ "Upgrade: websocket\r\n"
+ "%s%s%s"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n%s%s"
+ "Access-Control-Allow-Headers: content-type\r\n\r\n", WEBSOCK_PACKAGE_NAME,
+ WEBSOCK_PACKAGE_VERSION, origin ? "Access-Control-Allow-Origin: " : "", origin ? origin : "", origin ? "\r\n" : "", base64buf, protocol ? protocol : "", protocol ? "\r\n" : "");
+
+ (void)write(fd,buf,strlen(buf));
+
+ // INOTIFY SETUP
+
+ // wait for changes and reload page
+ enum {yes, no};
+ i8 foundRelevantUpdate = no;
+ while (foundRelevantUpdate == no) {
+ // creating the INOTIFY instance
+ int length;
+ char ibuffer[EVENT_BUF_LEN];
+ int ifd = inotiFy_init();
+ if (ifd == -1) {
+ perror("inotiFy_init error: ");
+ }
+
+ int wd; // watch descriptor
+
+ forEachSmallArray(dirList, E) {
+ castS(e, E);
+ wd = inotiFy_add_watch( ifd, ssGet(e), IN_CREATE | IN_DELETE | IN_MODIFY);
+ finishG(e);
+ }
+
+ // read to determine the event change happens on “.” directory. Actually this read blocks until the change event occurs
+ length = read( ifd, ibuffer, EVENT_BUF_LEN );;
+
+ if (length == -1) {
+ perror("read error: ");
+ }
+
+ u32 i = 0;
+ // actually read return the list of change events happens. Here, read the change event one by one and process it accordingly.
+ while (( i < length )) {
+ struct inotiFy_event *event = ( struct inotiFy_event * ) &ibuffer[ i ];
+
+ for(i=0;extensions[i].ext != 0;i++) {
+ if (endsWithG(event->name, extensions[i].ext)) {
+ if ((event->mask & IN_CREATE) || (event->mask & IN_DELETE) || (event->mask & IN_MODIFY)) {
+ foundRelevantUpdate = yes;
+ }
+ }
+ }
+
+ i += EVENT_SIZE + event->len;
+ }
+
+
+ // removing the “/tmp” directory from the watch list.
+ inotiFy_rm_watch( ifd, wd );
+
+ // closing the INOTIFY instance
+ close(ifd);
+ }
+
+ if (waitTime > 0) {
+ // wait before reloading
+ sleep(waitTime);
+ }
+
+ // WEBSOCKET SEND RELOAD TO CLIENT
+
+ // send reload
+ u32 fin = 1;
+ u32 op = 1;
+ u32 plen = 6;
+ u32 blen = plen+2;
+
+ buf[0] = (1 << 7) + fin; // 0x81
+ buf[1] = plen;
+ /* uint16_t *b = (uint16_t*)buf; */
+ /* *b = (plen << 9) + (op << 4) + fin; */
+ strcpy(buf+2, "reload");
+
+ (void)write(fd,buf,blen);
+ } else {
+
+ // SERVE REGULAR HTTP REQUESTS
+
+ char *serveFile = &buffer[5];
+
+ if (isHTML) {
+ size_t endBodyAt = 0;
+ createAllocateSmallArray(f);
+ readFileG(f, &buffer[5]);
+ enumerateSmallArray(f, S, n) {
+ castS(s, S);
+ if (hasG(lowerG(s), "</body>")) {
+ endBodyAt = n;
+ finishG(s);
+ break;
+ }
+ finishG(s);
+ }
+ injectSG(f, endBodyAt, injected);
+ writeFileG(f, "a");
+ terminateG(f);
+ serveFile = "a";
+ }
+
+ if(( file_fd = open(serveFile,O_RDONLY)) == -1)
+ slog(SORRY, "failed to open file",serveFile,fd);
+
+ slog(LOG,"SEND",serveFile,hit);
+
+ (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
+ (void)write(fd,buffer,strlen(buffer));
+
+ while ((ret = read(file_fd, buffer, BUFSIZE)) > 0 ) {
+ (void)write(fd,buffer,ret);
+ }
+ }
+
+ terminateManyG(reqS, req);
+
+#ifdef LINUX
+ sleep(1);
+#endif
+ exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+ int i, port, pid, listenfd, socketfd, hit;
+ size_t length;
+ static struct sockaddr_in cli_addr;
+ static struct sockaddr_in serv_addr;
+ char *portS;
+ char *dir = NULL;
+
+ // defaults
+ port = 8080;
+ portS = "8080";
+
+ forEachS(argv, a) {
+ if (startsWithG(a, "-port=")) {
+ portS = a+6;
+ port = parseInt(portS);
+ }
+ else if (startsWithG(a, "-path=")) {
+ dir = a+6;
+ }
+ else if (startsWithG(a, "-wait=")) {
+ waitTime = parseInt(a+6);
+ }
+ else if (startsWithG(a, "--help") or startsWithG(a, "-h") or startsWithG(a, "-?")) {
+ printf("usage: server [-port=80] [-path=server_directory] [-wait=1]\n"
+ "\twait unit is second.\n"
+ "\tExample: server -port=8080 -path=./ -wait=1\n\n"
+ "\tOnly Supports:");
+ for(i=0;extensions[i].ext != 0;i++)
+ (void)printf(" %s",extensions[i].ext);
+
+ (void)printf("\n\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n"
+ );
+ XSUCCESS
+ }
+ }
+
+ if (dir) {
+ if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) ||
+ !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) ||
+ !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) ||
+ !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){
+ (void)printf("ERROR: Bad top directory %s, see server -?\n",dir);
+ exit(3);
+ }
+ if(chdir(dir) == -1){
+ (void)printf("ERROR: Can't Change to directory %s\n",dir);
+ exit(4);
+ }
+ }
+
+ dirList = allocG(rtSmallArrayt);
+ execO("find . -type d", dirList, NULL);
+
+ if(fork() != 0)
+ return 0;
+ (void)signal(SIGCLD, SIG_IGN);
+ (void)signal(SIGHUP, SIG_IGN);
+ for(i=0;i<32;i++)
+ (void)close(i);
+ (void)setpgrp();
+
+ slog(LOG,"http server starting",portS,getpid());
+
+ if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
+ slog(ERROR, "system call","socket",0);
+
+ if(port < 0 || port >60000)
+ slog(ERROR,"Invalid port number try [1,60000]",portS,0);
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+ if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
+ slog(ERROR,"system call","bind",0);
+ if( listen(listenfd,64) <0)
+ slog(ERROR,"system call","listen",0);
+
+ for(hit=1; ;hit++) {
+ length = sizeof(cli_addr);
+ if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t *)&length)) < 0)
+ slog(ERROR,"system call","accept",0);
+
+ if((pid = fork()) < 0) {
+ slog(ERROR,"system call","fork",0);
+ }
+ else {
+ if(pid == 0) {
+ (void)close(listenfd);
+ web(socketfd,hit);
+ } else {
+ (void)close(socketfd);
+ }
+ }
+ }
+
+ terminateG(dirList);
+
+}
diff --git a/package.yml b/package.yml
@@ -0,0 +1,20 @@
+---
+ name: live-server
+ version: 0.0.1
+ description: "explaination"
+ bin: ./live-server.c
+ #cflags: -DA
+ #lflags: -lpcre
+ repository:
+ type: git
+ url: git+https://github.com/USER/live-server.git
+ keywords:
+ #- utility
+ - command
+ author: Anonymous
+ license: MIT
+ bugs:
+ url: https://github.com/USER/live-server/issues
+ homepage: https://github.com/USER/live-server#readme
+ #dependencies:
+ # md4c: +
\ No newline at end of file
diff --git a/page2.html b/page2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>Oscar's web server example</title>
+<BODY BGCOLOR="grey">
+<center>
+<H1>PAGE 2</H1>
+<p>
+This is page 2! Welcome :)
+</p>
+
+</center>
+</table>
+</BODY>
+</HTML>
diff --git a/scott.jpg b/scott.jpg
Binary files differ.
diff --git a/server.c b/server.c
@@ -0,0 +1,448 @@
+#! /usr/bin/env sheepy
+
+/*
+ * project: miniweb
+ * author: Oscar Sanchez (oms1005@gmail.com)
+ * HTTP Server
+ * WORKS ON BROWSERS TOO!
+ * Inspired by IBM's nweb
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "inoty.h"
+
+#include "libsheepyObject.h"
+#include "utilities.h"
+
+#define BUFSIZE 8096
+#define ERROR 42
+#define SORRY 43
+#define LOG 44
+
+i64 waitTime = 0;
+
+smallArrayt *dirList;
+
+const char injected[] = "<!-- Code injected by live-server -->\n\
+<script type=\"text/javascript\">\n\
+ // <![CDATA[ <-- For SVG support\n\
+ if ('WebSocket' in window) {\n\
+ (function() {\n\
+ function refreshCSS() {\n\
+ var sheets = [].slice.call(document.getElementsByTagName(\"link\"));\n\
+ var head = document.getElementsByTagName(\"head\")[0];\n\
+ for (var i = 0; i < sheets.length; ++i) {\n\
+ var elem = sheets[i];\n\
+ head.removeChild(elem);\n\
+ var rel = elem.rel;\n\
+ if (elem.href && typeof rel != \"string\" || rel.length == 0 || rel.toLowerCase() == \"stylesheet\") {\n\
+ var url = elem.href.replace(/(&|\\?)_cacheOverride=\\d+/, '');\n\
+ elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());\n\
+ }\n\
+ head.appendChild(elem);\n\
+ }\n\
+ }\n\
+ var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';\n\
+ var address = protocol + window.location.host + window.location.pathname + '/ws';\n\
+ var socket = new WebSocket(address);\n\
+ socket.onmessage = function(msg) {\n\
+ if (msg.data == 'reload') window.location.reload();\n\
+ else if (msg.data == 'refreshcss') refreshCSS();\n\
+ };\n\
+ console.log('Live reload enabled.');\n\
+ })();\n\
+ }\n\
+ // ]]>\n\
+</script>\n\
+";
+
+struct {
+ char *ext;
+ char *filetype;
+} extensions [] = {
+ {"gif", "image/gif" },
+ {"jpg", "image/jpeg"},
+ {"jpeg","image/jpeg"},
+ {"png", "image/png" },
+ {"zip", "image/zip" },
+ {"gz", "image/gz" },
+ {"tar", "image/tar" },
+ {"htm", "text/html" },
+ {"html","text/html" },
+ {"php", "image/php" },
+ {"cgi", "text/cgi" },
+ {"asp", "text/asp" },
+ {"jsp", "image/jsp" },
+ {"xml", "text/xml" },
+ {"js", "text/js" },
+ {"css", "text/css" },
+ {"//ws", "websocket"},
+
+ {0,0} };
+
+#define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+#define WEBSOCK_PACKAGE_NAME "s"
+#define WEBSOCK_PACKAGE_VERSION "0"
+
+void slog(int type, char *s1, char *s2, int num)
+{
+ int fd ;
+ char logbuffer[BUFSIZE*2];
+
+ switch (type) {
+ case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break;
+ case SORRY:
+ (void)sprintf(logbuffer, "<HTML><BODY><H1>Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2);
+ (void)write(num,logbuffer,strlen(logbuffer));
+ (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2);
+ break;
+ case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break;
+ }
+
+ if((fd = open("server.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) {
+ (void)write(fd,logbuffer,strlen(logbuffer));
+ (void)write(fd,"\n",1);
+ (void)close(fd);
+ }
+ if(type == ERROR || type == SORRY) exit(3);
+}
+
+void web(int fd, int hit)
+{
+ int j, file_fd, buflen, len;
+ long i, ret;
+ char * fstr;
+ static char buffer[BUFSIZE+1];
+
+ ret =read(fd,buffer,BUFSIZE);
+ if(ret == 0 || ret == -1) {
+ slog(SORRY,"failed to read browser request","",fd);
+ }
+ if(ret > 0 && ret < BUFSIZE)
+ buffer[ret]=0;
+ else buffer[0]=0;
+
+ smallStringt *reqS = allocG(buffer);
+ smallArrayt *req = splitG(reqS, "\r\n");
+ char *clientKey = NULL;
+ char *origin = NULL;
+ char *protocol = NULL; // protocol not parsed, firefox 52 doesnt send it
+ forEachSmallArray(req, S) {
+ castS(s,S);
+ if (startsWithG(s, "Sec-WebSocket-Key:")) {
+ clientKey = ssGet(s) + 19;
+ }
+ if(startsWithSG(s, "Origin:")) {
+ origin = ssGet(s) + 8;
+ }
+ finishG(s);
+ }
+
+ for(i=0;i<ret;i++)
+ if(buffer[i] == '\r' || buffer[i] == '\n')
+ buffer[i]='*';
+ slog(LOG,"request",buffer,hit);
+
+ if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) )
+ slog(SORRY,"Only simple GET operation supported",buffer,fd);
+
+ for(i=4;i<BUFSIZE;i++) {
+ if(buffer[i] == ' ') {
+ buffer[i] = 0;
+ break;
+ }
+ }
+
+ for(j=0;j<i-1;j++)
+ if(buffer[j] == '.' && buffer[j+1] == '.')
+ slog(SORRY,"Parent directory (..) path names not supported",buffer,fd);
+
+ if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) )
+ (void)strcpy(buffer,"GET /index.html");
+
+ bool isHTML = false;
+ bool isWebsocket = false;
+
+ buflen=strlen(buffer);
+ fstr = (char *)0;
+ for(i=0;extensions[i].ext != 0;i++) {
+ len = strlen(extensions[i].ext);
+ if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
+ fstr =extensions[i].filetype;
+ if (startsWithG(extensions[i].ext, "htm")) isHTML = true;
+ else if (eqS(extensions[i].ext, "//ws")) isWebsocket = true;
+ break;
+ }
+ }
+ if(fstr == 0) slog(SORRY,"file extension type not supported",buffer,fd);
+
+ if (isWebsocket) {
+ // WEBSOCKET HANDLING
+
+ slog(LOG,"websocket","detected",hit);
+ slog(LOG,"key",clientKey,hit);
+
+ SHA1Context shactx;
+ SHA1Reset(&shactx);
+
+ char buf[1024];
+ sprintf(buf, "%s%s", clientKey, WEBSOCKET_SERVER_RESPONSE);
+
+ SHA1Input(&shactx, (unsigned char *) buf, strlen(buf));
+ SHA1Result(&shactx);
+
+ char sha1buf[45];
+ unsigned char sha1mac[20];
+
+ sprintf(sha1buf, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0],
+ shactx.Message_Digest[1], shactx.Message_Digest[2],
+ shactx.Message_Digest[3], shactx.Message_Digest[4]);
+
+ for (i16 n = 0; n < (strlen(sha1buf) / 2); n++) {
+ sscanf(sha1buf + (n * 2), "%02hhx", sha1mac + n);
+ }
+
+ char base64buf[256];
+ base64_encode(sha1mac, 20, base64buf, 256);
+ memset(buf, 0, 1024);
+ snprintf(buf, 1024, "HTTP/1.1 101 Switching Protocols\r\n"
+ "Server: %s/%s\r\n"
+ "Upgrade: websocket\r\n"
+ "%s%s%s"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n%s%s"
+ "Access-Control-Allow-Headers: content-type\r\n\r\n", WEBSOCK_PACKAGE_NAME,
+ WEBSOCK_PACKAGE_VERSION, origin ? "Access-Control-Allow-Origin: " : "", origin ? origin : "", origin ? "\r\n" : "", base64buf, protocol ? protocol : "", protocol ? "\r\n" : "");
+
+ (void)write(fd,buf,strlen(buf));
+
+ // INOTIFY SETUP
+
+ // wait for changes and reload page
+ enum {yes, no};
+ i8 foundRelevantUpdate = no;
+ while (foundRelevantUpdate == no) {
+ // creating the INOTIFY instance
+ int length;
+ char ibuffer[EVENT_BUF_LEN];
+ int ifd = inotiFy_init();
+ if (ifd == -1) {
+ perror("inotiFy_init error: ");
+ }
+
+ int wd; // watch descriptor
+
+ forEachSmallArray(dirList, E) {
+ castS(e, E);
+ wd = inotiFy_add_watch( ifd, ssGet(e), IN_CREATE | IN_DELETE | IN_MODIFY);
+ finishG(e);
+ }
+
+ // read to determine the event change happens on “.” directory. Actually this read blocks until the change event occurs
+ length = read( ifd, ibuffer, EVENT_BUF_LEN );;
+
+ if (length == -1) {
+ perror("read error: ");
+ }
+
+ u32 i = 0;
+ // actually read return the list of change events happens. Here, read the change event one by one and process it accordingly.
+ while (( i < length )) {
+ struct inotiFy_event *event = ( struct inotiFy_event * ) &ibuffer[ i ];
+
+ for(i=0;extensions[i].ext != 0;i++) {
+ if (endsWithG(event->name, extensions[i].ext)) {
+ if ((event->mask & IN_CREATE) || (event->mask & IN_DELETE) || (event->mask & IN_MODIFY)) {
+ foundRelevantUpdate = yes;
+ }
+ }
+ }
+
+ i += EVENT_SIZE + event->len;
+ }
+
+
+ // removing the “/tmp” directory from the watch list.
+ inotiFy_rm_watch( ifd, wd );
+
+ // closing the INOTIFY instance
+ close(ifd);
+ }
+
+ if (waitTime > 0) {
+ // wait before reloading
+ sleep(waitTime);
+ }
+
+ // WEBSOCKET SEND RELOAD TO CLIENT
+
+ // send reload
+ u32 fin = 1;
+ u32 op = 1;
+ u32 plen = 6;
+ u32 blen = plen+2;
+
+ buf[0] = (1 << 7) + fin; // 0x81
+ buf[1] = plen;
+ /* uint16_t *b = (uint16_t*)buf; */
+ /* *b = (plen << 9) + (op << 4) + fin; */
+ strcpy(buf+2, "reload");
+
+ (void)write(fd,buf,blen);
+ } else {
+
+ // SERVE REGULAR HTTP REQUESTS
+
+ char *serveFile = &buffer[5];
+
+ if (isHTML) {
+ size_t endBodyAt = 0;
+ createAllocateSmallArray(f);
+ readFileG(f, &buffer[5]);
+ enumerateSmallArray(f, S, n) {
+ castS(s, S);
+ if (hasG(lowerG(s), "</body>")) {
+ endBodyAt = n;
+ finishG(s);
+ break;
+ }
+ finishG(s);
+ }
+ injectSG(f, endBodyAt, injected);
+ writeFileG(f, "a");
+ terminateG(f);
+ serveFile = "a";
+ }
+
+ if(( file_fd = open(serveFile,O_RDONLY)) == -1)
+ slog(SORRY, "failed to open file",serveFile,fd);
+
+ slog(LOG,"SEND",serveFile,hit);
+
+ (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
+ (void)write(fd,buffer,strlen(buffer));
+
+ while ((ret = read(file_fd, buffer, BUFSIZE)) > 0 ) {
+ (void)write(fd,buffer,ret);
+ }
+ }
+
+ terminateManyG(reqS, req);
+
+#ifdef LINUX
+ sleep(1);
+#endif
+ exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+ int i, port, pid, listenfd, socketfd, hit;
+ size_t length;
+ static struct sockaddr_in cli_addr;
+ static struct sockaddr_in serv_addr;
+ char *portS;
+ char *dir = NULL;
+
+ // defaults
+ port = 8080;
+ portS = "8080";
+
+ forEachS(argv, a) {
+ if (startsWithG(a, "-port=")) {
+ portS = a+6;
+ port = parseInt(portS);
+ }
+ else if (startsWithG(a, "-path=")) {
+ dir = a+6;
+ }
+ else if (startsWithG(a, "-wait=")) {
+ waitTime = parseInt(a+6);
+ }
+ else if (startsWithG(a, "--help") or startsWithG(a, "-h") or startsWithG(a, "-?")) {
+ printf("usage: server [-port=80] [-path=server_directory] [-wait=1]\n"
+ "\twait unit is second.\n"
+ "\tExample: server -port=8080 -path=./ -wait=1\n\n"
+ "\tOnly Supports:");
+ for(i=0;extensions[i].ext != 0;i++)
+ (void)printf(" %s",extensions[i].ext);
+
+ (void)printf("\n\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n"
+ );
+ XSUCCESS
+ }
+ }
+
+ if (dir) {
+ if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) ||
+ !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) ||
+ !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) ||
+ !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){
+ (void)printf("ERROR: Bad top directory %s, see server -?\n",dir);
+ exit(3);
+ }
+ if(chdir(dir) == -1){
+ (void)printf("ERROR: Can't Change to directory %s\n",dir);
+ exit(4);
+ }
+ }
+
+ dirList = allocG(rtSmallArrayt);
+ execO("find . -type d", dirList, NULL);
+
+ if(fork() != 0)
+ return 0;
+ (void)signal(SIGCLD, SIG_IGN);
+ (void)signal(SIGHUP, SIG_IGN);
+ for(i=0;i<32;i++)
+ (void)close(i);
+ (void)setpgrp();
+
+ slog(LOG,"http server starting",portS,getpid());
+
+ if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
+ slog(ERROR, "system call","socket",0);
+
+ if(port < 0 || port >60000)
+ slog(ERROR,"Invalid port number try [1,60000]",portS,0);
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+ if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
+ slog(ERROR,"system call","bind",0);
+ if( listen(listenfd,64) <0)
+ slog(ERROR,"system call","listen",0);
+
+ for(hit=1; ;hit++) {
+ length = sizeof(cli_addr);
+ if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t *)&length)) < 0)
+ slog(ERROR,"system call","accept",0);
+
+ if((pid = fork()) < 0) {
+ slog(ERROR,"system call","fork",0);
+ }
+ else {
+ if(pid == 0) {
+ (void)close(listenfd);
+ web(socketfd,hit);
+ } else {
+ (void)close(socketfd);
+ }
+ }
+ }
+
+ terminateG(dirList);
+
+}
diff --git a/server.log b/server.log
@@ -0,0 +1,8 @@
+ INFO: http server starting:8080:21304
+ INFO: request:GET / HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8**Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Connection: keep-alive**Upgrade-Insecure-Requests: 1**Cache-Control: max-age=0****:1
+ INFO: SEND:a:1
+ INFO: request:GET /scott.jpg HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: */***Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Referer: http://127.0.0.1:8080/**Connection: keep-alive**Cache-Control: max-age=0****:2
+ INFO: SEND:scott.jpg:2
+ INFO: request:GET //ws HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8**Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Sec-WebSocket-Version: 13**Origin: http://127.0.0.1:8080**Sec-WebSocket-Extensions: permessage-deflate**Sec-WebSocket-Key: WXZtbQjAV6Mm+b4CAr4VeQ==**Connection: keep-alive, Upgrade**Pragma: no-cache**Cache-Control: no-cache**Upgrade: websocket****:3
+ INFO: websocket:detected:3
+ INFO: key:WXZtbQjAV6Mm+b4CAr4VeQ==:3
diff --git a/utilities.c b/utilities.c
@@ -0,0 +1,482 @@
+/*
+ utilities.c
+
+ Jonathan D. Hall - jhall@futuresouth.us
+ Copyright 2015 Future South Technologies
+
+ This file is part of libwebsock2.
+
+ libwebsock2 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libwebsock2 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libwebsock2. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "utilities.h"
+
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 1
+
+#define SHA1CircularShift(bits,word) \
+((((word) << (bits)) & 0xFFFFFFFF) | \
+((word) >> (32-(bits))))
+
+uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte);
+
+//these functions assume little endian machine as they're only used on windows
+uint16_t lws_htobe16(uint16_t x)
+{
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+}
+
+uint16_t lws_be16toh(uint16_t x)
+{
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+}
+
+uint64_t lws_htobe64(uint64_t x)
+{
+ return (x >> 56) |
+ ((x << 40) & 0x00ff000000000000LL) |
+ ((x << 24) & 0x0000ff0000000000LL) |
+ ((x << 8) & 0x000000ff00000000LL) |
+ ((x >> 8) & 0x00000000ff000000LL) |
+ ((x >> 24) & 0x0000000000ff0000LL) |
+ ((x >> 40) & 0x000000000000ff00LL) |
+ (x << 56);
+}
+
+uint64_t lws_be64toh(uint64_t x)
+{
+ return (x >> 56) |
+ ((x << 40) & 0x00ff000000000000LL) |
+ ((x << 24) & 0x0000ff0000000000LL) |
+ ((x << 8) & 0x000000ff00000000LL) |
+ ((x >> 8) & 0x00000000ff000000LL) |
+ ((x >> 24) & 0x0000000000ff0000LL) |
+ ((x >> 40) & 0x000000000000ff00LL) |
+ (x << 56);
+}
+
+int validate_utf8_sequence(uint8_t *s)
+{
+ uint32_t codepoint;
+ uint32_t state = 0;
+
+ for(; *s; ++s) {
+ decode(&state, &codepoint, *s);
+ }
+
+ return state == UTF8_ACCEPT;
+}
+
+const uint8_t utf8d[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
+ 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
+ 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
+ 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
+};
+
+uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte)
+{
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state*16 + type];
+ return *state;
+}
+
+const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void _base64_encode_triple(unsigned char triple[3], char result[4])
+{
+ int tripleValue, i;
+
+ tripleValue = triple[0];
+ tripleValue *= 256;
+ tripleValue += triple[1];
+ tripleValue *= 256;
+ tripleValue += triple[2];
+
+ for (i=0; i<4; i++)
+ {
+ result[3-i] = BASE64_CHARS[tripleValue%64];
+ tripleValue /= 64;
+ }
+}
+
+int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen)
+{
+ if ((sourcelen+2)/3*4 > targetlen-1)
+ return 0;
+
+ while (sourcelen >= 3)
+ {
+ _base64_encode_triple(source, target);
+ sourcelen -= 3;
+ source += 3;
+ target += 4;
+ }
+
+ if (sourcelen > 0)
+ {
+ unsigned char temp[3];
+ memset(temp, 0, sizeof(temp));
+ memcpy(temp, source, sourcelen);
+ _base64_encode_triple(temp, target);
+ target[3] = '=';
+ if (sourcelen == 1)
+ target[2] = '=';
+
+ target += 4;
+ }
+
+ target[0] = 0;
+
+ return 1;
+}
+
+int _base64_char_value(char base64char)
+{
+ if (base64char >= 'A' && base64char <= 'Z')
+ return base64char-'A';
+ if (base64char >= 'a' && base64char <= 'z')
+ return base64char-'a'+26;
+ if (base64char >= '0' && base64char <= '9')
+ return base64char-'0'+2*26;
+ if (base64char == '+')
+ return 2*26+10;
+ if (base64char == '/')
+ return 2*26+11;
+ return -1;
+}
+
+int _base64_decode_triple(char quadruple[4], unsigned char *result)
+{
+ int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1;
+ int char_value[4];
+
+ for (i=0; i<4; i++)
+ char_value[i] = _base64_char_value(quadruple[i]);
+
+ for (i=3; i>=0; i--)
+ {
+ if (char_value[i]<0)
+ {
+ if (only_equals_yet && quadruple[i]=='=')
+ {
+ char_value[i]=0;
+ bytes_to_decode--;
+ continue;
+ }
+ return 0;
+ }
+ only_equals_yet = 0;
+ }
+
+ if (bytes_to_decode < 0)
+ bytes_to_decode = 0;
+
+ triple_value = char_value[0];
+ triple_value *= 64;
+ triple_value += char_value[1];
+ triple_value *= 64;
+ triple_value += char_value[2];
+ triple_value *= 64;
+ triple_value += char_value[3];
+
+ for (i=bytes_to_decode; i<3; i++)
+ triple_value /= 256;
+ for (i=bytes_to_decode-1; i>=0; i--)
+ {
+ result[i] = triple_value%256;
+ triple_value /= 256;
+ }
+
+ return bytes_to_decode;
+}
+
+size_t base64_decode(char *source, unsigned char *target, size_t targetlen)
+{
+ char *src, *tmpptr;
+ char quadruple[4];
+ unsigned char tmpresult[3];
+ int i, tmplen = 3;
+ size_t converted = 0;
+
+ src = (char *)malloc(strlen(source)+5);
+ if (src == NULL)
+ return -1;
+ strcpy(src, source);
+ strcat(src, "====");
+ tmpptr = src;
+
+ while (tmplen == 3)
+ {
+ for (i=0; i<4; i++)
+ {
+ while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0)
+ tmpptr++;
+
+ quadruple[i] = *(tmpptr++);
+ }
+
+ tmplen = _base64_decode_triple(quadruple, tmpresult);
+
+ if (targetlen < tmplen)
+ {
+ free(src);
+ return -1;
+ }
+
+ memcpy(target, tmpresult, tmplen);
+ target += tmplen;
+ targetlen -= tmplen;
+ converted += tmplen;
+ }
+
+ free(src);
+ return converted;
+}
+
+void SHA1Reset(SHA1Context *context)
+{
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Message_Digest[0] = 0x67452301;
+ context->Message_Digest[1] = 0xEFCDAB89;
+ context->Message_Digest[2] = 0x98BADCFE;
+ context->Message_Digest[3] = 0x10325476;
+ context->Message_Digest[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+}
+
+int SHA1Result(SHA1Context *context)
+{
+
+ if (context->Corrupted)
+ {
+ return 0;
+ }
+
+ if (!context->Computed)
+ {
+ SHA1PadMessage(context);
+ context->Computed = 1;
+ }
+
+ return 1;
+}
+
+void SHA1Input( SHA1Context *context,
+ const unsigned char *message_array,
+ unsigned length)
+{
+ if (!length)
+ {
+ return;
+ }
+
+ if (context->Computed || context->Corrupted)
+ {
+ context->Corrupted = 1;
+ return;
+ }
+
+ while(length-- && !context->Corrupted)
+ {
+ context->Message_Block[context->Message_Block_Index++] =
+ (*message_array & 0xFF);
+
+ context->Length_Low += 8;
+ /* Force it to 32 bits */
+ context->Length_Low &= 0xFFFFFFFF;
+ if (context->Length_Low == 0)
+ {
+ context->Length_High++;
+ /* Force it to 32 bits */
+ context->Length_High &= 0xFFFFFFFF;
+ if (context->Length_High == 0)
+ {
+ /* Message is too long */
+ context->Corrupted = 1;
+ }
+ }
+
+ if (context->Message_Block_Index == 64)
+ {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+}
+
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+ const unsigned K[] = /* Constants defined in SHA-1 */
+ {
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ unsigned temp; /* Temporary word value */
+ unsigned W[80]; /* Word sequence */
+ unsigned A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for(t = 0; t < 16; t++)
+ {
+ W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
+ W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
+ W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
+ W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
+ }
+
+ for(t = 16; t < 80; t++)
+ {
+ W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Message_Digest[0];
+ B = context->Message_Digest[1];
+ C = context->Message_Digest[2];
+ D = context->Message_Digest[3];
+ E = context->Message_Digest[4];
+
+ for(t = 0; t < 20; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 20; t < 40; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 40; t < 60; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 60; t < 80; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Message_Digest[0] =
+ (context->Message_Digest[0] + A) & 0xFFFFFFFF;
+ context->Message_Digest[1] =
+ (context->Message_Digest[1] + B) & 0xFFFFFFFF;
+ context->Message_Digest[2] =
+ (context->Message_Digest[2] + C) & 0xFFFFFFFF;
+ context->Message_Digest[3] =
+ (context->Message_Digest[3] + D) & 0xFFFFFFFF;
+ context->Message_Digest[4] =
+ (context->Message_Digest[4] + E) & 0xFFFFFFFF;
+
+ context->Message_Block_Index = 0;
+}
+
+void SHA1PadMessage(SHA1Context *context)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index > 55)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while(context->Message_Block_Index < 64)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+
+ while(context->Message_Block_Index < 56)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while(context->Message_Block_Index < 56)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
+ context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
+ context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
+ context->Message_Block[59] = (context->Length_High) & 0xFF;
+ context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
+ context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
+ context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
+ context->Message_Block[63] = (context->Length_Low) & 0xFF;
+
+ SHA1ProcessMessageBlock(context);
+}
diff --git a/utilities.h b/utilities.h
@@ -0,0 +1,74 @@
+/*
+ utilities.h
+
+ Jonathan D. Hall - jhall@futuresouth.us
+ Copyright 2015 Future South Technologies
+
+ This file is part of libwebsock2.
+
+ libwebsock2 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libwebsock2 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libwebsock2. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+ * SHA1 functions written by Paul E. Jones <paulej@packetizer.com>
+ * Copyright (C) 1998, 2009 - All Rights Reserved
+ */
+
+#ifndef _utilities_h
+#define _utilities_h
+
+#include "libsheepyObject.h"
+
+typedef struct SHA1Context
+{
+ unsigned Message_Digest[5]; /* Message Digest (output) */
+
+ unsigned Length_Low; /* Message length in bits */
+ unsigned Length_High; /* Message length in bits */
+
+ unsigned char Message_Block[64]; /* 512-bit message blocks */
+ int Message_Block_Index; /* Index into message block array */
+
+ int Computed; /* Is the digest computed? */
+ int Corrupted; /* Is the message digest corruped? */
+} SHA1Context;
+
+
+/* UTF prototypes */
+
+int validate_utf8_sequence(uint8_t *s);
+uint16_t lws_htobe16(uint16_t x);
+uint16_t lws_be16toh(uint16_t x);
+uint64_t lws_htobe64(uint64_t x);
+uint64_t lws_be64toh(uint64_t x);
+
+
+/* Base64 prototypes */
+
+int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen);
+void _base64_encode_triple(unsigned char triple[3], char result[4]);
+int _base64_char_value(char base64char);
+int _base64_decode_triple(char quadruple[4], unsigned char *result);
+size_t base64_decode(char *source, unsigned char *target, size_t targetlen);
+
+/* SHA1 prototypes */
+
+void SHA1Reset(SHA1Context *);
+int SHA1Result(SHA1Context *);
+void SHA1Input( SHA1Context *, const unsigned char *, unsigned);
+void SHA1ProcessMessageBlock(SHA1Context *);
+void SHA1PadMessage(SHA1Context *);
+
+#endif /* utilities_h */