00001 #include <sys/types.h>
00002 #include <sys/socket.h>
00003 #include <netdb.h>
00004 #include <stdio.h>
00005 #include <string.h>
00006 #include <unistd.h>
00007 #include <stdlib.h>
00008 #include <poll.h>
00009 #include <errno.h>
00010 #include <glib-object.h>
00011
00012 #include "lpbot.h"
00013
00014 lp_config *config;
00015
00025 int lp_resolve(char *server, struct hostent *host)
00026 {
00027 struct hostent *ptr;
00028 if(!server)
00029 return -1;
00030 if (!(ptr = gethostbyname(server)))
00031 {
00032 perror("gethostbyname");
00033 return -1;
00034 }
00035 *host = *ptr;
00036 return 0;
00037 }
00038
00042 int lp_create_sock()
00043 {
00044 int sock;
00045
00046 if((sock = socket(AF_INET, SOCK_STREAM, 0))<0)
00047 perror("socket");
00048 return sock;
00049 }
00050
00056 int lp_send(lp_server* server, char *fmt, ...)
00057 {
00058 va_list ap;
00059 char buf[IRC_LINE_LENGHT+1];
00060 struct pollfd pfd[1];
00061
00062 if(!server || server->sock<0)
00063 return -1;
00064
00065 va_start(ap, fmt);
00066 vsnprintf(buf, IRC_LINE_LENGHT-1, fmt, ap);
00067 va_end(ap);
00068 #ifdef DEBUG
00069 printf("sending message '%s'\n", buf);
00070 #endif
00071 buf[strlen(buf)+1] = '\0';
00072 buf[strlen(buf)] = '\n';
00073
00074
00075 if(server->sock != STDIN_FILENO)
00076 {
00077 pfd[0].fd = server->sock;
00078 pfd[0].events = POLLOUT;
00079
00080 poll(pfd, 1, 1000);
00081 if(pfd[0].revents & POLLHUP)
00082 {
00083 lp_reconnect(server, NULL);
00084 return FALSE;
00085 }
00086 }
00087 return write(server->sock, buf, strlen(buf));
00088 }
00089
00093 void lp_dump_msg(lp_msg *msg)
00094 {
00095 int i;
00096
00097 printf("lp_dump_msg() from: '%s', cmd: '%s', to: '%s'", msg->from, msg->cmd, msg->to);
00098 if(g_list_length(msg->params))
00099 printf(", params: ");
00100 for(i=0;i<g_list_length(msg->params);i++)
00101 {
00102 printf("'%s' ", (char*)g_list_nth_data(msg->params, i));
00103 }
00104 printf("\n");
00105 }
00106
00111 lp_msg *lp_parse(char *str)
00112 {
00113 char *p;
00114 lp_msg *msg = g_new0(lp_msg, 1);
00115 msg->raw = g_strdup(str);
00116 msg->from = msg->raw;
00117 if(msg->from[0] == ':')
00118 msg->from++;
00119
00120 #ifdef DEBUG
00121 printf("parsing message '%s'\n", msg->raw);
00122 #endif
00123
00124 p = strchr(msg->raw, ' ');
00125
00126 if(!p)
00127 return NULL;
00128 *p = '\0';
00129 msg->cmd = ++p;
00130 p = strchr(msg->cmd, ' ');
00131 if(!p)
00132 return NULL;
00133 *p = '\0';
00134 msg->to = ++p;
00135 p = strchr(msg->to, ' ');
00136
00137 if(p)
00138 {
00139 if(*(p+1)==':')
00140 {
00141 *p = '\0';
00142 p++;
00143 }
00144 while(p)
00145 {
00146 *p = '\0';
00147 msg->params = g_list_append(msg->params, ++p);
00148 p = strchr(p, ' ');
00149 }
00150 }
00151 p = strchr(msg->from, '!');
00152 if(p)
00153 *p = '\0';
00154 #ifdef DEBUG
00155 lp_dump_msg(msg);
00156 #endif
00157 return msg;
00158 }
00159
00163 void lp_msg_free(lp_msg *msg)
00164 {
00165 free(msg->raw);
00166 free(msg);
00167 }
00168
00173 int lp_ping(gpointer data)
00174 {
00175 lp_server *server = (lp_server*)data;
00176 int lag = time(NULL) - server->lastpong;
00177
00178 if(server->lastpong != 0 && lag > 300)
00179 {
00180 lp_reconnect(server, "Connection timed out");
00181 return FALSE;
00182 }
00183 lp_send(server, "PING %s", server->nick);
00184 return TRUE;
00185 }
00186
00191 int lp_check_rss(gpointer data)
00192 {
00193 int i;
00194
00195 for(i=0;i<g_list_length(config->rsslist);i++)
00196 {
00197 lp_rss *rss = g_list_nth_data(config->rsslist, i);
00198 check_rss(rss);
00199 }
00200 return TRUE;
00201 }
00202
00209 char *lp_to(lp_server *server, lp_msg *msg)
00210 {
00211
00212
00213 char *ret;
00214
00215 if(!strcmp(server->nick, msg->to))
00216 ret = msg->from;
00217 else
00218 ret = msg->to;
00219 return ret;
00220 }
00221
00226 int lp_identified(char *who)
00227 {
00228 lp_user *user = lp_find_user(who);
00229 if(user && user->identified)
00230 return 1;
00231 return 0;
00232 }
00233
00238 lp_user *lp_find_user(char *who)
00239 {
00240 int i;
00241
00242 for(i=0;i<g_list_length(config->users);i++)
00243 {
00244 lp_user *user = g_list_nth_data(config->users, i);
00245 if(!strcmp(user->login, who))
00246 return user;
00247 }
00248 return NULL;
00249 }
00250
00256 int lp_handle_command(lp_server *server, lp_msg *msg, GList *params)
00257 {
00258 int i;
00259 char *to = lp_to(server, msg);
00260
00261
00262
00263 if(!strcmp("ping", g_list_nth_data(params, 0)))
00264 lp_send(server, "privmsg %s :pong", to);
00265 if(!strcmp("eval", g_list_nth_data(params, 0)))
00266 {
00267 if(lp_identified(msg->from))
00268 {
00269 lp_user *user = lp_find_user(msg->from);
00270 if(user->rights & LP_RIGHT_OP && g_list_length(params) >1)
00271 {
00272 GString *cmd = g_string_new(g_list_nth_data(params, 1));
00273 for(i=2;i<g_list_length(params);i++)
00274 g_string_append_printf(cmd, " %s", (char*)g_list_nth_data(params, i));
00275 lp_send(server, cmd->str);
00276 }
00277 else
00278 lp_send(server, "privmsg %s :you don't have rights to alter the db", to);
00279 }
00280 else
00281 lp_send(server, "privmsg %s :identify first to control the bot", to);
00282 }
00283 if(!strcmp("db", g_list_nth_data(params, 0)))
00284 {
00285 int found = 0;
00286 if(!strcmp("get", g_list_nth_data(params, 1)) && g_list_length(params) == 3)
00287 {
00288 for(i=0;i<g_list_length(config->records);i++)
00289 {
00290 lp_record *record = g_list_nth_data(config->records, i);
00291 if(!strcmp(record->name, g_list_nth_data(params, 2)))
00292 {
00293 lp_record_ver *ver = g_list_nth_data(record->versions, 0);
00294 lp_send(server, "privmsg %s :%s", to, ver->content->str);
00295 found = 1;
00296 break;
00297 }
00298 }
00299 if(!found)
00300 lp_send(server, "privmsg %s :no such record", to);
00301 }
00302 else if(!strcmp("put", g_list_nth_data(params, 1)) && g_list_length(params) > 3)
00303 {
00304 if(lp_identified(msg->from))
00305 {
00306 lp_user *user = lp_find_user(msg->from);
00307 if(user->rights & LP_RIGHT_DB)
00308 {
00309 lp_record *record;
00310 for(i=0;i<g_list_length(config->records);i++)
00311 {
00312 record = g_list_nth_data(config->records, i);
00313 if(!strcmp(record->name, g_list_nth_data(params, 2)))
00314 {
00315 found = 1;
00316 break;
00317 }
00318 }
00319 if(!found)
00320 {
00321 record = g_new0(lp_record, 1);
00322 record->name = g_strdup(g_list_nth_data(params, 2));
00323 config->records = g_list_append(config->records, record);
00324 }
00325 lp_record_ver *ver = g_new0(lp_record_ver, 1);
00326 ver->date = time(NULL);
00327 ver->author = g_strdup(msg->from);
00328 ver->content = g_string_new(g_strdup(g_list_nth_data(params, 3)));
00329 for(i=4;i<g_list_length(params);i++)
00330 g_string_append_printf(ver->content, " %s",
00331 (char*)g_list_nth_data(params, i));
00332 record->versions = g_list_insert(record->versions, ver, 0);
00333 saveRecords("db.xml");
00334 if(found)
00335 lp_send(server, "privmsg %s :okay, updated", to);
00336 else
00337 lp_send(server, "privmsg %s :okay, inserted", to);
00338 }
00339 else
00340 lp_send(server, "privmsg %s :you don't have rights to alter the db", to);
00341 }
00342 else
00343 lp_send(server, "privmsg %s :identify first to alter the db", to);
00344 }
00345 else if(!strcmp("del", g_list_nth_data(params, 1)) && g_list_length(params) == 3)
00346 {
00347 if(lp_identified(msg->from))
00348 {
00349 lp_user *user = lp_find_user(msg->from);
00350 if(user->rights & LP_RIGHT_DB)
00351 {
00352 lp_record *record;
00353 for(i=0;i<g_list_length(config->records);i++)
00354 {
00355 record = g_list_nth_data(config->records, i);
00356 if(!strcmp(record->name, g_list_nth_data(params, 2)))
00357 {
00358 found = 1;
00359 break;
00360 }
00361 }
00362 if(!found)
00363 lp_send(server, "privmsg %s :no such record", to);
00364 else
00365 {
00366 config->records = g_list_remove(config->records, record);
00367 saveRecords("db.xml");
00368 lp_send(server, "privmsg %s :okay, deleted", to);
00369 }
00370 }
00371 else
00372 lp_send(server, "privmsg %s :you don't have rights to alter the db", to);
00373 }
00374 else
00375 lp_send(server, "privmsg %s :identify first to alter the db", to);
00376 }
00377 }
00378 if(!strcmp("whoami", g_list_nth_data(params, 0)))
00379 {
00380 if(lp_identified(msg->from))
00381 lp_send(server, "privmsg %s :i know who you are, %s.",
00382 to, msg->from);
00383 }
00384 if(!strcmp("remind", g_list_nth_data(params, 0)))
00385 {
00386 lp_user *user = lp_find_user(msg->from);
00387 if(user)
00388 {
00389 remind(user);
00390 lp_send(server, "privmsg %s :okay, reminder sent", to);
00391 }
00392 else
00393 lp_send(server, "privmsg %s :no such user, so i don't know the pass!", to);
00394 }
00395 if(!strcmp("identify", g_list_nth_data(params, 0)))
00396 {
00397 if(config->ident_method == LP_IDENT_PASS)
00398 {
00399 for(i=0;i<g_list_length(config->users);i++)
00400 {
00401 lp_user *user = g_list_nth_data(config->users, i);
00402 if(!strcmp(user->login, msg->from) &&
00403 g_list_length(params) == 2 &&
00404 !strcmp(user->pass, g_list_nth_data(params, 1)))
00405 {
00406 user->identified = 1;
00407 lp_send(server, "privmsg %s :ok, now i know you, %s.",
00408 to, user->login);
00409 break;
00410 }
00411 }
00412 }
00413 if(config->ident_method == LP_IDENT_SERVICES)
00414 {
00415 if(config->ident_to)
00416 free(config->ident_to);
00417 config->ident_to = g_strdup(to);
00418 lp_send(server, "whois %s", msg->from);
00419 }
00420 }
00421 if(!strcmp("quit", g_list_nth_data(params, 0)) && server->is_console)
00422 {
00423 lp_user *user = lp_find_user(msg->from);
00424 if(user)
00425 user->identified = 0;
00426 lp_disconnect(server, "bye");
00427 return FALSE;
00428 }
00429 if(!strcmp("connect", g_list_nth_data(params, 0)))
00430 {
00431 lp_user *user = lp_find_user(msg->from);
00432 if(user && user->identified)
00433 {
00434 if(user->rights & LP_RIGHT_OP && g_list_length(params) >5)
00435 {
00436 lp_server *new = g_new0(lp_server, 1);
00437 new->chatname = g_strdup(g_list_nth_data(params, 1));
00438 new->address = g_strdup(g_list_nth_data(params, 2));
00439 new->port = atoi(g_list_nth_data(params, 3));
00440 new->nick = g_strdup(g_list_nth_data(params, 4));
00441 new->username = g_strdup(g_list_nth_data(params, 4));
00442 new->realname = g_strdup(g_list_nth_data(params, 4));
00443 for(i=5;i<g_list_length(params);i++)
00444 new->channels = g_list_append(new->channels, g_strdup(g_list_nth_data(params, i)));
00445 config->servers = g_list_append(config->servers, new);
00446 lp_connect(new);
00447 lp_send(server, "privmsg %s :ok, connected", to);
00448 }
00449 else
00450 lp_send(server, "privmsg %s :you don't have rights to alter the db", to);
00451 }
00452 else
00453 lp_send(server, "privmsg %s :identify first to alter the db", to);
00454 }
00455 return TRUE;
00456 }
00457
00464 int lp_handler(GIOChannel *source, GIOCondition condition, gpointer data)
00465 {
00466 char c = 0, buf[IRC_LINE_LENGHT+1] = "";
00467 int i = 0, len;
00468 lp_server *server = (lp_server*)data;
00469 lp_msg *msg;
00470
00471 while(c != '\n' && i < IRC_LINE_LENGHT)
00472 {
00473 len = read(server->sock, &c, 1);
00474 if(len == 0 || ( len < 0 && !sockerr_again() ))
00475 {
00476 lp_reconnect(server, "Read error");
00477 return FALSE;
00478 }
00479 buf[i++] = c;
00480 }
00481 if(buf[i-2]=='\r')
00482 i--;
00483 buf[i-1] = '\0';
00484 msg = lp_parse(buf);
00485
00486 if(!msg)
00487 return TRUE;
00488 if(!strcmp(msg->cmd, "001"))
00489 {
00490
00491 for(i=0;i<g_list_length(server->channels); i++)
00492 lp_send(server, "join :%s", (char*)g_list_nth_data(server->channels, i));
00493 g_timeout_add(10000, lp_ping, (gpointer)server);
00494 }
00495 else if(!strcmp(msg->cmd, "320"))
00496 {
00497
00498 if(config->ident_method == LP_IDENT_SERVICES)
00499 {
00500 for(i=0;i<g_list_length(config->users);i++)
00501 {
00502 lp_user *user = g_list_nth_data(config->users, i);
00503 if(g_list_length(msg->params) > 0 &&
00504 !strcmp(user->login, g_list_nth_data(msg->params, 0)))
00505 {
00506 user->identified = 1;
00507 lp_send(server, "privmsg %s :ok, now i know you, %s.",
00508 config->ident_to, user->login);
00509 break;
00510 }
00511 }
00512 }
00513 }
00514 else if(!strcmp(msg->cmd, "PONG"))
00515 {
00516 server->lastpong = time(NULL);
00517 }
00518 else if(!strcmp(msg->cmd, "PRIVMSG"))
00519 {
00520 if(!strncmp(server->nick, g_list_nth_data(msg->params, 0), strlen(server->nick)))
00521
00522 return lp_handle_command(server, msg, g_list_next(msg->params));
00523 else if(!strncmp(server->nick, msg->to, strlen(server->nick)))
00524
00525 return lp_handle_command(server, msg, msg->params);
00526 }
00527 else if(!strcmp(msg->cmd, "QUIT") || !strcmp(msg->cmd, "PART"))
00528 {
00529 for(i=0;i<g_list_length(config->users);i++)
00530 {
00531 lp_user *user = g_list_nth_data(config->users, i);
00532 if(!strcmp(user->login, msg->from))
00533 {
00534 user->identified = 0;
00535 break;
00536 }
00537 }
00538 }
00539 lp_msg_free(msg);
00540 return TRUE;
00541 }
00542
00543
00544
00545
00546
00547
00548
00549 int lp_connect(lp_server *server)
00550 {
00551 struct hostent host;
00552 struct sockaddr_in conn;
00553
00554 if(lp_resolve(server->address, &host)<0)
00555 return -1;
00556 server->sock = lp_create_sock();
00557 conn.sin_family = AF_INET;
00558 conn.sin_port = htons(server->port);
00559 conn.sin_addr = *((struct in_addr *) host.h_addr);
00560 memset(&(conn.sin_zero), 0, 8);
00561 if(connect(server->sock, (struct sockaddr *)&conn, sizeof(struct sockaddr))<0)
00562 {
00563 perror("connect");
00564 return -1;
00565 }
00566 server->chan = g_io_channel_unix_new(server->sock);
00567 if(server->nick)
00568 {
00569 g_io_add_watch(server->chan, G_IO_IN, lp_handler, (gpointer)server);
00570 lp_send(server, "nick %s", server->nick);
00571 lp_send(server, "user %s 8 * :%s", server->username, server->realname);
00572 }
00573 return 0;
00574 }
00575
00581 int lp_disconnect(lp_server *server, char *msg)
00582 {
00583 if(msg)
00584 lp_send(server, "quit :%s", msg);
00585 close(server->sock);
00586 server->sock = 0;
00587 return 0;
00588 }
00589
00595 int lp_reconnect(lp_server *server, char *msg)
00596 {
00597 lp_disconnect(server, msg);
00598 lp_connect(server);
00599 return 0;
00600 }
00601
00608 int lp_listen(GIOChannel *source, GIOCondition condition, gpointer data)
00609 {
00610 int sock = (int)data;
00611 lp_server *server = g_new0(lp_server, 1);
00612 server->sock = accept(sock, NULL, NULL);
00613 server->chan = g_io_channel_unix_new(server->sock);
00614 server->nick = g_strdup("lpbot");
00615 server->is_console = 1;
00616 g_io_add_watch(server->chan, G_IO_IN, lp_handler, (gpointer)server);
00617 return TRUE;
00618 }
00619
00623 int lp_serve()
00624 {
00625 int sock = lp_create_sock();
00626 int opt = 1;
00627 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
00628 struct sockaddr_in conn;
00629 memset(&conn, 0, sizeof(conn));
00630 conn.sin_family = AF_INET;
00631 conn.sin_port = htons(1100);
00632 conn.sin_addr.s_addr = htonl(INADDR_ANY);
00633 bind(sock,(struct sockaddr*) &conn, sizeof(conn));
00634 listen(sock, 1);
00635 GIOChannel *chan = g_io_channel_unix_new(sock);
00636 g_io_add_watch(chan, G_IO_IN, lp_listen, (gpointer)sock);
00637 return 0;
00638 }
00639