1 /*
2 * Copyright (C) 2001 Marco Ziech (mmz@gmx.net)
3 * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "common/setup_before.h"
21 #ifdef STDC_HEADERS
22 # include <stdlib.h>
23 #else
24 # ifdef HAVE_MALLOC_H
25 # include <malloc.h>
26 # endif
27 #endif
28 #ifdef HAVE_STRING_H
29 # include <string.h>
30 #else
31 # ifdef HAVE_STRINGS_H
32 # include <strings.h>
33 # endif
34 # ifdef HAVE_MEMORY_H
35 # include <memory.h>
36 # endif
37 #endif
38 #include "compat/strdup.h"
39 #include <errno.h>
40 #include "compat/strerror.h"
41 #ifdef TIME_WITH_SYS_TIME
42 # include <sys/time.h>
43 # include <time.h>
44 #else
45 # ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 # else
48 # include <time.h>
49 # endif
50 #endif
51 #include "common/irc_protocol.h"
52 #include "common/packet.h"
53 #include "common/eventlog.h"
54 #include "connection.h"
55 #include "common/bn_type.h"
56 #include "common/field_sizes.h"
57 #include "common/addr.h"
58 #include "common/version.h"
59 #include "common/queue.h"
60 #include "common/list.h"
61 #include "common/bnethash.h"
62 #include "common/bnethashconv.h"
63 #include "common/tag.h"
64 #include "message.h"
65 #include "account.h"
66 #include "account_wrap.h"
67 #include "channel.h"
68 #include "irc.h"
69 #include "prefs.h"
70 #include "server.h"
71 #include "tick.h"
72 #include "message.h"
73 #include "command_groups.h"
74 #include "common/util.h"
75 #include "common/xalloc.h"
76 #include "common/setup_after.h"
77
78 typedef struct {
79 char const * nick;
80 char const * user;
81 char const * host;
82 } t_irc_message_from;
83
84
85 static char ** irc_split_elems(char * list, int separator, int ignoreblank);
86 static int irc_unget_elems(char ** elems);
87 static char * irc_message_preformat(t_irc_message_from const * from, char const * command, char const * dest, char const * text);
88
irc_send_cmd(t_connection * conn,char const * command,char const * params)89 extern int irc_send_cmd(t_connection * conn, char const * command, char const * params)
90 {
91 t_packet * p;
92 char data[MAX_IRC_MESSAGE_LEN+1];
93 int len;
94 char const * ircname = server_get_hostname();
95 char const * nick;
96
97 if (!conn) {
98 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
99 return -1;
100 }
101 if (!command) {
102 eventlog(eventlog_level_error,__FUNCTION__,"got NULL command");
103 return -1;
104 }
105 if (!(p = packet_create(packet_class_raw))) {
106 eventlog(eventlog_level_error,__FUNCTION__,"could not create packet");
107 return -1;
108 }
109
110 nick = conn_get_loggeduser(conn);
111 if (!nick)
112 nick = "";
113
114 /* snprintf isn't portable -> check message length first */
115 if (params) {
116 len = 1+strlen(ircname)+1+strlen(command)+1+strlen(nick)+1+strlen(params)+2;
117 if (len > MAX_IRC_MESSAGE_LEN) {
118 eventlog(eventlog_level_error,__FUNCTION__,"message to send is too large (%d bytes)",len);
119 return -1;
120 }
121 else
122 sprintf(data,":%s %s %s %s\r\n",ircname,command,nick,params);
123 } else {
124 len = 1+strlen(ircname)+1+strlen(command)+1+strlen(nick)+1+2;
125 if (len > MAX_IRC_MESSAGE_LEN) {
126 eventlog(eventlog_level_error,__FUNCTION__,"message to send is too large (%d bytes)",len);
127 return -1;
128 }
129 else
130 sprintf(data,":%s %s %s\r\n",ircname,command,nick);
131 }
132 packet_set_size(p,0);
133 packet_append_data(p,data,len);
134 // eventlog(eventlog_level_debug,__FUNCTION__,"[%d] sent \"%s\"",conn_get_socket(conn),data);
135 conn_push_outqueue(conn,p);
136 packet_del_ref(p);
137 return 0;
138 }
139
irc_send(t_connection * conn,int code,char const * params)140 extern int irc_send(t_connection * conn, int code, char const * params)
141 {
142 char temp[4]; /* '000\0' */
143
144 if (!conn) {
145 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
146 return -1;
147 }
148 if ((code>999)||(code<0)) { /* more than 3 digits or negative */
149 eventlog(eventlog_level_error,__FUNCTION__,"invalid message code (%d)",code);
150 return -1;
151 }
152 sprintf(temp,"%03u",code);
153 return irc_send_cmd(conn,temp,params);
154 }
155
irc_send_cmd2(t_connection * conn,char const * prefix,char const * command,char const * postfix,char const * comment)156 extern int irc_send_cmd2(t_connection * conn, char const * prefix, char const * command, char const * postfix, char const * comment)
157 {
158 t_packet * p;
159 char data[MAX_IRC_MESSAGE_LEN+1];
160 int len;
161
162 if (!conn) {
163 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
164 return -1;
165 }
166 if (!prefix)
167 {
168 eventlog(eventlog_level_error,__FUNCTION__,"got NULL prefix");
169 return -1;
170 }
171 if (!command) {
172 eventlog(eventlog_level_error,__FUNCTION__,"got NULL command");
173 return -1;
174 }
175 if (!postfix)
176 {
177 eventlog(eventlog_level_error,__FUNCTION__,"got NULL postfix");
178 return -1;
179 }
180
181 if (!(p = packet_create(packet_class_raw))) {
182 eventlog(eventlog_level_error,__FUNCTION__,"could not create packet");
183 return -1;
184 }
185
186 if (comment) {
187 len = 1+strlen(prefix)+1+strlen(command)+1+strlen(postfix)+2+strlen(comment)+1+2;
188 if (len > MAX_IRC_MESSAGE_LEN) {
189 eventlog(eventlog_level_error,__FUNCTION__,"message to send is too large (%d bytes)",len);
190 return -1;
191 }
192 else
193 sprintf(data,":%s %s %s :%s\r\n",prefix,command,postfix,comment);
194 } else {
195 len = 1+strlen(prefix)+1+strlen(command)+1+strlen(postfix)+1+2;
196 if (len > MAX_IRC_MESSAGE_LEN) {
197 eventlog(eventlog_level_error,__FUNCTION__,"message to send is too large (%d bytes)",len);
198 return -1;
199 }
200 else
201 sprintf(data,":%s %s %s\r\n",prefix,command,postfix);
202 }
203 packet_set_size(p,0);
204 packet_append_data(p,data,len);
205 // eventlog(eventlog_level_debug,__FUNCTION__,"[%d] sent \"%s\"",conn_get_socket(conn),data);
206 conn_push_outqueue(conn,p);
207 packet_del_ref(p);
208 return 0;
209 }
210
irc_send_ping(t_connection * conn)211 extern int irc_send_ping(t_connection * conn)
212 {
213 t_packet * p;
214 char data[MAX_IRC_MESSAGE_LEN];
215
216 if (!conn) {
217 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
218 return -1;
219 }
220
221 if((conn_get_wol(conn) == 1))
222 return 0;
223
224 if (!(p = packet_create(packet_class_raw))) {
225 eventlog(eventlog_level_error,__FUNCTION__,"could not create packet");
226 return -1;
227 }
228
229 conn_set_ircping(conn,get_ticks());
230 if (conn_get_state(conn)==conn_state_bot_username)
231 sprintf(data,"PING :%u\r\n",conn_get_ircping(conn)); /* Undernet doesn't reveal the servername yet ... neither do we */
232 else if ((6+strlen(server_get_hostname())+2+1)<=MAX_IRC_MESSAGE_LEN)
233 sprintf(data,"PING :%s\r\n",server_get_hostname());
234 else
235 eventlog(eventlog_level_error,__FUNCTION__,"maximum message length exceeded");
236 eventlog(eventlog_level_debug,__FUNCTION__,"[%d] sent \"%s\"",conn_get_socket(conn),data);
237 packet_set_size(p,0);
238 packet_append_data(p,data,strlen(data));
239 conn_push_outqueue(conn,p);
240 packet_del_ref(p);
241 return 0;
242 }
243
irc_send_pong(t_connection * conn,char const * params)244 extern int irc_send_pong(t_connection * conn, char const * params)
245 {
246 t_packet * p;
247 char data[MAX_IRC_MESSAGE_LEN];
248
249 if (!conn) {
250 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
251 return -1;
252 }
253 if ((1+strlen(server_get_hostname())+1+4+1+strlen(server_get_hostname())+((params)?(2+strlen(params)):(0))+2+1) > MAX_IRC_MESSAGE_LEN) {
254 eventlog(eventlog_level_error,__FUNCTION__,"max message length exceeded");
255 return -1;
256 }
257 if (!(p = packet_create(packet_class_raw))) {
258 eventlog(eventlog_level_error,__FUNCTION__,"could not create packet");
259 return -1;
260 }
261
262 if (params)
263 sprintf(data,":%s PONG %s :%s\r\n",server_get_hostname(),server_get_hostname(),params);
264 else
265 sprintf(data,":%s PONG %s\r\n",server_get_hostname(),server_get_hostname());
266 eventlog(eventlog_level_debug,__FUNCTION__,"[%d] sent \"%s\"",conn_get_socket(conn),data);
267 packet_set_size(p,0);
268 packet_append_data(p,data,strlen(data));
269 conn_push_outqueue(conn,p);
270 packet_del_ref(p);
271 return 0;
272 }
273
irc_authenticate(t_connection * conn,char const * passhash)274 extern int irc_authenticate(t_connection * conn, char const * passhash)
275 {
276 t_hash h1;
277 t_hash h2;
278 t_account * a;
279 char const * temphash;
280 char const * username;
281 char temp[MAX_IRC_MESSAGE_LEN];
282
283 char const * tempapgar;
284
285 if (!conn) {
286 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
287 return 0;
288 }
289 if (!passhash) {
290 eventlog(eventlog_level_error,__FUNCTION__,"got NULL passhash");
291 return 0;
292 }
293 username = conn_get_loggeduser(conn);
294 if (!username) {
295 /* redundant sanity check */
296 eventlog(eventlog_level_error,__FUNCTION__,"got NULL conn->protocol.loggeduser");
297 return 0;
298 }
299 a = accountlist_find_account(username);
300 if (!a) {
301 irc_send_cmd(conn,"NOTICE",":Authentication failed."); /* user does not exist */
302 return 0;
303 }
304
305 if (connlist_find_connection_by_account(a) && prefs_get_kick_old_login()==0) {
306 snprintf(temp, sizeof(temp), "%s :Account is already in use!",conn_get_loggeduser(conn));
307 irc_send(conn,ERR_NICKNAMEINUSE,temp);
308 }
309 else if (account_get_auth_lock(a)==1) {
310 irc_send_cmd(conn,"NOTICE",":Authentication rejected (account is locked) ");
311 }
312 else
313 {
314 if((conn_get_wol(conn) == 1)) {
315 temphash = account_get_wol_apgar(a);
316 tempapgar = conn_wol_get_apgar(conn);
317
318 if(temphash == NULL) {
319 account_set_wol_apgar(a,tempapgar);
320 temphash = account_get_wol_apgar(a);
321 }
322
323 if(tempapgar == NULL) {
324 irc_send(conn,RPL_BAD_LOGIN,":Authentication failed."); /* bad APGAR */
325 conn_increment_passfail_count(conn);
326 return 0;
327 }
328
329 if(strcmp(temphash,tempapgar) == 0) {
330 conn_set_clienttag(conn,CLIENTTAG_WWOL_UINT); /* WWOL hope here is ok */
331 conn_login(conn,a,username);
332 conn_set_state(conn,conn_state_loggedin);
333 return 1;
334 }
335 else {
336 irc_send(conn,RPL_BAD_LOGIN,":Authentication failed."); /* bad APGAR */
337 conn_increment_passfail_count(conn);
338 return 0;
339 }
340 }
341
342 hash_set_str(&h1,passhash);
343 temphash = account_get_pass(a);
344 hash_set_str(&h2,temphash);
345 if (hash_eq(h1,h2)) {
346 conn_set_clienttag(conn,CLIENTTAG_IIRC_UINT); /* IIRC hope here is ok */
347 conn_login(conn,a,username);
348 conn_set_state(conn,conn_state_loggedin);
349 irc_send_cmd(conn,"NOTICE",":Authentication successful. You are now logged in.");
350 return 1;
351 } else {
352 irc_send_cmd(conn,"NOTICE",":Authentication failed."); /* wrong password */
353 conn_increment_passfail_count(conn);
354 }
355 }
356 return 0;
357 }
358
irc_welcome(t_connection * conn)359 extern int irc_welcome(t_connection * conn)
360 {
361 char temp[MAX_IRC_MESSAGE_LEN];
362 time_t temptime;
363 char const * tempname;
364 char const * temptimestr;
365 char const * filename;
366 FILE *fp;
367 char * line, * formatted_line;
368 char send_line[MAX_IRC_MESSAGE_LEN];
369 char motd_failed = 0;
370
371 if (!conn) {
372 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
373 return -1;
374 }
375
376 tempname = conn_get_loggeduser(conn);
377
378 if ((34+strlen(tempname)+1)<=MAX_IRC_MESSAGE_LEN)
379 sprintf(temp,":Welcome to the %s IRC Network %s",prefs_get_irc_network_name(), tempname);
380 else
381 sprintf(temp,":Maximum length exceeded");
382 irc_send(conn,RPL_WELCOME,temp);
383
384 if ((14+strlen(server_get_hostname())+10+strlen(PVPGN_SOFTWARE" "PVPGN_VERSION)+1)<=MAX_IRC_MESSAGE_LEN)
385 sprintf(temp,":Your host is %s, running "PVPGN_SOFTWARE" "PVPGN_VERSION,server_get_hostname());
386 else
387 sprintf(temp,":Maximum length exceeded");
388 irc_send(conn,RPL_YOURHOST,temp);
389
390 temptime = server_get_starttime(); /* FIXME: This should be build time */
391 temptimestr = ctime(&temptime);
392 if ((25+strlen(temptimestr)+1)<=MAX_IRC_MESSAGE_LEN)
393 sprintf(temp,":This server was created %s",temptimestr); /* FIXME: is ctime() portable? */
394 else
395 sprintf(temp,":Maximum length exceeded");
396 irc_send(conn,RPL_CREATED,temp);
397
398 /* we don't give mode information on MYINFO we give it on ISUPPORT */
399 if ((strlen(server_get_hostname())+7+strlen(PVPGN_SOFTWARE" "PVPGN_VERSION)+9+1)<=MAX_IRC_MESSAGE_LEN)
400 sprintf(temp,"%s "PVPGN_SOFTWARE" "PVPGN_VERSION" - -",server_get_hostname());
401 else
402 sprintf(temp,":Maximum length exceeded");
403 irc_send(conn,RPL_MYINFO,temp);
404
405 if((conn_get_wol(conn) == 1))
406 sprintf(temp,"NICKLEN=%d TOPICLEN=%d CHANNELLEN=%d PREFIX="CHANNEL_PREFIX" CHANTYPES="CHANNEL_TYPE" NETWORK=%s IRCD="PVPGN_SOFTWARE,
407 WOL_NICKNAME_LEN, MAX_TOPIC_LEN, CHANNEL_NAME_LEN, prefs_get_irc_network_name());
408 else
409 sprintf(temp,"NICKLEN=%d TOPICLEN=%d CHANNELLEN=%d PREFIX="CHANNEL_PREFIX" CHANTYPES="CHANNEL_TYPE" NETWORK=%s IRCD="PVPGN_SOFTWARE,
410 CHAR_NAME_LEN, MAX_TOPIC_LEN, CHANNEL_NAME_LEN, prefs_get_irc_network_name());
411
412 if((strlen(temp))<=MAX_IRC_MESSAGE_LEN)
413 irc_send(conn,RPL_ISUPPORT,temp);
414 else {
415 sprintf(temp,":Maximum length exceeded");
416 irc_send(conn,RPL_ISUPPORT,temp);
417 }
418
419 if ((3+strlen(server_get_hostname())+22+1)<=MAX_IRC_MESSAGE_LEN)
420 sprintf(temp,":- %s, "PVPGN_SOFTWARE" "PVPGN_VERSION", built on %s",server_get_hostname(),temptimestr);
421 else
422 sprintf(temp,":Maximum length exceeded");
423 irc_send(conn,RPL_MOTDSTART,temp);
424
425 if ((filename = prefs_get_motdfile())) {
426 if ((fp = fopen(filename,"r"))) {
427 while ((line=file_get_line(fp))) {
428 if ((formatted_line = message_format_line(conn,line))) {
429 formatted_line[0]=' ';
430 sprintf(send_line,":-%s",formatted_line);
431 irc_send(conn,RPL_MOTD,send_line);
432 xfree(formatted_line);
433 }
434 }
435
436 file_get_line(NULL); // clear file_get_line buffer
437 fclose(fp);
438 }
439 else
440 motd_failed = 1;
441 }
442 else
443 motd_failed = 1;
444
445 if (motd_failed) {
446 irc_send(conn,RPL_MOTD,":- Failed to load motd, sending default motd ");
447 irc_send(conn,RPL_MOTD,":- ====================================================== ");
448 irc_send(conn,RPL_MOTD,":- http://www.pvpgn.org ");
449 irc_send(conn,RPL_MOTD,":- ====================================================== ");
450 }
451 irc_send(conn,RPL_ENDOFMOTD,":End of /MOTD command");
452 irc_send_cmd(conn,"NOTICE",":This is an experimental service.");
453
454 conn_set_state(conn,conn_state_bot_password);
455
456 if (conn_get_ircpass(conn)) {
457 irc_send_cmd(conn,"NOTICE",":Trying to authenticate with PASS ...");
458 irc_authenticate(conn,conn_get_ircpass(conn));
459 } else {
460 irc_send_cmd(conn,"NOTICE",":No PASS command received. Please identify yourself by /msg NICKSERV identify <password>.");
461 }
462 return 0;
463 }
464
465 /* Channel name conversion rules: */
466 /* Not allowed in IRC (RFC2812): NUL, BELL, CR, LF, ' ', ':' and ','*/
467 /* ' ' -> '_' */
468 /* '_' -> '%_' */
469 /* '%' -> '%%' */
470 /* '\b' -> '%b' */
471 /* '\n' -> '%n' */
472 /* '\r' -> '%r' */
473 /* ':' -> '%=' */
474 /* ',' -> '%-' */
475 /* In IRC a channel can be specified by '#'+channelname or '!'+channelid */
irc_convert_channel(t_channel const * channel)476 extern char const * irc_convert_channel(t_channel const * channel)
477 {
478 char const * bname;
479 static char out[CHANNEL_NAME_LEN];
480 unsigned int outpos;
481 int i;
482
483 if (!channel)
484 return "*";
485
486 memset(out,0,sizeof(out));
487 out[0] = '#';
488 outpos = 1;
489 bname = channel_get_name(channel);
490 for (i=0; bname[i]!='\0'; i++) {
491 if (bname[i]==' ') {
492 out[outpos++] = '_';
493 } else if (bname[i]=='_') {
494 out[outpos++] = '%';
495 out[outpos++] = '_';
496 } else if (bname[i]=='%') {
497 out[outpos++] = '%';
498 out[outpos++] = '%';
499 } else if (bname[i]=='\b') {
500 out[outpos++] = '%';
501 out[outpos++] = 'b';
502 } else if (bname[i]=='\n') {
503 out[outpos++] = '%';
504 out[outpos++] = 'n';
505 } else if (bname[i]=='\r') {
506 out[outpos++] = '%';
507 out[outpos++] = 'r';
508 } else if (bname[i]==':') {
509 out[outpos++] = '%';
510 out[outpos++] = '=';
511 } else if (bname[i]==',') {
512 out[outpos++] = '%';
513 out[outpos++] = '-';
514 } else {
515 out[outpos++] = bname[i];
516 }
517 if ((outpos+2)>=(sizeof(out))) {
518 sprintf(out,"!%u",channel_get_channelid(channel));
519 return out;
520 }
521 }
522 return out;
523 }
524
irc_convert_ircname(char const * pircname)525 extern char const * irc_convert_ircname(char const * pircname)
526 {
527 static char out[CHANNEL_NAME_LEN];
528 unsigned int outpos;
529 int special;
530 int i;
531 char const * ircname = pircname + 1;
532
533 if (!ircname) {
534 eventlog(eventlog_level_error,__FUNCTION__,"got NULL ircname");
535 return NULL;
536 }
537
538 outpos = 0;
539 memset(out,0,sizeof(out));
540 special = 0;
541 if (pircname[0]=='!') {
542 t_channel * channel;
543
544 channel = channellist_find_channel_bychannelid(atoi(ircname));
545 if (channel)
546 return channel_get_name(channel);
547 else
548 return NULL;
549 } else if (pircname[0]!='#') {
550 return NULL;
551 }
552 for (i=0; ircname[i]!='\0'; i++) {
553 if (ircname[i]=='_') {
554 out[outpos++] = ' ';
555 } else if (ircname[i]=='%') {
556 if (special) {
557 out[outpos++] = '%';
558 special = 0;
559 } else {
560 special = 1;
561 }
562 } else if (special) {
563 if (ircname[i]=='_') {
564 out[outpos++] = '_';
565 } else if (ircname[i]=='b') {
566 out[outpos++] = '\b';
567 } else if (ircname[i]=='n') {
568 out[outpos++] = '\n';
569 } else if (ircname[i]=='r') {
570 out[outpos++] = '\r';
571 } else if (ircname[i]=='=') {
572 out[outpos++] = ':';
573 } else if (ircname[i]=='-') {
574 out[outpos++] = ',';
575 } else {
576 /* maybe it's just a typo :) */
577 out[outpos++] = '%';
578 out[outpos++] = ircname[i];
579 }
580 } else {
581 out[outpos++] = ircname[i];
582 }
583 if ((outpos+2)>=(sizeof(out))) {
584 return NULL;
585 }
586 }
587 return out;
588 }
589
590 /* splits an string list into its elements */
591 /* (list will be modified) */
irc_split_elems(char * list,int separator,int ignoreblank)592 static char ** irc_split_elems(char * list, int separator, int ignoreblank)
593 {
594 int i;
595 int count;
596 char ** out;
597
598 if (!list) {
599 eventlog(eventlog_level_error,__FUNCTION__,"got NULL list");
600 return NULL;
601 }
602
603 for (count=0,i=0;list[i]!='\0';i++) {
604 if (list[i]==separator) {
605 count++;
606 if (ignoreblank) {
607 /* ignore more than one separators "in a row" */
608 while ((list[i+1]!='\0')&&(list[i]==separator)) i++;
609 }
610 }
611 }
612 count++; /* count separators -> we have one more element ... */
613 /* we also need a terminating element */
614 out = xmalloc((count+1)*sizeof(char *));
615
616 out[0] = list;
617 if (count>1) {
618 for (i=1;i<count;i++) {
619 out[i] = strchr(out[i-1],separator);
620 if (!out[i]) {
621 eventlog(eventlog_level_error,__FUNCTION__,"BUG: wrong number of separators");
622 xfree(out);
623 return NULL;
624 }
625 if (ignoreblank)
626 while ((*out[i]+1)==separator) out[i]++;
627 *out[i]++ = '\0';
628 }
629 if ((ignoreblank)&&(out[count-1])&&(*out[count-1]=='\0')) {
630 out[count-1] = NULL; /* last element is blank */
631 }
632 } else if ((ignoreblank)&&(*out[0]=='\0')) {
633 out[0] = NULL; /* now we have 2 terminators ... never mind */
634 }
635 out[count] = NULL; /* terminating element */
636 return out;
637 }
638
irc_unget_elems(char ** elems)639 static int irc_unget_elems(char ** elems)
640 {
641 if (!elems) {
642 eventlog(eventlog_level_error,__FUNCTION__,"got NULL elems");
643 return -1;
644 }
645 xfree(elems);
646 return 0;
647 }
648
irc_get_listelems(char * list)649 extern char ** irc_get_listelems(char * list)
650 {
651 return irc_split_elems(list,',',0);
652 }
653
irc_unget_listelems(char ** elems)654 extern int irc_unget_listelems(char ** elems)
655 {
656 return irc_unget_elems(elems);
657 }
658
irc_get_paramelems(char * list)659 extern char ** irc_get_paramelems(char * list)
660 {
661 return irc_split_elems(list,' ',1);
662 }
663
irc_unget_paramelems(char ** elems)664 extern int irc_unget_paramelems(char ** elems)
665 {
666 return irc_unget_elems(elems);
667 }
668
irc_message_preformat(t_irc_message_from const * from,char const * command,char const * dest,char const * text)669 static char * irc_message_preformat(t_irc_message_from const * from, char const * command, char const * dest, char const * text)
670 {
671 char * myfrom;
672 char const * mydest = "";
673 char const * mytext = "";
674 int len;
675 char * msg;
676
677 if (!command) {
678 eventlog(eventlog_level_error,__FUNCTION__,"got NULL command");
679 return NULL;
680 }
681 if (from) {
682 if ((!from->nick)||(!from->user)||(!from->host)) {
683 eventlog(eventlog_level_error,__FUNCTION__,"got malformed from");
684 return NULL;
685 }
686 myfrom = xmalloc(strlen(from->nick)+1+strlen(from->user)+1+strlen(from->host)+1); /* nick + "!" + user + "@" + host + "\0" */
687 sprintf(myfrom,"%s!%s@%s",from->nick,from->user,from->host);
688 } else
689 myfrom = xstrdup(server_get_hostname());
690 if (dest)
691 mydest = dest;
692 if (text)
693 mytext = text;
694
695 len = 1+strlen(myfrom)+1+
696 strlen(command)+1+
697 strlen(mydest)+1+
698 1+strlen(mytext)+1;
699
700
701 msg = xmalloc(len);
702 sprintf(msg,":%s\n%s\n%s\n%s",myfrom,command,mydest,mytext);
703 xfree(myfrom);
704 return msg;
705 }
706
irc_message_postformat(t_packet * packet,t_connection const * dest)707 extern int irc_message_postformat(t_packet * packet, t_connection const * dest)
708 {
709 int len;
710 /* the four elements */
711 char * e1;
712 char * e1_2;
713 char * e2;
714 char * e3;
715 char * e4;
716 char const * tname = NULL;
717 char const * toname = "AUTH"; /* fallback name */
718
719 if (!packet) {
720 eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet");
721 return -1;
722 }
723 if (!dest) {
724 eventlog(eventlog_level_error,__FUNCTION__,"got NULL dest");
725 return -1;
726 }
727
728 e1 = packet_get_raw_data(packet,0);
729 e2 = strchr(e1,'\n');
730 if (!e2) {
731 eventlog(eventlog_level_warn,__FUNCTION__,"malformed message (e2 missing)");
732 return -1;
733 }
734 *e2++ = '\0';
735 e3 = strchr(e2,'\n');
736 if (!e3) {
737 eventlog(eventlog_level_warn,__FUNCTION__,"malformed message (e3 missing)");
738 return -1;
739 }
740 *e3++ = '\0';
741 e4 = strchr(e3,'\n');
742 if (!e4) {
743 eventlog(eventlog_level_warn,__FUNCTION__,"malformed message (e4 missing)");
744 return -1;
745 }
746 *e4++ = '\0';
747
748 if (prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(dest)) & command_get_group("/admin-addr")))
749 {
750 e1_2 = strchr(e1,'@');
751 if (e1_2)
752 {
753 *e1_2++ = '\0';
754 }
755 }
756 else
757 e1_2 = NULL;
758
759 if (e3[0]=='\0') { /* fill in recipient */
760 if ((tname = conn_get_chatname(dest)))
761 toname = tname;
762 } else
763 toname = e3;
764
765 if (strcmp(toname,"\r")==0) {
766 toname = ""; /* HACK: the target field is really empty */
767 }
768
769 len = (strlen(e1)+1+strlen(e2)+1+strlen(toname)+1+strlen(e4)+2+1);
770 if (len<=MAX_IRC_MESSAGE_LEN) {
771 char msg[MAX_IRC_MESSAGE_LEN+1];
772
773 if (e1_2)
774 sprintf(msg,"%s@hidden %s %s %s\r\n",e1,e2,toname,e4);
775 else
776 sprintf(msg,"%s %s %s %s\r\n",e1,e2,toname,e4);
777 eventlog(eventlog_level_debug,__FUNCTION__,"sent \"%s\"",msg);
778 packet_set_size(packet,0);
779 packet_append_data(packet,msg,strlen(msg));
780 if (tname)
781 conn_unget_chatname(dest,tname);
782 return 0;
783 } else {
784 /* FIXME: split up message? */
785 eventlog(eventlog_level_warn,__FUNCTION__,"maximum IRC message length exceeded");
786 if (tname)
787 conn_unget_chatname(dest,tname);
788 return -1;
789 }
790 }
791
irc_message_format(t_packet * packet,t_message_type type,t_connection * me,t_connection * dst,char const * text,unsigned int dstflags)792 extern int irc_message_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags)
793 {
794 char * msg;
795 char const * ctag;
796 t_irc_message_from from;
797
798 if (!packet)
799 {
800 eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet");
801 return -1;
802 }
803
804 msg = NULL;
805 if (me)
806 ctag = clienttag_uint_to_str(conn_get_clienttag(me));
807 else
808 ctag = clienttag_uint_to_str(CLIENTTAG_IIRC_UINT);
809
810 switch (type)
811 {
812 /* case message_type_adduser: this is sent manually in handle_irc */
813 case message_type_adduser:
814 /* when we do it somewhere else, then we can also make sure to not get our logs spammed */
815 break;
816 case message_type_join:
817 from.nick = conn_get_chatname(me);
818 from.user = ctag;
819 from.host = addr_num_to_ip_str(conn_get_addr(me));
820
821 if((conn_get_wol(me) == 1))
822 {
823 char temp[MAX_IRC_MESSAGE_LEN];
824 memset(temp,0,sizeof(temp));
825
826 /**
827 * For WOL the channel JOIN output must be like the following:
828 * user!WWOL@hostname JOIN :clanID,longIP channelName
829 */
830 sprintf(temp,":0,%u",conn_get_addr(me));
831 msg = irc_message_preformat(&from,"JOIN",temp,irc_convert_channel(conn_get_channel(me)));
832 }
833 else
834 msg = irc_message_preformat(&from,"JOIN","\r",irc_convert_channel(conn_get_channel(me)));
835 conn_unget_chatname(me,from.nick);
836 break;
837 case message_type_part:
838 from.nick = conn_get_chatname(me);
839 from.user = ctag;
840 from.host = addr_num_to_ip_str(conn_get_addr(me));
841 msg = irc_message_preformat(&from,"PART","\r",irc_convert_channel(conn_get_channel(me)));
842 conn_unget_chatname(me,from.nick);
843 break;
844 case message_type_talk:
845 case message_type_whisper:
846 {
847 char const * dest;
848 char temp[MAX_IRC_MESSAGE_LEN];
849
850 if (me)
851 {
852 from.nick = conn_get_chatname(me);
853 from.host = addr_num_to_ip_str(conn_get_addr(me));
854 }
855 else
856 {
857 from.nick = server_get_hostname();
858 from.host = server_get_hostname();
859 }
860
861 from.user = ctag;
862
863 if (type==message_type_talk)
864 dest = irc_convert_channel(conn_get_channel(me)); /* FIXME: support more channels and choose right one! */
865 else
866 dest = ""; /* will be replaced with username in postformat */
867 sprintf(temp,":%s",text);
868 msg = irc_message_preformat(&from,"PRIVMSG",dest,temp);
869 if (me)
870 conn_unget_chatname(me,from.nick);
871 }
872 break;
873 case message_type_emote:
874 {
875 char const * dest;
876 char temp[MAX_IRC_MESSAGE_LEN];
877
878 /* "\001ACTION " + text + "\001" + \0 */
879 if ((8+strlen(text)+1+1)<=MAX_IRC_MESSAGE_LEN) {
880 sprintf(temp,":\001ACTION %s\001",text);
881 } else {
882 sprintf(temp,":\001ACTION (maximum message length exceeded)\001");
883 }
884 from.nick = conn_get_chatname(me);
885 from.user = ctag;
886 from.host = addr_num_to_ip_str(conn_get_addr(me));
887 /* FIXME: also supports whisper emotes? */
888 dest = irc_convert_channel(conn_get_channel(me)); /* FIXME: support more channels and choose right one! */
889 msg = irc_message_preformat(&from,"PRIVMSG",dest,temp);
890 conn_unget_chatname(me,from.nick);
891 }
892 break;
893 case message_type_broadcast:
894 case message_type_info:
895 case message_type_error:
896 {
897 char temp[MAX_IRC_MESSAGE_LEN];
898 sprintf(temp,":%s",text);
899 if ((type==message_type_info || type==message_type_error) && (conn_get_wol(dst) == 1))
900 msg = irc_message_preformat(NULL,"PAGE",NULL,temp);
901 else
902 msg = irc_message_preformat(NULL,"NOTICE",NULL,temp);
903 }
904 break;
905 case message_type_channel:
906 /* ignore it */
907 break;
908 case message_type_userflags:
909 /* ignore it */
910 break;
911 case message_type_mode:
912 from.nick = conn_get_chatname(me);
913 from.user = ctag;
914 from.host = addr_num_to_ip_str(conn_get_addr(me));
915 msg = irc_message_preformat(&from,"MODE","\r",text);
916 conn_unget_chatname(me,from.nick);
917 break;
918 case message_type_notice:
919 {
920 char temp[MAX_IRC_MESSAGE_LEN];
921 sprintf(temp,":%s",text);
922
923 if (me && conn_get_chatname(me))
924 {
925 from.nick = conn_get_chatname(me);
926 from.host = addr_num_to_ip_str(conn_get_addr(me));
927 from.user = ctag;
928 msg = irc_message_preformat(&from,"NOTICE","",temp);
929 }else{
930 msg = irc_message_preformat(NULL,"NOTICE","",temp);
931 }
932 }
933 break;
934 /**
935 * Westwood Online Extensions
936 */
937 case message_wol_joingame:
938 from.nick = conn_get_chatname(me);
939 from.user = ctag;
940 from.host = addr_num_to_ip_str(conn_get_addr(me));
941 msg = irc_message_preformat(&from,"JOINGAME",text,"\r");
942 conn_unget_chatname(me,from.nick);
943 break;
944 case message_wol_gameopt_owner:
945 from.nick = conn_get_chatname(me);
946 from.user = ctag;
947 from.host = addr_num_to_ip_str(conn_get_addr(me));
948 msg = irc_message_preformat(&from,"GAMEOPT",irc_convert_channel(conn_get_channel(me)),text);
949 conn_unget_chatname(me,from.nick);
950 break;
951 case message_wol_gameopt_join:
952 from.nick = conn_get_chatname(me);
953 from.user = ctag;
954 from.host = addr_num_to_ip_str(conn_get_addr(me));
955 msg = irc_message_preformat(&from,"GAMEOPT",channel_wol_get_game_owner(conn_get_channel(me)),text);
956 conn_unget_chatname(me,from.nick);
957 break;
958 case message_wol_start_game:
959 from.nick = conn_get_chatname(me);
960 from.user = ctag;
961 from.host = addr_num_to_ip_str(conn_get_addr(me));
962 msg = irc_message_preformat(&from,"STARTG","u",text);
963 conn_unget_chatname(me,from.nick);
964 break;
965 case message_wol_page:
966 from.nick = conn_get_chatname(me);
967 from.user = ctag;
968 from.host = addr_num_to_ip_str(conn_get_addr(me));
969 msg = irc_message_preformat(&from,"PAGE","u",text);
970 conn_unget_chatname(me,from.nick);
971 break;
972 default:
973 eventlog(eventlog_level_warn,__FUNCTION__,"%d not yet implemented",type);
974 return -1;
975 }
976
977 if (msg) {
978 packet_append_string(packet,msg);
979 xfree(msg);
980 return 0;
981 }
982 return -1;
983 }
984
irc_send_rpl_namreply(t_connection * c,t_channel const * channel)985 extern int irc_send_rpl_namreply(t_connection * c, t_channel const * channel)
986 {
987 char temp[MAX_IRC_MESSAGE_LEN];
988 char const * ircname;
989 int first = 1;
990 t_connection * m;
991
992 if (!c) {
993 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
994 return -1;
995 }
996 if (!channel) {
997 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
998 return -1;
999 }
1000 memset(temp,0,sizeof(temp));
1001 ircname = irc_convert_channel(channel);
1002 if (!ircname) {
1003 eventlog(eventlog_level_error,__FUNCTION__,"channel has NULL ircname");
1004 return -1;
1005 }
1006 /* '@' = secret; '*' = private; '=' = public */
1007 if ((1+1+strlen(ircname)+2+1)<=MAX_IRC_MESSAGE_LEN) {
1008 sprintf(temp,"%c %s :",((channel_get_permanent(channel))?('='):('*')),ircname);
1009 } else {
1010 eventlog(eventlog_level_warn,__FUNCTION__,"maximum message length exceeded");
1011 return -1;
1012 }
1013 /* FIXME: Add per user flags (@(op) and +(voice))*/
1014 for (m = channel_get_first(channel);m;m = channel_get_next()) {
1015 char const * name = conn_get_chatname(m);
1016 char flg[5] = "";
1017 unsigned int flags;
1018
1019 if (!name)
1020 continue;
1021 flags = conn_get_flags(m);
1022 if (flags & MF_BLIZZARD)
1023 strcat(flg,"@");
1024 else if ((flags & MF_BNET) || (flags & MF_GAVEL))
1025 strcat(flg,"%");
1026 else if (flags & MF_VOICE)
1027 strcat(flg,"+");
1028 if ((strlen(temp)+((!first)?(1):(0))+strlen(flg)+strlen(name)+1)<=sizeof(temp)) {
1029 if (!first) strcat(temp," ");
1030
1031 if((conn_get_wol(c) == 1))
1032 {
1033 char _temp[MAX_IRC_MESSAGE_LEN];
1034 if ((channel_wol_get_game_owner(channel) != NULL) && (strcmp(channel_wol_get_game_owner(channel),name) == 0)) {
1035 strcat(temp,"@");
1036 }
1037 snprintf(_temp,sizeof(_temp),"%s,0,%u",name,conn_get_addr(m));
1038 strcat(temp,_temp);
1039 }
1040 else
1041 {
1042 strcat(temp,flg);
1043 strcat(temp,name);
1044 }
1045
1046 first = 0;
1047 }
1048 conn_unget_chatname(m,name);
1049 }
1050 irc_send(c,RPL_NAMREPLY,temp);
1051 return 0;
1052 }
1053
irc_who_connection(t_connection * dest,t_connection * c)1054 static int irc_who_connection(t_connection * dest, t_connection * c)
1055 {
1056 t_account * a;
1057 char const * tempuser;
1058 char const * tempowner;
1059 char const * tempname;
1060 char const * tempip;
1061 char const * tempflags = "@"; /* FIXME: that's dumb */
1062 char temp[MAX_IRC_MESSAGE_LEN];
1063 char const * tempchannel;
1064
1065 if (!dest) {
1066 eventlog(eventlog_level_error,__FUNCTION__,"got NULL destination");
1067 return -1;
1068 }
1069 if (!c) {
1070 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
1071 return -1;
1072 }
1073 a = conn_get_account(c);
1074 if (!(tempuser = clienttag_uint_to_str(conn_get_clienttag(c))))
1075 {
1076 eventlog(eventlog_level_error,__FUNCTION__,"got NULL clienttag (tempuser)");
1077 return -1;
1078 }
1079 if (!(tempowner = account_get_ll_owner(a)))
1080 {
1081 eventlog(eventlog_level_error,__FUNCTION__,"got NULL ll_owner (tempowner)");
1082 return -1;
1083 }
1084 if (!(tempname = conn_get_username(c)))
1085 {
1086 eventlog(eventlog_level_error,__FUNCTION__,"got NULL username (tempname)");
1087 return -1;
1088 }
1089 if (!(tempip = addr_num_to_ip_str(conn_get_addr(c))))
1090 {
1091 eventlog(eventlog_level_error,__FUNCTION__,"got NULL addr (tempip)");
1092 return -1;
1093 }
1094 if (!(tempchannel = irc_convert_channel(conn_get_channel(c))))
1095 {
1096 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel (tempchannel)");
1097 return -1;
1098 }
1099 if ((strlen(tempchannel)+1+strlen(tempuser)+1+strlen(tempip)+1+strlen(server_get_hostname())+1+strlen(tempname)+1+1+strlen(tempflags)+4+strlen(tempowner)+1)>MAX_IRC_MESSAGE_LEN) {
1100 eventlog(eventlog_level_info,__FUNCTION__,"WHO reply too long - skip");
1101 return -1;
1102 } else
1103 sprintf(temp,"%s %s %s %s %s %c%s :0 %s",tempchannel,tempuser,tempip,server_get_hostname(),tempname,'H',tempflags,tempowner);
1104 irc_send(dest,RPL_WHOREPLY,temp);
1105 return 0;
1106 }
1107
irc_who(t_connection * c,char const * name)1108 extern int irc_who(t_connection * c, char const * name)
1109 {
1110 /* FIXME: support wildcards! */
1111
1112 if (!c) {
1113 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
1114 return -1;
1115 }
1116 if (!name) {
1117 eventlog(eventlog_level_error,__FUNCTION__,"got NULL name");
1118 return -1;
1119 }
1120 if ((name[0]=='#')||(name[0]=='&')||(name[0]=='!')) {
1121 /* it's a channel */
1122 t_connection * info;
1123 t_channel * channel;
1124 char const * ircname;
1125
1126 ircname = irc_convert_ircname(name);
1127 channel = channellist_find_channel_by_name(ircname,NULL,NULL);
1128 if (!channel) {
1129 char temp[MAX_IRC_MESSAGE_LEN];
1130
1131 if ((strlen(":No such channel")+1+strlen(name)+1)<=MAX_IRC_MESSAGE_LEN) {
1132 sprintf(temp,":No such channel %s",name);
1133 irc_send(c,ERR_NOSUCHCHANNEL,temp);
1134 } else {
1135 irc_send(c,ERR_NOSUCHCHANNEL,":No such channel");
1136 }
1137 return 0;
1138 }
1139 for (info = channel_get_first(channel);info;info = channel_get_next()) {
1140 irc_who_connection(c,info);
1141 }
1142 } else {
1143 /* it's just one user */
1144 t_connection * info;
1145
1146 if ((info = connlist_find_connection_by_accountname(name)))
1147 return irc_who_connection(c,info);
1148 }
1149 return 0;
1150 }
1151
1152