Use MSG_NOSIGNAL flag when sending the user list to prevent possible SIGPIPE signals.
[vomak.git] / src / irc.c
blob183da14fef004fd5fd7682209b36477f17955f21
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_command_sender(const gchar *line, guint len, const gchar *command)
167 guint i;
168 gsize cmd_len = strlen(command);
170 // :eht16!n=enrico@uvena.de PRIVMSG GeanyTestBot :hi
171 for (i = 0; i < len; i++)
173 if (line[i] != ' ')
174 continue;
176 if (strncmp(command, line + i + 1, cmd_len) == 0)
178 static gchar name[20];
179 g_snprintf(name, sizeof(name), "%s :", config->nickname);
180 // if the receiver of the message is me, then it's a private message and
181 // we return the sender's nickname, otherwise NULL
182 if (strncmp(name, line + i + cmd_len + 2, strlen(config->nickname) + 2) == 0 ||
183 strncmp("KICK", command, 4) == 0)
185 return get_nickname(line, len);
187 else
188 return NULL;
192 return NULL;
196 static gint get_response(const gchar *line, guint len)
198 static gchar result[4];
199 guint i, j = 0;
200 gboolean in_response = FALSE;
202 // :kornbluth.freenode.net 353 GeanyTestBot @ #eht16 :GeanyTestBot eht16
203 // :kornbluth.freenode.net 366 GeanyTestBot #eht16 :End of /NAMES list.
204 for (i = 0; i < len; i++)
206 // before a response code
207 if (line[i] != ' ' && ! in_response)
208 continue;
210 // after a response code
211 if (line[i] == ' ' && in_response)
213 in_response = FALSE;
214 break;
217 if (line[i] == ' ' )
218 i++; // skip the space
220 result[j++] = line[i];
221 in_response = TRUE;
223 result[j] = '\0';
225 return atoi(result);
229 const gchar *irc_get_message(const gchar *line, guint len)
231 static gchar result[1024] = { 0 };
232 guint i, j = 0;
233 gint state = 0; // represents the current part of the whole line, separated by spaces
235 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df foo: var
236 // -> df foo: var
237 for (i = 0; i < len; i++)
239 if (state < 3)
241 if (line[i] == ' ')
243 state++;
244 i++; // skip the ':'
245 continue;
248 else if (line[i] != '\r' && line[i] != '\n')
250 result[j++] = line[i];
253 result[j] = '\0';
255 return result;
259 static const gchar *irc_get_message_with_name(const gchar *line, guint len, const gchar *name)
261 const gchar *tmp;
262 gsize name_len;
264 tmp = irc_get_message(line, len);
265 name_len = strlen(name);
267 if (strncmp(tmp, name, name_len) == 0)
268 tmp += name_len;
270 return tmp;
274 // returns a nickname argument given to cmd, it may also be "cmd for nickname", then the "for" is
275 // skipped
276 static const gchar *get_argument_target(const gchar *line, guint len, const gchar *cmd)
278 static gchar result[20];
279 const gchar *tmp;
280 gchar **parts;
282 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!beer for me
283 tmp = irc_get_message(line, len);
285 // -> !beer for me
286 parts = g_strsplit(tmp, " ", -1);
287 if (parts == NULL || parts[0] == NULL)
289 g_strfreev(parts);
290 return NULL;
293 // if cmd doesn't match, skip it
294 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
296 g_strfreev(parts);
297 return NULL;
300 if (parts[1] == NULL)
302 g_strfreev(parts);
303 return NULL;
306 if (strcmp("for", parts[1]) == 0)
308 if (parts[2] != NULL)
310 if (strcmp(parts[2], "me") == 0)
312 g_strfreev(parts);
313 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
315 else
316 g_strlcpy(result, parts[2], sizeof(result));
318 else
319 g_strlcpy(result, parts[1], sizeof(result));
321 else if (strcmp(parts[1], "me") == 0)
323 g_strfreev(parts);
324 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
326 else
328 g_strlcpy(result, parts[1], sizeof(result));
331 g_strfreev(parts);
332 return result;
336 // Parses the line and puts the first argument in arg1, all further arguments are concatenated
337 // in arg2. arg1 and arg2 should be freed when no longer needed.
338 // If arg1 and arg2 were set successfully, TRUE is returned, if any error occurs, FALSE is returned
339 // and arg1 and arg2 are set to NULL.
340 static gboolean get_argument_two(const gchar *line, guint len, const gchar *cmd,
341 gchar **arg1, gchar **arg2)
343 const gchar *tmp;
344 gchar **parts;
346 *arg1 = NULL;
347 *arg2 = NULL;
349 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!learn keyword text to be added
350 tmp = irc_get_message(line, len);
352 // -> !learn keyword text to be added
353 parts = g_strsplit(tmp, " ", 3);
354 if (parts == NULL || parts[0] == NULL)
356 g_strfreev(parts);
357 return FALSE;
360 // if cmd doesn't match, skip it
361 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
363 g_strfreev(parts);
364 return FALSE;
367 if (parts[1] == NULL || parts[2] == NULL)
369 g_strfreev(parts);
370 return FALSE;
373 *arg1 = g_strdup(parts[1]);
374 *arg2 = g_strdup(parts[2]);
376 g_strfreev(parts);
378 return TRUE;
382 static void command_fortune(irc_conn_t *irc_conn)
384 GError *error = NULL;
385 gchar *out = NULL;
386 gchar *err = NULL;
388 if (g_spawn_command_line_sync(config->fortune_cmd, &out, &err, NULL, &error))
390 if (out != NULL && *out != '\0')
392 gchar **lines = g_strsplit(out, "\n", -1);
393 gsize i, len;
395 len = g_strv_length(lines);
396 for (i = 0; i < len; i++)
398 if (strlen(g_strchomp(lines[i])) > 0)
399 irc_send_message(irc_conn, NULL, lines[i]);
401 g_strfreev(lines);
403 else
405 vomak_warning("Executing fortune failed (%s)", err);
407 g_free(out);
408 g_free(err);
410 else
412 vomak_warning("Executing fortune failed (%s)", error->message);
413 g_error_free(error);
418 static void command_moo(irc_conn_t *irc_conn)
420 gint32 rand = g_random_int();
422 if (rand % 2 == 0)
424 gsize i;
425 const gchar *moo_str[] = {
426 " ^__^\r\n",
427 " (oo)\r\n",
428 " /-----(__)\r\n",
429 " / | ||\r\n",
430 " * /\\---/\\\r\n",
431 " ~~ ~~\n\r\n",
432 "..\"Have you mooed today?\"..\r\n",
433 NULL
436 for (i = 0; moo_str[i] != NULL; i++)
438 irc_send_message(irc_conn, NULL, moo_str[i]);
441 else
443 irc_send_message(irc_conn, NULL, "I have Super Cow Powers. Have you mooed today?");
448 static void command_learn(irc_conn_t *irc_conn, const gchar *line, guint len)
450 gchar *arg1, *arg2;
452 if (get_argument_two(line, len, "!learn", &arg1, &arg2))
454 gint result = help_system_learn(arg1, arg2);
455 gchar *text;
457 switch (result)
459 case 0:
461 text = g_strdup_printf("new keyword \"%s\" was added.", arg1);
462 break;
464 case 1:
466 text = g_strdup_printf("existing keyword \"%s\" was updated.", arg1);
467 break;
469 default:
471 text = g_strdup("an error occurred. Database not updated.");
472 break;
475 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
477 g_free(text);
478 g_free(arg1);
479 g_free(arg2);
481 else
482 irc_send_message(irc_conn, get_nickname(line, len),
483 "wrong usage of !learn. Use \"?? learn\" for usage information.");
487 static void command_alias(irc_conn_t *irc_conn, const gchar *line, guint len)
489 gchar *arg1, *arg2;
491 if (get_argument_two(line, len, "!alias", &arg1, &arg2))
493 // detect if arg2 has more than one word by scanning for spaces in
494 // the string
495 if (strchr(arg2, ' '))
497 irc_send_message(irc_conn, get_nickname(line, len),
498 "You gave me more than two arguments for !alias. I can not handle this.");
500 // check if the target actually exist and refuse if it doesn't exist
501 else if (g_hash_table_lookup(config->data, arg2) == NULL)
503 irc_send_message(irc_conn, get_nickname(line, len),
504 "The given target for the alias does not exist. I will refuse your request.");
506 else
508 gint result;
509 gchar *text;
510 gchar *alias = g_strconcat("@", arg2, NULL);
512 result = help_system_learn(arg1, alias);
514 switch (result)
516 case 0:
518 text = g_strdup_printf("new alias \"%s\" was added.", arg1);
519 break;
521 case 1:
523 text = g_strdup_printf("existing alias \"%s\" was updated.", arg1);
524 break;
526 default:
528 text = g_strdup("An error occurred. Database not updated.");
529 break;
533 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
535 g_free(alias);
536 g_free(text);
537 g_free(arg1);
538 g_free(arg2);
541 else
542 irc_send_message(irc_conn, get_nickname(line, len),
543 "wrong usage of !alias. Use \"?? alias\" for usage information.");
547 static void command_goodies(irc_conn_t *irc_conn, const gchar *line, guint len, gint goodie)
549 const goodies_t *g = &goodies[goodie];
550 const gchar *arg = get_argument_target(line, len, g->command);
552 if (arg == NULL)
553 arg = get_nickname(line, len);
555 irc_send_message(irc_conn, NULL,
556 g->message, arg);
560 static void process_command(irc_conn_t *irc_conn, const gchar *line, guint len, const gchar *content)
562 // !test
563 if (strncmp(content, "!test", 5) == 0)
565 irc_send_message(irc_conn, get_nickname(line, len), "I don't like tests!");
567 // !moo
568 else if (strncmp(content, "!moo", 4) == 0)
570 command_moo(irc_conn);
572 // !fortune
573 else if (config->fortune_cmd != NULL && strncmp(content, "!fortune", 8) == 0)
575 command_fortune(irc_conn);
577 // !coffee
578 else if (strncmp(content, "!coffee", 7) == 0)
580 command_goodies(irc_conn, line, len, GOODIES_COFFEE);
582 // !coke
583 else if (strncmp(content, "!coke", 5) == 0)
585 command_goodies(irc_conn, line, len, GOODIES_COKE);
587 // !beer
588 else if (strncmp(content, "!beer", 5) == 0)
590 command_goodies(irc_conn, line, len, GOODIES_BEER);
592 // !pizza
593 else if (strncmp(content, "!pizza", 5) == 0)
595 command_goodies(irc_conn, line, len, GOODIES_PIZZA);
597 // !help
598 else if (strncmp(content, "!help", 5) == 0)
600 help_system_query("?? help");
603 * Fun with !roulette
604 * You have to register your bot with nickserv and add it to the access-list
605 * of your channel to make the !roulette-command work.
606 * This is just tested on FreeNode. Please feel free to write patches, that
607 * will make this work on other Networks.
609 else if (strncmp(content, "!roulette", 9) == 0)
611 gint32 rand = g_random_int();
612 static gint bullets_left = 6;
614 if (rand % 6 == 0 || bullets_left <= 0)
616 irc_send_message(irc_conn, NULL, "*bang*");
617 irc_kick(irc_conn, get_nickname(line, len));
618 bullets_left = 6;
621 else
623 irc_send_message(irc_conn, NULL, "*click*");
624 bullets_left--;
627 // !learn
628 /// TODO require op privileges for !learn
629 else if (strncmp(content, "!learn", 6) == 0)
631 command_learn(irc_conn, line, len);
633 // !alias
634 else if (strncmp(content, "!alias", 6) == 0)
636 command_alias(irc_conn, line, len);
641 static gboolean process_line(irc_conn_t *irc_conn, const gchar *line, guint len)
643 static gchar msg[1024];
644 guint msg_len;
645 gint response = get_response(line, len);
646 static gchar tmp_userlist[1024];
647 gchar *priv_sender;
648 const gchar *content;
649 static gboolean connected = FALSE;
651 // don't log the NAMES command's output (prevent log file spam)
652 if (response != 353 && response != 366)
653 irc_log(irc_conn, line, FALSE);
655 // An error occurred, try to quit cleanly and print the error
656 if ((response > 400 && response < 503))
658 // ignore Freenode's info messages sent with error code 477
659 // (see http://freenode.net/faq.shtml#freenode-info)
660 if (response != 477 || strstr(line, "[freenode-info]") == NULL)
662 #ifndef DEBUG
663 syslog(LOG_WARNING, "received error: %d (%s)", response, g_strstrip((gchar*) line));
664 #else
665 g_print("Error: %s", line);
666 #endif
667 main_quit();
668 return FALSE;
672 if (! connected)
674 if (response == 376)
675 connected = TRUE;
676 else
677 return TRUE;
680 content = irc_get_message(line, len);
682 // retrieve user name list
683 if (response == 353)
685 if (tmp_userlist[0] == '\0')
686 g_strlcpy(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
687 else
688 g_strlcat(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
690 // retrieve end of user name list
691 else if (response == 366)
693 if (tmp_userlist[0] != '\0')
695 set_user_list(tmp_userlist);
696 tmp_userlist[0] = '\0';
699 else if (! connected)
701 // don't do anything else until we got finished connecting (to skip MOTD messages)
703 // PING-PONG
704 else if (strncmp("PING :", line, 6) == 0)
706 msg_len = g_snprintf(msg, sizeof msg, "PONG %s\r\n", line + 6); // 7 = "PING :"
707 debug("PONG: -%s-\n", msg);
708 irc_send(irc_conn, msg, msg_len);
710 // handle private message
711 else if ((priv_sender = get_command_sender(line, len, "PRIVMSG")) != NULL)
713 // to be able to send private messages to users, you need to register your bot's
714 // nickname with Nickserv (at least on Freenode)
715 irc_send_private_message(irc_conn, priv_sender, "I don't like private messages!");
717 // handle kicks (we were kicked, bastards)
718 else if (get_command_sender(line, len, "KICK") != NULL)
720 if (config->auto_rejoin)
722 g_usleep(5000000); // just wait a little bit
723 msg_len = g_snprintf(msg, sizeof msg, "JOIN #%s\r\n", config->channel);
724 irc_send(irc_conn, msg, msg_len);
726 else
728 main_quit();
731 // Hi /me, acts on "hello $nickname" and "hi $nickname", hi and hello are case-insensitive
732 // Thanks /me
733 else if (strstr(content, config->nickname) != NULL)
735 const gchar *tmp_msg = irc_get_message_with_name(line, len, config->nickname);
737 if (strncasecmp("hi", content, 2) == 0 || strncasecmp("hello", content, 5) == 0 || strncasecmp("hey", content, 3) == 0 ||
738 strcasecmp(", hi", tmp_msg) == 0 || strcasecmp(", hello", tmp_msg) == 0 || strcasecmp(", hey", tmp_msg) == 0 ||
739 strcasecmp(": hi", tmp_msg) == 0 || strcasecmp(": hello", tmp_msg) == 0 || strcasecmp(": hey", tmp_msg) == 0)
741 irc_send_message(irc_conn, NULL,
742 "Hi %s. My name is %s and I'm here to offer additional services to you! "
743 "Try \"?? help\" for general information and \"?? vomak\" for information about me.",
744 get_nickname(line, len), config->nickname);
746 else if (strncasecmp("thanks", content, 6) == 0 || strncasecmp("thx", content, 3) == 0 ||
747 strcasecmp(", thanks", tmp_msg) == 0 || strcasecmp(", thx", tmp_msg) == 0 ||
748 strcasecmp(": thanks", tmp_msg) == 0 || strcasecmp(": thx", tmp_msg) == 0)
750 irc_send_message(irc_conn, get_nickname(line, len),
751 "no problem. It was a pleasure to serve you.");
754 // ?? ...
755 else if (strncmp(content, "?? ", 3) == 0)
757 help_system_query(content);
759 // pass to process_command() to process other commands (!beer, !test, !learn, ...)
760 else if (*content == '!')
762 process_command(irc_conn, line, len, content);
765 return TRUE;
770 * Please note that this will not work on Networks without ChanServ, e.g. on
771 * Quakenet or IRCnet. Your Bot has to be registered with NickServ and to be
772 * added to the channel access list for this to work.
774 static gboolean irc_toggle_op(irc_conn_t *irc_conn, gboolean request_op)
776 const gchar *cmd;
777 static gchar msg[1024];
778 guint msg_len;
780 if (irc_is_user_op(irc_conn, "ChanServ"))
782 cmd = (request_op) ? "op" : "deop";
783 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG ChanServ :%s #%s\r\n", cmd, config->channel);
784 irc_send(irc_conn, msg, msg_len);
786 return TRUE;
788 return FALSE; /* it seems we don't have a ChanServ bot */
792 static gboolean irc_is_user_op(irc_conn_t *irc_conn, const gchar *nickname)
794 const gchar *pos;
795 const gchar *userlist;
797 if (nickname == NULL)
798 return FALSE;
800 userlist = get_user_list();
802 if ( (pos = strstr(userlist, nickname)) )
804 if ( (pos - 1) >= userlist && (*(pos - 1) == '@') )
806 return TRUE;
809 else
811 #ifdef DEBUG
812 irc_send_message(irc_conn, NULL,
813 "Hey. There are crazy things going on here. (O.o)");
814 #else
815 syslog(LOG_WARNING, "user %s not found in names list of #%s", nickname, config->channel);
816 #endif
819 return FALSE;
823 static void irc_kick(irc_conn_t *irc_conn, const gchar *nickname)
825 static gchar msg[1024];
826 gboolean need_deop = FALSE;
827 guint msg_len;
829 TRACE
831 if (! irc_is_user_op(irc_conn, config->nickname))
833 // if irc_toggle_op fails, most probably we don't have a ChanServ and at this point we
834 // know we are not an op, so fail silently and don't try to kick
835 if (! irc_toggle_op(irc_conn, TRUE)) /// TODO: prüfen, ob das auch erfolreich war
836 return;
837 need_deop = TRUE;
840 // give the server a chance to set the op status for us before we make us of it,
841 // and let the victim read his *bang* message
842 g_usleep(1500000);
844 msg_len = g_snprintf(msg, sizeof msg, "KICK #%s %s\r\n", config->channel, nickname);
845 irc_send(irc_conn, msg, msg_len);
846 if (need_deop)
847 irc_toggle_op(irc_conn, FALSE);
851 static gboolean input_cb(GIOChannel *source, GIOCondition cond, gpointer data)
853 #if 1
854 gchar buf[1024];
855 guint buf_len;
856 irc_conn_t *irc = data;
857 gboolean ret = TRUE;
859 if (cond & (G_IO_ERR | G_IO_HUP))
860 return FALSE;
862 if ((buf_len = socket_fd_gets(irc->socket, buf, sizeof(buf))) != -1)
864 ret = process_line(irc, buf, buf_len);
866 #else
867 gsize buf_len;
868 irc_conn_t *irc = data;
870 if (cond & (G_IO_IN | G_IO_PRI))
872 gchar *buf = NULL;
873 GIOStatus rv;
874 GError *err = NULL;
878 rv = g_io_channel_read_line(source, &buf, &buf_len, NULL, &err);
879 if (buf != NULL)
881 buf_len -= 2;
882 buf[buf_len] = '\0'; // skip trailing \r\n
884 process_line(irc, buf, buf_len);
885 g_free(buf);
887 if (err != NULL)
889 debug("%s: error: %s", __func__, err->message);
890 g_error_free(err);
891 err = NULL;
894 while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
895 debug("%s: status %d\n", __func__, rv);
897 #endif
898 return ret;
902 void irc_send_message(irc_conn_t *irc_conn, const gchar *target, const gchar *format, ...)
904 static gchar tmp_msg[1024];
905 static gchar msg[1024];
906 guint msg_len;
907 va_list ap;
909 va_start(ap, format);
910 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
911 va_end(ap);
913 if (target)
914 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s, %s\r\n", config->channel, target, tmp_msg);
915 else
916 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s\r\n", config->channel, tmp_msg);
918 irc_send(irc_conn, msg, msg_len);
922 // simple wrapper for send() to enable logging for sent commands
923 gint irc_send(irc_conn_t *irc_conn, const gchar *msg, guint msg_len)
925 irc_log(irc_conn, msg, TRUE);
926 return send(irc_conn->socket, msg, msg_len, 0);
930 void irc_goodbye(irc_conn_t *irc)
932 guint len;
933 gchar msg[256];
935 if (NZV(irc->quit_msg))
936 len = g_snprintf(msg, sizeof msg, "QUIT :%s\r\n", irc->quit_msg);
937 else
938 len = g_strlcpy(msg, "QUIT :Good bye. It was a pleasure to serve you\r\n", sizeof msg);
939 irc_send(irc, msg, len);
943 void irc_logfile_reopen(irc_conn_t *irc_conn)
945 TRACE
947 if (irc_conn->log_fd != NULL)
948 fclose(irc_conn->log_fd);
950 if (NZV(config->logfile))
952 irc_conn->log_fd = g_fopen(config->logfile, "a");
953 if (! irc_conn->log_fd)
954 vomak_warning("Logfile could not be opened.");
959 gint irc_finalize(irc_conn_t *irc_conn)
961 if (irc_conn->socket < 0)
962 return -1;
964 if (irc_conn->lock_tag > 0)
965 g_source_remove(irc_conn->lock_tag);
967 if (irc_conn->log_fd != NULL)
969 irc_log(irc_conn, "Stop logging\r\n", FALSE);
970 fclose(irc_conn->log_fd);
973 if (irc_conn->read_ioc)
975 g_io_channel_shutdown(irc_conn->read_ioc, TRUE, NULL);
976 g_io_channel_unref(irc_conn->read_ioc);
977 irc_conn->read_ioc = NULL;
979 socket_fd_close(irc_conn->socket);
980 irc_conn->socket = -1;
982 g_free(irc_conn->quit_msg);
984 return 0;
988 void irc_connect(irc_conn_t *irc_conn)
990 struct hostent *he;
991 struct sockaddr_in their_addr;
992 gchar msg[256];
993 guint msg_len;
995 TRACE
997 // Connect the socket to the server
998 if ((he = gethostbyname(config->server)) == NULL)
1000 perror("gethostbyname");
1001 exit(1);
1004 if ((irc_conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1)
1006 perror("socket");
1007 exit(1);
1010 their_addr.sin_family = PF_INET;
1011 their_addr.sin_port = htons(6667);
1012 their_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
1013 memset(&(their_addr.sin_zero), '\0', 8);
1015 if (connect(irc_conn->socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
1017 perror("connect");
1018 exit(1);
1020 // Logging
1021 if (NZV(config->logfile))
1023 irc_conn->log_fd = g_fopen(config->logfile, "a");
1024 if (irc_conn->log_fd)
1025 irc_log(irc_conn, "Start logging\r\n", FALSE);
1026 else
1027 vomak_warning("Logfile could not be opened.");
1029 // say who we are
1030 msg_len = g_snprintf(msg, sizeof(msg), "USER %s %s %s :%s\r\n",
1031 config->username, config->servername, config->servername, config->realname);
1032 if (irc_send(irc_conn, msg, msg_len) == -1)
1034 perror("send USER");
1036 // and how we are called
1037 msg_len = g_snprintf(msg, sizeof(msg), "NICK %s\r\n", config->nickname);
1038 if (irc_send(irc_conn, msg, msg_len) == -1)
1040 perror("send NICK");
1042 // identify our nick
1043 if (NZV(config->nickserv_password))
1045 msg_len = g_snprintf(msg, sizeof(msg), "PRIVMSG nickserv :identify %s\r\n", config->nickserv_password);
1046 // don't use irc_send() here, no need to log our password
1047 if (send(irc_conn->socket, msg, msg_len, 0) == -1)
1049 perror("send NICKSERV");
1052 // join the channel
1053 msg_len = g_snprintf(msg, sizeof msg, "JOIN #%s\r\n", config->channel);
1054 if (irc_send(irc_conn, msg, msg_len) == -1)
1056 perror("send");
1059 // input callback, attached to the main loop
1060 irc_conn->read_ioc = g_io_channel_unix_new(irc_conn->socket);
1061 //~ g_io_channel_set_flags(irc_conn.read_ioc, G_IO_FLAG_NONBLOCK, NULL);
1062 g_io_channel_set_encoding(irc_conn->read_ioc, NULL, NULL);
1063 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);