1 /*
2  *  Project   : tin - a Usenet reader
3  *  Module    : xface.c
4  *  Author    : Joshua Crawford & Drazen Kacar
5  *  Created   : 2003-04-27
6  *  Updated   : 2013-11-06
7  *  Notes     :
8  *
9  * Copyright (c) 2003-2021 Joshua Crawford <mortarn@softhome.net> & Drazen Kacar <dave@willfork.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * 1. Redistributions of source code must retain the above copyright notice,
17  *    this list of conditions and the following disclaimer.
18  *
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * 3. Neither the name of the copyright holder nor the names of its
24  *    contributors may be used to endorse or promote products derived from
25  *    this software without specific prior written permission.
26  *
27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 
41 /*
42  * TODO: - document the used vars/files/dir in the manpage
43  *       - move strings to lang.c
44  */
45 
46 #ifndef TIN_H
47 #	include "tin.h"
48 #endif /* !TIN_H */
49 
50 #ifdef XFACE_ABLE
51 
52 static int slrnface_fd = -1;
53 
54 
55 void
slrnface_start(void)56 slrnface_start(
57 	void)
58 {
59 	char *fifo;
60 	const char *ptr;
61 	int status;
62 	pid_t pid, pidst;
63 	size_t pathlen;
64 	struct utsname u;
65 
66 	if (tinrc.use_slrnface == FALSE)
67 		return;
68 
69 #ifdef HAVE_IS_XTERM
70 	if (!is_xterm()) {
71 #	ifdef DEBUG
72 		if (debug & DEBUG_MISC)
73 			error_message(2, _("Can't run slrnface: Not running in an xterm."));
74 #	endif /* DEBUG */
75 		return;
76 	}
77 #endif /* HAVE_IS_XTERM */
78 
79 	/*
80 	 * $DISPLAY holds the (default) display name
81 	 */
82 	if (!getenv("DISPLAY")) {
83 #	ifdef DEBUG
84 		if (debug & DEBUG_MISC)
85 			error_message(2, _("Can't run slrnface: Environment variable %s not found."), "DISPLAY");
86 #	endif /* DEBUG */
87 		return;
88 	}
89 
90 	/*
91 	 * $WINDOWID holds the X window id number of the xterm window
92 	 */
93 	if (!getenv("WINDOWID")) {
94 #	ifdef DEBUG
95 		if (debug & DEBUG_MISC)
96 			error_message(2, _("Can't run slrnface: Environment variable %s not found."), "WINDOWID");
97 #	endif /* DEBUG */
98 		return;
99 	}
100 
101 	uname(&u);
102 	ptr = get_val("XDG_RUNTIME_DIR", get_val("HOME", ""));
103 	/*
104 	 * TODO:
105 	 * - check if $XDG_RUNTIME_DIR is on a local filesystem and has secure permissions
106 	 * <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>
107 	 */
108 	if (!strlen(ptr)) { /* TODO: mention XDG_RUNTIME_DIR in error message? */
109 #	ifdef DEBUG
110 		if (debug & DEBUG_MISC)
111 			error_message(2, _("Can't run slrnface: Environment variable %s not found."), "HOME");
112 #	endif /* DEBUG */
113 		return;
114 	}
115 	pathlen = strlen(ptr) + strlen("/.slrnfaces/") + strlen(u.nodename) + 30;
116 	fifo = my_malloc(pathlen);
117 	snprintf(fifo, pathlen, "%s/.slrnfaces", ptr);
118 	if (my_mkdir(fifo, (mode_t) S_IRWXU)) {
119 		if (errno != EEXIST) {
120 			perror_message(_("Can't run slrnface: failed to create %s"), fifo);
121 			free(fifo);
122 			return;
123 		}
124 	} else {
125 		FILE *fp;
126 
127 		/* We abuse fifo filename memory here. It is long enough. */
128 		snprintf(fifo, pathlen, "%s/.slrnfaces/README", ptr);
129 		if ((fp = fopen(fifo, "w")) != NULL) {
130 			fputs(_("This directory is used to create named pipes for communication between\n"
131 "slrnface and its parent process. It should normally be empty because\n"
132 "the pipe is deleted right after it has been opened by both processes.\n\n"
133 "File names generated by slrnface have the form \"hostname.pid\". It is\n"
134 "probably an error if they linger here longer than a fraction of a second.\n\n"
135 "However, if the directory is mounted from an NFS server, you might see\n"
136 "special files created by your NFS server while slrnface is running.\n"
137 "Do not try to remove them.\n"), fp);
138 			fclose(fp);
139 		}
140 	}
141 
142 	status = snprintf(fifo, pathlen, "%s/.slrnfaces/%s.%ld", ptr, u.nodename, (long) getpid());
143 	if (status <= 0 || status >= (int) pathlen) {
144 		error_message(2, _("Can't run slrnface: couldn't construct fifo name."));
145 		unlink(fifo);
146 		free(fifo);
147 		return;
148 	}
149 
150 	unlink(fifo);
151 	if (mkfifo(fifo, (S_IRUSR|S_IWUSR)) < 0) {
152 		perror_message(_("Can't run slrnface: failed to create %s"), fifo);
153 		unlink(fifo);
154 		free(fifo);
155 		return;
156 	}
157 
158 	switch ((pid = fork())) {
159 		case -1:
160 			break;
161 
162 		case 0:
163 			/*
164 			 * TODO: allow positioning, coloring, ...
165 			 *       execl(PATH_SLRNFACE, "slrnface",
166 			 *              "-xOffsetChar", tinrc.xfacex,
167 			 *              "-yOffsetChar", tinrc.xfacey,
168 			 *              "-ink", tinrc.xfacefg,
169 			 *              "-paper", tinrc.xfacebg,
170 			 *              fifo, NULL);
171 			 */
172 			execlp("slrnface", "slrnface", fifo, NULL);
173 			/* This is child, exit on error. */
174 			giveup();
175 			/* NOTREACHED */
176 			break;
177 
178 		default:
179 			do {
180 				pidst = waitpid(pid, &status, 0);
181 			} while (pidst == -1 && errno == EINTR);
182 			if (!WIFEXITED(status))
183 				error_message(2, _("Slrnface abnormally exited, code %d."), status);
184 			else {
185 				const char *message;
186 
187 				switch (WEXITSTATUS(status)) {
188 					case 0:	/* All fine, open the pipe */
189 						slrnface_fd = open(fifo, O_WRONLY, (S_IRUSR|S_IWUSR));
190 						if (slrnface_fd != -1) {
191 							write(slrnface_fd, "start\n", strlen("start\n"));
192 							message = NULL;
193 						} else
194 							message = "can't open FIFO";
195 						break;
196 
197 					/* TODO: warp into _()? */
198 					case 1:
199 						message = "couldn't connect to display";
200 						break;
201 
202 					case 2:
203 						message = "WINDOWID not found in environment";
204 						break;
205 
206 					case 3:
207 						message = "couldn't find controlling terminal";
208 						break;
209 
210 					case 4:
211 						message = "terminal doesn't export width and height";
212 						break;
213 
214 					case 5:
215 						message = "can't open FIFO";
216 						break;
217 
218 					case 6:
219 						message = "fork() failed";
220 						break;
221 
222 					case 10:
223 						message = "executable not found";
224 						break;
225 
226 					default:
227 						message = "unknown error";
228 				}
229 				if (message)
230 					error_message(2, _("Slrnface failed: %s."), message);
231 			}
232 	}
233 	unlink(fifo);
234 	free(fifo);
235 }
236 
237 
238 void
slrnface_stop(void)239 slrnface_stop(
240 	void)
241 {
242 	if (slrnface_fd >= 0)
243 		close(slrnface_fd);
244 
245 	slrnface_fd = -1;
246 	/* FIFO has been unlinked in the startup function. */
247 }
248 
249 
250 void
slrnface_display_xface(char * face)251 slrnface_display_xface(
252 	char *face)
253 {
254 	if (slrnface_fd < 0)
255 		return;
256 
257 	if (!face || !*face)
258 		write(slrnface_fd, "clear\n", strlen("clear\n"));
259 	else {
260 		char buf[2000];	/* slrnface will ignore X-Faces larger than approx. 2000 chars. */
261 
262 		snprintf(buf, sizeof(buf), "xface %s\n", face);
263 		write(slrnface_fd, buf, strlen(buf));
264 	}
265 }
266 
267 
268 void
slrnface_clear_xface(void)269 slrnface_clear_xface(
270 	void)
271 {
272 	if (slrnface_fd < 0)
273 		return;
274 
275 	write(slrnface_fd, "clear\n", strlen("clear\n"));
276 }
277 
278 
279 void
slrnface_suppress_xface(void)280 slrnface_suppress_xface(
281 	void)
282 {
283 	if (slrnface_fd < 0)
284 		return;
285 
286 	write(slrnface_fd, "suppress\n", strlen("suppress\n"));
287 }
288 
289 
290 void
slrnface_show_xface(void)291 slrnface_show_xface(
292 	void)
293 {
294 	if (slrnface_fd < 0)
295 		return;
296 
297 	write(slrnface_fd, "show\n", strlen("show\n"));
298 }
299 
300 #else
301 static void no_xface(void);	/* proto-type */
302 static void
no_xface(void)303 no_xface(	/* ANSI C requires non-empty source file */
304 	void)
305 {
306 }
307 #endif /* XFACE_ABLE */
308