1 /* assuan-handler.c - dispatch commands
2  * Copyright (C) 2001, 2002, 2003, 2007, 2009,
3  *               2011 Free Software Foundation, Inc.
4  *
5  * This file is part of Assuan.
6  *
7  * Assuan is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * Assuan is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1+
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 
31 #include "assuan-defs.h"
32 #include "debug.h"
33 
34 
35 #define spacep(p)  (*(p) == ' ' || *(p) == '\t')
36 #define digitp(a) ((a) >= '0' && (a) <= '9')
37 
38 static int my_strcasecmp (const char *a, const char *b);
39 
40 
41 #define PROCESS_DONE(ctx, rc) \
42   ((ctx)->in_process_next ? assuan_process_done ((ctx), (rc)) : (rc))
43 
44 static gpg_error_t
dummy_handler(assuan_context_t ctx,char * line)45 dummy_handler (assuan_context_t ctx, char *line)
46 {
47   return
48     PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT,
49 				  "no handler registered"));
50 }
51 
52 
53 static const char std_help_nop[] =
54   "NOP\n"
55   "\n"
56   "No operation.  Returns OK without any action.";
57 static gpg_error_t
std_handler_nop(assuan_context_t ctx,char * line)58 std_handler_nop (assuan_context_t ctx, char *line)
59 {
60   return PROCESS_DONE (ctx, 0); /* okay */
61 }
62 
63 static const char std_help_cancel[] =
64   "CANCEL\n"
65   "\n"
66   "Run the server's cancel handler if one has been registered.";
67 static gpg_error_t
std_handler_cancel(assuan_context_t ctx,char * line)68 std_handler_cancel (assuan_context_t ctx, char *line)
69 {
70   if (ctx->cancel_notify_fnc)
71     /* Return value ignored.  */
72     ctx->cancel_notify_fnc (ctx, line);
73   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
74 }
75 
76 static const char std_help_option[] =
77   "OPTION <NAME> [ [=] <VALUE> ]\n"
78   "\n"
79   "Set option <NAME> to configure server operation.  Leading and\n"
80   "trailing spaces around <NAME> and <VALUE> are allowed but should be\n"
81   "ignored.  For compatibility reasons, <NAME> may be prefixed with two\n"
82   "dashes.  The use of the equal sign is optional but suggested if\n"
83   "<VALUE> is given.";
84 static gpg_error_t
std_handler_option(assuan_context_t ctx,char * line)85 std_handler_option (assuan_context_t ctx, char *line)
86 {
87   char *key, *value, *p;
88 
89   for (key=line; spacep (key); key++)
90     ;
91   if (!*key)
92     return
93       PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required"));
94   if (*key == '=')
95     return
96       PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
97 				    "no option name given"));
98   for (value=key; *value && !spacep (value) && *value != '='; value++)
99     ;
100   if (*value)
101     {
102       if (spacep (value))
103         *value++ = 0; /* terminate key */
104       for (; spacep (value); value++)
105         ;
106       if (*value == '=')
107         {
108           *value++ = 0; /* terminate key */
109           for (; spacep (value); value++)
110             ;
111           if (!*value)
112             return
113 	      PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
114 					    "option argument expected"));
115         }
116       if (*value)
117         {
118           for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
119             ;
120           if (p > value)
121             *++p = 0; /* strip trailing spaces */
122         }
123     }
124 
125   if (*key == '-' && key[1] == '-' && key[2])
126     key += 2; /* the double dashes are optional */
127   if (*key == '-')
128     return PROCESS_DONE (ctx,
129 			 set_error (ctx, GPG_ERR_ASS_SYNTAX,
130 				    "option should not begin with one dash"));
131 
132   if (ctx->option_handler_fnc)
133     return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value));
134   return PROCESS_DONE (ctx, 0);
135 }
136 
137 static const char std_help_bye[] =
138   "BYE\n"
139   "\n"
140   "Close the connection.  The server will reply with OK.";
141 static gpg_error_t
std_handler_bye(assuan_context_t ctx,char * line)142 std_handler_bye (assuan_context_t ctx, char *line)
143 {
144   if (ctx->bye_notify_fnc)
145     /* Return value ignored.  */
146     ctx->bye_notify_fnc (ctx, line);
147   assuan_close_input_fd (ctx);
148   assuan_close_output_fd (ctx);
149   /* pretty simple :-) */
150   ctx->process_complete = 1;
151   return PROCESS_DONE (ctx, 0);
152 }
153 
154 static const char std_help_auth[] =
155   "AUTH\n"
156   "\n"
157   "Reserved for future extensions.";
158 static gpg_error_t
std_handler_auth(assuan_context_t ctx,char * line)159 std_handler_auth (assuan_context_t ctx, char *line)
160 {
161   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
162 }
163 
164 static const char std_help_reset[] =
165   "RESET\n"
166   "\n"
167   "Reset the connection but not any existing authentication.  The server\n"
168   "should release all resources associated with the connection.";
169 static gpg_error_t
std_handler_reset(assuan_context_t ctx,char * line)170 std_handler_reset (assuan_context_t ctx, char *line)
171 {
172   gpg_error_t err = 0;
173 
174   if (ctx->reset_notify_fnc)
175     err = ctx->reset_notify_fnc (ctx, line);
176   if (! err)
177     {
178       assuan_close_input_fd (ctx);
179       assuan_close_output_fd (ctx);
180       _assuan_uds_close_fds (ctx);
181     }
182   return PROCESS_DONE (ctx, err);
183 }
184 
185 static const char std_help_help[] =
186   "HELP [<COMMAND>]\n"
187   "\n"
188   "Lists all commands that the server understands as comment lines on\n"
189   "the status channel.  If <COMMAND> is given, list detailed help for\n"
190   "that command.";
191 static gpg_error_t
std_handler_help(assuan_context_t ctx,char * line)192 std_handler_help (assuan_context_t ctx, char *line)
193 {
194   unsigned int i;
195   char buf[ASSUAN_LINELENGTH];
196   const char *helpstr;
197   size_t n;
198 
199   n = strcspn (line, " \t\n");
200   if (!n)
201     {
202       /* Print all commands.  If a help string is available and that
203          starts with the command name, print the first line of the
204          help string.  */
205       for (i = 0; i < ctx->cmdtbl_used; i++)
206         {
207           n = strlen (ctx->cmdtbl[i].name);
208           helpstr = ctx->cmdtbl[i].helpstr;
209           if (helpstr
210               && !strncmp (ctx->cmdtbl[i].name, helpstr, n)
211               && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ')
212               && (n = strcspn (helpstr, "\n"))          )
213             snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
214           else
215             snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name);
216           buf[ASSUAN_LINELENGTH - 1] = '\0';
217           assuan_write_line (ctx, buf);
218         }
219     }
220   else
221     {
222       /* Print the help for the given command.  */
223       int c = line[n];
224       line[n] = 0;
225       for (i=0; ctx->cmdtbl[i].name; i++)
226         if (!my_strcasecmp (line, ctx->cmdtbl[i].name))
227           break;
228       line[n] = c;
229       if (!ctx->cmdtbl[i].name)
230         return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL));
231       helpstr = ctx->cmdtbl[i].helpstr;
232       if (!helpstr)
233         return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL));
234       do
235         {
236           n = strcspn (helpstr, "\n");
237           snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
238           helpstr += n;
239           if (*helpstr == '\n')
240             helpstr++;
241           buf[ASSUAN_LINELENGTH - 1] = '\0';
242           assuan_write_line (ctx, buf);
243         }
244       while (*helpstr);
245     }
246 
247   return PROCESS_DONE (ctx, 0);
248 }
249 
250 static const char std_help_end[] =
251   "END\n"
252   "\n"
253   "Used by a client to mark the end of raw data.";
254 static gpg_error_t
std_handler_end(assuan_context_t ctx,char * line)255 std_handler_end (assuan_context_t ctx, char *line)
256 {
257   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
258 }
259 
260 
261 gpg_error_t
assuan_command_parse_fd(assuan_context_t ctx,char * line,assuan_fd_t * rfd)262 assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd)
263 {
264   char *endp;
265 
266   if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2))
267       || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2])))
268     return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=<n>] expected");
269   line += 2;
270   if (*line == '=')
271     {
272       line ++;
273       if (!digitp (*line))
274 	return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required");
275 #if HAVE_W64_SYSTEM
276       *rfd = (void*)strtoull (line, &endp, 10);
277 #elif HAVE_W32_SYSTEM
278       *rfd = (void*)strtoul (line, &endp, 10);
279 #else
280       *rfd = strtoul (line, &endp, 10);
281 #endif
282       /* Remove that argument so that a notify handler won't see it. */
283       memset (line, ' ', endp? (endp-line):strlen(line));
284 
285       if (*rfd == ctx->inbound.fd)
286 	return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd");
287       if (*rfd == ctx->outbound.fd)
288 	return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd");
289       return 0;
290     }
291   else
292     /* Our peer has sent the file descriptor.  */
293     return assuan_receivefd (ctx, rfd);
294 }
295 
296 
297 static const char std_help_input[] =
298   "INPUT FD[=<N>]\n"
299   "\n"
300   "Used by a client to pass an input file descriptor to the server.\n"
301   "The server opens <N> as a local file descriptor.  Without <N>, the\n"
302   "server opens the file descriptor just sent by the client using\n"
303   "assuan_sendfd.";
304 static gpg_error_t
std_handler_input(assuan_context_t ctx,char * line)305 std_handler_input (assuan_context_t ctx, char *line)
306 {
307   gpg_error_t rc;
308   assuan_fd_t fd, oldfd;
309 
310   rc = assuan_command_parse_fd (ctx, line, &fd);
311   if (rc)
312     return PROCESS_DONE (ctx, rc);
313 
314 #ifdef HAVE_W32CE_SYSTEM
315   oldfd = fd;
316   fd = _assuan_w32ce_finish_pipe ((int)fd, 0);
317   if (fd == INVALID_HANDLE_VALUE)
318     return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_PARAMETER,
319 					 "rvid conversion failed"));
320   TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_input", ctx,
321 	  "turned RVID 0x%x into handle 0x%x", oldfd, fd);
322 #endif
323 
324   if (ctx->input_notify_fnc)
325     {
326       oldfd = ctx->input_fd;
327       ctx->input_fd = fd;
328       rc = ctx->input_notify_fnc (ctx, line);
329       if (rc)
330         ctx->input_fd = oldfd;
331     }
332   else if (!rc)
333     ctx->input_fd = fd;
334   return PROCESS_DONE (ctx, rc);
335 }
336 
337 
338 static const char std_help_output[] =
339   "OUTPUT FD[=<N>]\n"
340   "\n"
341   "Used by a client to pass an output file descriptor to the server.\n"
342   "The server opens <N> as a local file descriptor.  Without <N>, the\n"
343   "server opens the file descriptor just sent by the client using\n"
344   "assuan_sendfd.";
345 static gpg_error_t
std_handler_output(assuan_context_t ctx,char * line)346 std_handler_output (assuan_context_t ctx, char *line)
347 {
348   gpg_error_t rc;
349   assuan_fd_t fd, oldfd;
350 
351   rc = assuan_command_parse_fd (ctx, line, &fd);
352   if (rc)
353     return PROCESS_DONE (ctx, rc);
354 
355 #ifdef HAVE_W32CE_SYSTEM
356   oldfd = fd;
357   fd = _assuan_w32ce_finish_pipe ((int)fd, 1);
358   if (fd == INVALID_HANDLE_VALUE)
359     return PROCESS_DONE (ctx, set_error (ctx, gpg_err_code_from_syserror (),
360 					 "rvid conversion failed"));
361   TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_output", ctx,
362 	  "turned RVID 0x%x into handle 0x%x", oldfd, fd);
363 #endif
364 
365   if (ctx->output_notify_fnc)
366     {
367       oldfd = ctx->output_fd;
368       ctx->output_fd = fd;
369       rc = ctx->output_notify_fnc (ctx, line);
370       if (rc)
371         ctx->output_fd = oldfd;
372     }
373   else if (!rc)
374     ctx->output_fd = fd;
375   return PROCESS_DONE (ctx, rc);
376 }
377 
378 
379 /* This is a table with the standard commands and handler for them.
380    The table is used to initialize a new context and associate strings
381    with default handlers */
382 static struct {
383   const char *name;
384   gpg_error_t (*handler)(assuan_context_t, char *line);
385   const char *help;
386   int always; /* always initialize this command */
387 } std_cmd_table[] = {
388   { "NOP",    std_handler_nop, std_help_nop, 1 },
389   { "CANCEL", std_handler_cancel, std_help_cancel, 1 },
390   { "OPTION", std_handler_option, std_help_option, 1 },
391   { "BYE",    std_handler_bye, std_help_bye, 1 },
392   { "AUTH",   std_handler_auth, std_help_auth, 1 },
393   { "RESET",  std_handler_reset, std_help_reset, 1 },
394   { "END",    std_handler_end, std_help_end, 1 },
395   { "HELP",   std_handler_help, std_help_help, 1 },
396 
397   { "INPUT",  std_handler_input, std_help_input, 0 },
398   { "OUTPUT", std_handler_output, std_help_output, 0 },
399   { } };
400 
401 
402 /**
403  * assuan_register_command:
404  * @ctx: the server context
405  * @cmd_name: A string with the command name
406  * @handler: The handler function to be called or NULL to use a default
407  *           handler.
408  * HELPSTRING
409  *
410  * Register a handler to be used for a given command.  Note that
411  * several default handlers are already registered with a new context.
412  * This function however allows to override them.
413  *
414  * Return value: 0 on success or an error code
415  **/
416 gpg_error_t
assuan_register_command(assuan_context_t ctx,const char * cmd_name,assuan_handler_t handler,const char * help_string)417 assuan_register_command (assuan_context_t ctx, const char *cmd_name,
418                          assuan_handler_t handler, const char *help_string)
419 {
420   int i, cmd_index = -1;
421   const char *s;
422 
423   if (cmd_name && !*cmd_name)
424     cmd_name = NULL;
425 
426   if (!cmd_name)
427     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
428 
429   if (!handler)
430     { /* find a default handler. */
431       for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++)
432         ;
433       if (!s)
434         { /* Try again but case insensitive. */
435           for (i=0; (s=std_cmd_table[i].name)
436                     && my_strcasecmp (cmd_name, s); i++)
437             ;
438         }
439       if (s)
440         handler = std_cmd_table[i].handler;
441       if (!handler)
442         handler = dummy_handler; /* Last resort is the dummy handler. */
443     }
444 
445   if (!ctx->cmdtbl)
446     {
447       ctx->cmdtbl_size = 50;
448       ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
449       if (!ctx->cmdtbl)
450 	return _assuan_error (ctx, gpg_err_code_from_syserror ());
451       ctx->cmdtbl_used = 0;
452     }
453   else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
454     {
455       struct cmdtbl_s *x;
456 
457       x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
458       if (!x)
459 	return _assuan_error (ctx, gpg_err_code_from_syserror ());
460       ctx->cmdtbl = x;
461       ctx->cmdtbl_size += 50;
462     }
463 
464   for (i=0; i<ctx->cmdtbl_used; i++)
465     {
466       if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name))
467         {
468 	  cmd_index = i;
469 	  break;
470 	}
471     }
472 
473   if (cmd_index == -1)
474     cmd_index = ctx->cmdtbl_used++;
475 
476   ctx->cmdtbl[cmd_index].name = cmd_name;
477   ctx->cmdtbl[cmd_index].handler = handler;
478   ctx->cmdtbl[cmd_index].helpstr = help_string;
479   return 0;
480 }
481 
482 /* Return the name of the command currently processed by a handler.
483    The string returned is valid until the next call to an assuan
484    function on the same context.  Returns NULL if no handler is
485    executed or the command is not known.  */
486 const char *
assuan_get_command_name(assuan_context_t ctx)487 assuan_get_command_name (assuan_context_t ctx)
488 {
489   return ctx? ctx->current_cmd_name : NULL;
490 }
491 
492 gpg_error_t
assuan_register_pre_cmd_notify(assuan_context_t ctx,gpg_error_t (* fnc)(assuan_context_t,const char * cmd))493 assuan_register_pre_cmd_notify (assuan_context_t ctx,
494                                  gpg_error_t (*fnc)(assuan_context_t,
495 				   const char *cmd))
496 {
497   if (!ctx)
498     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
499   ctx->pre_cmd_notify_fnc = fnc;
500   return 0;
501 }
502 
503 gpg_error_t
assuan_register_post_cmd_notify(assuan_context_t ctx,void (* fnc)(assuan_context_t,gpg_error_t))504 assuan_register_post_cmd_notify (assuan_context_t ctx,
505                                  void (*fnc)(assuan_context_t, gpg_error_t))
506 {
507   if (!ctx)
508     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
509   ctx->post_cmd_notify_fnc = fnc;
510   return 0;
511 }
512 
513 gpg_error_t
assuan_register_bye_notify(assuan_context_t ctx,assuan_handler_t fnc)514 assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc)
515 {
516   if (!ctx)
517     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
518   ctx->bye_notify_fnc = fnc;
519   return 0;
520 }
521 
522 gpg_error_t
assuan_register_reset_notify(assuan_context_t ctx,assuan_handler_t fnc)523 assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc)
524 {
525   if (!ctx)
526     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
527   ctx->reset_notify_fnc = fnc;
528   return 0;
529 }
530 
531 gpg_error_t
assuan_register_cancel_notify(assuan_context_t ctx,assuan_handler_t fnc)532 assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc)
533 {
534   if (!ctx)
535     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
536   ctx->cancel_notify_fnc = fnc;
537   return 0;
538 }
539 
540 gpg_error_t
assuan_register_option_handler(assuan_context_t ctx,gpg_error_t (* fnc)(assuan_context_t,const char *,const char *))541 assuan_register_option_handler (assuan_context_t ctx,
542 				gpg_error_t (*fnc)(assuan_context_t,
543 						   const char*, const char*))
544 {
545   if (!ctx)
546     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
547   ctx->option_handler_fnc = fnc;
548   return 0;
549 }
550 
551 gpg_error_t
assuan_register_input_notify(assuan_context_t ctx,assuan_handler_t fnc)552 assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc)
553 {
554   if (!ctx)
555     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
556   ctx->input_notify_fnc = fnc;
557   return 0;
558 }
559 
560 gpg_error_t
assuan_register_output_notify(assuan_context_t ctx,assuan_handler_t fnc)561 assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc)
562 {
563   if (!ctx)
564     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
565   ctx->output_notify_fnc = fnc;
566   return 0;
567 }
568 
569 
570 /* Helper to register the standards commands */
571 gpg_error_t
_assuan_register_std_commands(assuan_context_t ctx)572 _assuan_register_std_commands (assuan_context_t ctx)
573 {
574   gpg_error_t rc;
575   int i;
576 
577   for (i = 0; std_cmd_table[i].name; i++)
578     {
579       if (std_cmd_table[i].always)
580         {
581           rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL);
582           if (rc)
583             return rc;
584         }
585     }
586   return 0;
587 }
588 
589 
590 
591 /* Process the special data lines.  The "D " has already been removed
592    from the line.  As all handlers this function may modify the line.  */
593 static gpg_error_t
handle_data_line(assuan_context_t ctx,char * line,int linelen)594 handle_data_line (assuan_context_t ctx, char *line, int linelen)
595 {
596   return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL);
597 }
598 
599 /* like ascii_strcasecmp but assume that B is already uppercase */
600 static int
my_strcasecmp(const char * a,const char * b)601 my_strcasecmp (const char *a, const char *b)
602 {
603     if (a == b)
604         return 0;
605 
606     for (; *a && *b; a++, b++)
607       {
608 	if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
609 	    break;
610       }
611     return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
612 }
613 
614 
615 /* Parse the line, break out the command, find it in the command
616    table, remove leading and white spaces from the arguments, call the
617    handler with the argument line and return the error.  */
618 static gpg_error_t
dispatch_command(assuan_context_t ctx,char * line,int linelen)619 dispatch_command (assuan_context_t ctx, char *line, int linelen)
620 {
621   gpg_error_t err;
622   char *p;
623   const char *s;
624   int shift, i;
625 
626   /* Note that as this function is invoked by assuan_process_next as
627      well, we need to hide non-critical errors with PROCESS_DONE.  */
628 
629   if (*line == 'D' && line[1] == ' ') /* divert to special handler */
630     /* FIXME: Depending on the final implementation of
631        handle_data_line, this may be wrong here.  For example, if a
632        user callback is invoked, and that callback is responsible for
633        calling assuan_process_done, then this is wrong.  */
634     return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2));
635 
636   for (p=line; *p && *p != ' ' && *p != '\t'; p++)
637     ;
638   if (p==line)
639     return PROCESS_DONE
640       (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space"));
641   if (*p)
642     { /* Skip over leading WS after the keyword */
643       *p++ = 0;
644       while ( *p == ' ' || *p == '\t')
645         p++;
646     }
647   shift = p - line;
648 
649   for (i=0; (s=ctx->cmdtbl[i].name); i++)
650     {
651       if (!strcmp (line, s))
652         break;
653     }
654   if (!s)
655     { /* and try case insensitive */
656       for (i=0; (s=ctx->cmdtbl[i].name); i++)
657         {
658           if (!my_strcasecmp (line, s))
659             break;
660         }
661     }
662   if (!s)
663     return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL));
664   line += shift;
665   /* linelen -= shift; -- not needed.  */
666 
667   if (ctx->pre_cmd_notify_fnc) {
668     err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name);
669 
670     if (err)
671       return PROCESS_DONE(ctx, err);
672   }
673 
674 /*    fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
675   ctx->current_cmd_name = ctx->cmdtbl[i].name;
676   err = ctx->cmdtbl[i].handler (ctx, line);
677   ctx->current_cmd_name = NULL;
678   return err;
679 }
680 
681 
682 /* Call this to acknowledge the current command.  */
683 gpg_error_t
assuan_process_done(assuan_context_t ctx,gpg_error_t rc)684 assuan_process_done (assuan_context_t ctx, gpg_error_t rc)
685 {
686   if (!ctx->in_command)
687     return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
688 
689   if (ctx->flags.force_close)
690     ctx->process_complete = 1;
691 
692   ctx->in_command = 0;
693 
694   /* Check for data write errors.  */
695   if (ctx->outbound.data.fp)
696     {
697       /* Flush the data lines.  */
698       fclose (ctx->outbound.data.fp);
699       ctx->outbound.data.fp = NULL;
700       if (!rc && ctx->outbound.data.error)
701 	rc = ctx->outbound.data.error;
702     }
703   else
704     {
705       /* Flush any data send without using the data FP.  */
706       assuan_send_data (ctx, NULL, 0);
707       if (!rc && ctx->outbound.data.error)
708 	rc = ctx->outbound.data.error;
709     }
710 
711   /* Error handling.  */
712   if (!rc)
713     {
714       if (ctx->process_complete)
715 	{
716 	  /* No error checking because the peer may have already
717 	     disconnect. */
718 	  assuan_write_line (ctx, "OK closing connection");
719 	  ctx->finish_handler (ctx);
720 	}
721       else
722 	rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK");
723     }
724   else
725     {
726       char errline[300];
727       const char *text = ctx->err_no == rc ? ctx->err_str : NULL;
728       char ebuf[50];
729 
730       if (ctx->flags.force_close)
731         text = "[closing connection]";
732 
733       gpg_strerror_r (rc, ebuf, sizeof (ebuf));
734       snprintf (errline, sizeof errline, "ERR %d %.50s <%.30s>%s%.100s",
735                 rc, ebuf, gpg_strsource (rc),
736                 text? " - ":"", text?text:"");
737 
738       rc = assuan_write_line (ctx, errline);
739 
740       if (ctx->flags.force_close)
741         ctx->finish_handler (ctx);
742     }
743 
744   if (ctx->post_cmd_notify_fnc)
745     ctx->post_cmd_notify_fnc (ctx, rc);
746 
747   ctx->flags.confidential = 0;
748   if (ctx->okay_line)
749     {
750       _assuan_free (ctx, ctx->okay_line);
751       ctx->okay_line = NULL;
752     }
753 
754   return rc;
755 }
756 
757 
758 static gpg_error_t
process_next(assuan_context_t ctx)759 process_next (assuan_context_t ctx)
760 {
761   gpg_error_t rc;
762 
763   /* What the next thing to do is depends on the current state.
764      However, we will always first read the next line.  The client is
765      required to write full lines without blocking long after starting
766      a partial line.  */
767   rc = _assuan_read_line (ctx);
768   if (_assuan_error_is_eagain (ctx, rc))
769     return 0;
770   if (gpg_err_code (rc) == GPG_ERR_EOF)
771     {
772       ctx->process_complete = 1;
773       return 0;
774     }
775   if (rc)
776     return rc;
777   if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
778      /* Comment lines are ignored.  */
779     return 0;
780 
781   /* Now we have a line that really means something.  It could be one
782      of the following things: First, if we are not in a command
783      already, it is the next command to dispatch.  Second, if we are
784      in a command, it can only be the response to an INQUIRE
785      reply.  */
786 
787   if (!ctx->in_command)
788     {
789       ctx->in_command = 1;
790 
791       ctx->outbound.data.error = 0;
792       ctx->outbound.data.linelen = 0;
793       /* Dispatch command and return reply.  */
794       ctx->in_process_next = 1;
795       rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
796       ctx->in_process_next = 0;
797     }
798   else if (ctx->in_inquire)
799     {
800       /* FIXME: Pick up the continuation.  */
801       rc = _assuan_inquire_ext_cb (ctx);
802     }
803   else
804     {
805       /* Should not happen.  The client is sending data while we are
806 	 in a command and not waiting for an inquire.  We log an error
807 	 and discard it.  */
808       TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx,
809 	      "unexpected client data");
810       rc = 0;
811     }
812 
813   return rc;
814 }
815 
816 
817 /* This function should be invoked when the assuan connected FD is
818    ready for reading.  If the equivalent to EWOULDBLOCK is returned
819    (this should be done by the command handler), assuan_process_next
820    should be invoked the next time the connected FD is readable.
821    Eventually, the caller will finish by invoking assuan_process_done.
822    DONE is set to 1 if the connection has ended.  */
823 gpg_error_t
assuan_process_next(assuan_context_t ctx,int * done)824 assuan_process_next (assuan_context_t ctx, int *done)
825 {
826   gpg_error_t rc;
827 
828   if (done)
829     *done = 0;
830   ctx->process_complete = 0;
831   do
832     {
833       rc = process_next (ctx);
834     }
835   while (!rc && !ctx->process_complete && assuan_pending_line (ctx));
836 
837   if (done)
838     *done = !!ctx->process_complete;
839 
840   return rc;
841 }
842 
843 
844 
845 static gpg_error_t
process_request(assuan_context_t ctx)846 process_request (assuan_context_t ctx)
847 {
848   gpg_error_t rc;
849 
850   if (ctx->in_inquire)
851     return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS);
852 
853   do
854     {
855       rc = _assuan_read_line (ctx);
856     }
857   while (_assuan_error_is_eagain (ctx, rc));
858   if (gpg_err_code (rc) == GPG_ERR_EOF)
859     {
860       ctx->process_complete = 1;
861       return 0;
862     }
863   if (rc)
864     return rc;
865   if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
866     return 0; /* comment line - ignore */
867 
868   ctx->in_command = 1;
869   ctx->outbound.data.error = 0;
870   ctx->outbound.data.linelen = 0;
871   /* dispatch command and return reply */
872   rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
873 
874   return assuan_process_done (ctx, rc);
875 }
876 
877 /**
878  * assuan_process:
879  * @ctx: assuan context
880  *
881  * This function is used to handle the assuan protocol after a
882  * connection has been established using assuan_accept().  This is the
883  * main protocol handler.
884  *
885  * Return value: 0 on success or an error code if the assuan operation
886  * failed.  Note, that no error is returned for operational errors.
887  **/
888 gpg_error_t
assuan_process(assuan_context_t ctx)889 assuan_process (assuan_context_t ctx)
890 {
891   gpg_error_t rc;
892 
893   ctx->process_complete = 0;
894   do {
895     rc = process_request (ctx);
896   } while (!rc && !ctx->process_complete);
897 
898   return rc;
899 }
900 
901 
902 /**
903  * assuan_get_active_fds:
904  * @ctx: Assuan context
905  * @what: 0 for read fds, 1 for write fds
906  * @fdarray: Caller supplied array to store the FDs
907  * @fdarraysize: size of that array
908  *
909  * Return all active filedescriptors for the given context.  This
910  * function can be used to select on the fds and call
911  * assuan_process_next() if there is an active one.  The first fd in
912  * the array is the one used for the command connection.
913  *
914  * Note, that write FDs are not yet supported.
915  *
916  * Return value: number of FDs active and put into @fdarray or -1 on
917  * error which is most likely a too small fdarray.
918  **/
919 int
assuan_get_active_fds(assuan_context_t ctx,int what,assuan_fd_t * fdarray,int fdarraysize)920 assuan_get_active_fds (assuan_context_t ctx, int what,
921                        assuan_fd_t *fdarray, int fdarraysize)
922 {
923   int n = 0;
924 
925   if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
926     return -1;
927 
928   if (!what)
929     {
930       if (ctx->inbound.fd != ASSUAN_INVALID_FD)
931         fdarray[n++] = ctx->inbound.fd;
932     }
933   else
934     {
935       if (ctx->outbound.fd != ASSUAN_INVALID_FD)
936         fdarray[n++] = ctx->outbound.fd;
937       if (ctx->outbound.data.fp)
938 #if defined(HAVE_W32CE_SYSTEM)
939         fdarray[n++] = (void*)fileno (ctx->outbound.data.fp);
940 #elif defined(HAVE_W32_SYSTEM)
941         fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp));
942 #else
943         fdarray[n++] = fileno (ctx->outbound.data.fp);
944 #endif
945     }
946 
947   return n;
948 }
949 
950 
951 /* Two simple wrappers to make the expected function types match. */
952 #ifdef HAVE_FUNOPEN
953 static int
fun1_cookie_write(void * cookie,const char * buffer,int orig_size)954 fun1_cookie_write (void *cookie, const char *buffer, int orig_size)
955 {
956   return _assuan_cookie_write_data (cookie, buffer, orig_size);
957 }
958 #endif /*HAVE_FUNOPEN*/
959 #ifdef HAVE_FOPENCOOKIE
960 static ssize_t
fun2_cookie_write(void * cookie,const char * buffer,size_t orig_size)961 fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size)
962 {
963   return _assuan_cookie_write_data (cookie, buffer, orig_size);
964 }
965 #endif /*HAVE_FOPENCOOKIE*/
966 
967 /* Return a FP to be used for data output.  The FILE pointer is valid
968    until the end of a handler.  So a close is not needed.  Assuan does
969    all the buffering needed to insert the status line as well as the
970    required line wappping and quoting for data lines.
971 
972    We use GNU's custom streams here.  There should be an alternative
973    implementaion for systems w/o a glibc, a simple implementation
974    could use a child process */
975 FILE *
assuan_get_data_fp(assuan_context_t ctx)976 assuan_get_data_fp (assuan_context_t ctx)
977 {
978 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
979   if (ctx->outbound.data.fp)
980     return ctx->outbound.data.fp;
981 
982 #ifdef HAVE_FUNOPEN
983   ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write,
984 				   0, _assuan_cookie_write_flush);
985 #else
986   ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write,
987 				   0, _assuan_cookie_write_flush);
988 #endif
989 
990   ctx->outbound.data.error = 0;
991   return ctx->outbound.data.fp;
992 #else
993   gpg_err_set_errno (ENOSYS);
994   return NULL;
995 #endif
996 }
997 
998 
999 /* Set the text used for the next OK response.  This string is
1000    automatically reset to NULL after the next command. */
1001 gpg_error_t
assuan_set_okay_line(assuan_context_t ctx,const char * line)1002 assuan_set_okay_line (assuan_context_t ctx, const char *line)
1003 {
1004   if (!ctx)
1005     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
1006   if (!line)
1007     {
1008       _assuan_free (ctx, ctx->okay_line);
1009       ctx->okay_line = NULL;
1010     }
1011   else
1012     {
1013       /* FIXME: we need to use gcry_is_secure() to test whether
1014          we should allocate the entire line in secure memory */
1015       char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1);
1016       if (!buf)
1017         return _assuan_error (ctx, gpg_err_code_from_syserror ());
1018       strcpy (buf, "OK ");
1019       strcpy (buf+3, line);
1020       _assuan_free (ctx, ctx->okay_line);
1021       ctx->okay_line = buf;
1022     }
1023   return 0;
1024 }
1025 
1026 
1027 
1028 gpg_error_t
assuan_write_status(assuan_context_t ctx,const char * keyword,const char * text)1029 assuan_write_status (assuan_context_t ctx,
1030                      const char *keyword, const char *text)
1031 {
1032   char buffer[256];
1033   char *helpbuf;
1034   size_t n;
1035   gpg_error_t ae;
1036 
1037   if ( !ctx || !keyword)
1038     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
1039   if (!text)
1040     text = "";
1041 
1042   n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
1043   if (n < sizeof (buffer))
1044     {
1045       strcpy (buffer, "S ");
1046       strcat (buffer, keyword);
1047       if (*text)
1048         {
1049           strcat (buffer, " ");
1050           strcat (buffer, text);
1051         }
1052       ae = assuan_write_line (ctx, buffer);
1053     }
1054   else if ( (helpbuf = _assuan_malloc (ctx, n)) )
1055     {
1056       strcpy (helpbuf, "S ");
1057       strcat (helpbuf, keyword);
1058       if (*text)
1059         {
1060           strcat (helpbuf, " ");
1061           strcat (helpbuf, text);
1062         }
1063       ae = assuan_write_line (ctx, helpbuf);
1064       _assuan_free (ctx, helpbuf);
1065     }
1066   else
1067     ae = 0;
1068   return ae;
1069 }
1070