Add preference for query names interval, makes it also possible to disable.
[vomak.git] / src / irc.c
blob67ba4f6a63915eeb3ce183dbfb01248614680a10
1 /*
2 * irc.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>
5 * Copyright 2008 Dominic Hopf <dh(at)dmaphy(dot)de>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * 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 <strings.h>
34 #include <signal.h>
35 #include <time.h>
36 #ifndef DEBUG
37 # include <syslog.h>
38 #endif
40 #include <glib.h>
41 #include <glib/gstdio.h>
43 #include "vomak.h"
44 #include "socket.h"
45 #include "irc.h"
48 config_t *config;
51 static void irc_kick(irc_conn_t *irc_conn, const gchar *nickname);
52 static gboolean irc_is_user_op(irc_conn_t *irc_conn, const gchar *nickname);
54 enum
56 GOODIES_COFFEE,
57 GOODIES_COKE,
58 GOODIES_BEER,
59 GOODIES_PIZZA
61 typedef struct
63 const gchar *command;
64 const gchar *message;
65 } goodies_t;
67 const goodies_t goodies[] = {
68 { "!coffee", "A nice sexy waitress brings %s a big cup of coffee!" },
69 { "!coke", "A nice sexy waitress brings %s a cool bottle of coke!" },
70 { "!beer", "A nice sexy waitress brings %s a nice bottle of beer!" },
71 { "!pizza", "Someone calls Mario, and he brings %s a tasty hawaiian pizza!" },
72 { NULL, NULL }
75 gboolean irc_query_names(gpointer data)
77 irc_conn_t *irc = data;
78 static gchar msg[1024];
79 guint msg_len;
81 TRACE
83 msg_len = g_snprintf(msg, sizeof msg, "NAMES #%s\r\n", config->channel);
84 // send the message directly to avoid logging (prevent log file spamming)
85 send(irc->socket, msg, msg_len, 0);
87 return TRUE;
91 /* 'line' should be terminated by "\r\n" (CRLF) */
92 static void irc_log(irc_conn_t *irc_conn, const gchar *line, gboolean send)
94 time_t t;
95 struct tm *tp;
96 static gchar str[256];
97 GString *log_line;
99 if (! irc_conn->log_fd)
100 return;
102 t = time(NULL);
103 tp = localtime(&t);
104 strftime(str, sizeof str, "%F %T %z ", tp);
106 log_line = g_string_new(str);
107 if (send) // if we are sending a message, add our ident string
109 g_string_append_printf(log_line, ":%s!n=%s@%s ",
110 config->nickname, config->username, config->servername);
112 g_string_append(log_line, line);
113 /* add \r\n if it is missing */
114 if (strncmp(log_line->str + log_line->len - 2, "\r\n", 2) != 0)
116 g_string_append(log_line, "\r\n");
118 fwrite(log_line->str, log_line->len, 1, irc_conn->log_fd);
119 fflush(irc_conn->log_fd);
120 g_string_free(log_line, TRUE);
124 static void irc_send_private_message(irc_conn_t *irc_conn, const gchar *target, const gchar *format, ...)
126 static gchar tmp_msg[1024];
127 static gchar msg[1024];
128 guint msg_len;
129 va_list ap;
131 if (target == NULL)
132 return;
134 va_start(ap, format);
135 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
136 va_end(ap);
138 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG %s :%s, %s\r\n", target, target, tmp_msg);
139 irc_send(irc_conn, msg, msg_len);
143 static gchar *get_nickname(const gchar *line, guint len)
145 static gchar result[20];
146 guint i, j = 0;
148 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df
149 for (i = 0; i < len; i++)
151 if (line[i] == '!' || line[i] == '=' || j >= 19)
152 break;
154 if (line[i] == ':')
155 continue;
157 result[j++] = line[i];
159 result[j] = '\0';
161 return result;
165 static gchar *get_private_message_sender(const gchar *line, guint len)
167 guint i;
169 // :eht16!n=enrico@uvena.de PRIVMSG GeanyTestBot :hi
170 for (i = 0; i < len; i++)
172 if (line[i] != ' ')
173 continue;
175 if (strncmp("PRIVMSG", line + i + 1, 7) == 0)
177 static gchar name[20];
178 g_snprintf(name, sizeof(name), "%s :", config->nickname);
179 // if the receiver of the message is me, then it's a private message and we return the
180 // sender's nickname, otherwise NULL
181 if (strncmp(name, line + i + 9, strlen(config->nickname) + 2) == 0)
182 return get_nickname(line, len);
183 else
184 return NULL;
188 return NULL;
192 static gint get_response(const gchar *line, guint len)
194 static gchar result[4];
195 guint i, j = 0;
196 gboolean in_response = FALSE;
198 // :kornbluth.freenode.net 353 GeanyTestBot @ #eht16 :GeanyTestBot eht16
199 // :kornbluth.freenode.net 366 GeanyTestBot #eht16 :End of /NAMES list.
200 for (i = 0; i < len; i++)
202 // before a response code
203 if (line[i] != ' ' && ! in_response)
204 continue;
206 // after a response code
207 if (line[i] == ' ' && in_response)
209 in_response = FALSE;
210 break;
213 if (line[i] == ' ' )
214 i++; // skip the space
216 result[j++] = line[i];
217 in_response = TRUE;
219 result[j] = '\0';
221 return atoi(result);
225 const gchar *irc_get_message(const gchar *line, guint len)
227 static gchar result[1024] = { 0 };
228 guint i, j = 0;
229 gint state = 0; // represents the current part of the whole line, separated by spaces
231 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df foo: var
232 // -> df foo: var
233 for (i = 0; i < len; i++)
235 if (state < 3)
237 if (line[i] == ' ')
239 state++;
240 i++; // skip the ':'
241 continue;
244 else if (line[i] != '\r' && line[i] != '\n')
246 result[j++] = line[i];
249 result[j] = '\0';
251 return result;
255 static const gchar *irc_get_message_with_name(const gchar *line, guint len, const gchar *name)
257 const gchar *tmp;
258 gsize name_len;
260 tmp = irc_get_message(line, len);
261 name_len = strlen(name);
263 if (strncmp(tmp, name, name_len) == 0)
264 tmp += name_len;
266 return tmp;
270 // returns a nickname argument given to cmd, it may also be "cmd for nickname", then the "for" is
271 // skipped
272 static const gchar *get_argument_target(const gchar *line, guint len, const gchar *cmd)
274 static gchar result[20];
275 const gchar *tmp;
276 gchar **parts;
278 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!beer for me
279 tmp = irc_get_message(line, len);
281 // -> !beer for me
282 parts = g_strsplit(tmp, " ", -1);
283 if (parts == NULL || parts[0] == NULL)
285 g_strfreev(parts);
286 return NULL;
289 // if cmd doesn't match, skip it
290 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
292 g_strfreev(parts);
293 return NULL;
296 if (parts[1] == NULL)
298 g_strfreev(parts);
299 return NULL;
302 if (strcmp("for", parts[1]) == 0)
304 if (parts[2] != NULL)
306 if (strcmp(parts[2], "me") == 0)
308 g_strfreev(parts);
309 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
311 else
312 g_strlcpy(result, parts[2], sizeof(result));
314 else
315 g_strlcpy(result, parts[1], sizeof(result));
317 else if (strcmp(parts[1], "me") == 0)
319 g_strfreev(parts);
320 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
322 else
324 g_strlcpy(result, parts[1], sizeof(result));
327 g_strfreev(parts);
328 return result;
332 // Parses the line and puts the first argument in arg1, all further arguments are concatenated
333 // in arg2. arg1 and arg2 should be freed when no longer needed.
334 // If arg1 and arg2 were set successfully, TRUE is returned, if any error occurs, FALSE is returned
335 // and arg1 and arg2 are set to NULL.
336 static gboolean get_argument_two(const gchar *line, guint len, const gchar *cmd,
337 gchar **arg1, gchar **arg2)
339 const gchar *tmp;
340 gchar **parts;
342 *arg1 = NULL;
343 *arg2 = NULL;
345 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!learn keyword text to be added
346 tmp = irc_get_message(line, len);
348 // -> !learn keyword text to be added
349 parts = g_strsplit(tmp, " ", 3);
350 if (parts == NULL || parts[0] == NULL)
352 g_strfreev(parts);
353 return FALSE;
356 // if cmd doesn't match, skip it
357 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
359 g_strfreev(parts);
360 return FALSE;
363 if (parts[1] == NULL || parts[2] == NULL)
365 g_strfreev(parts);
366 return FALSE;
369 *arg1 = g_strdup(parts[1]);
370 *arg2 = g_strdup(parts[2]);
372 g_strfreev(parts);
374 return TRUE;
378 static void command_fortune(irc_conn_t *irc_conn)
380 GError *error = NULL;
381 gchar *out = NULL;
382 gchar *err = NULL;
384 if (g_spawn_command_line_sync(config->fortune_cmd, &out, &err, NULL, &error))
386 if (out != NULL && *out != '\0')
388 gchar **lines = g_strsplit(out, "\n", -1);
389 gsize i, len;
391 len = g_strv_length(lines);
392 for (i = 0; i < len; i++)
394 if (strlen(g_strchomp(lines[i])) > 0)
395 irc_send_message(irc_conn, NULL, lines[i]);
397 g_strfreev(lines);
399 else
401 vomak_warning("Executing fortune failed (%s)", err);
403 g_free(out);
404 g_free(err);
406 else
408 vomak_warning("Executing fortune failed (%s)", error->message);
409 g_error_free(error);
414 static void command_moo(irc_conn_t *irc_conn)
416 gint32 rand = g_random_int();
418 if (rand % 2 == 0)
420 gsize i;
421 const gchar *moo_str[] = {
422 " ^__^\r\n",
423 " (oo)\r\n",
424 " /-----(__)\r\n",
425 " / | ||\r\n",
426 " * /\\---/\\\r\n",
427 " ~~ ~~\n\r\n",
428 "..\"Have you mooed today?\"..\r\n",
429 NULL
432 for (i = 0; moo_str[i] != NULL; i++)
434 irc_send_message(irc_conn, NULL, moo_str[i]);
437 else
439 irc_send_message(irc_conn, NULL, "I have Super Cow Powers. Have you mooed today?");
444 static void command_learn(irc_conn_t *irc_conn, const gchar *line, guint len)
446 gchar *arg1, *arg2;
448 if (get_argument_two(line, len, "!learn", &arg1, &arg2))
450 gint result = help_system_learn(arg1, arg2);
451 gchar *text;
453 switch (result)
455 case 0:
457 text = g_strdup_printf("new keyword \"%s\" was added.", arg1);
458 break;
460 case 1:
462 text = g_strdup_printf("existing keyword \"%s\" was updated.", arg1);
463 break;
465 default:
467 text = g_strdup("an error occurred. Database not updated.");
468 break;
471 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
473 g_free(text);
474 g_free(arg1);
475 g_free(arg2);
477 else
478 irc_send_message(irc_conn, get_nickname(line, len),
479 "wrong usage of !learn. Use \"?? learn\" for usage information.");
483 static void command_alias(irc_conn_t *irc_conn, const gchar *line, guint len)
485 gchar *arg1, *arg2;
487 if (get_argument_two(line, len, "!alias", &arg1, &arg2))
489 // detect if arg2 has more than one word by scanning for spaces in
490 // the string
491 if (strchr(arg2, ' '))
493 irc_send_message(irc_conn, get_nickname(line, len),
494 "You gave me more than two arguments for !alias. I can not handle this.");
496 // check if the target actually exist and refuse if it doesn't exist
497 else if (g_hash_table_lookup(config->data, arg2) == NULL)
499 irc_send_message(irc_conn, get_nickname(line, len),
500 "The given target for the alias does not exist. I will refuse your request.");
502 else
504 gint result;
505 gchar *text;
506 gchar *alias = g_strconcat("@", arg2, NULL);
508 result = help_system_learn(arg1, alias);
510 switch (result)
512 case 0:
514 text = g_strdup_printf("new alias \"%s\" was added.", arg1);
515 break;
517 case 1:
519 text = g_strdup_printf("existing alias \"%s\" was updated.", arg1);
520 break;
522 default:
524 text = g_strdup("An error occurred. Database not updated.");
525 break;
529 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
531 g_free(alias);
532 g_free(text);
533 g_free(arg1);
534 g_free(arg2);
537 else
538 irc_send_message(irc_conn, get_nickname(line, len),
539 "wrong usage of !alias. Use \"?? alias\" for usage information.");
543 static void command_goodies(irc_conn_t *irc_conn, const gchar *line, guint len, gint goodie)
545 const goodies_t *g = &goodies[goodie];
546 const gchar *arg = get_argument_target(line, len, g->command);
548 if (arg == NULL)
549 arg = get_nickname(line, len);
551 irc_send_message(irc_conn, NULL,
552 g->message, arg);
556 static void process_command(irc_conn_t *irc_conn, const gchar *line, guint len, const gchar *content)
558 // !test
559 if (strncmp(content, "!test", 5) == 0)
561 irc_send_message(irc_conn, get_nickname(line, len), "I don't like tests!");
563 // !moo
564 else if (strncmp(content, "!moo", 4) == 0)
566 command_moo(irc_conn);
568 // !fortune
569 else if (config->fortune_cmd != NULL && strncmp(content, "!fortune", 8) == 0)
571 command_fortune(irc_conn);
573 // !coffee
574 else if (strncmp(content, "!coffee", 7) == 0)
576 command_goodies(irc_conn, line, len, GOODIES_COFFEE);
578 // !coke
579 else if (strncmp(content, "!coke", 5) == 0)
581 command_goodies(irc_conn, line, len, GOODIES_COKE);
583 // !beer
584 else if (strncmp(content, "!beer", 5) == 0)
586 command_goodies(irc_conn, line, len, GOODIES_BEER);
588 // !pizza
589 else if (strncmp(content, "!pizza", 5) == 0)
591 command_goodies(irc_conn, line, len, GOODIES_PIZZA);
593 // !help
594 else if (strncmp(content, "!help", 5) == 0)
596 help_system_query("?? help");
599 * Fun with !roulette
600 * You have to register your bot with nickserv and add it to the access-list
601 * of your channel to make the !roulette-command work.
602 * This is just tested on FreeNode. Please feel free to write patches, that
603 * will make this work on other Networks.
605 else if (strncmp(content, "!roulette", 9) == 0)
607 gint32 rand = g_random_int();
608 static gint bullets_left = 6;
610 if (rand % 6 == 0 || bullets_left <= 0)
612 irc_send_message(irc_conn, NULL, "*bang*");
613 irc_kick(irc_conn, get_nickname(line, len));
614 bullets_left = 6;
617 else
619 irc_send_message(irc_conn, NULL, "*click*");
620 bullets_left--;
623 // !learn
624 /// TODO require op privileges for !learn
625 else if (strncmp(content, "!learn", 6) == 0)
627 command_learn(irc_conn, line, len);
629 // !alias
630 else if (strncmp(content, "!alias", 6) == 0)
632 command_alias(irc_conn, line, len);
634 // ?? ...
635 else if (strncmp(content, "?? ", 3) == 0)
637 help_system_query(content);
642 static gboolean process_line(irc_conn_t *irc_conn, const gchar *line, guint len)
644 static gchar msg[1024];
645 guint msg_len;
646 gint response = get_response(line, len);
647 static gchar tmp_userlist[1024];
648 gchar *priv_sender;
649 const gchar *content;
650 static gboolean connected = FALSE;
652 content = irc_get_message(line, len);
654 // don't log the NAMES command's output (prevent log file spam)
655 if (response != 353 && response != 366)
656 irc_log(irc_conn, line, FALSE);
658 if (! connected)
660 if (response == 376)
661 connected = TRUE;
662 else
663 return TRUE;
666 // An error occurred, try to quit cleanly and print the error
667 if (response > 400 && response < 503)
669 // ignore Freenode's info messages sent with error code 477
670 // (see http://freenode.net/faq.shtml#freenode-info)
671 if (response != 477 || strstr(line, "[freenode-info]") == NULL)
673 g_print("Error: %s", line);
674 #ifndef DEBUG
675 syslog(LOG_WARNING, "received error: %d (%s)", response, g_strstrip((gchar*) line));
676 #endif
677 main_quit();
678 return FALSE;
681 // retrieve user name list
682 else if (response == 353)
684 if (tmp_userlist[0] == '\0')
685 g_strlcpy(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
686 else
687 g_strlcat(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
689 // retrieve end of user name list
690 else if (response == 366)
692 if (tmp_userlist[0] != '\0')
694 set_user_list(tmp_userlist);
695 tmp_userlist[0] = '\0';
698 else if (! connected)
700 // don't do anything else until we got finished connecting (to skip MOTD messages)
702 // PING-PONG
703 else if (strncmp("PING :", line, 6) == 0)
705 msg_len = g_snprintf(msg, sizeof msg, "PONG %s\r\n", line + 6); // 7 = "PING :"
706 debug("PONG: -%s-\n", msg);
707 irc_send(irc_conn, msg, msg_len);
709 // handle private message
710 else if ((priv_sender = get_private_message_sender(line, len)) != NULL)
712 // to be able to send private messages to users, you need to register your bot's
713 // nickname with Nickserv (at least on Freenode)
714 irc_send_private_message(irc_conn, priv_sender, "I don't like private messages!");
716 // Hi /me, acts on "hello $nickname" and "hi $nickname", hi and hello are case-insensitive
717 // Thanks /me
718 else if (strstr(content, config->nickname) != NULL)
720 const gchar *tmp_msg = irc_get_message_with_name(line, len, config->nickname);
722 if (strncasecmp("hi", content, 2) == 0 || strncasecmp("hello", content, 5) == 0 || strncasecmp("hey", content, 3) == 0 ||
723 strcasecmp(", hi", tmp_msg) == 0 || strcasecmp(", hello", tmp_msg) == 0 || strcasecmp(", hey", tmp_msg) == 0 ||
724 strcasecmp(": hi", tmp_msg) == 0 || strcasecmp(": hello", tmp_msg) == 0 || strcasecmp(": hey", tmp_msg) == 0)
726 irc_send_message(irc_conn, NULL,
727 "Hi %s. My name is %s and I'm here to offer additional services to you! "
728 "Try \"?? help\" for general information and \"?? vomak\" for information about me.",
729 get_nickname(line, len), config->nickname);
731 else if (strncasecmp("thanks", content, 6) == 0 || strncasecmp("thx", content, 3) == 0 ||
732 strcasecmp(", thanks", tmp_msg) == 0 || strcasecmp(", thx", tmp_msg) == 0 ||
733 strcasecmp(": thanks", tmp_msg) == 0 || strcasecmp(": thx", tmp_msg) == 0)
735 irc_send_message(irc_conn, get_nickname(line, len),
736 "no problem. It was a pleasure to serve you.");
739 // pass to process_command() to process other commands (!beer, !test, !learn, ...)
740 else
742 process_command(irc_conn, line, len, content);
745 return TRUE;
750 * Please note that this will not work on Networks without ChanServ, e.g. on
751 * Quakenet or IRCnet. Your Bot has to be registered with NickServ and to be
752 * added to the channel access list for this to work.
754 static gboolean irc_toggle_op(irc_conn_t *irc_conn, gboolean request_op)
756 const gchar *cmd;
757 static gchar msg[1024];
758 guint msg_len;
760 if (irc_is_user_op(irc_conn, "ChanServ"))
762 cmd = (request_op) ? "op" : "deop";
763 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG ChanServ :%s #%s\r\n", cmd, config->channel);
764 irc_send(irc_conn, msg, msg_len);
766 return TRUE;
768 return FALSE; /* it seems we don't have a ChanServ bot */
772 static gboolean irc_is_user_op(irc_conn_t *irc_conn, const gchar *nickname)
774 const gchar *pos;
775 const gchar *userlist;
777 if (nickname == NULL)
778 return FALSE;
780 userlist = get_user_list();
782 if ( (pos = strstr(userlist, nickname)) )
784 if ( (pos - 1) >= userlist && (*(pos - 1) == '@') )
786 return TRUE;
789 else
791 #ifdef DEBUG
792 irc_send_message(irc_conn, NULL,
793 "Hey. There are crazy things going on here. (O.o)");
794 #else
795 syslog(LOG_WARNING, "user %s not found in names list of #%s", nickname, config->channel);
796 #endif
799 return FALSE;
803 static void irc_kick(irc_conn_t *irc_conn, const gchar *nickname)
805 static gchar msg[1024];
806 gboolean need_deop = FALSE;
807 guint msg_len;
809 TRACE
811 if (! irc_is_user_op(irc_conn, config->nickname))
813 // if irc_toggle_op fails, most probably we don't have a ChanServ and at this point we
814 // know we are not an op, so fail silently and don't try to kick
815 if (! irc_toggle_op(irc_conn, TRUE)) /// TODO: prüfen, ob das auch erfolreich war
816 return;
817 need_deop = TRUE;
820 // give the server a chance to set the op status for us before we make us of it,
821 // and let the victim read his *bang* message
822 g_usleep(1500000);
824 msg_len = g_snprintf(msg, sizeof msg, "KICK #%s %s\r\n", config->channel, nickname);
825 irc_send(irc_conn, msg, msg_len);
826 if (need_deop)
827 irc_toggle_op(irc_conn, FALSE);
831 static gboolean input_cb(GIOChannel *source, GIOCondition cond, gpointer data)
833 #if 1
834 gchar buf[1024];
835 guint buf_len;
836 irc_conn_t *irc = data;
837 gboolean ret = TRUE;
839 if ((buf_len = socket_fd_gets(irc->socket, buf, sizeof(buf))) != -1)
841 ret = process_line(irc, buf, buf_len);
843 #else
844 gsize buf_len;
845 irc_conn_t *irc = data;
847 if (cond & (G_IO_IN | G_IO_PRI))
849 gchar *buf = NULL;
850 GIOStatus rv;
851 GError *err = NULL;
855 rv = g_io_channel_read_line(source, &buf, &buf_len, NULL, &err);
856 if (buf != NULL)
858 buf_len -= 2;
859 buf[buf_len] = '\0'; // skip trailing \r\n
861 process_line(irc, buf, buf_len);
862 g_free(buf);
864 if (err != NULL)
866 debug("%s: error: %s", __func__, err->message);
867 g_error_free(err);
868 err = NULL;
871 while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
872 debug("%s: status %d\n", __func__, rv);
874 #endif
875 return ret;
879 void irc_send_message(irc_conn_t *irc_conn, const gchar *target, const gchar *format, ...)
881 static gchar tmp_msg[1024];
882 static gchar msg[1024];
883 guint msg_len;
884 va_list ap;
886 va_start(ap, format);
887 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
888 va_end(ap);
890 if (target)
891 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s, %s\r\n", config->channel, target, tmp_msg);
892 else
893 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s\r\n", config->channel, tmp_msg);
895 irc_send(irc_conn, msg, msg_len);
899 // simple wrapper for send() to enable logging for sent commands
900 gint irc_send(irc_conn_t *irc_conn, const gchar *msg, guint msg_len)
902 irc_log(irc_conn, msg, TRUE);
903 return send(irc_conn->socket, msg, msg_len, 0);
907 void irc_goodbye(irc_conn_t *irc)
909 guint len;
910 gchar msg[256];
912 if (NZV(irc->quit_msg))
913 len = g_snprintf(msg, sizeof msg, "QUIT :%s\r\n", irc->quit_msg);
914 else
915 len = g_strlcpy(msg, "QUIT :Good bye. It was a pleasure to serve you\r\n", sizeof msg);
916 irc_send(irc, msg, len);
920 void irc_logfile_reopen(irc_conn_t *irc_conn)
922 TRACE
924 if (irc_conn->log_fd != NULL)
925 fclose(irc_conn->log_fd);
927 if (NZV(config->logfile))
929 irc_conn->log_fd = g_fopen(config->logfile, "a");
930 if (! irc_conn->log_fd)
931 vomak_warning("Logfile could not be opened.");
936 gint irc_finalize(irc_conn_t *irc_conn)
938 if (irc_conn->socket < 0)
939 return -1;
941 if (irc_conn->lock_tag > 0)
942 g_source_remove(irc_conn->lock_tag);
944 if (irc_conn->log_fd != NULL)
946 irc_log(irc_conn, "Stop logging\r\n", FALSE);
947 fclose(irc_conn->log_fd);
950 if (irc_conn->read_ioc)
952 g_io_channel_shutdown(irc_conn->read_ioc, TRUE, NULL);
953 g_io_channel_unref(irc_conn->read_ioc);
954 irc_conn->read_ioc = NULL;
956 socket_fd_close(irc_conn->socket);
957 irc_conn->socket = -1;
959 g_free(irc_conn->quit_msg);
961 return 0;
965 void irc_connect(irc_conn_t *irc_conn)
967 struct hostent *he;
968 struct sockaddr_in their_addr;
969 gchar msg[256];
970 guint msg_len;
972 TRACE
974 // Connect the socket to the server
975 if ((he = gethostbyname(config->server)) == NULL)
977 perror("gethostbyname");
978 exit(1);
981 if ((irc_conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1)
983 perror("socket");
984 exit(1);
987 their_addr.sin_family = PF_INET;
988 their_addr.sin_port = htons(6667);
989 their_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
990 memset(&(their_addr.sin_zero), '\0', 8);
992 if (connect(irc_conn->socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
994 perror("connect");
995 exit(1);
997 // Logging
998 if (NZV(config->logfile))
1000 irc_conn->log_fd = g_fopen(config->logfile, "a");
1001 if (irc_conn->log_fd)
1002 irc_log(irc_conn, "Start logging\r\n", FALSE);
1003 else
1004 vomak_warning("Logfile could not be opened.");
1006 // say who we are
1007 msg_len = g_snprintf(msg, sizeof(msg), "USER %s %s %s :%s\r\n",
1008 config->username, config->servername, config->servername, config->realname);
1009 if (irc_send(irc_conn, msg, msg_len) == -1)
1011 perror("send USER");
1013 // and how we are called
1014 msg_len = g_snprintf(msg, sizeof(msg), "NICK %s\r\n", config->nickname);
1015 if (irc_send(irc_conn, msg, msg_len) == -1)
1017 perror("send NICK");
1019 // identify our nick
1020 if (NZV(config->nickserv_password))
1022 msg_len = g_snprintf(msg, sizeof(msg), "PRIVMSG nickserv :identify %s\r\n", config->nickserv_password);
1023 // don't use irc_send() here, no need to log our password
1024 if (send(irc_conn->socket, msg, msg_len, 0) == -1)
1026 perror("send NICKSERV");
1029 // join the channel
1030 g_snprintf(msg, sizeof msg, "JOIN #%s\r\n", config->channel);
1031 if (irc_send(irc_conn, msg, strlen(msg)) == -1)
1033 perror("send");
1036 // input callback, attached to the main loop
1037 irc_conn->read_ioc = g_io_channel_unix_new(irc_conn->socket);
1038 //~ g_io_channel_set_flags(irc_conn.read_ioc, G_IO_FLAG_NONBLOCK, NULL);
1039 g_io_channel_set_encoding(irc_conn->read_ioc, NULL, NULL);
1040 irc_conn->lock_tag = g_io_add_watch(irc_conn->read_ioc, G_IO_IN|G_IO_PRI|G_IO_ERR, input_cb, irc_conn);