Sfoglia il codice sorgente

* Corrected string lengths according to bugreport:198
- CHATBOX_SIZE: 70 -> 70+1
- removed some too aggressive checks in clif_parse_globalmessage()
- removed CHAT_SIZE define as it actually doesn't apply anywhere
- added CHAT_SIZE_MAX to serve as a custom limit to input string lengths
- added length/contents checks to /b and /lb (against fake names)

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@11386 54d463be-8e91-2dee-dedb-b68131a5f0ec

ultramage 17 anni fa
parent
commit
52a7948f8b
8 ha cambiato i file con 164 aggiunte e 172 eliminazioni
  1. 8 1
      Changelog-Trunk.txt
  2. 3 3
      src/map/atcommand.c
  3. 92 83
      src/map/clif.c
  4. 29 55
      src/map/log.c
  5. 1 1
      src/map/log.h
  6. 6 7
      src/map/map.h
  7. 20 20
      src/map/script.c
  8. 5 2
      src/map/skill.c

+ 8 - 1
Changelog-Trunk.txt

@@ -3,6 +3,13 @@ Date	Added
 AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
 AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
 
+2007/10/09
+	* Corrected string lengths according to bugreport:198 [ultramage]
+	- CHATBOX_SIZE: 70 -> 70+1
+	- removed some too aggressive checks in clif_parse_globalmessage()
+	- removed CHAT_SIZE define as it actually doesn't apply anywhere
+	- added CHAT_SIZE_MAX to serve as a custom limit to input string lengths
+	- added length/contents checks to /b and /lb (against fake names)
 2007/10/08
 2007/10/08
 	* Delayed the check for required items when a skill is cast to when they 
 	* Delayed the check for required items when a skill is cast to when they 
 	  are consumed. Now skills only fail due to lack of items after being cast.
 	  are consumed. Now skills only fail due to lack of items after being cast.
@@ -20,7 +27,7 @@ IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 	* Removed that ridiculous spinner that displays during map/npc loading
 	* Removed that ridiculous spinner that displays during map/npc loading
 	- and added a more informative progress indicator (idea from jA/eapp)
 	- and added a more informative progress indicator (idea from jA/eapp)
 	* Removed loads of code that supported these functions
 	* Removed loads of code that supported these functions
-	- -100b per npc => -1,5MB of wasted memory
+	- -230b per npc => -3MB of wasted memory
 	* Fixed related npcs that erroneously used 'stoptimer'
 	* Fixed related npcs that erroneously used 'stoptimer'
 	* Checked/fixed/removed some old script and npc commands
 	* Checked/fixed/removed some old script and npc commands
 	+ cmdothernpc
 	+ cmdothernpc

+ 3 - 3
src/map/atcommand.c

