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