# HG changeset patch # User Markus Bröker # Date 1229178450 -3600 # Node ID 586472add385ada845170826e7f209434cbd26e3 Initial Layout committer: Markus Bröker diff --git a/INSTALL b/INSTALL new file mode 100644 --- /dev/null +++ b/INSTALL @@ -0,0 +1,24 @@ +MCBOT INSTALLATION +------------------ + +STEP 1) + REGISTER YOUR BOT TO AN IRC SERVER + EDIT /var/lib/nobody/.mcbotrc + +STEP 2) + FIX PERMISSIONS AND HIDE YOUR REGISTRATION DATA + chown -R nobody:nogroup /var/lib/nobody + chmod 775 /var/lib/nobody + chmod 600 /var/lib/nobody/.mcbotrc + +STEP 3) + /etc/init.d/mcbot start + +STEP 4) + dbtool -a -k mcbot.cgi -v http://your.domain.com/cgi-bin/mcbot.cgi + dbtool -a -k mcbot.user -v # mbroeker + dbtool -a -k mcbot.email -v # pppool.de + +STEP 5) + Check your locale settings in /etc/init.d/mcbot + LANGUAGE="de_DE.UTF-8" or LANGUAGE="en_US.UTF-8" for example diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,18 @@ +# +# Project : mcbot +# + +Known Bugs: + * no known bugs :) + * PRIVMSG for n=some@unaffiliated/users + +Todo: + * UTF-8 aware Character-Encoding + * Dynamic Loading of plugins with dlopen + * implementation of some fun stuff + +Author: + * Markus Broeker mbroeker@largo.homelinux.org + +Comments: + * scanner/parser rewritten diff --git a/config/.mcbotrc b/config/.mcbotrc new file mode 100644 --- /dev/null +++ b/config/.mcbotrc @@ -0,0 +1,10 @@ +# +# This is a simple ~/.mcbotrc +# NO SPACING, PLEASE !! +# +NICK:test +PASSWORD:test +SERVER:irc.freenode.net +PORT:6667 +CHANNEL:#test +TOPIC:[:test:] Support Channel || ENTER !help for help || Source: http://largo.homelinux.org/svn/c/mcbot/trunk diff --git a/config/.screenrc b/config/.screenrc new file mode 100644 --- /dev/null +++ b/config/.screenrc @@ -0,0 +1,1 @@ +termcapinfo xterm|xterms|xs|rxvt ti@te@ diff --git a/debian/changelog b/debian/changelog new file mode 100644 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,9 @@ +mcbot (0.93-2) unstable; urgency=low + + * Initial Release + * the location of mcbot.cgi is stored in the db + * never include malloc, use stdlib instead + * mcbot.email and mcbot.user holds the admin information + * Locale Settings changed to C + + -- Markus Broeker Sun, 27 Apr 2008 09:05:54 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 --- /dev/null +++ b/debian/compat @@ -0,0 +1,1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 --- /dev/null +++ b/debian/control @@ -0,0 +1,12 @@ +Source: mcbot +Section: net +Priority: extra +Maintainer: Markus Broeker +Build-Depends: debhelper (>= 5) +Standards-Version: 3.7.2 + +Package: mcbot +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: mcbot is an irc bot + mcbot is a small irc bot with a factoids database. diff --git a/debian/copyright b/debian/copyright new file mode 100644 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,22 @@ +This package was debianized by Markus Broeker on +Tue, 25 Mar 2008 20:32:44 +0100. + +It was downloaded from http://largo.homelinux.org + +Upstream Author(s): + + mbroeker@largo.homelinux.org + +Copyright: + + mbroeker@largo.homelinux.org + +License: + + GPL + +The Debian packaging is (C) 2008, Markus Broeker and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff --git a/debian/dirs b/debian/dirs new file mode 100644 --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +var/lib/nobody/data +var/lib/nobody diff --git a/debian/docs b/debian/docs new file mode 100644 --- /dev/null +++ b/debian/docs @@ -0,0 +1,1 @@ +TODO diff --git a/debian/postinst b/debian/postinst new file mode 100644 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,57 @@ +#!/bin/sh +# postinst script for mcbot +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + if [ ! -f /var/lib/nobody/data/mcbot.dat ]; + then + echo "Creating initial database in /var/lib/nobody/data"; + dbtool -a -k 1 -v 1; + dbtool -d 1; + fi + + if [ -f /var/lib/nobody/.mcbotrc.dpkg-old ]; + then + mv -f /var/lib/nobody/.mcbotrc.dpkg-old /var/lib/nobody/.mcbotrc; + fi + + echo "Changing ownership in /var/lib/nobody" + usermod -s /bin/bash -d /var/lib/nobody nobody + chown -R nobody:nogroup /var/lib/nobody + echo "Setting permissions for /var/lib/nobody/.mcbotrc to 600" + chmod 600 /var/lib/nobody/.mcbotrc + echo "ENTER /etc/init.d/mcbot start after editing .mcbotrc" + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/postrm b/debian/postrm new file mode 100644 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,47 @@ +#!/bin/sh +# postrm script for mcbot +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + rm -rf /var/lib/nobody/data; + rm -f /var/lib/nobody/.mcbotrc; + rm -f /var/lib/nobody/mcbot-*.log; + usermod -d /nonexistent -s /bin/bash nobody + ;; + + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + usermod -d /nonexistent -s /bin/bash nobody + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/debian/preinst b/debian/preinst new file mode 100644 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,46 @@ +#!/bin/sh +# preinst script for mcbot +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + if [ -x /etc/init.d/mcbot ]; + then + /etc/init.d/mcbot stop; + fi + + if [ -f /var/lib/nobody/.mcbotrc ]; + then + cp -f /var/lib/nobody/.mcbotrc /var/lib/nobody/.mcbotrc.dpkg-old + fi + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/debian/rules b/debian/rules new file mode 100755 --- /dev/null +++ b/debian/rules @@ -0,0 +1,91 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + cmake src/ + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #docbook-to-man debian/mcbot.sgml > mcbot.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + if [ -f Makefile ]; then $(MAKE) clean; fi + rm -rf Makefile CMake* install* mcbot dbtool cmake_install.cmake + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/mcbot. + $(MAKE) DESTDIR=$(CURDIR)/debian/mcbot install + cp config/.mcbotrc debian/mcbot/var/lib/nobody/ + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/include/config.h b/include/config.h new file mode 100644 --- /dev/null +++ b/include/config.h @@ -0,0 +1,23 @@ +/** + * $Id: config.h 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/include/config.h $ + * + */ + +#ifndef CONFIG_H +#define CONFIG_H + +struct UserConfig { + char *nick; + char *pass; + char *server; + int port; + char *topic; + char *channel; +}; + +typedef struct UserConfig UC; + +int config (UC *, char *); + +#endif diff --git a/include/database.h b/include/database.h new file mode 100644 --- /dev/null +++ b/include/database.h @@ -0,0 +1,16 @@ +/** + * $Id: database.h 46 2008-01-09 23:59:17Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/include/database.h $ + * + */ + +#ifndef DATABASE_H +#define DATABASE_H + +char *db_insert (char *, char *, char *, int); +char *db_remove (char *, char *); +char *db_lookup (char *, char *); +char *db_list (char *); +char *db_elements (char *); +char *db_vaccuum (char *); +#endif diff --git a/include/irc.h b/include/irc.h new file mode 100644 --- /dev/null +++ b/include/irc.h @@ -0,0 +1,29 @@ +/** + * $Id: irc.h 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/include/irc.h $ + * + */ + +#ifndef IRC_H +#define IRC_H + +#define IRC_GENERAL_ERROR -50 +#define IRC_LOGIN_ERROR -51 +#define IRC_CONNECT_ERROR -52 + +struct Message { + FILE *stream; + char *nick; + char *user; + char *email; + char *command; + char *channel; + char *line; +}; + +typedef struct Message MSG; + +FILE *irc_connect (char *, unsigned int); +int irc_login (FILE *, char *, char *); +char *irc_parsemessage (const char *, MSG *); +#endif diff --git a/include/mcbot.h b/include/mcbot.h new file mode 100644 --- /dev/null +++ b/include/mcbot.h @@ -0,0 +1,19 @@ +/** + * $Id: mcbot.h 46 2008-01-09 23:59:17Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/include/mcbot.h $ + * + */ + +#ifndef MCBOT_H +#define MCBOT_H + +#ifndef VERSION +#define VERSION 0.0 +#endif + +#ifndef IRC_H +#include +#endif + +char *parse (MSG *, short *); +#endif diff --git a/locale/de/LC_MESSAGES/.keep b/locale/de/LC_MESSAGES/.keep new file mode 100644 diff --git a/locale/de/mcbot.po b/locale/de/mcbot.po new file mode 100644 --- /dev/null +++ b/locale/de/mcbot.po @@ -0,0 +1,57 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-12-26 15:44+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Markus Broeker \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: parse.c:78 +msgid "Request cannot be performed" +msgstr "Operation kann nicht durchgefuehrt werden" + +#: parse.c:94 +msgid "What's up" +msgstr "Was ist los" + +#: parse.c:149 +msgid "I need more parameters to add" +msgstr "Ich brauche zwei Parameter zum Anfuegen" + +#: parse.c:164 +msgid "I need more parameters to replace" +msgstr "Ich brauche zwei Parameter zum Ersetzen" + +#: parse.c:177 +msgid "I need a key to delete" +msgstr "Ich brauche einen Schluessel zum Loeschen" + +#: parse.c:195 +msgid "I need a key to lookup" +msgstr "Ich brauche einen Parameter zum Aufsuchen" + +#: parse.c:214 +msgid "Autolearn enabled for channel" +msgstr "Autolearn ist aktiviert im Channel" + +#: parse.c:220 +msgid "Autolearn disabled for channel" +msgstr "Autolearn ist deaktiviert im Channel" + +#: parse.c:240 +msgid "Bye, have a nice day!" +msgstr "Machts gut, ich brauch ne Pause!" + +#: parse.c:268 +msgid "It is" +msgstr "Es ist" diff --git a/scripts/mcbot.cgi b/scripts/mcbot.cgi new file mode 100755 --- /dev/null +++ b/scripts/mcbot.cgi @@ -0,0 +1,32 @@ +#!/usr/bin/perl -W + +use strict; + +my $line; +my $a; +my $b; + +print "CONTENT-TYPE: text/html\n\n"; + +print "\n\t\n\t\tFactoids\n\t\n\n"; +open(FD, "dbtool -l |") or die("Cannot open file"); + +foreach $line() { + ($a, $b) = split(/:/, $line, 2); + $a =~ s/^[\t ]+//; # skip blanks + $b =~ s/^[\t ]+//; # skip blanks + $b =~ s/[\n]+//; # for html source :) + + if ($b) { + if($b =~ /^http/ ) { + print "$a - $b
"; + } else { + print "$a - $b
"; + } + } +} + +print "\n
$a\n"; + +print "\n"; +close(FD); diff --git a/scripts/runbot b/scripts/runbot new file mode 100755 --- /dev/null +++ b/scripts/runbot @@ -0,0 +1,56 @@ +#!/bin/sh + +BINARY="mcbot" +DIRECTORY="/usr/local/sbin/" +LANGUAGE="de_DE.UTF-8" + +[[ ! -x "${DIRECTORY}/${BINARY}" ]] && exit 0 + +start() { + echo -n "Starting ${BINARY}" + su - nobody -c "LANG=$LANGUAGE ${DIRECTORY}/${BINARY} &>/var/lib/nobody/mcbot-`date "+%d-%m-%y"`.log &" + if [ "$?" == 0 ]; then + echo " [started]" + fi +} + +stop() { + echo -n "Stopping ${BINARY}" + pid=`pidof ${BINARY}` + if [ "$pid" != "" ]; then + kill -TERM `pidof ${BINARY}` + sleep 5 + fi + echo " [stopped]" +} + +status() { + pid="`pidof ${BINARY}`" + if [ "$pid" != "" ]; then + echo "${BINARY} is started as ${pid}" + else + echo "${BINARY} is not running" + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status + ;; + restart) + echo "Restarting ${BINARY}" + stop + start + ;; + *) + echo "Usage: start|stop|restart|status" + ;; +esac + +exit 0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,37 @@ +PROJECT(mc_bot) + +# Project Settings +SET(CMAKE_VERBOSE_MAKEFILE Off) +SET(CMAKE_BUILD_TYPE Release) +SET(CMAKE_C_FLAGS_RELEASE "-g -ggdb") + +# C-Definitions +ADD_DEFINITIONS(-Wall -O2 -ansi -pedantic) +ADD_DEFINITIONS(-D_XOPEN_SOURCE=500) +ADD_DEFINITIONS(-DVERSION=0.91) + +# Includes +INCLUDE_DIRECTORIES(../include) + +# Target Definitions +ADD_EXECUTABLE(mcbot config.c database.c irc.c main.c parse.c) +ADD_EXECUTABLE(dbtool database.c dbtool.c) + +# Install Rules +INSTALL(TARGETS mcbot RUNTIME DESTINATION sbin) +INSTALL(TARGETS dbtool RUNTIME DESTINATION bin) +INSTALL(DIRECTORY ../locale/ DESTINATION /var/lib/nobody/data/locale) +INSTALL(PROGRAMS ../scripts/runbot DESTINATION /etc/init.d/ + RENAME mcbot) + +EXEC_PROGRAM(msgfmt ../locale ARGS -o de/LC_MESSAGES/mcbot.mo de/mcbot.po) + +# Search required libraries +FIND_LIBRARY(GDBM_LIB NAMES gdbm + PATHS /usr/lib/ /usr/local/lib) +IF (GDBM_LIB) + TARGET_LINK_LIBRARIES(mcbot ${GDBM_LIB}) + TARGET_LINK_LIBRARIES(dbtool ${GDBM_LIB}) +ELSE(GDBM_LIB) + MESSAGE("YOU NEED TO INSTALL libgdbm-dev") +ENDIF (GDBM_LIB) diff --git a/src/config.c b/src/config.c new file mode 100644 --- /dev/null +++ b/src/config.c @@ -0,0 +1,110 @@ +/** + * $Id: config.c 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/config.c $ + * + */ + +#include +#include +#include + +#include + +const +char *CONFIG_OPTIONS[] = { + "NICK", "PASSWORD", "SERVER", "PORT", + "CHANNEL", "TOPIC", + NULL +}; + +int config (UC * uc, char *fname) +{ + FILE *f; + char buffer[513]; + static char **line; + char *token; + char *value; + int i = 0; + + if ((f = fopen (fname, "r")) == NULL) + return -1; + + while (CONFIG_OPTIONS[i] != NULL) + i++; + + line = calloc (i, sizeof (char *)); + + /* + * We can easily provide default values ... + */ + uc->nick = uc->pass = uc->server = uc->channel = uc->topic = 0; + uc->port = 6667; + + fgets (buffer, 512, f); + + token = buffer; + + while (!feof (f)) { + /* + * eat trailing tabs + */ + while (*token == '\t') + token++; + + /* + * eat trailing whitespaces + */ + while (*token == ' ') + token++; + + token = strtok (token, ":"); + + if (token != NULL) { + value = strtok (NULL, "\n"); + i = 0; + while (CONFIG_OPTIONS[i] != NULL) { + if (!strcmp (token, CONFIG_OPTIONS[i])) { + line[i] = malloc (strlen (value) + 1); + strncpy (line[i], value, strlen (value)); + } + i++; + } + } + *buffer = 0; + fgets (buffer, 512, f); + } + + if (fclose (f) != 0) + return -1; + + i = 0; + while (CONFIG_OPTIONS[i] != NULL) { + if (line[i] != NULL) + switch (i) { + case 0: /* NICK */ + uc->nick = line[i]; + break; + case 1: /* PASSWORD */ + uc->pass = line[i]; + break; + case 2: /* SERVER */ + uc->server = line[i]; + break; + case 3: /* PORT */ + uc->port = atoi (line[i]); + break; + case 4: /* CHANNEL */ + uc->channel = line[i]; + break; + case 5: /* TOPIC */ + uc->topic = line[i]; + break; + } + i++; + } + + if (!(uc->nick && uc->pass && uc->server && uc->channel)) + return -2; + + return 0; +} diff --git a/src/database.c b/src/database.c new file mode 100644 --- /dev/null +++ b/src/database.c @@ -0,0 +1,150 @@ +/** + * $Id: database.c 108 2008-04-17 01:01:52Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/database.c $ + * + */ + +#include +#include +#include +#include + +#include + +char *db_insert (char *file_name, char *name, char *msg, int mode) +{ + GDBM_FILE dbf; + datum key; + datum content; + int ret; + + if ((dbf = gdbm_open (file_name, 512, GDBM_WRCREAT, 0644, 0)) == NULL) + return "db_insert error"; + + key.dptr = name; + key.dsize = strlen (name) + 1; + + content.dptr = msg; + content.dsize = strlen (msg) + 1; + + if (mode) + ret = gdbm_store (dbf, key, content, GDBM_REPLACE); + else + ret = gdbm_store (dbf, key, content, GDBM_INSERT); + + gdbm_close (dbf); + + if (!ret) + return "Key added"; + + return "error: key not added"; +} + +char *db_remove (char *file_name, char *name) +{ + GDBM_FILE dbf; + datum key; + int ret; + + if ((dbf = gdbm_open (file_name, 512, GDBM_WRITER, 0, 0)) == NULL) + return "db_remove error"; + + key.dptr = name; + key.dsize = strlen (name) + 1; + + ret = gdbm_delete (dbf, key); + + gdbm_close (dbf); + + if (!ret) + return "Key removed"; + return "error: key not removed"; +} + +char *db_lookup (char *file_name, char *entry) +{ + GDBM_FILE dbf; + datum content; + datum key; + static char msg[513]; + + if ((dbf = gdbm_open (file_name, 512, GDBM_READER, 0, 0)) == NULL) + return "db_lookup error"; + + key.dptr = entry; + key.dsize = strlen (entry) + 1; + + content = gdbm_fetch (dbf, key); + if (content.dptr != NULL) + snprintf (msg, 512, "%s", content.dptr); + else + snprintf (msg, 512, "I haven't heard anything about %s.", entry); + + gdbm_close (dbf); + return msg; +} + +char *db_elements (char *file_name) +{ + GDBM_FILE dbf; + datum key; + int count; + static char msg[81]; + + if ((dbf = gdbm_open (file_name, 512, GDBM_READER, 0, 0)) == NULL) + return "db_lookup error"; + + key = gdbm_firstkey (dbf); + + count = 0; + while (key.dptr != NULL) { + key = gdbm_nextkey (dbf, key); + count++; + } + + gdbm_close (dbf); + snprintf (msg, 80, "I am holding %d %s in my database.", count, (count > 0) ? "rows" : "row"); + return msg; +} + +char *db_list (char *file_name) +{ + GDBM_FILE dbf; + datum content; + datum key; + static char msg[81]; + int count; + + if ((dbf = gdbm_open (file_name, 512, GDBM_READER, 0, 0)) == NULL) + return "db_list error"; + + key = gdbm_firstkey (dbf); + + count = 0; + while (key.dptr) { + content = gdbm_fetch (dbf, key); + printf ("%11s: %s\n", key.dptr, content.dptr); + free (content.dptr); + key = gdbm_nextkey (dbf, key); + count++; + } + + gdbm_close (dbf); + snprintf (msg, 80, "I am holding %d %s in my database.", count, (count > 0) ? "rows" : "row"); + return msg; +} + +char *db_vaccuum (char *file_name) +{ + GDBM_FILE dbf; + static char msg[81]; + + if ((dbf = gdbm_open (file_name, 512, GDBM_WRITER, 0, 0)) == NULL) + return "db_vaccuum error"; + + gdbm_reorganize (dbf); + + gdbm_close (dbf); + snprintf (msg, 80, "I reorganized the database."); + return msg; +} diff --git a/src/dbtool.c b/src/dbtool.c new file mode 100644 --- /dev/null +++ b/src/dbtool.c @@ -0,0 +1,137 @@ +/** + * $Id: dbtool.c 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/dbtool.c $ + * + */ + +#include +#include +#include + +#include + +#ifndef FILE_NAME +#define FILE_NAME "/var/lib/nobody/data/mcbot.dat" +#endif + +void help (char *prgname) +{ + printf ("Usage: %s [[-a|-r] -k -v ]\n", prgname); + printf (" %s [-d ]\n", prgname); + printf (" %s [-f ]\n", prgname); + printf (" %s [-s ]\n", prgname); + printf (" %s [-h|-?]\n\n", prgname); + printf ("Administration tool for adding/replacing, deleting, ... of DataBase Entries.\n\n"); + printf ("Options:\n"); + printf (" -a\t\tadds a new key with value to the db\n"); + printf (" -r\t\treplaces a key with value in the db\n"); + printf (" -d\t\tdeletes a from the db\n"); + printf (" -f\t\tsets the db file\n"); + printf (" -l\t\tlists the db entries\n"); + printf (" -s\t\tsearches the db entry \n"); + printf (" -V\t\treorganizes the db\n"); + printf (" -h\t\tprints this help\n"); + printf (" -?\t\tprints this help\n\n"); + printf ("Report bugs to mbroeker@largo.homelinux.org\n"); + exit (0); +} + +#define ADD 1 +#define REPLACE 2 +#define DELETE 3 +#define SEARCH 4 +#define LIST 5 +#define VACCUUM 6 + +int main (int argc, char **argv) +{ + int i; + int mode = 0; + char *key = NULL; + char *value = NULL; + char *file_name = NULL; + + while ((i = getopt (argc, argv, "arlVh?d:f:k:s:v:")) > 0) { + switch (i) { + case '?': + case 'h': + help (argv[0]); + break; + case 'a': + mode = ADD; + break; + case 'r': + mode = REPLACE; + break; + case 'd': + mode = DELETE; + key = optarg; + break; + case 'f': + file_name = optarg; + break; + case 'k': + key = optarg; + break; + case 's': + key = optarg; + mode = SEARCH; + break; + case 'v': + value = optarg; + break; + case 'l': + mode = LIST; + break; + case 'V': + mode = VACCUUM; + break; + default: + printf ("Unknown Option %c\n", i); + } + } + + for (i = optind; i < argc; i++) { + printf ("Unknown Parameter: %s\n", argv[i]); + } + + if (!file_name) + file_name = FILE_NAME; + + switch (mode) { + case ADD: + if ((key != NULL) && (value != NULL)) + printf ("%s\n", db_insert (file_name, key, value, 0)); + else + help (argv[0]); + break; + case REPLACE: + if ((key != NULL) && (value != NULL)) + printf ("%s\n", db_insert (file_name, key, value, 1)); + else + help (argv[0]); + break; + case DELETE: + if (key != NULL) + printf ("%s\n", db_remove (file_name, key)); + else + help (argv[0]); + break; + case SEARCH: + if (key != NULL) + printf ("%s\n", db_lookup (file_name, key)); + else + help (argv[0]); + break; + case LIST: + printf ("%s\n", db_list (file_name)); + break; + case VACCUUM: + printf ("%s\n", db_vaccuum (file_name)); + break; + default: + printf ("Nothing to do: Try %s -h\n", argv[0]); + } + + return EXIT_SUCCESS; +} diff --git a/src/irc.c b/src/irc.c new file mode 100644 --- /dev/null +++ b/src/irc.c @@ -0,0 +1,323 @@ +/** + * $Id: irc.c 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/irc.c $ + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define VERSION_STRING "MCBOT on GNU/LINUX" + +const char *IRC_Commands[] = { + "NOTICE", "MODE", "JOIN", "PART", + "TOPIC", "PING", "ENOMEM", "ERROR", + "VERSION", "PRIVMSG", "QUIT", "NICK", + NULL, +}; + +FILE *irc_connect (char *server, unsigned int port) +{ + struct hostent *he; + char *ip; + struct sockaddr_in ca; + int csocket; + FILE *stream; + + he = gethostbyname (server); + if (he == NULL) { + perror ("GETHOSTBYNAME"); + return NULL; + } + + if ((ip = inet_ntoa (*((struct in_addr *)he->h_addr_list[0]))) == NULL) { + perror ("GETHOSTBYNAME"); + return NULL; + } else + printf ("IP: %s\n", ip); + + ca.sin_family = AF_INET; + ca.sin_addr.s_addr = inet_addr (ip); + ca.sin_port = htons (port); + + csocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connect (csocket, (struct sockaddr *)&ca, (socklen_t) sizeof (ca)) == -1) { + perror ("CONNECT"); + return NULL; + } + + /* + * rw mode,but many seek errors ... + */ + stream = fdopen (csocket, "a+"); + return stream; +} + +int irc_login (FILE * stream, char *nick, char *password) +{ + MSG message; + char msg[513]; + char *user; + struct passwd *pwd = NULL; + + if ((user = getenv ("USER")) != NULL) + if ((pwd = getpwnam (user)) == NULL) + return IRC_GENERAL_ERROR; + + if (strlen (password) == 0) + return IRC_LOGIN_ERROR; + + if (stream == NULL) + return IRC_GENERAL_ERROR; + + fprintf (stream, "NICK %s\r\n", nick); + + fprintf (stream, "USER %s 0 irc.freenode.net %s\r\n", user, pwd->pw_gecos); + + fprintf (stream, "PRIVMSG NICKSERV :IDENTIFY %s\r\n", password); + + for (;;) { + fgets (msg, 512, stream); + if ((user = irc_parsemessage (msg, &message)) != NULL) + printf ("%10s %s\n", user, message.line); + + if (strstr (msg, "VERSION") != NULL) { + printf ("%10s %s", "VERSION", msg); + printf ("%10s %s %s :%s\n", "WRITE", message.command, message.user, VERSION_STRING); + fprintf (stream, "VERSION :%s\r\n", VERSION_STRING); + } + + if (strstr (msg, ":Password accepted") != NULL) { + break; + } + + if (strstr (msg, ":Password Incorrect") != NULL) { + return IRC_LOGIN_ERROR; + } + + if (strstr (msg, "ERROR :Closing Link") != NULL) { + return IRC_GENERAL_ERROR; + } + + if (strstr (msg, "is not registered") != NULL) { + return IRC_LOGIN_ERROR; + } + } + + sleep (2); + return 0; +} + +static char *irc_getmessage (const char *line, MSG * message) +{ + char *theLine; + char *token; + char *ptr; + + if ((theLine = strdup (line)) == NULL) + return "ENOMEM"; + + message->user = message->email = NULL; + message->command = NULL; + message->channel = message->line = NULL; + + token = strtok (theLine, " "); + if (*token != ':') { + /* + * SERVER MESSAGES + */ + if ((ptr = strtok (NULL, "\r\n")) != NULL) + ++ptr; + message->line = ptr; + return token; + } + + message->user = ++token; + message->command = strtok (NULL, " "); + + message->line = strtok (NULL, "\r\n"); + + return message->command; +} + +/* + * Main prints ("%10s %s %s\n", MSG->command MSG->channel MSG->line). + * This functions makes sure, that there will be someting to print... + */ + +char *irc_parsemessage (const char *line, MSG * message) +{ + int i; + char *command; + char *ptr; + int value; + + i = 0; + if ((command = irc_getmessage (line, message)) != NULL) { + while (IRC_Commands[i] != NULL) { + if (strcmp (IRC_Commands[i], command) == 0) { + switch (i) { + + case 0: /* NOTICE */ + if (message->line == NULL) { + message->channel = "*"; + message->line = "*"; + } else { + message->channel = strtok (message->line, " "); + if ((message->line = strtok (NULL, "\r\n")) != NULL) + ++message->line; + } + return command; + case 1: /* MODE */ + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + case 2: /* JOIN */ + case 3: /* PART */ + if ((message->channel = strchr (message->line, ':'))) + ++message->channel; + message->line = message->user; + return command; + case 4: /* TOPIC */ + message->channel = strtok (message->line, " "); + if ((message->line = strtok (NULL, "\r\n"))) + ++message->line; + return command; + case 5: /* PING */ +#ifdef DEBUG + printf ("%10s %s localhost\n", "PING", message->line); +#endif + fprintf (message->stream, "PONG :%s\r\n", message->line); + message->channel = "localhost"; +#ifdef DEBUG + return "PONG"; +#else + return NULL; +#endif + case 6: /* ENOMEM */ + case 7: /* ERROR */ + exit (-1); + break; + case 8: /* VERSION */ + if ((ptr = strchr (message->user, ' '))) + *ptr = 0; + return command; + case 9: /* PRIVMSG */ + if ((message->email = strchr (message->user, '='))) + ++message->email; + if ((ptr = strchr (message->user, '!'))) + *ptr = 0; + + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + message->line++; + printf ("%10s %s %s :%s\n", "READ", message->command, message->channel, message->line); + return NULL; + case 10: /* QUIT */ + message->channel = message->user; + return command; + case 11: /* NICK */ + message->channel = message->user; + return command; + } + } + i++; + } + + if ((value = atoi (command)) != 0) { + switch (value) { + case 1: /* CONNECTION INFOS */ + case 2: + case 3: + case 4: + case 5: + case 250: + case 251: + case 252: + case 254: + case 255: + case 265: + case 266: + /* + * prints as is in irc_login + */ + return command; + break; + case 311: + case 312: + case 315: /* END OF WHO */ + case 318: + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + case 319: + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + fprintf (message->stream, "PRIVMSG %s :%s\r\n", "#mcbot", message->line); + return command; + case 320: + case 332: /* TOPIC OF CHANNEL */ + case 333: /* NAMES IN CHANNEL */ + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + case 351: /* SVERSION */ + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + case 352: /* WHO LIST */ + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + /* + * MORE THAN 3 LINES AND YOU WILL be KICKED + */ + return command; + break; + case 353: + case 365: + case 366: /* END OF NAMES */ + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + break; + case 372: /* MOTD MESSAGES */ + case 375: + case 376: /* END OF MOTD */ + return command; + break; + case 401: /* NO SUCH NICK/CHANNEL */ + case 403: /* That CHANNEL doesnt exist */ + return command; + break; + case 474: + case 475: + case 476: + case 477: + message->channel = strtok (message->line, " "); + message->line = strtok (NULL, "\r\n"); + return command; + break; + default: + printf ("DEBUG %s", line); + printf ("Unknown Value: %d\n", value); + } + } + printf ("DEBUG %s", line); + printf ("Unknown Command: %s\n", command); + return command; + } + printf ("DEBUG %s", line); + return NULL; +} diff --git a/src/main.c b/src/main.c new file mode 100644 --- /dev/null +++ b/src/main.c @@ -0,0 +1,135 @@ +/** + * $Id: main.c 51 2008-01-10 00:19:39Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/main.c $ + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/var/lib/nobody/.mcbotrc" +#endif + +#ifndef LOCALE_PATH +#define LOCALE_PATH "/var/lib/nobody/data/locale" +#endif + +short active = 1; + +void sigproc () +{ + signal (SIGTERM, sigproc); + active = 0; +} + +int main (int argc, char **argv) +{ + UC uc; + MSG message; + char buf[513]; + char *msg; + char *command; + int len; + + if (bindtextdomain ("mcbot", LOCALE_PATH) != NULL) { + (void)textdomain ("mcbot"); + (void)setlocale (LC_MESSAGES, ""); + } + + printf ("mcbot-%1.2f\n", VERSION); + + if ((len = config (&uc, CONFIG_FILE)) != 0) { + switch (len) { + case -1: + printf ("You need to create a config file %s\n", CONFIG_FILE); + break; + case -2: + printf + ("You need at least entries for user, password, server and channel in your config file %s\n", + CONFIG_FILE); + break; + } + return len; + } + + if (uc.nick) + message.nick = uc.nick; + else + return -1; + + if (!(message.stream = irc_connect (uc.server, uc.port))) + return EXIT_FAILURE; + + if ((len = irc_login (message.stream, uc.nick, uc.pass)) != 0) { + switch (len) { + case IRC_GENERAL_ERROR: + printf ("GENERAL ERROR\n"); + break; + case IRC_LOGIN_ERROR: + printf ("LOGIN ERROR\n"); + break; + default: + printf ("Unknown Error %d\n", len); + } + return len; + } + + signal (SIGTERM, sigproc); + + if (uc.channel) { + fprintf (message.stream, "JOIN :%s\r\n", uc.channel); + if (uc.topic) + fprintf (message.stream, "TOPIC %s :%s\r\n", uc.channel, uc.topic); + } + + while (active) { + if (fgets (buf, 512, message.stream) == NULL) { + active = 0; + break; + } + + if ((command = irc_parsemessage (buf, &message))) { + printf ("%10s %s %s\n", command, message.channel, message.line); + } else { + if ((msg = parse (&message, &active)) != NULL) { + fprintf (message.stream, "%s\r\n", msg); + printf ("%10s %s", "WRITE", msg); + } + } + *buf = 0; + } + printf ("\n\nClosing Connection\n\n"); + fclose (message.stream); + + /* + * cleanup + */ + + if (uc.nick) + free (uc.nick); + if (uc.pass) + free (uc.pass); + if (uc.server) + free (uc.server); + if (uc.channel) + free (uc.channel); + if (uc.topic) + free (uc.topic); + + return EXIT_SUCCESS; +} diff --git a/src/parse.c b/src/parse.c new file mode 100644 --- /dev/null +++ b/src/parse.c @@ -0,0 +1,278 @@ +/** + * $Id: parse.c 153 2008-04-27 07:26:15Z mbroeker $ + * $URL: http://localhost/svn/c/mcbot/trunk/src/parse.c $ + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef DATABASE_FILE +#define DATABASE_FILE "/var/lib/nobody/data/mcbot.dat" +#endif + +const +char *COMMAND_LIST[] = { + "!help Known Commands: join(1), leave(2), add(3), replace(4), delete(5), list(6), search(7), info(8)\r\n", + "!join: Joins a new channel\r\n", + "!leave: Parts from the current channel\r\n", + "!add: adds an entry\r\n", + "!replace: replaces an entry\r\n", + "!delete: deletes an entry\r\n", + "!list: lists the number of stored values\r\n", + "!search: searches an entry up\r\n", + "!info: Prints the current Bot-Version\r\n", + "!ping: pings an host\r\n", + "!on: enables autolearning mode\r\n", + "!off: disables autolearning\r\n", + "!debug: prints some debug infos\r\n", + "!vaccuum: reorganizes the database\r\n", + "!logout: Protected logout function\r\n", + NULL, +}; + +const +char ITEMS = 14; + +const +char *Bot_Commands[] = { + "!help", "!join", "!leave", + "!add", "!replace", "!delete", + "!list", "!search", "!info", + "!ping", "!on", "!off", + "!debug", "!vaccuum", "!logout", + "!who", "!whois", "!time", "!tell", + "!op", + NULL, +}; + +char *parse (MSG * message, short *active) +{ + static char msg[513]; + int cmd = -1; + int i; + char *token; + char *parameters; + + time_t t; + struct tm *timeptr; + static int counter = 0; + + /* + * default message + */ + snprintf (msg, 512, "PRIVMSG %s :%s.\r\n", message->channel, gettext ("Request cannot be performed")); + + /* + * PRIVATE MESSAGES + */ + if (!strcmp (message->channel, message->nick)) + message->channel = message->user; + + if (strstr (message->line, message->nick)) { + if (*message->line != '!') { + /* + * DEAD - LOCK - CHECK + */ + if (strcmp (message->user, message->nick)) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s?\r\n", message->channel, gettext ("What's up"), message->user); + if (counter++ > 3) + return NULL; + return msg; + } + /* + * DEAD - LOCK - CHECK ENDS + */ + } + return NULL; + } + + counter = 0; + + /* + * NO BOT Commands + */ + if (*message->line != '!') { + return NULL; + } + + i = 0; + token = strtok (message->line, " "); + while (Bot_Commands[i]) { + if (!strcmp (token, Bot_Commands[i])) { + switch (i) { + case 0: /* !help */ + if ((token = strtok (NULL, "\r\n"))) + cmd = atoi (token); + if ((cmd > 0) && (cmd < ITEMS)) + snprintf (msg, 512, "PRIVMSG %s :%s\r\n", message->channel, COMMAND_LIST[cmd]); + else + snprintf (msg, 512, "PRIVMSG %s :%s\r\n", message->channel, COMMAND_LIST[0]); + return msg; + + case 1: /* !join */ + if ((token = strtok (NULL, "\r\n"))) + snprintf (msg, 512, "JOIN %s\r\n", token); + return msg; + + case 2: /* !leave */ + if (*message->channel != '#') + return NULL; + snprintf (msg, 512, "PART %s :Leaving.\r\n", message->channel); + return msg; + + case 3: /* !add */ + if ((token = strtok (NULL, " "))) { + if ((parameters = strtok (NULL, "\r\n"))) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s\r\n", + message->channel, message->user, db_insert (DATABASE_FILE, token, parameters, 0)); + } else { + snprintf (msg, 512, + "PRIVMSG %s :%s, %s!\r\n", + message->channel, gettext ("I need more parameters to add"), message->user); + } + } + return msg; + + case 4: /* !replace */ + if ((token = strtok (NULL, " "))) { + if ((parameters = strtok (NULL, "\r\n"))) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s\r\n", + message->channel, message->user, db_insert (DATABASE_FILE, token, parameters, 1)); + } else { + snprintf (msg, 512, + "PRIVMSG %s :%s, %s!\r\n", + message->channel, gettext ("I need more parameters to replace"), message->user); + } + } + return msg; + + case 5: /* !delete */ + if ((token = strtok (NULL, "\r\n"))) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s\r\n", + message->channel, message->user, db_remove (DATABASE_FILE, token)); + } else { + snprintf (msg, 512, + "PRIVMSG %s :%s, %s!\r\n", + message->channel, gettext ("I need a key to delete"), message->user); + } + return msg; + + case 6: /* !count */ + snprintf (msg, 512, "PRIVMSG %s :%s %s\r\n", message->channel, + db_elements (DATABASE_FILE), db_lookup(DATABASE_FILE, "mcbot.cgi")); + return msg; + + case 7: /* !search */ + if ((token = strtok (NULL, "\r\n"))) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s\r\n", + message->channel, message->user, db_lookup (DATABASE_FILE, token)); + } else { + snprintf (msg, 512, + "PRIVMSG %s :%s, %s!\r\n", + message->channel, gettext ("I need a key to lookup"), message->user); + } + return msg; + + case 8: /* !info */ + snprintf (msg, 512, + "PRIVMSG %s :I am MCBot-%1.2f and my source code can be found on %s\r\n", + message->channel, VERSION, "http://largo.homelinux.org/svn/c/mcbot/trunk"); + return msg; + + case 9: /* !ping */ + if ((token = strtok (NULL, "\r\n"))) + snprintf (msg, 512, "PRIVMSG %s :PING 0815\r\n", token); + return msg; + + case 10: /* !on */ + snprintf (msg, 512, + "PRIVMSG %s :%s %s.\r\n", + message->user, gettext ("Autolearn enabled for channel"), message->channel); + return msg; + + case 11: /* !off */ + snprintf (msg, 512, + "PRIVMSG %s :%s %s.\r\n", + message->user, gettext ("Autolearn disabled for channel"), message->channel); + return msg; + + case 12: /* !debug */ + snprintf (msg, 512, + "PRIVMSG %s :USER: %s EMAIL: %s CHANNEL: %s LINE: %s\r\n", + message->channel, message->user, message->email, message->channel, message->line); + return msg; + + case 13: /* !vaccum */ + snprintf (msg, 512, "PRIVMSG %s :%s\r\n", message->channel, db_vaccuum (DATABASE_FILE)); + return msg; + + case 14: /* !logout */ + if (strstr (message->user, db_lookup(DATABASE_FILE, "mcbot.user"))) { + if (strstr (message->email, db_lookup(DATABASE_FILE, "mcbot.email"))) { + snprintf (msg, 512, + "PRIVMSG %s :%s!\r\nQUIT\r\n", message->channel, gettext ("Bye, have a nice day!")); + *active = 0; + } + } + /* + * the returned message is either the default one or the generated one + */ + return msg; + + case 15: /* !who */ + if ((token = strtok (NULL, "\r\n")) != NULL) { + snprintf (msg, 512, "WHO %s\r\n", token); + } + return msg; + break; + + case 16: /* !whois */ + if ((token = strtok (NULL, "\r\n")) != NULL) { + snprintf (msg, 512, "WHOIS %s\r\n", token); + } + return msg; + break; + case 17: /* time */ + t = time (NULL); + timeptr = localtime (&t); + if ((token = malloc (81))) { + strftime (token, 80, "%I:%M:%S %p", timeptr); + snprintf (msg, 512, "PRIVMSG %s :%s %s, %s!\r\n", + message->channel, gettext ("It is"), token, message->user); + free (token); + } + return msg; + break; + case 18: /* tell */ + if ((token = strtok (NULL, " "))) { + if ((parameters = strtok (NULL, "\r\n"))) { + snprintf (msg, 512, "PRIVMSG %s :%s, %s\r\n", + (*token == + '*') ? ++token : message->channel, token, db_lookup (DATABASE_FILE, parameters)); + } + } + return msg; + break; + case 19: /* op */ + if ((token = strtok (NULL, "\r\n")) != NULL) { + if (strstr (message->email, db_lookup(DATABASE_FILE, "mcbot.email"))) + snprintf (msg, 512, "MODE %s +o %s\r\n", message->channel, token); + } + return msg; + break; + } + } + i++; + } + + return NULL; +}