wmbiff: Remove #define _GNU_SOURCE; already defined in CFLAGS.
[dockapps.git] / wmbiff / wmbiff / ShellClient.c
blob8c6647106aa83b727e3fd26d182b42b36279dd24
1 /* Author: Benoît Rouits ( brouits@free.fr ) thanks to Neil Spring.
2 from LicqClient by Yong-iL Joh ( tolkien@mizi.com )
3 and Jorge García ( Jorge.Garcia@uv.es )
5 * generic Shell command support
7 * Last Updated : Tue Mar 5 15:23:35 CET 2002
9 */
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
15 #include "Client.h"
16 #include <errno.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <signal.h>
21 #include <assert.h>
22 #include <strings.h>
23 #include "charutil.h"
24 #include "MessageList.h"
25 #ifdef USE_DMALLOC
26 #include <dmalloc.h>
27 #endif
29 #define SH_DM(pc, lvl, args...) DM(pc, lvl, "shell: " args)
31 /* kind_popen bumps off the sigchld handler - we care whether
32 a checking program fails. */
34 #ifdef __LCLINT__
35 void (*old_signal_handler) (int);
36 #else
37 RETSIGTYPE(*old_signal_handler) (int);
38 #endif
40 /*@null@*/
41 FILE *kind_popen(const char *command, const char *type)
43 FILE *ret;
44 assert(strcmp(type, "r") == 0);
45 assert(old_signal_handler == NULL);
46 old_signal_handler = signal(SIGCHLD, SIG_DFL);
47 ret = popen(command, type);
48 if (ret == NULL) {
49 DMA(DEBUG_ERROR, "popen: error while reading '%s': %s\n",
50 command, strerror(errno));
51 (void) signal(SIGCHLD, old_signal_handler);
52 old_signal_handler = NULL;
54 return (ret);
57 /* kind_pclose checks the return value from pclose and prints
58 some nice error messages about it. ordinarily, this would be
59 a good idea, but wmbiff has a sigchld handler that reaps
60 children immediately (needed when spawning other child processes),
61 so no error checking can be done here until that's disabled */
63 /* returns as a mailcheck function does: -1 on fail, 0 on success */
64 static int kind_pclose( /*@only@ */ FILE * F,
65 const char *command,
66 /*@null@ */ Pop3 pc)
68 int exit_status = pclose(F);
70 if (old_signal_handler != NULL) {
71 (void) signal(SIGCHLD, old_signal_handler);
72 old_signal_handler = NULL;
75 if (exit_status != 0) {
76 if (exit_status == -1) {
77 /* wmbiff has a sigchld handler already, so wait is likely
78 to fail */
79 SH_DM(pc, DEBUG_ERROR, "pclose '%s' failed: %s\n",
80 command, strerror(errno));
81 } else {
82 SH_DM(pc, DEBUG_ERROR,
83 "'%s' exited with non-zero status %d\n", command,
84 exit_status);
87 return (exit_status);
90 int grabCommandOutput(Pop3 pc, const char *command, /*@out@ */
91 char **output, /*@out@ *//*@null@ */ char **details)
93 FILE *F;
94 char linebuf[512];
95 SH_DM(pc, DEBUG_INFO, "Executing '%s'\n", command);
96 *output = NULL;
97 if ((F = kind_popen(command, "r")) == NULL) {
98 return -1;
100 if (fgets(linebuf, 512, F) == NULL) {
101 SH_DM(pc, DEBUG_ERROR,
102 "fgets: unable to read the output of '%s': %s\n", command,
103 strerror(errno));
104 } else {
105 chomp(linebuf); /* remove trailing newline */
106 *output = strdup_ordie(linebuf);
108 if (details) {
109 static char allbuf[4096];
110 allbuf[0] = '\0';
111 if (fread(allbuf, 1, 4095, F) == 0 && !feof(F)) {
112 SH_DM(pc, DEBUG_ERROR,
113 "fread: unable to read the detailed output of '%s': %s\n",
114 command, strerror(errno));
116 allbuf[4095] = '\0';
117 *details = strdup_ordie(allbuf);
119 return (kind_pclose(F, command, pc));
122 /* returns null on failure */
123 /*@null@*/
124 char *backtickExpand(Pop3 pc, const char *path)
126 char bigbuffer[1024];
127 const char *tickstart;
128 const char *tickend;
129 bigbuffer[0] = '\0';
130 while ((tickstart = strchr(path, '`')) != NULL) {
131 char *command;
132 char *commandoutput;
133 tickend = strchr(tickstart + 1, '`');
134 if (tickend == NULL) {
135 SH_DM(pc, DEBUG_ERROR, "unbalanced \' in %s\n", path);
136 return NULL;
138 strncat(bigbuffer, path, tickstart - path);
139 command = strdup_ordie(tickstart + 1);
140 command[tickend - tickstart - 1] = '\0';
141 (void) grabCommandOutput(pc, command, &commandoutput, NULL);
142 free(command);
143 if (commandoutput != NULL) {
144 strcat(bigbuffer, commandoutput);
145 free(commandoutput);
147 path = tickend + 1;
149 /* grab the rest */
150 strcat(bigbuffer, path);
151 SH_DM(pc, DEBUG_INFO, "expanded to %s\n", bigbuffer);
152 return (strdup_ordie(bigbuffer));
155 int shellCmdCheck(Pop3 pc)
157 int count_status = 0;
158 char *commandOutput;
160 if (pc == NULL)
161 return -1;
162 SH_DM(pc, DEBUG_INFO, ">Mailbox: '%s'\n", pc->path);
164 /* fetch the first line of input */
165 pc->TextStatus[0] = '\0';
166 if (pc->u.shell.detail != NULL) {
167 free(pc->u.shell.detail);
168 pc->u.shell.detail = NULL;
170 (void) grabCommandOutput(pc, pc->path, &commandOutput,
171 &pc->u.shell.detail);
172 if (commandOutput == NULL) {
173 return -1;
175 SH_DM(pc, DEBUG_INFO, "'%s' returned '%s'\n", pc->path, commandOutput);
177 /* see if it's numeric; the numeric check is somewhat
178 useful, as wmbiff renders 4-digit numbers, but not
179 4-character strings. */
180 if (sscanf(commandOutput, "%d", &(count_status)) == 1) {
181 if (strstr(commandOutput, "new")) {
182 pc->UnreadMsgs = count_status;
183 pc->TotalMsgs = 0;
184 } else if (strstr(commandOutput, "old")) {
185 pc->UnreadMsgs = 0;
186 pc->TotalMsgs = count_status;
187 } else {
188 /* this default should be configurable. */
189 pc->UnreadMsgs = 0;
190 pc->TotalMsgs = count_status;
192 } else if (strcasestr(commandOutput, "unable")) {
193 return -1;
194 } else if (sscanf(commandOutput, "%9s\n", pc->TextStatus) == 1) {
195 /* validate the string input */
196 int i;
197 for (i = 0; pc->TextStatus[i] != '\0' && isalnum(pc->TextStatus[i])
198 && i < 10; i++);
199 if (pc->TextStatus[i] != '\0') {
200 SH_DM(pc, DEBUG_ERROR,
201 "wmbiff only supports alphanumeric (isalnum) strings:\n"
202 " '%s' is not ok\n", pc->TextStatus);
203 /* null terminate it at the first bad char: */
204 pc->TextStatus[i] = '\0';
206 /* see if we should print as new or not */
207 pc->UnreadMsgs = (strstr(commandOutput, "new")) ? 1 : 0;
208 pc->TotalMsgs = -1; /* we might alternat numeric /string */
209 } else {
210 SH_DM(pc, DEBUG_ERROR,
211 "'%s' returned something other than an integer message count"
212 " or short string.\n", pc->path);
213 free(commandOutput);
214 return -1;
217 SH_DM(pc, DEBUG_INFO, "from: %s status: %s %d %d\n",
218 pc->path, pc->TextStatus, pc->TotalMsgs, pc->UnreadMsgs);
219 free(commandOutput);
220 return (0);
223 struct msglst *shell_getHeaders( /*@notnull@ */ Pop3 pc)
225 struct msglst *message_list = NULL;
227 char *ln = pc->u.shell.detail;
228 int i, j;
229 if (ln == NULL)
230 return NULL;
232 for (j = 0; ln[j] != '\0';) {
233 struct msglst *m = malloc(sizeof(struct msglst));
234 m->next = message_list;
235 m->subj[0] = '\0';
236 m->from[0] = '\0';
238 for (i = 0; i < SUBJ_LEN - 1 && ln[j + i] != '\n'; i++) {
239 m->subj[i] = ln[j + i];
241 m->subj[i] = '\0';
242 j += i + 1;
244 message_list = m;
246 return message_list;
249 void
250 shell_releaseHeaders(Pop3 pc __attribute__ ((unused)), struct msglst *h)
252 for (; h != NULL;) {
253 struct msglst *n = h->next;
254 free(h);
255 h = n;
259 int shellCreate( /*@notnull@ */ Pop3 pc, const char *str)
261 /* SHELL format: shell:::/path/to/script */
262 const char *reserved1, *reserved2, *commandline;
264 pc->TotalMsgs = 0;
265 pc->UnreadMsgs = 0;
266 pc->OldMsgs = -1;
267 pc->OldUnreadMsgs = -1;
268 pc->checkMail = shellCmdCheck;
269 pc->getHeaders = shell_getHeaders;
270 reserved1 = str + 6; /* shell:>:: */
272 assert(strncasecmp("shell:", str, 6) == 0);
274 reserved2 = index(reserved1, ':');
275 if (reserved2 == NULL) {
276 SH_DM(pc, DEBUG_ERROR, "unable to parse '%s', expecting ':'", str);
277 return 0;
279 reserved2++; /* shell::>: */
281 commandline = index(reserved2, ':');
282 if (commandline == NULL) {
283 SH_DM(pc, DEBUG_ERROR,
284 "unable to parse '%s', expecting another ':'", str);
285 return 0;
287 commandline++; /* shell:::> */
289 /* strcpy is not specified to handle overlapping regions */
290 SH_DM(pc, DEBUG_INFO, "path= '%s'\n", commandline);
292 char *tmp = strdup(commandline);
293 if (strlen(tmp) + 1 > BUF_BIG) {
294 SH_DM(pc, DEBUG_ERROR, "commandline '%s' is too long.\n",
295 commandline);
296 memset(pc->path, 0, BUF_BIG);
297 } else {
298 strcpy(pc->path, tmp);
300 free(tmp);
302 return 0;
305 /* vim:set ts=4: */
307 * Local Variables:
308 * tab-width: 4
309 * c-indent-level: 4
310 * c-basic-offset: 4
311 * End: