lib.c

Go to the documentation of this file.
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         // check if we can write to avoid a sigpipe
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         // every irc msg should have a from/to/cmd
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         // params are optional
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         // if the msg is from a channel, then returns the channel, if
00212         // it's from a query, then return the user
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         // FIXME: this could be more generic to provide
00262         // help, etc
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         // probably a ping which is handled by the idle handler or so
00486         if(!msg)
00487                 return TRUE;
00488         if(!strcmp(msg->cmd, "001"))
00489         {
00490                 // welcome
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                 // identified
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                         // highlight
00522                         return lp_handle_command(server, msg, g_list_next(msg->params));
00523                 else if(!strncmp(server->nick, msg->to, strlen(server->nick)))
00524                         // query
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  * Connects to an TCP server, if server->nick is given, then it logins
00545  * in to an IRC server as well.
00546  * @param server the server to connect to
00547  * @return -1 on error, 0 on success
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 /* @} */

Generated on Mon May 19 15:36:55 2008 for lpbot by  doxygen 1.5.4