5749371 [rkeene@sledge /home/rkeene/projects/ircbot/bot]$ cat -n bot.c
  1 /*
  2  * bot.c version 0.333
  3  * all versions previous to 0.333 are completely public domain
  4  * all contributed code falls under the same BSD style license as
  5  * noted below unless the contributing author places a copyright
  6  * notice in their file/s.
  7  */
  8 
  9 
 10 /*
 11  *  Copyright (c) 2001 David T. Stiles
 12  *  All rights reserved.
 13  * 
 14  *  Redistribution and use in source and binary forms, with or without
 15  *  modification, are permitted provided that the following conditions
 16  *  are met:
 17  *  1. Redistributions of source code must retain the above copyright
 18  *     notice, this list of conditions and the following disclaimer.
 19  *  2. Redistributions in binary form must reproduce the above copyright
 20  *     notice, this list of conditions and the following disclaimer in the
 21  *     documentation and/or other materials provided with the distribution.
 22  *  3. All advertising materials mentioning features or use of this software
 23  *     must display the following acknowledgement:
 24  *       This product includes software developed by David T. Stiles
 25  *  4. The name David T. Stiles may not be used to endorse or promote
 26  *     products derived from this software without specific prior written
 27  *     permission.
 28  * 
 29  *  THIS SOFTWARE IS PROVIDED BY DAVID T. STILES `AS IS'' AND ANY
 30  *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 31  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 32  *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 33  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 34  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 35  *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 36  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 37  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 38  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 39  *  SUCH DAMAGE.
 40  */
 41 
 42 /* this code would not be possible without the patience and intelligence
 43  * provided by many of the people from #c/efnet. I thank all of you sincerely.
 44  */
 45 
 46 /*
 47  * a client/bot irc thingy. 1 dec 98
 48  * This #c project is officially named "Sahib" on 3 February 1999
 49  * serious modification/cleaning/extending proceeding on 1 September 2000
 50  * merging Aegis' code starting sometime in October of 2000.
 51  * ripped out the database and turned it into a separate server 13 Oct. 2000
 52  * checking comments on 31 jan 2001
 53  * radical restructuring of all source code starting on 4 june 2001
 54  */
 55 
 56 #include "bot.h"
 57 #include "users.h"
 58 #include "calcdb.h"
 59 #include "dcalc.h"
 60 #include "rpn.h"
 61 
 62 
 63 /* yeah, i know that globals are considered evil. byte me. */
 64 
 65 /* config file variables. declared in the order that they are read, to facilitate
 66  * config file re/creation. PORT and MAXCALCS have defaults that are
 67  * overwritten by the contents of the config file
 68  */
 69 
 70    static   char SERVER[MAXDATASIZE];  /* name of the irc server to connect with */
 71    static   int  PORT = 6667;          /* default to the standard irc server port */
 72    static   char NICK1[MAXDATASIZE];   /* preferred nickname to use */
 73    static   char NICK2[MAXDATASIZE];   /* alternate nickname */
 74    static   char USER[MAXDATASIZE];    /* the USER irc protocol message */
 75    static   int  MAXCALCS = 5000;      /* make the default max database size conservative */
 76    static   char CALCDB[MAXDATASIZE];  /* name of, and possibly path to, the calc database file */
 77 
 78 
 79 
 80 /* other misc local globals that are needed. i fail to see any non-hacked way
 81  * way to get rid of these.
 82  */
 83 
 84    static   int  sockfd;               /* used for the socket file descriptor */
 85    static   char BOTNAME[MAXDATASIZE]; /* holds the actual nickname in use by the bot */
 86    static   struct message cur_msg;    /* defined in bot.h, holds a parsed irc message */
 87 
 88 
 89 /* this variable is extern'ed to ALL modules needing to send their own irc messages */
 90 
 91    char MSGTO[MAXDATASIZE];   /* set to nick/channel on each incoming message */
 92 
 93 
 94 /******************************----BEGIN CODE----*********************************/
 95 
 96 
 97 
 98 /* return if there are network difficulties, otherwise, this is where the main purpose
 99  * of this program really begins. an endless loop around select().
100  * select on stdin and the socket.
101  */
102 
103 void main_loop( void )
104 { 
105    int whatever;
106    fd_set fdgroup;
107    struct timeval tv;
108 
109    for( ;; )
110     {
111       FD_ZERO(&fdgroup);
112       FD_SET(STDIN_FILENO, &fdgroup);
113       FD_SET(sockfd, &fdgroup);
114       tv.tv_sec = 360;          /* if no data for 6 minutes, something is wrong. */
115       tv.tv_usec = 0;
116 
117       whatever = select( (sockfd + 1) , &fdgroup, NULL, NULL, &tv);
118  
119       if( !whatever ) break;     /* we must not be connected anymore, return. */
120       if( FD_ISSET( sockfd, &fdgroup ) ) if( process_in( ) ) break;
121       if( FD_ISSET( STDIN_FILENO, &fdgroup ) ) if( process_out( ) ) break;
122 
123     }
124 
125    return;
126 }
127 
128 
129 
130 /* you can pretend you have a console through this code. '/' will allow raw irc protocol out.
131  * btw, there are no real sanity checks on this function. the person at the console is supposed
132  * to be not malicious. fgets should prevent accidental buffer overruns though. :)
133  */
134 
135 int process_out( void )
136 {
137    char ray[MAXDATASIZE], tmp[MAXDATASIZE];
138 
139    fgets( tmp, MAXDATASIZE - 1, stdin );
140    if( tmp[0] == '/' ) strncpy( ray, (tmp + 1), MAXDATASIZE );
141    else snprintf( ray, MAXDATASIZE, "PRIVMSG %s :%s", DEF_CHAN, tmp );
142    send_irc_message( ray );
143 
144    return 0;
145 }
146 
147 
148 
149 /* ugh. this one is impossible to comment on. what it does is make sure that only
150  * complete lines are processed. It uses a static buffer to hold the incomplete
151  * information until the next packet comes in. X is the placeholder index.
152  * marked High Priority for recoding. this code sucks badly... even though it works
153  * this hurts my eyes every time i see it.
154  */
155 
156 int process_in( void )
157 {
158    char buf[MAXDATASIZE], *ptr;
159    int numbytes = 0;  
160    int z = 0;
161    int y = 0;
162    static int x = 0;
163    static char tmpbuf[MAXDATASIZE];
164 
165    if( (numbytes = recv( sockfd, buf, MAXDATASIZE, 0)) == -1)
166     {
167       perror("recv");
168       return 1;
169     }
170 
171    if( !numbytes ) return 1;
172    if( numbytes < MAXDATASIZE ) buf[numbytes] = '\0';
173    buf[MAXDATASIZE - 1] = '\0';
174 
175    ptr = buf;
176 
177    for( y = 0; y < numbytes; y++)
178     {
179       while( *(ptr + y) != '\n' )
180        {
181          tmpbuf[x] = *( ptr + y );
182          x+=1; y+=1;
183          if( x == MAXDATASIZE ) x--;
184          if( *(ptr + y) == '\0' ) break;
185        }
186       tmpbuf[x] = '\0';
187       if( *(ptr + y) == '\n' )
188        {
189           x = 0;
190           parse_incoming( tmpbuf );
191           for( z = 0; z < MAXDATASIZE; z++ ) tmpbuf[z] = '\0';
192        }
193     }
194 
195 
196    return 0;
197 }
198 
199 
200 
201 /* full ircd messages come through here for parsing.
202  * this form of parsing is based around the "typical" irc message where someone
203  * actually says something. if it is some other type of message, the parser tries
204  * to copy into msgarg1 etc but will fall through when the arguments don't fall
205  * neatly into place.
206  */
207 
208 void parse_incoming( char *ptr )
209 {
210    int position = 0;
211 
212    clean_message( ptr );
213 
214    if( ptr[0] == 'P' ) {  /* this is will catch server PINGs */
215       reply_ping( ptr );
216       return;             /* no need to continue parsing */
217      }
218 
219    memset( &cur_msg, 0, sizeof(cur_msg) );  /* clear the buffer... i want a safe default */
220 
221    /* do an initial breakup of the message based on whitespace */
222 
223    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.userline, 1, ' ' );  /* 1 to skip the leading colon */
224    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.msgtype, position, ' ' );
225    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.msgto, position, ' ' );
226    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.fulltext, position + 1, '\n' );  /* + 1 to skip the
	colon */