@@ -906,7 +906,7 @@ int atcommand_config_read(const char *cfgName)
  *------------------------------------------*/
  *------------------------------------------*/
 int atcommand_commands(const int fd, struct map_session_data* sd, const char* command, const char* message)
 int atcommand_commands(const int fd, struct map_session_data* sd, const char* command, const char* message)
 {
 {
-	char cz_line_buff[CHATBOX_SIZE+1];
+	char cz_line_buff[CHATBOX_SIZE];
 
 
 	register char *lpcz_cur = cz_line_buff;
 	register char *lpcz_cur = cz_line_buff;
 	register unsigned int ui_slen;
 	register unsigned int ui_slen;
@@ -914,7 +914,7 @@ int atcommand_commands(const int fd, struct map_session_data* sd, const char* co
 	int i_cur_cmd,gm_lvl = pc_isGM(sd), count = 0;
 	int i_cur_cmd,gm_lvl = pc_isGM(sd), count = 0;
 
 
 	memset(cz_line_buff,' ',CHATBOX_SIZE);
 	memset(cz_line_buff,' ',CHATBOX_SIZE);
-	cz_line_buff[CHATBOX_SIZE] = 0;
+	cz_line_buff[CHATBOX_SIZE-1] = 0;
 
 
 	clif_displaymessage(fd, msg_txt(273));
 	clif_displaymessage(fd, msg_txt(273));
 
 
@@ -932,7 +932,7 @@ int atcommand_commands(const int fd, struct map_session_data* sd, const char* co
 			clif_displaymessage(fd,(char*)cz_line_buff);
 			clif_displaymessage(fd,(char*)cz_line_buff);
 			lpcz_cur = cz_line_buff;
 			lpcz_cur = cz_line_buff;
 			memset(cz_line_buff,' ',CHATBOX_SIZE);
 			memset(cz_line_buff,' ',CHATBOX_SIZE);
-			cz_line_buff[CHATBOX_SIZE] = 0;
+			cz_line_buff[CHATBOX_SIZE-1] = 0;
 		}
 		}
 
 
 		memcpy(lpcz_cur,atcommand_info[i_cur_cmd].command,ui_slen);
 		memcpy(lpcz_cur,atcommand_info[i_cur_cmd].command,ui_slen);

+ 92 - 83
src/map/clif.c

@@ -6923,12 +6923,11 @@ void clif_emotion(struct block_list *bl,int type)
 void clif_talkiebox(struct block_list* bl, const char* talkie)
 void clif_talkiebox(struct block_list* bl, const char* talkie)
 {
 {
 	unsigned char buf[86];
 	unsigned char buf[86];
-
 	nullpo_retv(bl);
 	nullpo_retv(bl);
 
 
-	WBUFW(buf,0)=0x191;
-	WBUFL(buf,2)=bl->id;
-	memcpy(WBUFP(buf,6),talkie,MESSAGE_SIZE);
+	WBUFW(buf,0) = 0x191;
+	WBUFL(buf,2) = bl->id;
+	safestrncpy((char*)WBUFP(buf,6),talkie,MESSAGE_SIZE);
 	clif_send(buf,packet_len(0x191),bl,AREA);
 	clif_send(buf,packet_len(0x191),bl,AREA);
 }
 }
 
 
@@ -8212,7 +8211,7 @@ void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
 
 
 /*==========================================
 /*==========================================
  * Validates and processes global messages
  * Validates and processes global messages
- * S 008c/00f3 <packet len>.w <text>.?B (<name> : <message>)
+ * S 008c/00f3 <packet len>.w <text>.?B (<name> : <message>) 00
  *------------------------------------------*/
  *------------------------------------------*/
 void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 {
 {
@@ -8223,12 +8222,12 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 	// basic structure checks
 	// basic structure checks
 	if( packetlen > RFIFOREST(fd) )
 	if( packetlen > RFIFOREST(fd) )
 	{	// there has to be enough data to read
 	{	// there has to be enough data to read
-		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!", sd->status.name);
+		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
 		return;
 		return;
 	}
 	}
 	if( packetlen < 4 + 1 )
 	if( packetlen < 4 + 1 )
 	{	// 4-byte header and at least an empty string is expected
 	{	// 4-byte header and at least an empty string is expected
-		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (no message data)!", sd->status.name);
+		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (no message data)!\n", sd->status.name);
 		return;
 		return;
 	}
 	}
 
 
@@ -8242,41 +8241,31 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 		name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
 		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.
 		//Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
-		ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message using an incorrect name! Forcing a relog...", sd->status.name);
+		ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
 		clif_setwaitclose(fd); // Just kick them out to correct it.
 		clif_setwaitclose(fd); // Just kick them out to correct it.
 		return;
 		return;
 	}
 	}
 
 
 	message = text + namelen + 3;
 	message = text + namelen + 3;
-	messagelen = textlen - namelen - 3 - 1; // this should be the message length
+	messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included)
 	// verify <message> part of the packet
 	// verify <message> part of the packet
-	if( message[messagelen] != '\0' )
+	if( message[messagelen-1] != '\0' )
 	{	// message must be zero-terminated
 	{	// message must be zero-terminated
-		ShowWarning("clif_parse_GlobalMessage: Player '%s' sent an unterminated string!", sd->status.name);
+		ShowWarning("clif_parse_GlobalMessage: Player '%s' sent an unterminated string!\n", sd->status.name);
 		return;		
 		return;		
 	}
 	}
-	if( messagelen != strnlen(message, messagelen+1) )
+	if( messagelen != strnlen(message, messagelen)+1 )
 	{	// the declared length must match real length
 	{	// the declared length must match real length
-		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!", sd->status.name);
+		ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
 		return;		
 		return;		
 	}
 	}
-	if( messagelen > CHATBOX_SIZE )
+	if( messagelen > CHAT_SIZE_MAX )
 	{	// messages mustn't be too long
 	{	// messages mustn't be too long
-		int i;
-		// special case here - allow some more freedom for frost joke & dazzler 
-		// TODO:? You could use a state flag when FrostJoke/Scream is used, and unset it once the skill triggers. [Skotlex]
-		ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, sd->ud.skilltimerskill[i] == 0 || sd->ud.skilltimerskill[i]->skill_id == BA_FROSTJOKE || sd->ud.skilltimerskill[i]->skill_id == DC_SCREAM );
-
-		if( i == MAX_SKILLTIMERSKILL || !sd->ud.skilltimerskill[i])
-		{	// normal message, too long
-			ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!", sd->status.name, CHATBOX_SIZE, message);
-			return;
-		}
-		if( messagelen > 255 )
-		{	// frost joke/dazzler, but still too long
-			ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!", sd->status.name, 255, message);
-			return;
-		}
+		// Normally you can only enter CHATBOX_SIZE-1 chars into the chat box, but Frost Joke / Dazzler's text can be longer.
+		// Neither the official client nor server place any restriction on the length of the text in the packet,
+		// but we'll only allow reasonably long strings here. This also makes sure all strings fit into the `chatlog` table.
+		ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX, message);
+		return;
 	}
 	}
 
 
 	if( is_atcommand(fd, sd, text) != AtCommand_None || is_charcommand(fd, sd, text) != CharCommand_None )
 	if( is_atcommand(fd, sd, text) != AtCommand_None || is_charcommand(fd, sd, text) != CharCommand_None )
