Increase the delay between requesting OP status and actually performing a kick.
[vomak.git] / src / irc.c
blob9af3aa9d5d2fb001f862bb363a93237467197732
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_TEA,
60 GOODIES_PIZZA,
62 typedef struct
64 const gchar *command;
65 const gchar *message;
66 } goodies_t;
68 const goodies_t goodies[] = {
69 { "!coffee", "A nice sexy waitress brings %s a big cup of coffee!" },
70 { "!coke", "A nice sexy waitress brings %s a cool bottle of coke!" },
71 { "!beer", "A nice sexy waitress brings %s a nice bottle of beer!" },
72 { "!tea", "A nice sexy waitress brings %s a cup of hot tea!" },
73 { "!pizza", "Someone calls Mario, and he brings %s a tasty hawaiian pizza!" },
74 { NULL, NULL }
77 gboolean irc_query_names(gpointer data)
79 irc_conn_t *irc = data;
80 static gchar msg[1024];
81 guint msg_len;
83 TRACE
85 msg_len = g_snprintf(msg, sizeof msg, "NAMES #%s\r\n", config->channel);
86 // send the message directly to avoid logging (prevent log file spamming)
87 send(irc->socket, msg, msg_len, MSG_NOSIGNAL);
89 return TRUE;
93 /* 'line' should be terminated by "\r\n" (CRLF) */
94 static void irc_log(irc_conn_t *irc_conn, const gchar *line, gboolean send)
96 time_t t;
97 struct tm *tp;
98 static gchar str[256];
99 GString *log_line;
101 if (! irc_conn->log_fd)
102 return;
104 t = time(NULL);
105 tp = localtime(&t);
106 strftime(str, sizeof str, "%F %T %z ", tp);
108 log_line = g_string_new(str);
109 if (send) // if we are sending a message, add our ident string
111 g_string_append_printf(log_line, ":%s!n=%s@%s ",
112 config->nickname, config->username, config->servername);
114 g_string_append(log_line, line);
115 /* add \r\n if it is missing */
116 if (strncmp(log_line->str + log_line->len - 2, "\r\n", 2) != 0)
118 g_string_append(log_line, "\r\n");
120 fwrite(log_line->str, log_line->len, 1, irc_conn->log_fd);
121 fflush(irc_conn->log_fd);
122 g_string_free(log_line, TRUE);
126 static void irc_send_private_message(irc_conn_t *irc_conn, const gchar *target, const gchar *format, ...)
128 static gchar tmp_msg[1024];
129 static gchar msg[1024];
130 guint msg_len;
131 va_list ap;
133 if (target == NULL)
134 return;
136 va_start(ap, format);
137 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
138 va_end(ap);
140 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG %s :%s, %s\r\n", target, target, tmp_msg);
141 irc_send(irc_conn, msg, msg_len);
145 static gchar *get_nickname(const gchar *line, guint len)
147 static gchar result[20];
148 guint i, j = 0;
150 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df
151 for (i = 0; i < len; i++)
153 if (line[i] == '!' || line[i] == '=' || j >= 19)
154 break;
156 if (line[i] == ':')
157 continue;
159 result[j++] = line[i];
161 result[j] = '\0';
163 return result;
167 static gchar *get_command_sender(const gchar *line, guint len, const gchar *command)
169 guint i;
170 gsize cmd_len = strlen(command);
172 // :eht16!n=enrico@uvena.de PRIVMSG GeanyTestBot :hi
173 for (i = 0; i < len; i++)
175 if (line[i] != ' ')
176 continue;
178 if (strncmp(command, line + i + 1, cmd_len) == 0)
180 static gchar name[20];
181 g_snprintf(name, sizeof(name), "%s :", config->nickname);
182 // if the receiver of the message is me, then it's a private message and
183 // we return the sender's nickname, otherwise NULL
184 if (strncmp(name, line + i + cmd_len + 2, strlen(config->nickname) + 2) == 0 ||
185 strncmp("KICK", command, 4) == 0)
187 return get_nickname(line, len);
189 else
190 return NULL;
194 return NULL;
198 static gint get_response(const gchar *line, guint len)
200 static gchar result[4];
201 guint i, j = 0;
202 gboolean in_response = FALSE;
204 // :kornbluth.freenode.net 353 GeanyTestBot @ #eht16 :GeanyTestBot eht16
205 // :kornbluth.freenode.net 366 GeanyTestBot #eht16 :End of /NAMES list.
206 for (i = 0; i < len; i++)
208 // before a response code
209 if (line[i] != ' ' && ! in_response)
210 continue;
212 // after a response code
213 if (line[i] == ' ' && in_response)
215 in_response = FALSE;
216 break;
219 if (line[i] == ' ' )
220 i++; // skip the space
222 result[j++] = line[i];
223 in_response = TRUE;
225 result[j] = '\0';
227 return atoi(result);
231 const gchar *irc_get_message(const gchar *line, guint len)
233 static gchar result[1024] = { 0 };
234 guint i, j = 0;
235 gint state = 0; // represents the current part of the whole line, separated by spaces
237 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df foo: var
238 // -> df foo: var
239 for (i = 0; i < len; i++)
241 if (state < 3)
243 if (line[i] == ' ')
245 state++;
246 i++; // skip the ':'
247 continue;
250 else if (line[i] != '\r' && line[i] != '\n')
252 result[j++] = line[i];
255 result[j] = '\0';
257 return result;
261 static const gchar *irc_get_message_with_name(const gchar *line, guint len, const gchar *name)
263 const gchar *tmp;
264 gsize name_len;
266 tmp = irc_get_message(line, len);
267 name_len = strlen(name);
269 if (strncmp(tmp, name, name_len) == 0)
270 tmp += name_len;
272 return tmp;
276 // returns a nickname argument given to cmd, it may also be "cmd for nickname", then the "for" is
277 // skipped
278 static const gchar *get_argument_target(const gchar *line, guint len, const gchar *cmd)
280 static gchar result[20];
281 const gchar *tmp;
282 gchar **parts;
284 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!beer for me
285 tmp = irc_get_message(line, len);
287 // -> !beer for me
288 parts = g_strsplit(tmp, " ", -1);
289 if (parts == NULL || parts[0] == NULL)
291 g_strfreev(parts);
292 return NULL;
295 // if cmd doesn't match, skip it
296 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
298 g_strfreev(parts);
299 return NULL;
302 if (parts[1] == NULL)
304 g_strfreev(parts);
305 return NULL;
308 if (strcmp("for", parts[1]) == 0)
310 if (parts[2] != NULL)
312 if (strcmp(parts[2], "me") == 0)
314 g_strfreev(parts);
315 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
317 else
318 g_strlcpy(result, parts[2], sizeof(result));
320 else
321 g_strlcpy(result, parts[1], sizeof(result));
323 else if (strcmp(parts[1], "me") == 0)
325 g_strfreev(parts);
326 return NULL; // if we return NULL, the nickname of the caller is used, aka "me"
328 else
330 g_strlcpy(result, parts[1], sizeof(result));
333 g_strfreev(parts);
334 return result;
338 // Parses the line and puts the first argument in arg1, all further arguments are concatenated
339 // in arg2. arg1 and arg2 should be freed when no longer needed.
340 // If arg1 and arg2 were set successfully, TRUE is returned, if any error occurs, FALSE is returned
341 // and arg1 and arg2 are set to NULL.
342 static gboolean get_argument_two(const gchar *line, guint len, const gchar *cmd,
343 gchar **arg1, gchar **arg2)
345 const gchar *tmp;
346 gchar **parts;
348 *arg1 = NULL;
349 *arg2 = NULL;
351 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :!learn keyword text to be added
352 tmp = irc_get_message(line, len);
354 // -> !learn keyword text to be added
355 parts = g_strsplit(tmp, " ", 3);
356 if (parts == NULL || parts[0] == NULL)
358 g_strfreev(parts);
359 return FALSE;
362 // if cmd doesn't match, skip it
363 if (parts[0] != NULL && strcmp(parts[0], cmd) != 0)
365 g_strfreev(parts);
366 return FALSE;
369 if (parts[1] == NULL || parts[2] == NULL)
371 g_strfreev(parts);
372 return FALSE;
375 *arg1 = g_strdup(parts[1]);
376 *arg2 = g_strdup(parts[2]);
378 g_strfreev(parts);
380 return TRUE;
384 static void command_fortune(irc_conn_t *irc_conn)
386 GError *error = NULL;
387 gchar *out = NULL;
388 gchar *err = NULL;
390 if (g_spawn_command_line_sync(config->fortune_cmd, &out, &err, NULL, &error))
392 if (NZV(out))
394 gchar **lines = g_strsplit(out, "\n", -1);
395 gsize i, len;
397 len = g_strv_length(lines);
398 for (i = 0; i < len; i++)
400 if (strlen(g_strchomp(lines[i])) > 0)
401 irc_send_message(irc_conn, NULL, lines[i]);
403 g_strfreev(lines);
405 else
407 vomak_warning("Executing fortune failed (%s)", err);
409 g_free(out);
410 g_free(err);
412 else
414 vomak_warning("Executing fortune failed (%s)", error->message);
415 g_error_free(error);
420 static void command_moo(irc_conn_t *irc_conn)
422 gint32 rand = g_random_int();
424 if (rand % 2 == 0)
426 gsize i;
427 const gchar *moo_str[] = {
428 " ^__^\r\n",
429 " (oo)\r\n",
430 " /-----(__)\r\n",
431 " / | ||\r\n",
432 " * /\\---/\\\r\n",
433 " ~~ ~~\n\r\n",
434 "..\"Have you mooed today?\"..\r\n",
435 NULL
438 for (i = 0; moo_str[i] != NULL; i++)
440 irc_send_message(irc_conn, NULL, moo_str[i]);
443 else
445 irc_send_message(irc_conn, NULL, "I have Super Cow Powers. Have you mooed today?");
450 static void command_learn(irc_conn_t *irc_conn, const gchar *line, guint len)
452 gchar *arg1, *arg2;
454 if (get_argument_two(line, len, "!learn", &arg1, &arg2))
456 gint result = help_system_learn(arg1, arg2);
457 gchar *text;
459 switch (result)
461 case 0:
463 text = g_strdup_printf("new keyword \"%s\" was added.", arg1);
464 break;
466 case 1:
468 text = g_strdup_printf("existing keyword \"%s\" was updated.", arg1);
469 break;
471 default:
473 text = g_strdup("an error occurred. Database not updated.");
474 break;
477 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
479 g_free(text);
480 g_free(arg1);
481 g_free(arg2);
483 else
484 irc_send_message(irc_conn, get_nickname(line, len),
485 "wrong usage of !learn. Use \"?? learn\" for usage information.");
489 static void command_alias(irc_conn_t *irc_conn, const gchar *line, guint len)
491 gchar *arg1, *arg2;
493 if (get_argument_two(line, len, "!alias", &arg1, &arg2))
495 // detect if arg2 has more than one word by scanning for spaces in
496 // the string
497 if (strchr(arg2, ' '))
499 irc_send_message(irc_conn, get_nickname(line, len),
500 "You gave me more than two arguments for !alias. I can not handle this.");
502 // check if the target actually exist and refuse if it doesn't exist
503 else if (g_hash_table_lookup(config->data, arg2) == NULL)
505 irc_send_message(irc_conn, get_nickname(line, len),
506 "The given target for the alias does not exist. I will refuse your request.");
508 else
510 gint result;
511 gchar *text;
512 gchar *alias = g_strconcat("@", arg2, NULL);
514 result = help_system_learn(arg1, alias);
516 switch (result)
518 case 0:
520 text = g_strdup_printf("new alias \"%s\" was added.", arg1);
521 break;
523 case 1:
525 text = g_strdup_printf("existing alias \"%s\" was updated.", arg1);
526 break;
528 default:
530 text = g_strdup("An error occurred. Database not updated.");
531 break;
535 irc_send_message(irc_conn, get_nickname(line, len), "%s", text);
537 g_free(alias);
538 g_free(text);
539 g_free(arg1);
540 g_free(arg2);
543 else
544 irc_send_message(irc_conn, get_nickname(line, len),
545 "wrong usage of !alias. Use \"?? alias\" for usage information.");
549 static void command_goodies(irc_conn_t *irc_conn, const gchar *line, guint len, gint goodie)
551 const goodies_t *g = &goodies[goodie];
552 const gchar *arg = get_argument_target(line, len, g->command);
554 if (arg == NULL)
555 arg = get_nickname(line, len);
557 irc_send_message(irc_conn, NULL,
558 g->message, arg);
562 static void process_command(irc_conn_t *irc_conn, const gchar *line, guint len, const gchar *content)
564 // !test
565 if (strncmp(content, "!test", 5) == 0)
567 irc_send_message(irc_conn, get_nickname(line, len), "I don't like tests!");
569 // !moo
570 else if (strncmp(content, "!moo", 4) == 0)
572 command_moo(irc_conn);
574 // !fortune
575 else if (config->fortune_cmd != NULL && strncmp(content, "!fortune", 8) == 0)
577 command_fortune(irc_conn);
579 // !coffee
580 else if (strncmp(content, "!coffee", 7) == 0)
582 command_goodies(irc_conn, line, len, GOODIES_COFFEE);
584 // !coke
585 else if (strncmp(content, "!coke", 5) == 0)
587 command_goodies(irc_conn, line, len, GOODIES_COKE);
589 // !beer
590 else if (strncmp(content, "!beer", 5) == 0)
592 command_goodies(irc_conn, line, len, GOODIES_BEER);
594 // !pizza
595 else if (strncmp(content, "!pizza", 5) == 0)
597 command_goodies(irc_conn, line, len, GOODIES_PIZZA);
599 // !tea
600 else if (strncmp(content, "!tea", 5) == 0)
602 command_goodies(irc_conn, line, len, GOODIES_TEA);
604 // !help
605 else if (strncmp(content, "!help", 5) == 0)
607 help_system_query("?? help");
610 * Fun with !roulette
611 * You have to register your bot with nickserv and add it to the access-list
612 * of your channel to make the !roulette-command work.
613 * This is just tested on FreeNode. Please feel free to write patches, that
614 * will make this work on other Networks.
616 else if (strncmp(content, "!roulette", 9) == 0)
618 gint32 rand = g_random_int();
619 static gint bullets_left = 6;
621 if (rand % 6 == 0 || bullets_left <= 0)
623 irc_send_message(irc_conn, NULL, "*bang*");
624 irc_kick(irc_conn, get_nickname(line, len));
625 bullets_left = 6;
628 else
630 irc_send_message(irc_conn, NULL, "*click*");
631 bullets_left--;
634 // !learn
635 /// TODO require op privileges for !learn
636 else if (strncmp(content, "!learn", 6) == 0)
638 command_learn(irc_conn, line, len);
640 // !alias
641 else if (strncmp(content, "!alias", 6) == 0)
643 command_alias(irc_conn, line, len);
648 static gboolean process_line(irc_conn_t *irc_conn, const gchar *line, guint len)
650 static gchar msg[1024];
651 guint msg_len;
652 gint response = get_response(line, len);
653 static gchar tmp_userlist[1024];
654 gchar *priv_sender;
655 const gchar *content;
656 static gboolean connected = FALSE;
658 // don't log the NAMES command's output (prevent log file spam)
659 if (response != 353 && response != 366)
660 irc_log(irc_conn, line, FALSE);
662 // An error occurred, try to quit cleanly and print the error
663 if ((response > 400 && response < 503))
665 // ignore Freenode's info messages sent with error code 477
666 // (see http://freenode.net/faq.shtml#freenode-info)
667 if (response != 477 || strstr(line, "[freenode-info]") == NULL)
669 #ifndef DEBUG
670 syslog(LOG_WARNING, "received error: %d (%s)", response, g_strstrip((gchar*) line));
671 #else
672 g_print("Error: %s", line);
673 #endif
674 main_quit();
675 return FALSE;
679 if (! connected)
681 if (response == 376)
682 connected = TRUE;
683 else
684 return TRUE;
687 content = irc_get_message(line, len);
689 // retrieve user name list
690 if (response == 353)
692 if (tmp_userlist[0] == '\0')
693 g_strlcpy(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
694 else
695 g_strlcat(tmp_userlist, strchr(content, ':') + 1, sizeof(tmp_userlist));
697 // retrieve end of user name list
698 else if (response == 366)
700 if (tmp_userlist[0] != '\0')
702 set_user_list(tmp_userlist);
703 tmp_userlist[0] = '\0';
706 else if (! connected)
708 // don't do anything else until we got finished connecting (to skip MOTD messages)
710 // PING-PONG
711 else if (strncmp("PING :", line, 6) == 0)
713 msg_len = g_snprintf(msg, sizeof msg, "PONG %s\r\n", line + 6); // 7 = "PING :"
714 debug("PONG: -%s-\n", msg);
715 irc_send(irc_conn, msg, msg_len);
717 // handle private message
718 else if ((priv_sender = get_command_sender(line, len, "PRIVMSG")) != NULL)
720 // to be able to send private messages to users, you need to register your bot's
721 // nickname with Nickserv (at least on Freenode)
722 irc_send_private_message(irc_conn, priv_sender, "I don't like private messages!");
724 // handle kicks (we were kicked, bastards)
725 else if (get_command_sender(line, len, "KICK") != NULL)
727 if (config->auto_rejoin)
729 g_usleep(5000000); // just wait a little bit
730 msg_len = g_snprintf(msg, sizeof msg, "JOIN #%s\r\n", config->channel);
731 irc_send(irc_conn, msg, msg_len);
733 else
735 main_quit();
738 // Hi /me, acts on "hello $nickname" and "hi $nickname", hi and hello are case-insensitive
739 // Thanks /me
740 else if (strstr(content, config->nickname) != NULL)
742 const gchar *tmp_msg = irc_get_message_with_name(line, len, config->nickname);
744 if (strncasecmp("hi", content, 2) == 0 || strncasecmp("hello", content, 5) == 0 || strncasecmp("hey", content, 3) == 0 ||
745 strcasecmp(", hi", tmp_msg) == 0 || strcasecmp(", hello", tmp_msg) == 0 || strcasecmp(", hey", tmp_msg) == 0 ||
746 strcasecmp(": hi", tmp_msg) == 0 || strcasecmp(": hello", tmp_msg) == 0 || strcasecmp(": hey", tmp_msg) == 0)
748 irc_send_message(irc_conn, NULL,
749 "Hi %s. My name is %s and I'm here to offer additional services to you! "
750 "Try \"?? help\" for general information and \"?? vomak\" for information about me.",
751 get_nickname(line, len), config->nickname);
753 else if (strncasecmp("thanks", content, 6) == 0 || strncasecmp("thx", content, 3) == 0 ||
754 strcasecmp(", thanks", tmp_msg) == 0 || strcasecmp(", thx", tmp_msg) == 0 ||
755 strcasecmp(": thanks", tmp_msg) == 0 || strcasecmp(": thx", tmp_msg) == 0)
757 irc_send_message(irc_conn, get_nickname(line, len),
758 "no problem. It was a pleasure to serve you.");
761 // ?? ...
762 else if (strncmp(content, "?? ", 3) == 0)
764 help_system_query(content);
766 // pass to process_command() to process other commands (!beer, !test, !learn, ...)
767 else if (*content == '!')
769 process_command(irc_conn, line, len, content);
772 return TRUE;
777 * Please note that this will not work on Networks without ChanServ, e.g. on
778 * Quakenet or IRCnet. Your Bot has to be registered with NickServ and to be
779 * added to the channel access list for this to work.
781 static gboolean irc_toggle_op(irc_conn_t *irc_conn, gboolean request_op)
783 const gchar *cmd;
784 static gchar msg[1024];
785 guint msg_len;
787 if (irc_is_user_op(irc_conn, "ChanServ"))
789 cmd = (request_op) ? "op" : "deop";
790 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG ChanServ :%s #%s\r\n", cmd, config->channel);
791 irc_send(irc_conn, msg, msg_len);
793 return TRUE;
795 return FALSE; /* it seems we don't have a ChanServ bot */
799 static gboolean irc_is_user_op(irc_conn_t *irc_conn, const gchar *nickname)
801 const gchar *pos;
802 const gchar *userlist;
804 if (nickname == NULL)
805 return FALSE;
807 userlist = get_user_list();
809 if ( (pos = strstr(userlist, nickname)) )
811 if ( (pos - 1) >= userlist && (*(pos - 1) == '@') )
813 return TRUE;
816 else
818 #ifdef DEBUG
819 irc_send_message(irc_conn, NULL,
820 "Hey. There are crazy things going on here. (O.o)");
821 #else
822 syslog(LOG_WARNING, "user %s not found in names list of #%s", nickname, config->channel);
823 #endif
826 return FALSE;
830 static void irc_kick(irc_conn_t *irc_conn, const gchar *nickname)
832 static gchar msg[1024];
833 gboolean need_deop = FALSE;
834 guint msg_len;
836 TRACE
838 if (! irc_is_user_op(irc_conn, config->nickname))
840 // if irc_toggle_op fails, most probably we don't have a ChanServ and at this point we
841 // know we are not an op, so fail silently and don't try to kick
842 if (! irc_toggle_op(irc_conn, TRUE)) /// TODO: prüfen, ob das auch erfolreich war
843 return;
844 need_deop = TRUE;
847 // give the server a chance to set the op status for us before we make us of it,
848 // and let the victim read his *bang* message
849 g_usleep(2000000);
851 msg_len = g_snprintf(msg, sizeof msg, "KICK #%s %s\r\n", config->channel, nickname);
852 irc_send(irc_conn, msg, msg_len);
853 if (need_deop)
854 irc_toggle_op(irc_conn, FALSE);
858 static gboolean input_cb(GIOChannel *source, GIOCondition cond, gpointer data)
860 #if 1
861 gchar buf[1024];
862 guint buf_len;
863 irc_conn_t *irc = data;
864 gboolean ret = TRUE;
866 if (cond & (G_IO_ERR | G_IO_HUP))
867 return FALSE;
869 if ((buf_len = socket_fd_gets(irc->socket, buf, sizeof(buf))) != -1)
871 ret = process_line(irc, buf, buf_len);
873 #else
874 gsize buf_len;
875 irc_conn_t *irc = data;
877 if (cond & (G_IO_IN | G_IO_PRI))
879 gchar *buf = NULL;
880 GIOStatus rv;
881 GError *err = NULL;
885 rv = g_io_channel_read_line(source, &buf, &buf_len, NULL, &err);
886 if (buf != NULL)
888 buf_len -= 2;
889 buf[buf_len] = '\0'; // skip trailing \r\n
891 process_line(irc, buf, buf_len);
892 g_free(buf);
894 if (err != NULL)
896 debug("%s: error: %s", __func__, err->message);
897 g_error_free(err);
898 err = NULL;
901 while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
902 debug("%s: status %d\n", __func__, rv);
904 #endif
905 return ret;
909 void irc_send_message(irc_conn_t *irc_conn, const gchar *target, const gchar *format, ...)
911 static gchar tmp_msg[1024];
912 static gchar msg[1024];
913 guint msg_len;
914 va_list ap;
916 va_start(ap, format);
917 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
918 va_end(ap);
920 if (target)
921 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s, %s\r\n", config->channel, target, tmp_msg);
922 else
923 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s\r\n", config->channel, tmp_msg);
925 irc_send(irc_conn, msg, msg_len);
929 // simple wrapper for send() to enable logging for sent commands
930 gint irc_send(irc_conn_t *irc_conn, const gchar *msg, guint msg_len)
932 irc_log(irc_conn, msg, TRUE);
933 return send(irc_conn->socket, msg, msg_len, MSG_NOSIGNAL);
937 void irc_goodbye(irc_conn_t *irc)
939 guint len;
940 gchar msg[256];
942 if (NZV(irc->quit_msg))
943 len = g_snprintf(msg, sizeof msg, "QUIT :%s\r\n", irc->quit_msg);
944 else
945 len = g_strlcpy(msg, "QUIT :Good bye. It was a pleasure to serve you\r\n", sizeof msg);
946 irc_send(irc, msg, len);
950 void irc_logfile_reopen(irc_conn_t *irc_conn)
952 TRACE
954 if (irc_conn->log_fd != NULL)
955 fclose(irc_conn->log_fd);
957 if (NZV(config->logfile))
959 irc_conn->log_fd = g_fopen(config->logfile, "a");
960 if (! irc_conn->log_fd)
961 vomak_warning("Logfile could not be opened.");
966 gint irc_finalize(irc_conn_t *irc_conn)
968 if (irc_conn->socket < 0)
969 return -1;
971 if (irc_conn->lock_tag > 0)
972 g_source_remove(irc_conn->lock_tag);
974 if (irc_conn->log_fd != NULL)
976 irc_log(irc_conn, "Stop logging\r\n", FALSE);
977 fclose(irc_conn->log_fd);
980 if (irc_conn->read_ioc)
982 g_io_channel_shutdown(irc_conn->read_ioc, TRUE, NULL);
983 g_io_channel_unref(irc_conn->read_ioc);
984 irc_conn->read_ioc = NULL;
986 socket_fd_close(irc_conn->socket);
987 irc_conn->socket = -1;
989 g_free(irc_conn->quit_msg);
991 return 0;
995 void irc_connect(irc_conn_t *irc_conn)
997 struct hostent *he;
998 struct sockaddr_in their_addr;
999 gchar msg[256];
1000 guint msg_len;
1002 TRACE
1004 // Connect the socket to the server
1005 if ((he = gethostbyname(config->server)) == NULL)
1007 perror("gethostbyname");
1008 exit(1);
1011 if ((irc_conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1)
1013 perror("socket");
1014 exit(1);
1017 their_addr.sin_family = PF_INET;
1018 their_addr.sin_port = htons(6667);
1019 their_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
1020 memset(&(their_addr.sin_zero), '\0', 8);
1022 if (connect(irc_conn->socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
1024 perror("connect");
1025 exit(1);
1027 // Logging
1028 if (NZV(config->logfile))
1030 irc_conn->log_fd = g_fopen(config->logfile, "a");
1031 if (irc_conn->log_fd)
1032 irc_log(irc_conn, "Start logging\r\n", FALSE);
1033 else
1034 vomak_warning("Logfile could not be opened.");
1036 // say who we are
1037 msg_len = g_snprintf(msg, sizeof(msg), "USER %s %s %s :%s\r\n",
1038 config->username, config->servername, config->servername, config->realname);
1039 if (irc_send(irc_conn, msg, msg_len) == -1)
1041 perror("send USER");
1043 // and how we are called
1044 msg_len = g_snprintf(msg, sizeof(msg), "NICK %s\r\n", config->nickname);
1045 if (irc_send(irc_conn, msg, msg_len) == -1)
1047 perror("send NICK");
1049 // identify our nick
1050 if (NZV(config->nickserv_password))
1052 msg_len = g_snprintf(msg, sizeof(msg), "PRIVMSG nickserv :identify %s\r\n", config->nickserv_password);
1053 // don't use irc_send() here, no need to log our password
1054 if (send(irc_conn->socket, msg, msg_len, MSG_NOSIGNAL) == -1)
1056 perror("send NICKSERV");
1059 // join the channel
1060 msg_len = g_snprintf(msg, sizeof msg, "JOIN #%s\r\n", config->channel);
1061 if (irc_send(irc_conn, msg, msg_len) == -1)
1063 perror("send");
1066 // input callback, attached to the main loop
1067 irc_conn->read_ioc = g_io_channel_unix_new(irc_conn->socket);
1068 //~ g_io_channel_set_flags(irc_conn.read_ioc, G_IO_FLAG_NONBLOCK, NULL);
1069 g_io_channel_set_encoding(irc_conn->read_ioc, NULL, NULL);
1070 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);