1 /*
2  * lastlog.c: handles the lastlog features of irc.
3  *
4  * Written By Michael Sandrof and Matthew R. Green.
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2019 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: lastlog.c,v 1.61 2019/01/18 00:16:18 mrg Exp $");
37 
38 #include "lastlog.h"
39 #include "window.h"
40 #include "screen.h"
41 #include "vars.h"
42 #include "ircaux.h"
43 #include "output.h"
44 #include "ircterm.h"
45 #include "strcasestr.h"
46 
47 /* Keep this private to lastlog.c */
48 struct	lastlog_stru
49 {
50 	int	level;
51 	u_char	*msg;
52 	u_char	**lines;
53 	int	cols;	/* If this doesn't match the current columns,
54 			 * we have to recalculate the whole thing. */
55 	struct	lastlog_stru	*next;
56 	struct	lastlog_stru	*prev;
57 };
58 
59 struct lastlog_info_stru
60 {
61 	Lastlog	*lastlog_head;		/* pointer to top of lastlog list */
62 	Lastlog	*lastlog_tail;		/* pointer to bottom of lastlog list */
63 	int	lastlog_level;		/* The LASTLOG_LEVEL, determines what
64 					 * messages go to lastlog */
65 	int	lastlog_size;		/* Max number of messages for the window
66 					 * lastlog */
67 };
68 
69 struct lastlog_line_back_info
70 {
71 	int	row;
72 	Lastlog	*LogLine;
73 	Window	*window;
74 };
75 
76 static	void	remove_from_lastlog(LastlogInfo *);
77 static	void	lastlog_print_one_line(FILE *, u_char *);
78 static	void	lastlog_load(FILE *, u_char *);
79 static	void	lastlog_saveall_one_line(FILE *, Lastlog *);
80 
81 /*
82  * lastlog_level: current bitmap setting of which things should be stored in
83  * the lastlog.  The LOG_MSG, LOG_NOTICE, etc., defines tell more about this
84  */
85 static	int	lastlog_level;
86 static	int	notify_level;
87 
88 /*
89  * msg_level: the mask for the current message level.  What?  Did he really
90  * say that?  This is set in the set_lastlog_msg_level() routine as it
91  * compared to the lastlog_level variable to see if what ever is being added
92  * should actually be added
93  */
94 static	int	msg_level = LOG_CRAP;
95 
96 static	char	*levels[] =
97 {
98 	"CRAP",		"PUBLIC",	"MSGS",		"NOTICES",
99 	"WALLS",	"WALLOPS",	"NOTES",	"OPNOTES",
100 	"SNOTES",	"ACTIONS",	"DCC",		"CTCP",
101 	"USERLOG1",	"USERLOG2",	"USERLOG3",	"USERLOG4",
102 	"BEEP",		"HELP"
103 };
104 #define NUMBER_OF_LEVELS ARRAY_SIZE(levels)
105 
106 /*
107  * bits_to_lastlog_level: converts the bitmap of lastlog levels into a nice
108  * string format.
109  */
110 u_char	*
bits_to_lastlog_level(int level)111 bits_to_lastlog_level(int level)
112 {
113 	static	u_char	lbuf[128]; /* this *should* be enough for this */
114 	int	i,
115 		p;
116 	int	first = 1;
117 
118 	if (level == LOG_ALL)
119 		my_strcpy(lbuf, UP("ALL"));
120 	else if (level == 0)
121 		my_strcpy(lbuf, UP("NONE"));
122 	else
123 	{
124 		*lbuf = '\0';
125 		for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
126 		{
127 			if (level & p)
128 			{
129 				if (first)
130 					first = 0;
131 				else
132 					my_strmcat(lbuf, " ", sizeof lbuf);
133 				my_strmcat(lbuf, levels[i], sizeof lbuf);
134 			}
135 		}
136 	}
137 	return (lbuf);
138 }
139 
140 int
parse_lastlog_level(u_char * str)141 parse_lastlog_level(u_char *str)
142 {
143 	u_char	*ptr,
144 		*rest,
145 		*s;
146 	int	i,
147 		p,
148 		level,
149 		neg;
150 	size_t	len;
151 
152 	level = 0;
153 	while ((str = next_arg(str, &rest)) != NULL)
154 	{
155 		while (str)
156 		{
157 			if ((ptr = my_index(str, ',')) != NULL)
158 				*ptr++ = '\0';
159 			if ((len = my_strlen(str)) != 0)
160 			{
161 				u_char	*cmd = NULL;
162 
163 				malloc_strcpy(&cmd, str);
164 				upper(cmd);
165 				if (my_strncmp(cmd, "ALL", len) == 0)
166 					level = LOG_ALL;
167 				else if (my_strncmp(cmd, "NONE", len) == 0)
168 					level = 0;
169 				else
170 				{
171 					if (*str == '-')
172 					{
173 						str++;
174 						s = cmd + 1;
175 						neg = 1;
176 					}
177 					else {
178 						neg = 0;
179 						s = cmd;
180 					}
181 					for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
182 					{
183 						if (!my_strncmp(s, levels[i], len))
184 						{
185 							if (neg)
186 								level &= (LOG_ALL ^ p);
187 							else
188 								level |= p;
189 							break;
190 						}
191 					}
192 					if (i == NUMBER_OF_LEVELS)
193 						say("Unknown lastlog level: %s",
194 							str);
195 				}
196 				new_free(&cmd);
197 			}
198 			str = ptr;
199 		}
200 		str = rest;
201 	}
202 	return (level);
203 }
204 
205 /*
206  * set_lastlog_level: called whenever a "SET LASTLOG_LEVEL" is done.  It
207  * parses the settings and sets the lastlog_level variable appropriately.  It
208  * also rewrites the LASTLOG_LEVEL variable to make it look nice
209  */
210 void
set_lastlog_level(u_char * str)211 set_lastlog_level(u_char *str)
212 {
213 	LastlogInfo *info = window_get_lastlog_info(curr_scr_win);
214 
215 	lastlog_level = parse_lastlog_level(str);
216 	set_string_var(LASTLOG_LEVEL_VAR, bits_to_lastlog_level(lastlog_level));
217 	info->lastlog_level = lastlog_level;
218 }
219 
220 static	void
free_lastlog_lines(Lastlog * log)221 free_lastlog_lines(Lastlog *log)
222 {
223 	u_char **lines;
224 
225 	for (lines = log->lines; *lines; lines++)
226 		new_free(lines);
227 	new_free(&log->lines);
228 }
229 
230 static	void
free_lastlog_entry(Lastlog * log)231 free_lastlog_entry(Lastlog *log)
232 {
233 	new_free(&log->msg);
234 	free_lastlog_lines(log);
235 	new_free(&log);
236 }
237 
238 static	void
remove_from_lastlog(LastlogInfo * info)239 remove_from_lastlog(LastlogInfo *info)
240 {
241 	Lastlog *tmp;
242 
243 	if (info->lastlog_tail)
244 	{
245 		tmp = info->lastlog_tail->prev;
246 		free_lastlog_entry(info->lastlog_tail);
247 		info->lastlog_tail = tmp;
248 		if (tmp)
249 			tmp->next = NULL;
250 		else
251 			info->lastlog_head = NULL;
252 		info->lastlog_size--;
253 	}
254 	else
255 		info->lastlog_size = 0;
256 }
257 
258 /*
259  * set_lastlog_size: sets up a lastlog buffer of size given.  If the lastlog
260  * has gotten larger than it was before, all previous lastlog entry remain.
261  * If it get smaller, some are deleted from the end.
262  */
263 void
set_lastlog_size(int size)264 set_lastlog_size(int size)
265 {
266 	LastlogInfo *info = window_get_lastlog_info(curr_scr_win);
267 	int	i,
268 		diff;
269 
270 	if (info->lastlog_size > size)
271 	{
272 		diff = info->lastlog_size - size;
273 		for (i = 0; i < diff; i++)
274 			remove_from_lastlog(info);
275 	}
276 }
277 
278 /*
279  * lastlog_print_one_line: print one line to the lastlog file or screen,
280  * cutting off the trailing ^O (ALL_OFF) that may be present.
281  */
282 static void
lastlog_print_one_line(FILE * fp,u_char * msg)283 lastlog_print_one_line(FILE *fp, u_char *msg)
284 {
285 	size_t	len = my_strlen(msg);
286 	int	hacked = 0;
287 
288 	if (len == 0)
289 		return;
290 	if (msg[len - 1] == ALL_OFF)
291 	{
292 		msg[len - 1] = '\0';
293 		hacked = 1;
294 	}
295 	if (fp)
296 		fprintf(fp, "%s\n", msg);
297 	else
298 		put_it("%s", msg);
299 	if (hacked)
300 		msg[len - 1] = ALL_OFF;
301 }
302 
303 /*
304  * lastlog saveall files:
305  *
306  * the way to handle restarting while not losing lastlog is to
307  * store the lastlog data into a file, that is reloaded upload
308  * starting.  the way we implement this is:
309  *
310  *  - at exit time, /lastlog -saveall in each window to a file
311  *  - at load time, when each window is created, /lastlog -loadall
312  *    from this same file.
313  *
314  * need to automate this, perhaps with scripts that run at both
315  * window destroy/quit and window add (not related to /window
316  * create feature.)
317  *
318  * the format in these files is as follows:
319  *
320  *     <header>
321  *     <timestamp> <msglevel> <msg>
322  *     [repeat 2nd line]
323  *
324  * where the header includes a version check and user supplied
325  * info, possibly available in the load hook?  the timestamp will
326  * be 0 initially, as lastlog doesn't yet keep timestamps on the
327  * messages (but often in the message string).
328  *
329  * loading and saving these files will be an all/nothing thing.
330  * eg, loading in the middle will replace the current settings,
331  * though future expansion could extend this to insert at/after
332  * a certain place.
333  *
334  * <header>'s format is:
335  *   "IRCII-SAVEALL <version> [<userdata>]"
336  * eg, "IRCII-SAVEALL V1 [blah blah]".
337  */
338 
339 #define LASTLOG_SAVEALL_HEADER           "IRCII-SAVEALL"
340 #define LASTLOG_SAVEALL_HEADER_VERSION   "V1"
341 
342 /*
343  * lastlog_saveall_header: print the lastlog saveall header
344  *
345  * XXX: make the info settable
346  */
347 static void
lastlog_saveall_header(FILE * fp)348 lastlog_saveall_header(FILE *fp)
349 {
350 	// make settable
351 	const char *lastlog_saveall_info = "";
352 
353 	fprintf(fp, "%s %s [%s]\n", LASTLOG_SAVEALL_HEADER,
354 		LASTLOG_SAVEALL_HEADER_VERSION, lastlog_saveall_info);
355 }
356 
357 /*
358  * lastlog_saveall_one_line: like lastlog_print_one_line() but always
359  * print to the file, but with saveall info.
360  */
361 static void
lastlog_saveall_one_line(FILE * fp,Lastlog * line)362 lastlog_saveall_one_line(FILE *fp, Lastlog *line)
363 {
364 	size_t	len = my_strlen(line->msg);
365 	int	hacked = 0;
366 
367 	if (len == 0)
368 		return;
369 	if (line->msg[len - 1] == ALL_OFF)
370 	{
371 		line->msg[len - 1] = '\0';
372 		hacked = 1;
373 	}
374 	fprintf(fp, "%llu 0x%x %s\n", (unsigned long long) 0 /* timestamp */, line->level, line->msg);
375 	if (hacked)
376 		line->msg[len - 1] = ALL_OFF;
377 }
378 
379 /*
380  * lastlog_load: load a lastlog saveall file into the current window
381  */
382 static void
lastlog_load(FILE * fp,u_char * file)383 lastlog_load(FILE *fp, u_char *file)
384 {
385 	u_char	buffer[FS_BUFFER_SIZE];
386 	u_char	*user = NULL;
387 	u_char	*curbuf = buffer;
388 	size_t	curlen = sizeof buffer;
389 	int	got_version = 0;
390 	unsigned lineno = 0;
391 
392 	/* clear the current (if any) list */
393 	free_lastlog(curr_scr_win);
394 
395 	while (fgets(CP(curbuf), curlen, fp) != NULL)
396 	{
397 		char *ep;
398 		u_char	*p, *level, *ts, *args;
399 		unsigned long ulval;
400 		int old_level;
401 
402 		while ((p = my_index(curbuf, '\n')) == NULL)
403 		{
404 			size_t	oldlen = curlen;
405 			u_char	*newbuf;
406 
407 			curlen *= 2;
408 			if (curbuf != buffer)
409 				newbuf = new_realloc(curbuf, curlen);
410 			else
411 			{
412 				newbuf = new_malloc(curlen);
413 				memmove(newbuf, curbuf, oldlen);
414 			}
415 			curbuf = newbuf;
416 			if (fgets(CP(curbuf) + oldlen - 1, oldlen + 1, fp) == NULL)
417 				break;
418 		}
419 		if (p)
420 			*p = '\0';
421 		lineno++;
422 		args = curbuf;
423 		if (!got_version)
424 		{
425 			u_char	*header, *version;
426 			size_t	len;
427 
428 			header = next_arg(args, &args);
429 			if (!header ||
430 			    my_strcmp(header, UP(LASTLOG_SAVEALL_HEADER)) != 0)
431 			{
432 				yell("--- header wrong on args '%s %s'", header, args);
433 				goto out;
434 			}
435 
436 			version = next_arg(args, &args);
437 			if (!version ||
438 			    my_strcmp(version, UP(LASTLOG_SAVEALL_HEADER_VERSION)) != 0)
439 			{
440 				yell("--- version wrong on args '%s %s %s'", header, version, args);
441 				goto out;
442 			}
443 
444 			len = my_strlen(args);
445 			if (args[0] != '[' || args[len - 1] != ']')
446 			{
447 				yell("--- user wrong on args '%s %s %s'", header, version, args);
448 				goto out;
449 			}
450 			args[len - 1] = '\0';
451 			user = args + 1;
452 
453 			got_version = 1;
454 			continue;
455 		}
456 		args = curbuf;
457 		ts = next_arg(args, &args);
458 		if (!ts || !*ts)
459 		{
460 			yell("--- missing timestamp at line %u", lineno);
461 			continue;
462 		}
463 		level = next_arg(args, &args);
464 		if (!level || !*level)
465 		{
466 			yell("--- missing level at line %u", lineno);
467 			continue;
468 		}
469 
470 		errno = 0;
471 		ulval = strtoul(CP(level), &ep, 16);
472 		if (*ep != '\0')
473 		{
474 			yell("--- '%s' is not a base 16 number at line %u", level, lineno);
475 			continue;
476 		}
477 		if (errno == ERANGE && ulval == ULONG_MAX)
478 		{
479 			yell("--- '%s' is out of range of unsigned long at line %u", level, lineno);
480 			continue;
481 		}
482 		old_level = set_lastlog_msg_level(ulval);
483 		add_to_lastlog(curr_scr_win, args);
484 		set_lastlog_msg_level(old_level);
485 	}
486 
487 	if (user && lineno > 1)
488 		say("Loaded %u lines from %s [%s]", lineno - 1, file, user);
489 
490 out:
491 	if (curbuf != buffer)
492 		new_free(&curbuf);
493 }
494 
495 /*
496  * lastlog: the /LASTLOG command.  Displays the lastlog to the screen. If
497  * args contains a valid integer, only that many lastlog entries are shown
498  * (if the value is less than lastlog_size), otherwise the entire lastlog is
499  * displayed
500  *
501  * /lastlog -save filename
502  * by StElb <stlb@cs.tu-berlin.de>
503  */
504 void
lastlog(u_char * command,u_char * args,u_char * subargs)505 lastlog(u_char *command, u_char *args, u_char *subargs)
506 {
507 	int	cnt,
508 		from = 0,
509 		p,
510 		i,
511 		level = 0,
512 		m_level,
513 		mask = 0,
514 		header = 1;
515 	Lastlog *start_pos;
516 	u_char	*match = NULL,
517 		*save = NULL,
518 		*saveall = NULL,
519 		*expanded = NULL,
520 		*arg;
521 	FILE	*fp = NULL;
522 	u_char	*cmd = NULL;
523 	size_t	len;
524 	LastlogInfo *info = window_get_lastlog_info(curr_scr_win);
525 
526 	save_message_from();
527 	message_from(NULL, LOG_CURRENT);
528 	cnt = info->lastlog_size;
529 
530 	while ((arg = next_arg(args, &args)) != NULL)
531 	{
532 		if (*arg == '-')
533 		{
534 			arg++;
535 			if (!(len = my_strlen(arg)))
536 			{
537 				header = 0;
538 				continue;
539 			}
540 			malloc_strcpy(&cmd, arg);
541 			upper(cmd);
542 			if (!my_strncmp(cmd, "LITERAL", len))
543 			{
544 				if (match)
545 				{
546 					say("Second -LITERAL argument ignored");
547 					(void) next_arg(args, &args);
548 					continue;
549 				}
550 				if ((match = next_arg(args, &args)) != NULL)
551 					continue;
552 				say("Need pattern for -LITERAL");
553 				goto out;
554 			}
555 			else if (!my_strncmp(cmd, "BEEP", len))
556 			{
557 				if (match)
558 				{
559 					say("-BEEP is exclusive; ignored");
560 					continue;
561 				}
562 				else
563 					match = UP("\007");
564 			}
565 			else if (!my_strncmp(cmd, "SAVEALL", len))
566 			{
567 #ifdef DAEMON_UID
568 				if (getuid() == DAEMON_UID)
569 				{
570 					say("You are not permitted to use -SAVEALL flag");
571 					goto out;
572 				}
573 #endif /* DAEMON_UID */
574 				if (saveall)
575 				{
576 					say("Second -SAVEALL argument ignored");
577 					(void) next_arg(args, &args);
578 					continue;
579 				}
580 				if ((saveall = next_arg(args, &args)) != NULL)
581 				{
582 					expanded = expand_twiddle(saveall);
583 					if ((fp = fopen(CP(expanded), "w")) != NULL)
584 					{
585 						lastlog_saveall_header(fp);
586 						header = 0;
587 						continue;
588 					}
589 					say("Error opening %s: %s", save, strerror(errno));
590 					goto out;
591 				}
592 				say("Need filename for -SAVE");
593 				goto out;
594 			}
595 			else if (!my_strncmp(cmd, "LOADALL", len))
596 			{
597 #ifdef DAEMON_UID
598 				if (getuid() == DAEMON_UID)
599 				{
600 					say("You are not permitted to use -LOADALL flag");
601 					goto out;
602 				}
603 #endif /* DAEMON_UID */
604 				if ((saveall = next_arg(args, &args)) != NULL)
605 				{
606 					expanded = expand_twiddle(saveall);
607 					if ((fp = fopen(CP(expanded), "r")) != NULL)
608 						lastlog_load(fp, expanded);
609 					else
610 						say("Error opening %s: %s", save,
611 						    strerror(errno));
612 				}
613 				goto out;
614 			}
615 			else if (!my_strncmp(cmd, "SAVE", len))
616 			{
617 #ifdef DAEMON_UID
618 				if (getuid() == DAEMON_UID)
619 				{
620 					say("You are not permitted to use -SAVE flag");
621 					goto out;
622 				}
623 #endif /* DAEMON_UID */
624 				if (save)
625 				{
626 					say("Second -SAVE argument ignored");
627 					(void) next_arg(args, &args);
628 					continue;
629 				}
630 				if ((save = next_arg(args, &args)) != NULL)
631 				{
632 					expanded = expand_twiddle(save);
633 					if ((fp = fopen(CP(expanded), "w")) != NULL)
634 						continue;
635 					say("Error opening %s: %s", save, strerror(errno));
636 					goto out;
637 				}
638 				say("Need filename for -SAVE");
639 				goto out;
640 			}
641 			else
642 			{
643 				for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
644 				{
645 					if (my_strncmp(cmd, levels[i], len) == 0)
646 					{
647 						mask |= p;
648 						break;
649 					}
650 				}
651 				if (i == NUMBER_OF_LEVELS)
652 				{
653 					say("Unknown flag: %s", arg);
654 					message_from(NULL, LOG_CRAP);
655 					goto out;
656 				}
657 			}
658 			continue;
659 out:
660 			new_free(&expanded);
661 			new_free(&cmd);
662 			restore_message_from();
663 			if (fp)
664 				fclose(fp);
665 			return;
666 		}
667 		else
668 		{
669 			if (level == 0)
670 			{
671 				if (match || isdigit(*arg))
672 				{
673 					cnt = my_atoi(arg);
674 					level++;
675 				}
676 				else
677 					match = arg;
678 			}
679 			else if (level == 1)
680 			{
681 				from = my_atoi(arg);
682 				level++;
683 			}
684 		}
685 	}
686 	if (cmd)
687 		new_free(&cmd);
688 
689 	if (saveall && (save || match || mask || cnt != info->lastlog_size))
690 	{
691 		say("-SAVEALL does not work with -save or any matching");
692 		goto out;
693 	}
694 
695 	start_pos = info->lastlog_head;
696 	for (i = 0; (i < from) && start_pos; start_pos = start_pos->next)
697 		if (!mask || (mask & start_pos->level))
698 			i++;
699 
700 	for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->next)
701 		if (!mask || (mask & start_pos->level))
702 			i++;
703 
704 	level = info->lastlog_level;
705 	m_level = set_lastlog_msg_level(0);
706 	if (start_pos == NULL)
707 		start_pos = info->lastlog_tail;
708 	else
709 		start_pos = start_pos->prev;
710 
711 	/* Let's not get confused here, display a seperator.. -lynx */
712 	if (header && !save)
713 		say("Lastlog:");
714 	for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->prev)
715 	{
716 		if (saveall)
717 			lastlog_saveall_one_line(fp, start_pos);
718 		else if (!mask || (mask & start_pos->level))
719 		{
720 			i++;
721 			if (!match ||
722 			    my_strcasestr(start_pos->msg, match))
723 				lastlog_print_one_line(fp, start_pos->msg);
724 		}
725 	}
726 	if (save)
727 	{
728 		say("Saved Lastlog to %s", expanded);
729 		fclose(fp);
730 	}
731 	else if (header)
732 		say("End of Lastlog");
733 	new_free(&expanded);
734 	set_lastlog_msg_level(m_level);
735 	restore_message_from();
736 }
737 
738 /* set_lastlog_msg_level: sets the message level for recording in the lastlog */
739 int
set_lastlog_msg_level(int level)740 set_lastlog_msg_level(int level)
741 {
742 	int	old;
743 
744 	old = msg_level;
745 	msg_level = level;
746 	return (old);
747 }
748 
749 /*
750  * add_to_lastlog: adds the line to the lastlog, based upon the current
751  * lastlog level.  if added, the split up lines ready for display are
752  * returned.
753  */
754 u_char	**
add_to_lastlog(Window * window,u_char * line)755 add_to_lastlog(Window *window, u_char *line)
756 {
757 	Lastlog *new = NULL;
758 	LastlogInfo *info;
759 
760 	if (window == NULL)
761 		window = curr_scr_win;
762 	info = window_get_lastlog_info(window);
763 	if (info->lastlog_level & msg_level)
764 	{
765 		/* no nulls or empty lines (they contain "> ") */
766 		if (line /*&& ((int) my_strlen(line) > 2)*/)
767 		{
768 			new = new_malloc(sizeof *new);
769 			new->next = info->lastlog_head;
770 			new->prev = NULL;
771 			new->level = msg_level;
772 			new->msg = NULL;
773 			malloc_strcpy(&new->msg, line);
774 			copy_window_size(NULL, &new->cols);
775 			Debug(DB_LASTLOG, "columns = %d", new->cols);
776 			new->lines = split_up_line_alloc(line);
777 
778 			if (info->lastlog_head)
779 				info->lastlog_head->prev = new;
780 			info->lastlog_head = new;
781 
782 			if (info->lastlog_tail == NULL)
783 				info->lastlog_tail = info->lastlog_head;
784 
785 			if (info->lastlog_size++ == get_int_var(LASTLOG_VAR))
786 				remove_from_lastlog(info);
787 		}
788 	}
789 	if (new)
790 		return new->lines;
791 	return NULL;
792 }
793 
794 int
islogged(Window * window)795 islogged(Window	*window)
796 {
797 	LastlogInfo *info = window_get_lastlog_info(window);
798 
799 	return (info->lastlog_level & msg_level) ? 1 : 0;
800 }
801 
802 int
real_notify_level(void)803 real_notify_level(void)
804 {
805 	return (notify_level);
806 }
807 
808 int
real_lastlog_level(void)809 real_lastlog_level(void)
810 {
811 	return (lastlog_level);
812 }
813 
814 void
set_notify_level(u_char * str)815 set_notify_level(u_char *str)
816 {
817 	notify_level = parse_lastlog_level(str);
818 	set_string_var(NOTIFY_LEVEL_VAR, bits_to_lastlog_level(notify_level));
819 	window_set_notify_level(curr_scr_win, notify_level);
820 }
821 
822 /*
823  * free_lastlog: This frees all data and structures associated with the
824  * lastlog of the given window
825  */
826 void
free_lastlog(Window * window)827 free_lastlog(Window *window)
828 {
829 	Lastlog *tmp, *next;
830 	LastlogInfo *info = window_get_lastlog_info(window);
831 
832 	for (tmp = info->lastlog_head; tmp; tmp = next)
833 	{
834 		next = tmp->next;
835 		free_lastlog_entry(tmp);
836 	}
837 }
838 
839 /*
840  * lastlog_line_back():
841  *
842  * initially call lastlog_line_back_alloc().  then, each call to
843  * lastlog_line_back() will return the next line of the screen output,
844  * going backwards through the lastlog history.  call
845  * lastlog_line_back_free() when done.  uses split_up_line() from earlier
846  * in this file to do the actual splitting of each individiual line.
847  */
848 
849 LastlogLineBackInfo *
lastlog_line_back_alloc(Window * window)850 lastlog_line_back_alloc(Window *window)
851 {
852 	LastlogLineBackInfo *info = new_malloc(sizeof *info);
853 	LastlogInfo *linfo = window_get_lastlog_info(window);
854 
855 	info->LogLine = linfo->lastlog_head;
856 	info->row = -1;
857 
858 	info->window = NULL;
859 	lastlog_line_back(info);
860 	info->window = window;
861 
862 	return info;
863 }
864 
865 void
lastlog_line_back_free(LastlogLineBackInfo * info)866 lastlog_line_back_free(LastlogLineBackInfo *info)
867 {
868 	new_free(&info);
869 }
870 
871 u_char	*
lastlog_line_back(LastlogLineBackInfo * info)872 lastlog_line_back(LastlogLineBackInfo *info)
873 {
874 	if (info->row <= 0)
875 	{
876 		int cols;
877 
878 		if (info->window && info->LogLine)
879 			info->LogLine = info->LogLine->next;
880 		if (!info->LogLine)
881 			return NULL;
882 		copy_window_size(NULL, &cols);
883 		Debug(DB_LASTLOG, "save cols %d, new cols %d", info->LogLine->cols, cols);
884 		if (info->LogLine->cols != cols) {
885 			/* Must free and re-calculate */
886 			free_lastlog_lines(info->LogLine);
887 			info->LogLine->cols = cols;
888 			info->LogLine->lines = split_up_line_alloc(info->LogLine->msg);
889 		}
890 		for (info->row = 0; info->LogLine->lines[info->row]; info->row++)
891 			/* count the rows */;
892 		if (!info->window)
893 			return NULL;
894 	}
895 	return info->LogLine->lines[--info->row];
896 }
897 
898 LastlogInfo *
lastlog_new_window(void)899 lastlog_new_window(void)
900 {
901 	LastlogInfo *new = new_malloc(sizeof *new);
902 
903 	new->lastlog_head = 0;
904 	new->lastlog_tail = 0;
905 	new->lastlog_size = 0;
906 	new->lastlog_level = real_lastlog_level();
907 
908 	return new;
909 }
910 
911 int
lastlog_get_size(LastlogInfo * info)912 lastlog_get_size(LastlogInfo *info)
913 {
914 	return info->lastlog_size;
915 }
916 
917 int
lastlog_get_level(LastlogInfo * info)918 lastlog_get_level(LastlogInfo *info)
919 {
920 	return info->lastlog_level;
921 }
922 
923 void
lastlog_set_level(LastlogInfo * info,int level)924 lastlog_set_level(LastlogInfo *info, int level)
925 {
926 	info->lastlog_level = level;
927 }
928