1
/* assuan-handler.c - dispatch commands
2
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
4
* This file is part of Assuan.
6
* Assuan is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU Lesser General Public License as
8
* published by the Free Software Foundation; either version 2.1 of
9
* the License, or (at your option) any later version.
11
* Assuan is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
26
#include "assuan-defs.h"
28
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
29
#define digitp(a) ((a) >= '0' && (a) <= '9')
33
/* Provide structure for our dummy replacement function. Usually this
34
is defined in ../common/util.h but assuan should be self
36
/* Fixme: Remove fopencoookie :-(( */
39
ssize_t (*read)(void*,char*,size_t);
40
ssize_t (*write)(void*,const char*,size_t);
41
int (*seek)(void*,off_t*,int);
43
} _IO_cookie_io_functions_t;
44
typedef _IO_cookie_io_functions_t cookie_io_functions_t;
45
FILE *fopencookie (void *cookie, const char *opentype,
46
cookie_io_functions_t funclist);
47
#endif /*!HAVE_FOPENCOOKIE*/
53
dummy_handler (ASSUAN_CONTEXT ctx, char *line)
55
return set_error (ctx, Server_Fault, "no handler registered");
60
std_handler_nop (ASSUAN_CONTEXT ctx, char *line)
66
std_handler_cancel (ASSUAN_CONTEXT ctx, char *line)
68
if (ctx->cancel_notify_fnc)
69
ctx->cancel_notify_fnc (ctx);
70
return set_error (ctx, Not_Implemented, NULL);
74
std_handler_option (ASSUAN_CONTEXT ctx, char *line)
76
char *key, *value, *p;
78
for (key=line; spacep (key); key++)
81
return set_error (ctx, Syntax_Error, "argument required");
83
return set_error (ctx, Syntax_Error, "no option name given");
84
for (value=key; *value && !spacep (value) && *value != '='; value++)
89
*value++ = 0; /* terminate key */
90
for (; spacep (value); value++)
94
*value++ = 0; /* terminate key */
95
for (; spacep (value); value++)
98
return set_error (ctx, Syntax_Error, "option argument expected");
102
for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
105
*++p = 0; /* strip trailing spaces */
109
if (*key == '-' && key[1] == '-' && key[2])
110
key += 2; /* the double dashes are optional */
112
return set_error (ctx, Syntax_Error,
113
"option should not begin with one dash");
115
if (ctx->option_handler_fnc)
116
return ctx->option_handler_fnc (ctx, key, value);
121
std_handler_bye (ASSUAN_CONTEXT ctx, char *line)
123
if (ctx->bye_notify_fnc)
124
ctx->bye_notify_fnc (ctx);
125
assuan_close_input_fd (ctx);
126
assuan_close_output_fd (ctx);
127
return -1; /* pretty simple :-) */
131
std_handler_auth (ASSUAN_CONTEXT ctx, char *line)
133
return set_error (ctx, Not_Implemented, NULL);
137
std_handler_reset (ASSUAN_CONTEXT ctx, char *line)
139
if (ctx->reset_notify_fnc)
140
ctx->reset_notify_fnc (ctx);
141
assuan_close_input_fd (ctx);
142
assuan_close_output_fd (ctx);
147
std_handler_end (ASSUAN_CONTEXT ctx, char *line)
149
return set_error (ctx, Not_Implemented, NULL);
153
parse_cmd_input_output (ASSUAN_CONTEXT ctx, char *line, int *rfd)
157
if (strncmp (line, "FD=", 3))
158
return set_error (ctx, Syntax_Error, "FD=<n> expected");
161
return set_error (ctx, Syntax_Error, "number required");
162
*rfd = strtoul (line, &endp, 10);
163
/* remove that argument so that a notify handler won't see it */
164
memset (line, ' ', endp? (endp-line):strlen(line));
166
if (*rfd == ctx->inbound.fd)
167
return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
168
if (*rfd == ctx->outbound.fd)
169
return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
173
/* Format is INPUT FD=<n> */
175
std_handler_input (ASSUAN_CONTEXT ctx, char *line)
179
rc = parse_cmd_input_output (ctx, line, &fd);
183
if (ctx->input_notify_fnc)
184
ctx->input_notify_fnc (ctx, line);
188
/* Format is OUTPUT FD=<n> */
190
std_handler_output (ASSUAN_CONTEXT ctx, char *line)
194
rc = parse_cmd_input_output (ctx, line, &fd);
198
if (ctx->output_notify_fnc)
199
ctx->output_notify_fnc (ctx, line);
207
/* This is a table with the standard commands and handler for them.
208
The table is used to initialize a new context and assuciate strings
209
and handlers with cmd_ids */
213
int (*handler)(ASSUAN_CONTEXT, char *line);
214
int always; /* always initialize this command */
215
} std_cmd_table[] = {
216
{ "NOP", ASSUAN_CMD_NOP, std_handler_nop, 1 },
217
{ "CANCEL", ASSUAN_CMD_CANCEL, std_handler_cancel, 1 },
218
{ "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
219
{ "BYE", ASSUAN_CMD_BYE, std_handler_bye, 1 },
220
{ "AUTH", ASSUAN_CMD_AUTH, std_handler_auth, 1 },
221
{ "RESET", ASSUAN_CMD_RESET, std_handler_reset, 1 },
222
{ "END", ASSUAN_CMD_END, std_handler_end, 1 },
224
{ "INPUT", ASSUAN_CMD_INPUT, std_handler_input },
225
{ "OUTPUT", ASSUAN_CMD_OUTPUT, std_handler_output },
226
{ "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
232
* assuan_register_command:
233
* @ctx: the server context
234
* @cmd_id: An ID value for the command
235
* @cmd_name: A string with the command name
236
* @handler: The handler function to be called
238
* Register a handler to be used for a given command.
240
* The @cmd_name must be %NULL or an empty string for all @cmd_ids
241
* below %ASSUAN_CMD_USER because predefined values are used.
246
assuan_register_command (ASSUAN_CONTEXT ctx,
247
int cmd_id, const char *cmd_name,
248
int (*handler)(ASSUAN_CONTEXT, char *))
252
if (cmd_name && !*cmd_name)
255
if (cmd_id < ASSUAN_CMD_USER)
258
return ASSUAN_Invalid_Value; /* must be NULL for these values*/
260
for (i=0; std_cmd_table[i].name; i++)
262
if (std_cmd_table[i].cmd_id == cmd_id)
264
cmd_name = std_cmd_table[i].name;
266
handler = std_cmd_table[i].handler;
270
if (!std_cmd_table[i].name)
271
return ASSUAN_Invalid_Value; /* not a pre-registered one */
275
handler = dummy_handler;
278
return ASSUAN_Invalid_Value;
280
/* fprintf (stderr, "DBG-assuan: registering %d as `%s'\n", cmd_id, cmd_name); */
284
ctx->cmdtbl_size = 50;
285
ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
287
return ASSUAN_Out_Of_Core;
288
ctx->cmdtbl_used = 0;
290
else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
294
x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
296
return ASSUAN_Out_Of_Core;
298
ctx->cmdtbl_size += 50;
301
ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
302
ctx->cmdtbl[ctx->cmdtbl_used].cmd_id = cmd_id;
303
ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
309
assuan_register_bye_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
312
return ASSUAN_Invalid_Value;
313
ctx->bye_notify_fnc = fnc;
318
assuan_register_reset_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
321
return ASSUAN_Invalid_Value;
322
ctx->reset_notify_fnc = fnc;
327
assuan_register_cancel_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
330
return ASSUAN_Invalid_Value;
331
ctx->cancel_notify_fnc = fnc;
336
assuan_register_option_handler (ASSUAN_CONTEXT ctx,
337
int (*fnc)(ASSUAN_CONTEXT,
338
const char*, const char*))
341
return ASSUAN_Invalid_Value;
342
ctx->option_handler_fnc = fnc;
347
assuan_register_input_notify (ASSUAN_CONTEXT ctx,
348
void (*fnc)(ASSUAN_CONTEXT, const char *))
351
return ASSUAN_Invalid_Value;
352
ctx->input_notify_fnc = fnc;
357
assuan_register_output_notify (ASSUAN_CONTEXT ctx,
358
void (*fnc)(ASSUAN_CONTEXT, const char *))
361
return ASSUAN_Invalid_Value;
362
ctx->output_notify_fnc = fnc;
367
/* Helper to register the standards commands */
369
_assuan_register_std_commands (ASSUAN_CONTEXT ctx)
373
for (i=0; std_cmd_table[i].name; i++)
375
if (std_cmd_table[i].always)
377
rc = assuan_register_command (ctx, std_cmd_table[i].cmd_id,
388
/* Process the special data lines. The "D " has already been removed
389
from the line. As all handlers this function may modify the line. */
391
handle_data_line (ASSUAN_CONTEXT ctx, char *line, int linelen)
393
return set_error (ctx, Not_Implemented, NULL);
396
/* like ascii_strcasecmp but assume that B is already uppercase */
398
my_strcasecmp (const char *a, const char *b)
403
for (; *a && *b; a++, b++)
405
if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
408
return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
411
/* Parse the line, break out the command, find it in the command
412
table, remove leading and white spaces from the arguments, all the
413
handler with the argument line and return the error */
415
dispatch_command (ASSUAN_CONTEXT ctx, char *line, int linelen)
421
if (*line == 'D' && line[1] == ' ') /* divert to special handler */
422
return handle_data_line (ctx, line+2, linelen-2);
424
for (p=line; *p && *p != ' ' && *p != '\t'; p++)
427
return set_error (ctx, Invalid_Command, "leading white-space");
429
{ /* Skip over leading WS after the keyword */
431
while ( *p == ' ' || *p == '\t')
436
for (i=0; (s=ctx->cmdtbl[i].name); i++)
438
if (!strcmp (line, s))
442
{ /* and try case insensitive */
443
for (i=0; (s=ctx->cmdtbl[i].name); i++)
445
if (!my_strcasecmp (line, s))
450
return set_error (ctx, Unknown_Command, NULL);
454
/* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
455
return ctx->cmdtbl[i].handler (ctx, line);
462
process_request (ASSUAN_CONTEXT ctx)
467
return ASSUAN_Nested_Commands;
469
rc = _assuan_read_line (ctx);
472
if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
473
return 0; /* comment line - ignore */
475
ctx->outbound.data.error = 0;
476
ctx->outbound.data.linelen = 0;
477
/* dispatch command and return reply */
478
rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
479
/* check from data write errors */
480
if (ctx->outbound.data.fp)
481
{ /* Flush the data lines */
482
fclose (ctx->outbound.data.fp);
483
ctx->outbound.data.fp = NULL;
484
if (!rc && ctx->outbound.data.error)
485
rc = ctx->outbound.data.error;
487
else /* flush any data send w/o using the data fp */
489
assuan_send_data (ctx, NULL, 0);
490
if (!rc && ctx->outbound.data.error)
491
rc = ctx->outbound.data.error;
496
rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK");
499
{ /* No error checking because the peer may have already disconnect */
500
assuan_write_line (ctx, "OK closing connection");
501
ctx->finish_handler (ctx);
508
sprintf (errline, "ERR %d server fault (%.50s)",
509
ASSUAN_Server_Fault, assuan_strerror (rc));
512
const char *text = ctx->err_no == rc? ctx->err_str:NULL;
514
sprintf (errline, "ERR %d %.50s%s%.100s",
515
rc, assuan_strerror (rc), text? " - ":"", text?text:"");
517
rc = assuan_write_line (ctx, errline);
520
ctx->confidential = 0;
523
xfree (ctx->okay_line);
524
ctx->okay_line = NULL;
531
* @ctx: assuan context
533
* This fucntion is used to handle the assuan protocol after a
534
* connection has been established using assuan_accept(). This is the
535
* main protocol handler.
537
* Return value: 0 on success or an error code if the assuan operation
538
* failed. Note, that no error is returned for operational errors.
541
assuan_process (ASSUAN_CONTEXT ctx)
546
rc = process_request (ctx);
557
* assuan_process_next:
558
* @ctx: Assuan context
560
* Same as assuan_process() but the user has to provide the outer
561
* loop. He should loop as long as the return code is zero and stop
562
* otherwise; -1 is regular end.
564
* See also: assuan_get_active_fds()
565
* Return value: -1 for end of server, 0 on success or an error code
568
assuan_process_next (ASSUAN_CONTEXT ctx)
570
return process_request (ctx);
575
* assuan_get_active_fds:
576
* @ctx: Assuan context
577
* @what: 0 for read fds, 1 for write fds
578
* @fdarray: Caller supplied array to store the FDs
579
* @fdarraysize: size of that array
581
* Return all active filedescriptors for the given context. This
582
* function can be used to select on the fds and call
583
* assuan_process_next() if there is an active one. The first fd in
584
* the array is the one used for the command connection.
586
* Note, that write FDs are not yet supported.
588
* Return value: number of FDs active and put into @fdarray or -1 on
589
* error which is most likely a too small fdarray.
592
assuan_get_active_fds (ASSUAN_CONTEXT ctx, int what,
593
int *fdarray, int fdarraysize)
597
if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
602
if (ctx->inbound.fd != -1)
603
fdarray[n++] = ctx->inbound.fd;
607
if (ctx->outbound.fd != -1)
608
fdarray[n++] = ctx->outbound.fd;
609
if (ctx->outbound.data.fp)
610
fdarray[n++] = fileno (ctx->outbound.data.fp);
616
/* Return a FP to be used for data output. The FILE pointer is valid
617
until the end of a handler. So a close is not needed. Assuan does
618
all the buffering needed to insert the status line as well as the
619
required line wappping and quoting for data lines.
621
We use GNU's custom streams here. There should be an alternative
622
implementaion for systems w/o a glibc, a simple implementation
623
could use a child process */
625
assuan_get_data_fp (ASSUAN_CONTEXT ctx)
627
cookie_io_functions_t cookie_fnc;
629
if (ctx->outbound.data.fp)
630
return ctx->outbound.data.fp;
632
cookie_fnc.read = NULL;
633
cookie_fnc.write = _assuan_cookie_write_data;
634
cookie_fnc.seek = NULL;
635
cookie_fnc.close = _assuan_cookie_write_flush;
637
ctx->outbound.data.fp = fopencookie (ctx, "wb", cookie_fnc);
638
ctx->outbound.data.error = 0;
639
return ctx->outbound.data.fp;
643
/* Set the text used for the next OK reponse. This string is
644
automatically reset to NULL after the next command. */
646
assuan_set_okay_line (ASSUAN_CONTEXT ctx, const char *line)
649
return ASSUAN_Invalid_Value;
652
xfree (ctx->okay_line);
653
ctx->okay_line = NULL;
657
/* FIXME: we need to use gcry_is_secure() to test whether
658
we should allocate the entire line in secure memory */
659
char *buf = xtrymalloc (3+strlen(line)+1);
661
return ASSUAN_Out_Of_Core;
663
strcpy (buf+3, line);
664
xfree (ctx->okay_line);
665
ctx->okay_line = buf;
673
assuan_write_status (ASSUAN_CONTEXT ctx, const char *keyword, const char *text)
679
if ( !ctx || !keyword)
684
n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
685
if (n < sizeof (buffer))
687
strcpy (buffer, "S ");
688
strcat (buffer, keyword);
691
strcat (buffer, " ");
692
strcat (buffer, text);
694
assuan_write_line (ctx, buffer);
696
else if ( (helpbuf = xtrymalloc (n)) )
698
strcpy (helpbuf, "S ");
699
strcat (helpbuf, keyword);
702
strcat (helpbuf, " ");
703
strcat (helpbuf, text);
705
assuan_write_line (ctx, helpbuf);