Add GIT repo information to the docs.
[vomak.git] / irc.c
blobb9531fecd63c5c95236b213561347b6470dee08f
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>
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.
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <netinet/in.h>
28 #include <sys/socket.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <signal.h>
33 #include <glib.h>
35 #include "vomak.h"
36 #include "socket.h"
37 #include "irc.h"
40 config_t *config;
44 gboolean irc_query_names(gpointer data)
46 irc_conn_t *irc = data;
47 static gchar msg[1024];
48 guint msg_len;
50 TRACE
52 msg_len = g_snprintf(msg, sizeof msg, "NAMES #%s\r\n", config->channel);
53 send(irc->socket, msg, msg_len, 0);
55 return TRUE;
59 static gchar *get_nickname(const gchar *line, guint len)
61 static gchar result[20];
62 guint i, j = 0;
64 // :eht16!n=enrico@uvena.de PRIVMSG #eht16 :df
65 for (i = 0; i < len; i++)
67 if (line[i] == '!' || line[i] == '=' || j >= 19)
68 break;
70 if (line[i] == ':')
71 continue;
73 result[j++] = line[i];
75 result[j] = '\0';
77 return result;
81 static gint get_response(const gchar *line, guint len)
83 static gchar result[4];
84 guint i, j = 0;
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)
93 continue;
95 // after a response code
96 if (line[i] == ' ' && in_response)
98 in_response = FALSE;
99 break;
102 if (line[i] == ' ' )
103 i++; // skip the space
105 result[j++] = line[i];
106 in_response = TRUE;
108 result[j] = '\0';
110 return atoi(result);
114 gchar *irc_get_message(const gchar *line, guint len)
116 static gchar result[1024] = { 0 };
117 guint i, j = 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
121 // -> df foo: var
122 for (i = 0; i < len; i++)
124 if (state < 3)
126 if (line[i] == ' ')
128 state++;
129 i++; // skip the ':'
130 continue;
133 else if (line[i] != '\r' && line[i] != '\n')
135 result[j++] = line[i];
138 result[j] = '\0';
140 return result;
144 static void process_line(irc_conn_t *irc_conn, const gchar *line, guint len)
146 static gchar msg[1024];
147 guint msg_len;
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)
155 // PING-PONG
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));
168 else
169 g_strlcat(tmp_userlist, strchr(names, ':') + 1, sizeof(tmp_userlist));
171 // retrieve end of user name list
172 else if (response == 366)
174 /// TESTME
175 if (tmp_userlist[0] != '\0')
177 set_user_list(tmp_userlist);
178 tmp_userlist[0] = '\0';
181 // !test
182 else if (strstr(line, ":!test") != NULL)
184 irc_send_message(irc_conn, get_nickname(line, len), "I don't like tests!");
186 // !roulette
187 else if (strstr(line, ":!roulette") != NULL)
189 irc_send_message(irc_conn, NULL, "*click*");
191 // !coffee
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));
198 // !beer
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));
205 // !help
206 else if (strstr(line, ":!help") != NULL)
208 irc_send_message(irc_conn, get_nickname(line, len), "please use ?? help");
210 // ?? ...
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)
233 #if 1
234 gchar buf[1024];
235 guint buf_len;
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);
242 #else
243 gsize buf_len;
244 irc_conn_t *irc = data;
246 if (cond & (G_IO_IN | G_IO_PRI))
248 gchar *buf = NULL;
249 GIOStatus rv;
250 GError *err = NULL;
254 rv = g_io_channel_read_line(source, &buf, &buf_len, NULL, &err);
255 if (buf != NULL)
257 buf_len -= 2;
258 buf[buf_len] = '\0'; // skip trailing \r\n
260 process_line(irc, buf, buf_len);
261 g_free(buf);
263 if (err != NULL)
265 debug("%s: error: %s", __func__, err->message);
266 g_error_free(err);
267 err = NULL;
270 while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
271 debug("%s: status %d\n", __func__, rv);
273 #endif
274 return TRUE;
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];
282 guint msg_len;
283 va_list ap;
285 va_start(ap, format);
286 g_vsnprintf(tmp_msg, sizeof tmp_msg, format, ap);
287 va_end(ap);
289 if (target)
290 msg_len = g_snprintf(msg, sizeof msg, "PRIVMSG #%s :%s, %s\r\n", config->channel, target, tmp_msg);
291 else
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)
300 guint len;
301 gchar msg[256];
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)
311 return -1;
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;
325 return 0;
329 void irc_connect(irc_conn_t *irc_conn)
331 struct hostent *he;
332 struct sockaddr_in their_addr;
333 gchar msg[256];
334 guint msg_len;
336 TRACE
338 // Connect the socket to the server
339 if ((he = gethostbyname(config->server)) == NULL)
341 perror("gethostbyname");
342 exit(1);
345 if ((irc_conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1)
347 perror("socket");
348 exit(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)
358 perror("connect");
359 exit(1);
361 // say who we are
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)
366 perror("send USER");
368 // identify our nick
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");
377 // join the channel
378 g_snprintf(msg, sizeof msg, "JOIN #%s\r\n ", config->channel);
379 if (send(irc_conn->socket, msg, strlen(msg), 0) == -1)
381 perror("send");
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);