/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2001,2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define MESSAGE_INTERNAL_ACCESS #include "common/setup_before.h" #include #ifdef HAVE_STDDEF_H # include #else # ifndef NULL # define NULL ((void *)0) # endif #endif #ifdef STDC_HEADERS # include #else # ifdef HAVE_MALLOC_H # include # endif #endif #ifdef HAVE_STRING_H # include #else # ifdef HAVE_STRINGS_H # include # endif #endif #include "compat/strdup.h" #ifdef HAVE_UNISTD_H # include #endif #include "compat/gethostname.h" #include #include "compat/strerror.h" #include "connection.h" #include "common/bn_type.h" #include "common/queue.h" #include "common/packet.h" #include "common/bot_protocol.h" #include "common/bnet_protocol.h" #include "common/field_sizes.h" #include "common/eventlog.h" #include "common/list.h" #include "common/util.h" #include "common/version.h" #include "common/addr.h" #include "account.h" #include "account_wrap.h" #include "game.h" #include "channel.h" #include "channel_conv.h" #include "command.h" #include "irc.h" #include "message.h" #include "mail.h" #include "prefs.h" #include "common/tag.h" #include "common/xalloc.h" #include "common/setup_after.h" static int message_telnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static int message_bot_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static int message_bnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static t_packet * message_cache_lookup(t_message * message, t_connection *dst, unsigned int flags); static char const * message_type_get_str(t_message_type type) { switch (type) { case message_type_adduser: return "adduser"; case message_type_join: return "join"; case message_type_part: return "part"; case message_type_whisper: return "whisper"; case message_type_talk: return "talk"; case message_type_broadcast: return "broadcast"; case message_type_channel: return "channel"; case message_type_userflags: return "userflags"; case message_type_whisperack: return "whisperack"; case message_type_friendwhisperack: //[zap-zero] 20020518 return "friendwhisperack"; case message_type_channelfull: return "channelfull"; case message_type_channeldoesnotexist: return "channeldoesnotexist"; case message_type_channelrestricted: return "channelrestricted"; case message_type_info: return "info"; case message_type_error: return "error"; case message_type_emote: return "emote"; case message_type_uniqueid: return "uniqueid"; case message_type_mode: return "mode"; case message_type_notice: return "notice"; case message_type_null: return "null"; default: return "UNKNOWN"; } } /* make sure none of the expanded format symbols is longer than this (with null) */ #define MAX_INC 64 extern char * message_format_line(t_connection const * c, char const * in) { char * out; unsigned int inpos; unsigned int outpos; unsigned int outlen=MAX_INC; unsigned int inlen; char clienttag_str[5]; out = xmalloc(outlen+1); inlen = strlen(in); out[0] = 'I'; for (inpos=0,outpos=1; inpos=outlen) { char * newout; outlen += MAX_INC; newout = xrealloc(out,outlen); out = newout; } } out[outpos] = '\0'; return out; } static int message_telnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { char * msgtemp; if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet"); return -1; } switch (type) { case message_type_uniqueid: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(strlen(text)+32); sprintf(msgtemp,"Your unique name: %s\r\n",text); break; case message_type_adduser: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(strlen(tname)+32); sprintf(msgtemp,"[%s is here]\r\n",tname); conn_unget_chatcharname(me,tname); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(strlen(tname)+32); sprintf(msgtemp,"[%s enters]\r\n",tname); conn_unget_chatcharname(me,tname); } break; case message_type_part: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(strlen(tname)+32); sprintf(msgtemp,"[%s leaves]\r\n",tname); conn_unget_chatcharname(me,tname); } break; case message_type_whisper: case message_type_notice: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(strlen(tname)+8+strlen(newtext)+4); sprintf(msgtemp," %s\r\n",tname,newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(16+strlen(tname)); sprintf(msgtemp," \r\n",tname); } if (me) conn_unget_chatcharname(me,tname); } break; case message_type_talk: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(strlen(tname)+4+strlen(newtext)+4); sprintf(msgtemp,"<%s> %s\r\n",tname,newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(strlen(tname)+8); sprintf(msgtemp,"<%s> \r\n",tname); } if (me) conn_unget_chatcharname(me,tname); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * newtext; if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(16+strlen(newtext)+4); sprintf(msgtemp,"Broadcast: %s\r\n",newtext); /* FIXME: show source? */ xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(16); sprintf(msgtemp,"Broadcast: \r\n"); /* FIXME: show source? */ } } break; case message_type_channel: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(strlen(text)+32); sprintf(msgtemp,"Joining channel: \"%s\"\r\n",text); break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } msgtemp = xstrdup(""); break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { char const * tname; char const * newtext; tname = conn_get_chatcharname(me, dst); if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(strlen(tname)+8+strlen(newtext)+4); sprintf(msgtemp," %s\r\n",tname,newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(strlen(tname)+8+strlen(text)+4); sprintf(msgtemp," %s\r\n",tname,text); } conn_unget_chatcharname(me,tname); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(14+8+strlen(newtext)+4); sprintf(msgtemp," %s\r\n",newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(14+8+strlen(text)+4); sprintf(msgtemp," %s\r\n",text); } } break; case message_type_channelfull: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_channeldoesnotexist: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_channelrestricted: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_info: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(strlen(newtext)+4); sprintf(msgtemp,"%s\r\n",newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(strlen(text)+4); sprintf(msgtemp,"%s\r\n",text); } } break; case message_type_error: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(8+strlen(newtext)+4); sprintf(msgtemp,"ERROR: %s\r\n",newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(8+strlen(text)+4); sprintf(msgtemp,"ERROR: %s\r\n",text); } } break; case message_type_emote: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; tname = conn_get_chatcharname(me, dst); if ((newtext = escape_chars(text,strlen(text)))) { msgtemp = xmalloc(strlen(tname)+4+strlen(newtext)+4); sprintf(msgtemp,"<%s %s>\r\n",tname,newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = xmalloc(strlen(tname)+4+strlen(text)+4); sprintf(msgtemp,"<%s %s>\r\n",tname,text); } conn_unget_chatcharname(me,tname); } break; case message_type_mode: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me,dst); msgtemp = xmalloc(strlen(tname)+32); sprintf(msgtemp,"%s change mode: %s\r\n",tname,text); conn_unget_chatcharname(me,tname); } default: eventlog(eventlog_level_error,__FUNCTION__,"got bad message type %d",(int)type); return -1; } { int retval; retval = packet_append_ntstring(packet,msgtemp); xfree(msgtemp); return retval; } } static int message_bot_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { char * msgtemp; char clienttag_str[5]; if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet"); return -1; } /* special-case the login banner so it doesn't have numbers * at the start of each line */ if (me && conn_get_state(me)!=conn_state_loggedin && conn_get_state(me)!=conn_state_destroy && type!=message_type_null) /* this does not apply for NULL messages */ { if (!text) { #if 0 /* battle.net actually sends them during login */ if (type==message_type_null) return 0; /* don't display null messages during the login */ #endif eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for non-loggedin state"); return -1; } msgtemp = xmalloc(strlen(text)+4); sprintf(msgtemp,"%s\r\n",text); } else switch (type) { case message_type_null: msgtemp = xmalloc(32); sprintf(msgtemp,"%u %s\r\n",EID_NULL,"NULL"); break; case message_type_uniqueid: /* FIXME: need to send this for some bots, also needed to support guest accounts */ if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(strlen(text)+32); sprintf(msgtemp,"%u %s %s\r\n",EID_UNIQUENAME,"NAME",text); break; case message_type_adduser: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32); sprintf(msgtemp,"%u %s %s %04x [%s]\r\n",EID_SHOWUSER,"USER",tname,conn_get_flags(me)|dstflags,tag_uint_to_str(clienttag_str,conn_get_fake_clienttag(me))); conn_unget_chatcharname(me,tname); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32); sprintf(msgtemp,"%u %s %s %04x [%s]\r\n",EID_JOIN,"JOIN",tname,conn_get_flags(me)|dstflags,tag_uint_to_str(clienttag_str,conn_get_fake_clienttag(me))); conn_unget_chatcharname(me,tname); } break; case message_type_part: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32); sprintf(msgtemp,"%u %s %s %04x\r\n",EID_LEAVE,"LEAVE",tname,conn_get_flags(me)|dstflags); conn_unget_chatcharname(me,tname); } break; case message_type_whisper: case message_type_notice: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); msgtemp = xmalloc(32+strlen(tname)+32+strlen(text)); sprintf(msgtemp,"%u %s %s %04x \"%s\"\r\n",EID_WHISPER,"WHISPER",tname,me?conn_get_flags(me)|dstflags:dstflags,text); if (me) conn_unget_chatcharname(me,tname); } break; case message_type_talk: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32+strlen(text)); sprintf(msgtemp,"%u %s %s %04x \"%s\"\r\n",EID_TALK,"TALK",tname,conn_get_flags(me)|dstflags,text); conn_unget_chatcharname(me,tname); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ msgtemp = xmalloc(32+32+strlen(text)); sprintf(msgtemp,"%u %s \"%s\"\r\n",EID_BROADCAST,"_",text); /* FIXME: what does this look like on Battle.net? */ break; case message_type_channel: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(32+strlen(text)); sprintf(msgtemp,"%u %s \"%s\"\r\n",EID_CHANNEL,"CHANNEL",text); break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+16); sprintf(msgtemp,"%u %s %s %04x\r\n",EID_USERFLAGS,"USER",tname,conn_get_flags(me)|dstflags); conn_unget_chatcharname(me,tname); } break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32+strlen(text)); sprintf(msgtemp,"%u %s %s %04x \"%s\"\r\n",EID_WHISPERSENT,"WHISPER",tname,conn_get_flags(me)|dstflags,text); conn_unget_chatcharname(me,tname); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } { msgtemp = xmalloc(32+16+32+strlen(text)); sprintf(msgtemp,"%u %s \"your friends\" %04x \"%s\"\r\n",EID_WHISPERSENT,"WHISPER",conn_get_flags(me)|dstflags,text); } break; case message_type_channelfull: msgtemp = xmalloc(32); sprintf(msgtemp,"%u \r\n",EID_CHANNELFULL); /* FIXME */ break; case message_type_channeldoesnotexist: msgtemp = xmalloc(32); sprintf(msgtemp,"%u \r\n",EID_CHANNELDOESNOTEXIST); /* FIXME */ break; case message_type_channelrestricted: msgtemp = xmalloc(32); sprintf(msgtemp,"%u \r\n",EID_CHANNELRESTRICTED); /* FIXME */ break; case message_type_info: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(32+16+strlen(text)); sprintf(msgtemp,"%u %s \"%s\"\r\n",EID_INFO,"INFO",text); break; case message_type_error: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } msgtemp = xmalloc(32+16+strlen(text)); sprintf(msgtemp,"%u %s \"%s\"\r\n",EID_ERROR,"ERROR",text); break; case message_type_emote: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = xmalloc(32+strlen(tname)+32+strlen(text)); sprintf(msgtemp,"%u %s %s %04x \"%s\"\r\n",EID_EMOTE,"EMOTE",tname,conn_get_flags(me)|dstflags,text); conn_unget_chatcharname(me,tname); } break; default: eventlog(eventlog_level_error,__FUNCTION__,"got bad message type %d",(int)type); return -1; } if (strlen(msgtemp)>MAX_MESSAGE_LEN) msgtemp[MAX_MESSAGE_LEN] = '\0'; /* now truncate to max size */ { int retval; retval = packet_append_ntstring(packet,msgtemp); xfree(msgtemp); return retval; } } static int message_bnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet"); return -1; } if (text && text[0]=='\0') text = " "; /* empty messages crash some clients, just send whitespace */ packet_set_size(packet,sizeof(t_server_message)); packet_set_type(packet,SERVER_MESSAGE); bn_int_set(&packet->u.server_message.player_ip,SERVER_MESSAGE_PLAYER_IP_DUMMY); bn_int_nset(&packet->u.server_message.account_num,SERVER_MESSAGE_ACCOUNT_NUM); bn_int_set(&packet->u.server_message.reg_auth,SERVER_MESSAGE_REG_AUTH); switch (type) { case message_type_adduser: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_ADDUSER); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet,playerinfo); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_JOIN); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet, playerinfo); } break; case message_type_part: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_PART); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); packet_append_string(packet,""); } break; case message_type_whisper: case message_type_notice: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_WHISPER); bn_int_set(&packet->u.server_message.flags,me?conn_get_flags(me)|dstflags:dstflags); bn_int_set(&packet->u.server_message.latency,me?conn_get_latency(me):0); if (me) { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); } else packet_append_string(packet,prefs_get_servername()); packet_append_string(packet,text); break; case message_type_talk: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_TALK); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); packet_append_string(packet,text); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_BROADCAST); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); packet_append_string(packet,text); } break; case message_type_channel: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_CHANNEL); { t_channel const * channel; if (!(channel = conn_get_channel(me))) bn_int_set(&packet->u.server_message.flags,0); else bn_int_set(&packet->u.server_message.flags,cflags_to_bncflags(channel_get_flags(channel))); } bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatname(me); packet_append_string(packet,tname); conn_unget_chatname(me,tname); packet_append_string(packet,text); } break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_USERFLAGS); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet, playerinfo); } break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_WHISPERACK); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); packet_append_string(packet,text); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_WHISPERACK); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { packet_append_string(packet,"your friends"); packet_append_string(packet,text); } break; case message_type_channelfull: /* FIXME */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_CHANNELFULL); bn_int_set(&packet->u.server_message.flags,0); bn_int_set(&packet->u.server_message.latency,0); packet_append_string(packet,""); packet_append_string(packet,""); break; case message_type_channeldoesnotexist: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_CHANNELDOESNOTEXIST); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatname(me); packet_append_string(packet,tname); conn_unget_chatname(me,tname); packet_append_string(packet,text); } break; case message_type_channelrestricted: /* FIXME */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_CHANNELRESTRICTED); bn_int_set(&packet->u.server_message.flags,0); bn_int_set(&packet->u.server_message.latency,0); packet_append_string(packet,""); packet_append_string(packet,""); break; case message_type_info: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_INFO); bn_int_set(&packet->u.server_message.flags,0); bn_int_set(&packet->u.server_message.latency,0); packet_append_string(packet,""); packet_append_string(packet,text); break; case message_type_error: if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_ERROR); bn_int_set(&packet->u.server_message.flags,0); bn_int_set(&packet->u.server_message.latency,0); packet_append_string(packet,""); packet_append_string(packet,text); break; case message_type_emote: if (!me) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection for %s",message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL text for %s",message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type,SERVER_MESSAGE_TYPE_EMOTE); bn_int_set(&packet->u.server_message.flags,conn_get_flags(me)|dstflags); bn_int_set(&packet->u.server_message.latency,conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet,tname); conn_unget_chatcharname(me,tname); packet_append_string(packet,text); } break; default: eventlog(eventlog_level_error,__FUNCTION__,"got bad message type %d",(int)type); return -1; } return 0; } extern t_message * message_create(t_message_type type, t_connection * src, t_connection * dst, char const * text) { t_message * message; message = xmalloc(sizeof(t_message)); message->num_cached = 0; message->packets = NULL; message->classes = NULL; message->dstflags = NULL; message->mclasses = NULL; message->type = type; message->src = src; message->dst = dst; message->text = text; return message; } extern int message_destroy(t_message * message) { unsigned int i; if (!message) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL message"); return -1; } for (i=0; inum_cached; i++) if (message->packets[i]) packet_del_ref(message->packets[i]); if (message->packets) xfree(message->packets); if (message->classes) xfree(message->classes); if (message->dstflags) xfree(message->dstflags); if (message->mclasses) xfree(message->mclasses); xfree(message); return 0; } static t_packet * message_cache_lookup(t_message * message, t_connection *dst, unsigned int dstflags) { unsigned int i; t_packet * packet; t_message_class mclass; t_conn_class class; if (!message) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL message"); return NULL; } class = conn_get_class(dst); mclass = conn_get_message_class(message->src, dst); for (i=0; inum_cached; i++) if (message->classes[i]==class && message->dstflags[i]==dstflags && message->mclasses[i]==mclass) return message->packets[i]; { t_packet * * temp_packets; t_conn_class * temp_classes; unsigned int * temp_dstflags; t_message_class *temp_mclasses; if (!message->packets) temp_packets = xmalloc(sizeof(t_packet *)*(message->num_cached+1)); else temp_packets = xrealloc(message->packets,sizeof(t_packet *)*(message->num_cached+1)); if (!message->classes) temp_classes = xmalloc(sizeof(t_conn_class)*(message->num_cached+1)); else temp_classes = xrealloc(message->classes,sizeof(t_conn_class)*(message->num_cached+1)); if (!message->dstflags) temp_dstflags = xmalloc(sizeof(unsigned int)*(message->num_cached+1)); else temp_dstflags = xrealloc(message->dstflags,sizeof(unsigned int)*(message->num_cached+1)); if (!message->mclasses) temp_mclasses = xmalloc(sizeof(t_message_class)*(message->num_cached+1)); else temp_mclasses = xrealloc(message->mclasses,sizeof(t_message_class)*(message->num_cached+1)); message->packets = temp_packets; message->classes = temp_classes; message->dstflags = temp_dstflags; message->mclasses = temp_mclasses; } switch (class) { case conn_class_telnet: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error,__FUNCTION__,"could not create packet"); return NULL; } if (message_telnet_format(packet,message->type,message->src,message->dst,message->text,dstflags)<0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_bot: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error,__FUNCTION__,"could not create packet"); return NULL; } if (message_bot_format(packet,message->type,message->src,message->dst,message->text,dstflags)<0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error,__FUNCTION__,"could not create packet"); return NULL; } if (message_bnet_format(packet,message->type,message->src,dst,message->text,dstflags)<0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_irc: case conn_class_wol: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error,__FUNCTION__,"could not create packet"); return NULL; } /* irc_message_format() is in irc.c */ if (irc_message_format(packet,message->type,message->src,message->dst,message->text,dstflags)<0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_init: case conn_class_file: case conn_class_d2cs_bnetd: case conn_class_w3route: packet = NULL; break; /* cache the NULL but dont send any error, * this are normal connections */ default: eventlog(eventlog_level_error,__FUNCTION__,"unsupported connection class %d",(int)class); packet = NULL; /* we can cache the NULL too */ } message->num_cached++; message->packets[i] = packet; message->classes[i] = class; message->dstflags[i] = dstflags; message->mclasses[i] = mclass; return packet; } extern int message_send(t_message * message, t_connection * dst) { t_packet * packet; unsigned int dstflags; if (!message) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL message"); return -1; } if (!dst) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL dst connection"); return -1; } dstflags = 0; if (message->src) { char const * tname; if ((tname = conn_get_chatname(message->src)) && conn_check_ignoring(dst,tname)==1) { conn_unget_chatname(message->src,tname); dstflags |= MF_X; } if (tname) conn_unget_chatname(message->src,tname); } if (!(packet = message_cache_lookup(message,dst,dstflags))) return -1; /* FIXME: this is not needed now, message has dst */ if ((conn_get_class(dst)==conn_class_irc)||(conn_get_class(dst)==conn_class_wol)) { /* HACK: IRC message always need the recipient and are therefore bad to cache. */ /* So we only cache a pseudo packet and convert it to a real packet later ... */ packet = packet_duplicate(packet); /* we want to modify packet so we have to create a copy ... */ if (irc_message_postformat(packet,dst)<0) { packet_del_ref(packet); /* we don't need the previously created copy anymore ... */ return -1; } } conn_push_outqueue(dst,packet); if ((conn_get_class(dst)==conn_class_irc)||(conn_get_class(dst)==conn_class_wol)) packet_del_ref(packet); /* we don't need the previously created copy anymore ... */ return 0; } extern int message_send_all(t_message * message) { t_connection * c; t_elem const * curr; int rez; if (!message) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL message"); return -1; } rez = -1; LIST_TRAVERSE_CONST(connlist(),curr) { c = elem_get_data(curr); if (message_send(message,c)==0) rez = 0; } return rez; } extern int message_send_text(t_connection * dst, t_message_type type, t_connection * src, char const * text) { t_message * message; int rez; if (!dst) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection"); return -1; } if (!(message = message_create(type,src,dst,text))) return -1; rez = message_send(message,dst); message_destroy(message); return rez; } extern int message_send_admins(t_connection * src, t_message_type type, char const * text) { t_elem const * curr; t_connection * tc; int counter = 0; LIST_TRAVERSE_CONST(connlist(),curr) { tc = elem_get_data(curr); if (!tc) continue; if (account_get_auth_admin(conn_get_account(tc),NULL)==1 && tc != src) { message_send_text(tc,type,src,text); counter++; } } return counter; } extern int message_send_formatted(t_connection * dst, char const * text) { char * line; if (!dst) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection"); return -1; } if (!(line = message_format_line(dst,text))) { eventlog(eventlog_level_error,__FUNCTION__,"could not format input text \"%s\"",text); return -1; } /* caller beware: empty messages can crash Blizzard clients */ switch (line[0]) { case 'C': if (line[1]=='/') handle_command(dst,&line[1]); else if (conn_get_channel(dst) && !conn_quota_exceeded(dst,&line[1])) channel_message_send(conn_get_channel(dst),message_type_talk,dst,&line[1]); break; case 'B': message_send_text(dst,message_type_broadcast,dst,&line[1]); break; case 'E': message_send_text(dst,message_type_error,dst,&line[1]); break; case 'M': message_send_text(dst,message_type_talk,dst,&line[1]); break; case 'T': message_send_text(dst,message_type_emote,dst,&line[1]); break; case 'I': case 'W': message_send_text(dst,message_type_info,dst,&line[1]); break; default: eventlog(eventlog_level_error,__FUNCTION__,"unknown message type '%c'",line[0]); xfree(line); return -1; } xfree(line); return 0; } extern int message_send_file(t_connection * dst, FILE * fd) { char * buff; if (!dst) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection"); return -1; } if (!fd) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL fd"); return -1; } while ((buff = file_get_line(fd))) { message_send_formatted(dst,buff); } file_get_line(NULL); // clear file_get_line buffer return 0; }