diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/civetweb/examples | |
parent | Initial commit. (diff) | |
download | ceph-upstream.tar.xz ceph-upstream.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/civetweb/examples')
39 files changed, 4669 insertions, 0 deletions
diff --git a/src/civetweb/examples/README.md b/src/civetweb/examples/README.md new file mode 100644 index 000000000..cf5495977 --- /dev/null +++ b/src/civetweb/examples/README.md @@ -0,0 +1,13 @@ + +Examples +===== + +Two examples show how to embed civetweb into a C ([embedded_c](https://github.com/civetweb/civetweb/tree/master/examples/embedded_c)) or a C++ ([embedded_cpp](https://github.com/civetweb/civetweb/tree/master/examples/embedded_cpp)) application. +The C++ wrapper only offers a subset of the full C API, thus the C example is more complete than the C++ example. These examples were not designed with security in mind, but to show how the API can be used in principle. For more information, see the [documentation](https://github.com/civetweb/civetweb/tree/master/docs). Some examples can also be found in the [test](https://github.com/civetweb/civetweb/tree/master/test) folder (but they are less documented and adapted to the needs of the test framework). + +In addition, there is one example how to configure a HTTPS server, to comply with modern security standards ([https](https://github.com/civetweb/civetweb/tree/master/examples/https)). It does not hold any source, but only a configuration file and some documentation how to use it. + +Some no longer maintained examples can be found in the ["obsolete"](https://github.com/civetweb/civetweb/tree/master/examples/_obsolete) folder. It is not guaranteed that they work in the current version - they are kept for reference, but might be removed in the future. + +All examples are subject to the MIT license (unless noted otherwise) - they come without warranty of any kind. + diff --git a/src/civetweb/examples/_obsolete/chat/Makefile b/src/civetweb/examples/_obsolete/chat/Makefile new file mode 100644 index 000000000..e8d12be69 --- /dev/null +++ b/src/civetweb/examples/_obsolete/chat/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = chat +SRC = chat.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a +SSL_CERT = ssl_cert.pem + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) $(SSL_CERT) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +$(SSL_CERT): + cp $(TOP)/resources/$(SSL_CERT) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) $(SSL_CERT) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/chat/chat.c b/src/civetweb/examples/_obsolete/chat/chat.c new file mode 100644 index 000000000..ad146183b --- /dev/null +++ b/src/civetweb/examples/_obsolete/chat/chat.c @@ -0,0 +1,403 @@ +// This file is part of the Civetweb project, http://code.google.com/p/civetweb +// It implements an online chat server. For more details, +// see the documentation on the project web site. +// To test the application, +// 1. type "make" in the directory where this file lives +// 2. point your browser to http://127.0.0.1:8081 + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <pthread.h> + +#include "civetweb.h" + +#define MAX_USER_LEN 20 +#define MAX_MESSAGE_LEN 100 +#define MAX_MESSAGES 5 +#define MAX_SESSIONS 2 +#define SESSION_TTL 120 + +static const char *authorize_url = "/authorize"; +static const char *login_url = "/login.html"; +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; // Message ID + char user[MAX_USER_LEN]; // User that have sent the message + char text[MAX_MESSAGE_LEN]; // Message text + time_t timestamp; // Message timestamp, UTC +}; + +// Describes web session. +struct session { + char session_id[33]; // Session ID, must be unique + char random[20]; // Random data used for extra user validation + char user[MAX_USER_LEN]; // Authenticated user + time_t expire; // Expiration timestamp, UTC +}; + +static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages +static struct session sessions[MAX_SESSIONS]; // Current sessions +static long last_message_id; + +// Protects messages, sessions, last_message_id +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +// Get session object for the connection. Caller must hold the lock. +static struct session *get_session(const struct mg_connection *conn) +{ + int i; + const char *cookie = mg_get_header(conn, "Cookie"); + char session_id[33]; + time_t now = time(NULL); + mg_get_cookie(cookie, "session", session_id, sizeof(session_id)); + for (i = 0; i < MAX_SESSIONS; i++) { + if (sessions[i].expire != 0 && + sessions[i].expire > now && + strcmp(sessions[i].session_id, session_id) == 0) { + break; + } + } + return i == MAX_SESSIONS ? NULL : &sessions[i]; +} + +static void get_qsvar(const struct mg_request_info *request_info, + const char *name, char *dst, size_t dst_len) +{ + const char *qs = request_info->query_string; + mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len); +} + +// 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->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: %ld},", + message->user, message->text, message->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]; + + 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); + + 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", ")"); + } +} + +// Allocate new message. Caller must hold the lock. +static struct message *new_message(void) +{ + static int size = sizeof(messages) / sizeof(messages[0]); + struct message *message = &messages[last_message_id % size]; + message->id = last_message_id++; + message->timestamp = time(0); + return message; +} + +static void my_strlcpy(char *dst, const char *src, size_t len) +{ + strncpy(dst, src, len); + dst[len - 1] = '\0'; +} + +// 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; + struct session *session; + char text[sizeof(message->text) - 1]; + int is_jsonp; + + mg_printf(conn, "%s", ajax_reply_start); + is_jsonp = handle_jsonp(conn, request_info); + + 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 = new_message(); + // TODO(lsm): JSON-encode all text strings + session = get_session(conn); + assert(session != NULL); + my_strlcpy(message->text, text, sizeof(text)); + my_strlcpy(message->user, session->user, sizeof(message->user)); + 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 that user and password are not empty + return (user[0] && password[0]); +} + +// Allocate new session object +static struct session *new_session(void) +{ + int i; + time_t now = time(NULL); + pthread_rwlock_wrlock(&rwlock); + for (i = 0; i < MAX_SESSIONS; i++) { + if (sessions[i].expire == 0 || sessions[i].expire < now) { + sessions[i].expire = time(0) + SESSION_TTL; + break; + } + } + pthread_rwlock_unlock(&rwlock); + return i == MAX_SESSIONS ? NULL : &sessions[i]; +} + +// Generate session ID. buf must be 33 bytes in size. +// Note that it is easy to steal session cookies by sniffing traffic. +// This is why all communication must be SSL-ed. +static void generate_session_id(char *buf, const char *random, + const char *user) +{ + mg_md5(buf, random, user, NULL); +} + +static void send_server_message(const char *fmt, ...) +{ + va_list ap; + struct message *message; + + pthread_rwlock_wrlock(&rwlock); + message = new_message(); + message->user[0] = '\0'; // Empty user indicates server message + va_start(ap, fmt); + vsnprintf(message->text, sizeof(message->text), fmt, ap); + va_end(ap); + + pthread_rwlock_unlock(&rwlock); +} + +// 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[MAX_USER_LEN], password[MAX_USER_LEN]; + struct session *session; + + // Fetch user name and password. + get_qsvar(request_info, "user", user, sizeof(user)); + get_qsvar(request_info, "password", password, sizeof(password)); + + if (check_password(user, password) && (session = new_session()) != NULL) { + // 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 + // + // The most secure way is to stay HTTPS all the time. However, just to + // show the technique, we redirect to HTTP after the successful + // authentication. The danger of doing this is that session cookie can + // be stolen and an attacker may impersonate the user. + // Secure application must use HTTPS all the time. + my_strlcpy(session->user, user, sizeof(session->user)); + snprintf(session->random, sizeof(session->random), "%d", rand()); + generate_session_id(session->session_id, session->random, session->user); + send_server_message("<%s> joined", session->user); + mg_printf(conn, "HTTP/1.1 302 Found\r\n" + "Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID + "Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code + "Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url + "Location: /\r\n\r\n", + session->session_id, session->user); + } else { + // Authentication failure, redirect to login. + 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) +{ + struct session *session; + char valid_id[33]; + int authorized = 0; + + // Always authorize accesses to login page and to authorize URI + if (!strcmp(request_info->uri, login_url) || + !strcmp(request_info->uri, authorize_url)) { + return 1; + } + + pthread_rwlock_rdlock(&rwlock); + if ((session = get_session(conn)) != NULL) { + generate_session_id(valid_id, session->random, session->user); + if (strcmp(valid_id, session->session_id) == 0) { + session->expire = time(0) + SESSION_TTL; + authorized = 1; + } + } + pthread_rwlock_unlock(&rwlock); + + return authorized; +} + +static void redirect_to_ssl(struct mg_connection *conn, + const struct mg_request_info *request_info) +{ + const char *p, *host = mg_get_header(conn, "Host"); + if (host != NULL && (p = strchr(host, ':')) != NULL) { + mg_printf(conn, "HTTP/1.1 302 Found\r\n" + "Location: https://%.*s:8082/%s:8082\r\n\r\n", + (int) (p - host), host, request_info->uri); + } else { + mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set"); + } +} + +static int begin_request_handler(struct mg_connection *conn) +{ + const struct mg_request_info *request_info = mg_get_request_info(conn); + int processed = 1; + + if (!request_info->is_ssl) { + redirect_to_ssl(conn, request_info); + } else if (!is_authorized(conn, request_info)) { + 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. Civetweb will + // try to serve the request. + processed = 0; + } + return processed; +} + +static const char *options[] = { + "document_root", "html", + "listening_ports", "8081,8082s", + "ssl_certificate", "ssl_cert.pem", + "num_threads", "5", + NULL +}; + +int main(void) +{ + struct mg_callbacks callbacks; + struct mg_context *ctx; + + // Initialize random number generator. It will be used later on for + // the session identifier creation. + srand((unsigned) time(0)); + + // Setup and start Civetweb + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) { + printf("%s\n", "Cannot start chat server, fatal exit"); + exit(EXIT_FAILURE); + } + + // Wait until enter is pressed, then exit + printf("Chat server started on ports %s, press enter to quit.\n", + mg_get_option(ctx, "listening_ports")); + getchar(); + mg_stop(ctx); + printf("%s\n", "Chat server stopped."); + + return EXIT_SUCCESS; +} + +// vim:ts=2:sw=2:et diff --git a/src/civetweb/examples/_obsolete/docroot/favicon.ico b/src/civetweb/examples/_obsolete/docroot/favicon.ico Binary files differnew file mode 100644 index 000000000..2179aba89 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/favicon.ico diff --git a/src/civetweb/examples/_obsolete/docroot/index.html b/src/civetweb/examples/_obsolete/docroot/index.html new file mode 100644 index 000000000..c85bec992 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/index.html @@ -0,0 +1,73 @@ +<!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 Civetweb project, + http://sourceforge.net/projects/civetweb/ --> + <head> + <title>Civetweb 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 id="logo"></div> + <div class="rounded infobox help-message" id="motd"> + Chat room implemented using + <a href="http://sourceforge.net/projects/civetweb/" target="_blank">Civetweb</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 + gives data to the client in JSON format. + </div> + </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 chat-title">Settings</span> + <div class="bottom-rounded chat-content"> + <table> + <tr><td>Max messages to display:</td><td>blah blah</td></tr> + <tr><td>Text color:</td><td>blah blah</td></tr> + </table> + </div> + </div> + </div> + + </div> + </div> + + <div id="footer"> + Copyright © 2004-2010 by Sergey Lyubka + </div> + + </body> +</html> diff --git a/src/civetweb/examples/_obsolete/docroot/jquery.js b/src/civetweb/examples/_obsolete/docroot/jquery.js new file mode 100644 index 000000000..7c2430802 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/jquery.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i? +e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r= +j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g, +"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e= +true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)|| +c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded", +L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype, +"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+ +a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f], +d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]=== +a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&& +!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari= +true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ", +i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ", +" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className= +this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i= +e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!= +null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), +fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| +d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this, +"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent= +a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y, +isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit= +{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}}; +if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&& +!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}}, +toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector, +u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "), +function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q]; +if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[]; +for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length- +1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, +CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}}, +relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]= +l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[]; +h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m= +m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition|| +!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m= +h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>"; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/, +gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length; +c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)? +a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&& +this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]|| +u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length=== +1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay"); +this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a], +"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)}, +animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing= +j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]); +this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length|| +c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement? +function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b= +this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle; +k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&& +f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/src/civetweb/examples/_obsolete/docroot/login.html b/src/civetweb/examples/_obsolete/docroot/login.html new file mode 100644 index 000000000..2b09f01c5 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/login.html @@ -0,0 +1,43 @@ +<!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 Civetweb project, + http://sourceforge.net/projects/civetweb/ --> + <head> + <title>Civetweb 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>Civetweb chat server login</h2> + <div style="max-width: 30em;"> + Username and password can be any non-empty strings. + </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> diff --git a/src/civetweb/examples/_obsolete/docroot/logo.png b/src/civetweb/examples/_obsolete/docroot/logo.png Binary files differnew file mode 100644 index 000000000..8a47c0ab8 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/logo.png diff --git a/src/civetweb/examples/_obsolete/docroot/main.js b/src/civetweb/examples/_obsolete/docroot/main.js new file mode 100644 index 000000000..d4af86ddb --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/main.js @@ -0,0 +1,107 @@ +// This file is part of Civetweb project, +// http://sourceforge.net/projects/civetweb/ + +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, + getMessagesIntervalMs: 1000, +}; + +chat.normalizeText = function(text) { + return text.replace('<', '<').replace('>', '>'); +}; + +chat.refresh = function(data) { + + if (data === undefined) { + return; + } + + $.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') + .addClass(entry.user ? '' : 'message-user-server') + .html(chat.normalizeText((entry.user || '[server]') + ':')) + .appendTo(row); + $('<span>') + .addClass('message-text') + .addClass(entry.user ? '' : 'message-text-server') + .html(chat.normalizeText(entry.text)) + .appendTo(row); + chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1; + }); + + // Keep only chat.maxVisibleMessages, delete older ones. + while ($('#mml').children().length > chat.maxVisibleMessages) { + $('#mml div:first-child').remove(); + } +}; + +chat.getMessages = function(enter_loop) { + $.ajax({ + dataType: 'jsonp', + url: chat.backendUrl + '/ajax/get_messages', + data: {last_id: chat.lastMessageId}, + success: chat.refresh, + error: function() { + }, + }); + if (enter_loop) { + window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs); + } +}; + +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(false); + }, + 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(true); +}); + +// vim:ts=2:sw=2:et diff --git a/src/civetweb/examples/_obsolete/docroot/prime_numbers.lp b/src/civetweb/examples/_obsolete/docroot/prime_numbers.lp new file mode 100644 index 000000000..0c71bb824 --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/prime_numbers.lp @@ -0,0 +1,46 @@ +HTTP/1.0 200 OK +Content-Type: text/html + +<html> + <p>Prime numbers from 0 to 100, calculated by Lua:</p> + <? + function is_prime(n) + if n <= 0 then return false end + if n <= 2 then return true end + if (n % 2 == 0) then return false end + for i = 3, n / 2, 2 do + if (n % i == 0) then return false end + end + return true + end + + for i = 1, 100 do + if is_prime(i) then mg.write('<span>' .. i .. '</span> ') end + end + + ?> + + <p>Reading POST data from Lua (click submit):</p> + <form method="POST" ><input type="text" name="t1"/><input type="submit"></form> + + <pre> + POST data: [<? + local post_data = '' + if mg.request_info.request_method == 'POST' then + post_data = mg.read() + end + mg.write(post_data) + ?>] + request method: [<? mg.write(mg.request_info.request_method) ?>] + IP/port: [<? mg.write(mg.request_info.remote_ip, ':', + mg.request_info.remote_port) ?>] + URI: [<? mg.write(mg.request_info.uri) ?>] + HTTP version [<? mg.write(mg.request_info.http_version) ?>] + HEADERS: + <? + for name, value in pairs(mg.request_info.http_headers) do + mg.write(name, ':', value, '\n') + end + ?> +</pre> +</html> diff --git a/src/civetweb/examples/_obsolete/docroot/style.css b/src/civetweb/examples/_obsolete/docroot/style.css new file mode 100644 index 000000000..716351d2d --- /dev/null +++ b/src/civetweb/examples/_obsolete/docroot/style.css @@ -0,0 +1,154 @@ +/* + * vim:ts=2:sw=2:et:ai + */ + +body { + font: 13px Arial; margin: 0.5em 1em; +} + +#logo { + background: url('logo.png') no-repeat ; + width: 160px; + height: 40px; + float: left; +} + +td { + text-align: left; +} + +#motd { + margin-left: 170px; +} + +.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; +} + +.message-user-server { + color: purple; +} + +.message-text-server { + font-style: italic; +} + +.main { + padding: 0.5em; + background: #f0fcff; +} + +#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; +} diff --git a/src/civetweb/examples/_obsolete/hello/Makefile b/src/civetweb/examples/_obsolete/hello/Makefile new file mode 100644 index 000000000..4424e3b58 --- /dev/null +++ b/src/civetweb/examples/_obsolete/hello/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = hello +SRC = hello.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/hello/hello.c b/src/civetweb/examples/_obsolete/hello/hello.c new file mode 100644 index 000000000..39253c1ad --- /dev/null +++ b/src/civetweb/examples/_obsolete/hello/hello.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <string.h> +#include "civetweb.h" + +// This function will be called by civetweb on every new request. +static int begin_request_handler(struct mg_connection *conn) +{ + const struct mg_request_info *request_info = mg_get_request_info(conn); + char content[100]; + + // Prepare the message we're going to send + int content_length = snprintf(content, sizeof(content), + "Hello from civetweb! Remote port: %d", + request_info->remote_port); + + // Send HTTP reply to the client + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %d\r\n" // Always set Content-Length + "\r\n" + "%s", + content_length, content); + + // Returning non-zero tells civetweb that our function has replied to + // the client, and civetweb should not send client any more data. + return 1; +} + +int main(void) +{ + struct mg_context *ctx; + struct mg_callbacks callbacks; + + // List of options. Last element must be NULL. + const char *options[] = {"listening_ports", "8080", NULL}; + + // Prepare callbacks structure. We have only one callback, the rest are NULL. + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + + // Start the web server. + ctx = mg_start(&callbacks, NULL, options); + + // Wait until user hits "enter". Server is running in separate thread. + // Navigating to http://localhost:8080 will invoke begin_request_handler(). + getchar(); + + // Stop the server. + mg_stop(ctx); + + return 0; +} diff --git a/src/civetweb/examples/_obsolete/lua/lua_dll.c b/src/civetweb/examples/_obsolete/lua/lua_dll.c new file mode 100644 index 000000000..79e6b97c0 --- /dev/null +++ b/src/civetweb/examples/_obsolete/lua/lua_dll.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#include "lua.h" +#include "lauxlib.h" + +static int smile(lua_State *L) +{ + (void) L; // Unused + printf("%s\n", ":-)"); + return 0; +} + +int LUA_API luaopen_lua_dll(lua_State *L) +{ + static const struct luaL_Reg api[] = { + {"smile", smile}, + {NULL, NULL}, + }; + luaL_openlib(L, "lua_dll", api, 0); + return 1; +} diff --git a/src/civetweb/examples/_obsolete/post/Makefile b/src/civetweb/examples/_obsolete/post/Makefile new file mode 100644 index 000000000..6e504f437 --- /dev/null +++ b/src/civetweb/examples/_obsolete/post/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = post +SRC = post.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/post/post.c b/src/civetweb/examples/_obsolete/post/post.c new file mode 100644 index 000000000..3c5c68c8d --- /dev/null +++ b/src/civetweb/examples/_obsolete/post/post.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <string.h> +#include "civetweb.h" + +static const char *html_form = + "<html><body>POST example." + "<form method=\"POST\" action=\"/handle_post_request\">" + "Input 1: <input type=\"text\" name=\"input_1\" /> <br/>" + "Input 2: <input type=\"text\" name=\"input_2\" /> <br/>" + "<input type=\"submit\" />" + "</form></body></html>"; + +static int begin_request_handler(struct mg_connection *conn) +{ + const struct mg_request_info *ri = mg_get_request_info(conn); + char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)]; + int post_data_len; + + if (!strcmp(ri->uri, "/handle_post_request")) { + // User has submitted a form, show submitted data and a variable value + post_data_len = mg_read(conn, post_data, sizeof(post_data)); + + // Parse form data. input1 and input2 are guaranteed to be NUL-terminated + mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1)); + mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); + + // Send reply to the client, showing submitted form values. + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Type: text/plain\r\n\r\n" + "Submitted data: [%.*s]\n" + "Submitted data length: %d bytes\n" + "input_1: [%s]\n" + "input_2: [%s]\n", + post_data_len, post_data, post_data_len, input1, input2); + } else { + // Show HTML form. + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/html\r\n\r\n%s", + (int) strlen(html_form), html_form); + } + return 1; // Mark request as processed +} + +int main(void) +{ + struct mg_context *ctx; + const char *options[] = {"listening_ports", "8080", NULL}; + struct mg_callbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + ctx = mg_start(&callbacks, NULL, options); + getchar(); // Wait until user hits "enter" + mg_stop(ctx); + + return 0; +} diff --git a/src/civetweb/examples/_obsolete/upload/Makefile b/src/civetweb/examples/_obsolete/upload/Makefile new file mode 100644 index 000000000..9c6270fb2 --- /dev/null +++ b/src/civetweb/examples/_obsolete/upload/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = upload +SRC = upload.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/upload/upload.c b/src/civetweb/examples/_obsolete/upload/upload.c new file mode 100644 index 000000000..56c4be4f3 --- /dev/null +++ b/src/civetweb/examples/_obsolete/upload/upload.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2014 the Civetweb developers + * Copyright (c) 2004-2012 Sergey Lyubka + * This file is a part of civetweb project, http://github.com/bel2125/civetweb + */ + +/* This example is deprecated and no longer maintained. + * All relevant parts have been merged into the embedded_c example. */ + + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include <windows.h> +#include <io.h> +#define strtoll strtol +typedef __int64 int64_t; +#else +#include <inttypes.h> +#include <unistd.h> +#endif /* !_WIN32 */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> + +#include "civetweb.h" + + +/* callback: used to generate all content */ +static int begin_request_handler(struct mg_connection *conn) +{ + const char * tempPath = "."; +#ifdef _WIN32 + const char * env = getenv("TEMP"); + if (!env) env = getenv("TMP"); + if (env) tempPath = env; +#else + tempPath = "/tmp"; +#endif + + if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) { + + mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); + mg_upload(conn, tempPath); + } else { + /* Show HTML form. */ + /* See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 */ + static const char *html_form = + "<html><body>Upload example." + "" + /* enctype="multipart/form-data" */ + "<form method=\"POST\" action=\"/handle_post_request\" " + " enctype=\"multipart/form-data\">" + "<input type=\"file\" name=\"file\" /> <br/>" + "<input type=\"file\" name=\"file2\" /> <br/>" + "<input type=\"submit\" value=\"Upload\" />" + "</form>" + "" + "</body></html>"; + + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/html\r\n\r\n%s", + (int) strlen(html_form), html_form); + } + + /* Mark request as processed */ + return 1; +} + + +/* callback: called after uploading a file is completed */ +static void upload_handler(struct mg_connection *conn, const char *path) +{ + mg_printf(conn, "Saved [%s]", path); +} + + +/* Main program: Set callbacks and start the server. */ +int main(void) +{ + /* Test server will use this port */ + const char * PORT = "8080"; + + /* Startup options for the server */ + struct mg_context *ctx; + const char *options[] = { + "listening_ports", PORT, + NULL}; + struct mg_callbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + callbacks.upload = upload_handler; + + /* Display a welcome message */ + printf("File upload demo.\n"); + printf("Open http://localhost:%s/ in your browser.\n\n", PORT); + + /* Start the server */ + ctx = mg_start(&callbacks, NULL, options); + + /* Wait until thr user hits "enter", then stop the server */ + getchar(); + mg_stop(ctx); + + return 0; +} diff --git a/src/civetweb/examples/_obsolete/websocket/Makefile b/src/civetweb/examples/_obsolete/websocket/Makefile new file mode 100644 index 000000000..3d6549297 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = websocket +SRC = WebSockCallbacks.c websocket.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib WITH_WEBSOCKET=1 + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.c b/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.c new file mode 100644 index 000000000..bb81fb0cc --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.c @@ -0,0 +1,225 @@ +/* This example uses deprecated interfaces: global websocket callbacks. + They have been superseeded by URI specific callbacks. + See examples/embedded_c for an up to date example. + */ + +#include <assert.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include "WebSockCallbacks.h" + +#ifdef _WIN32 +#include <windows.h> +#define mg_sleep(x) Sleep(x) +#else +#include <unistd.h> +#include <pthread.h> +#define mg_sleep(x) usleep((x)*1000) +#endif + + +static void +send_to_all_websockets(struct mg_context *ctx, const char *data, int data_len) +{ + + int i; + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + + mg_lock_context(ctx); + for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) { + if (ws_ctx->socketList[i] + && (ws_ctx->socketList[i]->webSockState == 2)) { + mg_websocket_write(ws_ctx->socketList[i]->conn, + WEBSOCKET_OPCODE_TEXT, + data, + data_len); + } + } + mg_unlock_context(ctx); +} + + +void +websocket_ready_handler(struct mg_connection *conn, void *_ignored) +{ + + int i; + const struct mg_request_info *rq = mg_get_request_info(conn); + struct mg_context *ctx = mg_get_context(conn); + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + tWebSockInfo *wsock = malloc(sizeof(tWebSockInfo)); + assert(wsock); + wsock->webSockState = 0; + mg_set_user_connection_data(conn, wsock); + + mg_lock_context(ctx); + for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) { + if (0 == ws_ctx->socketList[i]) { + ws_ctx->socketList[i] = wsock; + wsock->conn = conn; + wsock->webSockState = 1; + break; + } + } + printf("\nNew websocket attached: %s:%u\n", + rq->remote_addr, + rq->remote_port); + mg_unlock_context(ctx); +} + + +static void +websocket_done(tWebSockContext *ws_ctx, tWebSockInfo *wsock) +{ + + int i; + + if (wsock) { + wsock->webSockState = 99; + for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) { + if (wsock == ws_ctx->socketList[i]) { + ws_ctx->socketList[i] = 0; + break; + } + } + printf("\nClose websocket attached: %s:%u\n", + mg_get_request_info(wsock->conn)->remote_addr, + mg_get_request_info(wsock->conn)->remote_port); + free(wsock); + } +} + + +int +websocket_data_handler(struct mg_connection *conn, + int flags, + char *data, + size_t data_len, + void *_ignored) +{ + + const struct mg_request_info *rq = mg_get_request_info(conn); + tWebSockInfo *wsock = (tWebSockInfo *)rq->conn_data; + struct mg_context *ctx = mg_get_context(conn); + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + char msg[128]; + + mg_lock_context(ctx); + if (flags == 136) { + // close websock + websocket_done(ws_ctx, wsock); + mg_set_user_connection_data(conn, NULL); + mg_unlock_context(ctx); + return 1; + } + if (((data_len >= 5) && (data_len < 100) && (flags == 129)) + || (flags == 130)) { + + // init command + if ((wsock->webSockState == 1) && (!memcmp(data, "init ", 5))) { + char *chk; + unsigned long gid; + memcpy(msg, data + 5, data_len - 5); + msg[data_len - 5] = 0; + gid = strtoul(msg, &chk, 10); + wsock->initId = gid; + if (gid > 0 && chk != NULL && *chk == 0) { + wsock->webSockState = 2; + } + mg_unlock_context(ctx); + return 1; + } + + // chat message + if ((wsock->webSockState == 2) && (!memcmp(data, "msg ", 4))) { + send_to_all_websockets(ctx, data, data_len); + mg_unlock_context(ctx); + return 1; + } + } + + // keep alive + if ((data_len == 4) && !memcmp(data, "ping", 4)) { + mg_unlock_context(ctx); + return 1; + } + + mg_unlock_context(ctx); + return 0; +} + + +void +connection_close_handler(const struct mg_connection *conn, void *_ignored) +{ + + const struct mg_request_info *rq = mg_get_request_info(conn); + tWebSockInfo *wsock = (tWebSockInfo *)rq->conn_data; + struct mg_context *ctx = mg_get_context(conn); + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + + mg_lock_context(ctx); + websocket_done(ws_ctx, wsock); + mg_set_user_connection_data(conn, NULL); + mg_unlock_context(ctx); +} + + +static void * +eventMain(void *arg) +{ + + char msg[256]; + struct mg_context *ctx = (struct mg_context *)arg; + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + + ws_ctx->runLoop = 1; + while (ws_ctx->runLoop) { + time_t t = time(0); + struct tm *timestr = localtime(&t); + strftime(msg, sizeof(msg), "title %c", timestr); + send_to_all_websockets(ctx, msg, strlen(msg)); + + mg_sleep(1000); + } + + return NULL; +} + + +void +websock_send_broadcast(struct mg_context *ctx, const char *data, int data_len) +{ + + char buffer[260]; + + if (data_len <= 256) { + strcpy(buffer, "msg "); + memcpy(buffer + 4, data, data_len); + + send_to_all_websockets(ctx, buffer, data_len + 4); + } +} + + +void +websock_init_lib(const struct mg_context *ctx) +{ + + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + memset(ws_ctx, 0, sizeof(*ws_ctx)); + /* todo: use mg_start_thread_id instead of mg_start_thread */ + mg_start_thread(eventMain, (void *)ctx); +} + + +void +websock_exit_lib(const struct mg_context *ctx) +{ + + tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx); + ws_ctx->runLoop = 0; + /* todo: wait for the thread instead of a timeout */ + mg_sleep(2000); +} diff --git a/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.h b/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.h new file mode 100644 index 000000000..f44821daf --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket/WebSockCallbacks.h @@ -0,0 +1,44 @@ + +#ifndef WEBSOCKCALLBACKS_H_INCLUDED +#define WEBSOCKCALLBACKS_H_INCLUDED + +#include "civetweb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct tWebSockInfo { + int webSockState; + unsigned long initId; + struct mg_connection *conn; +} tWebSockInfo; + +#define MAX_NUM_OF_WEBSOCKS (256) +typedef struct tWebSockContext { + int runLoop; + void *thread_id; + tWebSockInfo *socketList[MAX_NUM_OF_WEBSOCKS]; +} tWebSockContext; + + +void websock_init_lib(const struct mg_context *ctx); +void websock_exit_lib(const struct mg_context *ctx); + +void +websock_send_broadcast(struct mg_context *ctx, const char *data, int data_len); + +void websocket_ready_handler(struct mg_connection *conn, void *_ignored); +int websocket_data_handler(struct mg_connection *conn, + int flags, + char *data, + size_t data_len, + void *_ignored); +void connection_close_handler(const struct mg_connection *conn, void *_ignored); + + +#ifdef __cplusplus +} +#endif + +#endif
\ No newline at end of file diff --git a/src/civetweb/examples/_obsolete/websocket/websock.htm b/src/civetweb/examples/_obsolete/websocket/websock.htm new file mode 100644 index 000000000..4ff3a5fae --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket/websock.htm @@ -0,0 +1,55 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>Test</title>
+ <script type='text/javascript' language="javascript">
+ <!--
+ var connection;
+ var keepAlive = false;
+
+ function webSockKeepAlive() {
+ if (keepAlive) {
+ connection.send('ping'); // Send the message 'ping' to the server
+ setTimeout("webSockKeepAlive()", 10000);
+ }
+ }
+
+ function load() {
+ connection = new WebSocket("ws://127.0.0.1/MyWebSock");
+
+ connection.onopen = function () {
+ var send = "init " + Math.round(Math.random()*4294967294+1);
+ console.log('Client: ' + send);
+ connection.send(send);
+ keepAlive = true;
+ webSockKeepAlive();
+ };
+
+ connection.onerror = function (error) {
+ keepAlive = false;
+ connection.close();
+ console.log('WebSocket error: ' + error);
+ alert("WebSocket error");
+ };
+
+ connection.onmessage = function (e) {
+ console.log('Server: ' + e.data);
+ if (e.data.substring(0,5) == "title") {window.document.title = e.data.substring(6);}
+ else if (e.data.substring(0,3) == "msg") {
+ var msgStr = document.getElementById('msg');
+ msgStr.innerHTML = msgStr.innerHTML + e.data.substring(4);
+ }
+ };
+ }
+ //-->
+ </script>
+
+</head>
+<body onload="load()">
+ <input type="button" onclick="connection.send('msg A');" value="A"></button>
+ <input type="button" onclick="connection.send('msg B');" value="B"></button>
+ <input type="button" onclick="connection.send('msg C');" value="C"></button>
+ <input type="button" onclick="connection.send('msg D');" value="D"></button>
+ <b id="msg"></b>
+</body>
+</html>
\ No newline at end of file diff --git a/src/civetweb/examples/_obsolete/websocket/websocket.c b/src/civetweb/examples/_obsolete/websocket/websocket.c new file mode 100644 index 000000000..3aadf98b8 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket/websocket.c @@ -0,0 +1,65 @@ +/* This example uses deprecated interfaces: global websocket callbacks. + They have been superseeded by URI specific callbacks. + See examples/embedded_c for an up to date example. + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +#include "civetweb.h" +#include "WebSockCallbacks.h" + +int +main(void) +{ + struct mg_context *ctx = 0; + struct mg_callbacks callback_funcs = {0}; + tWebSockContext ws_ctx; + char inbuf[4]; + + const char *server_options[] = { + /* document_root: The path to the test function websock.htm */ + "document_root", + "../../examples/websocket", + + /* port: use http standard to match websocket url in websock.htm: + ws://127.0.0.1/MyWebSock */ + /* if the port is changed here, it needs to be changed in + websock.htm as well */ + "listening_ports", + "80", + + NULL}; + + callback_funcs.init_context = websock_init_lib; + callback_funcs.exit_context = websock_exit_lib; + + ctx = mg_start(&callback_funcs, &ws_ctx, server_options); + + mg_set_websocket_handler(ctx, + "/MyWebSock", + NULL, + websocket_ready_handler, + websocket_data_handler, + connection_close_handler, + NULL); + + printf("Connect to localhost:%s/websock.htm\n", + mg_get_option(ctx, "listening_ports")); + + puts("Enter an (ASCII) character or * to exit:"); + for (;;) { + fgets(inbuf, sizeof(inbuf), stdin); + + if (inbuf[0] == '*') { + break; + } + inbuf[0] = toupper(inbuf[0]); + websock_send_broadcast(ctx, inbuf, 1); + } + + mg_stop(ctx); + + return 0; +} diff --git a/src/civetweb/examples/_obsolete/websocket_client/Makefile b/src/civetweb/examples/_obsolete/websocket_client/Makefile new file mode 100644 index 000000000..e3ef13468 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/Makefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2014 Jordan Shelley +# https://github.com/jshelley +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = websocket_client +SRC = websocket_client.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib WITH_WEBSOCKET=1 + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/websocket_client/ssl/server.crt b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.crt new file mode 100644 index 000000000..a26359abf --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQC2BCIqIvgSUTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE0MDgyMTEyMzAwMVoXDTI0MDgxODEyMzAwMVowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA9k9s +gH22BCo9neTeB/YnilK7n0sMe0+pjS9KSWhU59Q4w8hqPrW0tuYikIDd0wVggkJF +BZNg4WPoulTdwXsgNBeG88q2wnNtUosXTS+KQTQBSiQof9Ay9GHQtgxnogI1zIXb +HOppyyG5zre8a/X6fzDOnFc4iJMBwxTAnjCqObkCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQBX9V46VUVsB9P9fb8sFuMx2ezFE42ynEeJPrKRrof+dFYbjvR1OUZFSLCy +aZKwVH7iCnVBJiU12JxO7PR3L6ob3FYPyNHQWYq1/IFUvqBRagehldj5H8iFeEDz +Wtz2+p1rUyVxcSUqTjobaji0aC8lzPZio0nd1KKM6A92/adHyQ== +-----END CERTIFICATE----- diff --git a/src/civetweb/examples/_obsolete/websocket_client/ssl/server.csr b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.csr new file mode 100644 index 000000000..4d4723b24 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.csr @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQD2T2yAfbYEKj2d5N4H9ieKUrufSwx7T6mNL0pJaFTn1DjD +yGo+tbS25iKQgN3TBWCCQkUFk2DhY+i6VN3BeyA0F4bzyrbCc21SixdNL4pBNAFK +JCh/0DL0YdC2DGeiAjXMhdsc6mnLIbnOt7xr9fp/MM6cVziIkwHDFMCeMKo5uQID +AQABoAAwDQYJKoZIhvcNAQEFBQADgYEA1EOFwyFJ2NAnRNktZCy5yVcLx9C78HoC +oHPPCOElu0VDIqe6ZecYdaqWbYlhGE0+isbOQn2CwHOeBGN8mIDsNUYzVEpsEfgg +9OK873LpE5pf4mdjSiRBXkk/h8BxuqkcKi+Qx+qEE7+dH2nK5aKeIHVvbLyfGOch +9I85q+msBNE= +-----END CERTIFICATE REQUEST----- diff --git a/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key new file mode 100644 index 000000000..f09cc38cb --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQD2T2yAfbYEKj2d5N4H9ieKUrufSwx7T6mNL0pJaFTn1DjDyGo+ +tbS25iKQgN3TBWCCQkUFk2DhY+i6VN3BeyA0F4bzyrbCc21SixdNL4pBNAFKJCh/ +0DL0YdC2DGeiAjXMhdsc6mnLIbnOt7xr9fp/MM6cVziIkwHDFMCeMKo5uQIDAQAB +AoGAYwospr3lomcZv5N3c9wWqhf6OWMD8dFma87IIBxDh7Rd3tuHXQ/TSnffDhvD +FkbjN31OI5/PJNH3knTtdg78MywPloE4jYsbt4+fEaW7Fzww2nU61N1p+mYk5d/b +SCTAHhGzF9g9ZMw25CCUFGjDU2z+Ty6my22Euxhk2Qq8tMECQQD9ZYIxWkPhywDW +pX3v70dqIv7411hEYpuL/ZJl26UCmQsj4HPtXQCraQksVPs74WY5aTtd6MAV9V3M +UnErHO5/AkEA+NdG2MmfBOBPusDB/WwQaUPiCWGITS9llGTR2JXbvDqmKgL1+UTG +o27sLNIFCrF1wejpyRGqwjcObFYR0yKrxwJBAOB2uPuK4DL1psp9Uq/mIDbOxVod +OF1rlCpP9w0vol5Iv+uJ+mc7SUqOAsg4h0yl/+2/YA1yDiXlcq96IDF2sXUCQGAv +Nh9Nr72+xpK1N0axopZNuu1NWdYb3/PAFKzXIBxdvyS2CEXVo8JAeeHJPFGpzo6p +bNRfk9WGWnjdu/4UhLkCQQCekR9zpIpzdJiPYCd6XMya+TPCDYlOQL1jlnJIRa2V +BEOz0rSpzXAGh0PyCB/kMneyVk87LWn8joE6179RoUfv +-----END RSA PRIVATE KEY----- diff --git a/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key.orig b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key.orig new file mode 100644 index 000000000..58e5653ca --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.key.orig @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,89778A6427F05D4A + +4aXqO/8oCHVfMLB+a1DfjbXyEddjbd7nB+YVFLPKy68Tam9PRTvC1zRHBet59ll0 +1w7R8tXR6/xH7HRhBeqDHCcuvBhtw3xGxtXWv54WBFhzuq7TvKOAaCFl++cw/JHq +PCS0rAaYnqF2MAgMi7QBjZKmHFHL43Gy60VfOrB0mmOdxqqXA0NBFC2uEd7Z/MAx +S2A85bNJJKQaWEeDThP1u0OOlNCq99lkLJ31jiOH7ntdL0/vqcbZ+PUtdPLwAG4L +1GUHuiC2v5FvDlPiejMk2dvrxCNpcu2e3tQKHpg2KcsTVrpB7EVzRSazln4HywUZ +EJfBvxqqrS7plImZgj4LXSnln0JPuBb+aHnhKIFvisjYSwqDGJnnp/OaD7YdRhYh +UCcL011Ge+yUbRipeAmHdtJlSUSdB14KWq+WdIX/KgCRGx06QZm9s1PBLH+fww+I +EL3A/LFX0a5KUHkCp29akYYv9bUYaQ79Nt7BlaEON+/SW3pJMbGr+nx8aqogr0Yo +SJ/Zz5TSDBhecRjbCDGkT6DizVZ8cbm2xl8QLBd0H+ZA6uYMgfpAOJGrJx3Nm4Lv +prEApgFtjSrsQDGYHAcmDMW1UWOVHuNp7BSvwUze9Ftnzr/jlpdzES2rhgMyGhg1 +0Szbsfs3vgw4iM83LFJXza07GQJzF8gRF79dY5JiQX/sOKUprA6Lofk631jE0G8r +3z59cxblaq9y7EgFsE944Gk7/HIEimBRiqIZzGVJVukD0itynQ+XmYTdbyH1lpvi +c0ZheZPUoGwUW9RYy+nle5gEDFyZWXcCAuJasQvDBXt//r/bso3ZpA== +-----END RSA PRIVATE KEY----- diff --git a/src/civetweb/examples/_obsolete/websocket_client/ssl/server.pem b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.pem new file mode 100644 index 000000000..300834e39 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/ssl/server.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQC2BCIqIvgSUTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE0MDgyMTEyMzAwMVoXDTI0MDgxODEyMzAwMVowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA9k9s +gH22BCo9neTeB/YnilK7n0sMe0+pjS9KSWhU59Q4w8hqPrW0tuYikIDd0wVggkJF +BZNg4WPoulTdwXsgNBeG88q2wnNtUosXTS+KQTQBSiQof9Ay9GHQtgxnogI1zIXb +HOppyyG5zre8a/X6fzDOnFc4iJMBwxTAnjCqObkCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQBX9V46VUVsB9P9fb8sFuMx2ezFE42ynEeJPrKRrof+dFYbjvR1OUZFSLCy +aZKwVH7iCnVBJiU12JxO7PR3L6ob3FYPyNHQWYq1/IFUvqBRagehldj5H8iFeEDz +Wtz2+p1rUyVxcSUqTjobaji0aC8lzPZio0nd1KKM6A92/adHyQ== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQD2T2yAfbYEKj2d5N4H9ieKUrufSwx7T6mNL0pJaFTn1DjDyGo+ +tbS25iKQgN3TBWCCQkUFk2DhY+i6VN3BeyA0F4bzyrbCc21SixdNL4pBNAFKJCh/ +0DL0YdC2DGeiAjXMhdsc6mnLIbnOt7xr9fp/MM6cVziIkwHDFMCeMKo5uQIDAQAB +AoGAYwospr3lomcZv5N3c9wWqhf6OWMD8dFma87IIBxDh7Rd3tuHXQ/TSnffDhvD +FkbjN31OI5/PJNH3knTtdg78MywPloE4jYsbt4+fEaW7Fzww2nU61N1p+mYk5d/b +SCTAHhGzF9g9ZMw25CCUFGjDU2z+Ty6my22Euxhk2Qq8tMECQQD9ZYIxWkPhywDW +pX3v70dqIv7411hEYpuL/ZJl26UCmQsj4HPtXQCraQksVPs74WY5aTtd6MAV9V3M +UnErHO5/AkEA+NdG2MmfBOBPusDB/WwQaUPiCWGITS9llGTR2JXbvDqmKgL1+UTG +o27sLNIFCrF1wejpyRGqwjcObFYR0yKrxwJBAOB2uPuK4DL1psp9Uq/mIDbOxVod +OF1rlCpP9w0vol5Iv+uJ+mc7SUqOAsg4h0yl/+2/YA1yDiXlcq96IDF2sXUCQGAv +Nh9Nr72+xpK1N0axopZNuu1NWdYb3/PAFKzXIBxdvyS2CEXVo8JAeeHJPFGpzo6p +bNRfk9WGWnjdu/4UhLkCQQCekR9zpIpzdJiPYCd6XMya+TPCDYlOQL1jlnJIRa2V +BEOz0rSpzXAGh0PyCB/kMneyVk87LWn8joE6179RoUfv +-----END RSA PRIVATE KEY----- diff --git a/src/civetweb/examples/_obsolete/websocket_client/websocket_client.c b/src/civetweb/examples/_obsolete/websocket_client/websocket_client.c new file mode 100644 index 000000000..158ad7d85 --- /dev/null +++ b/src/civetweb/examples/_obsolete/websocket_client/websocket_client.c @@ -0,0 +1,421 @@ +/* +* Copyright (c) 2014-2016 the Civetweb developers +* Copyright (c) 2014 Jordan Shelley +* https://github.com/jshelley +* License http://opensource.org/licenses/mit-license.php MIT License +*/ + +/* This example is superseeded by other examples, and no longer + * actively maintained. + * See examples/embedded_c for an up to date example. + */ + +// Simple example program on how to use websocket client embedded C interface. +#ifdef _WIN32 +#include <windows.h> +#define sleep(x) Sleep(1000 * (x)) +#else +#include <unistd.h> +#endif + +#include <assert.h> +#include <string.h> +#include "civetweb.h" + +#define DOCUMENT_ROOT "." +#define PORT "8888" +#define SSL_CERT "./ssl/server.pem" + +const char *websocket_welcome_msg = "websocket welcome\n"; +const size_t websocket_welcome_msg_len = 18 /* strlen(websocket_welcome_msg) */; +const char *websocket_acknowledge_msg = "websocket msg ok\n"; +const size_t websocket_acknowledge_msg_len = + 17 /* strlen(websocket_acknowledge_msg) */; +const char *websocket_goodbye_msg = "websocket bye\n"; +const size_t websocket_goodbye_msg_len = 14 /* strlen(websocket_goodbye_msg) */; + + +/*************************************************************************************/ +/* WEBSOCKET SERVER */ +/*************************************************************************************/ +#if defined(MG_LEGACY_INTERFACE) +int +websock_server_connect(const struct mg_connection *conn) +#else +int +websocket_server_connect(const struct mg_connection *conn, void *_ignored) +#endif +{ + printf("Server: Websocket connected\n"); + return 0; /* return 0 to accept every connection */ +} + + +#if defined(MG_LEGACY_INTERFACE) +void +websocket_server_ready(struct mg_connection *conn) +#else +void +websocket_server_ready(struct mg_connection *conn, void *_ignored) +#endif +{ + printf("Server: Websocket ready\n"); + + /* Send websocket welcome message */ + mg_lock_connection(conn); + mg_websocket_write(conn, + WEBSOCKET_OPCODE_TEXT, + websocket_welcome_msg, + websocket_welcome_msg_len); + mg_unlock_connection(conn); +} + + +#if defined(MG_LEGACY_INTERFACE) +int +websocket_server_data(struct mg_connection *conn, + int bits, + char *data, + size_t data_len) +#else +int +websocket_server_data(struct mg_connection *conn, + int bits, + char *data, + size_t data_len, + void *_ignored) +#endif +{ + printf("Server: Got %lu bytes from the client\n", (unsigned long)data_len); + printf("Server received data from client: "); + fwrite(data, 1, data_len, stdout); + printf("\n"); + + if (data_len < 3 || 0 != memcmp(data, "bye", 3)) { + /* Send websocket acknowledge message */ + mg_lock_connection(conn); + mg_websocket_write(conn, + WEBSOCKET_OPCODE_TEXT, + websocket_acknowledge_msg, + websocket_acknowledge_msg_len); + mg_unlock_connection(conn); + } else { + /* Send websocket acknowledge message */ + mg_lock_connection(conn); + mg_websocket_write(conn, + WEBSOCKET_OPCODE_TEXT, + websocket_goodbye_msg, + websocket_goodbye_msg_len); + mg_unlock_connection(conn); + } + + return 1; /* return 1 to keep the connetion open */ +} + + +#if defined(MG_LEGACY_INTERFACE) +void +websocket_server_connection_close(const struct mg_connection *conn) +#else +void +websocket_server_connection_close(const struct mg_connection *conn, + void *_ignored) +#endif +{ + printf("Server: Close connection\n"); + + /* Can not send a websocket goodbye message here - the connection is already + * closed */ +} + + +struct mg_context * +start_websocket_server() +{ + const char *options[] = {"document_root", + DOCUMENT_ROOT, + "ssl_certificate", + SSL_CERT, + "listening_ports", + PORT, + "request_timeout_ms", + "5000", + 0}; + struct mg_callbacks callbacks; + struct mg_context *ctx; + + memset(&callbacks, 0, sizeof(callbacks)); + +#if defined(MG_LEGACY_INTERFACE) + /* Obsolete: */ + callbacks.websocket_connect = websock_server_connect; + callbacks.websocket_ready = websocket_server_ready; + callbacks.websocket_data = websocket_server_data; + callbacks.connection_close = websocket_server_connection_close; + + ctx = mg_start(&callbacks, 0, options); +#else + /* New interface: */ + ctx = mg_start(&callbacks, 0, options); + + mg_set_websocket_handler(ctx, + "/websocket", + websocket_server_connect, + websocket_server_ready, + websocket_server_data, + websocket_server_connection_close, + NULL); +#endif + + return ctx; +} + + +/*************************************************************************************/ +/* WEBSOCKET CLIENT */ +/*************************************************************************************/ +struct tclient_data { + void *data; + size_t len; + int closed; +}; + +static int +websocket_client_data_handler(struct mg_connection *conn, + int flags, + char *data, + size_t data_len, + void *user_data) +{ + struct mg_context *ctx = mg_get_context(conn); + struct tclient_data *pclient_data = + (struct tclient_data *)mg_get_user_data(ctx); + + printf("Client received data from server: "); + fwrite(data, 1, data_len, stdout); + printf("\n"); + + pclient_data->data = malloc(data_len); + assert(pclient_data->data != NULL); + memcpy(pclient_data->data, data, data_len); + pclient_data->len = data_len; + + return 1; +} + +static void +websocket_client_close_handler(const struct mg_connection *conn, + void *user_data) +{ + struct mg_context *ctx = mg_get_context(conn); + struct tclient_data *pclient_data = + (struct tclient_data *)mg_get_user_data(ctx); + + printf("Client: Close handler\n"); + pclient_data->closed++; +} + + +int +main(int argc, char *argv[]) +{ + struct mg_context *ctx = NULL; + struct tclient_data client1_data = {NULL, 0, 0}; + struct tclient_data client2_data = {NULL, 0, 0}; + struct tclient_data client3_data = {NULL, 0, 0}; + struct mg_connection *newconn1 = NULL; + struct mg_connection *newconn2 = NULL; + struct mg_connection *newconn3 = NULL; + char ebuf[100] = {0}; + + assert(websocket_welcome_msg_len == strlen(websocket_welcome_msg)); + + /* First set up a websocket server */ + ctx = start_websocket_server(); + assert(ctx != NULL); + printf("Server init\n\n"); + + /* Then connect a first client */ + newconn1 = mg_connect_websocket_client("localhost", + atoi(PORT), + 0, + ebuf, + sizeof(ebuf), + "/websocket", + NULL, + websocket_client_data_handler, + websocket_client_close_handler, + &client1_data); + + if (newconn1 == NULL) { + printf("Error: %s", ebuf); + return 1; + } + + sleep(1); /* Should get the websocket welcome message */ + assert(client1_data.closed == 0); + assert(client2_data.closed == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + assert(client1_data.data != NULL); + assert(client1_data.len == websocket_welcome_msg_len); + assert(!memcmp(client1_data.data, + websocket_welcome_msg, + websocket_welcome_msg_len)); + free(client1_data.data); + client1_data.data = NULL; + client1_data.len = 0; + + mg_websocket_client_write(newconn1, WEBSOCKET_OPCODE_TEXT, "data1", 5); + + sleep(1); /* Should get the acknowledge message */ + assert(client1_data.closed == 0); + assert(client2_data.closed == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + assert(client1_data.data != NULL); + assert(client1_data.len == websocket_acknowledge_msg_len); + assert(!memcmp(client1_data.data, + websocket_acknowledge_msg, + websocket_acknowledge_msg_len)); + free(client1_data.data); + client1_data.data = NULL; + client1_data.len = 0; + + /* Now connect a second client */ + newconn2 = mg_connect_websocket_client("localhost", + atoi(PORT), + 0, + ebuf, + sizeof(ebuf), + "/websocket", + NULL, + websocket_client_data_handler, + websocket_client_close_handler, + &client2_data); + + if (newconn2 == NULL) { + printf("Error: %s", ebuf); + return 1; + } + + sleep(1); /* Client 2 should get the websocket welcome message */ + assert(client1_data.closed == 0); + assert(client2_data.closed == 0); + assert(client1_data.data == NULL); + assert(client1_data.len == 0); + assert(client2_data.data != NULL); + assert(client2_data.len == websocket_welcome_msg_len); + assert(!memcmp(client2_data.data, + websocket_welcome_msg, + websocket_welcome_msg_len)); + free(client2_data.data); + client2_data.data = NULL; + client2_data.len = 0; + + mg_websocket_client_write(newconn1, WEBSOCKET_OPCODE_TEXT, "data2", 5); + + sleep(1); /* Should get the acknowledge message */ + assert(client1_data.closed == 0); + assert(client2_data.closed == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + assert(client1_data.data != NULL); + assert(client1_data.len == websocket_acknowledge_msg_len); + assert(!memcmp(client1_data.data, + websocket_acknowledge_msg, + websocket_acknowledge_msg_len)); + free(client1_data.data); + client1_data.data = NULL; + client1_data.len = 0; + + mg_websocket_client_write(newconn1, WEBSOCKET_OPCODE_TEXT, "bye", 3); + + sleep(1); /* Should get the goodbye message */ + assert(client1_data.closed == 0); + assert(client2_data.closed == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + assert(client1_data.data != NULL); + assert(client1_data.len == websocket_goodbye_msg_len); + assert(!memcmp(client1_data.data, + websocket_goodbye_msg, + websocket_goodbye_msg_len)); + free(client1_data.data); + client1_data.data = NULL; + client1_data.len = 0; + + mg_close_connection(newconn1); + + sleep(1); /* Won't get any message */ + assert(client1_data.closed == 1); + assert(client2_data.closed == 0); + assert(client1_data.data == NULL); + assert(client1_data.len == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + + mg_websocket_client_write(newconn2, WEBSOCKET_OPCODE_TEXT, "bye", 3); + + sleep(1); /* Should get the goodbye message */ + assert(client1_data.closed == 1); + assert(client2_data.closed == 0); + assert(client1_data.data == NULL); + assert(client1_data.len == 0); + assert(client2_data.data != NULL); + assert(client2_data.len == websocket_goodbye_msg_len); + assert(!memcmp(client2_data.data, + websocket_goodbye_msg, + websocket_goodbye_msg_len)); + free(client2_data.data); + client2_data.data = NULL; + client2_data.len = 0; + + mg_close_connection(newconn2); + + sleep(1); /* Won't get any message */ + assert(client1_data.closed == 1); + assert(client2_data.closed == 1); + assert(client1_data.data == NULL); + assert(client1_data.len == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + + /* Connect client 3 */ + newconn3 = mg_connect_websocket_client("localhost", + atoi(PORT), + 0, + ebuf, + sizeof(ebuf), + "/websocket", + NULL, + websocket_client_data_handler, + websocket_client_close_handler, + &client3_data); + + sleep(1); /* Client 3 should get the websocket welcome message */ + assert(client1_data.closed == 1); + assert(client2_data.closed == 1); + assert(client3_data.closed == 0); + assert(client1_data.data == NULL); + assert(client1_data.len == 0); + assert(client2_data.data == NULL); + assert(client2_data.len == 0); + assert(client3_data.data != NULL); + assert(client3_data.len == websocket_welcome_msg_len); + assert(!memcmp(client3_data.data, + websocket_welcome_msg, + websocket_welcome_msg_len)); + free(client3_data.data); + client3_data.data = NULL; + client3_data.len = 0; + + mg_stop(ctx); + printf("Server shutdown\n"); + + sleep(10); + + assert(client3_data.closed == 1); + + return 0; +} diff --git a/src/civetweb/examples/_obsolete/ws_server/Makefile b/src/civetweb/examples/_obsolete/ws_server/Makefile new file mode 100644 index 000000000..60d9dbedb --- /dev/null +++ b/src/civetweb/examples/_obsolete/ws_server/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = ws_server +SRC = ws_server.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl -DMG_LEGACY_INTERFACE=1 +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib WITH_WEBSOCKET=1 + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/_obsolete/ws_server/docroot/index.html b/src/civetweb/examples/_obsolete/ws_server/docroot/index.html new file mode 100644 index 000000000..f1d1af3eb --- /dev/null +++ b/src/civetweb/examples/_obsolete/ws_server/docroot/index.html @@ -0,0 +1,316 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset='UTF-8'> + + <title>Websocket Meters</title> + <!-- + Version 0.1 Contributed by William Greathouse 9-Sep-2013 + Simple demo of WebSocket connection use. Not a great example of web coding, + but it is functional. + + The meter displays are adapted from CSS-TRICKS Progress Bars by Chris Coyier + at http://css-tricks.com/css3-progress-bars/ + --> + <style> + body { + background: #222; + } + h1 { + color: white; + text-align: center; + } + button { + width: 225px; + height: 30px; + margin: auto 10px; + background-color: #ccc; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius:6px; + color: blue; + font-size: 20px; + } + button:hover { + background-color: #888; + } + button:hover:disabled { + background-color: #ccc; + } + button:disabled { + color: lightgray; + } + + .button_container { + width:550px; + display:block; + margin-left:auto; + margin-right:auto; + } + + .meter { + height: 20px; /* Can be anything */ + position: relative; + background: #555; + -moz-border-radius: 25px; + -webkit-border-radius: 25px; + border-radius: 25px; + padding: 10px; + -webkit-box-shadow: inset 0 -1px 1px rgba(255,255,255,0.3); + -moz-box-shadow : inset 0 -1px 1px rgba(255,255,255,0.3); + box-shadow : inset 0 -1px 1px rgba(255,255,255,0.3); + } + .meter > span { + display: block; + height: 100%; + -webkit-border-top-right-radius: 20px; + -webkit-border-bottom-right-radius: 20px; + -moz-border-radius-topright: 20px; + -moz-border-radius-bottomright: 20px; + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + -webkit-border-top-left-radius: 20px; + -webkit-border-bottom-left-radius: 20px; + -moz-border-radius-topleft: 20px; + -moz-border-radius-bottomleft: 20px; + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + background-color: rgb(43,194,83); + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0, rgb(43,194,83)), + color-stop(1, rgb(84,240,84)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(43,194,83) 37%, + rgb(84,240,84) 69% + ); + -webkit-box-shadow: + inset 0 2px 9px rgba(255,255,255,0.3), + inset 0 -2px 6px rgba(0,0,0,0.4); + -moz-box-shadow: + inset 0 2px 9px rgba(255,255,255,0.3), + inset 0 -2px 6px rgba(0,0,0,0.4); + box-shadow: + inset 0 2px 9px rgba(255,255,255,0.3), + inset 0 -2px 6px rgba(0,0,0,0.4); + position: relative; + overflow: hidden; + } + .meter > span:after, .animate > span > span { + content: ""; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + background-image: + -webkit-gradient(linear, 0 0, 100% 100%, + color-stop(.25, rgba(255, 255, 255, .2)), + color-stop(.25, transparent), color-stop(.5, transparent), + color-stop(.5, rgba(255, 255, 255, .2)), + color-stop(.75, rgba(255, 255, 255, .2)), + color-stop(.75, transparent), to(transparent) + ); + background-image: + -moz-linear-gradient( + -45deg, + rgba(255, 255, 255, .2) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .2) 50%, + rgba(255, 255, 255, .2) 75%, + transparent 75%, + transparent + ); + z-index: 1; + -webkit-background-size: 50px 50px; + -moz-background-size: 50px 50px; + -webkit-animation: move 2s linear infinite; + -webkit-border-top-right-radius: 20px; + -webkit-border-bottom-right-radius: 20px; + -moz-border-radius-topright: 20px; + -moz-border-radius-bottomright: 20px; + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + -webkit-border-top-left-radius: 20px; + -webkit-border-bottom-left-radius: 20px; + -moz-border-radius-topleft: 20px; + -moz-border-radius-bottomleft: 20px; + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + overflow: hidden; + } + + .animate > span:after { + display: none; + } + + @-webkit-keyframes move { + 0% { + background-position: 0 0; + } + 100% { + background-position: 50px 50px; + } + } + + .orange > span { + background-color: #f1a165; + background-image: -moz-linear-gradient(top, #f1a165, #f36d0a); + background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f1a165),color-stop(1, #f36d0a)); + background-image: -webkit-linear-gradient(#f1a165, #f36d0a); + } + + .red > span { + background-color: #f0a3a3; + background-image: -moz-linear-gradient(top, #f0a3a3, #f42323); + background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f0a3a3),color-stop(1, #f42323)); + background-image: -webkit-linear-gradient(#f0a3a3, #f42323); + } + + .nostripes > span > span, .nostripes > span:after { + -webkit-animation: none; + background-image: none; + } + + #output { + background-color: #ccc; + height: 240px; + overflow-y: auto; + } + + </style> +</head> + +<body> + <div id="page_wrap"> + + <h1>Meter Updates via WebSocket</h1> + + <p/> + + <div class="meter"> + <span id="meter1" style="width: 25%"></span> + </div> + + <p/> + + <div class="meter orange nostripes"> + <span id="meter2" style="width: 33.3%"></span> + </div> + + <p/> + + <div class="meter red"> + <span id="meter3" style="width: 80%"></span> + </div> + + <p/> + + </div> + <div class="button_container"> + <div> + <button id="connection" onclick="toggleConnection(this)">WebSocket Connect</button> + <button id="update" disabled onclick="toggleUpdate(this)">Disable Update</button> + </div> + </div> + <p/> + <div id="output"></div> + +</body> + +<script language="javascript" type="text/javascript"> + var connection; // websocket connection + + function writeToScreen (message) { + var div = document.createElement('div'); + var output = document.getElementById('output'); + div.innerHTML = message; + output.appendChild(div); + output.scrollTop = output.scrollHeight; + } + + function ws_connect() { + // check for websocket support + // for Internet Explorer < 10 there are options for websocket support that + // could be integrated into production code, but for now, we are expecting + // browser support to be present for this demo + if ('WebSocket' in window) { + + writeToScreen('Connecting'); + connection = new WebSocket('ws://' + window.location.host + '/meters'); + connection.onopen = function(ev) { + document.getElementById("connection").innerHTML = "WebSocket Disconnect"; + document.getElementById("update").disabled=false; + document.getElementById("update").innerHTML = "Disable Update"; + writeToScreen('CONNECTED'); + var message = 'update on'; + writeToScreen('SENT: ' + message); + connection.send(message); + }; + + connection.onclose = function(ev) { + document.getElementById("update").disabled=true; + document.getElementById("update").innerHTML = "Enable Update"; + document.getElementById("connection").innerHTML = "WebSocket Connect"; + writeToScreen('DISCONNECTED'); + }; + + connection.onmessage = function(ev) { + if (ev.data.substr(0,5) == "meter") + { + var target = ev.data.split(":")[0]; + var meter = document.getElementById(target); + var data = ev.data.split(":")[1].split(","); + var percent = (data[0]*100)/data[1]; + meter.style.width = percent+"%"; + } + else + writeToScreen('RECEIVED: ' + ev.data); + }; + + connection.onerror = function(ev) { + alert("WebSocket error"); + }; + + } else { + alert("WebSocket is not available!!!\n" + + "Demo will not function."); + } + } + + // user connect/disconnect + function toggleConnection(el) { + var tag=el.innerHTML; + if (tag == "WebSocket Connect") + { + ws_connect(); + } + else + { + connection.close(); + } + } + + // user turn updates on/off + function toggleUpdate(el) { + var tag=el.innerHTML; + var message; + if (tag == "Enable Update") + { + message = 'update on'; + el.innerHTML = "Disable Update"; + } + else + { + message = 'update off'; + el.innerHTML = "Enable Update"; + } + writeToScreen('SENT: ' + message); + connection.send(message); + } +</script> + +</html> diff --git a/src/civetweb/examples/_obsolete/ws_server/ws_server.c b/src/civetweb/examples/_obsolete/ws_server/ws_server.c new file mode 100644 index 000000000..575a26a69 --- /dev/null +++ b/src/civetweb/examples/_obsolete/ws_server/ws_server.c @@ -0,0 +1,271 @@ +// Copyright (c) 2004-2012 Sergey Lyubka +// This file is a part of civetweb project, http://github.com/bel2125/civetweb +// +// v 0.1 Contributed by William Greathouse 9-Sep-2013 + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "civetweb.h" + +// simple structure for keeping track of websocket connection +struct ws_connection { + struct mg_connection *conn; + int update; + int closing; +}; + +// time base and structure periodic updates to client for demo +#define BASETIME 100000 /* 0.1 seconds */ +struct progress { + int limit; + int increment; + int period; + int value; +}; + +// up to 16 independent client connections +#define CONNECTIONS 16 +static struct ws_connection ws_conn[CONNECTIONS]; + + +// ws_server_thread() +// Simple demo server thread. Sends periodic updates to connected clients +static void *ws_server_thread(void *parm) +{ + int wsd = (long)parm; + struct mg_connection *conn = ws_conn[wsd].conn; + int timer = 0; + char tstr[32]; + int i; + struct progress meter[] = { + /* first meter 0 to 1000, by 5 every 0.1 second */ + { 1000, 5, 1, 0 }, + /* second meter 0 to 500, by 10 every 0.5 second */ + { 500, 10, 5, 0 }, + /* third meter 0 to 100, by 10 every 1.0 second */ + { 100, 10, 10, 0}, + /* end of list */ + { 0, 0, 0, 0} + }; + + fprintf(stderr, "ws_server_thread %d\n", wsd); + + /* Send initial meter updates */ + for (i=0; meter[i].period != 0; i++) { + if (meter[i].value >= meter[i].limit) + meter[i].value = 0; + if (meter[i].value >= meter[i].limit) + meter[i].value = meter[i].limit; + sprintf(tstr, "meter%d:%d,%d", i+1, + meter[i].value, meter[i].limit); + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr)); + } + + /* While the connection is open, send periodic updates */ + while(!ws_conn[wsd].closing) { + usleep(100000); /* 0.1 second */ + timer++; + + /* Send meter updates */ + if (ws_conn[wsd].update) { + for (i=0; meter[i].period != 0; i++) { + if (timer%meter[i].period == 0) { + if (meter[i].value >= meter[i].limit) + meter[i].value = 0; + else + meter[i].value += meter[i].increment; + if (meter[i].value >= meter[i].limit) + meter[i].value = meter[i].limit; + // if we are closing, server should not send new data + if (!ws_conn[wsd].closing) { + sprintf(tstr, "meter%d:%d,%d", i+1, + meter[i].value, meter[i].limit); + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr)); + } + } + } + } + + /* Send periodic PING to assure websocket remains connected, except if we are closing */ + if (timer%100 == 0 && !ws_conn[wsd].closing) + mg_websocket_write(conn, WEBSOCKET_OPCODE_PING, NULL, 0); + } + + fprintf(stderr, "ws_server_thread %d exiting\n", wsd); + + // reset connection information to allow reuse by new client + ws_conn[wsd].conn = NULL; + ws_conn[wsd].update = 0; + ws_conn[wsd].closing = 2; + + return NULL; +} + +// websocket_connect_handler() +// On new client connection, find next available server connection and store +// new connection information. If no more server connections are available +// tell civetweb to not accept the client request. +static int websocket_connect_handler(const struct mg_connection *conn) +{ + int i; + + fprintf(stderr, "connect handler\n"); + + for(i=0; i < CONNECTIONS; ++i) { + if (ws_conn[i].conn == NULL) { + fprintf(stderr, "...prep for server %d\n", i); + ws_conn[i].conn = (struct mg_connection *)conn; + ws_conn[i].closing = 0; + ws_conn[i].update = 0; + break; + } + } + if (i >= CONNECTIONS) { + fprintf(stderr, "Refused connection: Max connections exceeded\n"); + return 1; + } + + return 0; +} + +// websocket_ready_handler() +// Once websocket negotiation is complete, start a server for the connection +static void websocket_ready_handler(struct mg_connection *conn) +{ + int i; + + fprintf(stderr, "ready handler\n"); + + for(i=0; i < CONNECTIONS; ++i) { + if (ws_conn[i].conn == conn) { + fprintf(stderr, "...start server %d\n", i); + mg_start_thread(ws_server_thread, (void *)(long)i); + break; + } + } +} + +// websocket_close_handler() +// When websocket is closed, tell the associated server to shut down +static void websocket_close_handler(struct mg_connection *conn) +{ + int i; + + //fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */ + + for(i=0; i < CONNECTIONS; ++i) { + if (ws_conn[i].conn == conn) { + fprintf(stderr, "...close server %d\n", i); + ws_conn[i].closing = 1; + } + } +} + +// Arguments: +// flags: first byte of websocket frame, see websocket RFC, +// http://tools.ietf.org/html/rfc6455, section 5.2 +// data, data_len: payload data. Mask, if any, is already applied. +static int websocket_data_handler(struct mg_connection *conn, int flags, + char *data, size_t data_len) +{ + int i; + int wsd; + + for(i=0; i < CONNECTIONS; ++i) { + if (ws_conn[i].conn == conn) { + wsd = i; + break; + } + } + if (i >= CONNECTIONS) { + fprintf(stderr, "Received websocket data from unknown connection\n"); + return 1; + } + + if (flags & 0x80) { + flags &= 0x7f; + switch (flags) { + case WEBSOCKET_OPCODE_CONTINUATION: + fprintf(stderr, "CONTINUATION...\n"); + break; + case WEBSOCKET_OPCODE_TEXT: + fprintf(stderr, "TEXT: %-.*s\n", (int)data_len, data); + /*** interpret data as commands here ***/ + if (strncmp("update on", data, data_len)== 0) { + /* turn on updates */ + ws_conn[wsd].update = 1; + /* echo back */ + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len); + } else if (strncmp("update off", data, data_len)== 0) { + /* turn off updates */ + ws_conn[wsd].update = 0; + /* echo back */ + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len); + } + break; + case WEBSOCKET_OPCODE_BINARY: + fprintf(stderr, "BINARY...\n"); + break; + case WEBSOCKET_OPCODE_CONNECTION_CLOSE: + fprintf(stderr, "CLOSE...\n"); + /* If client initiated close, respond with close message in acknowlegement */ + if (!ws_conn[wsd].closing) { + mg_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, data, data_len); + ws_conn[wsd].closing = 1; /* we should not send addional messages when close requested/acknowledged */ + } + return 0; /* time to close the connection */ + break; + case WEBSOCKET_OPCODE_PING: + /* client sent PING, respond with PONG */ + mg_websocket_write(conn, WEBSOCKET_OPCODE_PONG, data, data_len); + break; + case WEBSOCKET_OPCODE_PONG: + /* received PONG to our PING, no action */ + break; + default: + fprintf(stderr, "Unknown flags: %02x\n", flags); + break; + } + } + + return 1; /* keep connection open */ +} + + +int main(void) +{ + char server_name[40]; + struct mg_context *ctx; + struct mg_callbacks callbacks; + const char *options[] = { + "listening_ports", "8080", + "document_root", "docroot", + NULL + }; + + /* get simple greeting for the web server */ + snprintf(server_name, sizeof(server_name), + "Civetweb websocket server v. %s", + mg_version()); + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.websocket_connect = websocket_connect_handler; + callbacks.websocket_ready = websocket_ready_handler; + callbacks.websocket_data = websocket_data_handler; + callbacks.connection_close = websocket_close_handler; + + ctx = mg_start(&callbacks, NULL, options); + + /* show the greeting and some basic information */ + printf("%s started on port(s) %s with web root [%s]\n", + server_name, mg_get_option(ctx, "listening_ports"), + mg_get_option(ctx, "document_root")); + + getchar(); // Wait until user hits "enter" + mg_stop(ctx); + + return 0; +} diff --git a/src/civetweb/examples/embedded_c/Makefile b/src/civetweb/examples/embedded_c/Makefile new file mode 100644 index 000000000..93d6379db --- /dev/null +++ b/src/civetweb/examples/embedded_c/Makefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = embedded_c +SRC = embedded_c.c + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) -DUSE_WEBSOCKET -DUSE_IPV6 +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) -lcrypto -lssl -DUSE_SSL_DH=1 + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) WITH_IPV6=1 WITH_WEBSOCKET=1 COPT='-DNO_SSL_DL=1' clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean + diff --git a/src/civetweb/examples/embedded_c/embedded_c.c b/src/civetweb/examples/embedded_c/embedded_c.c new file mode 100644 index 000000000..34a361e85 --- /dev/null +++ b/src/civetweb/examples/embedded_c/embedded_c.c @@ -0,0 +1,1089 @@ +/* +* Copyright (c) 2013-2017 the CivetWeb developers +* Copyright (c) 2013 No Face Press, LLC +* License http://opensource.org/licenses/mit-license.php MIT License +*/ + +/* Simple example program on how to use CivetWeb embedded into a C program. */ +#ifdef _WIN32 +#include <windows.h> +#else +#include <unistd.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "civetweb.h" + + +#define DOCUMENT_ROOT "." +#ifdef NO_SSL +#ifdef USE_IPV6 +#define PORT "[::]:8888,8884" +#else +#define PORT "8888,8884" +#endif +#else +#ifdef USE_IPV6 +#define PORT "[::]:8888r,[::]:8843s,8884" +#else +#define PORT "8888r,8843s,8884" +#endif +#endif +#define EXAMPLE_URI "/example" +#define EXIT_URI "/exit" +int exitNow = 0; + + +int +ExampleHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is an example text from a C handler</h2>"); + mg_printf( + conn, + "<p>To see a page from the A handler <a href=\"A\">click A</a></p>"); + mg_printf(conn, + "<p>To see a page from the A handler <a href=\"A/A\">click " + "A/A</a></p>"); + mg_printf(conn, + "<p>To see a page from the A/B handler <a " + "href=\"A/B\">click A/B</a></p>"); + mg_printf(conn, + "<p>To see a page from the B handler (0) <a " + "href=\"B\">click B</a></p>"); + mg_printf(conn, + "<p>To see a page from the B handler (1) <a " + "href=\"B/A\">click B/A</a></p>"); + mg_printf(conn, + "<p>To see a page from the B handler (2) <a " + "href=\"B/B\">click B/B</a></p>"); + mg_printf(conn, + "<p>To see a page from the *.foo handler <a " + "href=\"xy.foo\">click xy.foo</a></p>"); + mg_printf(conn, + "<p>To see a page from the close handler <a " + "href=\"close\">click close</a></p>"); + mg_printf(conn, + "<p>To see a page from the FileHandler handler <a " + "href=\"form\">click form</a> (the starting point of the " + "<b>form</b> test)</p>"); + mg_printf(conn, + "<p>To see a page from the CookieHandler handler <a " + "href=\"cookie\">click cookie</a></p>"); + mg_printf(conn, + "<p>To see a page from the PostResponser handler <a " + "href=\"postresponse\">click post response</a></p>"); + mg_printf(conn, + "<p>To see an example for parsing files on the fly <a " + "href=\"on_the_fly_form\">click form</a> (form for " + "uploading files)</p>"); + +#ifdef USE_WEBSOCKET + mg_printf(conn, + "<p>To test the websocket handler <a href=\"/websocket\">click " + "websocket</a></p>"); +#endif + + mg_printf(conn, + "<p>To test the authentication handler <a href=\"/auth\">click " + "auth</a></p>"); + + mg_printf(conn, "<p>To exit <a href=\"%s\">click exit</a></p>", EXIT_URI); + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +ExitHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/plain\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "Server will shut down.\n"); + mg_printf(conn, "Bye!\n"); + exitNow = 1; + return 1; +} + + +int +AHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the A handler!!!</h2>"); + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +ABHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the AB handler!!!</h2>"); + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +BXHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the BX handler %p!!!</h2>", cbdata); + mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri); + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +FooHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the Foo handler!!!</h2>"); + mg_printf(conn, + "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>", + req_info->request_method, + req_info->local_uri, + req_info->http_version); + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +CloseHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, + "<h2>This handler will close the connection in a second</h2>"); +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + mg_printf(conn, "bye"); + printf("CloseHandler: close connection\n"); + mg_close_connection(conn); + printf("CloseHandler: wait 10 sec\n"); +#ifdef _WIN32 + Sleep(10000); +#else + sleep(10); +#endif + printf("CloseHandler: return from function\n"); + return 1; +} + + +int +FileHandler(struct mg_connection *conn, void *cbdata) +{ + /* In this handler, we ignore the req_info and send the file "fileName". */ + const char *fileName = (const char *)cbdata; + + mg_send_file(conn, fileName); + return 1; +} + + +int +field_found(const char *key, + const char *filename, + char *path, + size_t pathlen, + void *user_data) +{ + struct mg_connection *conn = (struct mg_connection *)user_data; + + mg_printf(conn, "\r\n\r\n%s:\r\n", key); + + if (filename && *filename) { +#ifdef _WIN32 + _snprintf(path, pathlen, "D:\\tmp\\%s", filename); +#else + snprintf(path, pathlen, "/tmp/%s", filename); +#endif + return FORM_FIELD_STORAGE_STORE; + } + return FORM_FIELD_STORAGE_GET; +} + + +int +field_get(const char *key, const char *value, size_t valuelen, void *user_data) +{ + struct mg_connection *conn = (struct mg_connection *)user_data; + + if (key[0]) { + mg_printf(conn, "%s = ", key); + } + mg_write(conn, value, valuelen); + + return 0; +} + + +int +field_stored(const char *path, long long file_size, void *user_data) +{ + struct mg_connection *conn = (struct mg_connection *)user_data; + + mg_printf(conn, + "stored as %s (%lu bytes)\r\n\r\n", + path, + (unsigned long)file_size); + + return 0; +} + + +int +FormHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + int ret; + struct mg_form_data_handler fdh = {field_found, field_get, field_stored, 0}; + + /* It would be possible to check the request info here before calling + * mg_handle_form_request. */ + (void)req_info; + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/plain\r\nConnection: close\r\n\r\n"); + fdh.user_data = (void *)conn; + + /* Call the form handler */ + mg_printf(conn, "Form data:"); + ret = mg_handle_form_request(conn, &fdh); + mg_printf(conn, "\r\n%i fields found", ret); + + return 1; +} + + +int +FileUploadForm(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "<!DOCTYPE html>\n"); + mg_printf(conn, "<html>\n<head>\n"); + mg_printf(conn, "<meta charset=\"UTF-8\">\n"); + mg_printf(conn, "<title>File upload</title>\n"); + mg_printf(conn, "</head>\n<body>\n"); + mg_printf(conn, + "<form action=\"%s\" method=\"POST\" " + "enctype=\"multipart/form-data\">\n", + (const char *)cbdata); + mg_printf(conn, "<input type=\"file\" name=\"filesin\" multiple>\n"); + mg_printf(conn, "<input type=\"submit\" value=\"Submit\">\n"); + mg_printf(conn, "</form>\n</body>\n</html>\n"); + return 1; +} + +#define MD5_STATIC static +#include "../src/md5.inl" + +struct tfile_checksum { + char name[128]; + unsigned long long length; + md5_state_t chksum; +}; + +#define MAX_FILES (10) + +struct tfiles_checksums { + int index; + struct tfile_checksum file[MAX_FILES]; +}; + + +int +field_disp_read_on_the_fly(const char *key, + const char *filename, + char *path, + size_t pathlen, + void *user_data) +{ + struct tfiles_checksums *context = (struct tfiles_checksums *)user_data; + + (void)key; + (void)path; + (void)pathlen; + + if (context->index < MAX_FILES) { + context->index++; + strncpy(context->file[context->index - 1].name, filename, 128); + context->file[context->index - 1].name[127] = 0; + context->file[context->index - 1].length = 0; + md5_init(&(context->file[context->index - 1].chksum)); + return FORM_FIELD_STORAGE_GET; + } + return FORM_FIELD_STORAGE_ABORT; +} + + +int +field_get_checksum(const char *key, + const char *value, + size_t valuelen, + void *user_data) +{ + struct tfiles_checksums *context = (struct tfiles_checksums *)user_data; + (void)key; + + context->file[context->index - 1].length += valuelen; + md5_append(&(context->file[context->index - 1].chksum), + (const md5_byte_t *)value, + valuelen); + + return 0; +} + + +int +CheckSumHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + int i, j, ret; + struct tfiles_checksums chksums; + md5_byte_t digest[16]; + struct mg_form_data_handler fdh = {field_disp_read_on_the_fly, + field_get_checksum, + 0, + (void *)&chksums}; + + /* It would be possible to check the request info here before calling + * mg_handle_form_request. */ + (void)req_info; + + memset(&chksums, 0, sizeof(chksums)); + + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + + /* Call the form handler */ + mg_printf(conn, "File checksums:"); + ret = mg_handle_form_request(conn, &fdh); + for (i = 0; i < chksums.index; i++) { + md5_finish(&(chksums.file[i].chksum), digest); + /* Visual Studio 2010+ support llu */ + mg_printf(conn, + "\r\n%s %llu ", + chksums.file[i].name, + chksums.file[i].length); + for (j = 0; j < 16; j++) { + mg_printf(conn, "%02x", (unsigned int)digest[j]); + } + } + mg_printf(conn, "\r\n%i files\r\n", ret); + + return 1; +} + + +int +CookieHandler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + const char *cookie = mg_get_header(conn, "Cookie"); + char first_str[64], count_str[64]; + int count; + + (void)mg_get_cookie(cookie, "first", first_str, sizeof(first_str)); + (void)mg_get_cookie(cookie, "count", count_str, sizeof(count_str)); + + mg_printf(conn, "HTTP/1.1 200 OK\r\nConnection: close\r\n"); + if (first_str[0] == 0) { + time_t t = time(0); + struct tm *ptm = localtime(&t); + mg_printf(conn, + "Set-Cookie: first=%04i-%02i-%02iT%02i:%02i:%02i\r\n", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec); + } + count = (count_str[0] == 0) ? 0 : atoi(count_str); + mg_printf(conn, "Set-Cookie: count=%i\r\n", count + 1); + mg_printf(conn, "Content-Type: text/html\r\n\r\n"); + + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the CookieHandler.</h2>"); + mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri); + + if (first_str[0] == 0) { + mg_printf(conn, "<p>This is the first time, you opened this page</p>"); + } else { + mg_printf(conn, "<p>You opened this page %i times before.</p>", count); + mg_printf(conn, "<p>You first opened this page on %s.</p>", first_str); + } + + mg_printf(conn, "</body></html>\n"); + return 1; +} + + +int +PostResponser(struct mg_connection *conn, void *cbdata) +{ + long long r_total = 0; + int r, s; + + char buf[2048]; + + const struct mg_request_info *ri = mg_get_request_info(conn); + + if (strcmp(ri->request_method, "POST")) { + char buf[1024]; + int ret = mg_get_request_link(conn, buf, sizeof(buf)); + + mg_printf(conn, + "HTTP/1.1 405 Method Not Allowed\r\nConnection: close\r\n"); + mg_printf(conn, "Content-Type: text/plain\r\n\r\n"); + mg_printf(conn, + "%s method not allowed in the POST handler\n", + ri->request_method); + if (ret >= 0) { + mg_printf(conn, + "use a web tool to send a POST request to %s\n", + buf); + } + return 1; + } + + if (ri->content_length >= 0) { + /* We know the content length in advance */ + } else { + /* We must read until we find the end (chunked encoding + * or connection close), indicated my mg_read returning 0 */ + } + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nConnection: " + "close\r\nTransfer-Encoding: chunked\r\n"); + mg_printf(conn, "Content-Type: text/plain\r\n\r\n"); + + r = mg_read(conn, buf, sizeof(buf)); + while (r > 0) { + r_total += r; + s = mg_send_chunk(conn, buf, r); + if (r != s) { + /* Send error */ + break; + } + r = mg_read(conn, buf, sizeof(buf)); + } + mg_printf(conn, "0\r\n"); + + return 1; +} + + +int +AuthStartHandler(struct mg_connection *conn, void *cbdata) +{ + static unsigned long long firstload = 0; + const char *passfile = "password_example_file.txt"; + const char *realm = "password_example"; + const char *user = "user"; + char passwd[64]; + + if (firstload == 0) { + + /* Set a random password (4 digit number - bad idea from a security + * point of view, but this is an API demo, not a security tutorial), + * and store it in some directory within the document root (extremely + * bad idea, but this is still not a security tutorial). + * The reason we create a new password every time the server starts + * is just for demonstration - we don't want the browser to store the + * password, so when we repeat the test we start with a new password. + */ + firstload = (unsigned long long)time(NULL); + sprintf(passwd, "%04u", (unsigned int)(firstload % 10000)); + mg_modify_passwords_file(passfile, realm, user, passwd); + + /* Just tell the user the new password generated for this test. */ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "<!DOCTYPE html>\n"); + mg_printf(conn, "<html>\n<head>\n"); + mg_printf(conn, "<meta charset=\"UTF-8\">\n"); + mg_printf(conn, "<title>Auth handlerexample</title>\n"); + mg_printf(conn, "</head>\n"); + + mg_printf(conn, "<body>\n"); + mg_printf(conn, + "<p>The first time you visit this page, it's free!</p>\n"); + mg_printf(conn, + "<p>Next time, use username \"%s\" and password \"%s\"</p>\n", + user, + passwd); + mg_printf(conn, "</body>\n</html>\n"); + + return 1; + } + + if (mg_check_digest_access_authentication(conn, realm, passfile) <= 0) { + /* No valid authorization */ + mg_send_digest_access_authentication_request(conn, realm); + return 1; + } + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "<!DOCTYPE html>\n"); + mg_printf(conn, "<html>\n<head>\n"); + mg_printf(conn, "<meta charset=\"UTF-8\">\n"); + mg_printf(conn, "<title>Auth handlerexample</title>\n"); + mg_printf(conn, "</head>\n"); + + mg_printf(conn, "<body>\n"); + mg_printf(conn, "<p>This is the password protected contents</p>\n"); + mg_printf(conn, "</body>\n</html>\n"); + + return 1; +} + + +int +WebSocketStartHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "<!DOCTYPE html>\n"); + mg_printf(conn, "<html>\n<head>\n"); + mg_printf(conn, "<meta charset=\"UTF-8\">\n"); + mg_printf(conn, "<title>Embedded websocket example</title>\n"); + +#ifdef USE_WEBSOCKET + /* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ... + * xhtml style */ + mg_printf(conn, "<script>\n"); + mg_printf( + conn, + "function load() {\n" + " var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n" + " connection = new WebSocket(wsproto + '//' + window.location.host + " + "'/websocket');\n" + " websock_text_field = " + "document.getElementById('websock_text_field');\n" + " connection.onmessage = function (e) {\n" + " websock_text_field.innerHTML=e.data;\n" + " }\n" + " connection.onerror = function (error) {\n" + " alert('WebSocket error');\n" + " connection.close();\n" + " }\n" + "}\n"); + /* mg_printf(conn, "]]></script>\n"); ... xhtml style */ + mg_printf(conn, "</script>\n"); + mg_printf(conn, "</head>\n<body onload=\"load()\">\n"); + mg_printf( + conn, + "<div id='websock_text_field'>No websocket connection yet</div>\n"); +#else + mg_printf(conn, "</head>\n<body>\n"); + mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n"); +#endif + mg_printf(conn, "</body>\n</html>\n"); + + return 1; +} + + +#ifdef USE_WEBSOCKET + +/* MAX_WS_CLIENTS defines how many clients can connect to a websocket at the + * same time. The value 5 is very small and used here only for demonstration; + * it can be easily tested to connect more than MAX_WS_CLIENTS clients. + * A real server should use a much higher number, or better use a dynamic list + * of currently connected websocket clients. */ +#define MAX_WS_CLIENTS (5) + +struct t_ws_client { + struct mg_connection *conn; + int state; +} static ws_clients[MAX_WS_CLIENTS]; + + +#define ASSERT(x) \ + { \ + if (!(x)) { \ + fprintf(stderr, \ + "Assertion failed in line %u\n", \ + (unsigned)__LINE__); \ + } \ + } + + +int +WebSocketConnectHandler(const struct mg_connection *conn, void *cbdata) +{ + struct mg_context *ctx = mg_get_context(conn); + int reject = 1; + int i; + + mg_lock_context(ctx); + for (i = 0; i < MAX_WS_CLIENTS; i++) { + if (ws_clients[i].conn == NULL) { + ws_clients[i].conn = (struct mg_connection *)conn; + ws_clients[i].state = 1; + mg_set_user_connection_data(ws_clients[i].conn, + (void *)(ws_clients + i)); + reject = 0; + break; + } + } + mg_unlock_context(ctx); + + fprintf(stdout, + "Websocket client %s\r\n\r\n", + (reject ? "rejected" : "accepted")); + return reject; +} + + +void +WebSocketReadyHandler(struct mg_connection *conn, void *cbdata) +{ + const char *text = "Hello from the websocket ready handler"; + struct t_ws_client *client = mg_get_user_connection_data(conn); + + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text)); + fprintf(stdout, "Greeting message sent to websocket client\r\n\r\n"); + ASSERT(client->conn == conn); + ASSERT(client->state == 1); + + client->state = 2; +} + + +int +WebsocketDataHandler(struct mg_connection *conn, + int bits, + char *data, + size_t len, + void *cbdata) +{ + struct t_ws_client *client = mg_get_user_connection_data(conn); + ASSERT(client->conn == conn); + ASSERT(client->state >= 1); + + fprintf(stdout, "Websocket got %lu bytes of ", (unsigned long)len); + switch (((unsigned char)bits) & 0x0F) { + case WEBSOCKET_OPCODE_CONTINUATION: + fprintf(stdout, "continuation"); + break; + case WEBSOCKET_OPCODE_TEXT: + fprintf(stdout, "text"); + break; + case WEBSOCKET_OPCODE_BINARY: + fprintf(stdout, "binary"); + break; + case WEBSOCKET_OPCODE_CONNECTION_CLOSE: + fprintf(stdout, "close"); + break; + case WEBSOCKET_OPCODE_PING: + fprintf(stdout, "ping"); + break; + case WEBSOCKET_OPCODE_PONG: + fprintf(stdout, "pong"); + break; + default: + fprintf(stdout, "unknown(%1xh)", ((unsigned char)bits) & 0x0F); + break; + } + fprintf(stdout, " data:\r\n"); + fwrite(data, len, 1, stdout); + fprintf(stdout, "\r\n\r\n"); + + return 1; +} + + +void +WebSocketCloseHandler(const struct mg_connection *conn, void *cbdata) +{ + struct mg_context *ctx = mg_get_context(conn); + struct t_ws_client *client = mg_get_user_connection_data(conn); + ASSERT(client->conn == conn); + ASSERT(client->state >= 1); + + mg_lock_context(ctx); + client->state = 0; + client->conn = NULL; + mg_unlock_context(ctx); + + fprintf(stdout, + "Client droped from the set of webserver connections\r\n\r\n"); +} + + +void +InformWebsockets(struct mg_context *ctx) +{ + static unsigned long cnt = 0; + char text[32]; + int i; + + sprintf(text, "%lu", ++cnt); + + mg_lock_context(ctx); + for (i = 0; i < MAX_WS_CLIENTS; i++) { + if (ws_clients[i].state == 2) { + mg_websocket_write(ws_clients[i].conn, + WEBSOCKET_OPCODE_TEXT, + text, + strlen(text)); + } + } + mg_unlock_context(ctx); +} +#endif + + +#ifdef USE_SSL_DH +#include "openssl/ssl.h" +#include "openssl/dh.h" +#include "openssl/ec.h" +#include "openssl/evp.h" +#include "openssl/ecdsa.h" + +DH * +get_dh2236() +{ + static unsigned char dh2236_p[] = { + 0x0E, 0x97, 0x6E, 0x6A, 0x88, 0x84, 0xD2, 0xD7, 0x55, 0x6A, 0x17, 0xB7, + 0x81, 0x9A, 0x98, 0xBC, 0x7E, 0xD1, 0x6A, 0x44, 0xB1, 0x18, 0xE6, 0x25, + 0x3A, 0x62, 0x35, 0xF0, 0x41, 0x91, 0xE2, 0x16, 0x43, 0x9D, 0x8F, 0x7D, + 0x5D, 0xDA, 0x85, 0x47, 0x25, 0xC4, 0xBA, 0x68, 0x0A, 0x87, 0xDC, 0x2C, + 0x33, 0xF9, 0x75, 0x65, 0x17, 0xCB, 0x8B, 0x80, 0xFE, 0xE0, 0xA8, 0xAF, + 0xC7, 0x9E, 0x82, 0xBE, 0x6F, 0x1F, 0x00, 0x04, 0xBD, 0x69, 0x50, 0x8D, + 0x9C, 0x3C, 0x41, 0x69, 0x21, 0x4E, 0x86, 0xC8, 0x2B, 0xCC, 0x07, 0x4D, + 0xCF, 0xE4, 0xA2, 0x90, 0x8F, 0x66, 0xA9, 0xEF, 0xF7, 0xFC, 0x6F, 0x5F, + 0x06, 0x22, 0x00, 0xCB, 0xCB, 0xC3, 0x98, 0x3F, 0x06, 0xB9, 0xEC, 0x48, + 0x3B, 0x70, 0x6E, 0x94, 0xE9, 0x16, 0xE1, 0xB7, 0x63, 0x2E, 0xAB, 0xB2, + 0xF3, 0x84, 0xB5, 0x3D, 0xD7, 0x74, 0xF1, 0x6A, 0xD1, 0xEF, 0xE8, 0x04, + 0x18, 0x76, 0xD2, 0xD6, 0xB0, 0xB7, 0x71, 0xB6, 0x12, 0x8F, 0xD1, 0x33, + 0xAB, 0x49, 0xAB, 0x09, 0x97, 0x35, 0x9D, 0x4B, 0xBB, 0x54, 0x22, 0x6E, + 0x1A, 0x33, 0x18, 0x02, 0x8A, 0xF4, 0x7C, 0x0A, 0xCE, 0x89, 0x75, 0x2D, + 0x10, 0x68, 0x25, 0xA9, 0x6E, 0xCD, 0x97, 0x49, 0xED, 0xAE, 0xE6, 0xA7, + 0xB0, 0x07, 0x26, 0x25, 0x60, 0x15, 0x2B, 0x65, 0x88, 0x17, 0xF2, 0x5D, + 0x2C, 0xF6, 0x2A, 0x7A, 0x8C, 0xAD, 0xB6, 0x0A, 0xA2, 0x57, 0xB0, 0xC1, + 0x0E, 0x5C, 0xA8, 0xA1, 0x96, 0x58, 0x9A, 0x2B, 0xD4, 0xC0, 0x8A, 0xCF, + 0x91, 0x25, 0x94, 0xB4, 0x14, 0xA7, 0xE4, 0xE2, 0x1B, 0x64, 0x5F, 0xD2, + 0xCA, 0x70, 0x46, 0xD0, 0x2C, 0x95, 0x6B, 0x9A, 0xFB, 0x83, 0xF9, 0x76, + 0xE6, 0xD4, 0xA4, 0xA1, 0x2B, 0x2F, 0xF5, 0x1D, 0xE4, 0x06, 0xAF, 0x7D, + 0x22, 0xF3, 0x04, 0x30, 0x2E, 0x4C, 0x64, 0x12, 0x5B, 0xB0, 0x55, 0x3E, + 0xC0, 0x5E, 0x56, 0xCB, 0x99, 0xBC, 0xA8, 0xD9, 0x23, 0xF5, 0x57, 0x40, + 0xF0, 0x52, 0x85, 0x9B, + }; + static unsigned char dh2236_g[] = { + 0x02, + }; + DH *dh; + + if ((dh = DH_new()) == NULL) + return (NULL); + dh->p = BN_bin2bn(dh2236_p, sizeof(dh2236_p), NULL); + dh->g = BN_bin2bn(dh2236_g, sizeof(dh2236_g), NULL); + if ((dh->p == NULL) || (dh->g == NULL)) { + DH_free(dh); + return (NULL); + } + return (dh); +} +#endif + + +#ifndef NO_SSL +int +init_ssl(void *ssl_context, void *user_data) +{ + /* Add application specific SSL initialization */ + struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_context; + +#ifdef USE_SSL_DH + /* example from https://github.com/civetweb/civetweb/issues/347 */ + DH *dh = get_dh2236(); + if (!dh) + return -1; + if (1 != SSL_CTX_set_tmp_dh(ctx, dh)) + return -1; + DH_free(dh); + + EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!ecdh) + return -1; + if (1 != SSL_CTX_set_tmp_ecdh(ctx, ecdh)) + return -1; + EC_KEY_free(ecdh); + + printf("ECDH ciphers initialized\n"); +#endif + return 0; +} +#endif + + +int +log_message(const struct mg_connection *conn, const char *message) +{ + puts(message); + return 1; +} + + +int +main(int argc, char *argv[]) +{ + const char *options[] = { + "document_root", + DOCUMENT_ROOT, + "listening_ports", + PORT, + "request_timeout_ms", + "10000", + "error_log_file", + "error.log", +#ifdef USE_WEBSOCKET + "websocket_timeout_ms", + "3600000", +#endif +#ifndef NO_SSL + "ssl_certificate", + "../../resources/cert/server.pem", + "ssl_protocol_version", + "3", + "ssl_cipher_list", +#ifdef USE_SSL_DH + "ECDHE-RSA-AES256-GCM-SHA384:DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256", +#else + "DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256", +#endif +#endif + "enable_auth_domain_check", + "no", + 0}; + struct mg_callbacks callbacks; + struct mg_context *ctx; + struct mg_server_ports ports[32]; + int port_cnt, n; + int err = 0; + +/* Check if libcivetweb has been built with all required features. */ +#ifdef USE_IPV6 + if (!mg_check_feature(8)) { + fprintf(stderr, + "Error: Embedded example built with IPv6 support, " + "but civetweb library build without.\n"); + err = 1; + } +#endif +#ifdef USE_WEBSOCKET + if (!mg_check_feature(16)) { + fprintf(stderr, + "Error: Embedded example built with websocket support, " + "but civetweb library build without.\n"); + err = 1; + } +#endif +#ifndef NO_SSL + if (!mg_check_feature(2)) { + fprintf(stderr, + "Error: Embedded example built with SSL support, " + "but civetweb library build without.\n"); + err = 1; + } +#endif + if (err) { + fprintf(stderr, "Cannot start CivetWeb - inconsistent build.\n"); + return EXIT_FAILURE; + } + + /* Start CivetWeb web server */ + memset(&callbacks, 0, sizeof(callbacks)); +#ifndef NO_SSL + callbacks.init_ssl = init_ssl; +#endif + callbacks.log_message = log_message; + ctx = mg_start(&callbacks, 0, options); + + /* Check return value: */ + if (ctx == NULL) { + fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n"); + return EXIT_FAILURE; + } + + /* Add handler EXAMPLE_URI, to explain the example */ + mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0); + mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0); + + /* Add handler for /A* and special handler for /A/B */ + mg_set_request_handler(ctx, "/A", AHandler, 0); + mg_set_request_handler(ctx, "/A/B", ABHandler, 0); + + /* Add handler for /B, /B/A, /B/B but not for /B* */ + mg_set_request_handler(ctx, "/B$", BXHandler, (void *)0); + mg_set_request_handler(ctx, "/B/A$", BXHandler, (void *)1); + mg_set_request_handler(ctx, "/B/B$", BXHandler, (void *)2); + + /* Add handler for all files with .foo extention */ + mg_set_request_handler(ctx, "**.foo$", FooHandler, 0); + + /* Add handler for /close extention */ + mg_set_request_handler(ctx, "/close", CloseHandler, 0); + + /* Add handler for /form (serve a file outside the document root) */ + mg_set_request_handler(ctx, + "/form", + FileHandler, + (void *)"../../test/form.html"); + + /* Add handler for form data */ + mg_set_request_handler(ctx, + "/handle_form.embedded_c.example.callback", + FormHandler, + (void *)0); + + /* Add a file upload handler for parsing files on the fly */ + mg_set_request_handler(ctx, + "/on_the_fly_form", + FileUploadForm, + (void *)"/on_the_fly_form.md5.callback"); + mg_set_request_handler(ctx, + "/on_the_fly_form.md5.callback", + CheckSumHandler, + (void *)0); + + /* Add handler for /cookie example */ + mg_set_request_handler(ctx, "/cookie", CookieHandler, 0); + + /* Add handler for /postresponse example */ + mg_set_request_handler(ctx, "/postresponse", PostResponser, 0); + + /* Add HTTP site to open a websocket connection */ + mg_set_request_handler(ctx, "/websocket", WebSocketStartHandler, 0); + + /* Add HTTP site with auth */ + mg_set_request_handler(ctx, "/auth", AuthStartHandler, 0); + + +#ifdef USE_WEBSOCKET + /* WS site for the websocket connection */ + mg_set_websocket_handler(ctx, + "/websocket", + WebSocketConnectHandler, + WebSocketReadyHandler, + WebsocketDataHandler, + WebSocketCloseHandler, + 0); +#endif + + /* List all listening ports */ + memset(ports, 0, sizeof(ports)); + port_cnt = mg_get_server_ports(ctx, 32, ports); + printf("\n%i listening ports:\n\n", port_cnt); + + for (n = 0; n < port_cnt && n < 32; n++) { + const char *proto = ports[n].is_ssl ? "https" : "http"; + const char *host; + + if ((ports[n].protocol & 1) == 1) { + /* IPv4 */ + host = "127.0.0.1"; + printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port); + printf("Run example at %s://%s:%i%s\n", + proto, + host, + ports[n].port, + EXAMPLE_URI); + printf( + "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI); + printf("\n"); + } + + if ((ports[n].protocol & 2) == 2) { + /* IPv6 */ + host = "[::1]"; + printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port); + printf("Run example at %s://%s:%i%s\n", + proto, + host, + ports[n].port, + EXAMPLE_URI); + printf( + "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI); + printf("\n"); + } + } + + /* Wait until the server should be closed */ + while (!exitNow) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif +#ifdef USE_WEBSOCKET + InformWebsockets(ctx); +#endif + } + + /* Stop the server */ + mg_stop(ctx); + printf("Server stopped.\n"); + printf("Bye!\n"); + + return EXIT_SUCCESS; +} diff --git a/src/civetweb/examples/embedded_cpp/Makefile b/src/civetweb/examples/embedded_cpp/Makefile new file mode 100644 index 000000000..65adf98bb --- /dev/null +++ b/src/civetweb/examples/embedded_cpp/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# + +#This makefile is used to test the other Makefiles + + +PROG = embedded_cpp +SRC = embedded_cpp.cpp + +TOP = ../.. +CIVETWEB_LIB = libcivetweb.a + +CFLAGS = -I$(TOP)/include $(COPT) +LIBS = -lpthread + +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(SRC) + $(CXX) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) clean lib WITH_CPP=1 + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean diff --git a/src/civetweb/examples/embedded_cpp/embedded_cpp.cpp b/src/civetweb/examples/embedded_cpp/embedded_cpp.cpp new file mode 100644 index 000000000..d45a573cf --- /dev/null +++ b/src/civetweb/examples/embedded_cpp/embedded_cpp.cpp @@ -0,0 +1,432 @@ +/* Copyright (c) 2013-2017 the Civetweb developers + * Copyright (c) 2013 No Face Press, LLC + * License http://opensource.org/licenses/mit-license.php MIT License + */ + +// Simple example program on how to use Embedded C++ interface. + +#include "CivetServer.h" +#include <cstring> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <unistd.h> +#endif + +#define DOCUMENT_ROOT "." +#define PORT "8081" +#define EXAMPLE_URI "/example" +#define EXIT_URI "/exit" +bool exitNow = false; + +class ExampleHandler : public CivetHandler +{ + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "<html><body>\r\n"); + mg_printf(conn, + "<h2>This is an example text from a C++ handler</h2>\r\n"); + mg_printf(conn, + "<p>To see a page from the A handler <a " + "href=\"a\">click here</a></p>\r\n"); + mg_printf(conn, + "<p>To see a page from the A handler with a parameter " + "<a href=\"a?param=1\">click here</a></p>\r\n"); + mg_printf(conn, + "<p>To see a page from the A/B handler <a " + "href=\"a/b\">click here</a></p>\r\n"); + mg_printf(conn, + "<p>To see a page from the *.foo handler <a " + "href=\"xy.foo\">click here</a></p>\r\n"); + mg_printf(conn, + "<p>To see a page from the WebSocket handler <a " + "href=\"ws\">click here</a></p>\r\n"); + mg_printf(conn, + "<p>To exit <a href=\"%s\">click here</a></p>\r\n", + EXIT_URI); + mg_printf(conn, "</body></html>\r\n"); + return true; + } +}; + +class ExitHandler : public CivetHandler +{ + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/plain\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "Bye!\n"); + exitNow = true; + return true; + } +}; + +class AHandler : public CivetHandler +{ + private: + bool + handleAll(const char *method, + CivetServer *server, + struct mg_connection *conn) + { + std::string s = ""; + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the A handler for \"%s\" !</h2>", method); + if (CivetServer::getParam(conn, "param", s)) { + mg_printf(conn, "<p>param set to %s</p>", s.c_str()); + } else { + mg_printf(conn, "<p>param not set</p>"); + } + mg_printf(conn, "</body></html>\n"); + return true; + } + + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + return handleAll("GET", server, conn); + } + bool + handlePost(CivetServer *server, struct mg_connection *conn) + { + return handleAll("POST", server, conn); + } +}; + +class ABHandler : public CivetHandler +{ + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "<html><body>"); + mg_printf(conn, "<h2>This is the AB handler!!!</h2>"); + mg_printf(conn, "</body></html>\n"); + return true; + } +}; + +class FooHandler : public CivetHandler +{ + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"); + + mg_printf(conn, "<html><body>\n"); + mg_printf(conn, "<h2>This is the Foo GET handler!!!</h2>\n"); + mg_printf(conn, + "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>\n", + req_info->request_method, + req_info->request_uri, + req_info->http_version); + mg_printf(conn, "</body></html>\n"); + + return true; + } + bool + handlePost(CivetServer *server, struct mg_connection *conn) + { + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + long long rlen, wlen; + long long nlen = 0; + long long tlen = req_info->content_length; + char buf[1024]; + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"); + + mg_printf(conn, "<html><body>\n"); + mg_printf(conn, "<h2>This is the Foo POST handler!!!</h2>\n"); + mg_printf(conn, + "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>\n", + req_info->request_method, + req_info->request_uri, + req_info->http_version); + mg_printf(conn, "<p>Content Length: %li</p>\n", (long)tlen); + mg_printf(conn, "<pre>\n"); + + while (nlen < tlen) { + rlen = tlen - nlen; + if (rlen > sizeof(buf)) { + rlen = sizeof(buf); + } + rlen = mg_read(conn, buf, (size_t)rlen); + if (rlen <= 0) { + break; + } + wlen = mg_write(conn, buf, (size_t)rlen); + if (wlen != rlen) { + break; + } + nlen += wlen; + } + + mg_printf(conn, "\n</pre>\n"); + mg_printf(conn, "</body></html>\n"); + + return true; + } + + #define fopen_recursive fopen + + bool + handlePut(CivetServer *server, struct mg_connection *conn) + { + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + long long rlen, wlen; + long long nlen = 0; + long long tlen = req_info->content_length; + FILE * f; + char buf[1024]; + int fail = 0; + +#ifdef _WIN32 + _snprintf(buf, sizeof(buf), "D:\\somewhere\\%s\\%s", req_info->remote_user, req_info->local_uri); + buf[sizeof(buf)-1] = 0; + if (strlen(buf)>255) { + /* Windows will not work with path > 260 (MAX_PATH), unless we use + * the unicode API. However, this is just an example code: A real + * code will probably never store anything to D:\\somewhere and + * must be adapted to the specific needs anyhow. */ + fail = 1; + f = NULL; + } else { + f = fopen_recursive(buf, "wb"); + } +#else + snprintf(buf, sizeof(buf), "~/somewhere/%s/%s", req_info->remote_user, req_info->local_uri); + buf[sizeof(buf)-1] = 0; + if (strlen(buf)>1020) { + /* The string is too long and probably truncated. Make sure an + * UTF-8 string is never truncated between the UTF-8 code bytes. + * This example code must be adapted to the specific needs. */ + fail = 1; + f = NULL; + } else { + f = fopen_recursive(buf, "w"); + } +#endif + + if (!f) { + fail = 1; + } else { + while (nlen < tlen) { + rlen = tlen - nlen; + if (rlen > sizeof(buf)) { + rlen = sizeof(buf); + } + rlen = mg_read(conn, buf, (size_t)rlen); + if (rlen <= 0) { + fail = 1; + break; + } + wlen = fwrite(buf, 1, (size_t)rlen, f); + if (wlen != rlen) { + fail = 1; + break; + } + nlen += wlen; + } + fclose(f); + } + + if (fail) { + mg_printf(conn, + "HTTP/1.1 409 Conflict\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + } else { + mg_printf(conn, + "HTTP/1.1 201 Created\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + } + + return true; + } +}; + +class WsStartHandler : public CivetHandler +{ + public: + bool + handleGet(CivetServer *server, struct mg_connection *conn) + { + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "<!DOCTYPE html>\n"); + mg_printf(conn, "<html>\n<head>\n"); + mg_printf(conn, "<meta charset=\"UTF-8\">\n"); + mg_printf(conn, "<title>Embedded websocket example</title>\n"); + +#ifdef USE_WEBSOCKET + /* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ... + * xhtml style */ + mg_printf(conn, "<script>\n"); + mg_printf( + conn, + "var i=0\n" + "function load() {\n" + " var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n" + " connection = new WebSocket(wsproto + '//' + window.location.host + " + "'/websocket');\n" + " websock_text_field = " + "document.getElementById('websock_text_field');\n" + " connection.onmessage = function (e) {\n" + " websock_text_field.innerHTML=e.data;\n" + " i=i+1;" + " connection.send(i);\n" + " }\n" + " connection.onerror = function (error) {\n" + " alert('WebSocket error');\n" + " connection.close();\n" + " }\n" + "}\n"); + /* mg_printf(conn, "]]></script>\n"); ... xhtml style */ + mg_printf(conn, "</script>\n"); + mg_printf(conn, "</head>\n<body onload=\"load()\">\n"); + mg_printf( + conn, + "<div id='websock_text_field'>No websocket connection yet</div>\n"); +#else + mg_printf(conn, "</head>\n<body>\n"); + mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n"); +#endif + mg_printf(conn, "</body>\n</html>\n"); + + return 1; +} +}; + + +#ifdef USE_WEBSOCKET +class WebSocketHandler : public CivetWebSocketHandler { + + virtual bool handleConnection(CivetServer *server, + const struct mg_connection *conn) { + printf("WS connected\n"); + return true; + } + + virtual void handleReadyState(CivetServer *server, + struct mg_connection *conn) { + printf("WS ready\n"); + + const char *text = "Hello from the websocket ready handler"; + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text)); + } + + virtual bool handleData(CivetServer *server, + struct mg_connection *conn, + int bits, + char *data, + size_t data_len) { + printf("WS got %lu bytes: ", (long unsigned)data_len); + fwrite(data, 1, data_len, stdout); + printf("\n"); + + mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len); + return (data_len<4); + } + + virtual void handleClose(CivetServer *server, + const struct mg_connection *conn) { + printf("WS closed\n"); + } +}; +#endif + + +int +main(int argc, char *argv[]) +{ + const char *options[] = { + "document_root", DOCUMENT_ROOT, "listening_ports", PORT, 0}; + + std::vector<std::string> cpp_options; + for (int i=0; i<(sizeof(options)/sizeof(options[0])-1); i++) { + cpp_options.push_back(options[i]); + } + + // CivetServer server(options); // <-- C style start + CivetServer server(cpp_options); // <-- C++ style start + + ExampleHandler h_ex; + server.addHandler(EXAMPLE_URI, h_ex); + + ExitHandler h_exit; + server.addHandler(EXIT_URI, h_exit); + + AHandler h_a; + server.addHandler("/a", h_a); + + ABHandler h_ab; + server.addHandler("/a/b", h_ab); + + WsStartHandler h_ws; + server.addHandler("/ws", h_ws); + +#ifdef NO_FILES + /* This handler will handle "everything else", including + * requests to files. If this handler is installed, + * NO_FILES should be set. */ + FooHandler h_foo; + server.addHandler("", h_foo); + + printf("See a page from the \"all\" handler at http://localhost:%s/\n", PORT); +#else + FooHandler h_foo; + server.addHandler("**.foo", h_foo); + printf("Browse files at http://localhost:%s/\n", PORT); +#endif + +#ifdef USE_WEBSOCKET + WebSocketHandler h_websocket; + server.addWebSocketHandler("/websocket", h_websocket); + printf("Run websocket example at http://localhost:%s/ws\n", PORT); +#endif + + printf("Run example at http://localhost:%s%s\n", PORT, EXAMPLE_URI); + printf("Exit at http://localhost:%s%s\n", PORT, EXIT_URI); + + while (!exitNow) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + + printf("Bye!\n"); + + return 0; +} diff --git a/src/civetweb/examples/https/README.md b/src/civetweb/examples/https/README.md new file mode 100644 index 000000000..7b18b4724 --- /dev/null +++ b/src/civetweb/examples/https/README.md @@ -0,0 +1,15 @@ +HTTPS Server configuration example +==== + +This directory contains an example [`civetweb.conf`](civetweb.conf) configuration file for a secure HTTPS server. You can run a HTTPS server without most of the options there - only `ssl_certificate` and one port (e.g., `443s`) in `listening_ports` is required. The default settings will work, but not comply with up to date security standards. It is somewhat debatable what "up to date security" means - you can use the following web sites to run tests: + +- https://securityheaders.io +- https://www.htbridge.com/ssl +- https://www.htbridge.com/websec +- https://www.ssllabs.com/ssltest/analyze.html / https://www.qualys.com/forms/freescan/ +- probably there are some more ... let me know! + +Instructions to run the test and to adapt the configuration can be found [`civetweb.conf`](civetweb.conf). You can test this configuration directly with the standalone server, or you can take the settings and add it into your embedding code. + +Note: I do not take any warranty or liability for this configuration, or for the content of any linked web site. + diff --git a/src/civetweb/examples/https/civetweb.conf b/src/civetweb/examples/https/civetweb.conf new file mode 100644 index 000000000..cd10eddab --- /dev/null +++ b/src/civetweb/examples/https/civetweb.conf @@ -0,0 +1,86 @@ +# Instructions to run (on Linux) to reproduce test results: +# +# 1) copy civetweb executable here (examples/https directory) +# 2) sudo ./civetweb +# +# Instructions to adapt to your own server: +# +# 1) generate your own server cert +# 2) generate at least one backup server cert +# in case you want a self signed cert, you can use the script +# in resources/cert for both steps +# 3) copy the content of the *.pin files into the Public-Key-Pins +# header config (the base64 encoded certificate hash) +# 4) set the document root, and all other required http server settings +# 5) Run the tests from the three websites below. They will tell you +# also what clients are compatible with your settings. The settings +# here are very strict and lock out most older clients/browsers. +# You will find some hints for fine tuning there as well. +# 6) If you know all your clients, and give them client certificates in +# advance, you can significantly improve security by setting +# "ssl_verify_peer" to "yes" and specifying a client cert (directory) +# using "ssl_ca_file/path". This will lock out all clients without a +# proper certificate. Don't use it for your public home page, but +# consider it for your private remote access server. +# 7) run civetweb, like above - or better create your own start script +# You are welcome to share your thoughts and experience on GitHub +# (or Google groups) - see README.md in CivetWeb main directory + +# Don't run as super user, switch back to a regular user +run_as_user user + +# The standard HTTP port 80 should redirect to the standard HTTPS port 443 +listening_ports 80r,443s + +# Don't forget to set the document root and domain +#document_root tdb +#authentication_domain mydomain.com + +# Set the a certificate +ssl_certificate ../../resources/cert/server.pem + +# Require a client cert for your private server (see above) +#ssl_verify_peer yes +#ssl_ca_file ../../resources/cert/client.pem + +# Enforce TLS1.2 and some strong cipher(s) +ssl_protocol_version 4 +ssl_cipher_list ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS + +# Tell all browsers to access this site only as HTTPS for the next 180 days +strict_transport_security_max_age 15552000 + +# Set some HTTP security header, see https://securityheaders.io +additional_header Content-Security-Policy: script-src 'self' +additional_header X-Frame-Options: SAMEORIGIN +additional_header X-Xss-Protection: 1; mode=block +additional_header X-Content-Type-Options: nosniff +additional_header Referrer-Policy: same-origin +additional_header Public-Key-Pins: pin-sha256="uz1UTAPen+xb+UoQqkVlEx4H653LbMjfRJcZx5OrjbI="; pin-sha256="pf3px1MBPmlTGAPoiHWqaSJ9L9Z+DKfwgsU7LfLnmsk="; max-age=7776000 +#additional_header Expect-CT: max-age=86400,report-uri="https://mydomain.com/report" + + +# Ratings from 2017-09-03 (tests performed later may require more +# strict security settings) +# +# Headers rated A+ from https://securityheaders.io/ +# +# SSL rated B from https://www.htbridge.com/ssl when using a self signed +# certificate, but no other weaknesses for modern browsers. +# Site remarks some older TLS versions and some weaker ciphers are not +# supported (but that's accessibility, not security). +# +# HTTPS rated A+ from https://www.htbridge.com/websec/ when using a self +# signed certificate, generated with make_certs.sh in resources/cert/ +# and adding the server.pin and server_bkup.pin content into the +# Public-Key-Pins header above. +# +# A rating of "T / If trust issues are ignored: A" (ignoring self-signed cert) +# from https://www.ssllabs.com/ssltest/, https://www.qualys.com/forms/freescan/ +# (Note: this test is runs with reverse DNS name, while all others use the +# IP address). +# +# Note: This settings are very strict and prevent some older but still common +# versions of major browsers to access this site. The test web sites will give +# you an overview. Test, before you use this settings. + |