Merge pull request #1277 from rathena/cleanup/chat_messages
Cleaned and merged the chat functions * This addresses the issue that clients from late 2015 will not zero terminate most of their messages anymore. * Also fixes the problem with the unknown packets that were most likely caused by adding the zero termination directly into the packet buffer. Thanks to @Lemongrass3110!
This commit is contained in:
commit
a89ab2f123
321
src/map/clif.c
321
src/map/clif.c
@ -9747,88 +9747,99 @@ void clif_msg_skill(struct map_session_data* sd, uint16 skill_id, int msg_id)
|
||||
|
||||
/// Validates one global/guild/party/whisper message packet and tries to recognize its components.
|
||||
/// Returns true if the packet was parsed successfully.
|
||||
/// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00
|
||||
/// 1 - <packet id>.w <packet len>.w <name>.24B <message>.?B 00
|
||||
static bool clif_process_message(struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_) {
|
||||
char *text, *name, *message;
|
||||
unsigned int packetlen, textlen, namelen, messagelen;
|
||||
int fd = sd->fd;
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
/// Formats: false - <packet id>.w <packet len>.w (<name> : <message>).?B 00
|
||||
/// true - <packet id>.w <packet len>.w <name>.24B <message>.?B 00
|
||||
static bool clif_process_message(struct map_session_data* sd, bool whisperFormat, char* out_name, char* out_message, char* out_full_message ){
|
||||
char* seperator = " : ";
|
||||
int fd;
|
||||
struct s_packet_db* info;
|
||||
uint16 packetLength, inputLength;
|
||||
const char *input, *name, *message;
|
||||
size_t nameLength, messageLength;
|
||||
|
||||
*name_ = NULL;
|
||||
*namelen_ = 0;
|
||||
*message_ = NULL;
|
||||
*messagelen_ = 0;
|
||||
fd = sd->fd;
|
||||
|
||||
packetlen = RFIFOW(fd,info->pos[0]);
|
||||
// basic structure checks
|
||||
if( packetlen < 4 + 1 ) { // 4-byte header and at least an empty string is expected
|
||||
info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
|
||||
packetLength = RFIFOW(fd,info->pos[0]);
|
||||
input = (const char*)RFIFOP(fd,info->pos[1]);
|
||||
|
||||
// basic structure check for the 4-byte header
|
||||
if( packetLength < 4 ){
|
||||
ShowWarning("clif_process_message: Received malformed packet from player '%s' (no message data)!\n", sd->status.name);
|
||||
return false;
|
||||
}else{
|
||||
inputLength = packetLength - 4;
|
||||
}
|
||||
|
||||
text = (char*)RFIFOP(fd,info->pos[1]);
|
||||
textlen = packetlen - 4;
|
||||
|
||||
// process <name> part of the packet
|
||||
if( format == 0 ) { // name and message are separated by ' : '
|
||||
// validate name
|
||||
name = text;
|
||||
namelen = strnlen(sd->status.name, NAME_LENGTH-1); // name length (w/o zero byte)
|
||||
|
||||
if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
|
||||
name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
|
||||
{
|
||||
//Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
|
||||
ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
|
||||
set_eof(fd); // Just kick them out to correct it.
|
||||
return false;
|
||||
}
|
||||
|
||||
message = name + namelen + 3;
|
||||
messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included)
|
||||
} else {
|
||||
if( whisperFormat ) {
|
||||
// name has fixed width
|
||||
if( textlen < NAME_LENGTH + 1 ) {
|
||||
if( inputLength < NAME_LENGTH + 1 ) {
|
||||
ShowWarning("clif_process_message: Received malformed packet from player '%s' (packet length is incorrect)!\n", sd->status.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
name = input;
|
||||
|
||||
// validate name
|
||||
name = text;
|
||||
namelen = strnlen(name, NAME_LENGTH-1); // name length (w/o zero byte)
|
||||
nameLength = strnlen( name, NAME_LENGTH - 1 );
|
||||
|
||||
// only restriction is that the name must be zero-terminated
|
||||
if( name[namelen] != '\0' ) {
|
||||
if( name[nameLength] != '\0' ) {
|
||||
ShowWarning("clif_process_message: Player '%s' sent an unterminated name!\n", sd->status.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
message = name + NAME_LENGTH;
|
||||
messagelen = textlen - NAME_LENGTH; // this should be the message length (w/ zero byte included)
|
||||
message = input + NAME_LENGTH;
|
||||
messageLength = inputLength - NAME_LENGTH;
|
||||
}else{
|
||||
// name and message are separated by ' : '
|
||||
size_t seperatorLength = strnlen( seperator, NAME_LENGTH );
|
||||
|
||||
nameLength = strnlen( sd->status.name, NAME_LENGTH - 1 ); // name length (w/o zero byte)
|
||||
|
||||
// check if there's enough data provided
|
||||
if( inputLength < nameLength + seperatorLength + 1 ){
|
||||
ShowWarning("clif_process_message: Received malformed packet from player '%s' (no username data)!\n", sd->status.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
name = input;
|
||||
|
||||
// validate name
|
||||
if( strncmp( name, sd->status.name, nameLength ) || // the text must start with the speaker's name
|
||||
strncmp( name + nameLength, seperator, seperatorLength ) ) // followed by the seperator
|
||||
{
|
||||
//Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
|
||||
ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
|
||||
set_eof(sd->fd); // Just kick them out to correct it.
|
||||
return false;
|
||||
}
|
||||
|
||||
message = input + nameLength + seperatorLength;
|
||||
messageLength = inputLength - nameLength - seperatorLength;
|
||||
}
|
||||
|
||||
#if PACKETVER >= 20151001
|
||||
if (message[messagelen-1] != '\0')
|
||||
{
|
||||
message[messagelen++] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PACKETVER < 20151001
|
||||
// the declared length must match real length
|
||||
if( messagelen != strnlen(message, messagelen)+1 ) {
|
||||
if( messageLength != strnlen(message, messageLength)+1 ) {
|
||||
ShowWarning("clif_process_message: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify <message> part of the packet
|
||||
if( message[messagelen-1] != '\0' ) { // message must be zero-terminated
|
||||
if( message[messageLength-1] != '\0' ) { // message must be zero-terminated
|
||||
ShowWarning("clif_process_message: Player '%s' sent an unterminated message string!\n", sd->status.name);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// No zero termination anymore
|
||||
messageLength += 1;
|
||||
#endif
|
||||
|
||||
// messages mustn't be too long
|
||||
if( messagelen > CHAT_SIZE_MAX-1 ) {
|
||||
if( messageLength > CHAT_SIZE_MAX-1 ) {
|
||||
// Normally you can only enter CHATBOX_SIZE-1 letters into the chat box, but Frost Joke / Dazzler's text can be longer.
|
||||
// Also, the physical size of strings that use multibyte encoding can go multiple times over the chatbox capacity.
|
||||
// Neither the official client nor server place any restriction on the length of the data in the packet,
|
||||
@ -9836,11 +9847,37 @@ static bool clif_process_message(struct map_session_data* sd, int format, char**
|
||||
ShowWarning("clif_process_message: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX-1, message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it is not a whisper message, set the name to the fakename of the player
|
||||
if( whisperFormat == false && sd->fakename[0] != '\0' ){
|
||||
strcpy( out_name, sd->fakename );
|
||||
}else{
|
||||
safestrncpy( out_name, name, nameLength + 1 );
|
||||
}
|
||||
safestrncpy( out_message, message, messageLength );
|
||||
|
||||
if( whisperFormat ){
|
||||
sprintf( out_full_message, "%-24s%s", out_name, out_message );
|
||||
out_full_message[nameLength] = '\0';
|
||||
}else{
|
||||
sprintf( out_full_message, "%s%s%s", out_name, seperator, out_message );
|
||||
}
|
||||
|
||||
if( is_atcommand( fd, sd, out_message, 1 ) )
|
||||
return false;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return false; //no "chatting" while muted.
|
||||
|
||||
if( battle_config.min_chat_delay ) { //[Skotlex]
|
||||
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
|
||||
return false;
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
*name_ = name;
|
||||
*namelen_ = namelen;
|
||||
*message_ = message;
|
||||
*messagelen_ = messagelen;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -10620,69 +10657,33 @@ void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
|
||||
/// There are various variants of this packet.
|
||||
void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
|
||||
{
|
||||
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
int textlen = RFIFOW(fd,info->pos[0]) - 4;
|
||||
const char* text = (char*)RFIFOP(fd,info->pos[1]);
|
||||
|
||||
char *name, *message, *fakename = NULL;
|
||||
int namelen, messagelen;
|
||||
|
||||
bool is_fake;
|
||||
char name[NAME_LENGTH], message[CHAT_SIZE_MAX], output[CHAT_SIZE_MAX+NAME_LENGTH*2];
|
||||
size_t length;
|
||||
|
||||
// validate packet and retrieve name and message
|
||||
if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
|
||||
if( !clif_process_message(sd, false, name, message, output ) )
|
||||
return;
|
||||
|
||||
if( is_atcommand(fd, sd, message, 1) )
|
||||
return;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return; //no "chatting" while muted.
|
||||
|
||||
if( battle_config.min_chat_delay ) { //[Skotlex]
|
||||
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
|
||||
return;
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
if( sd->gcbind ) {
|
||||
channel_send(sd->gcbind,sd,message);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake Name Design by FatalEror (bug report #9)
|
||||
**/
|
||||
if( ( is_fake = ( sd->fakename[0] ) ) ) {
|
||||
fakename = (char*) aMalloc(strlen(sd->fakename)+messagelen+3);
|
||||
strcpy(fakename, sd->fakename);
|
||||
strcat(fakename, " : ");
|
||||
strcat(fakename, message);
|
||||
textlen = strlen(fakename) + 1;
|
||||
}
|
||||
// send message to others (using the send buffer for temp. storage)
|
||||
clif_GlobalMessage(&sd->bl,is_fake ? fakename : text,sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
|
||||
clif_GlobalMessage(&sd->bl,output,sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
|
||||
|
||||
length = strlen(output) + 1;
|
||||
|
||||
// send back message to the speaker
|
||||
if( is_fake ) {
|
||||
WFIFOHEAD(fd, textlen + 4);
|
||||
WFIFOW(fd,0) = 0x8e;
|
||||
WFIFOW(fd,2) = textlen + 4;
|
||||
safestrncpy((char*)WFIFOP(fd,4), fakename, textlen);
|
||||
aFree(fakename);
|
||||
} else {
|
||||
WFIFOHEAD(fd, RFIFOW(fd, info->pos[0]));
|
||||
memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,info->pos[0]));
|
||||
WFIFOW(fd,0) = 0x8e;
|
||||
}
|
||||
WFIFOHEAD(fd,4+length);
|
||||
WFIFOW(fd,0) = 0x8e;
|
||||
WFIFOW(fd,2) = 4+length;
|
||||
safestrncpy((char*)WFIFOP(fd,4), output, length );
|
||||
WFIFOSET(fd, WFIFOW(fd,2));
|
||||
|
||||
#ifdef PCRE_SUPPORT
|
||||
// trigger listening npcs
|
||||
map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl);
|
||||
map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, output, strlen(output), &sd->bl);
|
||||
#endif
|
||||
|
||||
// Chat logging type 'O' / Global Chat
|
||||
@ -10965,30 +10966,12 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
|
||||
{
|
||||
struct map_session_data* dstsd;
|
||||
int i;
|
||||
|
||||
char *target, *message;
|
||||
int namelen, messagelen;
|
||||
char target[NAME_LENGTH], message[CHAT_SIZE_MAX], output[CHAT_SIZE_MAX+NAME_LENGTH*2];
|
||||
|
||||
// validate packet and retrieve name and message
|
||||
if( !clif_process_message(sd, 1, &target, &namelen, &message, &messagelen) )
|
||||
if( !clif_process_message( sd, true, target, message, output ) )
|
||||
return;
|
||||
|
||||
if ( is_atcommand(fd, sd, message, 1) )
|
||||
return;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return; //no "chatting" while muted.
|
||||
|
||||
if (battle_config.min_chat_delay) { //[Skotlex]
|
||||
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) {
|
||||
return;
|
||||
}
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
// Chat logging type 'W' / Whisper
|
||||
log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message);
|
||||
|
||||
@ -11059,7 +11042,7 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
|
||||
// if there are 'Test' player on an other map-server and 'test' player on this map-server,
|
||||
// and if we ask for 'Test', we must not contact 'test' player
|
||||
// so, we send information to inter-server, which is the only one which decide (and copy correct name).
|
||||
intif_wis_message(sd, target, message, messagelen);
|
||||
intif_wis_message(sd, target, message, strlen(message));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -11073,8 +11056,7 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
|
||||
}
|
||||
|
||||
// if player is autotrading
|
||||
if (dstsd->state.autotrade == 1) {
|
||||
char output[256];
|
||||
if (dstsd->state.autotrade == 1){
|
||||
safesnprintf(output,sizeof(output),"%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name);
|
||||
clif_wis_message(fd, wisp_server_name, output, strlen(output) + 1);
|
||||
return;
|
||||
@ -11093,7 +11075,7 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
|
||||
clif_wis_end(fd, 0); // 0: success to send wisper
|
||||
|
||||
// Normal message
|
||||
clif_wis_message(dstsd->fd, sd->status.name, message, messagelen);
|
||||
clif_wis_message(dstsd->fd, sd->status.name, message, strlen(message)+1 );
|
||||
}
|
||||
|
||||
|
||||
@ -12770,38 +12752,13 @@ void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd)
|
||||
/// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY).
|
||||
/// 0108 <packet len>.W <text>.?B (<name> : <message>) 00
|
||||
void clif_parse_PartyMessage(int fd, struct map_session_data* sd){
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
int textlen = RFIFOW(fd,info->pos[0]) - 4;
|
||||
const char* text = (char*)RFIFOP(fd,info->pos[1]);
|
||||
|
||||
char *name, *message;
|
||||
int namelen, messagelen;
|
||||
|
||||
#if PACKETVER >= 20151001
|
||||
textlen++;
|
||||
#endif
|
||||
char name[NAME_LENGTH], message[CHAT_SIZE_MAX], output[CHAT_SIZE_MAX+NAME_LENGTH*2];
|
||||
|
||||
// validate packet and retrieve name and message
|
||||
if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
|
||||
if( !clif_process_message( sd, false, name, message, output ) )
|
||||
return;
|
||||
|
||||
if( is_atcommand(fd, sd, message, 1) )
|
||||
return;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return; //no "chatting" while muted.
|
||||
|
||||
if( battle_config.min_chat_delay )
|
||||
{ //[Skotlex]
|
||||
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
|
||||
return;
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
party_send_message(sd, text, textlen);
|
||||
party_send_message(sd, output, strlen(output) + 1 );
|
||||
}
|
||||
|
||||
|
||||
@ -13375,38 +13332,16 @@ void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd){
|
||||
/// Validates and processes guild messages (CZ_GUILD_CHAT).
|
||||
/// 017e <packet len>.W <text>.?B (<name> : <message>) 00
|
||||
void clif_parse_GuildMessage(int fd, struct map_session_data* sd){
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
int textlen = RFIFOW(fd,info->pos[0]) - 4;
|
||||
const char* text = (char*)RFIFOP(fd,info->pos[1]);
|
||||
|
||||
|
||||
char *name, *message;
|
||||
int namelen, messagelen;
|
||||
char name[NAME_LENGTH], message[CHAT_SIZE_MAX], output[CHAT_SIZE_MAX+NAME_LENGTH*2];
|
||||
|
||||
// validate packet and retrieve name and message
|
||||
if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
|
||||
if( !clif_process_message( sd, false, name, message, output ) )
|
||||
return;
|
||||
|
||||
if( is_atcommand(fd, sd, message, 1) )
|
||||
return;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return; //no "chatting" while muted.
|
||||
|
||||
if( battle_config.min_chat_delay )
|
||||
{ //[Skotlex]
|
||||
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
|
||||
return;
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
if( sd->bg_id )
|
||||
bg_send_message(sd, text, textlen);
|
||||
bg_send_message(sd, output, strlen(output) );
|
||||
else
|
||||
guild_send_message(sd, text, textlen);
|
||||
guild_send_message(sd, output, strlen(output) );
|
||||
}
|
||||
|
||||
|
||||
@ -16484,32 +16419,12 @@ void clif_bg_message(struct battleground_data *bg, int src_id, const char *name,
|
||||
/// Validates and processes battlechat messages [pakpil] (CZ_BATTLEFIELD_CHAT).
|
||||
/// 0x2db <packet len>.W <text>.?B (<name> : <message>) 00
|
||||
void clif_parse_BattleChat(int fd, struct map_session_data* sd){
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
int textlen = RFIFOW(fd,info->pos[0]) - 4;
|
||||
const char* text = (char*)RFIFOP(fd,info->pos[1]);
|
||||
char name[NAME_LENGTH], message[CHAT_SIZE_MAX], output[CHAT_SIZE_MAX+NAME_LENGTH*2];
|
||||
|
||||
char *name, *message;
|
||||
int namelen, messagelen;
|
||||
|
||||
if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
|
||||
if( !clif_process_message( sd, false, name, message, output ) )
|
||||
return;
|
||||
|
||||
if( is_atcommand(fd, sd, message, 1) )
|
||||
return;
|
||||
|
||||
if (sd->sc.cant.chat)
|
||||
return; //no "chatting" while muted.
|
||||
|
||||
if( battle_config.min_chat_delay ) {
|
||||
if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 )
|
||||
return;
|
||||
sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
|
||||
}
|
||||
|
||||
if (battle_config.idletime_option&IDLE_CHAT)
|
||||
sd->idletime = last_tick;
|
||||
|
||||
bg_send_message(sd, text, textlen);
|
||||
bg_send_message(sd, output, strlen(output) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1293,11 +1293,11 @@ int mapif_parse_WisToGM(int fd)
|
||||
char *message;
|
||||
|
||||
mes_len = RFIFOW(fd,2) - 8+NAME_LENGTH;
|
||||
message = (char *) aMalloc(mes_len);
|
||||
message = (char *) aMalloc(mes_len+1);
|
||||
|
||||
permission = RFIFOL(fd,4+NAME_LENGTH);
|
||||
safestrncpy(Wisp_name, (char*)RFIFOP(fd,4), NAME_LENGTH);
|
||||
safestrncpy(message, (char*)RFIFOP(fd,8+NAME_LENGTH), mes_len);
|
||||
safestrncpy(message, (char*)RFIFOP(fd,8+NAME_LENGTH), mes_len+1);
|
||||
// information is sent to all online GM
|
||||
map_foreachpc(mapif_parse_WisToGM_sub, permission, Wisp_name, message, mes_len);
|
||||
aFree(message);
|
||||
|
Loading…
x
Reference in New Issue
Block a user