1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <time.h>
5 #include "text.h"
6 #include "achievements.h"
7 #include "actors.h"
8 #include "asc.h"
9 #include "books.h"
10 #include "buddy.h"
11 #include "chat.h"
12 #include "console.h"
13 #include "consolewin.h"
14 #include "elconfig.h"
15 #include "errors.h"
16 #include "filter.h"
17 #include "gl_init.h"
18 #include "hud_misc_window.h"
19 #include "highlight.h"
20 #include "init.h"
21 #include "items.h"
22 #include "knowledge.h"
23 #include "lights.h"
24 #include "main.h"
25 #include "misc.h"
26 #include "multiplayer.h"
27 #include "password_manager.h"
28 #include "paste.h"
29 #include "pm_log.h"
30 #include "translate.h"
31 #include "astrology.h"
32 #include "url.h"
33 #include "counters.h"
34 #include "io/elpathwrapper.h"
35 #include "spells.h"
36 #include "serverpopup.h"
37 #include "sky.h"
38 #include "sound.h"
39 #include "trade_log.h"
40 #include "actor_scripts.h"
41 #include "emotes.h"
42
43 int emote_filter=0;
44 int summoning_filter=0;
45
46 text_message display_text_buffer[DISPLAY_TEXT_BUFFER_SIZE];
47 int last_message = -1;
48 Uint8 current_filter = FILTER_ALL;
49
50 text_message input_text_line;
51
52 char last_pm_from[32];
53
54 static Uint32 last_server_message_time;
55
56 int show_timestamp = 0;
57
58 int dark_channeltext = 0;
59
60 /* Impliment the glow perk hud indicator state. */
61 static int glow_perk_check_state = 0;
62 static int glow_perk_active = 0;
63 static int glow_perk_unavailable = 1;
64 static Uint32 glow_perk_timer = 0;
65
check_glow_perk(void)66 void check_glow_perk(void) { glow_perk_check_state = 3; }
glow_perk_is_active(void)67 int glow_perk_is_active(void) { return glow_perk_active; };
68
69 /*
70 * Called each frame by the hud indicator code if the glow perk indcator is enabled.
71 */
glow_perk_is_unavailable(void)72 int glow_perk_is_unavailable(void)
73 {
74 /*
75 * The first #glow will be sent when a new login happens. When the
76 * message text arrives, another #glow will be sent after a timeout.
77 * By togginging twice, we get the starting state and preserve it.
78 */
79 if (glow_perk_check_state == 3)
80 {
81 send_input_text_line("#glow", 5);
82 glow_perk_unavailable = 1;
83 glow_perk_check_state--;
84 }
85 else if (glow_perk_timer && ((SDL_GetTicks()-glow_perk_timer) > 1500))
86 {
87 send_input_text_line("#glow", 5);
88 glow_perk_timer = 0;
89 }
90 return glow_perk_unavailable;
91 };
92
93 /*
94 * Called when we receive #glow command text.
95 */
set_glow_status(int value)96 static int set_glow_status(int value)
97 {
98 glow_perk_active = value;
99 if (glow_perk_check_state)
100 {
101 if (--glow_perk_check_state)
102 glow_perk_timer = SDL_GetTicks();
103 else
104 glow_perk_unavailable = 0;
105 return 1;
106 }
107 glow_perk_unavailable = 0;
108 return 0;
109 }
110 /* End glow perk hud indicator state. */
111
112 static int is_special_day = 0;
today_is_special_day(void)113 int today_is_special_day(void) { return is_special_day; };
set_today_is_special_day(void)114 void set_today_is_special_day(void) { is_special_day = 1; };
clear_today_is_special_day(void)115 void clear_today_is_special_day(void) { is_special_day = 0; };
116
117 /* functions to count, return and clear the number of PM or MODPM seen */
118 static int seen_pm_count = 0;
inc_seen_pm_count(void)119 static void inc_seen_pm_count(void) { seen_pm_count++; }
get_seen_pm_count(void)120 int get_seen_pm_count(void) { return seen_pm_count; }
clear_seen_pm_count(void)121 void clear_seen_pm_count(void) { seen_pm_count = 0; }
122
123 int log_chat = LOG_SERVER;
124
125 FILE *chat_log=NULL;
126 FILE *srv_log=NULL;
127
128 #ifndef NEW_SOUND
129 int afk_snd_warning = 0;
130 #endif
131
alloc_text_message_data(text_message * msg,int size)132 void alloc_text_message_data (text_message *msg, int size)
133 {
134 msg->data = size > 0 ? calloc (size, 1) : NULL;
135 msg->size = size;
136 msg->len = 0;
137 }
138
resize_text_message_data(text_message * msg,int len)139 void resize_text_message_data (text_message *msg, int len)
140 {
141 if (msg->size <= len)
142 {
143 int nsize = msg->size ? 2 * msg->size : len+1;
144 while (nsize < len)
145 nsize += nsize;
146 msg->data = realloc (msg->data, nsize);
147 msg->size = nsize;
148 }
149 }
150
set_text_message_data(text_message * msg,const char * data)151 void set_text_message_data (text_message *msg, const char* data)
152 {
153 if (data == NULL || data[0] == '\0')
154 {
155 clear_text_message_data (msg);
156 }
157 else if (msg->size > 0)
158 {
159 safe_strncpy (msg->data, data, msg->size);
160 msg->len = strlen (msg->data);
161 }
162 }
163
init_text_buffers()164 void init_text_buffers ()
165 {
166 int i;
167
168 for (i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
169 init_text_message (display_text_buffer + i, 0);
170
171 init_text_message (&input_text_line, MAX_TEXT_MESSAGE_LENGTH + 1);
172 input_text_line.chan_idx = CHAT_ALL;
173 set_text_message_color (&input_text_line, 1.0f, 1.0f, 1.0f);
174 }
175
cleanup_text_buffers(void)176 void cleanup_text_buffers(void)
177 {
178 int i;
179
180 free_text_message_data (&input_text_line);
181 for(i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
182 free_text_message_data (display_text_buffer + i);
183 }
184
open_chat_log()185 void open_chat_log(){
186 char starttime[200], sttime[200];
187 struct tm *l_time; time_t c_time;
188
189 char chat_log_file[100];
190 char srv_log_file[100];
191
192 time(&c_time);
193 l_time = localtime(&c_time);
194
195 if (get_rotate_chat_log())
196 {
197 char logsuffix[7];
198 strftime(logsuffix, sizeof(logsuffix), "%Y%m", l_time);
199 safe_snprintf (chat_log_file, sizeof (chat_log_file), "chat_log_%s.txt", logsuffix);
200 safe_snprintf (srv_log_file, sizeof (srv_log_file), "srv_log_%s.txt", logsuffix);
201 }
202 else
203 {
204 safe_strncpy(chat_log_file, "chat_log.txt", sizeof(chat_log_file));
205 safe_strncpy(srv_log_file, "srv_log.txt", sizeof(srv_log_file));
206 }
207
208 chat_log = open_file_config (chat_log_file, "a");
209 if (log_chat == LOG_SERVER || log_chat == LOG_SERVER_SEPERATE)
210 srv_log = open_file_config (srv_log_file, "a");
211 if (chat_log == NULL)
212 {
213 LOG_TO_CONSOLE(c_red3, "Unable to open log file to write. We will NOT be recording anything.");
214 log_chat = LOG_NONE;
215 return;
216 }
217 else if ((log_chat == LOG_SERVER || log_chat == LOG_SERVER_SEPERATE) && srv_log == NULL)
218 {
219 LOG_TO_CONSOLE(c_red3, "Unable to open server log file to write. We will fall back to recording everything in chat_log.txt.");
220 log_chat = LOG_CHAT;
221 return;
222 }
223 strftime(sttime, sizeof(sttime), "\n\nLog started at %Y-%m-%d %H:%M:%S localtime", l_time);
224 safe_snprintf(starttime, sizeof(starttime), "%s (%s)\n\n", sttime, tzname[l_time->tm_isdst>0]);
225 fwrite (starttime, strlen(starttime), 1, chat_log);
226 }
227
228
timestamp_chat_log()229 void timestamp_chat_log(){
230 char starttime[200], sttime[200];
231 struct tm *l_time; time_t c_time;
232
233 if(log_chat == LOG_NONE) {
234 return; //we're not logging anything
235 }
236
237 if (chat_log == NULL){
238 open_chat_log();
239 } else {
240 time(&c_time);
241 l_time = localtime(&c_time);
242 strftime(sttime, sizeof(sttime), "Hourly time-stamp: log continued at %Y-%m-%d %H:%M:%S localtime", l_time);
243 safe_snprintf(starttime, sizeof(starttime), "%s (%s)\n", sttime, tzname[l_time->tm_isdst>0]);
244 fwrite (starttime, strlen(starttime), 1, chat_log);
245 }
246 }
247
248
write_to_log(Uint8 channel,const Uint8 * const data,int len)249 void write_to_log (Uint8 channel, const Uint8* const data, int len)
250 {
251 int i, j;
252 Uint8 ch;
253 char str[1024];
254 struct tm *l_time; time_t c_time;
255 FILE *fout;
256
257 #ifdef NEW_SOUND
258 // Check if this string matches text we play a sound for
259 check_sound_alerts(data, len, channel);
260 #endif // NEW_SOUND
261
262 if(log_chat == LOG_NONE || (channel == CHAT_SERVER && log_chat == LOG_CHAT))
263 // We're not logging at all, or this is a server message and
264 // we're not logging those
265 return;
266
267 if (chat_log == NULL){
268 open_chat_log();
269 if(chat_log == NULL){
270 return;
271 }
272 }
273
274 // The file we'll write to
275 fout = (channel == CHAT_SERVER && log_chat >= 3) ? srv_log : chat_log;
276
277 if(!show_timestamp)
278 {
279 // Start filling the buffer with the time stamp
280 time (&c_time);
281 l_time = localtime (&c_time);
282 j = strftime (str, sizeof(str), "[%H:%M:%S] ", l_time);
283 }
284 else
285 {
286 //we already have a time stamp
287 j=0;
288 }
289
290 i = 0;
291 while (i < len)
292 {
293 for ( ; i < len && j < sizeof (str) - 1; i++)
294 {
295 ch = data[i];
296
297 // remove colorization and soft wrapping characters when
298 // writing to the chat log
299 if (!is_color (ch) && ch != '\r')
300 str[j++] = ch;
301 }
302 if (i >= len) str[j++]='\n';
303
304 fwrite (str, j, 1, fout);
305
306 // start again at the beginning of the buffer
307 j = 0;
308 }
309
310 // Flush the file, so the content is written even when EL crashes.
311 fflush (fout);
312 }
313
send_input_text_line(char * line,int line_len)314 void send_input_text_line (char *line, int line_len)
315 {
316 char str[256];
317 int i,j;
318 int len;
319 Uint8 ch;
320
321 change_to_channel_tab(line);
322
323 if ( caps_filter && line_len > 4 && my_isupper (line, -1) )
324 my_tolower (line);
325
326 i=0;
327 j=1;
328 if (line[0] != '/' && line[0] != char_slash_str[0]) //we don't have a PM
329 {
330 str[0] = RAW_TEXT;
331 }
332 else
333 {
334 str[0] = SEND_PM;
335 i++; // skip the leading /
336 }
337
338 for ( ; i < line_len && j < sizeof (str) - 1; i++) // copy it, but ignore the enter
339 {
340 ch = line[i];
341 if (ch != '\n' && ch != '\r')
342 {
343 str[j] = ch;
344 j++;
345 }
346 }
347 str[j] = 0; // always a NULL at the end
348
349 len = strlen (&str[1]);
350 if (my_tcp_send (my_socket, (Uint8*)str, len+1) < len+1)
351 {
352 //we got a nasty error, log it
353 }
354
355 return;
356 }
357
match_emote(emote_dict * command,actor * act,int send)358 int match_emote(emote_dict *command, actor *act, int send)
359 {
360 hash_entry *match;
361
362 // Try to match the input against an emote command and actor type
363 match=hash_get(emote_cmds,(void*)command->command);
364
365 if(match){
366 //printf("Emote <%s> sent (%p)\n",((emote_dict*)match->item)->command,((emote_dict*)match->item)->emote);
367 //SEND emote to server
368 if (send) send_emote(((emote_dict*)match->item)->emote->id);
369
370 return 1;
371 }
372 return 0;
373 }
374
375
parse_text_for_emote_commands(const char * text,int len)376 int parse_text_for_emote_commands(const char *text, int len)
377 {
378 int i=0, j = 0, wf=0,ef=0, itsme=0;
379 char name[20]; // Yeah, this should be done correctly
380 emote_dict emote_text;
381 actor *act;
382
383
384 //printf("parsing local for emotes\n");
385 //extract name
386 while(text[i]&&i<20){
387 if (is_color(text[i])) {i++; continue;}
388 name[j]=text[i];
389 if(text[i]==' ' || text[i]==':') {
390 name[j]=0;
391 if(text[i]==':') i++;
392 break;
393 }
394 i++;j++;
395 }
396
397 if(j>=20||name[j]) return 0; //out of bound or not terminated
398
399 //check if we are saying text
400 LOCK_ACTORS_LISTS();
401
402 act = get_actor_ptr_from_id(yourself);
403 if (!act){
404 UNLOCK_ACTORS_LISTS();
405 LOG_ERROR("Unable to find actor who just said local text?? name: %s", name);
406 return 1; // Eek! We don't have an actor match... o.O
407 }
408
409 if (!(!strncasecmp(act->actor_name, name, strlen(name)) &&
410 (act->actor_name[strlen(name)] == ' ' ||
411 act->actor_name[strlen(name)] == '\0'))){
412 //we are not saying this text, return
413 //UNLOCK_ACTORS_LISTS();
414 //return 0;
415 itsme=0;
416 } else itsme=1;
417
418 j=0;
419 do {
420 if (is_color(text[i])) continue;
421 if ((text[i]==' ' || text[i]==0)) {
422 if (j&&j<=MAX_EMOTE_LEN) {
423 wf++;
424 emote_text.command[j]=0;
425 ef+=match_emote(&emote_text,act,itsme);
426 } else wf+= (j) ? 1:0;
427 j=0;
428 } else {
429 if (j<MAX_EMOTE_LEN)
430 emote_text.command[j]=text[i];
431 j++;
432 }
433 } while(text[i++]);
434 //printf("ef=%i, wf=%i, filter=>%i\n",ef,wf,emote_filter);
435 UNLOCK_ACTORS_LISTS();
436
437 return ((ef==wf) ? (emote_filter):(0));
438
439 }
440
filter_or_ignore_text(char * text_to_add,int len,int size,Uint8 channel)441 int filter_or_ignore_text (char *text_to_add, int len, int size, Uint8 channel)
442 {
443 int l, idx;
444
445 if (len <= 0) return 0; // no point
446
447 //check for auto receiving #help
448 for (idx = 0; idx < len; idx++)
449 {
450 if (!is_color (text_to_add[idx])) break;
451 }
452 l = len - idx;
453 if (l >= strlen(help_request_str) && text_to_add[idx] == '#' && (strncasecmp (&text_to_add[idx], help_request_str, strlen(help_request_str)) == 0 || strncasecmp (&text_to_add[idx], "#mod chat", 9) == 0))
454 {
455 auto_open_encyclopedia = 0;
456 }
457
458 /*
459 DANGER, WILL ROBINSON!
460
461 The below code should not exist in it's present form. I'd change it,
462 but I'd need access to the server. Simply checking text output (which
463 is used for all sorts of things) for the phrase "Game Date" is very
464 dangerous. Example: what if, in the future, we allow spaces in
465 character names? Someone chooses the name "Game Date" and walks around
466 saying "hi". Everyone's clients in the area interpret this as being a
467 Game Date command.
468
469 I've made the below code not *as* dangerous. Had a user been able to
470 fake out the below code, previously, it would have caused a buffer overflow
471 in their client if they didn't write in only numbers after it. Now, they
472 won't crash; it'll just be misparsed.
473
474 General practice recommendation: don't mix server commands with user
475 input.
476
477 - Karen
478 */
479 /*
480 ed (ttlanhil): made it check if it's a server colour. still not perfect
481 (this should have been done server-side instead of parsing the date), but safer
482 */
483 if (from_color_char (text_to_add[0]) == c_green1 && !strncasecmp(text_to_add+1, "Game Date", 9))
484 {
485 //we assume that the server will still send little-endian dd/mm/yyyy... we could make it safer by parsing the format too, but it's simpler to assume
486 const char * const month_names[] = { "Aluwia", "Seedar", "Akbar", "Zartia", "Elandra", "Viasia", "Fruitfall", "Mortia", "Carnelar", "Nimlos", "Chimar", "Vespia" };
487 const char * const day_names[] = { "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th", "30th" };
488 char new_str[100];
489 const char *ptr=text_to_add;
490 short unsigned int day=1, month=1, year=0;
491 int offset = 0;
492
493 while(!isdigit(ptr[offset]))
494 {
495 offset++;
496 if (offset >= sizeof(new_str))
497 {
498 LOG_ERROR("error (1) parsing date string: %s",text_to_add);
499 //something evil this way comes...
500 return 0;
501 }
502 }
503 ptr += offset;
504
505 if (sscanf (ptr,"%hu%*[-/]%hu%*[-/]%hu",&day,&month,&year) < 3
506 || day <= 0 || month <= 0
507 || day > 30 || month > 12 || year > 9999)
508 {
509 LOG_ERROR("error (2) parsing date string: %s",text_to_add);
510 //something evil this way comes...
511 }
512 else
513 {
514 // only display initial or "#date" user requested date
515 if (!set_date(ptr))
516 {
517 safe_snprintf(new_str, sizeof(new_str), date_format, day_names[day-1], month_names[month-1], year);
518 LOG_TO_CONSOLE(c_green1, new_str);
519 }
520
521 //Calculate fraction Big Lunar month (2 conjunction months) less game clock time
522 //Represented in Degrees.
523 skybox_time_d = (SDL_GetTicks()%( 1296000 * 1000 ));
524 skybox_time_d *= 360.0/( 1296000.0 * 1000.0);
525 skybox_time_d = -skybox_time_d;
526 skybox_time_d += 360.0 * (((month%2)*30 + day-1)*360 + game_minute)/21600.0;
527 skybox_update_positions();
528 return 0;
529 }
530 }
531
532 if (from_color_char (text_to_add[0]) == c_green1 && !strncasecmp(text_to_add+1, "Game Time", 9))
533 {
534 real_game_second = atoi(&text_to_add[18]);
535 set_real_game_second_valid();
536 next_second_time = cur_time + 1000;
537 new_second();
538 }
539
540 // Check for local messages to be translated into actor movements (contains [somthing])
541 if (channel == CHAT_LOCAL)
542 {
543 if(parse_text_for_emote_commands(text_to_add, len)) return 0;
544 }
545
546 if (channel == CHAT_SERVER) {
547 if (!strncasecmp(text_to_add+1, "You started to harvest ", 23)) {
548 strncpy(harvest_name, text_to_add+1+23, len-1-23-1);
549 harvest_name[len-1-23-1] = '\0';
550 set_now_harvesting();
551 }
552 else if ((!strncasecmp(text_to_add+1, "You stopped harvesting.", 23)) ||
553 (!strncasecmp(text_to_add+1, "You can't harvest while fighting (duh)!", 39)) ||
554 (!strncasecmp(text_to_add+1, "You can't do that while trading!", 32)) ||
555 (!strncasecmp(text_to_add+1, "You can't harvest here", 22)) ||
556 (!strncasecmp(text_to_add+1, "You lack the knowledge of ", 26)) ||
557 ((!strncasecmp(text_to_add+1, "You need to wear ", 17) && strstr(text_to_add, "order to harvest") != NULL)) ||
558 ((!strncasecmp(text_to_add+1, "You need to have a ", 19) && strstr(text_to_add, "order to harvest") != NULL)))
559 {
560 clear_now_harvesting();
561 }
562 else if (!strncasecmp(text_to_add+1, "Great, you changed your password!", 33))
563 {
564 if (!passmngr_confirm_pw_change())
565 LOG_TO_CONSOLE(c_red1, passmngr_error_str);
566 }
567 else if (!strncasecmp(text_to_add+1, "Glow on!", 8))
568 {
569 if (set_glow_status(1))
570 return 0;
571 }
572 else if (!strncasecmp(text_to_add+1, "Glow off!", 9))
573 {
574 if (set_glow_status(0))
575 return 0;
576 }
577 else if (!strncasecmp(text_to_add+1, "You need the I glow in the dark perk in order to use this.", 58))
578 {
579 glow_perk_unavailable = 1;
580 if (glow_perk_check_state)
581 {
582 glow_perk_check_state = 0;
583 return 0;
584 }
585 }
586 else if (is_death_message(text_to_add+1)) {
587 // nothing to be done here cause all is done in the test function
588 }
589 else if (!strncasecmp(text_to_add+1, "You found ", 10) && strstr(text_to_add+1, " coins.")) {
590 decrement_harvest_counter(atoi(text_to_add+11));
591 }
592 else if (!strncasecmp(text_to_add+1, "Send Item UIDs ", 15)) {
593 if (text_to_add[1+15] == '0')
594 item_uid_enabled = 0;
595 else if (text_to_add[1+15] == '1')
596 item_uid_enabled = 1;
597 printf("item_uid_enabled=%d\n", item_uid_enabled);
598 }
599 else if ((copy_next_LOCATE_ME > 0) && !strncasecmp(text_to_add+1, "You are in ", 11)) {
600 char buffer[4096];
601 switch (copy_next_LOCATE_ME)
602 {
603 case 1:
604 copy_to_clipboard(text_to_add+1);
605 break;
606 case 2:
607 snprintf(buffer, sizeof(buffer), "@My Position: %s", text_to_add + 12);
608 send_input_text_line(buffer, strlen(buffer));
609 break;
610 }
611 copy_next_LOCATE_ME = 0;
612 return 0;
613 }
614 else if (!strncasecmp(text_to_add+1, "You see: ", 9)) {
615 achievements_player_name(text_to_add+10, len-10);
616 }
617 else if ((!strncasecmp(text_to_add+1, "You just got food poisoned!", 27)) ||
618 (!strncasecmp(text_to_add+1, "Oh well, no invisibility, but we got poisoned.", 46)))
619 {
620 increment_poison_incidence();
621 }
622 else if (strstr(text_to_add+1, "aborted the trade.")) {
623 trade_aborted(text_to_add+1);
624 }
625 else if (strstr(text_to_add+1, "Trade session failed")) {
626 trade_aborted(text_to_add+1);
627 }
628 else if (strstr(text_to_add+1, "You have been saved!")) {
629 last_save_time = time(NULL);
630 }
631 else if (strstr(text_to_add+1, "Day ends:") || strstr(text_to_add+1, "This day was removed by ")) {
632 clear_today_is_special_day();
633 }
634 else if (strstr(text_to_add+1, "Today is a special day:")) {
635 set_today_is_special_day();
636 }
637 else if (strstr(text_to_add+1, "You'd need a pair of binoculars to read the book from here - get closer!")) {
638 if (!book_window_is_open())
639 return 0;
640 }
641 else if (!strncasecmp(text_to_add+1, "You are researching ", 20)) {
642 if (get_true_knowledge_info(text_to_add+1))
643 return 0;
644 }
645 else if (!strncasecmp(text_to_add+1, "Your buddy list is now empty.", 29)) {
646 clear_buddy();
647 }
648
649
650 else {
651 static Uint32 last_time[] = { 0, 0 };
652 static int done_one[] = { 0, 0 };
653 int match_index = -1;
654 if (!strncasecmp(text_to_add+1, "You are too far away! Get closer!", 33))
655 match_index = 0;
656 else if (!strncasecmp(text_to_add+1, "Can't do, your target is already fighting with someone else,", 60))
657 match_index = 1;
658 if (match_index > -1)
659 {
660 Uint32 new_time = SDL_GetTicks();
661 clear_now_harvesting();
662 if(your_actor != NULL)
663 add_highlight(your_actor->x_tile_pos,your_actor->y_tile_pos, HIGHLIGHT_SOFT_FAIL);
664 /* suppress further messages within for 5 seconds of last */
665 if (done_one[match_index] && ((new_time - last_time[match_index]) < 5000))
666 return 0;
667 done_one[match_index] = 1;
668 last_time[match_index] = new_time;
669 }
670 }
671
672 } else if (channel == CHAT_LOCAL) {
673 if (now_harvesting() && !strncasecmp(text_to_add+1, get_username(), strlen(get_username()))) {
674 char *ptr = text_to_add+1+strlen(get_username());
675 if (!strncasecmp(ptr, " found a ", 9)) {
676 ptr += 9;
677 if (!strncasecmp(ptr, "bag of gold, getting ", 21)) {
678 decrement_harvest_counter(atoi(ptr+21));
679 } else if (!strstr(ptr, " could not carry ")) {
680 decrement_harvest_counter(1);
681 }
682 }
683 } else if (!strncasecmp(text_to_add+1, "(*) ", 4)) {
684 increment_summon_counter(text_to_add+1+4);
685 if (summoning_filter) return 0;
686 }
687 }
688 /* check for misc counter strings */
689 catch_counters_text(text_to_add+1);
690
691 /* put #mpm in a popup box, on top of all else */
692 if ((channel == CHAT_MODPM) && (!strncasecmp(text_to_add+1, "[Mod PM from", 12))) {
693 display_server_popup_win((const unsigned char*)text_to_add);
694 }
695
696 // look for astrology messages
697 if((channel == CHAT_SERVER) && is_astrology_message (text_to_add))
698 {
699 return 0;
700 }
701
702 //Make sure we don't check our own messages.
703 if( !(channel == CHAT_PERSONAL && len >= strlen(pm_from_str) && strncasecmp (text_to_add+1, pm_from_str, strlen(pm_from_str)) != 0) &&
704 !(channel == CHAT_MODPM && len >= strlen(mod_pm_from_str) && strncasecmp (text_to_add+1, mod_pm_from_str, strlen(mod_pm_from_str)) != 0)
705 ) {
706
707 //check if ignored - pre_check_if_ignored() checks for Mod PM's etc to not ignore (or it would be asking for trouble)
708 if (pre_check_if_ignored (text_to_add, len, channel))
709 {
710 return 0;
711 }
712 //All right, we do not ignore the person
713 if (channel == CHAT_PERSONAL || channel == CHAT_MODPM)
714 inc_seen_pm_count();
715 if (afk)
716 {
717 if (channel == CHAT_PERSONAL || channel == CHAT_MODPM)
718 {
719 // player sent us a PM
720 add_message_to_pm_log (text_to_add, len, channel);
721 if (afk_snd_warning) {
722 do_afk_sound();
723 }
724 }
725 else if (channel == CHAT_LOCAL && from_color_char (text_to_add[0]) == c_grey1 && is_talking_about_me (&text_to_add[1], len-1, 0))
726 {
727 // player mentions our name in local chat
728 if (afk_local) {
729 add_message_to_pm_log (&text_to_add[1], len - 1, channel);
730 if (afk_snd_warning) {
731 do_afk_sound();
732 }
733 } else {
734 send_afk_message (&text_to_add[1], len - 1, channel);
735 }
736 }
737 else if (channel == CHAT_SERVER)
738 {
739 // check if this was a trade attempt
740 int i;
741 for (i = 1; i < len; i++) {
742 if (text_to_add[i] == ' ' || text_to_add[i] == ':' || is_color (text_to_add[i])) {
743 break;
744 }
745 }
746 if (i < len-15 && strncasecmp (&text_to_add[i], " wants to trade", 15) == 0) {
747 send_afk_message (&text_to_add[1], len - 1, channel);
748 if (afk_snd_warning) {
749 do_afk_sound();
750 }
751 }
752 }
753 }
754 } else { //We sent this PM or MODPM. Can we expect a reply?
755 int len = 0;
756 char name[MAX_USERNAME_LENGTH];
757 for(;text_to_add[len+8] != ':' && len < MAX_USERNAME_LENGTH - 1; ++len);
758 safe_strncpy(name, text_to_add+8, len+1);
759 if(check_if_ignored(name)){
760 char msg[65];
761 safe_snprintf(msg, sizeof(msg), warn_currently_ignoring, name);
762 LOG_TO_CONSOLE(c_red2, msg);
763 }
764 }
765
766 // parse for URLs
767 find_all_url (text_to_add, len);
768
769 // look for buddy-wants-to-add-you messages
770 if(channel == CHAT_SERVER && from_color_char (text_to_add[0]) == c_green1)
771 {
772 for (l = 1; l < len; l++)
773 {
774 if (text_to_add[l] == ' ') break;
775 }
776 if (len - l >= strlen(msg_accept_buddy_str) && strncmp (&text_to_add[l], msg_accept_buddy_str, strlen(msg_accept_buddy_str)) == 0 && l <=32)
777 {
778 char name[32];
779 int i;
780 int cur_char;
781 /*entropy says: I really fail to understand the logic of that safe_snprintf. And gcc can't understand it either
782 because the name is corrupted. so we implement it the old fashioned way.
783 Grum responds: actually it's the MingW compiler on windows that doesn't understand it, because it doesn't
784 terminate the string when the buffer threatens to overflow. It works fine with gcc on Unix, and using
785 sane_safe_snprintf should also fix it on windows.
786 safe_snprintf (name, l, "%s", &text_to_add[1]);
787 */
788 for (i = 0; i < sizeof (name); i++)
789 {
790 cur_char = text_to_add[i+1];
791 name[i] = cur_char;
792 if (cur_char == ' ')
793 {
794 name[i] = '\0';
795 break;
796 }
797 }
798 add_buddy_confirmation (name);
799 }
800 }
801
802 // filter any naughty words out
803 return filter_text (text_to_add, len, size);
804 }
805
put_text_in_buffer(Uint8 channel,const Uint8 * text_to_add,int len)806 void put_text_in_buffer (Uint8 channel, const Uint8 *text_to_add, int len)
807 {
808 put_colored_text_in_buffer (c_grey1, channel, text_to_add, len);
809 }
810
811 //-- Logan Dugenoux [5/26/2004]
812 // Checks chat string, if it begins with an actor name,
813 // and the actor is displayed, put said sentence into an overtext bubble
814 #define ALLOWED_CHAR_IN_NAME(_x_) (isalnum(_x_)||(_x_=='_'))
check_chat_text_to_overtext(const Uint8 * text_to_add,int len,Uint8 channel)815 void check_chat_text_to_overtext (const Uint8 *text_to_add, int len, Uint8 channel)
816 {
817 if (!view_chat_text_as_overtext || channel != CHAT_LOCAL)
818 return; // disabled
819
820 if (from_color_char (text_to_add[0]) == c_grey1)
821 {
822 char playerName[128];
823 char textbuffer[1024];
824 int i;
825 int j;
826
827 j = 0;
828 i = 1;
829 while (text_to_add[i] < 128 && i < len)
830 {
831 if (text_to_add[i] != '[')
832 {
833 playerName[j] = (char)text_to_add[i];
834 j++;
835 if (j >= sizeof (playerName))
836 return;//over buffer
837 }
838 i++;
839 }
840
841 if (i < len)
842 {
843 playerName[j] = '\0';
844 while ( j > 0 && !ALLOWED_CHAR_IN_NAME (playerName[j]) )
845 playerName[j--] = '\0';
846 j = 0;
847 while (i < len)
848 {
849 if ( j >= sizeof (textbuffer) )
850 return;//over buffer
851 textbuffer[j] = (char) text_to_add[i];
852 i++; j++;
853 }
854 textbuffer[j] = '\0';
855 for (i = 0; i < max_actors; i++)
856 {
857 char actorName[128];
858 j = 0;
859 // Strip clan info
860 while ( ALLOWED_CHAR_IN_NAME (actors_list[i]->actor_name[j]) )
861 {
862 actorName[j] = actors_list[i]->actor_name[j];
863 j++;
864 if ( j >= sizeof (actorName) )
865 return; // over buffer
866 }
867 actorName[j] = '\0';
868 if (strcmp (actorName, playerName) == 0)
869 {
870 add_displayed_text_to_actor (actors_list[i], textbuffer);
871 break;
872 }
873 }
874 }
875 }
876 }
877
put_char_in_buffer(text_message * buf,Uint8 ch,int pos)878 int put_char_in_buffer (text_message *buf, Uint8 ch, int pos)
879 {
880 int i, nlen;
881
882 if (pos < 0 || pos > buf->len || pos >= buf->size) {
883 return 0;
884 }
885
886 // First shift everything after pos to the right
887 nlen = buf->len + 1;
888 if (nlen >= buf->size)
889 nlen = buf->size - 1;
890 buf->data[nlen] = '\0';
891 for (i = nlen - 1; i > pos; i--)
892 buf->data[i] = buf->data[i-1];
893
894 // insert the new character, and update the length
895 buf->data[pos] = ch;
896 buf->len = nlen;
897
898 return 1;
899 }
900
put_string_in_buffer(text_message * buf,const Uint8 * str,int pos)901 int put_string_in_buffer (text_message *buf, const Uint8 *str, int pos)
902 {
903 int nr_free, nr_paste, ib, jb, nb;
904 Uint8 ch;
905
906 if (pos < 0 || pos > buf->len) return 0;
907 if (str == NULL) return 0;
908
909 // find out how many characters to paste
910 nr_free = buf->size - pos - 1;
911 nr_paste = 0;
912 for (ib = 0; str[ib] && nr_paste < nr_free; ib++)
913 {
914 ch = str[ib];
915 if (is_printable (ch) || ch == '\n')
916 nr_paste++;
917 }
918
919 if (nr_paste == 0) {
920 return 0;
921 }
922
923 // now move the characters right of the cursor (if any)
924 nb = buf->len - pos;
925 if (nb > nr_free - nr_paste)
926 nb = nr_free - nr_paste;
927 if (nb > 0)
928 {
929 for (ib = nb-1; ib >= 0; ib--)
930 buf->data[pos+ib+nr_paste] = buf->data[pos+ib];
931 }
932 buf->data[pos+nb+nr_paste] = '\0';
933 buf->len = pos+nb+nr_paste;
934
935 // insert the pasted text
936 jb = 0;
937 for (ib = 0; str[ib]; ib++)
938 {
939 ch = str[ib];
940 if (ch == '\n')
941 ch = ' ';
942 if (is_printable (ch))
943 {
944 buf->data[pos+jb] = ch;
945 if (++jb >= nr_paste) break;
946 }
947 }
948
949 return nr_paste;
950 }
951
put_colored_text_in_buffer(Uint8 color,Uint8 channel,const Uint8 * text_to_add,int len)952 void put_colored_text_in_buffer (Uint8 color, Uint8 channel, const Uint8 *text_to_add, int len)
953 {
954 text_message *msg;
955 int minlen, text_color;
956 Uint32 cnr = 0, ibreak = -1, jbreak = -1;
957 char time_stamp[12];
958 struct tm *l_time; time_t c_time;
959
960 check_chat_text_to_overtext (text_to_add, len, channel);
961
962 // check for auto-length
963 if (len < 0)
964 len = strlen ((char*)text_to_add);
965
966 // set the time when we got this message
967 last_server_message_time = cur_time;
968
969 // if the buffer is full, delete some old lines and move the remainder to the front
970 if (++last_message >= DISPLAY_TEXT_BUFFER_SIZE)
971 {
972 const size_t num_move = DISPLAY_TEXT_BUFFER_SIZE - DISPLAY_TEXT_BUFFER_DEL;
973 size_t i;
974 for (i=0; i<DISPLAY_TEXT_BUFFER_DEL; i++)
975 {
976 msg = &(display_text_buffer[i]);
977 if (msg->data)
978 {
979 msg->deleted = 1;
980 update_text_windows(msg);
981 free_text_message_data (msg);
982 }
983 }
984 memmove(display_text_buffer, &display_text_buffer[DISPLAY_TEXT_BUFFER_DEL], sizeof(text_message)*num_move);
985 last_message -= DISPLAY_TEXT_BUFFER_DEL;
986 for (i = num_move; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
987 init_text_message (display_text_buffer + i, 0);
988 }
989
990 msg = &(display_text_buffer[last_message]);
991
992 // Try to make a guess at the number of wrapping newlines required,
993 // but allow al least for a null byte and up to 8 extra newlines and
994 // colour codes
995 minlen = len + 18 + (len/60);
996 if (show_timestamp)
997 {
998 minlen += 12;
999 time (&c_time);
1000 l_time = localtime (&c_time);
1001 strftime (time_stamp, sizeof(time_stamp), "[%H:%M:%S] ", l_time);
1002 }
1003 cnr = get_active_channel (channel);
1004 if (cnr != 0)
1005 // allow some space for the channel number
1006 minlen += 20;
1007 if (msg->data == NULL)
1008 alloc_text_message_data (msg, minlen);
1009 else
1010 resize_text_message_data (msg, minlen);
1011
1012 if (cnr != 0)
1013 {
1014 for (ibreak = 0; ibreak < len; ibreak++)
1015 {
1016 if (text_to_add[ibreak] == ']') break;
1017 }
1018 }
1019
1020 if (channel == CHAT_LOCAL)
1021 {
1022 for (jbreak = 0; jbreak < len; jbreak++)
1023 {
1024 if (text_to_add[jbreak] == ':' && text_to_add[jbreak+1] == ' ') break;
1025 }
1026 }
1027
1028 if (dark_channeltext==1)
1029 text_color = c_grey2;
1030 else if (dark_channeltext==2)
1031 text_color = c_grey4;
1032 else
1033 text_color = c_red1; // unexpected
1034
1035 if (ibreak >= len)
1036 {
1037 // not a channel, or something's messed up
1038 if(!is_color (text_to_add[0]))
1039 {
1040 // force the color
1041 if (show_timestamp)
1042 {
1043 safe_snprintf (msg->data, msg->size, "%c%s%.*s", to_color_char (color), time_stamp, len, text_to_add);
1044 }
1045 else
1046 {
1047 safe_snprintf (msg->data, msg->size, "%c%.*s", to_color_char (color), len, text_to_add);
1048 }
1049 }
1050 else
1051 {
1052 // color set by server
1053 if (show_timestamp)
1054 {
1055 if(dark_channeltext && channel==CHAT_LOCAL && from_color_char(text_to_add[0])==c_grey1 && jbreak < (len-3))
1056 {
1057 safe_snprintf (msg->data, msg->size, "%c%s%.*s%.*s", to_color_char (text_color), time_stamp, jbreak+1, &text_to_add[1], len-jbreak-3, &text_to_add[jbreak+3]);
1058 }
1059 else
1060 {
1061 safe_snprintf (msg->data, msg->size, "%c%s%.*s", text_to_add[0], time_stamp, len-1, &text_to_add[1]);
1062 }
1063 }
1064 else
1065 {
1066 if(dark_channeltext && channel==CHAT_LOCAL && from_color_char(text_to_add[0])==c_grey1 && jbreak < (len-3))
1067 {
1068 safe_snprintf (msg->data, msg->size, "%c%.*s%.*s", to_color_char (text_color), jbreak+1, &text_to_add[1], len-jbreak-3, &text_to_add[jbreak+3]);
1069 }
1070 else
1071 {
1072 safe_snprintf (msg->data, msg->size, "%.*s", len, text_to_add);
1073 }
1074 }
1075 }
1076 }
1077 else
1078 {
1079 char nr_str[16];
1080 int has_additional_color = is_color(text_to_add[ibreak+3]);
1081 if (cnr >= 1000000000)
1082 safe_snprintf (nr_str, sizeof (nr_str), "guild");
1083 else
1084 safe_snprintf (nr_str, sizeof (nr_str), "%u", cnr);
1085
1086 if(!is_color (text_to_add[0]))
1087 {
1088 // force the color
1089 if (show_timestamp)
1090 {
1091 if (dark_channeltext)
1092 {
1093 safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s%c%.*s", to_color_char (color), time_stamp, ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1094 }
1095 else
1096 {
1097 safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s", to_color_char (color), time_stamp, ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1098 }
1099 }
1100 else
1101 {
1102 if (dark_channeltext)
1103 {
1104 safe_snprintf (msg->data, msg->size, "%c%.*s @ %s%.*s%c%.*s", to_color_char (color), ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1105 }
1106 else
1107 {
1108 safe_snprintf (msg->data, msg->size, "%c%.*s @ %s%.*s", to_color_char (color), ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1109 }
1110 }
1111 }
1112 else
1113 {
1114 // color set by server
1115 if (show_timestamp)
1116 {
1117 if (dark_channeltext)
1118 {
1119 safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s%c%.*s", text_to_add[0], time_stamp, ibreak-1, &text_to_add[1], nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1120 }
1121 else
1122 {
1123 safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s", text_to_add[0], time_stamp, ibreak-1, &text_to_add[1], nr_str, len-ibreak, &text_to_add[ibreak]);
1124 }
1125 }
1126 else
1127 {
1128 if (dark_channeltext)
1129 {
1130 safe_snprintf (msg->data, msg->size, "%.*s @ %s%.*s%c%.*s", ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1131 }
1132 else
1133 {
1134 safe_snprintf (msg->data, msg->size, "%.*s @ %s%.*s", ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1135 }
1136 }
1137 }
1138 }
1139
1140 msg->len = strlen (msg->data);
1141 msg->chan_idx = channel;
1142 msg->channel = cnr;
1143
1144 // set invalid wrap data to force rewrapping
1145 msg->wrap_lines = 0;
1146 msg->wrap_zoom = 0.0f;
1147 msg->wrap_width = 0;
1148
1149 msg->deleted = 0;
1150 recolour_message(msg);
1151 update_text_windows(msg);
1152
1153 // log the message
1154 write_to_log (channel, (unsigned char*)msg->data, msg->len);
1155
1156 return;
1157 }
1158
1159 // find the last lines, according to the current time
find_last_lines_time(int * msg,int * offset,Uint8 filter,int width)1160 int find_last_lines_time (int *msg, int *offset, Uint8 filter, int width)
1161 {
1162 // adjust the lines_no according to the time elapsed since the last message
1163 if ( (cur_time - last_server_message_time) / 1000 > 3)
1164 {
1165 dec_lines_to_show();
1166 last_server_message_time = cur_time;
1167 }
1168 if (get_lines_to_show() <= 0) return 0;
1169
1170 find_line_nr(get_total_nr_lines(), get_total_nr_lines() - get_lines_to_show(),
1171 filter, msg, offset, CHAT_FONT, 1.0, width);
1172 return 1;
1173 }
1174
find_line_nr(int nr_lines,int line,Uint8 filter,int * msg,int * offset,font_cat font,float zoom,int width)1175 void find_line_nr(int nr_lines, int line, Uint8 filter, int *msg, int *offset,
1176 font_cat font, float zoom, int width)
1177 {
1178 int line_count = 0, lines_no = nr_lines - line;
1179 int imsg, ichar;
1180 char *data;
1181
1182 imsg = last_message;
1183 if ( imsg<0 ) {
1184 /* No data in buffer */
1185 *msg = *offset = 0;
1186 return;
1187 }
1188 do
1189 {
1190 int msgchan = display_text_buffer[imsg].chan_idx;
1191
1192 switch (msgchan) {
1193 case CHAT_LOCAL: if (!local_chat_separate) msgchan = CHAT_ALL; break;
1194 case CHAT_PERSONAL: if (!personal_chat_separate) msgchan = CHAT_ALL; break;
1195 case CHAT_GM: if (!guild_chat_separate) msgchan = CHAT_ALL; break;
1196 case CHAT_SERVER: if (!server_chat_separate) msgchan = CHAT_ALL; break;
1197 case CHAT_MOD: if (!mod_chat_separate) msgchan = CHAT_ALL; break;
1198 case CHAT_MODPM: msgchan = CHAT_ALL; break;
1199 }
1200
1201 if (msgchan == filter || msgchan == CHAT_ALL || filter == FILTER_ALL)
1202 {
1203 data = display_text_buffer[imsg].data;
1204 if (data == NULL)
1205 // Hmmm... we messed up. This should not be
1206 // happening.
1207 break;
1208
1209 rewrap_message(&display_text_buffer[imsg], font, zoom, width, NULL);
1210
1211 for (ichar = display_text_buffer[imsg].len - 1; ichar >= 0; ichar--)
1212 {
1213 if (data[ichar] == '\n' || data[ichar] == '\r')
1214 {
1215 line_count++;
1216 if (line_count >= lines_no)
1217 {
1218 *msg = imsg;
1219 *offset = ichar+1;
1220 return;
1221 }
1222 }
1223 }
1224
1225 line_count++;
1226 if (line_count >= lines_no)
1227 {
1228 *msg = imsg;
1229 *offset = 0;
1230 return;
1231 }
1232 }
1233
1234 --imsg;
1235
1236 } while (imsg >= 0 && imsg != last_message);
1237
1238 *msg = 0;
1239 *offset = 0;
1240 }
1241
clear_display_text_buffer()1242 void clear_display_text_buffer ()
1243 {
1244 int i;
1245 for (i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; ++i)
1246 {
1247 if (!display_text_buffer[i].deleted && display_text_buffer[i].data != NULL && display_text_buffer[i].data[0] != '\0'){
1248 free_text_message_data (display_text_buffer + i);
1249 }
1250 display_text_buffer[i].deleted= 1;
1251 }
1252
1253 last_message = -1;
1254 last_server_message_time = cur_time;
1255
1256 clear_console();
1257 if(use_windowed_chat == 2){
1258 clear_chat_wins();
1259 }
1260 }
1261
rewrap_message(text_message * msg,font_cat cat,float text_zoom,int width,int * cursor)1262 int rewrap_message(text_message* msg, font_cat cat, float text_zoom, int width, int *cursor)
1263 {
1264 int nlines;
1265
1266 if (msg == NULL || msg->data == NULL || msg->deleted)
1267 return 0;
1268
1269 if (msg->wrap_width != width || msg->wrap_zoom != text_zoom)
1270 {
1271 int max_line_width = 0;
1272 nlines = reset_soft_breaks((unsigned char*)msg->data, msg->len, msg->size,
1273 cat, text_zoom, width, cursor, &max_line_width);
1274 msg->len = strlen(msg->data);
1275 msg->wrap_lines = nlines;
1276 msg->wrap_width = width;
1277 msg->wrap_zoom = text_zoom;
1278 msg->max_line_width = max_line_width;
1279 } else {
1280 nlines = msg->wrap_lines;
1281 }
1282
1283 return nlines;
1284 }
1285