diff --git a/src/map/clif.c b/src/map/clif.c index 9009d53254..20324be6a5 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -9735,88 +9735,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 - .w .w ( : ).?B 00 -/// 1 - .w .w .24B .?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 - .w .w ( : ).?B 00 +/// true - .w .w .24B .?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 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 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, @@ -9824,11 +9835,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; } @@ -10607,67 +10644,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 ) { - WFIFOW(fd,0) = 0x8e; - WFIFOW(fd,2) = textlen + 4; - safestrncpy((char*)WFIFOP(fd,4), fakename, textlen); - aFree(fakename); - } else { - 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 @@ -10950,30 +10953,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); @@ -11044,7 +11029,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; } @@ -11058,8 +11043,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; @@ -11078,7 +11062,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 ); } @@ -12755,38 +12739,13 @@ void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) /// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY). /// 0108 .W .?B ( : ) 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 ); } @@ -13354,38 +13313,16 @@ void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd){ /// Validates and processes guild messages (CZ_GUILD_CHAT). /// 017e .W .?B ( : ) 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) ); } @@ -16463,32 +16400,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 .W .?B ( : ) 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) ); }