227 
228    /*break down the host/nick type stuff */
229 
230    position = chop( ptr, cur_msg.nick, 1, '!' );
231    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.logname, position, '@' );
232    if( *(ptr + position) != '\0' ) position = chop( ptr, cur_msg.hostname, position, ' ' );
233 
234    /* now that the protocol crap is gone, let's parse what the person said. */
235    if( (cur_msg.msgtype[0] == 'P') || (cur_msg.msgtype[0] == 'N') ) { /*PRIVMSG or NOTICE, NICK triggers too though */
236       position = chop( cur_msg.fulltext, cur_msg.msgarg1, 0, ' ' );
237       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg2, position, ' ' );
238       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg3, position, ' ' );
239       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg4, position, ' ' );
240       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg5, position, ' ' );
241       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg6, position, ' ' );
242       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg7, position, ' ' );
243       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg8, position, ' ' );
244       if( *(ptr + position) != '\0' ) position = chop( cur_msg.fulltext, cur_msg.msgarg9, position, ' ' );
245      }
246 
247    /* console output with simple formatting */
248    if( !strncasecmp( cur_msg.msgtype, "PRIVMSG", MAXDATASIZE ) ) printf( "|%s|  %s: %s\n", cur_msg.msgto, cur_msg.nick,
	cur_msg.fulltext );
