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>
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.
25 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <sys/socket.h>
44 gboolean
irc_query_names(gpointer data
)
46 irc_conn_t
*irc
= data
;
47 static gchar msg
[1024];
52 msg_len
= g_snprintf(msg
, sizeof msg
, "NAMES #%s\r\n", config
->channel
);
53 send(irc
->socket
, msg
, msg_len
, 0);
59 static gchar
*get_nickname(const gchar
*line
, guint len
)
61 static gchar result
[20];
64 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df
65 for (i
= 0; i
< len
; i
++)
67 if (line
[i
] == '!' || line
[i
] == '=' || j
>= 19)
73 result
[j
++] = line
[i
];
81 static gint
get_response(const gchar
*line
, guint len
)
83 static gchar result
[4];
85 gboolean in_response
= FALSE
;
87 // :kornbluth.freenode.net 353 GeanyTestBot @ #eht16 :GeanyTestBot eht16
88 // :kornbluth.freenode.net 366 GeanyTestBot #eht16 :End of /NAMES list.
89 for (i
= 0; i
< len
; i
++)
91 // before a response code
92 if (line
[i
] != ' ' && ! in_response
)
95 // after a response code
96 if (line
[i
] == ' ' && in_response
)
103 i
++; // skip the space
105 result
[j
++] = line
[i
];
114 gchar
*irc_get_message(const gchar
*line
, guint len
)
116 static gchar result
[1024] = { 0 };
118 gint state
= 0; // represents the current part of the whole line, separated by spaces
120 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df foo: var
122 for (i
= 0; i
< len
; i
++)
133 else if (line
[i
] != '\r' && line
[i
] != '\n')
135 result
[j
++] = line
[i
];
144 static void process_line(irc_conn_t
*irc_conn
, const gchar
*line
, guint len
)
146 static gchar msg
[1024];
148 gint response
= get_response(line
, len
);
149 static gchar tmp_userlist
[1024];
151 if (! irc_conn
->connected
)
153 // don't do anything else until we got finished connecting (to skip MOTD messages)
156 else if (strncmp("PING :", line
, 6) == 0)
158 msg_len
= g_snprintf(msg
, sizeof msg
, "PONG %s\r\n", line
+ 6); // 7 = "PING :"
159 debug("PONG: -%s-\n", msg
);
160 send(irc_conn
->socket
, msg
, msg_len
, 0);
162 // retrieve user name list
163 else if (response
== 353)
165 gchar
*names
= irc_get_message(line
, len
);
166 if (tmp_userlist
[0] == '\0')
167 g_strlcpy(tmp_userlist
, strchr(names
, ':') + 1, sizeof(tmp_userlist
));
169 g_strlcat(tmp_userlist
, strchr(names
, ':') + 1, sizeof(tmp_userlist
));
171 // retrieve end of user name list
172 else if (response
== 366)
175 if (tmp_userlist
[0] != '\0')
177 set_user_list(tmp_userlist
);
178 tmp_userlist
[0] = '\0';
182 else if (strstr(line
, ":!test") != NULL
)
184 irc_send_message(irc_conn
, get_nickname(line
, len
), "I don't like tests!");
187 else if (strstr(line
, ":!roulette") != NULL
)
189 irc_send_message(irc_conn
, NULL
, "*click*");
192 else if (strstr(line
, ":!coffee") != NULL
)
194 irc_send_message(irc_conn
, NULL
,
195 "A nice sexy waitress brings %s a big cup of coffee!",
196 get_nickname(line
, len
));
199 else if (strstr(line
, ":!beer") != NULL
)
201 irc_send_message(irc_conn
, NULL
,
202 "A nice sexy waitress brings %s a nice bottle of beer!",
203 get_nickname(line
, len
));
206 else if (strstr(line
, ":!help") != NULL
)
208 irc_send_message(irc_conn
, get_nickname(line
, len
), "please use ?? help");
211 else if (strstr(line
, ":?? ") != NULL
)
213 query_help_system(line
, len
);
215 // Hi /me, acts on "hello $nickname" and "hi $nickname", hi and hello are case-insensitive
216 else if (strstr(line
, config
->nickname
) != NULL
)
218 gchar
*tmp_msg
= irc_get_message(line
, len
);
220 if (strncasecmp("hi", tmp_msg
, 2) == 0 || strncasecmp("hello", tmp_msg
, 5) == 0)
222 irc_send_message(irc_conn
, NULL
,
223 "Hi %s. My name is %s and I'm here to offer additional services to you! "
224 "Try \"?? help\" for general information and \"?? vomak\" for information about me.",
225 get_nickname(line
, len
), config
->nickname
);
231 static gboolean
input_cb(GIOChannel
*source
, GIOCondition cond
, gpointer data
)
236 irc_conn_t
*irc
= data
;
238 if ((buf_len
= socket_fd_gets(irc
->socket
, buf
, sizeof(buf
))) != -1)
240 process_line(irc
, buf
, buf_len
);
244 irc_conn_t
*irc
= data
;
246 if (cond
& (G_IO_IN
| G_IO_PRI
))
254 rv
= g_io_channel_read_line(source
, &buf
, &buf_len
, NULL
, &err
);
258 buf
[buf_len
] = '\0'; // skip trailing \r\n
260 process_line(irc
, buf
, buf_len
);
265 debug("%s: error: %s", __func__
, err
->message
);
270 while (rv
== G_IO_STATUS_NORMAL
|| rv
== G_IO_STATUS_AGAIN
);
271 debug("%s: status %d\n", __func__
, rv
);
278 void irc_send_message(irc_conn_t
*irc_conn
, const gchar
*target
, const gchar
*format
, ...)
280 static gchar tmp_msg
[1024];
281 static gchar msg
[1024];
285 va_start(ap
, format
);
286 g_vsnprintf(tmp_msg
, sizeof tmp_msg
, format
, ap
);
290 msg_len
= g_snprintf(msg
, sizeof msg
, "PRIVMSG #%s :%s, %s\r\n", config
->channel
, target
, tmp_msg
);
292 msg_len
= g_snprintf(msg
, sizeof msg
, "PRIVMSG #%s :%s\r\n", config
->channel
, tmp_msg
);
294 send(irc_conn
->socket
, msg
, msg_len
, 0);
298 void irc_goodbye(irc_conn_t
*irc
)
303 len
= g_strlcpy(msg
, "QUIT :Good bye. It was a pleasure to serve you\r\n", sizeof msg
);
304 send(irc
->socket
, msg
, len
, 0);
308 gint
irc_finalize(irc_conn_t
*irc_conn
)
310 if (irc_conn
->socket
< 0)
313 if (irc_conn
->lock_tag
> 0)
314 g_source_remove(irc_conn
->lock_tag
);
316 if (irc_conn
->read_ioc
)
318 g_io_channel_shutdown(irc_conn
->read_ioc
, TRUE
, NULL
);
319 g_io_channel_unref(irc_conn
->read_ioc
);
320 irc_conn
->read_ioc
= NULL
;
322 socket_fd_close(irc_conn
->socket
);
323 irc_conn
->socket
= -1;
329 void irc_connect(irc_conn_t
*irc_conn
)
332 struct sockaddr_in their_addr
;
338 // Connect the socket to the server
339 if ((he
= gethostbyname(config
->server
)) == NULL
)
341 perror("gethostbyname");
345 if ((irc_conn
->socket
= socket(PF_INET
, SOCK_STREAM
, 0)) == -1)
351 their_addr
.sin_family
= PF_INET
;
352 their_addr
.sin_port
= htons(6667);
353 their_addr
.sin_addr
= *((struct in_addr
*)he
->h_addr
);
354 memset(&(their_addr
.sin_zero
), '\0', 8);
356 if (connect(irc_conn
->socket
, (struct sockaddr
*)&their_addr
, sizeof(struct sockaddr
)) == -1)
362 msg_len
= g_snprintf(msg
, sizeof(msg
), "USER %s %s %s :%s\r\nNICK %s\r\n",
363 config
->username
, config
->servername
, config
->servername
, config
->realname
, config
->nickname
);
364 if (send(irc_conn
->socket
, msg
, msg_len
, 0) == -1)
369 if (NZV(config
->nickserv_password
))
371 msg_len
= g_snprintf(msg
, sizeof(msg
), "PRIVMSG nickserv :identify %s\r\n", config
->nickserv_password
);
372 if (send(irc_conn
->socket
, msg
, msg_len
, 0) == -1)
374 perror("send NICKSERV");
378 g_snprintf(msg
, sizeof msg
, "JOIN #%s\r\n ", config
->channel
);
379 if (send(irc_conn
->socket
, msg
, strlen(msg
), 0) == -1)
384 // input callback, attached to the main loop
385 irc_conn
->read_ioc
= g_io_channel_unix_new(irc_conn
->socket
);
386 //~ g_io_channel_set_flags(irc_conn.read_ioc, G_IO_FLAG_NONBLOCK, NULL);
387 g_io_channel_set_encoding(irc_conn
->read_ioc
, NULL
, NULL
);
388 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
);