@@ -8771,26 +8760,40 @@ void clif_parse_Wis(int fd, struct map_session_data* sd)
 
 
 /*==========================================
 /*==========================================
  * /b
  * /b
+ * S 0099 <packet len>.w <text>.?B (<name>: <message>) 00
  *------------------------------------------*/
  *------------------------------------------*/
-void clif_parse_GMmessage(int fd, struct map_session_data *sd)
+void clif_parse_GMmessage(int fd, struct map_session_data* sd)
 {
 {
-	char* mes;
-	int size, lv;
+	char *text, *name, *message;
+	unsigned int textlen, namelen, messagelen;
+	int lv;
 
 
 	if (battle_config.atc_gmonly && !pc_isGM(sd))
 	if (battle_config.atc_gmonly && !pc_isGM(sd))
 		return;
 		return;
 	if (pc_isGM(sd) < (lv=get_atcommand_level(AtCommand_Broadcast)))
 	if (pc_isGM(sd) < (lv=get_atcommand_level(AtCommand_Broadcast)))
 		return;
 		return;
 
 
-	size = RFIFOW(fd,2)-4;
-	mes = (char*)RFIFOP(fd,4);
-	mes_len_check(mes, size, CHAT_SIZE);
+	text = (char*)RFIFOP(fd,4);
+	textlen = RFIFOW(fd,2) - 4;
+
+	name = text;
+	namelen = strnlen(sd->status.name, NAME_LENGTH - 1);
+	// verify <name> part of the packet
+	if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
+		name[namelen] != ':' || name[namelen+1] != ' ' ) // followed by ': '
+		return;
+
+	// make sure the <message> part of the packet is safe to handle
+	message = text + namelen + 2;
+	messagelen = textlen - namelen - 2; // this should be the message length (w/ zero byte included)
+	mes_len_check(message, messagelen, CHATBOX_SIZE);
+
+	intif_GMmessage(text, textlen, 0);
 
 
-	intif_GMmessage(mes, size, 0);
 	if(log_config.gm && lv >= log_config.gm) {
 	if(log_config.gm && lv >= log_config.gm) {
-		char message[CHAT_SIZE+4];
-		sprintf(message, "/b %s", mes);
-		log_atcommand(sd, message);
+		char msg[CHATBOX_SIZE+4];
+		sprintf(msg, "/b %s", message);
+		log_atcommand(sd, msg);
 	}
 	}
 }
 }
 
 
@@ -9468,8 +9471,7 @@ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, int skilll
 			return;
 			return;
 		}
 		}
 		//You can't use Graffiti/TalkieBox AND have a vending open, so this is safe.
 		//You can't use Graffiti/TalkieBox AND have a vending open, so this is safe.
-		memcpy(sd->message, RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
-		sd->message[MESSAGE_SIZE-1] = '\0'; //Overflow protection [Skotlex]
+		safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
 	}
 	}
 
 
 	if (sd->ud.skilltimer != -1)
 	if (sd->ud.skilltimer != -1)
@@ -9649,34 +9651,30 @@ void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
 }
 }
 
 
 /*==========================================
 /*==========================================
- *
+ * Value entered into a NPC input box.
+ * S 0143 <npcID>.l <amount>.l
  *------------------------------------------*/
  *------------------------------------------*/
 void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
 void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
 {
 {
-	sd->npc_amount=(int)RFIFOL(fd,6);
-	npc_scriptcont(sd,RFIFOL(fd,2));
+	int npcid = RFIFOL(fd,2);
+	int amount = (int)RFIFOL(fd,6);
+
+	sd->npc_amount = amount;
+	npc_scriptcont(sd, npcid);
 }
 }
 
 
 /*==========================================
 /*==========================================
- *
+ * Text entered into a NPC input box.
+ * S 01d5 <len>.w <npcID>.l <input>.?B 00
  *------------------------------------------*/
  *------------------------------------------*/
