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.
27 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <sys/socket.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
;
53 static void load_database()
55 gsize j
, len_keys
= 0;
56 gchar
*config_name
= g_build_filename(getenv("HOME"), ".vomak", "database", NULL
);
58 GKeyFile
*keyfile
= g_key_file_new();
62 // unload previously loaded data, aka reload
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
));
80 g_key_file_free(keyfile
);
86 static void signal_cb(gint sig
)
88 if (sig
== SIGTERM
|| sig
== SIGINT
)
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
)
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
);
125 while ((len
= socket_fd_gets(sock
, buf
, sizeof(buf
))) != -1)
127 if (strncmp("quit", buf
, 4) == 0)
131 else if (strncmp("reload", buf
, 6) == 0)
135 else if (strncmp("userlist", buf
, 8) == 0)
139 send(sock
, userlist
, strlen(userlist
), 0);
140 send(sock
, "\n", 1, 0);
143 send(sock
, "error\n", 6, 0);
147 msg_len
= g_snprintf(msg
, sizeof msg
, "%s\r\n", buf
);
148 send(irc_conn
.socket
, msg
, msg_len
, 0);
152 socket_fd_close(sock
);
158 static void hash_add_to_string(gpointer key
, gpointer value
, gpointer user_data
)
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
);
173 if (strncmp("?? ", msg
, 3) != 0)
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
);
188 result
= g_hash_table_lookup(config
->data
, msg
);
190 irc_send_message(&irc_conn
, NULL
, "%s: %s", msg
, result
);
195 static void config_free()
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);
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);
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
);
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");
255 static void init_socket()
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)
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
)
282 // delay the internal connected status to skip parsing of MOTD messages
283 irc_conn
.connected
= TRUE
;
289 void set_user_list(const gchar
*list
)
292 userlist
= g_strdup(list
);
296 gint
main(gint argc
, gchar
**argv
)
298 irc_conn
.connected
= FALSE
;
301 main_loop
= g_main_loop_new(NULL
, FALSE
);
302 config
= config_read();
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
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");
326 g_main_loop_run(main_loop
);