]>
jfr.im git - irc/quakenet/newserv.git/blob - irc/irc.c
72c8c8b621ad835177b9666eb317df3e6a3dce05
1 /* irc.c: handle the IRC interface */
4 #include "irc_config.h"
5 #include "../parser/parser.h"
6 #include "../core/events.h"
7 #include "../core/schedule.h"
8 #include "../core/error.h"
9 #include "../core/config.h"
10 #include "../core/hooks.h"
11 #include "../lib/base64.h"
12 #include "../lib/splitline.h"
13 #include "../lib/version.h"
14 #include "../lib/irc_string.h"
16 #include <sys/types.h>
17 #include <sys/socket.h>
25 #include <netinet/in.h>
29 #define READBUFSIZE 32768
30 #define MAX_SERVERARGS 20
31 #define MIN_NUMERIC 100
32 #define MAX_NUMERIC 999
34 void irc_connect(void *arg
);
35 void ircstats(int hooknum
, void *arg
);
37 CommandTree
*servercommands
;
38 Command
*numericcommands
[MAX_NUMERIC
-MIN_NUMERIC
];
40 char inbuf
[READBUFSIZE
];
42 char *args
[MAX_SERVERARGS
];
56 servercommands
=newcommandtree();
62 /* These values cannot be changed whilst the IRC module is running */
63 mynumeric
=getcopyconfigitem("irc","servernumeric","A]",2);
64 myserver
=getcopyconfigitem("irc","servername","services.lame.net",HOSTLEN
);
66 mylongnum
=numerictolong(mynumeric
->content
,2);
68 /* Schedule a connection to the IRC server */
69 scheduleoneshot(time(NULL
),&irc_connect
,NULL
);
71 registerserverhandler("G",&handleping
,1);
72 registerserverhandler("SE",&handlesettime
,1);
73 registerserverhandler("Z",&handlepingreply
,1);
74 registerserverhandler("SERVER",&irc_handleserver
,8);
76 registerhook(HOOK_CORE_STATSREQUEST
,&ircstats
);
81 irc_send("%s SQ %s 0 :Shutting down",mynumeric
->content
,myserver
->content
);
85 deregisterserverhandler("G",&handleping
);
86 deregisterserverhandler("SE",&handlesettime
);
87 deregisterserverhandler("Z",&handlepingreply
);
88 deregisterserverhandler("SERVER",&irc_handleserver
);
90 deregisterhook(HOOK_CORE_STATSREQUEST
,&ircstats
);
92 deleteschedule(NULL
,&sendping
,NULL
);
93 deleteschedule(NULL
,&irc_connect
,NULL
);
95 freesstring(mynumeric
);
96 freesstring(myserver
);
98 destroycommandtree(servercommands
);
101 void irc_connect(void *arg
) {
102 struct sockaddr_in sockaddress
;
103 struct hostent
*host
;
104 sstring
*conto
,*conport
,*conpass
;
105 sstring
*mydesc
, *pingfreq
;
114 conto
=getcopyconfigitem("irc","hubhost","127.0.0.1",HOSTLEN
);
115 conport
=getcopyconfigitem("irc","hubport","4400",7);
116 conpass
=getcopyconfigitem("irc","hubpass","erik",20);
118 mydesc
=getcopyconfigitem("irc","serverdescription","newserv 0.01",100);
120 pingfreq
=getcopyconfigitem("irc","pingfreq","90",10);
122 portnum
=strtol(conport
->content
,NULL
,10);
124 serverfd
= socket(PF_INET
, SOCK_STREAM
, 0);
125 if (serverfd
== -1) {
126 Error("irc",ERR_FATAL
,"Couldn't create socket.");
130 sockaddress
.sin_family
= AF_INET
;
131 sockaddress
.sin_port
= htons(portnum
);
132 host
= gethostbyname(conto
->content
);
134 Error("irc",ERR_FATAL
,"Couldn't resolve host %s.",conto
->content
);
137 memcpy(&sockaddress
.sin_addr
, host
->h_addr
, sizeof(struct in_addr
));
139 if (setsockopt(serverfd
, SOL_SOCKET
, SO_RCVBUF
, &opt
, sizeof(opt
))) {
140 Error("irc",ERR_WARNING
,"Error setting socket buffer.");
143 if (connect(serverfd
, (struct sockaddr
*) &sockaddress
, sizeof(struct sockaddr_in
)) == -1) {
144 Error("irc",ERR_ERROR
,"Couldn't connect to %s:%s, will retry in one minute",conto
->content
,conport
->content
);
145 scheduleoneshot(time(NULL
)+60,&irc_connect
,NULL
);
150 Error("irc",ERR_INFO
,"Connecting to %s:%s",conto
->content
,conport
->content
);
152 irc_send("PASS :%s",conpass
->content
);
153 /* remember when changing modes to change server/server.c too */
154 irc_send("SERVER %s 1 %ld %ld J10 %s%s +sh6n :%s",myserver
->content
,starttime
,time(NULL
),mynumeric
->content
,longtonumeric(MAXLOCALUSER
,3),mydesc
->content
);
156 registerhandler(serverfd
, POLLIN
|POLLPRI
|POLLERR
|POLLHUP
|POLLNVAL
, &handledata
);
158 /* Schedule our ping requests. Note that this will also server
159 * to time out a failed connection.. */
161 schedulerecurring(time(NULL
)+strtol(pingfreq
->content
,NULL
,10),0,
162 strtol(pingfreq
->content
,NULL
,10),&sendping
,NULL
);
165 freesstring(conport
);
166 freesstring(conpass
);
168 freesstring(pingfreq
);
171 int irc_handleserver(void *source
, int cargc
, char **cargv
) {
172 if (!strncmp((char *)source
,"INIT",4) && !connected
) {
173 /* OK, this is the SERVER response back from the remote server */
174 /* This completes the connection process. */
175 Error("irc",ERR_INFO
,"Connection accepted by %s",cargv
[0]);
177 /* Fix our timestamps before we do anything else */
178 setnettime(strtol(cargv
[3],NULL
,10));
182 Error("irc",ERR_INFO
,"Unexpected SERVER message");
188 void irc_disconnected() {
190 deregisterhandler(serverfd
,1);
195 triggerhook(HOOK_IRC_PRE_DISCON
,NULL
);
196 triggerhook(HOOK_IRC_DISCON
,NULL
);
198 deleteschedule(NULL
,&irc_connect
,NULL
);
199 deleteschedule(NULL
,&sendping
,NULL
);
200 scheduleoneshot(time(NULL
)+2,&irc_connect
,NULL
);
203 void irc_send(char *format
, ... ) {
208 va_start(val
,format
);
209 len
=vsnprintf(buf
,509,format
,val
);
212 if (len
>509 || len
<0) {
219 write(serverfd
,buf
,len
);
222 void handledata(int fd
, short events
) {
226 if (events
& (POLLPRI
| POLLERR
| POLLHUP
| POLLNVAL
)) {
227 /* Oh shit, we got dropped */
228 Error("irc",ERR_ERROR
,"Got socket error, dropping connection.");
236 res
=read(serverfd
, inbuf
+bytesleft
, READBUFSIZE
-bytesleft
);
238 Error("irc",ERR_ERROR
,"Disconnected by remote server.");
243 again
=((bytesleft
+=res
)==READBUFSIZE
);
247 memmove(inbuf
, nextline
, bytesleft
);
253 * Parse and dispatch a line from the IRC server
256 * 0: found at least one complete line in buffer
257 * 1: no complete line found in buffer
259 * Note that this returns 0 even if it finds a line which wasn't handled.
260 * The return code is just used to determine when there are no more lines
265 char *currentline
=nextline
;
268 char *cargv
[MAX_SERVERARGS
];
271 while (bytesleft
-->0) {
272 if (*nextline
=='\r' || *nextline
=='\n' || *nextline
=='\0') {
273 /* Found a newline character. Replace it with \0 */
274 /* If we found some non-newline characters first, we need to parse this */
275 /* Otherwise, don't bother and just skip ahead */
276 if (currentline
==nextline
) {
278 currentline
=nextline
;
290 bytesleft
=(nextline
-currentline
);
291 nextline
=currentline
;
295 /* OK, currentline points at a valid NULL-terminated line */
296 /* and nextline points at where we are going next */
301 cargc
=splitline(currentline
,cargv
,MAX_SERVERARGS
,1);
304 /* Less than two arguments? Not a valid command, sir */
308 if (linesreceived
<3) {
309 /* Special-case the first two lines
310 * These CANNOT be numeric responses,
311 * and the command is the FIRST thing on the line */
312 if ((c
=findcommandintree(servercommands
,cargv
[0],1))==NULL
) {
313 /* No handler, return. */
316 for (;c
!=NULL
;c
=c
->next
) {
317 if (((c
->handler
)("INIT",cargc
-1,&cargv
[1]))==CMD_LAST
)
321 if (cargv
[1][0]>='0' && cargv
[1][0]<='9') {
322 /* It's a numeric! */
323 long numeric
= strtol(cargv
[1], NULL
, 0);
324 if((numeric
>= MIN_NUMERIC
) && (numeric
<= MAX_NUMERIC
)) {
325 for(c
=numericcommands
[numeric
];c
;c
=c
->next
) {
326 if (((c
->handler
)((void *)numeric
,cargc
,cargv
))==CMD_LAST
)
331 if ((c
=findcommandintree(servercommands
,cargv
[1],1))==NULL
) {
332 /* We don't have a handler for this command */
335 for (;c
!=NULL
;c
=c
->next
) {
336 if (((c
->handler
)(cargv
[0],cargc
-2,cargv
+2))==CMD_LAST
)
344 int registerserverhandler(const char *command
, CommandHandler handler
, int maxparams
) {
345 if ((addcommandtotree(servercommands
,command
,0,maxparams
,handler
))==NULL
)
351 int deregisterserverhandler(const char *command
, CommandHandler handler
) {
352 return deletecommandfromtree(servercommands
, command
, handler
);
355 int registernumerichandler(const int numeric
, CommandHandler handler
, int maxparams
) {
357 if((numeric
< MIN_NUMERIC
) || (numeric
> MAX_NUMERIC
))
360 /* doesn't happen that often */
361 np
= (Command
*)malloc(sizeof(Command
));
362 np
->handler
= handler
;
365 /* I know I could just add to the beginning, but I guess since we have that LAST stuff
366 * it should go at the end */
367 if(!(cp
= numericcommands
[numeric
])) {
368 numericcommands
[numeric
] = np
;
370 for(;cp
->next
;cp
=cp
->next
);
379 int deregisternumerichandler(const int numeric
, CommandHandler handler
) {
381 if((numeric
< MIN_NUMERIC
) || (numeric
> MAX_NUMERIC
))
384 for(cp
=numericcommands
[numeric
],lp
=NULL
;cp
;lp
=cp
,cp
=cp
->next
) {
385 if(cp
->handler
== handler
) {
389 numericcommands
[numeric
] = cp
->next
;
399 char *getmynumeric() {
400 return mynumeric
->content
;
403 time_t getnettime() {
404 return (time(NULL
)+timeoffset
);
407 void setnettime(time_t newtime
) {
408 timeoffset
=newtime
-time(NULL
);
409 Error("irc",ERR_INFO
,"setnettime: Time offset is now %d",timeoffset
);
412 int handleping(void *sender
, int cargc
, char **cargv
) {
414 irc_send("%s Z %s",mynumeric
->content
,cargv
[cargc
-1]);
419 int handlesettime(void *sender
, int cargc
, char **cargv
) {
423 newtime
=strtol(cargv
[0],NULL
,10);
424 Error("irc",ERR_INFO
,"Received settime: %lu (current time is %lu, offset %ld)",newtime
,getnettime(),newtime
-getnettime());
430 void sendping(void *arg
) {
432 if (awaitingping
==1) {
433 /* We didn't get a ping reply, kill the connection */
434 irc_send("%s SQ %s 0 :Ping timeout",mynumeric
->content
,myserver
->content
);
438 irc_send("%s G :%s",mynumeric
->content
,myserver
->content
);
441 /* We tried to send a ping when we weren't connected.. */
442 Error("irc",ERR_INFO
,"Connection timed out.");
443 connected
=1; /* EVIL HACK */
448 int handlepingreply(void *sender
, int cargc
, char **cargv
) {
454 void ircstats(int hooknum
, void *arg
) {
455 long level
=(long)arg
;
459 sprintf(buf
,"irc : start time %lu (running %s)", starttime
,longtoduration(time(NULL
)-starttime
,0));
460 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
461 sprintf(buf
,"Time : %lu (current time is %lu, offset %ld)",getnettime(),time(NULL
),timeoffset
);
462 triggerhook(HOOK_CORE_STATSREPLY
,buf
);