-void clif_parse_NpcStringInput(int fd,struct map_session_data *sd)
+void clif_parse_NpcStringInput(int fd, struct map_session_data* sd)
 {
 {
-	short message_len;
-	message_len = RFIFOW(fd,2)-7;
-
-	if(message_len < 1)
-		return; //Blank message?
-
-	if(message_len >= sizeof(sd->npc_str)){
-		ShowWarning("clif: input string too long !\n");
-		message_len = sizeof(sd->npc_str);
-	}
+	int message_len = RFIFOW(fd,2)-8;
+	int npcid = RFIFOL(fd,4);
+	const char* message = (char*)RFIFOP(fd,8);
 
 
-	// Exploit prevention if crafted packets (without null) is being sent. [Lance]
-	memcpy(sd->npc_str,RFIFOP(fd,8),message_len); 
-	sd->npc_str[message_len-1]=0;
-	npc_scriptcont(sd,RFIFOL(fd,4));
+	safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE));
+	npc_scriptcont(sd, npcid);
 }
 }
 
 
 /*==========================================
 /*==========================================
@@ -9775,31 +9773,46 @@ void clif_parse_ResetChar(int fd, struct map_session_data *sd)
 }
 }
 
 
 /*==========================================
 /*==========================================
- * 019c /lb“™
+ * /lb
+ * S 019c <packet len>.w <text>.?B (<name>: <message>) 00
  *------------------------------------------*/
  *------------------------------------------*/
-void clif_parse_LGMmessage(int fd, struct map_session_data *sd)
+void clif_parse_LGMmessage(int fd, struct map_session_data* sd)
 {
 {
-	unsigned char buf[CHAT_SIZE+4];
-	char *mes;
-	int len, lv;
+	char *text, *name, *message;
+	unsigned int textlen, namelen, messagelen;
+
+	unsigned char buf[CHATBOX_SIZE+4];
+	int lv;
 
 
 	if (battle_config.atc_gmonly && !pc_isGM(sd))
 	if (battle_config.atc_gmonly && !pc_isGM(sd))
 		return;
 		return;
 	if (pc_isGM(sd) < (lv=get_atcommand_level(AtCommand_LocalBroadcast)))
 	if (pc_isGM(sd) < (lv=get_atcommand_level(AtCommand_LocalBroadcast)))
 		return;
 		return;
 
 
-	len = RFIFOW(fd,2) - 4;
-	mes = (char*)RFIFOP(fd,4);
-	mes_len_check(mes, len, CHAT_SIZE);
+	text = (char*)RFIFOP(fd,4);
+	textlen = RFIFOW(fd,2) - 4;
+
+	name = text;
+	namelen = strnlen(sd->status.name, NAME_LENGTH - 1);
+	// verify <name> part of the packet
+	if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
+		name[namelen] != ':' || name[namelen+1] != ' ' ) // followed by ': '
+		return;
+
+	// make sure the <message> part of the packet is safe to handle
+	message = text + namelen + 2;
+	messagelen = textlen - namelen - 2; // this should be the message length (w/ zero byte included)
+	mes_len_check(message, messagelen, CHATBOX_SIZE);
 
 
 	WBUFW(buf,0) = 0x9a;
 	WBUFW(buf,0) = 0x9a;
-	WBUFW(buf,2) = len+4;
-	memcpy(WBUFP(buf,4), mes, len);
+	WBUFW(buf,2) = textlen+4;
+	memcpy(WBUFP(buf,4), text, textlen);
 	clif_send(buf, WBUFW(buf,2), &sd->bl, ALL_SAMEMAP);
 	clif_send(buf, WBUFW(buf,2), &sd->bl, ALL_SAMEMAP);
+
 	if(log_config.gm && lv >= log_config.gm) {
 	if(log_config.gm && lv >= log_config.gm) {
-		char message[CHAT_SIZE+5];
-		sprintf(message, "/lb %s", mes);
-		log_atcommand(sd, message);
+		char msg[CHATBOX_SIZE+5];
+		sprintf(msg, "/lb %s", message);
+		log_atcommand(sd, msg);
 	}
 	}
 }
 }
 
 
