1 /*
2  * screen.c
3  *
4  * Written By Matthew Green, based on portions of window.c
5  * by Michael Sandrof.
6  *
7  * Copyright (c) 1990 Michael Sandrof.
8  * Copyright (c) 1993-2017 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: screen.c,v 1.209 2017/07/02 14:33:19 mrg Exp $");
37 
38 #ifdef HAVE_SYS_UN_H
39 # include <sys/un.h>
40 #endif /* HAVE_SYS_UN_H */
41 
42 #ifdef HAVE_ICONV_H
43 # include <iconv.h>
44 #endif /* HAVE ICONV_H */
45 
46 #include "screen.h"
47 #include "menu.h"
48 #include "window.h"
49 #include "output.h"
50 #include "vars.h"
51 #include "server.h"
52 #include "list.h"
53 #include "ircterm.h"
54 #include "names.h"
55 #include "ircaux.h"
56 #include "input.h"
57 #include "log.h"
58 #include "hook.h"
59 #include "dcc.h"
60 #include "translat.h"
61 #include "exec.h"
62 #include "newio.h"
63 #include "parse.h"
64 #include "edit.h"
65 #include "strsep.h"
66 
67 #ifdef lines
68 #undef lines
69 #undef columns
70 #endif /* lines */
71 
72 struct	screen_stru
73 {
74 	int	screennum;
75 	Window	*current_window;
76 	unsigned int	last_window_refnum;	/* reference number of the
77 						   window that was last
78 						   the current_window */
79 	Window	*window_list;			/* List of all visible
80 						   windows */
81 	Window	*window_list_end;		/* end of visible window
82 						   list */
83 	Window	*cursor_window;			/* Last window to have
84 						   something written to it */
85 	int	visible_windows;		/* total number of windows */
86 	WindowStack *window_stack;		/* the windows here */
87 
88 	EditInfo *edit_info;			/* our edit info */
89 
90 	Screen *next;				/* the Screen list */
91 
92 	int	fdin;				/* These are the file */
93 	FILE	*fpout;				/* pointers and descriptions */
94 	int	fdout;				/* input/output */
95 	int	wserv_fd;			/* control socket for wserv */
96 
97 	Prompt	*promptlist;
98 
99 	u_char	*redirect_name;
100 	u_char	*redirect_token;
101 	int	redirect_server;
102 
103 	u_char	*tty_name;
104 	int	co, li;
105 
106 	int	old_term_co, old_term_li;
107 
108 	ScreenInputData *inputdata;
109 
110 	int	alive;
111 };
112 
113 static	int	screen_get_fdout(Screen *);
114 static	void	screen_set_fdin(Screen *, int);
115 static	void	screen_set_fpout(Screen *, FILE *);
116 static	void	screen_set_wserv_fd(Screen *, int);
117 static	void	screen_set_fdout(Screen *, int);
118 
119 /* are we currently processing a redirect command? */
120 static	int	current_in_redirect;
121 
122 static	Window	*to_window;
123 static	Screen	*current_screen;
124 static	Screen	*main_screen;
125 static	Screen	*last_input_screen;
126 
127 /* Our list of screens */
128 static	Screen	*screen_list = NULL;
129 
130 /*
131  * create_new_screen creates a new screen structure. with the help of
132  * this structure we maintain ircII windows that cross screen window
133  * boundaries.
134  */
135 Screen	*
create_new_screen(void)136 create_new_screen(void)
137 {
138 	Screen	*new = NULL,
139 		**list;
140 	static	int	refnumber = 0;
141 
142 	for (list = &screen_list; *list; list = &((*list)->next))
143 	{
144 		if (!screen_get_alive(*list))
145 		{
146 			new = *list;
147 			break;
148 		}
149 	}
150 	if (!new)
151 	{
152 		new = new_malloc(sizeof *new);
153 		new->inputdata = NULL;
154 		new->screennum = ++refnumber;
155 		new->next = screen_list;
156 		screen_list = new;
157 	}
158 	new->last_window_refnum = 1;
159 	new->window_list = NULL;
160 	new->window_list_end = NULL;
161 	new->cursor_window = NULL;
162 	new->current_window = NULL;
163 	new->visible_windows = 0;
164 	new->window_stack = NULL;
165 	new->edit_info = alloc_edit_info();
166 	screen_set_fdout(new, 1);
167 	screen_set_fpout(new, stdout);
168 	screen_set_fdin(new, 0);
169 	new->alive = 1;
170 	new->promptlist = NULL;
171 	new->redirect_name = NULL;
172 	new->redirect_token = NULL;
173 	new->tty_name = NULL;
174 	new->li = main_screen ? main_screen->li : 24;
175 	new->co = main_screen ? main_screen->co : 79;
176 	new->old_term_li = -1;
177 	new->old_term_co = -1;
178 
179 	input_reset_screen(new);
180 
181 	new->redirect_server = -1;
182 	last_input_screen = new;
183 	return new;
184 }
185 
186 void
set_current_screen(Screen * screen)187 set_current_screen(Screen *screen)
188 {
189 	if (screen_get_alive(screen))
190 		current_screen = screen;
191 	else
192 		current_screen = screen_list;
193 	term_set_fp(screen_get_fpout(current_screen));
194 }
195 
196 Screen *
get_current_screen(void)197 get_current_screen(void)
198 {
199 	return current_screen;
200 }
201 
202 /*
203  * window_redirect: Setting who to non null will cause IRCII to echo all
204  * commands and output from that server (including output generated by IRCII)
205  * to who.  Setting who to null disables this
206  *
207  * XXXMRG why does this operate on the whole screen instead of a window?
208  */
209 void
window_redirect(u_char * who,int server)210 window_redirect(u_char *who, int server)
211 {
212 	if (who)
213 		malloc_snprintf(&current_screen->redirect_token, "%04d%s", server, who);
214 	else
215 		malloc_snprintf(&current_screen->redirect_token, "%04d#LAME", server);
216 	malloc_strcpy(&current_screen->redirect_name, who);
217 	current_screen->redirect_server = server;
218 }
219 
220 int
check_screen_redirect(u_char * nick)221 check_screen_redirect(u_char *nick)
222 {
223 	Screen	*screen,
224 		*tmp_screen;
225 
226 	for (screen = screen_first(); screen; screen = screen->next)
227 	{
228 		if (!screen->redirect_token)
229 			continue;
230 		if (!my_strcmp(nick, screen->redirect_token))
231 		{
232 			tmp_screen = current_screen;
233 			set_current_screen(screen);
234 			window_redirect(NULL, get_from_server());
235 			set_current_screen(tmp_screen);
236 			return 1;
237 		}
238 	}
239 	return 0;
240 }
241 
242 /* Old screens never die. They just fade away. */
243 
244 #ifdef WINDOW_CREATE
245 void
kill_screen(Screen * screen)246 kill_screen(Screen *screen)
247 {
248 	Window	*window;
249 
250 	if (main_screen == screen)
251 	{
252 		say("You may not kill the main screen");
253 		return;
254 	}
255 	if (screen_get_fdin(screen))
256 	{
257 		new_close(screen_get_fdin(screen));
258 		new_close(screen_get_fdout(screen));
259 		/* But fdout is attached to fpout, so close that too.. */
260 		fclose(screen_get_fpout(screen));
261 	}
262 	while ((window = screen_get_window_list(screen)))
263 	{
264 		screen->window_list = window_get_next(window);
265 		add_to_invisible_list(window);
266 	}
267 
268 	if (last_input_screen == screen)
269 		last_input_screen = screen_first();
270 	screen->alive = 0;
271 }
272 
273 int
is_main_screen(Screen * screen)274 is_main_screen(Screen *screen)
275 {
276 	return (screen == main_screen);
277 }
278 
279 #else
280 
281 int
is_main_screen(Screen * screen)282 is_main_screen(Screen *screen)
283 {
284 	return 1;
285 }
286 
287 #endif /* WINDOW_CREATE */
288 
289 
290 /*
291  * cursor_not_in_display: This forces the cursor out of the display by
292  * setting the cursor window to null.  This doesn't actually change the
293  * physical position of the cursor, but it will force rite() to reset the
294  * cursor upon its next call
295  */
296 void
cursor_not_in_display(void)297 cursor_not_in_display(void)
298 {
299 	Debug(DB_CURSOR, "screen %d cursor_window set to NULL",
300 	      screen_get_screennum(current_screen));
301 	set_cursor_window(NULL);
302 }
303 
304 /*
305  * cursor_in_display: this forces the cursor_window to be the
306  * current_screen->current_window.
307  * It is actually only used in hold.c to trick the system into thinking the
308  * cursor is in a window, thus letting the input updating routines move the
309  * cursor down to the input line.  Dumb dumb dumb
310  */
311 void
cursor_in_display(void)312 cursor_in_display(void)
313 {
314 	Debug(DB_CURSOR, "screen %d cursor_window set to window %d (%d)",
315 	       screen_get_screennum(current_screen), window_get_refnum(curr_scr_win),
316 	       screen_get_screennum(window_get_screen(curr_scr_win)));
317 	set_cursor_window(curr_scr_win);
318 }
319 
320 /*
321  * is_cursor_in_display: returns true if the cursor is in one of the windows
322  * (cursor_window is not null), false otherwise
323  */
324 int
is_cursor_in_display(void)325 is_cursor_in_display(void)
326 {
327 	if  (get_cursor_window())
328 		return (1);
329 	else
330 		return (0);
331 }
332 
333 void
decode_colour(u_char ** ptr,int * fg,int * bg)334 decode_colour(u_char **ptr, int *fg, int *bg)
335 {
336 	char c;
337 	/* At input, *ptr points to the first char after ^C */
338 
339 	*fg = *bg = 16; /* both unset */
340 
341 	/*
342 	 * mIRC doesn't accept codes beginning with comma (,),
343 	 * VIRC accepts. mIRC is out authority in this case.
344 	 */
345 	c = **ptr;
346 	if (c < '0' || c > '9')
347 	{
348 		--*ptr;
349 		return;
350 	}
351 
352 	*fg = c - '0';
353 
354 	c = (*ptr)[1];
355 	if (c >= '0' && c <= '9')
356 	{
357 		++*ptr;
358 		*fg = *fg * 10 + c - '0';
359 		c = (*ptr)[1];
360 	}
361 	if (c == ',')
362 	{
363 		/*
364 		 * mIRC doesn't eat the comma if it is not followed by
365 		 * a number, VIRC does. mIRC is again the authority.
366 		 */
367 		c = (*ptr)[2];
368 		if (c >= '0' && c <= '9')
369 		{
370 			*bg = c - '0';
371 			*ptr += 2;
372 			c = (*ptr)[1];
373 			if (c >= '0' && c <= '9')
374 			{
375 				++*ptr;
376 				*bg = *bg * 10 + c - '0';
377 			}
378 		}
379 	}
380 	/* At this point, *ptr points to last char WITHIN the colour code */
381 }
382 
383 /* strlen() with internal format codes stripped */
384 /* Return value: number of columns this string takes */
385 int
my_strlen_i(u_char * c)386 my_strlen_i(u_char *c)
387 {
388 	int result = 0;
389 	struct mb_data mbdata;
390 #ifdef HAVE_ICONV_OPEN
391 	mbdata_init(&mbdata, CP(current_irc_encoding()));
392 #else
393 	mbdata_init(&mbdata, NULL);
394 #endif /* HAVE_ICONV_OPEN */
395 
396 	while (*c)
397 	{
398 		if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
399 		{
400 			/* These don't add visual length */
401 			++c;
402 			continue;
403 		}
404 		if (*c == COLOUR_TAG)
405 		{
406 			/* Colour code didn't add visual length */
407 			c += 3;
408 			continue;
409 		}
410 
411 		/* Anything else is character data. */
412 		decode_mb(c, NULL, 0, &mbdata);
413 		result += mbdata.num_columns;
414 		c      += mbdata.input_bytes;
415 	}
416 
417 	mbdata_done(&mbdata);
418 	return result;
419 }
420 
421 /* strlen() with colour codes stripped */
422 /* Return value: Number of columns this string takes */
423 int
my_strlen_c(u_char * c)424 my_strlen_c(u_char *c)
425 {
426 	int result = 0;
427 	struct mb_data mbdata;
428 #ifdef HAVE_ICONV_OPEN
429 	mbdata_init(&mbdata, CP(current_irc_encoding()));
430 #else
431 	mbdata_init(&mbdata, NULL);
432 #endif /* HAVE_ICONV_OPEN */
433 
434 	while (*c)
435 	{
436 		if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
437 		{
438 			/* These don't add visual length */
439 			++c;
440 			continue;
441 		}
442 		if (*c == COLOUR_TAG)
443 		{
444 			int fg, bg;
445 
446 			++c;
447 			decode_colour(&c, &fg, &bg);
448 			++c;
449 			/* Colour code didn't add visual length */
450 			continue;
451 		}
452 
453 		/* Anything else is character data. */
454 		decode_mb(c, NULL, 0, &mbdata);
455 		result += mbdata.num_columns;
456 		c      += mbdata.input_bytes;
457 	}
458 	mbdata_done(&mbdata);
459 	return result;
460 }
461 
462 /* strlen() with colour codes converted to internal codes */
463 /* Doesn't do actual conversion */
464 /* Return value: Number of bytes this string takes when converted */
465 int
my_strlen_ci(u_char * c)466 my_strlen_ci(u_char *c)
467 {
468 	int result = 0;
469 
470 	struct mb_data mbdata;
471 #ifdef HAVE_ICONV_OPEN
472 	mbdata_init(&mbdata, CP(current_irc_encoding()));
473 #else
474 	mbdata_init(&mbdata, NULL);
475 #endif /* HAVE_ICONV_OPEN */
476 
477 	while (*c)
478 	{
479 		if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
480 		{
481 			/* These are preserved */
482 			++c;
483 			++result;
484 			continue;
485 		}
486 		if (*c == COLOUR_TAG)
487 		{
488 			int fg, bg;
489 
490 			++c;
491 			decode_colour(&c, &fg, &bg);
492 			++c;
493 			/* Modifies into three characters */
494 			result += 3;
495 			continue;
496 		}
497 
498 		/* Anything else is character data. */
499 		decode_mb(c, NULL, 0, &mbdata);
500 		result += mbdata.output_bytes;
501 		c      += mbdata.input_bytes;
502 	}
503 	mbdata_done(&mbdata);
504 	return result;
505 }
506 
507 /* strcpy() with colour codes converted to internal codes */
508 /* Converts the codes */
509 void
my_strcpy_ci(u_char * dest,size_t destlen,u_char * c)510 my_strcpy_ci(u_char *dest, size_t destlen, u_char *c)
511 {
512 	struct mb_data mbdata;
513 #ifdef HAVE_ICONV_OPEN
514 	mbdata_init(&mbdata, CP(current_irc_encoding()));
515 #else
516 	mbdata_init(&mbdata, NULL);
517 #endif /* HAVE_ICONV_OPEN */
518 
519 	while (*c && destlen > 1)
520 	{
521 		if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
522 		{
523 			*dest++ = *c++;
524 			destlen--;
525 			continue;
526 		}
527 		if (*c == COLOUR_TAG)
528 		{
529 			int fg, bg;
530 
531 			if (destlen <= 3)
532 				break;
533 			*dest++ = *c++;
534 			decode_colour(&c, &fg, &bg);
535 			*dest++ = fg+1;
536 			*dest++ = bg+1;
537 			++c;
538 			destlen -= 3;
539 			continue;
540 		}
541 
542 		/* Anything else is character data. */
543 		decode_mb(c, dest, destlen, &mbdata);
544 
545 		dest += mbdata.output_bytes;
546 		destlen -= mbdata.output_bytes;
547 		c += mbdata.input_bytes;
548 	}
549 	mbdata_done(&mbdata);
550 	*dest++ = '\0';
551 }
552 
553 int
in_redirect(void)554 in_redirect(void)
555 {
556 	return current_in_redirect;
557 }
558 
559 /*
560  * add_to_screen: This adds the given null terminated buffer to the screen.
561  * That is, it determines which window the information should go to, which
562  * lastlog the information should be added to, which log the information
563  * should be sent to, etc.
564  *
565  * All logging to files is performed here as well.
566  */
567 void
add_to_screen(u_char * incoming)568 add_to_screen(u_char *incoming)
569 {
570 	Win_Trav wt;
571 	Window	*tmp;
572 	u_char	*who_from = current_who_from();
573 	const	int	from_server = get_from_server();
574 
575 	/* eek! */
576 	if (!current_screen)
577 	{
578 		puts(CP(incoming));
579 		fflush(stdout);
580 		add_to_log(NULL, incoming);
581 		return;
582 	}
583 	/* Handles output redirection first */
584 	if (!current_in_redirect && current_screen->redirect_name &&
585 	    from_server == current_screen->redirect_server)
586 	{
587 		int	i;
588 
589 		/*
590 		 * More general stuff by Bisqwit, allow
591 		 * multiple target redirection (with different targets)
592 		 */
593 		current_in_redirect = 1;
594 		i = set_in_on_who(0); /* To allow redirecting /who, /whois and /join */
595 		redirect_msg(current_screen->redirect_name, incoming);
596 		set_in_on_who(i);
597 		current_in_redirect = 0;
598 	}
599 	if (term_basic())
600 	{
601 		/* FIXME: Do iconv for "incoming" in dumb mode too */
602 		(void)add_to_lastlog(curr_scr_win, incoming);
603 		if (do_hook(WINDOW_LIST, "%u %s",
604 		    window_get_refnum(curr_scr_win), incoming))
605 		{
606 			add_to_log(NULL, incoming);
607 			puts(CP(incoming));
608 		}
609 		term_flush();
610 		return;
611 	}
612 	if (current_who_level() == LOG_CURRENT &&
613 	    window_get_server(curr_scr_win) == from_server)
614 	{
615 		add_to_window(curr_scr_win, incoming);
616 		return;
617 	}
618 	if (to_window)
619 	{
620 		add_to_window(to_window, incoming);
621 		return;
622 	}
623 	/*
624 	 * XXX - hack alert
625 	 * If /echo or /xecho set the message_from, use it here before we
626 	 * look at who_from.  If not, look at it afterwards.
627 	 * XXX we could probably use to_window to fix this less hackily?
628 	 */
629 	if (current_who_level() && who_level_before_who_from())
630 	{
631 		wt.init = 1;
632 		while ((tmp = window_traverse(&wt)) != NULL)
633 		{
634 			if ((from_server == window_get_server(tmp) ||
635 			     from_server == -1) &&
636 			    (current_who_level() & window_get_window_level(tmp)))
637 			{
638 				add_to_window(tmp, incoming);
639 				return;
640 			}
641 		}
642 	}
643 	if (who_from)
644 	{
645 		if (is_channel(who_from))
646 		{
647 			Window	*window;
648 
649 			if ((window = channel_window(who_from, from_server)) != NULL)
650 			{
651 				add_to_window(window, incoming);
652 				return;
653 			}
654 			if (current_who_level() == LOG_DCC)
655 			{
656 				u_char	*buffer = NULL;
657 
658 				malloc_snprintf(&buffer, "=%s", who_from);
659 				window = channel_window(buffer, from_server);
660 				new_free(&buffer);
661 				if (window != NULL)
662 				{
663 					add_to_window(window, incoming);
664 					return;
665 				}
666 			}
667 		}
668 		else
669 		{
670 			wt.init = 1;
671 			while ((tmp = window_traverse(&wt)) != NULL)
672 			{
673 				u_char *nick = window_get_query_nick(tmp);
674 
675 				if (nick &&
676 				    (((current_who_level() == LOG_MSG ||
677 				       current_who_level() == LOG_NOTICE) &&
678 				      my_stricmp(who_from, nick) == 0 &&
679 				      from_server == window_get_server(tmp)) ||
680 				     (current_who_level() == LOG_DCC &&
681 				      (*nick == '=' || *nick == '@') &&
682 				      my_stricmp(who_from, nick + 1) == 0)))
683 				{
684 					add_to_window(tmp, incoming);
685 					return;
686 				}
687 			}
688 			wt.init = 1;
689 			while ((tmp = window_traverse(&wt)) != NULL)
690 			{
691 				if (from_server == window_get_server(tmp))
692 				{
693 					if (window_has_who_from(tmp))
694 					{
695 						add_to_window(tmp, incoming);
696 						return;
697 					}
698 				}
699 			}
700 		}
701 	}
702 	if (current_who_level() && !who_level_before_who_from())
703 	{
704 		wt.init = 1;
705 		while ((tmp = window_traverse(&wt)) != NULL)
706 		{
707 			if ((from_server == window_get_server(tmp) ||
708 			     from_server == -1) &&
709 			    (current_who_level() & window_get_window_level(tmp)))
710 			{
711 				add_to_window(tmp, incoming);
712 				return;
713 			}
714 		}
715 	}
716 	if (from_server == window_get_server(curr_scr_win))
717 		tmp = curr_scr_win;
718 	else
719 	{
720 		wt.init = 1;
721 		while ((tmp = window_traverse(&wt)) != NULL)
722 		{
723 			if (window_get_server(tmp) == from_server)
724 				break;
725 		}
726 		if (!tmp)
727 			tmp = curr_scr_win;
728 	}
729 	add_to_window(tmp, incoming);
730 }
731 
732 void
close_all_screen(void)733 close_all_screen(void)
734 {
735 	Screen *screen;
736 
737 	for (screen = screen_first(); screen && screen != current_screen;
738 			screen = screen->next)
739 		if (screen_get_alive(screen) && screen_get_fdin(screen) != 0)
740 			new_close(screen_get_fdin(screen));
741 }
742 
743 #ifdef WINDOW_CREATE
744 Window	*
create_additional_screen(int wanted_type)745 create_additional_screen(int wanted_type)
746 {
747 	Window	*win;
748 	Screen	*oldscreen;
749 	u_char	*displayvar,
750 		*termvar;
751 	int	screen_type = ST_NOTHING;
752 	struct	sockaddr_un sock, error_sock,
753 			*sockaddr = &sock,
754 			*error_sockaddr = &error_sock,
755 			NewSock;
756 	socklen_t	NsZ;
757 	int	s, es;
758 	int	fd;
759 	FILE	*fp;
760 	int	wserv_fd;
761 	fd_set	fd_read;
762 	struct	timeval	time_out;
763 	pid_t	child;
764 	int	old_timeout;
765 #define IRCXTERM_MAX 10
766 	u_char	*ircxterm[IRCXTERM_MAX];
767 	u_char	*ircxterm_env;
768 	u_char	*xterm = NULL;
769 	u_char	*def_xterm = get_string_var(XTERM_PATH_VAR);
770 	u_char	*p, *q;
771 	u_char	buffer[BIG_BUFFER_SIZE];
772 	pid_t	pid = getpid();
773 	int	ircxterm_num;
774 	int	i;
775 	static int cycle = 0;
776 	int	mycycle = cycle++;
777 
778 #ifdef DAEMON_UID
779 	if (DAEMON_UID == getuid())
780 	{
781 		say("you are not permitted to use WINDOW CREATE");
782 		return NULL;
783 	}
784 #endif /* DAEMON_UID */
785 
786 	if (!def_xterm)
787 		def_xterm = UP("xterm");
788 
789 	ircxterm_num = 0;
790 	p = ircxterm_env = my_getenv("IRCXTERM");
791 	if (p)
792 	{
793 		q = ircxterm_env + my_strlen(ircxterm_env);
794 		while (p < q && ircxterm_num < IRCXTERM_MAX)
795 		{
796 			while (':' == *p)
797 				p++;
798 			if ('\0' == *p)
799 				break;
800 			ircxterm[ircxterm_num++] = p;
801 			while (':' != *p && '\0' != *p)
802 				p++;
803 			if (':' == *p)
804 			{
805 				*p = '\0';
806 				p++;
807 			}
808 		}
809 	}
810 	else
811 	{
812 		ircxterm[ircxterm_num] = def_xterm;
813 		ircxterm_num++;
814 	}
815 
816 	/*
817 	 * Environment variable STY has to be set for screen to work..  so it is
818 	 * the best way to check screen..  regardless of what TERM is, the
819 	 * execpl() for screen won't just open a new window if STY isn't set,
820 	 * it will open a new screen process, and run the wserv in its first
821 	 * window, not what we want...  -phone
822 	 */
823 	if ((wanted_type == ST_NOTHING || wanted_type == ST_SCREEN) &&
824 	    0 != my_getenv("STY"))
825 		screen_type = ST_SCREEN;
826 	else if ((wanted_type == ST_NOTHING || wanted_type == ST_XTERM) &&
827 	    NULL != (displayvar = my_getenv("DISPLAY")) &&
828 	    NULL != (termvar = my_getenv("TERM")))
829 	{
830 		screen_type = ST_XTERM;
831 		if (0 == my_strncmp(termvar, "sun", 3))
832 		{
833 			xterm = def_xterm;
834 		}
835 		else
836 		{
837 			for (; *termvar; termvar++)
838 			{
839 				for (i = 0; i < ircxterm_num; i++)
840 				{
841 					if (!my_strncmp(termvar, ircxterm[i], my_strlen(ircxterm[i])))
842 					{
843 						xterm = ircxterm[i];
844 						termvar = empty_string();
845 						break;
846 					}
847 				}
848 			}
849 		}
850 		if (!xterm)
851 			xterm = def_xterm;
852 	}
853 
854 	if (screen_type == ST_NOTHING)
855 	{
856 		say("I don't know how to create new windows for this terminal");
857 		return NULL;
858 	}
859 	say("Opening new %s...",
860 		screen_type == ST_XTERM ?  "window" :
861 		screen_type == ST_SCREEN ? "screen" :
862 					   "wound" );
863 	snprintf(sock.sun_path, sizeof sock.sun_path, "/tmp/irc_%08d_%x", (int) pid, mycycle);
864 	sock.sun_family = AF_UNIX;
865 	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
866 	{
867 		say("Can't create UNIX socket, punting /WINDOW CREATE");
868 		return NULL;
869 	}
870 	if (bind(s, (struct sockaddr *) &sock, (int)(2 + my_strlen(sock.sun_path))) < 0)
871 	{
872 		say("Can't bind UNIX socket, punting /WINDOW CREATE");
873 		return NULL;
874 	}
875 	if (listen(s, 1) < 0)
876 	{
877 		say("Can't bind UNIX socket, punting /WINDOW CREATE");
878 		return NULL;
879 	}
880 	snprintf(error_sock.sun_path, sizeof error_sock.sun_path, "/tmp/irc_error_%08d_%x", (int) pid, mycycle);
881 	error_sock.sun_family = AF_UNIX;
882 	if ((es = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
883 	{
884 		say("Can't create UNIX socket, punting /WINDOW CREATE");
885 		return NULL;
886 	}
887 	if (bind(es, (struct sockaddr *) &error_sock, (int)(2 + my_strlen(error_sock.sun_path))) < 0)
888 	{
889 		say("Can't bind UNIX socket, punting /WINDOW CREATE");
890 		return NULL;
891 	}
892 	if (listen(es, 1) < 0)
893 	{
894 		say("Can't bind UNIX socket, punting /WINDOW CREATE");
895 		return NULL;
896 	}
897 
898 	oldscreen = current_screen;
899 	set_current_screen(create_new_screen());
900 	if (0 == (child = fork()))
901 	{
902 		(void)setuid(getuid());
903 		(void)setgid(getgid());
904 	/*
905 	 * Unlike most other cases, it is important here to close down
906 	 * *ALL* unneeded file descriptors. Failure to do so can cause
907 	 * Things like server and DCC connections to fail to close on
908 	 * request. This isn't a problem with "screen", but is with X.
909 	 */
910 		new_close(s);
911 		new_close(es);
912 		close_all_screen();
913 		close_all_dcc();
914 		close_all_exec();
915 		close_all_server();
916 		i = 0;
917 		if (screen_type == ST_SCREEN)
918 		{
919 #define MAX_ARGS	64
920 			char	*args[MAX_ARGS];
921 			u_char	*ss,
922 				*t,
923 				*opts = NULL;
924 
925 			Debug(DB_WINCREATE, "going to execvp screen wserv...");
926 			args[i++] = "screen";
927 			if ((ss = get_string_var(SCREEN_OPTIONS_VAR)) != NULL)
928 			{
929 				malloc_strcpy(&opts, ss);
930 				while ((t = next_arg(opts, &opts)) != NULL)
931 				{
932 					Debug(DB_WINCREATE, "added option `%s'", t);
933 					args[i++] = CP(t);
934 
935 					/* add 6 more below */
936 					if (i + 6 > MAX_ARGS) {
937 						say("Too many args in SCREEN_OPTIONS_VAR");
938 						break;
939 					}
940 				}
941 			}
942 			args[i++] = CP(get_string_var(WSERV_PATH_VAR));
943 #if 0
944 			args[i++] = "-t";
945 			args[i++] = CP(program_name());
946 #endif
947 			args[i++] = sockaddr->sun_path;
948 			args[i++] = error_sockaddr->sun_path;
949 			Debug(DB_WINCREATE, "added: %s %s %s", args[i-3], args[i-2], args[i-1]);
950 			args[i++] = NULL;
951 			execvp("screen", args);
952 		}
953 		else if (screen_type == ST_XTERM)
954 		{
955 			int	lines,
956 				columns;
957 			char	*args[64];
958 			u_char	geom[20],
959 				*ss,
960 				*t,
961 				*opts = NULL;
962 
963 			Debug(DB_WINCREATE, "going to execvp xterm wserv...");
964 			copy_window_size(&lines, &columns);
965 			snprintf(CP(geom), sizeof geom, "%dx%d", columns, lines);
966 			args[i++] = CP(xterm);
967 			Debug(DB_WINCREATE, "xterm name is %s", args[0]);
968 			if ((ss = get_string_var(XTERM_GEOMOPTSTR_VAR)) != NULL)
969 				args[i++] = CP(ss);
970 			else
971 				args[i++] = "-geom";
972 			args[i++] = CP(geom);
973 			Debug(DB_WINCREATE, "added geom: %s %s", args[i-2], args[i-1]);
974 			if ((ss = get_string_var(XTERM_OPTIONS_VAR)) != NULL)
975 			{
976 				malloc_strcpy(&opts, ss);
977 				Debug(DB_WINCREATE, "got xterm options: %s", opts);
978 				while ((t = next_arg(opts, &opts)) != NULL)
979 				{
980 					Debug(DB_WINCREATE, "added option `%s'", t);
981 					args[i++] = CP(t);
982 				}
983 			}
984 			args[i++] = "-e";
985 			args[i++] = CP(get_string_var(WSERV_PATH_VAR));
986 			args[i++] = sockaddr->sun_path;
987 			args[i++] = error_sockaddr->sun_path;
988 			Debug(DB_WINCREATE, "added: %s %s %s %s", args[i-4], args[i-3], args[i-2], args[i-1]);
989 			args[i] = NULL;
990 			execvp(CP(xterm), args);
991 		}
992 		perror("execve");
993 		unlink(sockaddr->sun_path);
994 		unlink(error_sockaddr->sun_path);
995 		exit(errno ? errno : -1);
996 	}
997 	NsZ = sizeof(NewSock);
998 	FD_ZERO(&fd_read);
999 	FD_SET(s, &fd_read);
1000 	FD_SET(es, &fd_read);
1001 	time_out.tv_sec = (time_t) 5;
1002 	time_out.tv_usec = 0;
1003 	sleep(1);
1004 
1005 	Debug(DB_WINCREATE, "-- forked wserv!");
1006 	/* using say(), yell() can be bad in this next section of code. */
1007 
1008 	switch (select(NFDBITS , &fd_read, NULL, NULL, &time_out))
1009 	{
1010 	case -1:
1011 	case 0:
1012 		errno = get_child_exit(child);
1013 		new_close(s);
1014 		new_close(es);
1015 		kill_screen(current_screen);
1016 		kill(child, SIGKILL);
1017 		last_input_screen = oldscreen;
1018 		set_current_screen(oldscreen);
1019 		yell("child %s with %d", (errno < 1) ? "signaled" : "exited",
1020 					 (errno < 1) ? -errno : errno);
1021 		win = NULL;
1022 		break;
1023 	default:
1024 		fd = accept(s, (struct sockaddr *) &NewSock, &NsZ);
1025 		if (fd < 0)
1026 		{
1027 			win = NULL;
1028 			break;
1029 		}
1030 		wserv_fd = accept(es, (struct sockaddr *) &NewSock, &NsZ);
1031 		if (wserv_fd < 0)
1032 		{
1033 			close(fd);
1034 			win = NULL;
1035 			break;
1036 		}
1037 
1038 		fp = fdopen(fd, "r+");
1039 		if (!fp) {
1040 			close(wserv_fd);
1041 			close(fd);
1042 			win = NULL;
1043 			break;
1044 		}
1045 
1046 		screen_set_fdin(current_screen, fd);
1047 		screen_set_fdout(current_screen, fd);
1048 		screen_set_fpout(current_screen, fp);
1049 		screen_set_wserv_fd(current_screen, wserv_fd);
1050 		Debug(DB_WINCREATE, "-- got connected!");
1051 
1052 		term_set_fp(fp);
1053 		new_close(s);
1054 		new_close(es);
1055 		old_timeout = dgets_timeout(5);
1056 		/*
1057 		 * dgets returns 0 on EOF and -1 on timeout.  both of these are
1058 		 * error conditions in this case, so we bail out here.
1059 		 */
1060 		if (dgets(buffer, sizeof buffer, screen_get_fdin(current_screen)) < 1)
1061 		{
1062 			new_close(screen_get_fdin(current_screen));
1063 			kill_screen(current_screen);
1064 			kill(child, SIGKILL);
1065 			last_input_screen = oldscreen;
1066 			set_current_screen(oldscreen);
1067 			(void) dgets_timeout(old_timeout);
1068 			win = NULL;
1069 			break;
1070 		}
1071 		else
1072 			malloc_strcpy(&current_screen->tty_name, buffer);
1073 		win = new_window();
1074 		(void) refresh_screen(0, NULL);
1075 		set_current_screen(oldscreen);
1076 		(void) dgets_timeout(old_timeout);
1077 	}
1078 	unlink(sockaddr->sun_path);
1079 	unlink(error_sockaddr->sun_path);
1080 	return win;
1081 }
1082 #endif /* WINDOW_CREATE */
1083 
1084 void
screen_wserv_message(Screen * screen)1085 screen_wserv_message(Screen *screen)
1086 {
1087 	u_char buf[128], *rs, *cs, *comma;
1088 	int old_timeout, li, co;
1089 
1090 	old_timeout = dgets_timeout(0);
1091 	if (dgets(buf, 128, current_screen->wserv_fd) < 1)
1092 	{
1093 		/* this should be impossible. */
1094 		if (!is_main_screen(screen))
1095 			kill_screen(screen);
1096 	}
1097 	(void) dgets_timeout(old_timeout);
1098 	Debug(DB_WINCREATE, "got buf: %s", buf);
1099 	/* should have "rows,cols\n" */
1100 	rs = buf;
1101 	comma = my_index(buf, ',');
1102 	if (comma == NULL)
1103 	{
1104 		yell("--- got wserv message: %s, no comma?", buf);
1105 		return;
1106 	}
1107 	*comma = 0;
1108 	cs = comma+1;
1109 	comma = my_index(buf, '\n');
1110 	if (comma != NULL)
1111 		*comma = 0;
1112 
1113 	li = atoi(CP(rs));
1114 	co = atoi(CP(cs));
1115 
1116 	Debug(DB_WINCREATE, "got li %d[%d] co %d[%d]", li, screen->li, co, screen->co);
1117 
1118 	if (screen->li != li || screen->co != co)
1119 	{
1120 		Screen *old_screen;
1121 
1122 		screen->li = li;
1123 		screen->co = co;
1124 		old_screen = current_screen;
1125 		current_screen = screen;
1126 		refresh_screen(0, 0);
1127 		current_screen = old_screen;
1128 	}
1129 }
1130 
1131 int
screen_get_screennum(Screen * screen)1132 screen_get_screennum(Screen *screen)
1133 {
1134 	return screen->screennum;
1135 }
1136 
1137 Window *
screen_get_current_window(Screen * screen)1138 screen_get_current_window(Screen *screen)
1139 {
1140 	return screen->current_window;
1141 }
1142 
1143 void
screen_set_current_window(Screen * screen,Window * window)1144 screen_set_current_window(Screen *screen, Window *window)
1145 {
1146 	screen->current_window = window;
1147 }
1148 
1149 ScreenInputData *
screen_get_inputdata(Screen * screen)1150 screen_get_inputdata(Screen *screen)
1151 {
1152 	return screen->inputdata;
1153 }
1154 
1155 void
screen_set_inputdata(Screen * screen,ScreenInputData * inputdata)1156 screen_set_inputdata(Screen *screen, ScreenInputData *inputdata)
1157 {
1158 	screen->inputdata = inputdata;
1159 }
1160 
1161 int
screen_get_alive(Screen * screen)1162 screen_get_alive(Screen *screen)
1163 {
1164 	return screen->alive;
1165 }
1166 
1167 u_char *
get_redirect_token(void)1168 get_redirect_token(void)
1169 {
1170 	return current_screen->redirect_token;
1171 }
1172 
1173 int
screen_get_wserv_fd(Screen * screen)1174 screen_get_wserv_fd(Screen *screen)
1175 {
1176 	return screen->wserv_fd;
1177 }
1178 
1179 Window *
screen_get_window_list(Screen * screen)1180 screen_get_window_list(Screen *screen)
1181 {
1182 	return screen->window_list;
1183 }
1184 
1185 void
screen_set_window_list(Screen * screen,Window * window)1186 screen_set_window_list(Screen *screen, Window *window)
1187 {
1188 	screen->window_list = window;
1189 }
1190 
1191 Window *
screen_get_window_list_end(Screen * screen)1192 screen_get_window_list_end(Screen *screen)
1193 {
1194 	return screen->window_list_end;
1195 }
1196 
1197 void
screen_set_window_list_end(Screen * screen,Window * window)1198 screen_set_window_list_end(Screen *screen, Window *window)
1199 {
1200 	screen->window_list_end = window;
1201 }
1202 
1203 int
screen_get_visible_windows(Screen * screen)1204 screen_get_visible_windows(Screen *screen)
1205 {
1206 	return screen->visible_windows;
1207 }
1208 
1209 void
incr_visible_windows(void)1210 incr_visible_windows(void)
1211 {
1212 	current_screen->visible_windows++;
1213 }
1214 
1215 void
decr_visible_windows(void)1216 decr_visible_windows(void)
1217 {
1218 	current_screen->visible_windows--;
1219 }
1220 
1221 int
screen_get_fdin(Screen * screen)1222 screen_get_fdin(Screen *screen)
1223 {
1224 	return screen->fdin;
1225 }
1226 
1227 static int
screen_get_fdout(Screen * screen)1228 screen_get_fdout(Screen *screen)
1229 {
1230 	return screen->fdout;
1231 }
1232 
1233 static void
screen_set_fdout(Screen * screen,int fdout)1234 screen_set_fdout(Screen *screen, int fdout)
1235 {
1236 	screen->fdout = fdout;
1237 }
1238 
1239 static void
screen_set_fdin(Screen * screen,int fdin)1240 screen_set_fdin(Screen *screen, int fdin)
1241 {
1242 	screen->fdin = fdin;
1243 }
1244 
1245 FILE *
screen_get_fpout(Screen * screen)1246 screen_get_fpout(Screen *screen)
1247 {
1248 	return screen->fpout;
1249 }
1250 
1251 static void
screen_set_fpout(Screen * screen,FILE * fpout)1252 screen_set_fpout(Screen *screen, FILE *fpout)
1253 {
1254 	screen->fpout = fpout;
1255 }
1256 
1257 static void
screen_set_wserv_fd(Screen * screen,int wserv_fd)1258 screen_set_wserv_fd(Screen *screen, int wserv_fd)
1259 {
1260 	screen->wserv_fd = wserv_fd;
1261 }
1262 
1263 unsigned
get_last_window_refnum(void)1264 get_last_window_refnum(void)
1265 {
1266 	return current_screen->last_window_refnum;
1267 }
1268 
1269 void
set_last_window_refnum(unsigned refnum)1270 set_last_window_refnum(unsigned refnum)
1271 {
1272 	current_screen->last_window_refnum = refnum;
1273 }
1274 
1275 Prompt *
get_current_promptlist(void)1276 get_current_promptlist(void)
1277 {
1278 	return current_screen->promptlist;
1279 }
1280 
1281 void
set_current_promptlist(Prompt * promptlist)1282 set_current_promptlist(Prompt *promptlist)
1283 {
1284 	current_screen->promptlist = promptlist;
1285 }
1286 
1287 Screen *
screen_get_next(Screen * screen)1288 screen_get_next(Screen *screen)
1289 {
1290 	return screen->next;
1291 }
1292 
1293 WindowStack *
get_window_stack(void)1294 get_window_stack(void)
1295 {
1296 	return current_screen->window_stack;
1297 }
1298 
1299 void
set_window_stack(WindowStack * window_stack)1300 set_window_stack(WindowStack *window_stack)
1301 {
1302 	current_screen->window_stack = window_stack;
1303 }
1304 
1305 Window *
get_cursor_window(void)1306 get_cursor_window(void)
1307 {
1308 	return current_screen->cursor_window;
1309 }
1310 
1311 void
set_cursor_window(Window * cursor_window)1312 set_cursor_window(Window *cursor_window)
1313 {
1314 	current_screen->cursor_window = cursor_window;
1315 }
1316 
1317 EditInfo *
get_edit_info(void)1318 get_edit_info(void)
1319 {
1320 	return current_screen->edit_info;
1321 }
1322 
1323 int
screen_set_size(int new_li,int new_co)1324 screen_set_size(int new_li, int new_co)
1325 {
1326 	int rv = 0;
1327 
1328 	if (current_screen->li != new_li ||
1329 	    current_screen->co != new_co)
1330 		rv = 1;
1331 
1332 	current_screen->old_term_li = current_screen->li;
1333 	current_screen->old_term_co = current_screen->co;
1334 	current_screen->li = new_li;
1335 	current_screen->co = new_co;
1336 
1337 	Debug(DB_WINDOW, "new lines/colums = %d/%d (old = %d/%d)",
1338 		new_li, new_co,
1339 		current_screen->old_term_li,
1340 		current_screen->old_term_co);
1341 	return rv;
1342 }
1343 
1344 int
screen_get_old_co(void)1345 screen_get_old_co(void)
1346 {
1347 	return current_screen->old_term_co;
1348 }
1349 
1350 int
screen_get_old_li(void)1351 screen_get_old_li(void)
1352 {
1353 	return current_screen->old_term_li;
1354 }
1355 
1356 int
get_co(void)1357 get_co(void)
1358 {
1359 	return current_screen->co;
1360 }
1361 
1362 int
get_li(void)1363 get_li(void)
1364 {
1365 	return current_screen->li;
1366 }
1367 
1368 Screen *
screen_first(void)1369 screen_first(void)
1370 {
1371 	return screen_list;
1372 }
1373 
1374 void
set_main_screen(Screen * screen)1375 set_main_screen(Screen *screen)
1376 {
1377 	main_screen = screen;
1378 }
1379 
1380 Screen *
get_last_input_screen(void)1381 get_last_input_screen(void)
1382 {
1383 	return last_input_screen;
1384 }
1385 
1386 void
set_last_input_screen(Screen * screen)1387 set_last_input_screen(Screen *screen)
1388 {
1389 	last_input_screen = screen;
1390 }
1391 
1392 Window *
get_to_window(void)1393 get_to_window(void)
1394 {
1395 	return to_window;
1396 }
1397 
1398 void
set_to_window(Window * window)1399 set_to_window(Window *window)
1400 {
1401 	to_window = window;
1402 }
1403