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