@@ -10040,12 +10053,10 @@ void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd)
  *------------------------------------------*/
  *------------------------------------------*/
 void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
 void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
 {
 {
-	char* message;
-	int len;
+	int len = RFIFOW(fd,2) - 4;
+	char* message = (char*)RFIFOP(fd,4);
 
 
-	len = RFIFOW(fd,2) - 4;
-	message = (char*)RFIFOP(fd,4);
-	mes_len_check(message, len, CHAT_SIZE);
+	mes_len_check(message, len, CHATBOX_SIZE);
 
 
 	if (is_charcommand(fd, sd, message) != CharCommand_None || is_atcommand(fd, sd, message) != AtCommand_None)
 	if (is_charcommand(fd, sd, message) != CharCommand_None || is_atcommand(fd, sd, message) != AtCommand_None)
 		return;
 		return;
@@ -10309,12 +10320,10 @@ void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd)
  *------------------------------------------*/
  *------------------------------------------*/
 void clif_parse_GuildMessage(int fd, struct map_session_data* sd)
 void clif_parse_GuildMessage(int fd, struct map_session_data* sd)
 {
 {
-	char* message;
-	int len;
+	int len = RFIFOW(fd,2) - 4;
+	char* message = (char*)RFIFOP(fd,4);
 
 
-	len = RFIFOW(fd,2) - 4;
-	message = (char*)RFIFOP(fd,4);
-	mes_len_check(message, len, CHAT_SIZE);
+	mes_len_check(message, len, CHATBOX_SIZE);
 
 
 	if (is_charcommand(fd, sd, message) != CharCommand_None || is_atcommand(fd, sd, message) != AtCommand_None)
 	if (is_charcommand(fd, sd, message) != CharCommand_None || is_atcommand(fd, sd, message) != AtCommand_None)
 		return;
 		return;

+ 29 - 55
src/map/log.c

@@ -66,7 +66,7 @@ int log_branch(struct map_session_data *sd)
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd);
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		SqlStmt* stmt;
 		SqlStmt* stmt;
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		stmt = SqlStmt_Malloc(logmysql_handle);
@@ -104,18 +104,16 @@ int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int a
 		return 0; //we skip logging this item set - it doesn't meet our logging conditions [Lupus]
 		return 0; //we skip logging this item set - it doesn't meet our logging conditions [Lupus]
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
-		if (itm == NULL) {
-			//We log common item
+		if( itm == NULL ) { //We log common item
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
 				log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapindex_id2name(sd->mapindex)) )
 				log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapindex_id2name(sd->mapindex)) )
 			{
 			{
 				Sql_ShowDebug(logmysql_handle);
 				Sql_ShowDebug(logmysql_handle);
 				return 0;
 				return 0;
 			}
 			}
-		} else {
-			//We log Extended item
+		} else { //We log Extended item
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
 				log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapindex_id2name(sd->mapindex)) )
 				log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapindex_id2name(sd->mapindex)) )
 			{
 			{
@@ -134,15 +132,10 @@ int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int a
 		time(&curtime);
 		time(&curtime);
 		strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
 		strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
 
 
-		if (itm == NULL) {
-		//We log common item
-			fprintf(logfp,"%s - %d\t%s\t%d,%d,%s\n",
-				timestring, sd->status.char_id, type, nameid, amount, mapindex_id2name(sd->mapindex));
-
-		} else {
-		//We log Extended item
-			fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s\n",
-				timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapindex_id2name(sd->mapindex));
+		if( itm == NULL ) { //We log common item
+			fprintf(logfp,"%s - %d\t%s\t%d,%d,%s\n", timestring, sd->status.char_id, type, nameid, amount, mapindex_id2name(sd->mapindex));
+		} else { //We log Extended item
+			fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s\n", timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapindex_id2name(sd->mapindex));
 		}
 		}
 		fclose(logfp);
 		fclose(logfp);
 	}
 	}
@@ -166,18 +159,16 @@ int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount,
 		mapname="";
 		mapname="";
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
-		if (itm==NULL) {
-		//We log common item
+		if( itm == NULL ) { //We log common item
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
 				log_config.log_pick_db, md->class_, type, nameid, amount, mapname) )
 				log_config.log_pick_db, md->class_, type, nameid, amount, mapname) )
 			{
 			{
 				Sql_ShowDebug(logmysql_handle);
 				Sql_ShowDebug(logmysql_handle);
 				return 0;
 				return 0;
 			}
 			}
