commit 2f36baf3236cfcfa1f09364855f58555ccb87006 Author: Gergely Polonkai Date: Thu Jun 23 17:37:35 2016 +0200 Initial import diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..09ee747 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,5 @@ +ACLOCAL_AMFLAGS=-I m4 +SUBDIRS = libwmud-session libwmud-world libwmud-state-sqlite3 libwmud-protocol-telnet src doc/reference/libwmud-session doc/reference/libwmud-world +dist_doc_DATA = README + +DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-debug diff --git a/README b/README new file mode 100644 index 0000000..5cbe4d9 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +What is wMUD? +============= + On one hand, wMUD is a new (well, yet another) MUD (Multi-User Dungeon) +server. It is designed to run as smoothly as possible, which is achieved by +using threads. During development I tried to eliminate everything which is not +GLib, so, although not tested yet, it should by highly portable. + + On the other hand, wMUD is a learning project for GLib's several features. +This means that although I double check every byte of code, it can still have +bugs or not-so-effective code chunks. If you find one, please don't hesitate to +contact me, or send a patch! diff --git a/TODO b/TODO new file mode 100644 index 0000000..f0553a0 --- /dev/null +++ b/TODO @@ -0,0 +1,76 @@ +CLIENT FLOW +=========== + +[X] connection arrives +[X] connection gets accept()'ed +[ ] connection is closed if the IP is banned +[X] session object created +[ ] welcome banner gets sent +[X] user sends commans +[ ] commands get processed +[X] user sends the QUIT command +[X] session gets destroyed +[X] connection gets destroyed + + +TO-DO LIST +========== + +[X] Create own text formatting style + +[o] Create converter that can convert from own style to + [X] ANSI + [ ] colourless text + [ ] HTML + [ ] Pango markup + only the first two are very important + +[X] Do basic jobs for later threading support + +[X] Create and run the world object in a separate thread + +[X] Implement session handling + +[X] Create telnet interface that can process commands and send colourful + responses. This should go in a separate thread. + +[X] Plan configurable parameters and configuration file format + +[X] Write configuration reading code + +[o] Create XML schemas for world description + +[ ] Specify a way how new attribute and object types can be inserted into + a running system + +[ ] Specify state-saving backends: + [ ] SQLite3 + [ ] MySQL + [ ] PostgreSQL + [ ] XML + [ ] GDBM + +[ ] Specify data to be saved during state-save + +[ ] Write state saving (command and timed hook) and loading (command and + startup time) code + +[o] Write world description loading and reloading code + +[ ] Write User Account management code (possibility of registration, + login, logout, account deletion, ban, kick and purge + +[o] Write chat possibility + +[ ] Write Light-calculation code + +[ ] Plan initial character state (what attributes to use, look, cloths, + armour, weapon, etc.) + +[ ] Create the possibility to enter the world + +[ ] Create the possibility to move in the world + +[ ] Create a DBus module to control the whole program + +# vim: textwidth=78 : tabstop=8 : expandtab diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..bfe9c2d --- /dev/null +++ b/configure.ac @@ -0,0 +1,30 @@ +AC_INIT([wmud], [1.0], [polesz@w00d5t0ck.info]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +LT_INIT +AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], [compile with debugging support. Be warned that debugging support can eat a large amount of CPU when many clients are connected. Also, debug logs can become very large!])], , enable_debug=no) + +if test "x$enable_debug" = "xyes" ; then + AC_DEFINE([DEBUG], [1], [Define if debugging is enabled.]) +fi + +AC_PROG_CC +AC_PROG_LIBTOOL +PKG_PROG_PKG_CONFIG +PKG_CHECK_MODULES([GLIB], [glib-2.0]) +PKG_CHECK_MODULES([GOBJECT], [gobject-2.0]) +PKG_CHECK_MODULES([GNET], [gnet-2.0]) +PKG_CHECK_MODULES([EXTLIBS], [libxml-2.0, glib-2.0, gobject-2.0, gthread-2.0, gnet-2.0, gnome-vfs-2.0, sqlite3 gmodule-2.0]) +GTK_DOC_CHECK([1.10]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([ + Makefile + libwmud-session/Makefile + libwmud-world/Makefile + libwmud-state-sqlite3/Makefile + libwmud-protocol-telnet/Makefile + src/Makefile + doc/reference/libwmud-session/Makefile + doc/reference/libwmud-world/Makefile +]) +AC_OUTPUT diff --git a/data/state.sql b/data/state.sql new file mode 100644 index 0000000..e69de29 diff --git a/data/wmud.conf b/data/wmud.conf new file mode 100644 index 0000000..67820da --- /dev/null +++ b/data/wmud.conf @@ -0,0 +1,68 @@ +# general options +[general] +# log can be "syslog" (to log through the syslog facility), "file:" +# to log to the specified file, "console" (to log to the console), or "none" +# (for no logging at all). console can be used only if compiled with debug +# support. If logging is set to console, wMUD cannot be daemonized! +log = console +# if you wish, you can send debug, info, warning and error message to different +# destinations by defining a "debug log", an "info log", a "warning log" and an +# "error log" respectively +#debug log = file:/home/polesz/Projektek/wMUD/data/debug.log +info log = console +warning log = console +error log = console +chat = yes +dbus = yes + +# after initialization, send into the background. This setting is automatically +# set to "no" if logging is set to "console". However, if daemonize is set to +# "force", wMUD will be sent to the background, and will be forced to log +# through the syslog facilty +daemonize = no + +# wMUD is highly extensible with the use of modules +[modules] +# modules dir sets the place where module files can be found +modules dir = /home/polesz/Projektek/wMUD/modules +# the statesave module is used to save all the state of a running wMUD server. +# Only one module can be specified here, and one MUST be specified. Without +# such a module, wMUD won't be able to save character states, thus won't start +# without one +statesave = sqlite3 +# wMUD doesn't speak any protocols on its own. Of course, several protocol +# handler modules are provided with wMUD, and these can be loaded here. Module +# names (thus, protocol names) can be separated by colons. At least one +# protocol module must be loaded here. After startup, a client with +# administrator privileges can load up more modules +protocol = telnet:irc + +# statesave * groups can have any keys and values, they won't get checked +# during the configfile read. They are only processed by the state saving +# module loaded in the modules group + +[statesave sqlite3] +# for the sqlite3 state saving module, only a state file must be specified +state file = /home/polesz/Projektek/wMUD/data/state.sql + +# similar to the statesave modules, protocol settings are also not checked +# during initialization, only while actually loading up the modules. + +# telnet interface on all interfaces' port 4000, with the timeout value of 10 +# minutes +[protocol telnet global] +port = 4000 +timeout = 600 + +# telnet interface on localhost's port 9683 with the timeout value of 10 +# minutes +[protocol telnet local] +address = 127.0.0.1 +port = 9683 +timeout = 600 + +# IRC interface listening on all interfaces' port 6667, with no timeout value +[protocol irc global] +port = 6667 + +# vim: ft=dosini diff --git a/data/world.xml b/data/world.xml new file mode 100644 index 0000000..b20d746 --- /dev/null +++ b/data/world.xml @@ -0,0 +1,6 @@ + + + + Iminiru + + diff --git a/doc/markup.txt b/doc/markup.txt new file mode 100644 index 0000000..19ca644 --- /dev/null +++ b/doc/markup.txt @@ -0,0 +1,29 @@ +All texts must conform to the following standards: + +They must provide a valid XML is wrapped with this template: + + + + + THE TEXT ITSELF + + + +Text can be formatted with the following tags: +TEXT - provides normal text (clears all formatting) +TEXT - provides bold text +TEXT - provides underlined text +TEXT - provides coloured text in the given colour, see below for exact tag names + +Possible values for COLOUR: +* black +* red +* green +* yellow +* blue +* magenta +* cyan +* white + +Please note that although most MUD clients support the above text formatting, some older terminals not. +Also, for telnet-like clients, the text is wrapped into 78 columns after formatting has been applied. diff --git a/doc/reference/libwmud-session/Makefile.am b/doc/reference/libwmud-session/Makefile.am new file mode 100644 index 0000000..a1809fc --- /dev/null +++ b/doc/reference/libwmud-session/Makefile.am @@ -0,0 +1,102 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=wmud-session + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +DOC_MODULE_VERSION=1 + + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=$(top_srcdir)/libwmud-session + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB= +CFILE_GLOB= + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS=$(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) +GTKDOC_LIBS=$(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) -L$(top_srcdir)/libwmud-session -lwmud-session-1.0 + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) && +#TESTS = $(GTKDOC_CHECK) +endif + +-include $(top_srcdir)/git.mk diff --git a/doc/reference/libwmud-session/wmud-session-docs.sgml b/doc/reference/libwmud-session/wmud-session-docs.sgml new file mode 100644 index 0000000..cca90bb --- /dev/null +++ b/doc/reference/libwmud-session/wmud-session-docs.sgml @@ -0,0 +1,32 @@ + + +]> + + + wmud-session Reference Manual + + for wmud-session [VERSION]. + The latest version of this documentation can be found on-line at + http://[SERVER]/wmud-session/. + + + + + [Insert title here] + + + + + Object Hierarchy + + + + API Index + + + + + diff --git a/doc/reference/libwmud-session/wmud-session-sections.txt b/doc/reference/libwmud-session/wmud-session-sections.txt new file mode 100644 index 0000000..d6f878f --- /dev/null +++ b/doc/reference/libwmud-session/wmud-session-sections.txt @@ -0,0 +1,19 @@ +
+wmud-session +wMUDSession +wMUDSession +wMUDSessionClass +wMUDSessionPrivate +wmud_session_new +wmud_session_new_with_connection +wmud_session_set_connection + +WMUD_SESSION +WMUD_IS_SESSION +WMUD_TYPE_SESSION +wmud_session_get_type +WMUD_SESSION_CLASS +WMUD_IS_SESSION_CLASS +WMUD_SESSION_GET_CLASS +
+ diff --git a/doc/reference/libwmud-world/Makefile.am b/doc/reference/libwmud-world/Makefile.am new file mode 100644 index 0000000..3121d79 --- /dev/null +++ b/doc/reference/libwmud-world/Makefile.am @@ -0,0 +1,102 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=wmud-world + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +DOC_MODULE_VERSION=1 + + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=$(top_srcdir)/libwmud-world + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB= +CFILE_GLOB= + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS=$(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) +GTKDOC_LIBS=$(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) -L$(top_srcdir)/libwmud-world -lwmud-world-1.0 + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) && +#TESTS = $(GTKDOC_CHECK) +endif + +-include $(top_srcdir)/git.mk diff --git a/doc/reference/libwmud-world/wmud-world-docs.sgml b/doc/reference/libwmud-world/wmud-world-docs.sgml new file mode 100644 index 0000000..d1f2a51 --- /dev/null +++ b/doc/reference/libwmud-world/wmud-world-docs.sgml @@ -0,0 +1,32 @@ + + +]> + + + wmud-world Reference Manual + + for wmud-world [VERSION]. + The latest version of this documentation can be found on-line at + http://[SERVER]/wmud-world/. + + + + + [Insert title here] + + + + + Object Hierarchy + + + + API Index + + + + + diff --git a/doc/reference/libwmud-world/wmud-world-sections.txt b/doc/reference/libwmud-world/wmud-world-sections.txt new file mode 100644 index 0000000..b6b9cec --- /dev/null +++ b/doc/reference/libwmud-world/wmud-world-sections.txt @@ -0,0 +1,17 @@ +
+wmud-world +wMUDWorld +wMUDWorld +wMUDWorldClass +wMUDWorldPrivate +wmud_world_new + +WMUD_WORLD +WMUD_IS_WORLD +WMUD_TYPE_WORLD +wmud_world_get_type +WMUD_WORLD_CLASS +WMUD_IS_WORLD_CLASS +WMUD_WORLD_GET_CLASS +
+ diff --git a/doc/world.xsd b/doc/world.xsd new file mode 100644 index 0000000..1e33fe8 --- /dev/null +++ b/doc/world.xsd @@ -0,0 +1 @@ + diff --git a/libwmud-protocol-telnet/Makefile.am b/libwmud-protocol-telnet/Makefile.am new file mode 100644 index 0000000..97d7e76 --- /dev/null +++ b/libwmud-protocol-telnet/Makefile.am @@ -0,0 +1,6 @@ +LIBWMUD_PROTOCOL_TELNET_VERSION=1:0:0 +lib_LTLIBRARIES = libwmud-protocol-telnet-1.0.la +libwmud_protocol_telnet_1_0_la_SOURCES = wmud-protocol-telnet.c +libwmud_protocol_telnet_1_0_la_CFLAGS = $(CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) -Wall -I../src -I../libwmud-session +libwmud_protocol_telnet_1_0_la_LIBADD = $(LIBS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) +libwmud_protocol_telnet_1_0_la_LDFLAGS = -version-info $(LIBWMUD_PROTOCOL_TELNET_VERSION) diff --git a/libwmud-protocol-telnet/wmud-protocol-telnet.c b/libwmud-protocol-telnet/wmud-protocol-telnet.c new file mode 100644 index 0000000..0dea161 --- /dev/null +++ b/libwmud-protocol-telnet/wmud-protocol-telnet.c @@ -0,0 +1,102 @@ +#include "wmud-module.h" + +gboolean +wmud_protocol_telnet_load(wMUDConfiguration *configuration) +{ + /* + GSList *temp; + wMUDConfigurationInterface *interface; + gchar *iface_name; + struct _wMUDIfaceFinder *finder; + + finder = g_new0(struct _wMUDIfaceFinder, 1); + + finder->type = WMUD_SESSION_TYPE_TELNET; + finder->name = iface_name = g_utf8_offset_to_pointer(group, 7); + Context; + temp = g_slist_find_custom(conf->interfaces, finder, _wmud_find_iface); + Context; + g_free(finder); + + if (temp == NULL) + { + Context; + interface = (wMUDConfigurationInterface *)g_new0(wMUDConfigurationInterface, 1); + interface->name = g_strdup(iface_name); + interface->type = WMUD_SESSION_TYPE_TELNET; + conf->interfaces = g_slist_append(conf->interfaces, interface); + } + else + { + Context; + interface = (wMUDConfigurationInterface *)(temp->data); + } + + if (g_utf8_collate("port", key) == 0) + { + guint64 portnumber; + gchar *endptr; + + portnumber = g_ascii_strtoull(value, &endptr, 10); + + if ((endptr != NULL) && (*endptr != 0)) + { + wmud_log_error("Error in configuration file. Value of port can only contain numbers in group [%s]!", group); + } + + if (interface->inetaddr == NULL) + { + interface->inetaddr = gnet_inetaddr_new("0.0.0.0", portnumber); + } + else + { + gnet_inetaddr_set_port(interface->inetaddr, (gint)portnumber); + } + + g_pattern_spec_free(group_ptn); + return TRUE; + } + + if (g_utf8_collate("address", key) == 0) + { + GInetAddr *temp = gnet_inetaddr_new(value, 0); + + if (interface->inetaddr) + { + gnet_inetaddr_set_port(temp, gnet_inetaddr_get_port(interface->inetaddr)); + gnet_inetaddr_unref(interface->inetaddr); + interface->inetaddr = NULL; + } + interface->inetaddr = temp; + + g_pattern_spec_free(group_ptn); + return TRUE; + } + + if (g_utf8_collate("timeout", key) == 0) + { + guint64 timeout; + gchar *endptr; + + timeout = g_ascii_strtoull(value, &endptr, 10); + + if ((endptr != NULL) && (*endptr != 0)) + { + wmud_log_error("Error in configuration file. Value of timeout can only contain numbers! in group [%s]", group); + } + + interface->timeout = (guint)timeout; + + g_pattern_spec_free(group_ptn); + return TRUE; + } + */ + + return TRUE; +} + +void +wmud_protocol_telnet_unload(void) +{ +} + diff --git a/libwmud-protocol-telnet/wmud-protocol-telnet.h b/libwmud-protocol-telnet/wmud-protocol-telnet.h new file mode 100644 index 0000000..e69de29 diff --git a/libwmud-session/Makefile.am b/libwmud-session/Makefile.am new file mode 100644 index 0000000..724f721 --- /dev/null +++ b/libwmud-session/Makefile.am @@ -0,0 +1,6 @@ +LIBWMUD_SESSION_VERSION=1:0:0 +lib_LTLIBRARIES = libwmud-session-1.0.la +libwmud_session_1_0_la_SOURCES = wmud-session.c +libwmud_session_1_0_la_CFLAGS = $(CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) -Wall +libwmud_session_1_0_la_LIBADD = $(LIBS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) +libwmud_session_1_0_la_LDFLAGS = -version-info $(LIBWMUD_SESSION_VERSION) diff --git a/libwmud-session/wmud-session.c b/libwmud-session/wmud-session.c new file mode 100644 index 0000000..2277135 --- /dev/null +++ b/libwmud-session/wmud-session.c @@ -0,0 +1,304 @@ +/* wMUDSession - wMUD Session handler object + * Copyright (C) 2010, Gergely Polonkai + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "wmud-session.h" + +/** SECTION:objects + * @short_description: wMUD Session object + * @title: wMUD Session handler object + * + * wMUDSession is an object to store session-related information in + * wMUD + */ + +/* --- macros --- */ +#define WMUD_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WMUD_TYPE_SESSION, wMUDSessionPrivate)) + +/* --- structures --- */ + +struct _wMUDSessionPrivate +{ + GConn *connection; + wMUDSessionType type; +}; + +/* --- signals --- */ + +/* --- properties --- */ +enum +{ + PROP_0, + PROP_CONNECTION, + PROP_TYPE +}; + +/* --- prototypes --- */ +static void wmud_session_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); +static void wmud_session_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); +static void wmud_session_dispose(GObject *object); +static void wmud_session_finalize(GObject *object); +static void wmud_session_class_init(wMUDSessionClass *klass); +static void wmud_session_init(wMUDSession *self); +wMUDSession *wmud_session_new(void); +wMUDSession *wmud_session_new_with_connection(GConn *connection); +GConn *wmud_session_get_connection(wMUDSession *session); +void wmud_session_set_connection(wMUDSession *session, GConn *connection); +wMUDSessionType wmud_session_get_session_type(wMUDSession *session); +void wmud_session_set_session_type(wMUDSession *session, wMUDSessionType type); + +/* --- variables --- */ + +/* --- functions --- */ + +G_DEFINE_TYPE(wMUDSession, wmud_session, G_TYPE_OBJECT); + +#define WMUD_TYPE_SESSION_TYPE wmud_session_type_get_type() +static GType +wmud_session_type_get_type(void) +{ + static GType wmud_session_type_type = 0; + static const GEnumValue wmud_session_types[] = { + { WMUD_SESSION_TYPE_UNKNOWN, "Unknown session type", "unknown" }, + { WMUD_SESSION_TYPE_TELNET, "Telnet session", "telnet" }, + { 0, NULL, NULL } + }; + + if (!wmud_session_type_type) + { + wmud_session_type_type = g_enum_register_static("wMUDSessionType", wmud_session_types); + } + + return wmud_session_type_type; +} + +static void +wmud_session_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + wMUDSession *self = WMUD_SESSION(object); + + switch (property_id) + { + case PROP_CONNECTION: + if (self->priv->connection) + { + gnet_conn_unref(self->priv->connection); + } + self->priv->connection = (GConn *)g_value_get_pointer(value); + if (self->priv->connection) + { + gnet_conn_ref(self->priv->connection); + } + break; + case PROP_TYPE: + self->priv->type = g_value_get_enum(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +wmud_session_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + wMUDSession *self = WMUD_SESSION(object); + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_pointer(value, self->priv->connection); + break; + case PROP_TYPE: + g_value_set_enum(value, self->priv->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +wmud_session_dispose(GObject *object) +{ + G_OBJECT_CLASS(wmud_session_parent_class)->dispose(object); +} + +static void +wmud_session_finalize(GObject *object) +{ + wMUDSession *self = WMUD_SESSION(object); + + if (self->priv->connection) + { + gnet_conn_disconnect(self->priv->connection); + gnet_conn_unref(self->priv->connection); + self->priv->connection = NULL; + } + + G_OBJECT_CLASS(wmud_session_parent_class)->finalize(object); +} + +static void +wmud_session_class_init(wMUDSessionClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GParamSpec *wmud_session_param_spec; + + gobject_class->set_property = wmud_session_set_property; + gobject_class->get_property = wmud_session_get_property; + gobject_class->dispose = wmud_session_dispose; + gobject_class->finalize = wmud_session_finalize; + + g_type_class_add_private(klass, sizeof(wMUDSessionPrivate)); + + wmud_session_param_spec = g_param_spec_pointer("connection", "Connection handle", "GConn * handle of the connection", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property(gobject_class, PROP_CONNECTION, wmud_session_param_spec); + + wmud_session_param_spec = g_param_spec_enum("type", "SessionType", "Type of the session's connection", WMUD_TYPE_SESSION_TYPE, WMUD_SESSION_TYPE_UNKNOWN, G_PARAM_READWRITE); + g_object_class_install_property(gobject_class, PROP_TYPE, wmud_session_param_spec); +} + +static void +wmud_session_init(wMUDSession *self) +{ + wMUDSessionPrivate *priv; + + self->priv = priv = WMUD_SESSION_GET_PRIVATE(self); + + priv->connection = NULL; +} + +/** + * wmud_session_new: + * + * Creates a new wMUDSession object + * + * Returns: a new instance of wMUDSession + */ +wMUDSession * +wmud_session_new(void) +{ + wMUDSession *new_session = g_object_new(WMUD_TYPE_SESSION, NULL); + + return new_session; +} + +/** + * wmud_session_new_with_connection: + * @connection: the connection this session is bound to. This object is + * g_unref()'d when the Session object is disposed + * + * Returns: a new instance of wMUDSession with the connection property set + */ +wMUDSession * +wmud_session_new_with_connection(GConn *connection) +{ + wMUDSession *new_session = g_object_new(WMUD_TYPE_SESSION, "connection", connection, NULL); + wmud_session_set_connection(new_session, connection); + + return new_session; +} + +GConn * +wmud_session_get_connection(wMUDSession *session) +{ + GConn *connection = NULL; + GValue value = {0}; + + g_value_init(&value, G_TYPE_POINTER); + + wmud_session_get_property(G_OBJECT(session), PROP_CONNECTION, &value, NULL); + + connection = g_value_get_pointer(&value); + + g_value_unset(&value); + + return connection; +} + +/** + * wmud_session_set_connection: + * @session: the object which should receive the new connection + * @connection: the connection this session should be bound to. This object is g_unref()'d when the Session object is disposed + */ +void +wmud_session_set_connection(wMUDSession *session, GConn *connection) +{ + GValue value = {0}; + + g_value_init(&value, G_TYPE_POINTER); + g_value_set_pointer(&value, (gpointer)connection); + + wmud_session_set_property(G_OBJECT(session), PROP_CONNECTION, &value, NULL); + + g_value_unset(&value); +} + +/** + * wmud_session_has_connection: + * @session: the object which should be inspected + * @connection: the connection we are looking for + * + * Checks if the given #wMUDSession has the given #GConn connection + * + * Returns: %TRUE, if the connection is owned by this session, %FALSE otherwise + */ +gboolean +wmud_session_has_connection(wMUDSession *session, GConn *connection) +{ + if (session->priv->connection == connection) + return TRUE; + + return FALSE; +} + +wMUDSessionType +wmud_session_get_session_type(wMUDSession *session) +{ + GValue value = {0}; + g_value_init(&value, WMUD_TYPE_SESSION_TYPE); + + wmud_session_get_property(G_OBJECT(session), PROP_TYPE, &value, NULL); + + return g_value_get_enum(&value); +} + +void +wmud_session_set_session_type(wMUDSession *session, wMUDSessionType type) +{ + GValue value = {0, }; + g_value_init(&value, WMUD_TYPE_SESSION_TYPE); + + g_value_set_enum(&value, type); + wmud_session_set_property(G_OBJECT(session), PROP_TYPE, &value, NULL); +} + +wMUDSession * +wmud_session_ref(wMUDSession *session) +{ + return WMUD_SESSION(g_object_ref(G_OBJECT(session))); +} + +void +wmud_session_unref(wMUDSession *session) +{ + g_object_unref(G_OBJECT(session)); +} diff --git a/libwmud-session/wmud-session.h b/libwmud-session/wmud-session.h new file mode 100644 index 0000000..b3e297c --- /dev/null +++ b/libwmud-session/wmud-session.h @@ -0,0 +1,53 @@ +#ifndef __WMUD_SESSION_H__ +#define __WMUD_SESSION_H__ + +#include +#include +#include + +#define WMUD_TYPE_SESSION wmud_session_get_type() +#define WMUD_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WMUD_TYPE_SESSION, wMUDSession)) +#define WMUD_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WMUD_TYPE_SESSION)) +#define WMUD_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WMUD_TYPE_SESSION, wMUDSessionClass)) +#define WMUD_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WMUD_TYPE_SESSION)) +#define WMUD_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WMUD_TYPE_SESSION, wMUDSessionClass)) + +typedef enum +{ + WMUD_SESSION_TYPE_UNKNOWN, + WMUD_SESSION_TYPE_TELNET, + WMUD_SESSION_TYPE_IRC, + WMUD_SESSION_TYPE_HTTP +} wMUDSessionType; + +typedef struct _wMUDSession wMUDSession; +typedef struct _wMUDSessionClass wMUDSessionClass; +typedef struct _wMUDSessionPrivate wMUDSessionPrivate; + +struct _wMUDSession +{ + GObject parent_object; + + wMUDSessionPrivate *priv; +}; + +struct _wMUDSessionClass +{ + GObjectClass parent_class; +}; + +GType wmud_session_get_type(void); + +wMUDSession *wmud_session_new(void); +wMUDSession *wmud_session_new_with_connection(GConn *connection); +gboolean wmud_session_has_connection(wMUDSession *session, GConn *connection); +wMUDSession *wmud_session_ref(wMUDSession *session); +void wmud_session_unref(wMUDSession *session); + +GConn *wmud_session_get_connection(wMUDSession *session); +void wmud_session_set_connection(wMUDSession *session, GConn *connection); +wMUDSessionType wmud_session_get_session_type(wMUDSession *session); +void wmud_session_set_session_type(wMUDSession *session, wMUDSessionType type); + +#endif /* __WMUD_SESSION_H__ */ + diff --git a/libwmud-session/wmud-session.pc.in b/libwmud-session/wmud-session.pc.in new file mode 100644 index 0000000..2fa8052 --- /dev/null +++ b/libwmud-session/wmud-session.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: wMUDSession +Description: Library to handle wMUD sessions +Requires: glib-2.0 gobject-2.0 +Version: PACKAGE_VERSION +Libs: -L${libdir} -lwmud-session-1.0 +Cflags: -I${includedir}/wmud-session -I${libdir}/wmud-session/include diff --git a/libwmud-state-sqlite3/Makefile.am b/libwmud-state-sqlite3/Makefile.am new file mode 100644 index 0000000..1966b0d --- /dev/null +++ b/libwmud-state-sqlite3/Makefile.am @@ -0,0 +1,6 @@ +LIBWMUD_STATE_SQLITE3_VERSION=1:0:0 +lib_LTLIBRARIES = libwmud-state-sqlite3-1.0.la +libwmud_state_sqlite3_1_0_la_SOURCES = wmud-state-sqlite3.c +libwmud_state_sqlite3_1_0_la_CFLAGS = $(CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) -Wall -I../src -I../libwmud-session +libwmud_state_sqlite3_1_0_la_LIBADD = $(LIBS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) +libwmud_state_sqlite3_1_0_la_LDFLAGS = -version-info $(LIBWMUD_STATE_SQLITE3_VERSION) diff --git a/libwmud-state-sqlite3/wmud-state-sqlite3.c b/libwmud-state-sqlite3/wmud-state-sqlite3.c new file mode 100644 index 0000000..ee1056d --- /dev/null +++ b/libwmud-state-sqlite3/wmud-state-sqlite3.c @@ -0,0 +1,91 @@ +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "module.h" + +static gchar *state_file = NULL; +static sqlite3 *statesave_connection = NULL; + +gboolean +wmud_statesave_sqlite3_is_statesave(void) +{ + return TRUE; +} + +static gint +_wmud_statesave_sqlite3_find_config_group(gconstpointer list_data, gconstpointer lookup_data) +{ + wMUDConfigurationGroup *group = (wMUDConfigurationGroup *)list_data; + gchar *name_to_find = (gchar *)lookup_data; + + Context return g_utf8_collate(group->name, name_to_find); +} + +static void +_wmud_statesave_sqlite3_parse_config(gpointer data, gpointer userdata) +{ + wMUDConfigurationValue *parameter = (wMUDConfigurationValue *)data; + + if (g_utf8_collate("state file", parameter->key) == 0) + { + state_file = g_strdup(parameter->value); + } +} + +gboolean +wmud_statesave_sqlite3_load(wMUDConfiguration *config) +{ + GSList *statesave_params; + wMUDConfigurationGroup *config_group; + + wmud_log_debug("Initializing SQLite3 state saving module..."); + + if (!sqlite3_threadsafe()) + { + wmud_log_error("SQLite3 library is not compiled in a thread-safe manner."); + return FALSE; + } + + Context statesave_params = g_slist_find_custom(config->statesave_parameters, "sqlite3", _wmud_statesave_sqlite3_find_config_group); + + if (statesave_params == NULL) + { + wmud_log_error("Cannot find group [statesave sqlite3] in configfile!"); + return FALSE; + } + + config_group = (wMUDConfigurationGroup *)(statesave_params->data); + g_slist_foreach(config_group->datalist, _wmud_statesave_sqlite3_parse_config, NULL); + + if (state_file == NULL) + { + wmud_log_error("Cannot find state file parameter in configuration file (should be under group [statesave sqlite3]"); + return FALSE; + } + + wmud_log_debug("Will save state into SQLite3 file %s", state_file); + + switch (sqlite3_open(state_file, &statesave_connection)) + { + case SQLITE_OK: + wmud_log_info("State file opened successfully"); + break; + default: + wmud_log_error("Unprocessed return value from sqlite3_open()!"); + return FALSE; + break; + } + + return TRUE; +} + +void +wmud_statesave_sqlite3_unload(void) +{ + sqlite3_close(statesave_connection); +} + diff --git a/libwmud-state-sqlite3/wmud-state-sqlite3.h b/libwmud-state-sqlite3/wmud-state-sqlite3.h new file mode 100644 index 0000000..e69de29 diff --git a/libwmud-world/Makefile.am b/libwmud-world/Makefile.am new file mode 100644 index 0000000..c9c21c6 --- /dev/null +++ b/libwmud-world/Makefile.am @@ -0,0 +1,4 @@ +lib_LTLIBRARIES = libwmud-world-1.0.la +libwmud_world_1_0_la_SOURCES = wmud-world.c +libwmud_world_1_0_la_CFLAGS = $(CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(GNET_CFLAGS) -Wall +libwmud_world_1_0_la_LIBADD = $(LIBS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(GNET_LIBS) diff --git a/libwmud-world/wmud-world.c b/libwmud-world/wmud-world.c new file mode 100644 index 0000000..cb6f13b --- /dev/null +++ b/libwmud-world/wmud-world.c @@ -0,0 +1,104 @@ +#include "wmud-world.h" + +#define WMUD_WORLD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WMUD_TYPE_WORLD, wMUDWorldPrivate)) + +struct _wMUDWorldPrivate +{ + gchar *name; +}; + +enum +{ + PROP_0, + PROP_NAME +}; + +G_DEFINE_TYPE(wMUDWorld, wmud_world, G_TYPE_OBJECT); + +static void +wmud_world_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + wMUDWorld *self = WMUD_WORLD(object); + + switch (property_id) + { + case PROP_NAME: + g_free(self->priv->name); + self->priv->name = g_value_dup_string(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +wmud_world_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + wMUDWorld *self = WMUD_WORLD(object); + + switch (property_id) + { + case PROP_NAME: + g_value_set_string(value, self->priv->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +wmud_world_dispose(GObject *object) +{ + G_OBJECT_CLASS(wmud_world_parent_class)->dispose(object); +} + +static void +wmud_world_finalize(GObject *object) +{ + wMUDWorld *self = WMUD_WORLD(object); + + if (self->priv->name) + { + g_free(self->priv->name); + } + + G_OBJECT_CLASS(wmud_world_parent_class)->finalize(object); +} + +static void +wmud_world_class_init(wMUDWorldClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GParamSpec *wmud_world_param_spec; + + gobject_class->set_property = wmud_world_set_property; + gobject_class->get_property = wmud_world_get_property; + gobject_class->dispose = wmud_world_dispose; + gobject_class->finalize = wmud_world_finalize; + + g_type_class_add_private(klass, sizeof(wMUDWorldPrivate)); + + wmud_world_param_spec = g_param_spec_string("name", "World name", "Set the name of the world", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property(gobject_class, PROP_NAME, wmud_world_param_spec); +} + +static void +wmud_world_init(wMUDWorld *self) +{ + wMUDWorldPrivate *priv; + + self->priv = priv = WMUD_WORLD_GET_PRIVATE(self); + + priv->name = NULL; +} + +wMUDWorld * +wmud_world_new(void) +{ + wMUDWorld *new_world = g_object_new(WMUD_TYPE_WORLD, NULL); + + return new_world; +} + diff --git a/libwmud-world/wmud-world.h b/libwmud-world/wmud-world.h new file mode 100644 index 0000000..a178726 --- /dev/null +++ b/libwmud-world/wmud-world.h @@ -0,0 +1,34 @@ +#ifndef __WMUD_WORLD_H__ +#define __WMUD_WORLD_H__ + +#include + +#define WMUD_TYPE_WORLD wmud_world_get_type() +#define WMUD_WORLD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WMUD_TYPE_WORLD, wMUDWorld)) +#define WMUD_IS_WORLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WMUD_TYPE_WORLD)) +#define WMUD_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WMUD_TYPE_WORLD, wMUDWorldClass)) +#define WMUD_IS_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WMUD_TYPE_WORLD)) +#define WMUD_WORLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WMUD_TYPE_WORLD, wMUDWorldClass)) + +typedef struct _wMUDWorld wMUDWorld; +typedef struct _wMUDWorldClass wMUDWorldClass; +typedef struct _wMUDWorldPrivate wMUDWorldPrivate; + +struct _wMUDWorld +{ + GObject parent_object; + + wMUDWorldPrivate *priv; +}; + +struct _wMUDWorldClass +{ + GObjectClass parent_class; +}; + +GType wmud_world_get_type(void); + +wMUDWorld *wmud_world_new(void); + +#endif /* __WMUD_WORLD_H__ */ + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..f85727d --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ +bin_PROGRAMS = wmud +wmud_SOURCES = wmud.c logger.c ansi.c networking.c world.c sessions.c configfile.c commands.c chat.c irc.c modules.c +wmud_CFLAGS = $(CFLAGS) $(EXTLIBS_CFLAGS) -I../libwmud-session -I../libwmud-world -Wall +wmud_LDADD = $(LIBS) $(EXTLIBS_LIBS) -L../libwmud-session -lwmud-session-1.0 -L../libwmud-world -lwmud-world-1.0 diff --git a/src/ansi.c b/src/ansi.c new file mode 100644 index 0000000..e73e0a3 --- /dev/null +++ b/src/ansi.c @@ -0,0 +1,104 @@ +#include +#include + +#include "ansi.h" + +typedef struct _wmud_tag_to_ansi { + gchar *open_tag; + gint open_tag_length; + gchar *close_tag; + gint close_tag_length; + gchar *ansi_sequence; +} wmud_tag_to_ansi; + +static wmud_tag_to_ansi tags[] = { + { "", 6, "", 7, ANSI_BOLD }, + { "", 11, "", 12, ANSI_UNDERLINE }, + { "", 8, "", 9, ANSI_NORMAL }, + { "", 7, "", 8, ANSI_COLOR_BLACK }, + { "", 5, "", 6, ANSI_COLOR_RED }, + { "", 7, "", 8, ANSI_COLOR_GREEN }, + { "", 8, "", 9, ANSI_COLOR_YELLOW }, + { "", 6, "", 7, ANSI_COLOR_BLUE }, + { "", 9, "", 10, ANSI_COLOR_MAGENTA }, + { "", 6, "", 7, ANSI_COLOR_CYAN }, + { "", 7, "", 8, ANSI_COLOR_WHITE }, +}; + +static void +_str_append_string(gchar **dest, gchar *src) +{ + gchar *temp; + + temp = g_strdup(*dest); + *dest = g_strdup_printf("%s%s", temp, src); + g_free(temp); +} + +static void +_str_append_char(gchar **dest, gchar src) +{ + gchar *temp; + + temp = g_strdup(*dest); + *dest = g_strdup_printf("%s%c", temp, src); + g_free(temp); +} + +static void +_add_remaining_formatters(gpointer element, gpointer data) +{ + + _str_append_string(data, (gchar *)element); +} + +gboolean +wmud_text_to_ansi(gchar *text, gchar **result) +{ + gchar *a; + GSList *current_formatters = NULL; + gchar *r = g_strdup(""); + + for (a = text; *a; a++) + { + gint i; + gboolean found = FALSE; + + for (i = 0; i < sizeof(tags) / sizeof(wmud_tag_to_ansi); i++) + { + if (g_strncasecmp(a, tags[i].open_tag, tags[i].open_tag_length) == 0) + { + _str_append_string(&r, tags[i].ansi_sequence); + + current_formatters = g_slist_append(current_formatters, tags[i].ansi_sequence); + a += tags[i].open_tag_length - 1; + found = TRUE; + break; + } + if (g_strncasecmp(a, tags[i].close_tag, tags[i].close_tag_length) == 0) + { + _str_append_string(&r, ANSI_NORMAL); + + current_formatters = g_slist_delete_link(current_formatters, g_slist_last(current_formatters)); + g_slist_foreach(current_formatters, _add_remaining_formatters, &r); + a += tags[i].close_tag_length - 1; + found = TRUE; + break; + } + } + + if (found) + { + continue; + } + + _str_append_char(&r, *a); + } + + _str_append_string(&r, ANSI_NORMAL); + + *result = r; + + return TRUE; +} + diff --git a/src/ansi.h b/src/ansi.h new file mode 100644 index 0000000..4f0374d --- /dev/null +++ b/src/ansi.h @@ -0,0 +1,20 @@ +#ifndef __WMUD_CORE_ANSI_H__ +#define __WMUD_CORE_ANSI_H__ + +#define ANSI_CSI "\x1b[" +#define ANSI_NORMAL ANSI_CSI "0m" +#define ANSI_BOLD ANSI_CSI "1m" +#define ANSI_UNDERLINE ANSI_CSI "4m" +#define ANSI_COLOR_BLACK ANSI_CSI "30m" +#define ANSI_COLOR_RED ANSI_CSI "31m" +#define ANSI_COLOR_GREEN ANSI_CSI "32m" +#define ANSI_COLOR_YELLOW ANSI_CSI "33m" +#define ANSI_COLOR_BLUE ANSI_CSI "34m" +#define ANSI_COLOR_MAGENTA ANSI_CSI "35m" +#define ANSI_COLOR_CYAN ANSI_CSI "36m" +#define ANSI_COLOR_WHITE ANSI_CSI "37m" + +gboolean wmud_text_to_ansi(gchar *text, gchar **result); + +#endif /* __WMUD_CODE__ANSI_H__ */ + diff --git a/src/chat.c b/src/chat.c new file mode 100644 index 0000000..b9891bd --- /dev/null +++ b/src/chat.c @@ -0,0 +1,42 @@ +#include + +#include "wmud.h" +#include "logger.h" + +gpointer +wmud_chat_thread(gpointer data) +{ + wMUDThreadData *thread_data = (wMUDThreadData *)data; + + Context wmud_log_info("Initializing chat layer..."); + /* g_main_context_get_thread_default() is only available since GLib 2.22; + * use g_main_context_new() otherwise + */ +#if (((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION >= 22)) || (GLIB_MAJOR_VERSION > 2)) + thread_data->main_context = g_main_context_get_thread_default(); +#else + thread_data->main_context = g_main_context_new(); +#endif + thread_data->main_loop = g_main_loop_new(thread_data->main_context, FALSE); + + /* Do the real initialization work here */ + + /* End of initialization */ + + wmud_log_info("Chat layer initialized"); + + Context; + thread_data->running = TRUE; + g_main_loop_run(thread_data->main_loop); + + wmud_log_info("Chat layer shutting down"); + + g_main_loop_unref(thread_data->main_loop); + thread_data->main_loop = NULL; + + thread_data->running = FALSE; + + Context; + + return NULL; +} diff --git a/src/chat.h b/src/chat.h new file mode 100644 index 0000000..def07f8 --- /dev/null +++ b/src/chat.h @@ -0,0 +1,7 @@ +#ifndef __WMUD_CORE_CHAT_H__ +#define __WMUD_CORE_CHAT_H__ + +gpointer wmud_chat_thread(gpointer data); + +#endif /* __WMUD_CORE_CHAT_H__ */ + diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..532bade --- /dev/null +++ b/src/commands.c @@ -0,0 +1,265 @@ +#include +#include + +#include + +#include "wmud.h" +#include "logger.h" +#include "networking.h" +#include "sessions.h" + +#include "wmud-session.h" + +#define COMMAND_DELIMITERS " \t\n\r" + +#define WMUD_COMMAND_ERROR g_quark_from_string("wMUDCommandError") +enum { + WMUD_COMMAND_ERROR_SUCCESS /* Will never be used */ +}; + +/* Internal command callback function for prototypes and function headers. If + * you modify this, don't forget to modify the wMUDCommandCallback type! + */ +#define WMUD_INTERNAL_COMMAND(x) gboolean x (wMUDSession *session, gchar **params, GError **error) + +/* Internal command callback function type to be usd in the wMUDCommand struct. + * If you modify this, don't forget to modify the WMUD_INTERNAL_COMMAND(x) + * macro! + */ +typedef gboolean (*wMUDCommandCallback)(wMUDSession *session, gchar **params, GError **error); + +typedef struct _wMUDCommand +{ + gchar *cmd; + gchar *collate_key; + gint min_params; + gint max_params; + wMUDCommandCallback callback; +} wMUDCommand; + +struct _wMUDSplitHelper +{ + gchar **list; + gint position; +}; + +WMUD_INTERNAL_COMMAND(_wmud_command_internal_quit); + +static wMUDCommand _wmud_command_list[] = { + { "QUIT", NULL, 0, 0, _wmud_command_internal_quit }, + { NULL, NULL, 0, 0, NULL } +}; + +WMUD_INTERNAL_COMMAND(_wmud_command_internal_quit) +{ + wmud_log_debug("Here in the QUIT function"); + wmud_finish_session(session, "Good bye"); + return TRUE; +} + +static void +_add_words(gpointer data, gpointer user_data) +{ + struct _wMUDSplitHelper *split_data = (struct _wMUDSplitHelper *)user_data; + gchar *word = (gchar *)data; + + Context split_data->list[split_data->position] = word; + split_data->position++; +} + +static void +_free_words(gpointer data, gpointer user_data) +{ + g_free(data); +} + +static gint +_wmud_split_command(gchar *command, gchar ***list) +{ + gchar *a; + gint word_started_at = 0; + gint apos_at = -1; + gint quot_at = -1; + gboolean was_in_word = FALSE; + gboolean in_string = TRUE; + GSList *word_list = NULL; + gchar **ret; + struct _wMUDSplitHelper split_data; + gint word_count; + + for (a = command; in_string; a++) + { + if (*a == 0) + { + in_string = FALSE; + } + if ( + (*a == 0) + || ( + (quot_at == -1) + && (*a == '\'') + ) + || ( + (apos_at == -1) + && (*a == '"') + ) + || ( + (apos_at != -1) + && (*a == '\'') + ) + || ( + (quot_at != -1) + && (*a == '"') + ) + || ( + (apos_at == -1) + && (quot_at == -1) + && strchr(COMMAND_DELIMITERS, *a) + ) + ) + { + if (was_in_word) + { + was_in_word = FALSE; + word_list = g_slist_append(word_list, g_strndup((command + word_started_at), (a - command) - word_started_at)); + word_started_at = (a - command); + } + if ((apos_at == -1) && (*a == '"')) + { + if (quot_at == -1) + { + quot_at = (a - command) - 1; + if (quot_at == -1) + { + quot_at = 0; + } + } + else + { + quot_at = -1; + } + } + if ((quot_at == -1) && (*a == '\'')) + { + if (apos_at == -1) + { + apos_at = (a - command) - 1; + if (apos_at == -1) + { + apos_at = 0; + } + } + else + { + apos_at = -1; + } + } + word_started_at++; + continue; + } + was_in_word = TRUE; + } + + if ((quot_at != -1) || (apos_at != -1)) + { + wmud_log_debug("Got illegal string: %s", command); + + g_slist_foreach(word_list, _free_words, NULL); + g_slist_free(word_list); + + return -1; + } + + word_count = g_slist_length(word_list); + Context ret = g_new0(gchar *, word_count + 1); + + split_data.list = ret; + split_data.position = 0; + + Context g_slist_foreach(word_list, _add_words, &split_data); + g_slist_free(word_list); + + if (list != NULL) + { + Context *list = ret; + } + + return word_count; +} + +void +wmud_process_command(GConn *connection, gchar *command) +{ + gchar **command_parts = NULL, + *command_casefold, + *command_key; + gint word_count; + wMUDCommand *command_rec; + gboolean command_found = FALSE; + wMUDSession *session; + + session = wmud_session_for_connection(connection); + + if (session == NULL) + { + wmud_log_error("Processing command for a non-existant session!"); + return; + } + + Context; + wmud_log_debug("Processing command %s", command); + + Context word_count = _wmud_split_command(command, &command_parts); + + if (word_count == -1) + { + Context wmud_connection_send(connection, "Illegal command. Maybe you forgot a closing apostroph or quote?"); + Context return; + } + + if (word_count == 0) + { + wmud_log_debug("Got an empty line"); + return; + } + + command_casefold = g_utf8_casefold(command_parts[0], -1); + command_key = g_utf8_collate_key(command_casefold, -1); + g_free(command_casefold); + for (command_rec = _wmud_command_list; command_rec->cmd; command_rec++) + { + if (command_rec->collate_key == NULL) + { + gchar *temp; + + temp = g_utf8_casefold(command_rec->cmd, -1); + command_rec->collate_key = g_utf8_collate_key(temp, -1); + g_free(temp); + } + + if (strcmp(command_key, command_rec->collate_key) == 0) + { + gint param_count = g_strv_length(command_parts) - 1; + GError *error = NULL; + command_found = TRUE; + + if ((param_count < command_rec->min_params) || (param_count > command_rec->max_params)) + { + Context wmud_connection_send(connection, "Wrong number of parameters. Maybe you should try HELP %s", command_rec->cmd); + } + wmud_log_debug("Executing command %s, having %d parameters", command_rec->cmd, param_count); + (command_rec->callback)(session, command_parts + 1, &error); + break; + } + } + g_free(command_key); + + if (!command_found) + { + /* TODO: command prediction (Maybe you wanted to type...) */ + Context wmud_connection_send(connection, "Unknown command."); + } + + Context g_strfreev(command_parts); +} + diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 0000000..6f91b35 --- /dev/null +++ b/src/commands.h @@ -0,0 +1,9 @@ +#ifndef __WMUD_CORE_COMMANDS_H +#define __WMUD_CORE_COMMANDS_H + +#include + +void wmud_process_command(GConn *connection, gchar *command); + +#endif /* __WMUD_CORE_COMMANDS_H */ + diff --git a/src/configfile-module.h b/src/configfile-module.h new file mode 100644 index 0000000..550fbd6 --- /dev/null +++ b/src/configfile-module.h @@ -0,0 +1,75 @@ +#ifndef __WMUD_MODULE_CONFIGFILE_H__ +#define __WMUD_MODULE_CONFIGFILE_H__ + +#include "wmud-session.h" + +typedef enum { + WMUD_CONF_LOG_NONE, + WMUD_CONF_LOG_SYSLOG, + WMUD_CONF_LOG_FILE, + WMUD_CONF_LOG_CONSOLE +} wMUDConfigurationLogging; + +typedef enum { + WMUD_CONF_DAEMONIZE_YES, + WMUD_CONF_DAEMONIZE_NO, + WMUD_CONF_DAEMONIZE_FORCE +} wMUDConfigurationDaemonize; + +typedef struct { + /* A GSList of _wMUDConfigurationInterfaces */ + GSList *interfaces; + + wMUDConfigurationLogging log_dest; + gchar *logfile; + gboolean log_found; + + wMUDConfigurationLogging debug_log_dest; + gchar *debug_logfile; + gboolean debug_log_found; + + wMUDConfigurationLogging info_log_dest; + gchar *info_logfile; + gboolean info_log_found; + + wMUDConfigurationLogging warning_log_dest; + gchar *warning_logfile; + gboolean warning_log_found; + + wMUDConfigurationLogging error_log_dest; + gchar *error_logfile; + gboolean error_log_found; + + wMUDConfigurationDaemonize daemonize; + + gboolean chat_enabled; + gboolean dbus_enabled; + + gchar *modules_dir; + + gchar *statesave_module; + gchar **protocol_modules; + + GSList *statesave_parameters; + GSList *protocol_parameters; +} wMUDConfiguration; + +typedef struct { + wMUDSessionType type; + gchar *name; + GInetAddr *inetaddr; + guint timeout; +} wMUDConfigurationInterface; + +typedef struct { + gchar *name; + GSList *datalist; +} wMUDConfigurationGroup; + +typedef struct { + gchar *key; + gchar *value; +} wMUDConfigurationValue; + +#endif /* __WMUD_MODULE_CONFIGFILE_H__ */ + diff --git a/src/configfile.c b/src/configfile.c new file mode 100644 index 0000000..a018c26 --- /dev/null +++ b/src/configfile.c @@ -0,0 +1,901 @@ +#include +#include +#include + +#include "wmud.h" +#include "configfile.h" +#include "logger.h" + +struct _wMUDIfaceFinder { + wMUDSessionType type; + gchar *name; +}; + +static gint +_wmud_find_keyed_member(gconstpointer this_group, gconstpointer group_to_find) +{ + wMUDConfigurationGroup *group_data = (wMUDConfigurationGroup *)this_group; + gchar *name = (gchar *)group_to_find; + + return g_utf8_collate(group_data->name, name); +} + +static gint +_wmud_find_iface(gconstpointer iface, gconstpointer iffinder) +{ + wMUDConfigurationInterface *interface = (wMUDConfigurationInterface *)iface; + gchar *name_to_find = ((struct _wMUDIfaceFinder *)iffinder)->name; + wMUDSessionType type = ((struct _wMUDIfaceFinder *)iffinder)->type; + + return ((g_utf8_collate(interface->name, name_to_find) == 0) && (interface->type == type)) ? 0 : 1; +} + +gboolean +_wmud_config_process_value(wMUDConfiguration *conf, gchar *group, gchar *key, gchar *value) +{ + GPatternSpec *group_ptn; + + if (g_utf8_collate("general", group) == 0) + { + if ( + (g_utf8_collate("log", key) == 0) + || (g_utf8_collate("debug log", key) == 0) + || (g_utf8_collate("info log", key) == 0) + || (g_utf8_collate("warning log", key) == 0) + || (g_utf8_collate("error log", key) == 0) + ) + { + if (g_utf8_collate("none", value) == 0) + { + if (g_utf8_collate("log", key) == 0) + { + conf->log_found = TRUE; + conf->log_dest = WMUD_CONF_LOG_NONE; + if (conf->logfile) + { + g_free(conf->logfile); + conf->logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("debug log", key) == 0) + { + conf->debug_log_found = TRUE; + conf->debug_log_dest = WMUD_CONF_LOG_NONE; + if (conf->debug_logfile) + { + g_free(conf->debug_logfile); + conf->debug_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("info log", key) == 0) + { + conf->info_log_found = TRUE; + conf->info_log_dest = WMUD_CONF_LOG_NONE; + if (conf->info_logfile) + { + g_free(conf->info_logfile); + conf->info_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("warning log", key) == 0) + { + conf->warning_log_found = TRUE; + conf->warning_log_dest = WMUD_CONF_LOG_NONE; + if (conf->warning_logfile) + { + g_free(conf->warning_logfile); + conf->warning_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("error log", key) == 0) + { + conf->error_log_found = TRUE; + conf->error_log_dest = WMUD_CONF_LOG_NONE; + if (conf->error_logfile) + { + g_free(conf->error_logfile); + conf->error_logfile = NULL; + } + return TRUE; + } + } + if (strncmp("file:", value, 5) == 0) + { + if (g_utf8_collate("log", key) == 0) + { + gchar *temp_file; + wMUDConfigurationLogging temp_logging; + gboolean temp_found; + + temp_logging = conf->log_dest; + temp_file = conf->logfile; + temp_found = conf->log_found; + + conf->logfile = g_strdup(value + 5); + if (!*(conf->logfile)) + { + wmud_log_error("Log file name must contain a string for %s/%s!", group, key); + conf->logfile = temp_file; + return FALSE; + } + if (temp_file) + { + g_free(temp_file); + } + conf->log_dest = WMUD_CONF_LOG_FILE; + conf->log_found = TRUE; + return TRUE; + } + else if (g_utf8_collate("debug log", key) == 0) + { + gchar *temp_file; + wMUDConfigurationLogging temp_logging; + gboolean temp_found; + + temp_logging = conf->debug_log_dest; + temp_file = conf->debug_logfile; + temp_found = conf->debug_log_found; + + conf->debug_logfile = g_strdup(value + 5); + if (!*(conf->debug_logfile)) + { + wmud_log_error("Debug log file name must contain a string for %s/%s!", group, key); + conf->debug_logfile = temp_file; + return FALSE; + } + if (temp_file) + { + g_free(temp_file); + } + conf->debug_log_dest = WMUD_CONF_LOG_FILE; + conf->debug_log_found = TRUE; + return TRUE; + } + else if (g_utf8_collate("info log", key) == 0) + { + gchar *temp_file; + wMUDConfigurationLogging temp_logging; + gboolean temp_found; + + temp_logging = conf->info_log_dest; + temp_file = conf->info_logfile; + temp_found = conf->info_log_found; + + conf->info_logfile = g_strdup(value + 5); + if (!*(conf->info_logfile)) + { + wmud_log_error("Info log file name must contain a string for %s/%s!", group, key); + conf->info_log_dest = temp_logging; + conf->info_logfile = temp_file; + conf->info_log_found = temp_found; + return FALSE; + } + if (temp_file) + { + g_free(temp_file); + } + conf->info_log_dest = WMUD_CONF_LOG_FILE; + conf->info_log_found = TRUE; + return TRUE; + } + else if (g_utf8_collate("warning log", key) == 0) + { + gchar *temp_file; + wMUDConfigurationLogging temp_logging; + gboolean temp_found; + + temp_logging = conf->warning_log_dest; + temp_file = conf->warning_logfile; + temp_found = conf->warning_log_found; + + conf->warning_logfile = g_strdup(value + 5); + if (!*(conf->warning_logfile)) + { + wmud_log_error("Warning log file name must contain a string for %s/%s!", group, key); + conf->warning_logfile = temp_file; + return FALSE; + } + if (temp_file) + { + g_free(temp_file); + } + conf->warning_log_dest = WMUD_CONF_LOG_FILE; + conf->warning_log_found = TRUE; + return TRUE; + } + else if (g_utf8_collate("error log", key) == 0) + { + gchar *temp_file; + wMUDConfigurationLogging temp_logging; + gboolean temp_found; + + temp_logging = conf->error_log_dest; + temp_file = conf->error_logfile; + temp_found = conf->error_log_found; + + conf->error_logfile = g_strdup(value + 5); + if (!*(conf->error_logfile)) + { + wmud_log_error("Error log file name must contain a string for %s/%s!", group, key); + conf->error_logfile = temp_file; + return FALSE; + } + if (temp_file) + { + g_free(temp_file); + } + conf->error_log_dest = WMUD_CONF_LOG_FILE; + conf->error_log_found = TRUE; + return TRUE; + } + } + if ( + (g_utf8_collate("syslog", value) == 0) + || ( + (conf->daemonize == WMUD_CONF_DAEMONIZE_FORCE) + && (g_utf8_collate("console", value) == 0) + ) + ) + { + if (g_utf8_collate("log", key) == 0) + { + conf->log_dest = WMUD_CONF_LOG_SYSLOG; + conf->log_found = TRUE; + if (conf->logfile) + { + g_free(conf->logfile); + conf->logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("debug log", key) == 0) + { + conf->debug_log_dest = WMUD_CONF_LOG_SYSLOG; + conf->debug_log_found = TRUE; + if (conf->debug_logfile) + { + g_free(conf->debug_logfile); + conf->debug_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("info log", key) == 0) + { + conf->info_log_dest = WMUD_CONF_LOG_SYSLOG; + conf->info_log_found = TRUE; + if (conf->info_logfile) + { + g_free(conf->info_logfile); + conf->info_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("warning log", key) == 0) + { + conf->warning_log_dest = WMUD_CONF_LOG_SYSLOG; + conf->warning_log_found = TRUE; + if (conf->warning_logfile) + { + g_free(conf->warning_logfile); + conf->warning_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("error log", key) == 0) + { + conf->error_log_dest = WMUD_CONF_LOG_SYSLOG; + conf->error_log_found = TRUE; + if (conf->error_logfile) + { + g_free(conf->error_logfile); + conf->error_logfile = NULL; + } + return TRUE; + } + } + if ((g_utf8_collate("console", value) == 0) && (conf->daemonize != WMUD_CONF_DAEMONIZE_FORCE)) + { + if (g_utf8_collate("log", key) == 0) + { + conf->log_dest = WMUD_CONF_LOG_CONSOLE; + conf->log_found = TRUE; + if (conf->logfile) + { + g_free(conf->logfile); + conf->logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("debug log", key) == 0) + { + conf->debug_log_dest = WMUD_CONF_LOG_CONSOLE; + conf->debug_log_found = TRUE; + if (conf->debug_logfile) + { + g_free(conf->debug_logfile); + conf->debug_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("info log", key) == 0) + { + conf->info_log_dest = WMUD_CONF_LOG_CONSOLE; + conf->info_log_found = TRUE; + if (conf->info_logfile) + { + g_free(conf->info_logfile); + conf->info_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("warning log", key) == 0) + { + conf->warning_log_dest = WMUD_CONF_LOG_CONSOLE; + conf->warning_log_found = TRUE; + if (conf->warning_logfile) + { + g_free(conf->warning_logfile); + conf->warning_logfile = NULL; + } + return TRUE; + } + else if (g_utf8_collate("error log", key) == 0) + { + conf->error_log_dest = WMUD_CONF_LOG_CONSOLE; + conf->error_log_found = TRUE; + if (conf->error_logfile) + { + g_free(conf->error_logfile); + conf->error_logfile = NULL; + } + return TRUE; + } + } + wmud_log_error("Found the log option with an unknown value of %s", value); + return FALSE; + } + + if (g_utf8_collate("daemonize", key) == 0) + { + if (g_utf8_collate("yes", value) == 0) + { + conf->daemonize = WMUD_CONF_DAEMONIZE_YES; + return TRUE; + } + else if (g_utf8_collate("no", value) == 0) + { + conf->daemonize = WMUD_CONF_DAEMONIZE_NO; + return TRUE; + } + else if (g_utf8_collate("force", value) == 0) + { + conf->daemonize = WMUD_CONF_DAEMONIZE_FORCE; + return TRUE; + } + wmud_log_error("Daemonize must have the values yes, no or force"); + return FALSE; + } + + if (g_utf8_collate("chat", key) == 0) + { + if (g_utf8_collate("yes", value) == 0) + { + conf->chat_enabled = TRUE; + return TRUE; + } + else if (g_utf8_collate("no", value) == 0) + { + conf->chat_enabled = FALSE; + return TRUE; + } + wmud_log_error("Chat must have the value yes or no"); + return FALSE; + } + + if (g_utf8_collate("dbus", key) == 0) + { + if (g_utf8_collate("yes", value) == 0) + { + conf->dbus_enabled = TRUE; + return TRUE; + } + else if (g_utf8_collate("no", value) == 0) + { + conf->dbus_enabled = TRUE; + return TRUE; + } + wmud_log_error("DBus must have the value yes or no"); + return FALSE; + } + + wmud_log_error("Unknown key (%s) found in configuration file in group [%s]", key, group); + return FALSE; + } + + if (g_utf8_collate("modules", group) == 0) + { + if (g_utf8_collate("modules dir", key) == 0) + { + if (conf->modules_dir) + { + g_free(conf->modules_dir); + } + conf->modules_dir = g_strdup(value); + return TRUE; + } + + if (g_utf8_collate("statesave", key) == 0) + { + if (conf->statesave_module) + { + g_free(conf->statesave_module); + } + conf->statesave_module = g_strdup(value); + return TRUE; + } + + if (g_utf8_collate("protocol", key) == 0) + { + if (conf->protocol_modules) + { + g_strfreev(conf->protocol_modules); + } + + conf->protocol_modules = g_strsplit(value, ":", 0); + return TRUE; + } + + wmud_log_error("Unknown key (%s) found in configuration file in group [%s]", key, group); + return FALSE; + } + + group_ptn = g_pattern_spec_new("statesave *"); + if (g_pattern_match(group_ptn, strlen(group), group, NULL)) + { + GSList *statesave_member; + wMUDConfigurationGroup *conf_group; + wMUDConfigurationValue *conf_data; + + statesave_member = g_slist_find_custom(conf->statesave_parameters, group + 10, _wmud_find_keyed_member); + if (statesave_member == NULL) + { + conf_group = g_new0(wMUDConfigurationGroup, 1); + conf_group->name = g_strdup(group + 10); + conf_group->datalist = NULL; + conf->statesave_parameters = g_slist_append(conf->statesave_parameters, conf_group); + } + else + { + conf_group = (wMUDConfigurationGroup *)(statesave_member->data); + } + + conf_data = g_new0(wMUDConfigurationValue, 1); + conf_data->key = g_strdup(key); + conf_data->value = g_strdup(value); + conf_group->datalist = g_slist_append(conf_group->datalist, conf_data); + + return TRUE; + } + g_pattern_spec_free(group_ptn); + + group_ptn = g_pattern_spec_new("protocol * *"); + if (g_pattern_match(group_ptn, strlen(group), group, NULL)) + { + GSList *protocol_member; + wMUDConfigurationGroup *conf_group; + wMUDConfigurationValue *conf_data; + + protocol_member = g_slist_find_custom(conf->protocol_parameters, group + 10, _wmud_find_keyed_member); + if (protocol_member == NULL) + { + conf_group = g_new0(wMUDConfigurationGroup, 1); + conf_group->name = g_strdup(group + 10); + conf_group->datalist = NULL; + conf->protocol_parameters = g_slist_append(conf->protocol_parameters, conf_group); + } + else + { + conf_group = (wMUDConfigurationGroup *)(protocol_member->data); + } + + conf_data = g_new0(wMUDConfigurationValue, 1); + conf_data->key = g_strdup(key); + conf_data->value = g_strdup(value); + conf_group->datalist = g_slist_append(conf_group->datalist, conf_data); + + return TRUE; + } + g_pattern_spec_free(group_ptn); + + group_ptn = g_pattern_spec_new("irc *"); + if (g_pattern_match(group_ptn, strlen(group), group, NULL)) + { + GSList *temp; + wMUDConfigurationInterface *interface; + gchar *iface_name; + struct _wMUDIfaceFinder *finder; + + finder = g_new0(struct _wMUDIfaceFinder, 1); + + finder->type = WMUD_SESSION_TYPE_IRC; + finder->name = iface_name = g_utf8_offset_to_pointer(group, 4); + Context; + temp = g_slist_find_custom(conf->interfaces, finder, _wmud_find_iface); + Context; + g_free(finder); + + if (temp == NULL) + { + Context; + interface = (wMUDConfigurationInterface *)g_new0(wMUDConfigurationInterface, 1); + interface->name = g_strdup(iface_name); + interface->type = WMUD_SESSION_TYPE_IRC; + conf->interfaces = g_slist_append(conf->interfaces, interface); + } + else + { + Context; + interface = (wMUDConfigurationInterface *)(temp->data); + } + + if (g_utf8_collate("port", key) == 0) + { + guint64 portnumber; + gchar *endptr; + + portnumber = g_ascii_strtoull(value, &endptr, 10); + + if ((endptr != NULL) && (*endptr != 0)) + { + wmud_log_error("Error in configuration file. Value of port can only contain numbers in group [%s]!", group); + } + + if (interface->inetaddr == NULL) + { + interface->inetaddr = gnet_inetaddr_new("0.0.0.0", portnumber); + } + else + { + gnet_inetaddr_set_port(interface->inetaddr, (gint)portnumber); + } + + g_pattern_spec_free(group_ptn); + return TRUE; + } + wmud_log_error("Unknown key (%s) found in configuration file in group [%s]", key, group); + g_pattern_spec_free(group_ptn); + return FALSE; + } + g_pattern_spec_free(group_ptn); + + wmud_log_error("Unknown group (%s) found in configuration file", group); + return FALSE; +} + +static void +_wmud_configuration_free_interface(gpointer data, gpointer user_data) +{ + wMUDConfigurationInterface *iface = (wMUDConfigurationInterface *)data; + + if (iface->name) + { + g_free(iface->name); + } + if (iface->inetaddr) + { + gnet_inetaddr_unref(iface->inetaddr); + } + g_free(data); +} + +static void +_wmud_configuration_free_value(gpointer data, gpointer user_data) +{ + wMUDConfigurationValue *v = (wMUDConfigurationValue *)data; + + if (v->key) + { + g_free(v->key); + } + if (v->value) + { + g_free(v->value); + } +} + +static void +_wmud_configuration_free_parameters(gpointer data, gpointer user_data) +{ + wMUDConfigurationGroup *group = (wMUDConfigurationGroup *)data; + + g_slist_foreach(group->datalist, _wmud_configuration_free_value, NULL); + g_slist_free(group->datalist); + if (group->name) + { + g_free(group->name); + } +} + +void +wmud_configuration_free(wMUDConfiguration **configuration) +{ + g_slist_foreach((*configuration)->interfaces, _wmud_configuration_free_interface, NULL); + g_slist_free((*configuration)->interfaces); + + g_slist_foreach((*configuration)->statesave_parameters, _wmud_configuration_free_parameters, NULL); + g_slist_free((*configuration)->statesave_parameters); + + g_slist_foreach((*configuration)->protocol_parameters, _wmud_configuration_free_parameters, NULL); + g_slist_free((*configuration)->protocol_parameters); + + if ((*configuration)->logfile) + { + g_free((*configuration)->logfile); + } + if ((*configuration)->debug_logfile) + { + g_free((*configuration)->debug_logfile); + } + if ((*configuration)->info_logfile) + { + g_free((*configuration)->info_logfile); + } + if ((*configuration)->warning_logfile) + { + g_free((*configuration)->warning_logfile); + } + if ((*configuration)->error_logfile) + { + g_free((*configuration)->error_logfile); + } + + if ((*configuration)->modules_dir) + { + g_free((*configuration)->modules_dir); + } + + if ((*configuration)->statesave_module) + { + g_free((*configuration)->statesave_module); + } + + if ((*configuration)->protocol_modules) + { + g_strfreev((*configuration)->protocol_modules); + } + + g_free((*configuration)); + + *configuration = NULL; +} + +gboolean +wmud_configfile_read(gchar *filename, wMUDConfiguration **configuration, GError **error) +{ + GKeyFile *configfile; + GError *file_error = NULL; + gchar **grouplist, + **group; + wMUDConfiguration *conf; + + *configuration = NULL; + + Context; + + if (filename == NULL) + { + return FALSE; + } + + conf = g_new0(wMUDConfiguration, 1); + + wmud_log_debug("Reading configuration file %s", filename); + + configfile = g_key_file_new(); + + if (!g_key_file_load_from_file(configfile, filename, G_KEY_FILE_NONE, &file_error)) + { + if (file_error->domain == G_FILE_ERROR) + { + wmud_log_error("Unable to open configuration file (%s): %s", filename, file_error->message); + } + + if (file_error->domain == G_KEY_FILE_ERROR) + { + wmud_log_error("Unable to parse configuration file (%s): %s", filename, file_error->message); + } + + g_key_file_free(configfile); + + return FALSE; + } + + grouplist = g_key_file_get_groups(configfile, NULL); + + for (group = grouplist; *group; group++) + { + gchar **keylist, + **key; + wmud_log_debug("Processing group: [%s]", *group); + + keylist = g_key_file_get_keys(configfile, *group, NULL, NULL); + + for (key = keylist; *key; key++) + { + gchar *value; + gboolean line_processed; + + value = g_key_file_get_value(configfile, *group, *key, NULL); + + line_processed = _wmud_config_process_value(conf, *group, *key, value); + + g_free(value); + + if (!line_processed) + { + g_strfreev(keylist); + g_strfreev(grouplist); + g_key_file_free(configfile); + + return FALSE; + } + } + + g_strfreev(keylist); + } + + g_strfreev(grouplist); + + g_key_file_free(configfile); + + /* If one of the logging options not found, fall back to the "log" + * option. If neither log can be found, configuration is invalid + */ +#ifdef DEBUG + if (!conf->debug_log_found) + { + if (!conf->log_found) + { + wmud_log_error("Debug log not found in configuration file"); + wmud_configuration_free(&conf); + return FALSE; + } + else + { + conf->debug_log_found = TRUE; + conf->debug_log_dest = conf->log_dest; + conf->debug_logfile = (conf->logfile == NULL) ? NULL : g_strdup(conf->logfile); + } + } +#endif + if (!conf->info_log_found) + { + if (!conf->log_found) + { + wmud_log_error("Info log not found in configuration file"); + wmud_configuration_free(&conf); + return FALSE; + } + else + { + conf->info_log_found = TRUE; + conf->info_log_dest = conf->log_dest; + conf->info_logfile = (conf->logfile == NULL) ? NULL : g_strdup(conf->logfile); + } + } + if (!conf->warning_log_found) + { + if (!conf->log_found) + { + wmud_log_error("Warning log not found in configuration file"); + wmud_configuration_free(&conf); + return FALSE; + } + else + { + conf->warning_log_found = TRUE; + conf->warning_log_dest = conf->log_dest; + conf->warning_logfile = (conf->logfile == NULL) ? NULL : g_strdup(conf->logfile); + } + } + if (!conf->error_log_found) + { + if (!conf->log_found) + { + wmud_log_error("Error log not found in configuration file"); + wmud_configuration_free(&conf); + return FALSE; + } + else + { + conf->error_log_found = TRUE; + conf->error_log_dest = conf->log_dest; + conf->error_logfile = (conf->logfile == NULL) ? NULL : g_strdup(conf->logfile); + } + } + + /* If daemonizing is forced, we cannot log to the console. However, + * daemonizing is not supported if DEBUG is turned on + */ +#ifdef DEBUG + if ((conf->daemonize == WMUD_CONF_DAEMONIZE_FORCE) || (conf->daemonize == WMUD_CONF_DAEMONIZE_YES)) + { + wmud_log_warning("Cannot daemonize in DEBUG mode"); + conf->daemonize = WMUD_CONF_DAEMONIZE_NO; + } +#endif + + if (conf->daemonize == WMUD_CONF_DAEMONIZE_FORCE) + { +#ifdef DEBUG + if (conf->debug_log_dest == WMUD_CONF_LOG_CONSOLE) + { + wmud_log_warning("Cannot log to console when daemonized. Debug log is falling back to syslog"); + conf->debug_log_dest = WMUD_CONF_LOG_SYSLOG; + } +#endif + if (conf->info_log_dest == WMUD_CONF_LOG_CONSOLE) + { + wmud_log_warning("Cannot log to console when daemonized. Info log is falling back to syslog"); + conf->info_log_dest = WMUD_CONF_LOG_SYSLOG; + } + if (conf->warning_log_dest == WMUD_CONF_LOG_CONSOLE) + { + wmud_log_warning("Cannot log to console when daemonized. Warning log is falling back to syslog"); + conf->warning_log_dest = WMUD_CONF_LOG_SYSLOG; + } + if (conf->error_log_dest == WMUD_CONF_LOG_CONSOLE) + { + wmud_log_warning("Cannot log to console when daemonized. Error log is falling back to syslog"); + conf->error_log_dest = WMUD_CONF_LOG_SYSLOG; + } + } + +#ifdef DEBUG + wmud_log_warning("Logging must be sent to the console in debug mode!"); + if (conf->debug_log_dest != WMUD_CONF_LOG_CONSOLE) + { + if (conf->debug_logfile) + { + g_free(conf->debug_logfile); + conf->debug_logfile = NULL; + } + conf->debug_log_dest = WMUD_CONF_LOG_CONSOLE; + } + if (conf->info_log_dest != WMUD_CONF_LOG_CONSOLE) + { + if (conf->info_logfile) + { + g_free(conf->info_logfile); + conf->info_logfile = NULL; + } + conf->info_log_dest = WMUD_CONF_LOG_CONSOLE; + } + if (conf->warning_log_dest != WMUD_CONF_LOG_CONSOLE) + { + if (conf->warning_logfile) + { + g_free(conf->warning_logfile); + conf->warning_logfile = NULL; + } + conf->warning_log_dest = WMUD_CONF_LOG_CONSOLE; + } + if (conf->error_log_dest != WMUD_CONF_LOG_CONSOLE) + { + if (conf->error_logfile) + { + g_free(conf->error_logfile); + conf->error_logfile = NULL; + } + conf->error_log_dest = WMUD_CONF_LOG_CONSOLE; + } +#endif + + *configuration = conf; + return TRUE; +} + diff --git a/src/configfile.h b/src/configfile.h new file mode 100644 index 0000000..16b9b93 --- /dev/null +++ b/src/configfile.h @@ -0,0 +1,16 @@ +#ifndef __WMUD_CORE_CONFIGFILE_H__ +#define __WMUD_CORE_CONFIGFILE_H__ + +#include + +#include "wmud-session.h" + +#include "configfile-module.h" + +extern wMUDConfiguration *wmud_configuration; + +gboolean wmud_configfile_read(gchar *filename, wMUDConfiguration **configuration, GError **error); +void wmud_configuration_free(wMUDConfiguration **configuration); + +#endif /* __WMUD_CORE_CONFIGFILE_H__ */ + diff --git a/src/irc.c b/src/irc.c new file mode 100644 index 0000000..594a908 --- /dev/null +++ b/src/irc.c @@ -0,0 +1,107 @@ +#include +#include +#include + +#include "wmud.h" +#include "logger.h" + +#define COMMAND_DELIMITERS " \t\n\r" + +struct _wMUDSplitHelper +{ + gchar **list; + gint position; +}; + +static void +_add_words(gpointer data, gpointer user_data) +{ + struct _wMUDSplitHelper *split_data = (struct _wMUDSplitHelper *)user_data; + gchar *word = (gchar *)data; + + split_data->list[split_data->position] = word; + split_data->position++; +} + +static gint +_wmud_split_irc_line(gchar *command, gchar ***list) +{ + gchar *a; + gint word_started_at = 0; + gboolean was_in_word = FALSE; + gboolean in_string = TRUE; + guint colon_at = -1; + GSList *word_list = NULL; + gchar **ret; + + struct _wMUDSplitHelper split_data; + gint word_count; + + Context; + + for (a = command; in_string; a++) + { + if (*a == 0) + { + in_string = FALSE; + } + if ( + (*a == 0) + || ( + (colon_at == -1) + && strchr(COMMAND_DELIMITERS, *a) + ) + || ( + (colon_at == -1) + && (*a == ':') + ) + ) + { + if (was_in_word) + { + was_in_word = FALSE; + word_list = g_slist_append(word_list, g_strndup((command + word_started_at), (a - command) - word_started_at)); + word_started_at = (a - command); + } + if ((*a == ':') && (colon_at == -1)) + { + colon_at = (a - command) - 1; + if (colon_at == -1) + { + colon_at = 0; + } + } + word_started_at++; + continue; + } + was_in_word = TRUE; + } + + word_count = g_slist_length(word_list); + Context ret = g_new0(gchar *, word_count + 1); + + split_data.list = ret; + split_data.position = 0; + + g_slist_foreach(word_list, _add_words, &split_data); + g_slist_free(word_list); + + if (list != NULL) + { + Context *list = ret; + } + + return word_count; +} + +void +wmud_process_irc_command(GConn *connection, gchar *command) +{ + gchar **command_parts; + gint word_count; + + word_count = _wmud_split_irc_line(command, &command_parts); + + wmud_log_debug("Got command '%s' with %d parts", command, word_count); +} + diff --git a/src/irc.h b/src/irc.h new file mode 100644 index 0000000..64d68e8 --- /dev/null +++ b/src/irc.h @@ -0,0 +1,10 @@ +#ifndef __WMUD_CORE_IRC_H__ +#define __WMUD_CORE_IRC_H__ + +#include +#include + +void wmud_process_irc_command(GConn *connection, gchar *command); + +#endif /* __WMUD_CORE_IRC_H__ */ + diff --git a/src/logger-module.h b/src/logger-module.h new file mode 100644 index 0000000..a4f326d --- /dev/null +++ b/src/logger-module.h @@ -0,0 +1,22 @@ +#ifndef __WMUD_MODULE_LOGGER_H__ +#define __WMUD_MODULE_LOGGER_H__ + +typedef enum +{ + WMUD_LOG_DEBUG, + WMUD_LOG_INFO, + WMUD_LOG_WARN, + WMUD_LOG_ERROR +} wMUDLogLevelType; + +#define wmud_log_info(x, ...) wmud_log(WMUD_LOG_INFO, __FILE__, __LINE__, x, ## __VA_ARGS__) +#define wmud_log_warning(x, ...) wmud_log(WMUD_LOG_WARN, __FILE__, __LINE__, x, ## __VA_ARGS__) +#define wmud_log_error(x, ...) wmud_log(WMUD_LOG_ERROR, __FILE__, __LINE__, x, ## __VA_ARGS__) +#if DEBUG +#define wmud_log_debug(x, ...) wmud_log(WMUD_LOG_DEBUG, __FILE__, __LINE__, x, ## __VA_ARGS__) +#else /* ! DEBUG */ +#define wmud_log_debug(x, ...) +#endif /* DEBUG */ + +#endif /* __WMUD_MODULE_LOGGER_H__ */ + diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..fcceb7b --- /dev/null +++ b/src/logger.c @@ -0,0 +1,232 @@ +#include + +#include +#include +#include +#include +#include + +#include "wmud.h" +#include "configfile.h" +#include "logger.h" + +enum { + _LOG_DEST_STDOUT, + _LOG_DEST_STDERR +}; + +typedef struct _wmud_message_level { + int colorcode; + const char *prefix; + int debug_dest; + wMUDConfigurationLogging dest; + GnomeVFSHandle *logfile; +} wMUDMessageLevelType; + +wMUDMessageLevelType wMUDMessageLevels[] = { + { 35, "debug", _LOG_DEST_STDERR, WMUD_CONF_LOG_CONSOLE, NULL }, /* LOG_DEBUG */ + { 32, "info", _LOG_DEST_STDOUT, WMUD_CONF_LOG_CONSOLE, NULL }, /* LOG_INFO */ + { 33, "warn", _LOG_DEST_STDOUT, WMUD_CONF_LOG_CONSOLE, NULL }, /* LOG_WARN */ + { 31, "error", _LOG_DEST_STDERR, WMUD_CONF_LOG_CONSOLE, NULL } /* LOG_ERROR */ +}; +#define wmud_message_level_count (sizeof(wmud_message_levels) / sizeof(wmud_message_level)) + +static void +_wmud_log_message(wMUDLogLevelType level, const char *filename, const int linenum, const char *fmt, va_list args) +{ + char *format, + *final_string; + wMUDMessageLevelType level_info = wMUDMessageLevels[level]; + int syslog_level; + static char *timestamp = NULL; + time_t now; + + if (timestamp == NULL) + { + timestamp = g_malloc0(17 * sizeof(char)); + } + + switch (level_info.dest) + { + case WMUD_CONF_LOG_NONE: + return; + case WMUD_CONF_LOG_FILE: + now = time(NULL); + Context strftime(timestamp, 16, "%b %e %H:%M:%S", localtime(&now)); + Context gnome_vfs_seek(level_info.logfile, GNOME_VFS_SEEK_END, 0); + format = g_strdup_printf("%s wMUD[%d]: [%s] %s:%d: %s\n", timestamp, wmud_pid, level_info.prefix, filename, linenum, fmt); + final_string = g_strdup_vprintf(format, args); + g_free(format); + gnome_vfs_write(level_info.logfile, final_string, strlen(final_string), NULL); + g_free(final_string); + break; + case WMUD_CONF_LOG_SYSLOG: + switch (level) + { + case WMUD_LOG_DEBUG: + syslog_level = LOG_DEBUG; + break; + case WMUD_LOG_INFO: + syslog_level = LOG_INFO; + break; + case WMUD_LOG_WARN: + syslog_level = LOG_WARNING; + break; + case WMUD_LOG_ERROR: + syslog_level = LOG_ERR; + break; + default: + syslog_level = LOG_CRIT; + break; + } + format = g_strdup_printf("[%s] %s:%d: %s\n", level_info.prefix, filename, linenum, fmt); + vsyslog(LOG_USER | syslog_level, format, args); + g_free(format); + break; + case WMUD_CONF_LOG_CONSOLE: + format = g_strdup_printf("\x1b[%dm\x1b[1m[%s] %s:%d: %s\x1b[0m\n", level_info.colorcode, level_info.prefix, filename, linenum, fmt); + vfprintf((level_info.debug_dest == _LOG_DEST_STDERR) ? stderr : stdout, format, args); + g_free(format); + break; + } +} + +void +wmud_log(wMUDLogLevelType level, const char *filename, const int linenum, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _wmud_log_message(level, filename, linenum, fmt, args); + va_end(args); +} + +gboolean +wmud_logger_init(wMUDConfiguration *config) +{ + gchar *full_logfile_uri; + GnomeVFSURI *log_uri; + + g_assert(config != NULL); + + if (config->debug_log_dest == WMUD_CONF_LOG_FILE) + { + GnomeVFSResult state; + full_logfile_uri = gnome_vfs_get_uri_from_local_path(config->debug_logfile); + log_uri = gnome_vfs_uri_new(full_logfile_uri); + g_free(full_logfile_uri); + + switch (gnome_vfs_open_uri(&(wMUDMessageLevels[WMUD_LOG_DEBUG].logfile), log_uri, GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM)) + { + case GNOME_VFS_ERROR_NOT_FOUND: + if ((state = gnome_vfs_create_uri(&(wMUDMessageLevels[WMUD_LOG_DEBUG].logfile), log_uri, GNOME_VFS_OPEN_WRITE, FALSE, 0640)) != GNOME_VFS_OK) + { + wmud_log_error("Unable to open debug logfile (%s)", config->debug_logfile); + wmud_shutdown(); + return FALSE; + } + break; + case GNOME_VFS_OK: + break; + default: + wmud_log_error("Unprocessed return state from gnome_vfs_open_uri()"); + break; + } + gnome_vfs_uri_unref(log_uri); + } + wMUDMessageLevels[WMUD_LOG_DEBUG].dest = config->debug_log_dest; + + if (config->info_log_dest == WMUD_CONF_LOG_FILE) + { + GnomeVFSResult state; + full_logfile_uri = gnome_vfs_get_uri_from_local_path(config->info_logfile); + log_uri = gnome_vfs_uri_new(full_logfile_uri); + g_free(full_logfile_uri); + + switch (gnome_vfs_open_uri(&(wMUDMessageLevels[WMUD_LOG_INFO].logfile), log_uri, GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM)) + { + case GNOME_VFS_ERROR_NOT_FOUND: + if ((state = gnome_vfs_create_uri(&(wMUDMessageLevels[WMUD_LOG_INFO].logfile), log_uri, GNOME_VFS_OPEN_WRITE, FALSE, 0640)) != GNOME_VFS_OK) + { + wmud_log_error("Unable to open info logfile (%s)", config->info_logfile); + wmud_shutdown(); + return FALSE; + } + break; + case GNOME_VFS_OK: + break; + default: + wmud_log_error("Unprocessed return state from gnome_vfs_open_uri()"); + break; + } + gnome_vfs_uri_unref(log_uri); + } + wMUDMessageLevels[WMUD_LOG_INFO].dest = config->info_log_dest; + + if (config->warning_log_dest == WMUD_CONF_LOG_FILE) + { + GnomeVFSResult state; + full_logfile_uri = gnome_vfs_get_uri_from_local_path(config->warning_logfile); + log_uri = gnome_vfs_uri_new(full_logfile_uri); + g_free(full_logfile_uri); + + switch (gnome_vfs_open_uri(&(wMUDMessageLevels[WMUD_LOG_WARN].logfile), log_uri, GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM)) + { + case GNOME_VFS_ERROR_NOT_FOUND: + if ((state = gnome_vfs_create_uri(&(wMUDMessageLevels[WMUD_LOG_WARN].logfile), log_uri, GNOME_VFS_OPEN_WRITE, FALSE, 0640)) != GNOME_VFS_OK) + { + wmud_log_error("Unable to open warning logfile (%s)", config->warning_logfile); + wmud_shutdown(); + return FALSE; + } + break; + case GNOME_VFS_OK: + break; + default: + wmud_log_error("Unprocessed return state from gnome_vfs_open_uri()"); + break; + } + gnome_vfs_uri_unref(log_uri); + } + wMUDMessageLevels[WMUD_LOG_WARN].dest = config->warning_log_dest; + + if (config->error_log_dest == WMUD_CONF_LOG_FILE) + { + GnomeVFSResult state; + full_logfile_uri = gnome_vfs_get_uri_from_local_path(config->error_logfile); + log_uri = gnome_vfs_uri_new(full_logfile_uri); + g_free(full_logfile_uri); + + switch (gnome_vfs_open_uri(&(wMUDMessageLevels[WMUD_LOG_ERROR].logfile), log_uri, GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM)) + { + case GNOME_VFS_ERROR_NOT_FOUND: + if ((state = gnome_vfs_create_uri(&(wMUDMessageLevels[WMUD_LOG_ERROR].logfile), log_uri, GNOME_VFS_OPEN_WRITE, FALSE, 0640)) != GNOME_VFS_OK) + { + wmud_log_error("Unable to open error logfile (%s)", config->error_logfile); + wmud_shutdown(); + return FALSE; + } + break; + case GNOME_VFS_OK: + break; + default: + wmud_log_error("Unprocessed return state from gnome_vfs_open_uri()"); + break; + } + gnome_vfs_uri_unref(log_uri); + } + wMUDMessageLevels[WMUD_LOG_ERROR].dest = config->error_log_dest; + + if ( + (config->debug_log_dest == WMUD_CONF_LOG_SYSLOG) + || (config->info_log_dest == WMUD_CONF_LOG_SYSLOG) + || (config->warning_log_dest == WMUD_CONF_LOG_SYSLOG) + || (config->error_log_dest == WMUD_CONF_LOG_SYSLOG) + ) + { + openlog("wMUD", LOG_PID, LOG_USER); + } + + return TRUE; +} + diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..e9eec26 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,16 @@ +#ifndef __WMUD_CORE_LOGGER_H__ +#define __WMUD_CORE_LOGGER_H__ + +#include +#include + +#include "configfile.h" + +#include "logger-module.h" +#include "wmud-module.h" + +void wmud_log(wMUDLogLevelType level, const char *filename, const int linenum, const char *fmt, ...); +gboolean wmud_logger_init(wMUDConfiguration *config); + +#endif /* __WMUD_CORE_LOGGER_H__ */ + diff --git a/src/module.h b/src/module.h new file mode 100644 index 0000000..4bb9ecc --- /dev/null +++ b/src/module.h @@ -0,0 +1,10 @@ +#ifndef __WMUD_MODULE_H__ +#define __WMUD_MODULE_H__ + +#include "wmud-module.h" + +#define wmud_log(level, filename, linenum, fmt, ...) ((void (*)(wMUDLogLevelType, const char *, const int, const char *, ...))(wMUDCallables[WMUD_CALLABLE_WMUD_LOG]))(level, filename, linenum, fmt, ## __VA_ARGS__) +#define wmud_print_context(filename, linenum) ((void (*)(char *, int))(wMUDCallables[WMUD_CALLABLE_WMUD_PRINT_CONTEXT]))(filename, linenum) + +#endif /* __WMUD_MODULE_H__ */ + diff --git a/src/modules.c b/src/modules.c new file mode 100644 index 0000000..24294b0 --- /dev/null +++ b/src/modules.c @@ -0,0 +1,112 @@ +#include +#include + +#if HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "modules.h" +#include "configfile.h" +#include "logger.h" +#include "wmud.h" + +#include "wmud-module.h" + +static GModule *statesave_module = NULL; + +gboolean (*wmud_save_state)(GError **error) = NULL; +gpointer wMUDCallables[WMUD_CALLABLE_LAST]; + +void +wmud_fill_callable_table(void) +{ + wMUDCallables[WMUD_CALLABLE_WMUD_LOG] = wmud_log; +#ifdef DEBUG + wMUDCallables[WMUD_CALLABLE_WMUD_PRINT_CONTEXT] = wmud_print_context; +#endif /* DEBUG */ + wMUDCallables[WMUD_CALLABLE_LAST] = NULL; +} + +gboolean +wmud_load_config_modules(wMUDConfiguration *config) +{ + gchar *statesave_module_short_name, + *statesave_module_file, + *func_name; + gboolean (*module_is_statesaving)(void); + gboolean (*module_load)(wMUDConfiguration *config); + + if (!g_module_supported()) + { + wmud_log_error("Module loading is not supported!"); + return FALSE; + } + Context; + + if (config->modules_dir == NULL) + { + wmud_log_error("Module directory must be set in the configfile!"); + return FALSE; + } + + if (config->statesave_module == NULL) + { + wmud_log_error("State saving module must be set in the configfile!"); + return FALSE; + } + + statesave_module_short_name = g_strdup_printf("state-%s", config->statesave_module); + statesave_module_file = g_module_build_path(config->modules_dir, statesave_module_short_name); + g_free(statesave_module_short_name); + + wmud_log_debug("Trying to load \"%s\" as state-saving module", statesave_module_file); + + if ((statesave_module = g_module_open(statesave_module_file, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL)) == NULL) + { + wmud_log_error("Unable to load state saving module %s", statesave_module_file); + return FALSE; + } + + func_name = g_strdup_printf("wmud_statesave_%s_is_statesave", config->statesave_module); + if (g_module_symbol(statesave_module, func_name, (gpointer *)&module_is_statesaving)) + { + if (!module_is_statesaving()) + { + wmud_log_error("Module %s is not marked as statesaving!", statesave_module_file); + g_module_close(statesave_module); + g_free(func_name); + return FALSE; + } + } + else + { + wmud_log_error("Undefined symbol \"%s\" in statesave module %s", func_name, statesave_module_file); + g_module_close(statesave_module); + g_free(func_name); + return FALSE; + } + g_free(func_name); + + func_name = g_strdup_printf("wmud_statesave_%s_load", config->statesave_module); + if (g_module_symbol(statesave_module, func_name, (gpointer *)&module_load)) + { + if (!module_load(config)) + { + wmud_log_error("Module initialization failed for %s", statesave_module_file); + g_module_close(statesave_module); + g_free(func_name); + return FALSE; + } + } + else + { + wmud_log_error("Undefined symbol \"%s\" in statesave module %s", func_name, statesave_module_file); + g_module_close(statesave_module); + g_free(func_name); + return FALSE; + } + + g_free(statesave_module_file); + return TRUE; +} + diff --git a/src/modules.h b/src/modules.h new file mode 100644 index 0000000..2173fc5 --- /dev/null +++ b/src/modules.h @@ -0,0 +1,12 @@ +#ifndef __WMUD_CORE_MODULES_H__ +#define __WMUD_CORE_MODULES_H__ + +#include + +#include "configfile.h" + +void wmud_fill_callable_table(void); +gboolean wmud_load_config_modules(wMUDConfiguration *config); + +#endif /* __WMUD_CORE_MODULES_H__ */ + diff --git a/src/networking.c b/src/networking.c new file mode 100644 index 0000000..dfc97b4 --- /dev/null +++ b/src/networking.c @@ -0,0 +1,427 @@ +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "wmud.h" +#include "logger.h" +#include "sessions.h" +#include "networking.h" +#include "configfile.h" +#include "commands.h" +#include "irc.h" + +#define LISTEN_PORT 4000 + +GSList *_wmud_active_interfaces = NULL; + +/** + * _wmud_telnet_event: + * @connection: the #GConn on which this event occured + * @event: type of event + * @user_data: user data arrived with this function call. Currently it is not used + * + * Callback function which gets called whenever a new event occurs on a telnet-type socket + */ +static void +_wmud_telnet_event(GConn *connection, GConnEvent *event, gpointer user_data) +{ + gchar *command; + GError *error = NULL; + + //Context; + + switch (event->type) + { + case GNET_CONN_READABLE: + command = NULL; + gsize term_pos; + gsize length; + + switch (g_io_channel_read_line(connection->iochannel, &command, &length, &term_pos, &error)) + { + case G_IO_STATUS_ERROR: + wmud_log_error("Telnet read error: %s", error->message); + break; + case G_IO_STATUS_NORMAL: + wmud_log_debug("Accepted data"); + break; + case G_IO_STATUS_EOF: + wmud_log_info("Client disconnected"); + wmud_connection_disconnect(connection); + break; + case G_IO_STATUS_AGAIN: + wmud_log_debug("Telnet read terminated, trying again"); + break; + default: + break; + } + if (command) + { + *(command + term_pos) = 0; + wmud_log_debug("Got command: %s", command); + wmud_process_command(connection, command); + *(command + term_pos) = '\n'; + g_free(command); + } + break; + case GNET_CONN_TIMEOUT: + wmud_log_info("Telnet connection timeout"); + wmud_connection_disconnect(connection); + break; + case GNET_CONN_ERROR: + wmud_log_error("Unprocessed connection event CONN_ERROR"); + break; + case GNET_CONN_CONNECT: + wmud_log_error("Unprocessed connection event CONN_CONNECT"); + break; + case GNET_CONN_CLOSE: + wmud_log_error("Unprocessed connection event CONN_CLOSE"); + break; + case GNET_CONN_READ: + wmud_log_error("Unprocessed connection event CONN_READ"); + break; + case GNET_CONN_WRITE: + wmud_log_error("Unprocessed connection event CONN_WRITE"); + break; + case GNET_CONN_WRITABLE: + wmud_log_error("Unprocessed connection event CONN_WRITABLE"); + break; + default: + wmud_log_error("Got an unknown connction event from GNet!"); + break; + } + +} + +static void +_wmud_irc_event(GConn *connection, GConnEvent *event, gpointer user_data) +{ + gchar *command; + GError *error = NULL; + + switch (event->type) + { + case GNET_CONN_READABLE: + command = NULL; + gsize term_pos; + gsize length; + + switch (g_io_channel_read_line(connection->iochannel, &command, &length, &term_pos, &error)) + { + case G_IO_STATUS_ERROR: + wmud_log_error("IRC read error: %s", error->message); + break; + case G_IO_STATUS_NORMAL: + wmud_log_debug("Accepted data"); + break; + case G_IO_STATUS_EOF: + wmud_log_info("Client disconnected"); + wmud_connection_disconnect(connection); + break; + case G_IO_STATUS_AGAIN: + wmud_log_debug("IRC read terminated, trying again"); + break; + default: + break; + } + if (command) + { + *(command + term_pos) = 0; + wmud_log_debug("Got command: %s", command); + wmud_process_irc_command(connection, command); + *(command + term_pos) = '\n'; + g_free(command); + } + break; + case GNET_CONN_TIMEOUT: + wmud_log_info("IRC connection timeout"); + wmud_connection_disconnect(connection); + break; + case GNET_CONN_ERROR: + wmud_log_error("Unprocessed connection event CONN_ERROR"); + break; + case GNET_CONN_CONNECT: + wmud_log_error("Unprocessed connection event CONN_CONNECT"); + break; + case GNET_CONN_CLOSE: + wmud_log_error("Unprocessed connection event CONN_CLOSE"); + break; + case GNET_CONN_READ: + wmud_log_error("Unprocessed connection event CONN_READ"); + break; + case GNET_CONN_WRITE: + wmud_log_error("Unprocessed connection event CONN_WRITE"); + break; + case GNET_CONN_WRITABLE: + wmud_log_error("Unprocessed connection event CONN_WRITABLE"); + break; + default: + wmud_log_error("Got an unknown connction event from GNet!"); + break; + } + +} + +void +wmud_connection_send(GConn *connection, gchar *fmt, ...) +{ + va_list args; + gchar *buffer, + *send_buffer; + gsize len; + GError *error = NULL; + GIOStatus status; + + va_start(args, fmt); + Context buffer = g_strdup_vprintf(fmt, args); + va_end(args); + + Context send_buffer = g_strdup_printf("%s%s", buffer, wmud_connection_get_linebreak(connection)); + Context g_free(buffer); + + /* TODO: Error checking! */ + Context; + switch ((status = g_io_channel_write_chars(connection->iochannel, send_buffer, -1, &len, &error))) + { + case G_IO_STATUS_ERROR: + wmud_log_error("Error during g_io_channel_write_chars(): %s", error->message); + break; + case G_IO_STATUS_NORMAL: + wmud_log_debug("Message sent"); + break; + case G_IO_STATUS_EOF: + wmud_log_warning("EOF during g_io_channel_write_chars()???"); + break; + case G_IO_STATUS_AGAIN: + wmud_log_warning("Resource temporarily unavailable while sending message to client"); + break; + default: + wmud_log_error("Unknown return value from g_io_channel_write_chars()!"); + break; + } + if (error != NULL) + { + g_error_free(error); + error = NULL; + } + Context g_free(send_buffer); + + switch ((status = g_io_channel_flush(connection->iochannel, &error))) + { + case G_IO_STATUS_ERROR: + wmud_log_error("Error during g_io_channel_flush(): %s", error->message); + break; + case G_IO_STATUS_NORMAL: + wmud_log_debug("IOChannel flushed"); + break; + case G_IO_STATUS_EOF: + wmud_log_warning("EOF during g_io_channel_flush()???"); + break; + case G_IO_STATUS_AGAIN: + wmud_log_warning("Resource temporarily unavailable while sending message to client"); + break; + default: + wmud_log_error("Unknown return value from g_io_channel_flush()!"); + break; + } + + if (error != NULL) + { + g_error_free(error); + error = NULL; + } + Context; +} + +const gchar * +wmud_connection_get_linebreak(GConn *connection) +{ + wMUDSession *session; + + Context session = wmud_session_for_connection(connection); + + if (session) + { + switch (wmud_session_get_session_type(session)) + { + case WMUD_SESSION_TYPE_TELNET: + Context return "\r\n"; + break; + case WMUD_SESSION_TYPE_IRC: + Context return "\r\n"; + break; + default: + wmud_log_warning("Looling for linebreak for unknown session type"); + return ""; + } + } + else + { + Context; + } + + return ""; +} + +/** + * _wmud_accept_telnet: + * @server: the #GServer object that has an incoming connection + * @connection: The #GConn created to hold the new connection + * @user_data: user data arrived with this function call. Currently it is not used + * + * Callback function which gets called whenever a new connection arrived on a server socket + */ +static void +_wmud_accept_telnet(GServer *server, GConn *connection, gpointer user_data) +{ + wMUDConfigurationInterface *interface = (wMUDConfigurationInterface *)user_data; + /* If the connection is not created for some reason, log an error and return */ + if (connection == NULL) + { + wmud_log_error("Error while accepting connection"); + return; + } + + /* TODO: Disconnect if the IP is banned */ + + /* Log a message that a new connection has arrived */ + wmud_log_info("Accepted telnet connection from [%s]", gnet_inetaddr_get_canonical_name(connection->inetaddr)); + + /* Set the parameters of this connection */ + /* The connection should be buffered, so it is easier to handle */ + g_io_channel_set_buffered(connection->iochannel, TRUE); + /* The function to call, whenever an event occurs */ + gnet_conn_set_callback(connection, _wmud_telnet_event, NULL); + /* Watch for readable events */ + gnet_conn_set_watch_readable(connection, TRUE); + /* Timeout interval */ + gnet_conn_timeout(connection, interface->timeout * 1000); + + Context; + + if (!wmud_new_session(connection, WMUD_SESSION_TYPE_TELNET)) + { + Context; + g_io_channel_write_chars(connection->iochannel, "Unable to create a session for you. If you experience this problem for more than once, please contact us!", -1, NULL, NULL); + gnet_conn_disconnect(connection); + } + + Context; +} + +static void +_wmud_accept_irc(GServer *server, GConn *connection, gpointer user_data) +{ + wMUDConfigurationInterface *interface = (wMUDConfigurationInterface *)user_data; + + if (connection == NULL) + { + wmud_log_error("Error while accepting connection"); + return; + } + + /* TODO: Disconnect if the IP is banned */ + + /* Log a message that a new connection has arrived */ + wmud_log_info("Accepted IRC connection from [%s]", gnet_inetaddr_get_canonical_name(connection->inetaddr)); + + /* Set the parameters of this connection */ + /* The connection should be buffered, so it is easier to handle */ + g_io_channel_set_buffered(connection->iochannel, TRUE); + /* The function to call, whenever an event occurs */ + gnet_conn_set_callback(connection, _wmud_irc_event, NULL); + /* Watch for readable events */ + gnet_conn_set_watch_readable(connection, TRUE); + /* Timeout interval */ + gnet_conn_timeout(connection, interface->timeout * 1000); + + Context; + + if (!wmud_new_session(connection, WMUD_SESSION_TYPE_TELNET)) + { + Context; + g_io_channel_write_chars(connection->iochannel, "Unable to create a session for you. If you experience this problem for more than once, please contact us!", -1, NULL, NULL); + gnet_conn_disconnect(connection); + } + + Context; +} + +static void +_wmud_create_interface(gpointer data, gpointer user_data) +{ + GServer *server; + wMUDConfigurationInterface *interface = (wMUDConfigurationInterface *)data; + + wmud_log_debug("Creating new interface '%s'", interface->name); + switch (interface->type) + { + case WMUD_SESSION_TYPE_TELNET: + server = gnet_server_new(interface->inetaddr, gnet_inetaddr_get_port(interface->inetaddr), _wmud_accept_telnet, interface); + _wmud_active_interfaces = g_slist_append(_wmud_active_interfaces, (gpointer)server); + break; + case WMUD_SESSION_TYPE_IRC: + server = gnet_server_new(interface->inetaddr, gnet_inetaddr_get_port(interface->inetaddr), _wmud_accept_irc, interface); + _wmud_active_interfaces = g_slist_append(_wmud_active_interfaces, (gpointer)server); + break; + default: + wmud_log_error("Unknown type of interface!"); + break; + } +} + +void +wmud_connection_disconnect(GConn *connection) +{ + gnet_conn_disconnect(connection); +} + +gpointer +wmud_networking_thread(gpointer data) +{ + wMUDThreadData *thread_data = (wMUDThreadData *)data; + + Context; + wmud_log_info("Initializing network layer..."); + + /* g_main_context_get_thread_default() is only available since GLib 2.22; + * use g_main_context_new() otherwise + */ +#if (((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION >= 22)) || (GLIB_MAJOR_VERSION > 2)) + thread_data->main_context = g_main_context_get_thread_default(); +#else + thread_data->main_context = g_main_context_new(); +#endif + thread_data->main_loop = g_main_loop_new(thread_data->main_context, FALSE); + + /* Do the real initialization work here */ + + Context g_slist_foreach(wmud_configuration->interfaces, _wmud_create_interface, NULL); + + /* End of initialization */ + + wmud_log_info("Network layer initialized"); + + Context; + thread_data->running = TRUE; + g_main_loop_run(thread_data->main_loop); + + wmud_log_info("Network layer shutting down"); + + wmud_destroy_all_sessions("Server is shutting down."); + + g_main_loop_unref(thread_data->main_loop); + thread_data->main_loop = NULL; + + thread_data->running = FALSE; + + Context; + + return NULL; +} + diff --git a/src/networking.h b/src/networking.h new file mode 100644 index 0000000..ca0777e --- /dev/null +++ b/src/networking.h @@ -0,0 +1,13 @@ +#ifndef __WMUD_CORE_NETWORKING_H__ +#define __WMUD_CORE_NETWORKING_H__ + +#include +#include + +gpointer wmud_networking_thread(gpointer data); +void wmud_connection_disconnect(GConn *connection); +void wmud_connection_send(GConn *connection, gchar *fmt, ...); +const gchar *wmud_connection_get_linebreak(GConn *connection); + +#endif /* __WMUD_CORE_NETWORKING_H__ */ + diff --git a/src/protocols-module.h b/src/protocols-module.h new file mode 100644 index 0000000..598c66f --- /dev/null +++ b/src/protocols-module.h @@ -0,0 +1,15 @@ +#ifndef __WMUD_MODULE_PROTOCOLS_H__ +#define __WMUD_MODULE_PROTOCOLS_H__ + +#include + +enum { + WMUD_ERROR_PROTOCOL_OK, + WMUD_ERROR_PROTOCOL_EXISTS +}; + +#define WMUD_ERROR_PROTOCOL g_quark_from_string("WMUD PROTOCOL ERROR") +GQuark wmud_register_protocol(gchar *name, GError **error); + +#endif /* __WMUD_MODULE_PROTOCOLS_H__ */ + diff --git a/src/protocols.c b/src/protocols.c new file mode 100644 index 0000000..93888dc --- /dev/null +++ b/src/protocols.c @@ -0,0 +1,32 @@ +#include + +#include "protocols.h" + +guint wmud_current_protocol_number = 0; + +GQuark +wmud_register_protocol(gchar *name, GError **error) +{ + gchar *full_protocol_name; + + if (name == NULL) + { + return g_quark_from_string(NULL); + } + + full_protocol_name = g_strdup_printf("WMUD PROTO %s", name); + + if (g_quark_try_string(full_protocol_name) != 0) + { + /* Quark already exists => protocol is already registered */ + if (*error) + { + *error = g_error_new(WMUD_ERROR_PROTOCOL, WMUD_ERROR_PROTOCOL_EXISTS, "Protocol %s is already registered", name); + } + + return 0; + } + + return g_quark_from_string(full_protocol_name); +} + diff --git a/src/protocols.h b/src/protocols.h new file mode 100644 index 0000000..6b5f922 --- /dev/null +++ b/src/protocols.h @@ -0,0 +1,9 @@ +#ifndef __WMUD_CORE_PROTOCOLS_H__ +#define __WMUD_CORE_PROTOCOLS_H__ + +#include + +#include "protocols-module.h" + +#endif /* __WMUD_CORE_PROTOCOLS_H__ */ + diff --git a/src/sessions.c b/src/sessions.c new file mode 100644 index 0000000..d548dae --- /dev/null +++ b/src/sessions.c @@ -0,0 +1,101 @@ +#include +#include + +#include "wmud.h" +#include "sessions.h" +#include "networking.h" +#include "logger.h" + +#include "wmud-session.h" + +GSList *wmud_session_list = NULL; + +gboolean +wmud_new_session(GConn *connection, wMUDSessionType type) +{ + wMUDSession *session; + + Context; + + if ((session = wmud_session_new_with_connection(connection)) == NULL) + { + return FALSE; + } + + Context wmud_session_set_session_type(session, type); + + Context; + + wmud_session_list = g_slist_append(wmud_session_list, session); + + Context; + + return TRUE; +} + +static gint +_wmud_find_session_with_connection(gconstpointer list_element, gconstpointer connection) +{ + wMUDSession *session = (wMUDSession *)list_element; + GConn *conn = (GConn *)connection; + + g_assert(WMUD_IS_SESSION(list_element)); + + Context return (wmud_session_has_connection(session, conn)) ? 0 : 1; +} + +wMUDSession * +wmud_session_for_connection(GConn *connection) +{ + GSList *temp; + + Context temp = g_slist_find_custom(wmud_session_list, connection, _wmud_find_session_with_connection); + + if (temp) + { + return temp->data; + } + + return NULL; +} + +void +wmud_finish_session(wMUDSession *session, gchar *message) +{ + if ((session == NULL) || (!WMUD_IS_SESSION(session))) + { + return; + } + GConn *connection = wmud_session_get_connection(session); + + if (connection) + { + wmud_log_debug("Destroying session with connection"); + if (message) + { + wmud_connection_send(connection, message); + } + wmud_connection_disconnect(connection); + wmud_session_set_connection(session, NULL); + } + + wmud_session_list = g_slist_remove_all(wmud_session_list, session); + wmud_session_unref(session); +} + +static void +_wmud_destroy_session(gpointer sess, gpointer msg) +{ + wMUDSession *session = (wMUDSession *)sess; + gchar *message = (gchar *)msg; + + wmud_log_debug("Shutting down session %lx", sess); + wmud_finish_session(session, message); +} + +void +wmud_destroy_all_sessions(gchar *message) +{ + g_slist_foreach(wmud_session_list, _wmud_destroy_session, message); +} + diff --git a/src/sessions.h b/src/sessions.h new file mode 100644 index 0000000..0aed1c8 --- /dev/null +++ b/src/sessions.h @@ -0,0 +1,17 @@ +#ifndef __WMUD_CORE_SESSIONS_H__ +#define __WMUD_CORE_SESSIONS_H__ + +#include +#include + +#include "wmud-session.h" + +extern GSList *wmud_session_list; + +gboolean wmud_new_session(GConn *connection, wMUDSessionType type); +wMUDSession *wmud_session_for_connection(GConn *connection); +void wmud_finish_session(wMUDSession *session, gchar *message); +void wmud_destroy_all_sessions(gchar *message); + +#endif /* __WMUD_CORE_SESSIONS_H__ */ + diff --git a/src/wmud-module.h b/src/wmud-module.h new file mode 100644 index 0000000..c5bfb23 --- /dev/null +++ b/src/wmud-module.h @@ -0,0 +1,27 @@ +#ifndef __WMUD_MODULE_WMUD_H__ +#define __WMUD_MODULE_WMUD_H__ + +#include + +#include "protocols-module.h" +#include "configfile-module.h" +#include "logger-module.h" + +enum _wMUDCallableNum { + WMUD_CALLABLE_WMUD_LOG, +#ifdef DEBUG + WMUD_CALLABLE_WMUD_PRINT_CONTEXT, +#endif /* DEBUG */ + WMUD_CALLABLE_LAST +}; + +extern gpointer wMUDCallables[]; + +#if DEBUG +#define Context wmud_print_context(__FILE__, __LINE__); +#else +#define Context +#endif + +#endif /* __WMUD_MODULE_WMUD_H__ */ + diff --git a/src/wmud.c b/src/wmud.c new file mode 100644 index 0000000..9800012 --- /dev/null +++ b/src/wmud.c @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "wmud.h" +#include "logger.h" +#include "ansi.h" +#include "networking.h" +#include "world.h" +#include "configfile.h" +#include "chat.h" +#include "modules.h" + +/* TODO: Make this configurable via configure, and/or countable based on sysconfdir */ +#define DEFAULT_CONFIGFILE "/home/polesz/Projektek/wMUD/data/wmud.conf" + +GSList *wmud_running_threads = NULL; +gchar *wmud_option_configfile = NULL; +wMUDConfiguration *wmud_configuration = NULL; +GMainLoop *wmud_main_loop = NULL; +gboolean wmud_is_running = FALSE; + +pid_t wmud_pid; + +static GOptionEntry option_entries[] = { + { "configfile", 'c', G_OPTION_FLAG_FILENAME, G_OPTION_ARG_FILENAME, &wmud_option_configfile, "Configuration file to parse instead of the default one", "filename" }, + { NULL }, +}; + +#if DEBUG +void +wmud_print_context(char *filename, int linenum) +{ + fprintf(stderr, "\x1b[34m\x1b[1m[DEBUG CONTEXT] %s:%d\x1b[0m\n", filename, linenum); +} +#endif + +void +_fatal_signal_handler(int signum) +{ + fprintf(stderr, "Segmentation fault.\n"); + + exit(1); +} + +void +_terminating_signal_handler(int signum) +{ + switch (signum) + { + case SIGINT: + wmud_shutdown(); + break; + default: + return; + } +} + +void +_wmud_shutdown_thread(gpointer data, gpointer user_data) +{ + wMUDThreadData *thread = (wMUDThreadData *)data; + + wmud_log_debug("Shutting down %s thread", thread->name); + if (thread->main_loop) + { + Context; + wmud_log_debug("Stopping %s thread's main loop", thread->name); + g_main_loop_quit(thread->main_loop); + while (1) + { + if (!thread->running) + { + break; + } + usleep(200); + } + } + + Context; +} + +void +wmud_shutdown(void) +{ + wmud_log_info("Shutting down..."); + + Context g_slist_foreach(wmud_running_threads, _wmud_shutdown_thread, NULL); + + if (wmud_main_loop) + { + Context g_main_loop_quit(wmud_main_loop); + g_main_loop_unref(wmud_main_loop); + wmud_main_loop = NULL; + } + + wmud_is_running = FALSE; + + Context wmud_log_info("Shutdown complete"); +} + +void +wmud_parse_options(gint *argc, gchar ***argv) +{ + GOptionContext *options_context; + GError *error = NULL; + + options_context = g_option_context_new("wMUD server"); + g_option_context_add_main_entries(options_context, option_entries, NULL); + + if (!g_option_context_parse(options_context, argc, argv, &error)) + { + g_log(NULL, G_LOG_FLAG_FATAL | G_LOG_LEVEL_ERROR, "Failed parsing options: %s\n", error->message); + } +} + +static GThread * +_new_thread(GThreadFunc thread_func, GError **error, gchar *thread_name, gboolean fatal_if_fail) +{ + GThread *thread; + wMUDThreadData *thread_data; + + /* Create and initialize thread data */ + thread_data = g_new0(wMUDThreadData, 1); + + thread_data->main_loop = NULL; + thread_data->main_context = NULL; + thread_data->thread = NULL; + thread_data->name = g_strdup(thread_name); + thread_data->running = FALSE; + + if ((thread = g_thread_create(thread_func, thread_data, FALSE, error)) == NULL) + { + wmud_log_error("Unable to start %s thread!", thread_name); + + if (fatal_if_fail) + { + wmud_shutdown(); + } + g_free(thread_data); + } + else + { + thread_data->thread = thread; + wmud_running_threads = g_slist_append(wmud_running_threads, thread_data); + } + + return thread; +} + +int +main(int argc, char **argv) +{ + GError *error; + GThread *thread; + struct sigaction signal_action; + + wmud_pid = getpid(); + + wmud_log_info("wMUD v%s starting up", PACKAGE_VERSION); + + signal_action.sa_handler = _fatal_signal_handler; + sigemptyset(&signal_action.sa_mask); + signal_action.sa_flags = 0; + sigaction(SIGSEGV, &signal_action, NULL); + + signal_action.sa_handler = _terminating_signal_handler; + sigemptyset(&signal_action.sa_mask); + signal_action.sa_flags = 0; + sigaction(SIGINT, &signal_action, NULL); + + Context g_type_init(); + Context gnet_init(); + gnome_vfs_init(); + + Context wmud_parse_options(&argc, &argv); + + Context; + + /* TODO: Make this configurable via configure, and/or countable based on sysconfdir */ + if (wmud_option_configfile == NULL) + { + wmud_option_configfile = g_strdup(DEFAULT_CONFIGFILE); + } + + if (!wmud_configfile_read(wmud_option_configfile, &wmud_configuration, &error)) + { + return 1; + } + if (wmud_configuration == NULL) + { + return 1; + } + Context g_free(wmud_option_configfile); + Context wmud_fill_callable_table(); + Context wmud_load_config_modules(wmud_configuration); + return 0; + + if (wmud_logger_init(wmud_configuration)) + { + wmud_log_debug("Logger initialized"); + Context if (!g_thread_supported()) + { + g_thread_init(NULL); + } + + Context thread = _new_thread(wmud_networking_thread, &error, "networking", TRUE); + Context thread = _new_thread(wmud_world_thread, &error, "world", TRUE); + Context thread = _new_thread(wmud_chat_thread, &error, "chat", TRUE); + Context; + + wmud_main_loop = g_main_loop_new(NULL, FALSE); + + wmud_is_running = TRUE; + + g_main_loop_run(wmud_main_loop); + } + + wmud_configuration_free(&wmud_configuration); + + if (wmud_is_running) + { + wmud_shutdown(); + } + + Context gnome_vfs_shutdown(); + + wmud_log_debug("Good bye..."); + + return 0; +} + diff --git a/src/wmud.h b/src/wmud.h new file mode 100644 index 0000000..847b1e2 --- /dev/null +++ b/src/wmud.h @@ -0,0 +1,32 @@ +#ifndef __WMUD_CORE_WMUD_H__ +#define __WMUD_CODE_WMUD_H__ + +#include + +/* DEBUG is defined in config.h, if present. + * DEBUG is needed for Context, so let's include config.h here. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct _wMUDThreadData +{ + GMainContext *main_context; + GMainLoop *main_loop; + GThread *thread; + gchar *name; + gboolean running; +} wMUDThreadData; + +extern pid_t wmud_pid; + +void wmud_shutdown(void); +#ifdef DEBUG +void wmud_print_context(char *filename, int linenum); +#endif /* DEBUG */ + +#endif /* __WMUD_CORE_WMUD_H__ */ + diff --git a/src/world.c b/src/world.c new file mode 100644 index 0000000..b57c319 --- /dev/null +++ b/src/world.c @@ -0,0 +1,47 @@ +#include + +#include "wmud-world.h" + +#include "wmud.h" +#include "logger.h" + +gpointer +wmud_world_thread(gpointer data) +{ + wMUDWorld *world; + + wMUDThreadData *thread_data = (wMUDThreadData *)data; + + wmud_log_info("Initializing world..."); + + /* g_main_context_get_thread_default() is only available since GLib 2.22; + * use g_main_context_new() otherwise + */ +#if (((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION >= 22)) || (GLIB_MAJOR_VERSION > 2)) + thread_data->main_context = g_main_context_get_thread_default(); +#else + thread_data->main_context = g_main_context_new(); +#endif + thread_data->main_loop = g_main_loop_new(thread_data->main_context, FALSE); + + /* TODO: Do the real initialization here */ + + world = wmud_world_new(); + + wmud_log_info("World initialized"); + + thread_data->running = TRUE; + + g_main_loop_run(thread_data->main_loop); + + g_main_loop_unref(thread_data->main_loop); + + thread_data->main_loop = NULL; + + wmud_log_info("World layer shutting down..."); + + thread_data->running = FALSE; + + return NULL; +} + diff --git a/src/world.h b/src/world.h new file mode 100644 index 0000000..6880265 --- /dev/null +++ b/src/world.h @@ -0,0 +1,7 @@ +#ifndef __WMUD_CORE_WORLD_H__ +#define __WMUD_CORE_WORLD_H__ + +gpointer wmud_world_thread(gpointer data); + +#endif /* __WMUD_CORE_WORLD_H__ */ +