]>
Commit | Line | Data |
---|---|---|
c86edd1d Q |
1 | /* irc.c: handle the IRC interface */ |
2 | ||
3 | #include "irc.h" | |
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" | |
87698d77 | 13 | #include "../lib/version.h" |
0850681b | 14 | #include "../lib/irc_string.h" |
9821c06c | 15 | #include "../lib/strlfunc.h" |
c86edd1d Q |
16 | #include <sys/poll.h> |
17 | #include <sys/types.h> | |
18 | #include <sys/socket.h> | |
19 | #include <netdb.h> | |
20 | #include <stdarg.h> | |
21 | #include <time.h> | |
22 | #include <string.h> | |
23 | #include <stdio.h> | |
24 | #include <unistd.h> | |
25 | #include <stdlib.h> | |
26 | #include <netinet/in.h> | |
27 | ||
70b0a4e5 | 28 | MODULE_VERSION(""); |
87698d77 | 29 | |
c86edd1d Q |
30 | #define READBUFSIZE 32768 |
31 | #define MAX_SERVERARGS 20 | |
32 | #define MIN_NUMERIC 100 | |
33 | #define MAX_NUMERIC 999 | |
34 | ||
35 | void irc_connect(void *arg); | |
0850681b | 36 | void ircstats(int hooknum, void *arg); |
971bb4b4 CP |
37 | void checkhubconfig(void); |
38 | void ircrehash(int hooknum, void *arg); | |
c86edd1d Q |
39 | |
40 | CommandTree *servercommands; | |
41 | Command *numericcommands[MAX_NUMERIC-MIN_NUMERIC]; | |
42 | int serverfd; | |
43 | char inbuf[READBUFSIZE]; | |
44 | char *nextline; | |
45 | char *args[MAX_SERVERARGS]; | |
46 | int bytesleft; | |
47 | int linesreceived; | |
48 | ||
49 | sstring *mynumeric; | |
50 | sstring *myserver; | |
51 | long mylongnum; | |
52 | ||
53 | time_t starttime; | |
54 | time_t timeoffset; | |
55 | int awaitingping; | |
56 | int connected; | |
57 | ||
971bb4b4 CP |
58 | static int hubnum, hubcount, previouslyconnected = 0; |
59 | static sstring **hublist; | |
60 | ||
c86edd1d Q |
61 | void _init() { |
62 | servercommands=newcommandtree(); | |
63 | starttime=time(NULL); | |
64 | ||
65 | connected=0; | |
66 | timeoffset=0; | |
67 | ||
68 | /* These values cannot be changed whilst the IRC module is running */ | |
69 | mynumeric=getcopyconfigitem("irc","servernumeric","A]",2); | |
70 | myserver=getcopyconfigitem("irc","servername","services.lame.net",HOSTLEN); | |
71 | ||
72 | mylongnum=numerictolong(mynumeric->content,2); | |
971bb4b4 CP |
73 | |
74 | checkhubconfig(); | |
75 | ||
c86edd1d Q |
76 | /* Schedule a connection to the IRC server */ |
77 | scheduleoneshot(time(NULL),&irc_connect,NULL); | |
78 | ||
79 | registerserverhandler("G",&handleping,1); | |
80 | registerserverhandler("SE",&handlesettime,1); | |
81 | registerserverhandler("Z",&handlepingreply,1); | |
82 | registerserverhandler("SERVER",&irc_handleserver,8); | |
0850681b P |
83 | |
84 | registerhook(HOOK_CORE_STATSREQUEST,&ircstats); | |
971bb4b4 | 85 | registerhook(HOOK_CORE_REHASH,&ircrehash); |
c86edd1d Q |
86 | } |
87 | ||
88 | void _fini() { | |
89 | if (connected) { | |
90 | irc_send("%s SQ %s 0 :Shutting down",mynumeric->content,myserver->content); | |
91 | irc_disconnected(); | |
92 | } | |
93 | ||
94 | deregisterserverhandler("G",&handleping); | |
95 | deregisterserverhandler("SE",&handlesettime); | |
96 | deregisterserverhandler("Z",&handlepingreply); | |
97 | deregisterserverhandler("SERVER",&irc_handleserver); | |
0850681b P |
98 | |
99 | deregisterhook(HOOK_CORE_STATSREQUEST,&ircstats); | |
971bb4b4 | 100 | deregisterhook(HOOK_CORE_REHASH,&ircrehash); |
c86edd1d Q |
101 | |
102 | deleteschedule(NULL,&sendping,NULL); | |
103 | deleteschedule(NULL,&irc_connect,NULL); | |
104 | ||
105 | freesstring(mynumeric); | |
106 | freesstring(myserver); | |
107 | ||
108 | destroycommandtree(servercommands); | |
109 | } | |
110 | ||
971bb4b4 CP |
111 | void resethubnum(void) { |
112 | hubnum=0; | |
113 | } | |
114 | ||
115 | void nexthub(void) { | |
116 | /* | |
117 | * this is set if the connection before this (failed one) | |
118 | * was successful, so we attempted reconnecting to the | |
119 | * same server, and now we've failed, so reset to the first | |
120 | * which should be the preferred one. | |
121 | */ | |
122 | ||
123 | if (previouslyconnected) { | |
124 | previouslyconnected=0; | |
125 | if((hubcount<2) || (hubnum!=0)) { | |
126 | hubnum=0; | |
127 | } else { | |
128 | hubnum=1; | |
129 | } | |
130 | return; | |
131 | } | |
132 | ||
133 | /* 1 % 0 is fun */ | |
134 | if (hubcount>0) | |
135 | hubnum=(hubnum+1)%hubcount; | |
136 | } | |
137 | ||
138 | int gethub(int servernum, char **conto, long *portnum, char **conpass, long *pingfreq) { | |
139 | sstring *conto_s, *portnum_s, *conpass_s, *pingfreq_s; | |
140 | static char conto_b[512], conpass_b[512]; | |
141 | int ret, s; | |
142 | char *section; | |
143 | ||
144 | if (hubcount) { | |
145 | static char realsection[512]; | |
146 | snprintf(realsection,sizeof(realsection),"hub-%s",hublist[servernum]->content); | |
147 | section=realsection; | |
148 | s=1; | |
149 | } else { | |
150 | section="irc"; | |
151 | s=0; | |
152 | } | |
153 | ||
154 | conto_s=getconfigitem(section,s?"host":"hubhost"); | |
155 | if (!conto_s || !conto_s->content || !conto_s->content[0]) { | |
156 | ret=0; | |
157 | } else { | |
158 | ret=1; | |
159 | } | |
160 | ||
161 | if (conto) { | |
162 | conto_s=getcopyconfigitem(section,s?"host":"hubhost","127.0.0.1",HOSTLEN); | |
163 | strlcpy(conto_b, conto_s->content, sizeof(conto_b)); | |
164 | *conto=conto_b; | |
165 | ||
166 | freesstring(conto_s); | |
167 | } | |
168 | ||
169 | if (portnum) { | |
170 | portnum_s=getcopyconfigitem(section,s?"port":"hubport","4400",7); | |
171 | *portnum=strtol(portnum_s->content,NULL,10); | |
172 | ||
173 | freesstring(portnum_s); | |
174 | } | |
175 | ||
176 | if (conpass) { | |
177 | conpass_s=getcopyconfigitem(section,s?"pass":"hubpass","erik",20); | |
178 | strlcpy(conpass_b, conpass_s->content, sizeof(conpass_b)); | |
179 | *conpass = conpass_b; | |
180 | ||
181 | freesstring(conpass_s); | |
182 | } | |
183 | ||
184 | if (pingfreq) { | |
185 | pingfreq_s=getcopyconfigitem(section,"pingfreq","90",10); | |
186 | *pingfreq=strtol(pingfreq_s->content,NULL,10); | |
187 | ||
188 | freesstring(pingfreq_s); | |
189 | } | |
190 | ||
191 | return ret; | |
192 | } | |
193 | ||
194 | /* we check at startup that (most things) resolve */ | |
195 | void checkhubconfig(void) { | |
196 | array *servers_ar; | |
197 | int i; | |
198 | ||
199 | resethubnum(); | |
200 | hubcount=0; | |
201 | ||
202 | servers_ar=getconfigitems("irc", "hub"); | |
203 | ||
204 | /* | |
205 | * we don't bother doing the gethostbyname check for | |
206 | * the old style of configuration, as it'll | |
207 | * be checked by irc_connect anyway. | |
208 | */ | |
209 | if (!servers_ar || !servers_ar->cursi) { | |
210 | Error("irc",ERR_WARNING,"Using legacy hub configuration format."); | |
211 | return; | |
212 | } | |
213 | ||
214 | hublist=(sstring **)servers_ar->content; | |
215 | hubcount=servers_ar->cursi; | |
216 | ||
217 | for (i=0;i<hubcount;i++) { | |
218 | char *conto; | |
219 | ||
220 | if (!gethub(i, &conto, NULL, NULL, NULL)) { | |
221 | Error("irc",ERR_FATAL,"No server configuration specified for '%s'.",hublist[i]->content); | |
222 | exit(1); | |
223 | } | |
224 | ||
225 | if (!gethostbyname(conto)) { | |
226 | Error("irc",ERR_FATAL,"Couldn't resolve host %s.",conto); | |
227 | exit(1); | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | void ircrehash(int hookhum, void *arg) { | |
233 | checkhubconfig(); | |
234 | } | |
235 | ||
c86edd1d Q |
236 | void irc_connect(void *arg) { |
237 | struct sockaddr_in sockaddress; | |
238 | struct hostent *host; | |
971bb4b4 CP |
239 | sstring *mydesc; |
240 | char *conto,*conpass; | |
241 | long portnum,pingfreq; | |
0555113a | 242 | /* socklen_t opt=1460;*/ |
c86edd1d Q |
243 | |
244 | nextline=inbuf; | |
245 | bytesleft=0; | |
246 | linesreceived=0; | |
247 | awaitingping=0; | |
c86edd1d | 248 | |
971bb4b4 | 249 | gethub(hubnum, &conto, &portnum, &conpass, &pingfreq); |
c86edd1d | 250 | |
971bb4b4 | 251 | mydesc=getcopyconfigitem("irc","serverdescription","newserv 0.01",100); |
c86edd1d Q |
252 | |
253 | serverfd = socket(PF_INET, SOCK_STREAM, 0); | |
254 | if (serverfd == -1) { | |
255 | Error("irc",ERR_FATAL,"Couldn't create socket."); | |
256 | exit(1); | |
257 | } | |
258 | ||
259 | sockaddress.sin_family = AF_INET; | |
260 | sockaddress.sin_port = htons(portnum); | |
971bb4b4 | 261 | host = gethostbyname(conto); |
c86edd1d | 262 | if (!host) { |
971bb4b4 | 263 | Error("irc",ERR_FATAL,"Couldn't resolve host %s.",conto); |
c86edd1d Q |
264 | exit(1); |
265 | } | |
8b8e6c68 CP |
266 | |
267 | #if !defined(h_addr) /* h_addr is deprecated */ | |
268 | memcpy(&sockaddress.sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); | |
269 | #else | |
c86edd1d | 270 | memcpy(&sockaddress.sin_addr, host->h_addr, sizeof(struct in_addr)); |
8b8e6c68 | 271 | #endif |
c86edd1d | 272 | |
af076c80 | 273 | /* if (setsockopt(serverfd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt))) { |
c86edd1d Q |
274 | Error("irc",ERR_WARNING,"Error setting socket buffer."); |
275 | } | |
d731c7d8 CP |
276 | */ |
277 | ||
16739dbe | 278 | Error("irc",ERR_INFO,"Connecting to %s:%lu",conto,portnum); |
971bb4b4 | 279 | |
c86edd1d | 280 | if (connect(serverfd, (struct sockaddr *) &sockaddress, sizeof(struct sockaddr_in)) == -1) { |
971bb4b4 | 281 | nexthub(); |
16739dbe | 282 | Error("irc",ERR_ERROR,"Couldn't connect to %s:%lu, will try next server in one minute",conto,portnum); |
c86edd1d Q |
283 | scheduleoneshot(time(NULL)+60,&irc_connect,NULL); |
284 | close(serverfd); | |
80ff7065 | 285 | freesstring(mydesc); |
c86edd1d Q |
286 | return; |
287 | } | |
288 | ||
971bb4b4 | 289 | irc_send("PASS :%s",conpass); |
4f2ceb49 | 290 | /* remember when changing modes to change server/server.c too */ |
843184e3 | 291 | irc_send("SERVER %s 1 %ld %ld J10 %s%s +sh6n :%s",myserver->content,starttime,time(NULL),mynumeric->content,longtonumeric(MAXLOCALUSER,3),mydesc->content); |
c86edd1d Q |
292 | |
293 | registerhandler(serverfd, POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL, &handledata); | |
294 | ||
295 | /* Schedule our ping requests. Note that this will also server | |
296 | * to time out a failed connection.. */ | |
297 | ||
971bb4b4 | 298 | schedulerecurring(time(NULL)+pingfreq,0,pingfreq,&sendping,NULL); |
c86edd1d | 299 | |
c86edd1d | 300 | freesstring(mydesc); |
c86edd1d Q |
301 | } |
302 | ||
303 | int irc_handleserver(void *source, int cargc, char **cargv) { | |
304 | if (!strncmp((char *)source,"INIT",4) && !connected) { | |
305 | /* OK, this is the SERVER response back from the remote server */ | |
306 | /* This completes the connection process. */ | |
307 | Error("irc",ERR_INFO,"Connection accepted by %s",cargv[0]); | |
308 | ||
309 | /* Fix our timestamps before we do anything else */ | |
310 | setnettime(strtol(cargv[3],NULL,10)); | |
311 | ||
971bb4b4 | 312 | previouslyconnected=1; |
c86edd1d | 313 | connected=1; |
c86edd1d Q |
314 | } else { |
315 | Error("irc",ERR_INFO,"Unexpected SERVER message"); | |
316 | } | |
317 | ||
318 | return CMD_OK; | |
319 | } | |
320 | ||
321 | void irc_disconnected() { | |
322 | if (serverfd>=0) { | |
323 | deregisterhandler(serverfd,1); | |
324 | } | |
325 | serverfd=-1; | |
326 | if (connected) { | |
327 | connected=0; | |
e609eb1f | 328 | triggerhook(HOOK_IRC_PRE_DISCON,NULL); |
c86edd1d | 329 | triggerhook(HOOK_IRC_DISCON,NULL); |
971bb4b4 CP |
330 | } else { |
331 | nexthub(); | |
c86edd1d | 332 | } |
e26df23c C |
333 | deleteschedule(NULL,&irc_connect,NULL); |
334 | deleteschedule(NULL,&sendping,NULL); | |
335 | scheduleoneshot(time(NULL)+2,&irc_connect,NULL); | |
c86edd1d Q |
336 | } |
337 | ||
338 | void irc_send(char *format, ... ) { | |
339 | char buf[512]; | |
340 | va_list val; | |
341 | int len; | |
342 | ||
343 | va_start(val,format); | |
344 | len=vsnprintf(buf,509,format,val); | |
345 | va_end(val); | |
346 | ||
347 | if (len>509 || len<0) { | |
348 | len=509; | |
349 | } | |
350 | ||
351 | buf[len++]='\r'; | |
352 | buf[len++]='\n'; | |
353 | ||
354 | write(serverfd,buf,len); | |
355 | } | |
356 | ||
357 | void handledata(int fd, short events) { | |
358 | int res; | |
359 | int again=1; | |
360 | ||
361 | if (events & (POLLPRI | POLLERR | POLLHUP | POLLNVAL)) { | |
362 | /* Oh shit, we got dropped */ | |
363 | Error("irc",ERR_ERROR,"Got socket error, dropping connection."); | |
364 | irc_disconnected(); | |
365 | return; | |
366 | } | |
367 | ||
368 | while(again) { | |
369 | again=0; | |
370 | ||
371 | res=read(serverfd, inbuf+bytesleft, READBUFSIZE-bytesleft); | |
372 | if (res<=0) { | |
373 | Error("irc",ERR_ERROR,"Disconnected by remote server."); | |
374 | irc_disconnected(); | |
375 | return; | |
376 | } | |
377 | ||
378 | again=((bytesleft+=res)==READBUFSIZE); | |
379 | while (!parseline()) | |
380 | ; /* empty loop */ | |
381 | ||
382 | memmove(inbuf, nextline, bytesleft); | |
383 | nextline=inbuf; | |
384 | } | |
385 | } | |
386 | ||
387 | /* | |
388 | * Parse and dispatch a line from the IRC server | |
389 | * | |
390 | * Returns: | |
391 | * 0: found at least one complete line in buffer | |
392 | * 1: no complete line found in buffer | |
393 | * | |
394 | * Note that this returns 0 even if it finds a line which wasn't handled. | |
395 | * The return code is just used to determine when there are no more lines | |
396 | * to parse. | |
397 | */ | |
398 | ||
399 | int parseline() { | |
400 | char *currentline=nextline; | |
401 | int foundcmd=0; | |
402 | int cargc; | |
403 | char *cargv[MAX_SERVERARGS]; | |
404 | Command *c; | |
405 | ||
406 | while (bytesleft-->0) { | |
407 | if (*nextline=='\r' || *nextline=='\n' || *nextline=='\0') { | |
408 | /* Found a newline character. Replace it with \0 */ | |
409 | /* If we found some non-newline characters first, we need to parse this */ | |
410 | /* Otherwise, don't bother and just skip ahead */ | |
411 | if (currentline==nextline) { | |
412 | *nextline++='\0'; | |
413 | currentline=nextline; | |
414 | } else { | |
415 | *nextline++='\0'; | |
416 | foundcmd=1; | |
417 | break; | |
418 | } | |
419 | } else { | |
420 | nextline++; | |
421 | } | |
422 | } | |
423 | ||
424 | if (foundcmd==0) { | |
425 | bytesleft=(nextline-currentline); | |
426 | nextline=currentline; | |
427 | return 1; | |
428 | } | |
429 | ||
430 | /* OK, currentline points at a valid NULL-terminated line */ | |
431 | /* and nextline points at where we are going next */ | |
432 | ||
433 | linesreceived++; | |
434 | ||
435 | /* Split it up */ | |
436 | cargc=splitline(currentline,cargv,MAX_SERVERARGS,1); | |
437 | ||
438 | if (cargc<2) { | |
439 | /* Less than two arguments? Not a valid command, sir */ | |
440 | return 0; | |
441 | } | |
442 | ||
443 | if (linesreceived<3) { | |
444 | /* Special-case the first two lines | |
445 | * These CANNOT be numeric responses, | |
446 | * and the command is the FIRST thing on the line */ | |
447 | if ((c=findcommandintree(servercommands,cargv[0],1))==NULL) { | |
448 | /* No handler, return. */ | |
449 | return 0; | |
450 | } | |
451 | for (;c!=NULL;c=c->next) { | |
09285ac1 | 452 | c->calls++; |
c86edd1d Q |
453 | if (((c->handler)("INIT",cargc-1,&cargv[1]))==CMD_LAST) |
454 | return 0; | |
455 | } | |
456 | } else { | |
457 | if (cargv[1][0]>='0' && cargv[1][0]<='9') { | |
458 | /* It's a numeric! */ | |
c3db6f7e | 459 | long numeric = strtol(cargv[1], NULL, 0); |
16d29ce2 CP |
460 | if((numeric >= MIN_NUMERIC) && (numeric <= MAX_NUMERIC)) { |
461 | for(c=numericcommands[numeric];c;c=c->next) { | |
09285ac1 | 462 | c->calls++; |
16d29ce2 CP |
463 | if (((c->handler)((void *)numeric,cargc,cargv))==CMD_LAST) |
464 | return 0; | |
465 | } | |
466 | } | |
c86edd1d Q |
467 | } else { |
468 | if ((c=findcommandintree(servercommands,cargv[1],1))==NULL) { | |
469 | /* We don't have a handler for this command */ | |
470 | return 0; | |
471 | } | |
472 | for (;c!=NULL;c=c->next) { | |
09285ac1 | 473 | c->calls++; |
c86edd1d Q |
474 | if (((c->handler)(cargv[0],cargc-2,cargv+2))==CMD_LAST) |
475 | return 0; | |
476 | } | |
477 | } | |
478 | } | |
479 | return 0; | |
480 | } | |
481 | ||
482 | int registerserverhandler(const char *command, CommandHandler handler, int maxparams) { | |
483 | if ((addcommandtotree(servercommands,command,0,maxparams,handler))==NULL) | |
484 | return 1; | |
485 | ||
486 | return 0; | |
487 | } | |
488 | ||
489 | int deregisterserverhandler(const char *command, CommandHandler handler) { | |
490 | return deletecommandfromtree(servercommands, command, handler); | |
491 | } | |
492 | ||
16d29ce2 CP |
493 | int registernumerichandler(const int numeric, CommandHandler handler, int maxparams) { |
494 | Command *cp, *np; | |
495 | if((numeric < MIN_NUMERIC) || (numeric > MAX_NUMERIC)) | |
496 | return 1; | |
497 | ||
498 | /* doesn't happen that often */ | |
499 | np = (Command *)malloc(sizeof(Command)); | |
500 | np->handler = handler; | |
501 | np->next = NULL; | |
502 | ||
503 | /* I know I could just add to the beginning, but I guess since we have that LAST stuff | |
504 | * it should go at the end */ | |
505 | if(!(cp = numericcommands[numeric])) { | |
506 | numericcommands[numeric] = np; | |
507 | } else { | |
508 | for(;cp->next;cp=cp->next); | |
509 | /* empty loop */ | |
510 | ||
511 | cp->next = np; | |
512 | } | |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
517 | int deregisternumerichandler(const int numeric, CommandHandler handler) { | |
518 | Command *cp, *lp; | |
519 | if((numeric < MIN_NUMERIC) || (numeric > MAX_NUMERIC)) | |
520 | return 1; | |
521 | ||
522 | for(cp=numericcommands[numeric],lp=NULL;cp;lp=cp,cp=cp->next) { | |
523 | if(cp->handler == handler) { | |
524 | if(lp) { | |
525 | lp->next = cp->next; | |
526 | } else { | |
527 | numericcommands[numeric] = cp->next; | |
528 | } | |
529 | free(cp); | |
530 | return 0; | |
531 | } | |
532 | } | |
533 | ||
534 | return 1; | |
535 | } | |
536 | ||
c86edd1d Q |
537 | char *getmynumeric() { |
538 | return mynumeric->content; | |
539 | } | |
540 | ||
541 | time_t getnettime() { | |
542 | return (time(NULL)+timeoffset); | |
543 | } | |
544 | ||
545 | void setnettime(time_t newtime) { | |
546 | timeoffset=newtime-time(NULL); | |
70e589ed | 547 | Error("irc",ERR_INFO,"setnettime: Time offset is now %ld",timeoffset); |
c86edd1d Q |
548 | } |
549 | ||
550 | int handleping(void *sender, int cargc, char **cargv) { | |
551 | if (cargc>0) { | |
552 | irc_send("%s Z %s",mynumeric->content,cargv[cargc-1]); | |
553 | } | |
554 | return CMD_OK; | |
555 | } | |
556 | ||
557 | int handlesettime(void *sender, int cargc, char **cargv) { | |
558 | time_t newtime; | |
559 | ||
560 | if (cargc>0) { | |
561 | newtime=strtol(cargv[0],NULL,10); | |
562 | Error("irc",ERR_INFO,"Received settime: %lu (current time is %lu, offset %ld)",newtime,getnettime(),newtime-getnettime()); | |
563 | setnettime(newtime); | |
564 | } | |
565 | return CMD_OK; | |
566 | } | |
567 | ||
568 | void sendping(void *arg) { | |
569 | if (connected) { | |
570 | if (awaitingping==1) { | |
571 | /* We didn't get a ping reply, kill the connection */ | |
eb167f60 CP |
572 | Error("irc",ERR_INFO,"Connection closed due to ping timeout."); |
573 | ||
c86edd1d Q |
574 | irc_send("%s SQ %s 0 :Ping timeout",mynumeric->content,myserver->content); |
575 | irc_disconnected(); | |
576 | } else { | |
577 | awaitingping=1; | |
578 | irc_send("%s G :%s",mynumeric->content,myserver->content); | |
579 | } | |
580 | } else { | |
581 | /* We tried to send a ping when we weren't connected.. */ | |
582 | Error("irc",ERR_INFO,"Connection timed out."); | |
971bb4b4 CP |
583 | |
584 | /* have to have this here because of the EVIL HACK below */ | |
585 | nexthub(); | |
586 | ||
c86edd1d Q |
587 | connected=1; /* EVIL HACK */ |
588 | irc_disconnected(); | |
589 | } | |
590 | } | |
591 | ||
592 | int handlepingreply(void *sender, int cargc, char **cargv) { | |
593 | awaitingping=0; | |
594 | return CMD_OK; | |
595 | } | |
0850681b P |
596 | |
597 | ||
598 | void ircstats(int hooknum, void *arg) { | |
599 | long level=(long)arg; | |
600 | char buf[100]; | |
601 | ||
602 | if (level>5) { | |
603 | sprintf(buf,"irc : start time %lu (running %s)", starttime,longtoduration(time(NULL)-starttime,0)); | |
604 | triggerhook(HOOK_CORE_STATSREPLY,buf); | |
605 | sprintf(buf,"Time : %lu (current time is %lu, offset %ld)",getnettime(),time(NULL),timeoffset); | |
606 | triggerhook(HOOK_CORE_STATSREPLY,buf); | |
607 | } | |
608 | } | |
7bec4aeb | 609 | |
610 | ||
611 | /* list stats commands / m to a user | |
612 | * | |
7bec4aeb | 613 | * sourcenum numeric of the user requesting the listing |
614 | */ | |
a7697869 | 615 | void stats_commands(char *sourcenum) { |
09285ac1 | 616 | Command *cmds[500]; |
617 | unsigned int c,i; | |
618 | ||
619 | c=getcommandlist(servercommands,cmds,500); | |
620 | ||
621 | for (i=0;i<c;i++) { | |
7bec4aeb | 622 | /* |
623 | * 212 RPL_STATSCOMMANDS "source 212 target command used_count bytes_count" | |
624 | * "irc.netsplit.net 212 foobar ACCOUNT 41 462" | |
625 | */ | |
a7697869 | 626 | irc_send("%s 212 %s %s %u 0", getmynumeric(), sourcenum, cmds[i]->command->content, cmds[i]->calls); |
09285ac1 | 627 | } |
628 | } |