-		} else {
-		//We log Extended item
+		} else { //We log Extended item
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
 			if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
 				log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname) )
 				log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname) )
 			{
 			{
@@ -196,15 +187,10 @@ int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount,
 		time(&curtime);
 		time(&curtime);
 		strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
 		strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
 
 
-		if (itm==NULL) {
-		//We log common item
-			fprintf(logfp,"%s - %d\t%s\t%d,%d,%s\n",
-				timestring, md->class_, type, nameid, amount, mapname);
-
-		} else {
-		//We log Extended item
-			fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s\n",
-				timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
+		if( itm == NULL ) { //We log common item
+			fprintf(logfp,"%s - %d\t%s\t%d,%d,%s\n", timestring, md->class_, type, nameid, amount, mapname);
+		} else { //We log Extended item
+			fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s\n", timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
 		}
 		}
 		fclose(logfp);
 		fclose(logfp);
 	}
 	}
@@ -220,7 +206,7 @@ int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *s
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd);
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
 		if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
 			 log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex)) )
 			 log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex)) )
@@ -252,7 +238,7 @@ int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd);
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ",
 		if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ",
 			log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex)) )
 			log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex)) )
@@ -285,16 +271,10 @@ int log_atcommand(struct map_session_data* sd, const char* message)
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd);
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		SqlStmt* stmt;
 		SqlStmt* stmt;
 
 
-		if (strlen(message) > CHAT_SIZE) {
-			if (battle_config.error_log)
-				ShowError("log atcommand: Received message too long from player %s (%d:%d)!\n", sd->status.name, sd->status.account_id, sd->status.char_id);
-			return 0;
-		}
-
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_gm_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) )
 		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_gm_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) )
 		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, sd->status.name, strnlen(sd->status.name, NAME_LENGTH))
 		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, sd->status.name, strnlen(sd->status.name, NAME_LENGTH))
