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