249    else puts( ptr );
250    make_a_decision();
251    return;
252 }
253 
254 
255 
256 /* after the message from the ircd has been parsed, i need to decide what course of
257  * action to take based upon that input. this is where events are triggered.
258  */
259 
260 void make_a_decision( void )
261 {
262 
263    /* don't let it talk to itself, so make sure this is first in the list. */
264    if( !strncasecmp( BOTNAME, cur_msg.nick, MAXDATASIZE ) ) return;
265 
266    /* this sets msgto to the msg sender's nick if it was a privmsg to the bot itself.*/
267    /* otherwise it makes it possible to talk to the channel that sent the message. */
268    if( !strcmp( cur_msg.msgto, BOTNAME ) ) strncpy( MSGTO, cur_msg.nick, MAXDATASIZE );
269    else strncpy( MSGTO, cur_msg.msgto, MAXDATASIZE );
270 
271    /* switch on the first character of the first "word" in the message */
272    switch( tolower(cur_msg.msgarg1[0]) ) {
273       case 1:
274         if( !strncasecmp( BOTNAME, cur_msg.msgto, MAXDATASIZE ) ) { do_ctcp(); return; }
275       case 'c':
276         if( !strncasecmp( "chpass", cur_msg.msgarg1, MAXDATASIZE ) ) { chpass_stub(); return; }
277         if( !strncasecmp( "calc", cur_msg.msgarg1, MAXDATASIZE ) ) { docalc_stub(); return; }
278         if( !strncasecmp( "clac", cur_msg.msgarg1, MAXDATASIZE ) ) { docalc_stub(); return; }
279       case 'o':
280         if( !strncasecmp( "op", cur_msg.msgarg1, MAXDATASIZE ) ) { oppeople_stub(); return; }
281         if( !strncasecmp( "owncalc", cur_msg.msgarg1, MAXDATASIZE ) ) { owncalc_stub(); return; }
282       case 'w':
283         if( !strncasecmp( "whois", cur_msg.msgarg1, MAXDATASIZE ) ) { whois_stub(); return; }
284       case 'a':
285         if( !strncasecmp( "adduser", cur_msg.msgarg1, MAXDATASIZE ) ) { adduser_stub(); return; }
286       case 'h':
287         if( !strncasecmp( "help", cur_msg.msgarg1, MAXDATASIZE ) ) { help(); return; }
288       case 'r':
289         if( !strncasecmp( "rmuser", cur_msg.msgarg1, MAXDATASIZE ) ) { rmuser_stub(); return; }
290         if( !strncasecmp( "rmcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { rmcalc_stub(); return; }
291         if( !strncasecmp( "rawirc", cur_msg.msgarg1, MAXDATASIZE ) ) { rawirc(); return; }
292         if( !strncasecmp( "rcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { rpn_stub(); return; }
293       case 'm':
294         if( !strncasecmp( "mkcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { mkcalc_stub(); return; }
295       case 'l':
296         if( !strncasecmp( "login", cur_msg.msgarg1, MAXDATASIZE ) ) { help(); return; }
297         if( !strncasecmp( "listcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { listcalc_stub(); return; }
298       case 'x':
299         if( !strncasecmp( "xpln", cur_msg.msgarg1, MAXDATASIZE ) ) { docalc_stub(); return; }
300       case 'd':
301         if( !strncasecmp( "dcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { dcalc_stub(); return; }
302       case 's':
303         if( !strncasecmp( "searchcalc", cur_msg.msgarg1, MAXDATASIZE ) ) { searchcalc_stub(); return; }
304       default:
305         break;
306      }
307 
308    return;
309 }
310 
311 
312 
313 /* how to comment? this function is almost pointless as is. its purpose will become
314  * clear later... after it is fully implemented. all it does now is ensure that a
315  * newline char is sent after each message and provide a wrapper for the network I/O.
316  * eventually, the bot will monitor its own output here.
317  */
318 
319 void send_irc_message( char *sndmsg )
320 {
321    char newline = '\n';
322 
323    if( send (sockfd, sndmsg, strlen( sndmsg ), 0) == -1) perror("send");
324    if( send (sockfd, &newline, 1, 0) == -1) perror("send");
325    sleep( 1 );   /* pause for a second to help keep things from getting too crazy. */
326    return;
327 }
328 
329 
330 /*******************************-----begin stubs-----************************************/
331 
332 
333 /* chpass stub finished */
334 
335 void chpass_stub()
336 {
337     chpass( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4 );
338     return;
339 }
340 
341 
342 /* docalc stub finished */
343 
344 void docalc_stub()
345 {
346     docalc( cur_msg.msgarg2 );
347     return;
348 }
349 
350 
351 /* op_people stub finished */
352 
353 void oppeople_stub()
354 {
355     /* making getting ops easier for other than the default channel */
356     if( (cur_msg.msgarg2[0] == '#') || (cur_msg.msgarg2[0] == '&') ) {
357         if( cur_msg.msgarg4[0] ) oppeople( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4, cur_msg.nick );
358         else oppeople( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.nick, cur_msg.nick );
359         return;
360     }
361     /* op only in the default channel */
362     if( cur_msg.msgarg3[0] ) oppeople( DEF_CHAN, cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.nick );
363     else oppeople( DEF_CHAN, cur_msg.msgarg2, cur_msg.nick, cur_msg.nick );
364     return;
365 }
366 
367 
368 /* owncalc stub finished */
369 
370 void owncalc_stub()
371 {
372     owncalc( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.nick );
373     return;
374 }
375 
376 
377 /* whois stub finished */
378 
379 void whois_stub()
380 {
381     whois( cur_msg.msgarg2 );
382     return;
383 }
384 
385 
386 /* adduser stub finished */
387 
388 void adduser_stub()
389 {
390     adduser( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4, cur_msg.msgarg5 );
391     return;
392 }
393 
394 
395 /* rmuser stub finished */
396 
397 void rmuser_stub()
398 {
399     rmuser( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4 );
400     return;
401 }
402 
403 
404 /* rmcalc stub finished */
405 
406 void rmcalc_stub()
407 {
408     rmcalc( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4 );
409     return;
410 }
411 
412 
413 /*
414  * i parse cur_msg.fulltext to step over command arguments and login info.
415  * the actual calc data can obviously have spaces, so the original
416  * parsing of the irc message is insufficient. note that 'newcalctext' gets
417  * overwritten by each call to chop(). after the last call, it holds the calc data.
418  */
419 
420 void mkcalc_stub()
421 {
422    int y;
423    char newcalctext[MAXDATASIZE];
424 
425    y = chop( cur_msg.fulltext, newcalctext, 0, ' ' );
426    y = chop( cur_msg.fulltext, newcalctext, y, ' ' );
427    y = chop( cur_msg.fulltext, newcalctext, y, ' ' );
428    y = chop( cur_msg.fulltext, newcalctext, y, ' ' );
429    y = chop( cur_msg.fulltext, newcalctext, y, '\0' );
430     
431     mkcalc( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.msgarg4, newcalctext );
432     return;
433 }
434 
435 
436 /* listcalc stub finished */
437 
438 void listcalc_stub()
439 {
440     listcalc( cur_msg.msgarg2, cur_msg.msgarg3, cur_msg.nick );
441     return;
442 }
443 
444 
445 /* searchcalc stub */
446 
447 void searchcalc_stub()
448 {
449     searchcalc( cur_msg.msgarg2, cur_msg.msgarg3 );
450     return;
451 }
452 
453 
454 /*stub for couts RPN calculator */
455 
456 int rpn_stub( void )
457 {
458   int x = 0;
459   char tmpray[MAXDATASIZE], answer[MAXDATASIZE];
460 
461   rpn_calc( cur_msg.fulltext + (strlen( cur_msg.msgarg1 ) + 1), answer, (MAXDATASIZE - 2) );
462 
463   if( x != RPN_OK ) snprintf( tmpray, MAXDATASIZE, "privmsg %s :error: %s", MSGTO, answer );
464   else snprintf( tmpray, MAXDATASIZE, "privmsg %s :cout says: %s", MSGTO, answer );
465 
466   send_irc_message( tmpray );
467 
468   return 0;
469 }
470 
471 
472 
473 /* stub for demoncrat's "normal" calculator code */
474 
475 int dcalc_stub( void )
476 {
477   char tmpray[MAXDATASIZE];
478   Value v;
479   const char *plaint;
480 
481 
482   plaint = dcalc(&v, cur_msg.fulltext + (strlen( cur_msg.msgarg1 ) + 1) );
483 
484   if( plaint ) snprintf( tmpray, MAXDATASIZE, "privmsg %s :answer: %s", MSGTO, plaint);
485   else snprintf( tmpray, MAXDATASIZE, "privmsg %s :answer: %.16g", MSGTO, v);
486 
487   send_irc_message( tmpray );
488 
489   return 0;
490 }
491 
492 
493 /********************************-----end stubs-----*************************************/
494 
495 
496 
497 /* repsond only to private requests for help. the actual messages are #defined in bot.h
498  * i should make these loadable from a file rather than compiled in.
499  * erm... why don't i make this a switch() like i did in make_a_decision()?
500  */
501 
502 void help( void )
503 {
504    char tmpray[MAXDATASIZE];
505 
506    if( strncasecmp( cur_msg.msgto, BOTNAME, MAXDATASIZE ) ) return;
507 
508    if( !strncasecmp( cur_msg.msgarg2, "commands", MAXDATASIZE ) ) {
509       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, COMMANDS );
510       send_irc_message( tmpray );
511       return;
512      }
513    if( !strncasecmp( cur_msg.msgarg2, "syntax", MAXDATASIZE ) ) {
514       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, SYNTAX );
515       send_irc_message( tmpray );
516       return;
517      }
518    if( !strncasecmp( cur_msg.msgarg2, "adduser", MAXDATASIZE ) ) {
519       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, ADDUSER );
520       send_irc_message( tmpray );
521       return;
522      }
523    if( !strncasecmp( cur_msg.msgarg2, "rmuser", MAXDATASIZE ) ) {
524       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, RMUSER );
525       send_irc_message( tmpray );
526       return;
527      }
528    if( !strncasecmp( cur_msg.msgarg2, "op", MAXDATASIZE ) ) {
529       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, OP );
530       send_irc_message( tmpray );
531       return;
532      }
533    if( !strncasecmp( cur_msg.msgarg2, "chpass", MAXDATASIZE ) ) {
534       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, CHPASS );
535       send_irc_message( tmpray );
536       return;
537      }
538    if( !strncasecmp( cur_msg.msgarg2, "rmcalc", MAXDATASIZE ) ) {
539       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, RMCALC );
540       send_irc_message( tmpray );
541       return;
542      }
543    if( !strncasecmp( cur_msg.msgarg2, "owncalc", MAXDATASIZE ) ) {
544       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, OWNCALC );
545       send_irc_message( tmpray );
546       return;
547      }
548    if( !strncasecmp( cur_msg.msgarg2, "mkcalc", MAXDATASIZE ) ) {
549       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, MKCALC );
550       send_irc_message( tmpray );
551       return;
552      }
553    if( !strncasecmp( cur_msg.msgarg2, "rawirc", MAXDATASIZE ) ) {
554       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, RAWIRC );
555       send_irc_message( tmpray );
556       return;
557      }
558    if( !strncasecmp( cur_msg.msgarg2, "whois", MAXDATASIZE ) ) {
559       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, WHOIS );
560       send_irc_message( tmpray );
561       return;
562      }
563    if( !strncasecmp( cur_msg.msgarg2, "listcalc", MAXDATASIZE ) ) {
564       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, LISTCALC );
565       send_irc_message( tmpray );
566       return;
567      }
568    if( !strncasecmp( cur_msg.msgarg2, "searchcalc", MAXDATASIZE ) ) {
569       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, SEARCHCALC );
570       send_irc_message( tmpray );
571       return;
572      }
573    if( !strncasecmp( cur_msg.msgarg2, "chattr", MAXDATASIZE ) ) {
574       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, CHATTR );
575       send_irc_message( tmpray );
576       return;
577      }
578 
579 
580    snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :%s", cur_msg.nick, HELPHELP );
581    send_irc_message( tmpray );
582 
583    return;
584 }
585 
586 
587 
588 /* CTCPs are accomplished through 'NOTICE' messages. the first letter of each
589  * important CTCP is unique so i merely switch on that value. the default is
590  * to merely echo back the same type of NOTICE but without arguments. (this covers ping)
591  * VERSION is the only CTCP that i care about currently.
592  */
593 
594 void do_ctcp( void )
595 {
596    char ray[MAXDATASIZE];
597 
598    switch( cur_msg.msgarg1[1] ) {
599       case 'V':
600         snprintf( ray, MAXDATASIZE, "NOTICE %s :\001VERSION ircII.5 not quite all there yet.\001", cur_msg.nick );
601         break;
602       default:
603         snprintf( ray, MAXDATASIZE, "NOTICE %s :%s\001", cur_msg.nick, cur_msg.msgarg1 );
604         break;
605      }
606 
607    send_irc_message( ray );
608    return;
609 }
610 
611 
612 
613 /* this function validates user/pass and then sends the rest of the text to the server.
614  * the code is trivial enough to remain inside of bot.c
615  */
616 
617 void rawirc( void )
618 {
619    int y;
620    char tmpray[MAXDATASIZE];
621 
622    if( !valid_login( cur_msg.msgarg3, cur_msg.msgarg2 ) ){
623       snprintf( tmpray, MAXDATASIZE, "PRIVMSG %s :failed login", cur_msg.nick );
624       send_irc_message( tmpray );
625       return;
626      }
627 
628    /* this is the same parsing technique described in mkcalc_stub() */
629    y = chop( cur_msg.fulltext, tmpray, 0, ' ' );
630    y = chop( cur_msg.fulltext, tmpray, y, ' ' );
631    y = chop( cur_msg.fulltext, tmpray, y, ' ' );
632    y = chop( cur_msg.fulltext, tmpray, y, '\0' );
633 
634    send_irc_message( tmpray );
635 
636    return;
637 }
638 
639 
640 
641 /* let the ^A chars stay, but no other nonprintable chars... walking through the array.
642  * i really like pointer arithmetic, i apologise to those who find this unreadable.
643  */
644 
645 void clean_message( char *msg )
646 {
647    register int x;
648    for( x = 0; x < strlen(msg); x++ ) {
649       if( *(msg+x) == 1 ) continue;
650       if( !isprint( *(msg+x) ) ) {
651          if( *(msg+x) == '\r' ) { *(msg+x) = '\0'; continue; }
652          if( *(msg+x) == '\n' ) { *(msg+x) = '\0'; continue; }
653          *(msg+x) = ' ';
654         }
655      }
656    return;
657 }
658 
659 
660 
661 /* the first array is the array you wish to break up nondestructively
662  * the second array will hold the section that is "chopped off".
663  * 'position' is the location in the array you want it to start looking for 'separator'.
664  * the separator character is the token you wish to chop on. e.g. a space character.
665  * this function is intentionally dangerous. it assumes that only proper NULL terminated
666  * arrays will be passed to it.
667  */
668 
669 int chop( char *in, char *out, int position, char separator )
670 {
671    int x = 0;
672 
673    while( *(in + position) != separator )
674     {
675       *(out + x) = *(in + position);
676       x+=1; position+=1;
677       if( *(in + position) == '\0' ) break;
678       if( *(in + position) == '\r' ) break;
679       if( *(in + position) == '\n' ) break;
680     }
681 
682    *(out + x) = '\0';
683    return (*(in + position)) ? position+1 : position; /* thanks for the simpler 'if' construct Xgc */
684 }
685 
686 
687 
688 /* the name of the function explains it fairly well. it merely replies to any
689  * PING requests from IRC servers.
690  * ptr holds "PING irc.home.com" so i start copying from the end of "PING "
691  * onwards, which is 5 chars, to get the irc server name for the PONG response.
692  */
693 
694 void reply_ping( char *ptr )
695 {
696    char str[MAXDATASIZE];
697 
698    snprintf( str, MAXDATASIZE - 5, "PONG %s", (ptr + 5) );
699    send_irc_message( str );
700 
701    return;
702 }
703 
704 
705 
706 /* hide most of the network stuff here.
707  * pass in a host name, port, and a file descriptor and this function
708  * will take care of the network details. the value it returns should
709  * be assigned to a file descriptor or evaluated for errors.
710  */
711 
712 int host_connect( char *exthost, int extport, int extsockfd )
713 {
714    struct hostent *he;
715    struct sockaddr_in their_addr;
716 
717    if ((he=gethostbyname( exthost )) == NULL)
718     {
719        perror("gethostbyname");
720        return -1;
721     }
722 
723    if ((extsockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
724     {
725        perror("socket");
726        close( extsockfd );
727        return -1;
728     }
729 
730    their_addr.sin_family = AF_INET;
731    their_addr.sin_port = htons(extport);
732    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
733    memset( &their_addr.sin_zero, '\0', 8 );
734 
735    if (connect(extsockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
736     {
737        perror("connect");
738        close( extsockfd );
739        return -1;
740     }
741 
742 
743    return extsockfd;
744 
745 }
746 
747 
748 
749 /* connect to the irc server and register.
750  * ugly ugly ugly
751  */
752 
753 int irc_connect( void )
754 {
755    char ray[MAXDATASIZE];
756    int numbytes = 0;
757 
758    sockfd = 0;
759 
760    if( (sockfd = host_connect(SERVER, PORT, sockfd)) < 1) return 1;
761 
762    strncpy( BOTNAME, NICK1, MAXDATASIZE );
763    send_irc_message( USER );
764 
765    memset( ray, '\0', sizeof(ray) );
766    snprintf( ray, MAXDATASIZE, "nick %s", BOTNAME );
767    send_irc_message( ray );
768 
769    sleep( 3 );
770 
771    numbytes = recv( sockfd, ray, MAXDATASIZE, 0);
772    if( numbytes == -1 )
773       {
774       perror("recv");
775       close( sockfd );
776       return 1;
777     }
778 
779    if( strstr( ray, "PING" ) ) { reply_ping( ray ); }
780    if( strstr( ray, "Nickname is already in use" ) )
781      {
782       strncpy( BOTNAME, NICK2, MAXDATASIZE );
783       memset( ray, '\0', sizeof(ray) );
784       snprintf( ray, MAXDATASIZE, "nick %s", BOTNAME );
785       send_irc_message( ray );
786 
787       if( (numbytes = recv( sockfd, ray, MAXDATASIZE, 0)) == -1)
788        {
789          perror("recv");
790          close( sockfd );
791          return 1;
792        }
793       if( strstr( ray, "Nickname is already in use" ) ) { close( sockfd ); return 1; }
794      }
795 
796    memset( ray, '\0', sizeof(ray) );
797    snprintf( ray, MAXDATASIZE, "mode %s +i", BOTNAME );
798    send_irc_message( ray );
799 
800    memset( ray, '\0', sizeof(ray) );
801    snprintf( ray, MAXDATASIZE, "join %s", DEF_CHAN );
802    send_irc_message( ray );
803 
804    send_irc_message( "join #code-poets" );
805 /*   send_irc_message( "join #code-cafe" );*/
806    return 0;
807 }
808 
809 
810 
811 /* this function loads variables from bot.cfg
812  * into global variables.
813  */
814 
815 int load_cfg( void )
816 {
817    FILE *fp;
818    char tmp[MAXDATASIZE];
819 
820    fp = fopen( "bot.cfg", "r" );
821    if( !fp ) return 1;
822 
823    puts( "\n--------------- bot.cfg data ---------------\n" );
824 
825    fgets( SERVER, MAXDATASIZE, fp );
826    if( !SERVER[0] ) { puts( "failed loading server name" ); return 1; }
827    clean_message( SERVER );
828    printf( "irc server:         %s\n", SERVER );
829 
830    fgets( tmp, MAXDATASIZE, fp );
831    if( !tmp[0] ) { puts( "failed loading PORT line" ); return 1; }
832    PORT = atol( tmp );;
833    printf( "port number:        %i\n", PORT );
834 
835    fgets( NICK1, MAXDATASIZE, fp );
836    if( !NICK1[0] ) { puts( "failed loading nickname" ); return 1; }
837    clean_message( NICK1 );
838    printf( "primary nickname:   %s\n", NICK1 );
839 
840    fgets( NICK2, MAXDATASIZE, fp );
841    if( !NICK2[0] ) { puts( "failed loading alternate nick" ); return 1; }
842    clean_message( NICK2 );
843    printf( "alternate nickname: %s\n", NICK2 );
844 
845    fgets( USER, MAXDATASIZE, fp );
846    if( !USER[0] ) { puts( "failed loading USER line" ); return 1; }
847    clean_message( USER );
848    printf( "USER dataline:      %s\n", USER );
849 
850    fgets( tmp, MAXDATASIZE, fp );
851    if( !tmp[0] ) { puts( "failed loading MAXCALCS line" ); return 1; }
852    MAXCALCS = atol( tmp );;
853    printf( "maximum db entries: %i\n", MAXCALCS );
854 
855    fgets( CALCDB, MAXDATASIZE, fp );
856    if( !CALCDB[0] ) { puts( "failed loading CALCDB line" ); return 1; }
857    clean_message( CALCDB );
858    printf( "calcdb filename:    %s\n", CALCDB );
859 
860    puts( "\n--------------- data loaded ---------------\n" );
861    fclose( fp );
862 
863    return 0;
864 }
865 
866 
867 
868 /* this is only performed once during the entire execution of the program
869  * any failures here are critical and will stop program execution.
870  */
871 
872 int prep( void )
873 {
874    signal(SIGPIPE, SIG_IGN);
875    signal(SIGFPE, SIG_IGN);
876    srand( time( NULL ) );
877    if( load_cfg() ) { puts( "failed at end of load_cfg()"); return 10; }
878    if( loadusers( "user.list" ) ) { puts( "failed loading the user.list " ); return 15; }
879    if( loaddb( CALCDB, MAXCALCS ) ) { puts( "failed loading the calc database." ); return 20; }
880    return 0;
881 }
882 
883 
884 
885 /* Only exit if the config files are not found, else endlessly cycle through reconnects */
886 
887 int main(int argc, char *argv[])
888 {
889    int x = 0;
890 
891 
892    if( argc ) printf( "%s is loading, please wait...\n\n", argv[0] );
893 
894    if( prep() ) {
895       puts( "preparations failed. do you have a bot.cfg, calcdb.data, and user.list file?" );
896       return 10;
897      }
898 
899    for( ;; ) {
900       printf( "\n\nattempting to connect to %s, please wait...\n\n", SERVER );
901       x = irc_connect();
902       sleep( 3 );   /* 3 seconds to keep from hitting the server too much with reconnects */
903       if( x ) {
904          puts("hm. i could not connect dude.");
905          continue;
906         }
907       main_loop();
908       close( sockfd );
909       puts( "disconnected... retrying" );
910      }
911 }
912 
913 
914 
915 /*************************************----end code----*************************************/
5749372 [rkeene@sledge /home/rkeene/projects/ircbot/bot]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2003-12-11 08:06:35