@@ -330,7 +310,7 @@ int log_npc(struct map_session_data* sd, const char* message)
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd);
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		SqlStmt* stmt;
 		SqlStmt* stmt;
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		stmt = SqlStmt_Malloc(logmysql_handle);
@@ -380,20 +360,14 @@ int log_chat(const char* type, int type_id, int src_charid, int src_accid, const
 		return 0; //Deactivated
 		return 0; //Deactivated
 
 
 #ifndef TXT_ONLY
 #ifndef TXT_ONLY
-	if(log_config.sql_logs > 0)
+	if( log_config.sql_logs )
 	{
 	{
 		SqlStmt* stmt;
 		SqlStmt* stmt;
 		
 		
-		if (strlen(message) > CHAT_SIZE) {
-			if (battle_config.error_log)
-				ShowError("log chat: Received message too long from type %d (%d:%d)!\n", type_id, src_accid, src_charid);
-			return 0;
-		}
-
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		stmt = SqlStmt_Malloc(logmysql_handle);
 		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', ?, ?)", log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y)
 		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', ?, ?)", log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y)
 		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (char*)dst_charname, safestrnlen(dst_charname, NAME_LENGTH))
 		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (char*)dst_charname, safestrnlen(dst_charname, NAME_LENGTH))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (char*)message, safestrnlen(message, CHAT_SIZE))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (char*)message, safestrnlen(message, CHAT_SIZE_MAX))
 		||  SQL_SUCCESS != SqlStmt_Execute(stmt) )
 		||  SQL_SUCCESS != SqlStmt_Execute(stmt) )
 		{
 		{
 			SqlStmt_ShowDebug(stmt);
 			SqlStmt_ShowDebug(stmt);
@@ -456,7 +430,7 @@ int log_config_read(char *cfgName)
 				if (log_config.enable_logs&1) //Log everything.
 				if (log_config.enable_logs&1) //Log everything.
 					log_config.enable_logs=0xFFFFFFFF;
 					log_config.enable_logs=0xFFFFFFFF;
 			} else if(strcmpi(w1,"sql_logs") == 0) {
 			} else if(strcmpi(w1,"sql_logs") == 0) {
-				log_config.sql_logs = (atoi(w2));
+				log_config.sql_logs = (bool)atoi(w2);
 //start of common filter settings
 //start of common filter settings
 			} else if(strcmpi(w1,"rare_items_log") == 0) {
 			} else if(strcmpi(w1,"rare_items_log") == 0) {
 				log_config.rare_items_log = (atoi(w2));
 				log_config.rare_items_log = (atoi(w2));
@@ -517,31 +491,31 @@ int log_config_read(char *cfgName)
 
 
 			else if(strcmpi(w1, "log_branch_file") == 0) {
 			else if(strcmpi(w1, "log_branch_file") == 0) {
 				strcpy(log_config.log_branch, w2);
 				strcpy(log_config.log_branch, w2);
-				if(log_config.branch > 0 && log_config.sql_logs < 1)
+				if(log_config.branch > 0 && !log_config.sql_logs)
 					ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
 					ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
 			} else if(strcmpi(w1, "log_pick_file") == 0) {
 			} else if(strcmpi(w1, "log_pick_file") == 0) {
 				strcpy(log_config.log_pick, w2);
 				strcpy(log_config.log_pick, w2);
-				if(log_config.filter > 0 && log_config.sql_logs < 1)
+				if(log_config.filter > 0 && !log_config.sql_logs)
 					ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
 					ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
 			} else if(strcmpi(w1, "log_zeny_file") == 0) {
 			} else if(strcmpi(w1, "log_zeny_file") == 0) {
 				strcpy(log_config.log_zeny, w2);
 				strcpy(log_config.log_zeny, w2);
-				if(log_config.zeny > 0 && log_config.sql_logs < 1)
+				if(log_config.zeny > 0 && !log_config.sql_logs)
 					ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
 					ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
 			} else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
 			} else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
 				strcpy(log_config.log_mvpdrop, w2);
 				strcpy(log_config.log_mvpdrop, w2);
-				if(log_config.mvpdrop > 0 && log_config.sql_logs < 1)
+				if(log_config.mvpdrop > 0 && !log_config.sql_logs)
 					ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
 					ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
 			} else if(strcmpi(w1, "log_gm_file") == 0) {
 			} else if(strcmpi(w1, "log_gm_file") == 0) {
 				strcpy(log_config.log_gm, w2);
 				strcpy(log_config.log_gm, w2);
-				if(log_config.gm > 0 && log_config.sql_logs < 1)
+				if(log_config.gm > 0 && !log_config.sql_logs)
 					ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
 					ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
 			} else if(strcmpi(w1, "log_npc_file") == 0) {
 			} else if(strcmpi(w1, "log_npc_file") == 0) {
 				strcpy(log_config.log_npc, w2);
 				strcpy(log_config.log_npc, w2);
-				if(log_config.npc > 0 && log_config.sql_logs < 1)
+				if(log_config.npc > 0 && !log_config.sql_logs)
 					ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
 					ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
 			} else if(strcmpi(w1, "log_chat_file") == 0) {
 			} else if(strcmpi(w1, "log_chat_file") == 0) {
 				strcpy(log_config.log_chat, w2);
 				strcpy(log_config.log_chat, w2);
-				if(log_config.chat > 0 && log_config.sql_logs < 1)					
+				if(log_config.chat > 0 && !log_config.sql_logs)					
 					ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
 					ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
 			//support the import command, just like any other config
 			//support the import command, just like any other config
 			} else if(strcmpi(w1,"import") == 0) {
 			} else if(strcmpi(w1,"import") == 0) {

+ 1 - 1
src/map/log.h

@@ -41,7 +41,7 @@ enum log_what {
 extern struct Log_Config {
 extern struct Log_Config {
 	enum log_what enable_logs;
 	enum log_what enable_logs;
 	int filter;
 	int filter;
-	int sql_logs;
+	bool sql_logs;
 	int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter
 	int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter
 	int branch, drop, mvpdrop, zeny, gm, npc, chat;
 	int branch, drop, mvpdrop, zeny, gm, npc, chat;
 	char log_branch[64], log_pick[64], log_zeny[64], log_mvpdrop[64], log_gm[64], log_npc[64], log_chat[64];
 	char log_branch[64], log_pick[64], log_zeny[64], log_mvpdrop[64], log_gm[64], log_npc[64], log_chat[64];

+ 6 - 7
src/map/map.h

@@ -163,16 +163,15 @@ enum {
 	MAPID_BABY_SOUL_LINKER,
 	MAPID_BABY_SOUL_LINKER,
 };
 };
 
 
-//Max size when inputting a string with those 'npc input boxes'
-//(also used for Graffiti, Talkie Box, Vending, and Chatrooms)
-#define MESSAGE_SIZE 80
+//Max size for inputs to Graffiti, Talkie Box and Vending text prompts
+#define MESSAGE_SIZE (79 + 1)
 //String length you can write in the 'talking box'
 //String length you can write in the 'talking box'
-#define CHATBOX_SIZE 70
-//Talk max size: <name> : <message of 70> [Skotlex]
-#define CHAT_SIZE (NAME_LENGTH + 3 + CHATBOX_SIZE)
+#define CHATBOX_SIZE (70 + 1)
 //Chatroom-related string sizes
 //Chatroom-related string sizes
 #define CHATROOM_TITLE_SIZE (36 + 1)
 #define CHATROOM_TITLE_SIZE (36 + 1)
 #define CHATROOM_PASS_SIZE (8 + 1)
 #define CHATROOM_PASS_SIZE (8 + 1)
+//Max allowed chat text length
+#define CHAT_SIZE_MAX 150
 
 
 #define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000
 #define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000
 
 
@@ -610,7 +609,7 @@ struct map_session_data {
 	int npc_menu;
 	int npc_menu;
 	int npc_amount;
 	int npc_amount;
 	struct script_state *st;
 	struct script_state *st;
-	char npc_str[256];
+	char npc_str[CHATBOX_SIZE]; // for passing npc input box text to script engine
 	int npc_timer_id; //For player attached npc timers. [Skotlex]
 	int npc_timer_id; //For player attached npc timers. [Skotlex]
 	unsigned int chatID;
 	unsigned int chatID;
 	time_t idletime;
 	time_t idletime;

+ 20 - 20
src/map/script.c

@@ -5334,29 +5334,29 @@ BUILDIN_FUNC(input)
 		return 1;
 		return 1;
 	}
 	}
 
 
-	if(sd->state.menu_or_input){
-		sd->state.menu_or_input=0;
-		if( postfix=='$' )
+	if( !sd->state.menu_or_input )
+	{	// first invocation, display npc input box
+		sd->state.menu_or_input = 1;
+		st->state = RERUNLINE;
+		if( postfix == '$' )
+			clif_scriptinputstr(sd,st->oid);
+		else	
+			clif_scriptinput(sd,st->oid);
+	}
+	else
+	{	// take received text/value and store it in the designated variable
+		sd->state.menu_or_input = 0;
+		if( postfix == '$' )
 		{
 		{
-			set_reg(st,sd,num,name,(void*)sd->npc_str,
-				script_getref(st,2));
-			return 0;
+			set_reg(st,sd,num,name,(void*)sd->npc_str,script_getref(st,2));
+		}
+		else
+		{
+			// limit the input to a non-negative value smaller than 'vending_max_value' (for scripts that didn't check this)
+			sd->npc_amount = cap_value(sd->npc_amount, 0, battle_config.vending_max_value);
+			set_reg(st,sd,num,name,(void*)sd->npc_amount,script_getref(st,2));
 		}
 		}
-		// Yor, Lupus & Fritz have messed with this.
-		// Basicly it prevents negative input since most scripts do not account for them.
-		sd->npc_amount = cap_value(sd->npc_amount, 0, battle_config.vending_max_value);
-
-		set_reg(st,sd,num,name,(void*)sd->npc_amount,
-			script_getref(st,2));
-		return 0;
 	}
 	}
-	//state.menu_or_input = 0
-	st->state=RERUNLINE;
-	if( postfix=='$' )
-		clif_scriptinputstr(sd,st->oid);
-	else	
-		clif_scriptinput(sd,st->oid);
-	sd->state.menu_or_input=1;
 	return 0;
 	return 0;
 }
 }
 
 

+ 5 - 2
src/map/skill.c

@@ -2729,8 +2729,8 @@ int skill_addtimerskill (struct block_list *src, unsigned int tick, int target,
 	ud = unit_bl2ud(src);
 	ud = unit_bl2ud(src);
 	nullpo_retr(1, ud);
 	nullpo_retr(1, ud);
 	
 	
-	for(i=0;i<MAX_SKILLTIMERSKILL && ud->skilltimerskill[i]; i++);
-	if (i==MAX_SKILLTIMERSKILL) return 1;
+	ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, ud->skilltimerskill[i] == 0 );
+	if( i == MAX_SKILLTIMERSKILL ) return 1;
 	
 	
 	ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill);
 	ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill);
 	ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i);
 	ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i);
@@ -4393,7 +4393,10 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case DC_SCREAM:
 	case DC_SCREAM:
 		clif_skill_nodamage(src,bl,skillid,skilllv,1);
 		clif_skill_nodamage(src,bl,skillid,skilllv,1);
 		skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag);
 		skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag);
+
 		if (md) {
 		if (md) {
+			// custom hack to make the mob display the skill, because these skills don't show the skill use text themselves
+			//NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches)
 			char temp[128];
 			char temp[128];
 			if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120)
 			if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120)
 				break; //Message won't fit on buffer. [Skotlex]
 				break; //Message won't fit on buffer. [Skotlex]