Add GIT repo information to the docs.
[vomak.git] / vomak.c
blobe4719a878bcddd72c1418eb464dc74b7b18daaf1
1 /*
2 * vomak.c - this file is part of vomak - a very simple IRC bot
4 * Copyright 2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #define _GNU_SOURCE
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <netdb.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <signal.h>
35 #include <glib.h>
37 #include "vomak.h"
38 #include "socket.h"
39 #include "irc.h"
41 #define CONFIG_SECTION_GENERAL "general"
42 #define CONFIG_SECTION_DATABASE "general"
45 static irc_conn_t irc_conn;
46 static socket_info_t socket_info;
47 static GMainLoop *main_loop = NULL;
48 static gchar *userlist = NULL;
49 config_t *config;
53 static void load_database()
55 gsize j, len_keys = 0;
56 gchar *config_name = g_build_filename(getenv("HOME"), ".vomak", "database", NULL);
57 gchar **keys;
58 GKeyFile *keyfile = g_key_file_new();
60 TRACE
62 // unload previously loaded data, aka reload
63 if (config->data)
65 g_hash_table_destroy(config->data);
68 g_key_file_load_from_file(keyfile, config_name, G_KEY_FILE_NONE, NULL);
70 config->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
72 keys = g_key_file_get_keys(keyfile, CONFIG_SECTION_DATABASE, &len_keys, NULL);
73 for (j = 0; j < len_keys; j++)
75 g_hash_table_insert(config->data, g_strdup(keys[j]),
76 g_key_file_get_string(keyfile, CONFIG_SECTION_DATABASE, keys[j], NULL));
78 g_strfreev(keys);
80 g_key_file_free(keyfile);
81 g_free(config_name);
86 static void signal_cb(gint sig)
88 if (sig == SIGTERM || sig == SIGINT)
90 gchar *signame;
92 switch (sig)
94 case SIGTERM: signame = "SIGTERM"; break;
95 case SIGINT: signame = "SIGINT"; break;
96 default: signame = "unknown"; break;
98 g_print("Received %s, cleaning up.\n", signame);
100 irc_goodbye(&irc_conn);
102 irc_finalize(&irc_conn);
103 socket_finalize(&socket_info);
105 g_main_loop_quit(main_loop);
111 static gboolean socket_input_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
113 gint fd, sock;
114 static gchar buf[1024];
115 static gchar msg[1024];
116 struct sockaddr_in caddr;
117 guint caddr_len, len, msg_len;
119 caddr_len = sizeof(caddr);
121 fd = g_io_channel_unix_get_fd(ioc);
122 sock = accept(fd, (struct sockaddr *)&caddr, &caddr_len);
124 TRACE
125 while ((len = socket_fd_gets(sock, buf, sizeof(buf))) != -1)
127 if (strncmp("quit", buf, 4) == 0)
129 signal_cb(SIGTERM);
131 else if (strncmp("reload", buf, 6) == 0)
133 load_database();
135 else if (strncmp("userlist", buf, 8) == 0)
137 if (userlist)
139 send(sock, userlist, strlen(userlist), 0);
140 send(sock, "\n", 1, 0);
142 else
143 send(sock, "error\n", 6, 0);
145 else
147 msg_len = g_snprintf(msg, sizeof msg, "%s\r\n", buf);
148 send(irc_conn.socket, msg, msg_len, 0);
149 vdebug(msg);
152 socket_fd_close(sock);
154 return TRUE;
158 static void hash_add_to_string(gpointer key, gpointer value, gpointer user_data)
160 if (key != NULL)
162 g_string_append(user_data, key);
163 g_string_append_c(user_data, ' ');
168 void query_help_system(const gchar *line, guint len)
170 gchar *msg = irc_get_message(line, len);
171 gchar *result;
173 if (strncmp("?? ", msg, 3) != 0)
174 return;
176 msg += 3;
178 if (strncmp("keywords", msg, 8) == 0)
180 GString *str = g_string_sized_new(256);
181 g_hash_table_foreach(config->data, hash_add_to_string, str);
183 irc_send_message(&irc_conn, NULL, "%s: %s", msg, str->str);
184 g_string_free(str, TRUE);
186 else
188 result = g_hash_table_lookup(config->data, msg);
189 if (result)
190 irc_send_message(&irc_conn, NULL, "%s: %s", msg, result);
195 static void config_free()
197 TRACE
198 g_free(config->socketname);
199 g_free(config->socketperm);
200 g_free(config->server);
201 g_free(config->channel);
202 g_free(config->servername);
203 g_free(config->nickname);
204 g_free(config->username);
205 g_free(config->realname);
206 g_free(config->nickserv_password);
207 //~ g_hash_table_destroy(config->data);
208 g_free(config);
209 config = NULL;
213 static config_t *config_read()
215 GKeyFile *keyfile = g_key_file_new();
216 gchar *config_name = g_build_filename(getenv("HOME"), ".vomak", "config", NULL);
217 config_t *config = g_new0(config_t, 1);
219 TRACE
221 g_key_file_load_from_file(keyfile, config_name, G_KEY_FILE_NONE, NULL);
223 config->socketname = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "socketname", NULL);
224 config->socketperm = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "socketperm", NULL);
225 config->server = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "server", NULL);
226 config->port = g_key_file_get_integer(keyfile, CONFIG_SECTION_GENERAL, "port", NULL);
227 config->channel = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "channel", NULL);
228 config->servername = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "servername", NULL);
229 config->nickname = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "nickname", NULL);
230 config->username = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "username", NULL);
231 config->realname = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "realname", NULL);
232 config->nickserv_password = g_key_file_get_string(keyfile, CONFIG_SECTION_GENERAL, "nickserv_password", NULL);
234 g_key_file_free(keyfile);
235 g_free(config_name);
237 // if anything could not be read, abort. We don't use default values
238 if (config->socketname == NULL ||
239 config->socketperm == NULL ||
240 config->server == NULL ||
241 config->channel == NULL ||
242 config->servername == NULL ||
243 config->nickname == NULL ||
244 config->username == NULL ||
245 config->realname == NULL)
247 fprintf(stderr, "Config file does not exist or is not complete.\n");
248 exit(1);
251 return config;
255 static void init_socket()
257 TRACE
259 /* create unix domain socket for remote operation */
260 socket_info.lock_socket = -1;
261 socket_info.lock_socket_tag = 0;
262 socket_init(&socket_info, config->socketname);
264 if (socket_info.lock_socket >= 0)
266 mode_t mode;
267 sscanf(config->socketperm, "%o", &mode);
268 /// FIXME for some reason the sticky bit is set when socketperm is "0640"
269 chmod(config->socketname, mode);
270 socket_info.read_ioc = g_io_channel_unix_new(socket_info.lock_socket);
271 g_io_channel_set_flags(socket_info.read_ioc, G_IO_FLAG_NONBLOCK, NULL);
272 socket_info.lock_socket_tag = g_io_add_watch(socket_info.read_ioc,
273 G_IO_IN|G_IO_PRI|G_IO_ERR, socket_input_cb, NULL);
278 static gboolean mark_connect(gpointer data)
280 TRACE
282 // delay the internal connected status to skip parsing of MOTD messages
283 irc_conn.connected = TRUE;
285 return FALSE;
289 void set_user_list(const gchar *list)
291 g_free(userlist);
292 userlist = g_strdup(list);
296 gint main(gint argc, gchar **argv)
298 irc_conn.connected = FALSE;
300 // init stuff
301 main_loop = g_main_loop_new(NULL, FALSE);
302 config = config_read();
303 load_database();
304 init_socket();
305 irc_connect(&irc_conn);
306 signal(SIGTERM, signal_cb);
307 signal(SIGINT, signal_cb);
309 g_timeout_add(10000, mark_connect, NULL);
310 g_timeout_add(180000, irc_query_names, &irc_conn);
312 // if not a debug build, deattach from console and go into background
313 #ifndef DEBUG
314 signal(SIGTTOU, SIG_IGN);
315 signal(SIGTTIN, SIG_IGN);
316 signal(SIGTSTP, SIG_IGN);
318 if (daemon(1, 0) < 0)
320 g_printerr("Unable to daemonize\n");
321 return -1;
323 #endif
325 // main loop
326 g_main_loop_run(main_loop);
328 config_free();
330 return 0;