webgui: mod_netconf: --with-notifications
using libwebsockets
diff --git a/src/Makefile.in b/src/Makefile.in
index 3c43b0a..bbc80db 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -24,7 +24,8 @@
APXS=/usr/sbin/apxs
CFLAGS=-I/usr/include/json
-LIBS=-lnetconf -ljson
+CPPFLAGS=@CPPFLAGS@
+LIBS=@LIBS@
SRCS=mod_netconf.c \
test-client.c
@@ -34,11 +35,14 @@
all: module client
module: mod_netconf.c
- $(APXS) $(LIBS) $(ARCSID) -c mod_netconf.c
+ $(APXS) $(LIBS) $(ARCSID) $(CPPFLAGS) -c mod_netconf.c notification-server.c
client: test-client.c
gcc $(RCSID) -std=gnu99 -Wall -g -ljson -I/usr/include/json test-client.c -o test-client
+notification-test: notification-server.c
+ gcc $(RCSID) -std=gnu99 -Wall -g -lwebsockets -DTEST_NOTIFICATION_SERVER notification-server.c -o notification-test
+
#install: $(NAME).la
# $(APXS) -i $(NAME).la
diff --git a/src/configure b/src/configure
index 5229695..5bce59a 100755
--- a/src/configure
+++ b/src/configure
@@ -696,6 +696,7 @@
ac_subst_files=''
ac_user_opts='
enable_option_checking
+with_notifications
'
ac_precious_vars='build_alias
host_alias
@@ -1323,6 +1324,11 @@
esac
cat <<\_ACEOF
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-notifications Enable notifications
+
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
@@ -1950,6 +1956,7 @@
RELEASE=1
+LIBS="-lnetconf -ljson"
if test "`git show > /dev/null 2>&1;echo $?`" = "0"; then
RCSID="-DRCSID=\\\"\$(IDGIT)\\\""
@@ -2990,6 +2997,7 @@
REQS="$REQS libnetconf"
BUILDREQS="$BUILDREQS libnetconf-devel"
+CPPFLAGS=""
# Checks for header files.
ac_ext=c
@@ -3763,6 +3771,21 @@
+# Check whether --with-notifications was given.
+if test "${with_notifications+set}" = set; then
+ withval=$with_notifications;
+ CPPFLAGS=" -DWITH_NOTIFICATIONS "
+ LIBS="-lwebsockets $LIBS"
+
+else
+ CPPFLAGS="$CPPFLAGS"
+
+fi
+
+
+
+
+
diff --git a/src/configure.in b/src/configure.in
index 2d122af..43e7ca2 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -14,6 +14,7 @@
RELEASE=1
AC_SUBST(RELEASE)
+LIBS="-lnetconf -ljson"
if test "`git show > /dev/null 2>&1;echo $?`" = "0"; then
RCSID="-DRCSID=\\\"\$(IDGIT)\\\""
@@ -30,6 +31,7 @@
AC_SEARCH_LIBS([ncds_apply_rpc],[netconf], ,AC_MSG_ERROR([libnetconf not found!]))
REQS="$REQS libnetconf"
BUILDREQS="$BUILDREQS libnetconf-devel"
+CPPFLAGS=""
# Checks for header files.
AC_CHECK_HEADER([libnetconf.h], ,AC_MSG_ERROR([libnetconf.h not found!]))
@@ -43,11 +45,22 @@
AC_MSG_WARN([Due to missing rpmbuild you will not able to generate RPM package.])
fi
+
+AC_ARG_WITH([notifications],
+ [AC_HELP_STRING([--with-notifications], [Enable notifications])],
+ [
+ CPPFLAGS=" -DWITH_NOTIFICATIONS "
+ LIBS="-lwebsockets $LIBS"
+ ],
+ [CPPFLAGS="$CPPFLAGS"]
+)
+
AC_SUBST(REQS)
AC_SUBST(BUILDREQS)
AC_SUBST(CPPFLAGS)
AC_SUBST(RCSID)
AC_SUBST(ARCSID)
+AC_SUBST(LIBS)
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([mod_netconf.spec])
diff --git a/src/libwebsockets.h b/src/libwebsockets.h
new file mode 100644
index 0000000..b7eaa41
--- /dev/null
+++ b/src/libwebsockets.h
@@ -0,0 +1,982 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef __LIBWEBSOCKET_H__
+#define __LIBWEBSOCKET_H__
+
+#ifdef __cplusplus
+extern "C" {
+#include <cstddef>
+#endif
+
+#ifdef WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <stddef.h>
+#include "../win32port/win32helpers/websock-w32.h"
+
+#include "../win32port/win32helpers/gettimeofday.h"
+
+#define strcasecmp stricmp
+#define getdtablesize() 30000
+
+typedef int ssize_t;
+
+#ifdef LWS_DLL
+#ifdef LWS_INTERNAL
+#define LWS_EXTERN extern __declspec(dllexport)
+#else
+#define LWS_EXTERN extern __declspec(dllimport)
+#endif
+#endif
+
+#else // NOT WIN32
+#include <poll.h>
+#include <unistd.h>
+#endif
+
+#include <assert.h>
+
+#ifndef LWS_EXTERN
+#define LWS_EXTERN extern
+#endif
+
+#define CONTEXT_PORT_NO_LISTEN 0
+#define MAX_MUX_RECURSION 2
+
+enum lws_log_levels {
+ LLL_ERR = 1 << 0,
+ LLL_WARN = 1 << 1,
+ LLL_NOTICE = 1 << 2,
+ LLL_INFO = 1 << 3,
+ LLL_DEBUG = 1 << 4,
+ LLL_PARSER = 1 << 5,
+ LLL_HEADER = 1 << 6,
+ LLL_EXT = 1 << 7,
+ LLL_CLIENT = 1 << 8,
+ LLL_LATENCY = 1 << 9,
+
+ LLL_COUNT = 10 /* set to count of valid flags */
+};
+
+LWS_EXTERN void _lws_log(int filter, const char *format, ...);
+
+/* notice, warn and log are always compiled in */
+#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__)
+#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__)
+#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__)
+/*
+ * weaker logging can be deselected at configure time using --disable-debug
+ * that gets rid of the overhead of checking while keeping _warn and _err
+ * active
+ */
+#ifdef _DEBUG
+
+#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__)
+#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__)
+#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__)
+#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__)
+#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__)
+#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__)
+#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__)
+LWS_EXTERN void lwsl_hexdump(void *buf, size_t len);
+
+#else /* no debug */
+
+#define lwsl_info(...)
+#define lwsl_debug(...)
+#define lwsl_parser(...)
+#define lwsl_header(...)
+#define lwsl_ext(...)
+#define lwsl_client(...)
+#define lwsl_latency(...)
+#define lwsl_hexdump(a, b)
+
+#endif
+
+enum libwebsocket_context_options {
+ LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2,
+ LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4,
+};
+
+enum libwebsocket_callback_reasons {
+ LWS_CALLBACK_ESTABLISHED,
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+ LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
+ LWS_CALLBACK_CLOSED,
+ LWS_CALLBACK_RECEIVE,
+ LWS_CALLBACK_CLIENT_RECEIVE,
+ LWS_CALLBACK_CLIENT_RECEIVE_PONG,
+ LWS_CALLBACK_CLIENT_WRITEABLE,
+ LWS_CALLBACK_SERVER_WRITEABLE,
+ LWS_CALLBACK_HTTP,
+ LWS_CALLBACK_HTTP_FILE_COMPLETION,
+ LWS_CALLBACK_HTTP_WRITEABLE,
+ LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
+ LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
+ LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
+ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+ LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
+ LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
+ LWS_CALLBACK_PROTOCOL_INIT,
+ LWS_CALLBACK_PROTOCOL_DESTROY,
+ /* external poll() management support */
+ LWS_CALLBACK_ADD_POLL_FD,
+ LWS_CALLBACK_DEL_POLL_FD,
+ LWS_CALLBACK_SET_MODE_POLL_FD,
+ LWS_CALLBACK_CLEAR_MODE_POLL_FD,
+};
+
+#ifndef LWS_NO_EXTENSIONS
+enum libwebsocket_extension_callback_reasons {
+ LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT,
+ LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT,
+ LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT,
+ LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT,
+ LWS_EXT_CALLBACK_CONSTRUCT,
+ LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
+ LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE,
+ LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
+ LWS_EXT_CALLBACK_DESTROY,
+ LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
+ LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED,
+ LWS_EXT_CALLBACK_PACKET_RX_PREPARSE,
+ LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
+ LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
+ LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
+ LWS_EXT_CALLBACK_FLUSH_PENDING_TX,
+ LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
+ LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION,
+ LWS_EXT_CALLBACK_1HZ,
+ LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE,
+ LWS_EXT_CALLBACK_IS_WRITEABLE,
+ LWS_EXT_CALLBACK_PAYLOAD_TX,
+ LWS_EXT_CALLBACK_PAYLOAD_RX,
+};
+#endif
+
+enum libwebsocket_write_protocol {
+ LWS_WRITE_TEXT,
+ LWS_WRITE_BINARY,
+ LWS_WRITE_CONTINUATION,
+ LWS_WRITE_HTTP,
+
+ /* special 04+ opcodes */
+
+ LWS_WRITE_CLOSE,
+ LWS_WRITE_PING,
+ LWS_WRITE_PONG,
+
+ /* flags */
+
+ LWS_WRITE_NO_FIN = 0x40,
+ /*
+ * client packet payload goes out on wire unmunged
+ * only useful for security tests since normal servers cannot
+ * decode the content if used
+ */
+ LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80
+};
+
+/*
+ * you need these to look at headers that have been parsed if using the
+ * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum
+ * list below is absent, .token = NULL and token_len = 0. Otherwise .token
+ * points to .token_len chars containing that header content.
+ */
+
+struct lws_tokens {
+ char *token;
+ int token_len;
+};
+
+enum lws_token_indexes {
+ WSI_TOKEN_GET_URI,
+ WSI_TOKEN_HOST,
+ WSI_TOKEN_CONNECTION,
+ WSI_TOKEN_KEY1,
+ WSI_TOKEN_KEY2,
+ WSI_TOKEN_PROTOCOL,
+ WSI_TOKEN_UPGRADE,
+ WSI_TOKEN_ORIGIN,
+ WSI_TOKEN_DRAFT,
+ WSI_TOKEN_CHALLENGE,
+
+ /* new for 04 */
+ WSI_TOKEN_KEY,
+ WSI_TOKEN_VERSION,
+ WSI_TOKEN_SWORIGIN,
+
+ /* new for 05 */
+ WSI_TOKEN_EXTENSIONS,
+
+ /* client receives these */
+ WSI_TOKEN_ACCEPT,
+ WSI_TOKEN_NONCE,
+ WSI_TOKEN_HTTP,
+ WSI_TOKEN_MUXURL,
+
+ /* use token storage to stash these */
+
+ _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+ _WSI_TOKEN_CLIENT_URI,
+ _WSI_TOKEN_CLIENT_HOST,
+ _WSI_TOKEN_CLIENT_ORIGIN,
+
+ /* always last real token index*/
+ WSI_TOKEN_COUNT,
+ /* parser state additions */
+ WSI_TOKEN_NAME_PART,
+ WSI_TOKEN_SKIPPING,
+ WSI_TOKEN_SKIPPING_SAW_CR,
+ WSI_PARSING_COMPLETE,
+ WSI_INIT_TOKEN_MUXURL,
+};
+
+/*
+ * From RFC 6455
+ 1000
+
+ 1000 indicates a normal closure, meaning that the purpose for
+ which the connection was established has been fulfilled.
+
+ 1001
+
+ 1001 indicates that an endpoint is "going away", such as a server
+ going down or a browser having navigated away from a page.
+
+ 1002
+
+ 1002 indicates that an endpoint is terminating the connection due
+ to a protocol error.
+
+ 1003
+
+ 1003 indicates that an endpoint is terminating the connection
+ because it has received a type of data it cannot accept (e.g., an
+ endpoint that understands only text data MAY send this if it
+ receives a binary message).
+
+ 1004
+
+ Reserved. The specific meaning might be defined in the future.
+
+ 1005
+
+ 1005 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that no status
+ code was actually present.
+
+ 1006
+
+ 1006 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that the
+ connection was closed abnormally, e.g., without sending or
+ receiving a Close control frame.
+
+ 1007
+
+ 1007 indicates that an endpoint is terminating the connection
+ because it has received data within a message that was not
+ consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
+ data within a text message).
+
+ 1008
+
+ 1008 indicates that an endpoint is terminating the connection
+ because it has received a message that violates its policy. This
+ is a generic status code that can be returned when there is no
+ other more suitable status code (e.g., 1003 or 1009) or if there
+ is a need to hide specific details about the policy.
+
+ 1009
+
+ 1009 indicates that an endpoint is terminating the connection
+ because it has received a message that is too big for it to
+ process.
+
+ 1010
+
+ 1010 indicates that an endpoint (client) is terminating the
+ connection because it has expected the server to negotiate one or
+ more extension, but the server didn't return them in the response
+ message of the WebSocket handshake. The list of extensions that
+ are needed SHOULD appear in the /reason/ part of the Close frame.
+ Note that this status code is not used by the server, because it
+ can fail the WebSocket handshake instead.
+
+ 1011
+
+ 1011 indicates that a server is terminating the connection because
+ it encountered an unexpected condition that prevented it from
+ fulfilling the request.
+
+ 1015
+
+ 1015 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that the
+ connection was closed due to a failure to perform a TLS handshake
+ (e.g., the server certificate can't be verified).
+*/
+
+enum lws_close_status {
+ LWS_CLOSE_STATUS_NOSTATUS = 0,
+ LWS_CLOSE_STATUS_NORMAL = 1000,
+ LWS_CLOSE_STATUS_GOINGAWAY = 1001,
+ LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002,
+ LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003,
+ LWS_CLOSE_STATUS_RESERVED = 1004,
+ LWS_CLOSE_STATUS_NO_STATUS = 1005,
+ LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006,
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007,
+ LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008,
+ LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009,
+ LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010,
+ LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011,
+ LWS_CLOSE_STATUS_TLS_FAILURE = 1015,
+};
+
+struct libwebsocket;
+struct libwebsocket_context;
+/* needed even with extensions disabled for create context */
+struct libwebsocket_extension;
+
+/**
+ * callback_function() - User server actions
+ * @context: Websockets context
+ * @wsi: Opaque websocket instance pointer
+ * @reason: The reason for the call
+ * @user: Pointer to per-session user data allocated by library
+ * @in: Pointer used for some callback reasons
+ * @len: Length set for some callback reasons
+ *
+ * This callback is the way the user controls what is served. All the
+ * protocol detail is hidden and handled by the library.
+ *
+ * For each connection / session there is user data allocated that is
+ * pointed to by "user". You set the size of this user data area when
+ * the library is initialized with libwebsocket_create_server.
+ *
+ * You get an opportunity to initialize user data when called back with
+ * LWS_CALLBACK_ESTABLISHED reason.
+ *
+ * LWS_CALLBACK_ESTABLISHED: after the server completes a handshake with
+ * an incoming client
+ *
+ * LWS_CALLBACK_CLIENT_CONNECTION_ERROR: the request client connection has
+ * been unable to complete a handshake with the remote server
+ *
+ * LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH: this is the last chance for the
+ * client user code to examine the http headers
+ * and decide to reject the connection. If the
+ * content in the headers is interesting to the
+ * client (url, etc) it needs to copy it out at
+ * this point since it will be destroyed before
+ * the CLIENT_ESTABLISHED call
+ *
+ * LWS_CALLBACK_CLIENT_ESTABLISHED: after your client connection completed
+ * a handshake with the remote server
+ *
+ * LWS_CALLBACK_CLOSED: when the websocket session ends
+ *
+ * LWS_CALLBACK_RECEIVE: data has appeared for this server endpoint from a
+ * remote client, it can be found at *in and is
+ * len bytes long
+ *
+ * LWS_CALLBACK_CLIENT_RECEIVE_PONG: if you elected to see PONG packets,
+ * they appear with this callback reason. PONG
+ * packets only exist in 04+ protocol
+ *
+ * LWS_CALLBACK_CLIENT_RECEIVE: data has appeared from the server for the
+ * client connection, it can be found at *in and
+ * is len bytes long
+ *
+ * LWS_CALLBACK_HTTP: an http request has come from a client that is not
+ * asking to upgrade the connection to a websocket
+ * one. This is a chance to serve http content,
+ * for example, to send a script to the client
+ * which will then open the websockets connection.
+ * @in points to the URI path requested and
+ * libwebsockets_serve_http_file() makes it very
+ * simple to send back a file to the client.
+ * Normally after sending the file you are done
+ * with the http connection, since the rest of the
+ * activity will come by websockets from the script
+ * that was delivered by http, so you will want to
+ * return 1; to close and free up the connection.
+ * That's important because it uses a slot in the
+ * total number of client connections allowed set
+ * by MAX_CLIENTS.
+ *
+ * LWS_CALLBACK_HTTP_WRITEABLE: you can write more down the http protocol
+ * link now.
+ *
+ * LWS_CALLBACK_HTTP_FILE_COMPLETION: a file requested to be send down
+ * http link has completed.
+ *
+ * LWS_CALLBACK_CLIENT_WRITEABLE:
+ * LWS_CALLBACK_SERVER_WRITEABLE: If you call
+ * libwebsocket_callback_on_writable() on a connection, you will
+ * get one of these callbacks coming when the connection socket
+ * is able to accept another write packet without blocking.
+ * If it already was able to take another packet without blocking,
+ * you'll get this callback at the next call to the service loop
+ * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
+ * and servers get LWS_CALLBACK_SERVER_WRITEABLE.
+ *
+ * LWS_CALLBACK_FILTER_NETWORK_CONNECTION: called when a client connects to
+ * the server at network level; the connection is accepted but then
+ * passed to this callback to decide whether to hang up immediately
+ * or not, based on the client IP. @in contains the connection
+ * socket's descriptor. Return non-zero to terminate
+ * the connection before sending or receiving anything.
+ * Because this happens immediately after the network connection
+ * from the client, there's no websocket protocol selected yet so
+ * this callback is issued only to protocol 0.
+ *
+ * LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: called when the handshake has
+ * been received and parsed from the client, but the response is
+ * not sent yet. Return non-zero to disallow the connection.
+ * @user is a pointer to an array of struct lws_tokens, you can
+ * use the header enums lws_token_indexes from libwebsockets.h
+ * to check for and read the supported header presence and
+ * content before deciding to allow the handshake to proceed or
+ * to kill the connection.
+ *
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to perform extra SSL_CTX_load_verify_locations() or similar
+ * calls to direct OpenSSL where to find certificates the client
+ * can use to confirm the remote server identity. @user is the
+ * OpenSSL SSL_CTX*
+ *
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS: if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to load extra certifcates into the server which allow it to
+ * verify the validity of certificates returned by clients. @user
+ * is the server's OpenSSL SSL_CTX*
+ *
+ * LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: if the
+ * libwebsockets context was created with the option
+ * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this
+ * callback is generated during OpenSSL verification of the cert
+ * sent from the client. It is sent to protocol[0] callback as
+ * no protocol has been negotiated on the connection yet.
+ * Notice that the libwebsockets context and wsi are both NULL
+ * during this callback. See
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+ * to understand more detail about the OpenSSL callback that
+ * generates this libwebsockets callback and the meanings of the
+ * arguments passed. In this callback, @user is the x509_ctx,
+ * @in is the ssl pointer and @len is preverify_ok
+ * Notice that this callback maintains libwebsocket return
+ * conventions, return 0 to mean the cert is OK or 1 to fail it.
+ * This also means that if you don't handle this callback then
+ * the default callback action of returning 0 allows the client
+ * certificates.
+ *
+ * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: this callback happens
+ * when a client handshake is being compiled. @user is NULL,
+ * @in is a char **, it's pointing to a char * which holds the
+ * next location in the header buffer where you can add
+ * headers, and @len is the remaining space in the header buffer,
+ * which is typically some hundreds of bytes. So, to add a canned
+ * cookie, your handler code might look similar to:
+ *
+ * char **p = (char **)in;
+ *
+ * if (len < 100)
+ * return 1;
+ *
+ * *p += sprintf(*p, "Cookie: a=b\x0d\x0a");
+ *
+ * return 0;
+ *
+ * Notice if you add anything, you just have to take care about
+ * the CRLF on the line you added. Obviously this callback is
+ * optional, if you don't handle it everything is fine.
+ *
+ * Notice the callback is coming to protocols[0] all the time,
+ * because there is no specific protocol handshook yet.
+ *
+ * LWS_CALLBACK_CONFIRM_EXTENSION_OKAY: When the server handshake code
+ * sees that it does support a requested extension, before
+ * accepting the extension by additing to the list sent back to
+ * the client it gives this callback just to check that it's okay
+ * to use that extension. It calls back to the requested protocol
+ * and with @in being the extension name, @len is 0 and @user is
+ * valid. Note though at this time the ESTABLISHED callback hasn't
+ * happened yet so if you initialize @user content there, @user
+ * content during this callback might not be useful for anything.
+ * Notice this callback comes to protocols[0].
+ *
+ * LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED: When a client
+ * connection is being prepared to start a handshake to a server,
+ * each supported extension is checked with protocols[0] callback
+ * with this reason, giving the user code a chance to suppress the
+ * claim to support that extension by returning non-zero. If
+ * unhandled, by default 0 will be returned and the extension
+ * support included in the header to the server. Notice this
+ * callback comes to protocols[0].
+ *
+ * LWS_CALLBACK_PROTOCOL_INIT: One-time call per protocol so it can
+ * do initial setup / allocations etc
+ *
+ * LWS_CALLBACK_PROTOCOL_DESTROY: One-time call per protocol indicating
+ * this protocol won't get used at all after this callback, the
+ * context is getting destroyed. Take the opportunity to
+ * deallocate everything that was allocated by the protocol.
+ *
+ * The next four reasons are optional and only need taking care of if you
+ * will be integrating libwebsockets sockets into an external polling
+ * array.
+ *
+ * LWS_CALLBACK_ADD_POLL_FD: libwebsocket deals with its poll() loop
+ * internally, but in the case you are integrating with another
+ * server you will need to have libwebsocket sockets share a
+ * polling array with the other server. This and the other
+ * POLL_FD related callbacks let you put your specialized
+ * poll array interface code in the callback for protocol 0, the
+ * first protocol you support, usually the HTTP protocol in the
+ * serving case. This callback happens when a socket needs to be
+ * added to the polling loop: @in contains the fd, and
+ * @len is the events bitmap (like, POLLIN). If you are using the
+ * internal polling loop (the "service" callback), you can just
+ * ignore these callbacks.
+ *
+ * LWS_CALLBACK_DEL_POLL_FD: This callback happens when a socket descriptor
+ * needs to be removed from an external polling array. @in is
+ * the socket desricptor. If you are using the internal polling
+ * loop, you can just ignore it.
+ *
+ * LWS_CALLBACK_SET_MODE_POLL_FD: This callback happens when libwebsockets
+ * wants to modify the events for the socket descriptor in @in.
+ * The handler should OR @len on to the events member of the pollfd
+ * struct for this socket descriptor. If you are using the
+ * internal polling loop, you can just ignore it.
+ *
+ * LWS_CALLBACK_CLEAR_MODE_POLL_FD: This callback occurs when libwebsockets
+ * wants to modify the events for the socket descriptor in @in.
+ * The handler should AND ~@len on to the events member of the
+ * pollfd struct for this socket descriptor. If you are using the
+ * internal polling loop, you can just ignore it.
+ */
+LWS_EXTERN int callback(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len);
+
+typedef int (callback_function)(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len);
+
+#ifndef LWS_NO_EXTENSIONS
+/**
+ * extension_callback_function() - Hooks to allow extensions to operate
+ * @context: Websockets context
+ * @ext: This extension
+ * @wsi: Opaque websocket instance pointer
+ * @reason: The reason for the call
+ * @user: Pointer to per-session user data allocated by library
+ * @in: Pointer used for some callback reasons
+ * @len: Length set for some callback reasons
+ *
+ * Each extension that is active on a particular connection receives
+ * callbacks during the connection lifetime to allow the extension to
+ * operate on websocket data and manage itself.
+ *
+ * Libwebsockets takes care of allocating and freeing "user" memory for
+ * each active extension on each connection. That is what is pointed to
+ * by the @user parameter.
+ *
+ * LWS_EXT_CALLBACK_CONSTRUCT: called when the server has decided to
+ * select this extension from the list provided by the client,
+ * just before the server will send back the handshake accepting
+ * the connection with this extension active. This gives the
+ * extension a chance to initialize its connection context found
+ * in @user.
+ *
+ * LWS_EXT_CALLBACK_CLIENT_CONSTRUCT: same as LWS_EXT_CALLBACK_CONSTRUCT
+ * but called when client is instantiating this extension. Some
+ * extensions will work the same on client and server side and then
+ * you can just merge handlers for both CONSTRUCTS.
+ *
+ * LWS_EXT_CALLBACK_DESTROY: called when the connection the extension was
+ * being used on is about to be closed and deallocated. It's the
+ * last chance for the extension to deallocate anything it has
+ * allocated in the user data (pointed to by @user) before the
+ * user data is deleted. This same callback is used whether you
+ * are in client or server instantiation context.
+ *
+ * LWS_EXT_CALLBACK_PACKET_RX_PREPARSE: when this extension was active on
+ * a connection, and a packet of data arrived at the connection,
+ * it is passed to this callback to give the extension a chance to
+ * change the data, eg, decompress it. @user is pointing to the
+ * extension's private connection context data, @in is pointing
+ * to an lws_tokens struct, it consists of a char * pointer called
+ * token, and an int called token_len. At entry, these are
+ * set to point to the received buffer and set to the content
+ * length. If the extension will grow the content, it should use
+ * a new buffer allocated in its private user context data and
+ * set the pointed-to lws_tokens members to point to its buffer.
+ *
+ * LWS_EXT_CALLBACK_PACKET_TX_PRESEND: this works the same way as
+ * LWS_EXT_CALLBACK_PACKET_RX_PREPARSE above, except it gives the
+ * extension a chance to change websocket data just before it will
+ * be sent out. Using the same lws_token pointer scheme in @in,
+ * the extension can change the buffer and the length to be
+ * transmitted how it likes. Again if it wants to grow the
+ * buffer safely, it should copy the data into its own buffer and
+ * set the lws_tokens token pointer to it.
+ */
+LWS_EXTERN int extension_callback(struct libwebsocket_context *context,
+ struct libwebsocket_extension *ext,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons reason,
+ void *user, void *in, size_t len);
+
+typedef int (extension_callback_function)(struct libwebsocket_context *context,
+ struct libwebsocket_extension *ext,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons reason,
+ void *user, void *in, size_t len);
+#endif
+
+/**
+ * struct libwebsocket_protocols - List of protocols and handlers server
+ * supports.
+ * @name: Protocol name that must match the one given in the client
+ * Javascript new WebSocket(url, 'protocol') name
+ * @callback: The service callback used for this protocol. It allows the
+ * service action for an entire protocol to be encapsulated in
+ * the protocol-specific callback
+ * @per_session_data_size: Each new connection using this protocol gets
+ * this much memory allocated on connection establishment and
+ * freed on connection takedown. A pointer to this per-connection
+ * allocation is passed into the callback in the 'user' parameter
+ * @rx_buffer_size: if you want atomic frames delivered to the callback, you
+ * should set this to the size of the biggest legal frame that
+ * you support. If the frame size is exceeded, there is no
+ * error, but the buffer will spill to the user callback when
+ * full, which you can detect by using
+ * libwebsockets_remaining_packet_payload(). Notice that you
+ * just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING
+ * and post-padding are automatically also allocated on top.
+ * @owning_server: the server init call fills in this opaque pointer when
+ * registering this protocol with the server.
+ * @protocol_index: which protocol we are starting from zero
+ *
+ * This structure represents one protocol supported by the server. An
+ * array of these structures is passed to libwebsocket_create_server()
+ * allows as many protocols as you like to be handled by one server.
+ */
+
+struct libwebsocket_protocols {
+ const char *name;
+ callback_function *callback;
+ size_t per_session_data_size;
+ size_t rx_buffer_size;
+
+ /*
+ * below are filled in on server init and can be left uninitialized,
+ * no need for user to use them directly either
+ */
+
+ struct libwebsocket_context *owning_server;
+ int protocol_index;
+};
+
+#ifndef LWS_NO_EXTENSIONS
+/**
+ * struct libwebsocket_extension - An extension we know how to cope with
+ *
+ * @name: Formal extension name, eg, "deflate-stream"
+ * @callback: Service callback
+ * @per_session_data_size: Libwebsockets will auto-malloc this much
+ * memory for the use of the extension, a pointer
+ * to it comes in the @user callback parameter
+ * @per_context_private_data: Optional storage for this extension that
+ * is per-context, so it can track stuff across
+ * all sessions, etc, if it wants
+ */
+
+struct libwebsocket_extension {
+ const char *name;
+ extension_callback_function *callback;
+ size_t per_session_data_size;
+ void *per_context_private_data;
+};
+#endif
+
+/**
+ * struct lws_context_creation_info: parameters to create context with
+ *
+ * @port: Port to listen on... you can use 0 to suppress listening on
+ * any port, that's what you want if you are not running a
+ * websocket server at all but just using it as a client
+ * @iface: NULL to bind the listen socket to all interfaces, or the
+ * interface name, eg, "eth2"
+ * @protocols: Array of structures listing supported protocols and a protocol-
+ * specific callback for each one. The list is ended with an
+ * entry that has a NULL callback pointer.
+ * It's not const because we write the owning_server member
+ * @extensions: NULL or array of libwebsocket_extension structs listing the
+ * extensions this context supports. If you configured with
+ * --without-extensions, you should give NULL here.
+ * @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want
+ * to listen using SSL, set to the filepath to fetch the
+ * server cert from, otherwise NULL for unencrypted
+ * @ssl_private_key_filepath: filepath to private key if wanting SSL mode,
+ * else ignored
+ * @ssl_ca_filepath: CA certificate filepath or NULL
+ * @ssl_cipher_list: List of valid ciphers to use (eg,
+ * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"
+ * or you can leave it as NULL to get "DEFAULT"
+ * @gid: group id to change to after setting listen socket, or -1.
+ * @uid: user id to change to after setting listen socket, or -1.
+ * @options: 0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
+ * @user: optional user pointer that can be recovered via the context
+ * pointer using libwebsocket_context_user
+ * @ka_time: 0 for no keepalive, otherwise apply this keepalive timeout to
+ * all libwebsocket sockets, client or server
+ * @ka_probes: if ka_time was nonzero, after the timeout expires how many
+ * times to try to get a response from the peer before giving up
+ * and killing the connection
+ * @ka_interval: if ka_time was nonzero, how long to wait before each ka_probes
+ * attempt
+ */
+
+struct lws_context_creation_info {
+ int port;
+ const char *iface;
+ struct libwebsocket_protocols *protocols;
+ struct libwebsocket_extension *extensions;
+ const char *ssl_cert_filepath;
+ const char *ssl_private_key_filepath;
+ const char *ssl_ca_filepath;
+ const char *ssl_cipher_list;
+ int gid;
+ int uid;
+ unsigned int options;
+ void *user;
+ int ka_time;
+ int ka_probes;
+ int ka_interval;
+
+};
+
+LWS_EXTERN
+void lws_set_log_level(int level,
+ void (*log_emit_function)(int level, const char *line));
+
+LWS_EXTERN void
+lwsl_emit_syslog(int level, const char *line);
+
+LWS_EXTERN struct libwebsocket_context *
+libwebsocket_create_context(struct lws_context_creation_info *info);
+
+LWS_EXTERN void
+libwebsocket_context_destroy(struct libwebsocket_context *context);
+
+LWS_EXTERN int
+libwebsocket_service(struct libwebsocket_context *context, int timeout_ms);
+
+LWS_EXTERN int
+libwebsocket_service_fd(struct libwebsocket_context *context,
+ struct pollfd *pollfd);
+
+LWS_EXTERN void *
+libwebsocket_context_user(struct libwebsocket_context *context);
+
+/*
+ * IMPORTANT NOTICE!
+ *
+ * When sending with websocket protocol (LWS_WRITE_TEXT or LWS_WRITE_BINARY)
+ * the send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE
+ * buf, and LWS_SEND_BUFFER_POST_PADDING bytes valid AFTER (buf + len).
+ *
+ * This allows us to add protocol info before and after the data, and send as
+ * one packet on the network without payload copying, for maximum efficiency.
+ *
+ * So for example you need this kind of code to use libwebsocket_write with a
+ * 128-byte payload
+ *
+ * char buf[LWS_SEND_BUFFER_PRE_PADDING + 128 + LWS_SEND_BUFFER_POST_PADDING];
+ *
+ * // fill your part of the buffer... for example here it's all zeros
+ * memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, 128);
+ *
+ * libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128,
+ * LWS_WRITE_TEXT);
+ *
+ * When sending LWS_WRITE_HTTP, there is no protocol addition and you can just
+ * use the whole buffer without taking care of the above.
+ */
+
+/*
+ * this is the frame nonce plus two header plus 8 length
+ * there's an additional two for mux extension per mux nesting level
+ * 2 byte prepend on close will already fit because control frames cannot use
+ * the big length style
+ */
+
+#define LWS_SEND_BUFFER_PRE_PADDING (4 + 10 + (2 * MAX_MUX_RECURSION))
+#define LWS_SEND_BUFFER_POST_PADDING 4
+
+LWS_EXTERN int
+libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, size_t len,
+ enum libwebsocket_write_protocol protocol);
+
+LWS_EXTERN int
+libwebsockets_serve_http_file(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, const char *file,
+ const char *content_type);
+LWS_EXTERN int
+libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
+ struct libwebsocket *wsi);
+
+LWS_EXTERN const struct libwebsocket_protocols *
+libwebsockets_get_protocol(struct libwebsocket *wsi);
+
+LWS_EXTERN int
+libwebsocket_callback_on_writable(struct libwebsocket_context *context,
+ struct libwebsocket *wsi);
+
+LWS_EXTERN int
+libwebsocket_callback_on_writable_all_protocol(
+ const struct libwebsocket_protocols *protocol);
+
+LWS_EXTERN int
+libwebsocket_get_socket_fd(struct libwebsocket *wsi);
+
+LWS_EXTERN int
+libwebsocket_is_final_fragment(struct libwebsocket *wsi);
+
+LWS_EXTERN unsigned char
+libwebsocket_get_reserved_bits(struct libwebsocket *wsi);
+
+LWS_EXTERN int
+libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable);
+
+LWS_EXTERN void
+libwebsocket_rx_flow_allow_all_protocol(
+ const struct libwebsocket_protocols *protocol);
+
+LWS_EXTERN size_t
+libwebsockets_remaining_packet_payload(struct libwebsocket *wsi);
+
+LWS_EXTERN struct libwebsocket *
+libwebsocket_client_connect(struct libwebsocket_context *clients,
+ const char *address,
+ int port,
+ int ssl_connection,
+ const char *path,
+ const char *host,
+ const char *origin,
+ const char *protocol,
+ int ietf_version_or_minus_one);
+
+LWS_EXTERN struct libwebsocket *
+libwebsocket_client_connect_extended(struct libwebsocket_context *clients,
+ const char *address,
+ int port,
+ int ssl_connection,
+ const char *path,
+ const char *host,
+ const char *origin,
+ const char *protocol,
+ int ietf_version_or_minus_one,
+ void *userdata);
+
+LWS_EXTERN const char *
+libwebsocket_canonical_hostname(struct libwebsocket_context *context);
+
+
+LWS_EXTERN void
+libwebsockets_get_peer_addresses(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, int fd, char *name, int name_len,
+ char *rip, int rip_len);
+
+LWS_EXTERN int
+libwebsockets_get_random(struct libwebsocket_context *context,
+ void *buf, int len);
+
+LWS_EXTERN int
+lws_daemonize(const char *_lock_path);
+
+LWS_EXTERN int
+lws_send_pipe_choked(struct libwebsocket *wsi);
+
+LWS_EXTERN int
+lws_frame_is_binary(struct libwebsocket *wsi);
+
+LWS_EXTERN unsigned char *
+libwebsockets_SHA1(const unsigned char *d, size_t n, unsigned char *md);
+
+LWS_EXTERN int
+lws_b64_encode_string(const char *in, int in_len, char *out, int out_size);
+
+LWS_EXTERN int
+lws_b64_decode_string(const char *in, char *out, int out_size);
+
+LWS_EXTERN const char *
+lws_get_library_version(void);
+
+/* access to headers... only valid while headers valid */
+
+LWS_EXTERN int
+lws_hdr_total_length(struct libwebsocket *wsi, enum lws_token_indexes h);
+
+LWS_EXTERN int
+lws_hdr_copy(struct libwebsocket *wsi, char *dest, int len,
+ enum lws_token_indexes h);
+
+/*
+ * Note: this is not normally needed as a user api. It's provided in case it is
+ * useful when integrating with other app poll loop service code.
+ */
+
+LWS_EXTERN int
+libwebsocket_read(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ unsigned char *buf, size_t len);
+
+#ifndef LWS_NO_EXTENSIONS
+LWS_EXTERN struct libwebsocket_extension *libwebsocket_get_internal_extensions();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index ccd6c05..3142225 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -8,7 +8,7 @@
* \date 2013
*/
/*
- * Copyright (C) 2011-2012 CESNET
+ * Copyright (C) 2011-2013 CESNET
*
* LICENSE TERMS
*
@@ -50,6 +50,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/fcntl.h>
#include <pthread.h>
#include <ctype.h>
@@ -67,6 +68,10 @@
#include <libnetconf.h>
+#ifdef WITH_NOTIFICATIONS
+#include "notification_module.h"
+#endif
+
#include "message_type.h"
@@ -1320,8 +1325,11 @@
*/
static void forked_proc(apr_pool_t * pool, server_rec * server)
{
+ struct timeval tv;
struct sockaddr_un local, remote;
int lsock, client, ret, i, pthread_count = 0;
+ char use_notifications = 0;
+ unsigned int olds = 0;
socklen_t len;
mod_netconf_cfg *cfg;
apr_hash_t *netconf_sessions_list;
@@ -1371,6 +1379,15 @@
return;
}
+ #ifdef WITH_NOTIFICATIONS
+ if (notification_init(pool, server) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "libwebsockets initialization failed");
+ use_notifications = 0;
+ } else {
+ use_notifications = 1;
+ }
+ #endif
+
/* prepare internal lists */
netconf_sessions_list = apr_hash_make(pool);
@@ -1396,12 +1413,25 @@
close (lsock);
return;
}
-
+
+ fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
while (isterminated == 0) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "waiting for another client's request");
+ gettimeofday(&tv, NULL);
+ #ifdef WITH_NOTIFICATIONS
+ if (((unsigned int)tv.tv_sec - olds) > 60) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "handling notifications");
+ }
+ if (use_notifications == 1) {
+ notification_handle();
+ }
+ #endif
/* open incoming connection if any */
len = sizeof(remote);
+ if (((unsigned int)tv.tv_sec - olds) > 60) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "accepting another client");
+ olds = tv.tv_sec;
+ }
client = accept(lsock, (struct sockaddr *) &remote, &len);
if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
apr_sleep(SLEEP_TIME);
@@ -1454,6 +1484,10 @@
close(lsock);
+ #ifdef WITH_NOTIFICATIONS
+ notification_close();
+ #endif
+
/* destroy rwlock */
pthread_rwlock_destroy(&session_lock);
pthread_rwlockattr_destroy(&lock_attrs);
diff --git a/src/notification-server.c b/src/notification-server.c
new file mode 100644
index 0000000..382629b
--- /dev/null
+++ b/src/notification-server.c
@@ -0,0 +1,738 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include "libwebsockets.h"
+#include "notification_module.h"
+
+#ifndef TEST_NOTIFICATION_SERVER
+#include <httpd.h>
+#include <http_log.h>
+#endif
+
+#if defined(TEST_NOTIFICATION_SERVER) || defined(WITH_NOTIFICATIONS)
+static int close_testing;
+static int max_poll_elements;
+
+static struct pollfd *pollfds;
+static int *fd_lookup;
+static int count_pollfds;
+static int force_exit = 0;
+static struct libwebsocket_context *context = NULL;
+static server_rec *http_server = NULL;
+
+/*
+ * This demo server shows how to use libwebsockets for one or more
+ * websocket protocols in the same server
+ *
+ * It defines the following websocket protocols:
+ *
+ * dumb-increment-protocol: once the socket is opened, an incrementing
+ * ascii string is sent down it every 50ms.
+ * If you send "reset\n" on the websocket, then
+ * the incrementing number is reset to 0.
+ *
+ * lws-mirror-protocol: copies any received packet to every connection also
+ * using this protocol, including the sender
+ */
+
+enum demo_protocols {
+ /* always first */
+ PROTOCOL_HTTP = 0,
+
+ PROTOCOL_NOTIFICATION,
+
+ /* always last */
+ DEMO_PROTOCOL_COUNT
+};
+
+
+#define LOCAL_RESOURCE_PATH "."
+char *resource_path = LOCAL_RESOURCE_PATH;
+
+/*
+ * We take a strict whitelist approach to stop ../ attacks
+ */
+
+struct serveable {
+ const char *urlpath;
+ const char *mimetype;
+};
+
+static const struct serveable whitelist[] = {
+ { "/favicon.ico", "image/x-icon" },
+ { "/libwebsockets.org-logo.png", "image/png" },
+
+ /* last one is the default served if no match */
+ { "/test.html", "text/html" },
+};
+
+struct per_session_data__http {
+ int fd;
+};
+
+/* this protocol server (always the first one) just knows how to do HTTP */
+
+static int callback_http(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len)
+{
+ char client_name[128];
+ char client_ip[128];
+ char buf[256];
+ int n, m;
+ unsigned char *p;
+ static unsigned char buffer[4096];
+ struct stat stat_buf;
+ struct per_session_data__http *pss = (struct per_session_data__http *)user;
+ int fd = (int)(long)in;
+
+ switch (reason) {
+ case LWS_CALLBACK_HTTP:
+
+ /* check for the "send a big file by hand" example case */
+
+ if (!strcmp((const char *)in, "/leaf.jpg")) {
+ char leaf_path[1024];
+ snprintf(leaf_path, sizeof(leaf_path), "%s/leaf.jpg", resource_path);
+
+ /* well, let's demonstrate how to send the hard way */
+
+ p = buffer;
+
+ pss->fd = open(leaf_path, O_RDONLY);
+
+ if (pss->fd < 0)
+ return -1;
+
+ fstat(pss->fd, &stat_buf);
+
+ /*
+ * we will send a big jpeg file, but it could be
+ * anything. Set the Content-Type: appropriately
+ * so the browser knows what to do with it.
+ */
+
+ p += sprintf((char *)p,
+ "HTTP/1.0 200 OK\x0d\x0a"
+ "Server: libwebsockets\x0d\x0a"
+ "Content-Type: image/jpeg\x0d\x0a"
+ "Content-Length: %u\x0d\x0a\x0d\x0a",
+ (unsigned int)stat_buf.st_size);
+
+ /*
+ * send the http headers...
+ * this won't block since it's the first payload sent
+ * on the connection since it was established
+ * (too small for partial)
+ */
+
+ n = libwebsocket_write(wsi, buffer,
+ p - buffer, LWS_WRITE_HTTP);
+
+ if (n < 0) {
+ close(pss->fd);
+ return -1;
+ }
+ /*
+ * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
+ */
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+ }
+
+ /* if not, send a file the easy way */
+
+ for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
+ if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
+ break;
+
+ sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
+
+ if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
+ return -1; /* through completion or error, close the socket */
+
+ /*
+ * notice that the sending of the file completes asynchronously,
+ * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+ * it's done
+ */
+
+ break;
+
+ case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
+ /* kill the connection after we sent one file */
+ return -1;
+
+ case LWS_CALLBACK_HTTP_WRITEABLE:
+ /*
+ * we can send more of whatever it is we were sending
+ */
+
+ do {
+ n = read(pss->fd, buffer, sizeof buffer);
+ /* problem reading, close conn */
+ if (n < 0)
+ goto bail;
+ /* sent it all, close conn */
+ if (n == 0)
+ goto bail;
+ /*
+ * because it's HTTP and not websocket, don't need to take
+ * care about pre and postamble
+ */
+ m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
+ if (m < 0)
+ /* write failed, close conn */
+ goto bail;
+ if (m != n)
+ /* partial write, adjust */
+ lseek(pss->fd, m - n, SEEK_CUR);
+
+ } while (!lws_send_pipe_choked(wsi));
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+
+bail:
+ close(pss->fd);
+ return -1;
+
+ /*
+ * callback for confirming to continue with client IP appear in
+ * protocol 0 callback since no websocket protocol has been agreed
+ * yet. You can just ignore this if you won't filter on client IP
+ * since the default uhandled callback return is 0 meaning let the
+ * connection continue.
+ */
+
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+ libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
+ sizeof(client_name), client_ip, sizeof(client_ip));
+
+ fprintf(stderr, "Received network connect from %s (%s)\n",
+ client_name, client_ip);
+ /* if we returned non-zero from here, we kill the connection */
+ break;
+
+ /*
+ * callbacks for managing the external poll() array appear in
+ * protocol 0 callback
+ */
+
+ case LWS_CALLBACK_ADD_POLL_FD:
+
+ if (count_pollfds >= max_poll_elements) {
+ lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
+ return 1;
+ }
+
+ fd_lookup[fd] = count_pollfds;
+ pollfds[count_pollfds].fd = fd;
+ pollfds[count_pollfds].events = (int)(long)len;
+ pollfds[count_pollfds++].revents = 0;
+ break;
+
+ case LWS_CALLBACK_DEL_POLL_FD:
+ if (!--count_pollfds)
+ break;
+ m = fd_lookup[fd];
+ /* have the last guy take up the vacant slot */
+ pollfds[m] = pollfds[count_pollfds];
+ fd_lookup[pollfds[count_pollfds].fd] = m;
+ break;
+
+ case LWS_CALLBACK_SET_MODE_POLL_FD:
+ pollfds[fd_lookup[fd]].events |= (int)(long)len;
+ break;
+
+ case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
+ pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * this is just an example of parsing handshake headers, you don't need this
+ * in your code unless you will filter allowing connections by the header
+ * content
+ */
+
+static void
+dump_handshake_info(struct libwebsocket *wsi)
+{
+ int n;
+ static const char *token_names[WSI_TOKEN_COUNT] = {
+ /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
+ /*[WSI_TOKEN_HOST] =*/ "Host",
+ /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
+ /*[WSI_TOKEN_KEY1] =*/ "key 1",
+ /*[WSI_TOKEN_KEY2] =*/ "key 2",
+ /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
+ /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
+ /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
+ /*[WSI_TOKEN_DRAFT] =*/ "Draft",
+ /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
+
+ /* new for 04 */
+ /*[WSI_TOKEN_KEY] =*/ "Key",
+ /*[WSI_TOKEN_VERSION] =*/ "Version",
+ /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
+
+ /* new for 05 */
+ /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
+
+ /* client receives these */
+ /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
+ /*[WSI_TOKEN_NONCE] =*/ "Nonce",
+ /*[WSI_TOKEN_HTTP] =*/ "Http",
+ /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
+ };
+ char buf[256];
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+ if (!lws_hdr_total_length(wsi, n))
+ continue;
+
+ lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+ fprintf(stderr, " %s = %s\n", token_names[n], buf);
+ }
+}
+
+/* dumb_increment protocol */
+
+/*
+ * one of these is auto-created for each connection and a pointer to the
+ * appropriate instance is passed to the callback in the user parameter
+ *
+ * for this example protocol we use it to individualize the count for each
+ * connection.
+ */
+
+struct per_session_data__dumb_increment {
+ int number;
+};
+
+//static void get_client_notification()
+//{
+// /* get non-exclusive (read) access to sessions_list (conns) */
+// if (pthread_rwlock_rdlock (&session_lock) != 0) {
+// ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+// return NULL;
+// }
+// /* get session where send the RPC */
+// locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+// if (locked_session != NULL) {
+// session = locked_session->session;
+// }
+// if (session != NULL) {
+// /* get exclusive access to session */
+// if (pthread_mutex_lock(&locked_session->lock) != 0) {
+// /* unlock before returning error */
+// if (pthread_rwlock_unlock (&session_lock) != 0) {
+// ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+// return NULL;
+// }
+// return NULL;
+// }
+// /* send the request and get the reply */
+// msgt = nc_session_send_recv(session, rpc, &reply);
+//
+// /* first release exclusive lock for this session */
+// pthread_mutex_unlock(&locked_session->lock);
+// /* end of critical section */
+// if (pthread_rwlock_unlock (&session_lock) != 0) {
+// ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+// return (NULL);
+// }
+//
+// /* process the result of the operation */
+// switch (msgt) {
+// case NC_MSG_UNKNOWN:
+// if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
+// ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
+// netconf_close(server, conns, session_key);
+// return (NULL);
+// }
+// /* no break */
+// case NC_MSG_NONE:
+// /* there is error handled by callback */
+// return (NULL);
+// break;
+// case NC_MSG_REPLY:
+// switch (replyt = nc_reply_get_type(reply)) {
+// case NC_REPLY_DATA:
+// if ((data = nc_reply_get_data (reply)) == NULL) {
+// ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
+// data = NULL;
+// }
+// break;
+// default:
+// ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply (%d)", replyt);
+// data = NULL;
+// break;
+// }
+// break;
+// default:
+// ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected reply message received (%d)", msgt);
+// data = NULL;
+// break;
+// }
+// nc_reply_free(reply);
+// return (data);
+// } else {
+// /* release lock on failure */
+// if (pthread_rwlock_unlock (&session_lock) != 0) {
+// ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+// }
+// ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
+// return (NULL);
+// }
+//}
+
+static int callback_notification(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ int isnotif = 0;
+ int n, m;
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING];
+ unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
+ struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
+ #ifndef TEST_NOTIFICATION_SERVER
+ if (http_server != NULL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, http_server, "libwebsockets callback_notification");
+ }
+ #endif
+
+ switch (reason) {
+
+ case LWS_CALLBACK_ESTABLISHED:
+ lwsl_info("callback_notification: LWS_CALLBACK_ESTABLISHED\n");
+ pss->number = 0;
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+// get_client_notification();
+ n = sprintf((char *)p, "not%d\n", pss->number++);
+ m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
+ if (m < n) {
+ lwsl_err("ERROR %d writing to di socket\n", n);
+ return -1;
+ }
+ if (close_testing && pss->number == 50) {
+ lwsl_info("close tesing limit, closing\n");
+ return -1;
+ }
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+// fprintf(stderr, "rx %d\n", (int)len);
+ if (len < 6)
+ break;
+ if (strcmp((const char *)in, "reset\n") == 0)
+ pss->number = 0;
+ break;
+ /*
+ * this just demonstrates how to use the protocol filter. If you won't
+ * study and reject connections based on header content, you don't need
+ * to handle this callback
+ */
+
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ //dump_handshake_info(wsi);
+ /* you could return non-zero here and kill the connection */
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* list of supported protocols and callbacks */
+
+static struct libwebsocket_protocols protocols[] = {
+ /* first protocol must always be HTTP handler */
+
+ {
+ "http-only", /* name */
+ callback_http, /* callback */
+ sizeof (struct per_session_data__http), /* per_session_data_size */
+ 0, /* max frame size / rx buffer */
+ },
+ {
+ "notification-protocol",
+ callback_notification,
+ sizeof(struct per_session_data__dumb_increment),
+ 10,
+ },
+ { NULL, NULL, 0, 0 } /* terminator */
+};
+
+
+int notification_init(apr_pool_t * pool, server_rec * server)
+{
+ char cert_path[1024];
+ char key_path[1024];
+ int use_ssl = 0;
+ struct lws_context_creation_info info;
+ int opts = 0;
+ char interface_name[128] = "";
+ const char *iface = NULL;
+ int debug_level = 7;
+
+ memset(&info, 0, sizeof info);
+ info.port = NOTIFICATION_SERVER_PORT;
+
+ /* tell the library what debug level to emit and to send it to syslog */
+ lws_set_log_level(debug_level, lwsl_emit_syslog);
+
+ #ifndef TEST_NOTIFICATION_SERVER
+ if (server != NULL) {
+ http_server = server;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, http_server, "Initialization of libwebsocket");
+ }
+ #endif
+ lwsl_notice("libwebsockets test server - "
+ "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
+ "licensed under LGPL2.1\n");
+ max_poll_elements = getdtablesize();
+ pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
+ fd_lookup = malloc(max_poll_elements * sizeof (int));
+ if (pollfds == NULL || fd_lookup == NULL) {
+ lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
+ return -1;
+ }
+
+ info.iface = iface;
+ info.protocols = protocols;
+
+ //snprintf(cert_path, sizeof(cert_path), "%s/libwebsockets-test-server.pem", resource_path);
+ //snprintf(key_path, sizeof(cert_path), "%s/libwebsockets-test-server.key.pem", resource_path);
+
+ //info.ssl_cert_filepath = cert_path;
+ //info.ssl_private_key_filepath = key_path;
+
+ info.gid = -1;
+ info.uid = -1;
+ info.options = opts;
+
+ /* create server */
+ context = libwebsocket_create_context(&info);
+ if (context == NULL) {
+ lwsl_err("libwebsocket init failed\n");
+ return -1;
+ }
+}
+
+void notification_close()
+{
+ libwebsocket_context_destroy(context);
+
+ lwsl_notice("libwebsockets-test-server exited cleanly\n");
+}
+
+/**
+ * \brief send notification if any
+ * \return < 0 on error
+ */
+int notification_handle()
+{
+ static struct timeval tv;
+ static unsigned int olds = 0;
+ int n = 0;
+
+ gettimeofday(&tv, NULL);
+
+ /*
+ * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
+ * live websocket connection using the DUMB_INCREMENT protocol,
+ * as soon as it can take more packets (usually immediately)
+ */
+
+ if (((unsigned int)tv.tv_sec - olds) > 0) {
+ libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_NOTIFICATION]);
+ olds = tv.tv_sec;
+ }
+
+
+ /*
+ * this represents an existing server's single poll action
+ * which also includes libwebsocket sockets
+ */
+
+ n = poll(pollfds, count_pollfds, 50);
+ if (n < 0)
+ return n;
+
+
+ if (n) {
+ for (n = 0; n < count_pollfds; n++) {
+ if (pollfds[n].revents) {
+ /*
+ * returns immediately if the fd does not
+ * match anything under libwebsockets
+ * control
+ */
+ if (libwebsocket_service_fd(context, &pollfds[n]) < 0) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+#endif
+
+
+#ifndef WITH_NOTIFICATIONS
+#ifdef TEST_NOTIFICATION_SERVER
+int main(int argc, char **argv)
+{
+// char cert_path[1024];
+// char key_path[1024];
+// int n = 0;
+// int use_ssl = 0;
+// struct libwebsocket_context *context;
+// int opts = 0;
+// char interface_name[128] = "";
+// const char *iface = NULL;
+// unsigned int oldus = 0;
+// unsigned int olds = 0;
+// struct lws_context_creation_info info;
+//
+// int debug_level = 7;
+//
+// memset(&info, 0, sizeof info);
+// info.port = 8081;
+//
+// /* tell the library what debug level to emit and to send it to syslog */
+// lws_set_log_level(debug_level, lwsl_emit_syslog);
+//
+// lwsl_notice("libwebsockets test server - "
+// "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
+// "licensed under LGPL2.1\n");
+// max_poll_elements = getdtablesize();
+// pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
+// fd_lookup = malloc(max_poll_elements * sizeof (int));
+// if (pollfds == NULL || fd_lookup == NULL) {
+// lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
+// return -1;
+// }
+//
+// info.iface = iface;
+// info.protocols = protocols;
+//
+// //snprintf(cert_path, sizeof(cert_path), "%s/libwebsockets-test-server.pem", resource_path);
+// //snprintf(key_path, sizeof(cert_path), "%s/libwebsockets-test-server.key.pem", resource_path);
+//
+// //info.ssl_cert_filepath = cert_path;
+// //info.ssl_private_key_filepath = key_path;
+// //
+// info.gid = -1;
+// info.uid = -1;
+// info.options = opts;
+//
+// /* create server */
+// context = libwebsocket_create_context(&info);
+// if (context == NULL) {
+// lwsl_err("libwebsocket init failed\n");
+// return -1;
+// }
+//
+// n = 0;
+// while (n >= 0 && !force_exit) {
+// struct timeval tv;
+//
+// gettimeofday(&tv, NULL);
+//
+// /*
+// * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
+// * live websocket connection using the DUMB_INCREMENT protocol,
+// * as soon as it can take more packets (usually immediately)
+// */
+//
+// if (((unsigned int)tv.tv_sec - olds) > 0) {
+// libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_NOTIFICATION]);
+// olds = tv.tv_sec;
+// }
+//
+//
+// /*
+// * this represents an existing server's single poll action
+// * which also includes libwebsocket sockets
+// */
+//
+// n = poll(pollfds, count_pollfds, 50);
+// if (n < 0)
+// continue;
+//
+//
+// if (n) {
+// for (n = 0; n < count_pollfds; n++) {
+// if (pollfds[n].revents) {
+// /*
+// * returns immediately if the fd does not
+// * match anything under libwebsockets
+// * control
+// */
+// if (libwebsocket_service_fd(context, &pollfds[n]) < 0) {
+// break;
+// }
+// }
+// }
+// }
+// }
+//
+// libwebsocket_context_destroy(context);
+//
+// lwsl_notice("libwebsockets-test-server exited cleanly\n");
+//
+// return 0;
+ if (notification_init(NULL, NULL) == -1) {
+ fprintf(stderr, "Error during initialization\n");
+ return 1;
+ }
+ while (!force_exit) {
+ notification_handle();
+ }
+ notification_close();
+}
+#endif
+#endif
diff --git a/src/notification_module.h b/src/notification_module.h
new file mode 100644
index 0000000..8166af3
--- /dev/null
+++ b/src/notification_module.h
@@ -0,0 +1,64 @@
+/*!
+ * \file notification_module.h
+ * \brief
+ * \author Tomas Cejka <cejkat@cesnet.cz>
+ * \date 2013
+ */
+/*
+ * Copyright (C) 2013 CESNET
+ *
+ * LICENSE TERMS
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of the Company nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this
+ * product may be distributed under the terms of the GNU General Public
+ * License (GPL) version 2 or later, in which case the provisions
+ * of the GPL apply INSTEAD OF those given above.
+ *
+ * This software is provided ``as is'', and any express or implied
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose are disclaimed.
+ * In no event shall the company or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ *
+ */
+#ifndef __NOTIFICATION_MODULE_H
+#define __NOTIFICATION_MODULE_H
+#include "libwebsockets.h"
+
+#ifndef TEST_NOTIFICATION_SERVER
+#include <httpd.h>
+#include <http_log.h>
+#else
+typedef struct p {} apr_pool_t;
+typedef struct s {} server_rec;
+#endif
+
+#define NOTIFICATION_SERVER_PORT 8080
+
+int notification_init(apr_pool_t * pool, server_rec * server);
+
+int notification_handle();
+
+void notification_close();
+
+#endif
+