diff options
Diffstat (limited to 'assuan/assuan-handler.c')
-rw-r--r-- | assuan/assuan-handler.c | 290 |
1 files changed, 206 insertions, 84 deletions
diff --git a/assuan/assuan-handler.c b/assuan/assuan-handler.c index d1b29cff..b940bfd4 100644 --- a/assuan/assuan-handler.c +++ b/assuan/assuan-handler.c @@ -1,5 +1,5 @@ /* assuan-handler.c - dispatch commands - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2007 Free Software Foundation, Inc. * * This file is part of Assuan. * @@ -14,9 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. + * License along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <config.h> @@ -35,18 +33,21 @@ static int my_strcasecmp (const char *a, const char *b); +#define PROCESS_DONE(ctx, rc) \ + ((ctx)->in_process_next ? assuan_process_done ((ctx), (rc)) : (rc)) static int dummy_handler (assuan_context_t ctx, char *line) { - return set_error (ctx, Server_Fault, "no handler registered"); + return + PROCESS_DONE (ctx, set_error (ctx, Server_Fault, "no handler registered")); } static int std_handler_nop (assuan_context_t ctx, char *line) { - return 0; /* okay */ + return PROCESS_DONE (ctx, 0); /* okay */ } static int @@ -54,7 +55,7 @@ std_handler_cancel (assuan_context_t ctx, char *line) { if (ctx->cancel_notify_fnc) ctx->cancel_notify_fnc (ctx); - return set_error (ctx, Not_Implemented, NULL); + return PROCESS_DONE (ctx, set_error (ctx, Not_Implemented, NULL)); } static int @@ -65,9 +66,12 @@ std_handler_option (assuan_context_t ctx, char *line) for (key=line; spacep (key); key++) ; if (!*key) - return set_error (ctx, Syntax_Error, "argument required"); + return + PROCESS_DONE (ctx, set_error (ctx, Syntax_Error, "argument required")); if (*key == '=') - return set_error (ctx, Syntax_Error, "no option name given"); + return + PROCESS_DONE (ctx, set_error (ctx, Syntax_Error, + "no option name given")); for (value=key; *value && !spacep (value) && *value != '='; value++) ; if (*value) @@ -82,7 +86,9 @@ std_handler_option (assuan_context_t ctx, char *line) for (; spacep (value); value++) ; if (!*value) - return set_error (ctx, Syntax_Error, "option argument expected"); + return + PROCESS_DONE (ctx, set_error (ctx, Syntax_Error, + "option argument expected")); } if (*value) { @@ -96,12 +102,13 @@ std_handler_option (assuan_context_t ctx, char *line) if (*key == '-' && key[1] == '-' && key[2]) key += 2; /* the double dashes are optional */ if (*key == '-') - return set_error (ctx, Syntax_Error, - "option should not begin with one dash"); + return PROCESS_DONE (ctx, + set_error (ctx, Syntax_Error, + "option should not begin with one dash")); if (ctx->option_handler_fnc) - return ctx->option_handler_fnc (ctx, key, value); - return 0; + return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value)); + return PROCESS_DONE (ctx, 0); } static int @@ -111,13 +118,13 @@ std_handler_bye (assuan_context_t ctx, char *line) ctx->bye_notify_fnc (ctx); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); - return -1; /* pretty simple :-) */ + return PROCESS_DONE (ctx, _assuan_error (-1)); /* pretty simple :-) */ } static int std_handler_auth (assuan_context_t ctx, char *line) { - return set_error (ctx, Not_Implemented, NULL); + return PROCESS_DONE (ctx, set_error (ctx, Not_Implemented, NULL)); } static int @@ -128,17 +135,35 @@ std_handler_reset (assuan_context_t ctx, char *line) assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); _assuan_uds_close_fds (ctx); - return 0; + return PROCESS_DONE (ctx, 0); } static int +std_handler_help (assuan_context_t ctx, char *line) +{ + unsigned int i; + char buf[ASSUAN_LINELENGTH]; + + for (i = 0; i < ctx->cmdtbl_used; i++) + { + snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name); + buf[ASSUAN_LINELENGTH - 1] = '\0'; + assuan_write_line (ctx, buf); + } + + return PROCESS_DONE (ctx, 0); +} + + +static int std_handler_end (assuan_context_t ctx, char *line) { - return set_error (ctx, Not_Implemented, NULL); + return PROCESS_DONE (ctx, set_error (ctx, Not_Implemented, NULL)); } + assuan_error_t -assuan_command_parse_fd (assuan_context_t ctx, char *line, int *rfd) +assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd) { char *endp; @@ -151,7 +176,13 @@ assuan_command_parse_fd (assuan_context_t ctx, char *line, int *rfd) line ++; if (!digitp (*line)) return set_error (ctx, Syntax_Error, "number required"); +#ifdef HAVE_W32_SYSTEM + /* Fixme: For a W32/64bit system we will need to change the cast + and the conversion fucntion. */ + *rfd = (void*)strtoul (line, &endp, 10); +#else *rfd = strtoul (line, &endp, 10); +#endif /* Remove that argument so that a notify handler won't see it. */ memset (line, ' ', endp? (endp-line):strlen(line)); @@ -166,34 +197,37 @@ assuan_command_parse_fd (assuan_context_t ctx, char *line, int *rfd) return assuan_receivefd (ctx, rfd); } + /* Format is INPUT FD=<n> */ static int std_handler_input (assuan_context_t ctx, char *line) { - int rc, fd; + int rc; + assuan_fd_t fd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) - return rc; + return PROCESS_DONE (ctx, rc); ctx->input_fd = fd; if (ctx->input_notify_fnc) ctx->input_notify_fnc (ctx, line); - return 0; + return PROCESS_DONE (ctx, 0); } /* Format is OUTPUT FD=<n> */ static int std_handler_output (assuan_context_t ctx, char *line) { - int rc, fd; + int rc; + assuan_fd_t fd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) - return rc; + return PROCESS_DONE (ctx, rc); ctx->output_fd = fd; if (ctx->output_notify_fnc) ctx->output_notify_fnc (ctx, line); - return 0; + return PROCESS_DONE (ctx, 0); } @@ -215,11 +249,12 @@ static struct { { "AUTH", std_handler_auth, 1 }, { "RESET", std_handler_reset, 1 }, { "END", std_handler_end, 1 }, + { "HELP", std_handler_help, 1 }, - { "INPUT", std_handler_input }, - { "OUTPUT", std_handler_output }, + { "INPUT", std_handler_input, 0 }, + { "OUTPUT", std_handler_output, 0 }, { "OPTION", std_handler_option, 1 }, - { NULL } + { NULL, NULL, 0 } }; @@ -406,9 +441,10 @@ my_strcasecmp (const char *a, const char *b) return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b); } + /* Parse the line, break out the command, find it in the command table, remove leading and white spaces from the arguments, call the - handler with the argument line and return the error */ + handler with the argument line and return the error. */ static int dispatch_command (assuan_context_t ctx, char *line, int linelen) { @@ -416,13 +452,21 @@ dispatch_command (assuan_context_t ctx, char *line, int linelen) const char *s; int shift, i; + /* Note that as this function is invoked by assuan_process_next as + well, we need to hide non-critical errors with PROCESS_DONE. */ + if (*line == 'D' && line[1] == ' ') /* divert to special handler */ - return handle_data_line (ctx, line+2, linelen-2); + /* FIXME: Depending on the final implementation of + handle_data_line, this may be wrong here. For example, if a + user callback is invoked, and that callback is responsible for + calling assuan_process_done, then this is wrong. */ + return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2)); for (p=line; *p && *p != ' ' && *p != '\t'; p++) ; if (p==line) - return set_error (ctx, Syntax_Error, "leading white-space"); + return PROCESS_DONE + (ctx, set_error (ctx, Syntax_Error, "leading white-space")); if (*p) { /* Skip over leading WS after the keyword */ *p++ = 0; @@ -445,7 +489,7 @@ dispatch_command (assuan_context_t ctx, char *line, int linelen) } } if (!s) - return set_error (ctx, Unknown_Command, NULL); + return PROCESS_DONE (ctx, set_error (ctx, Unknown_Command, NULL)); line += shift; linelen -= shift; @@ -453,42 +497,34 @@ dispatch_command (assuan_context_t ctx, char *line, int linelen) return ctx->cmdtbl[i].handler (ctx, line); } - - -static int -process_request (assuan_context_t ctx) +/* Call this to acknowledge the current command. */ +int +assuan_process_done (assuan_context_t ctx, int rc) { - int rc; - - if (ctx->in_inquire) - return _assuan_error (ASSUAN_Nested_Commands); + if (!ctx->in_command) + return _assuan_error (ASSUAN_General_Error); - rc = _assuan_read_line (ctx); - if (rc) - return rc; - if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) - return 0; /* comment line - ignore */ + ctx->in_command = 0; - ctx->outbound.data.error = 0; - ctx->outbound.data.linelen = 0; - /* dispatch command and return reply */ - rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); - /* check from data write errors */ + /* Check for data write errors. */ if (ctx->outbound.data.fp) - { /* Flush the data lines */ + { + /* Flush the data lines. */ fclose (ctx->outbound.data.fp); ctx->outbound.data.fp = NULL; if (!rc && ctx->outbound.data.error) - rc = ctx->outbound.data.error; + rc = ctx->outbound.data.error; } - else /* flush any data send w/o using the data fp */ + else { + /* Flush any data send without using the data FP. */ assuan_send_data (ctx, NULL, 0); if (!rc && ctx->outbound.data.error) - rc = ctx->outbound.data.error; + rc = ctx->outbound.data.error; } - /* Error handling */ + + /* Error handling. */ if (!rc) { rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK"); @@ -501,26 +537,26 @@ process_request (assuan_context_t ctx) else { char errline[300]; - + if (rc < 100) sprintf (errline, "ERR %d server fault (%.50s)", _assuan_error (ASSUAN_Server_Fault), assuan_strerror (rc)); else { const char *text = ctx->err_no == rc? ctx->err_str:NULL; - + #if defined(HAVE_W32_SYSTEM) unsigned int source, code; char ebuf[50]; const char *esrc; - + source = ((rc >> 24) & 0xff); code = (rc & 0x00ffffff); if (source && !_assuan_gpg_strerror_r (rc, ebuf, sizeof ebuf) && (esrc=_assuan_gpg_strsource (rc))) { - /* Assume this is an libgpg-error. */ + /* Assume this is an libgpg-error. */ sprintf (errline, "ERR %d %.50s <%.30s>%s%.100s", rc, ebuf, esrc, text? " - ":"", text?text:""); @@ -554,7 +590,7 @@ process_request (assuan_context_t ctx) { /* Assume this is an libgpg-error. */ char ebuf[50]; - + gpg_strerror_r (rc, ebuf, sizeof ebuf ); sprintf (errline, "ERR %d %.50s <%.30s>%s%.100s", rc, @@ -569,19 +605,123 @@ process_request (assuan_context_t ctx) } rc = assuan_write_line (ctx, errline); } - + if (ctx->post_cmd_notify_fnc) ctx->post_cmd_notify_fnc (ctx, rc); - + ctx->confidential = 0; if (ctx->okay_line) { xfree (ctx->okay_line); ctx->okay_line = NULL; } + + return rc; +} + + +static int +process_next (assuan_context_t ctx) +{ + int rc; + + /* What the next thing to do is depends on the current state. + However, we will always first read the next line. The client is + required to write full lines without blocking long after starting + a partial line. */ + rc = _assuan_read_line (ctx); + if (_assuan_error_is_eagain (rc)) + return 0; + if (rc) + return rc; + if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) + /* Comment lines are ignored. */ + return 0; + + /* Now we have a line that really means something. It could be one + of the following things: First, if we are not in a command + already, it is the next command to dispatch. Second, if we are + in a command, it can only be the response to an INQUIRE + reply. */ + + if (!ctx->in_command) + { + ctx->in_command = 1; + + ctx->outbound.data.error = 0; + ctx->outbound.data.linelen = 0; + /* Dispatch command and return reply. */ + ctx->in_process_next = 1; + rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); + ctx->in_process_next = 0; + } + else if (ctx->in_inquire) + { + /* FIXME: Pick up the continuation. */ + rc = _assuan_inquire_ext_cb (ctx); + } + else + { + /* Should not happen. The client is sending data while we are + in a command and not waiting for an inquire. We log an error + and discard it. */ + _assuan_log_printf ("unexpected client data\n"); + rc = 0; + } + return rc; } + +/* This function should be invoked when the assuan connected FD is + ready for reading. If the equivalent to EWOULDBLOCK is returned + (this should be done by the command handler), assuan_process_next + should be invoked the next time the connected FD is readable. + Eventually, the caller will finish by invoking + assuan_process_done. */ +int +assuan_process_next (assuan_context_t ctx) +{ + int rc; + + do + { + rc = process_next (ctx); + } + while (!rc && assuan_pending_line (ctx)); + + return rc; +} + + + +static int +process_request (assuan_context_t ctx) +{ + int rc; + + if (ctx->in_inquire) + return _assuan_error (ASSUAN_Nested_Commands); + + do + { + rc = _assuan_read_line (ctx); + } + while (_assuan_error_is_eagain (rc)); + if (rc) + return rc; + if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) + return 0; /* comment line - ignore */ + + ctx->in_command = 1; + ctx->outbound.data.error = 0; + ctx->outbound.data.linelen = 0; + /* dispatch command and return reply */ + rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); + + return assuan_process_done (ctx, rc); +} + /** * assuan_process: * @ctx: assuan context @@ -610,24 +750,6 @@ assuan_process (assuan_context_t ctx) /** - * assuan_process_next: - * @ctx: Assuan context - * - * Same as assuan_process() but the user has to provide the outer - * loop. He should loop as long as the return code is zero and stop - * otherwise; -1 is regular end. - * - * See also: assuan_get_active_fds() - * Return value: -1 for end of server, 0 on success or an error code - **/ -int -assuan_process_next (assuan_context_t ctx) -{ - return process_request (ctx); -} - - -/** * assuan_get_active_fds: * @ctx: Assuan context * @what: 0 for read fds, 1 for write fds @@ -646,7 +768,7 @@ assuan_process_next (assuan_context_t ctx) **/ int assuan_get_active_fds (assuan_context_t ctx, int what, - int *fdarray, int fdarraysize) + assuan_fd_t *fdarray, int fdarraysize) { int n = 0; @@ -655,16 +777,16 @@ assuan_get_active_fds (assuan_context_t ctx, int what, if (!what) { - if (ctx->inbound.fd != -1) + if (ctx->inbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->inbound.fd; } else { - if (ctx->outbound.fd != -1) + if (ctx->outbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->outbound.fd; if (ctx->outbound.data.fp) #ifdef HAVE_W32_SYSTEM - fdarray[n++] = _get_osfhandle (fileno (ctx->outbound.data.fp)); + fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp)); #else fdarray[n++] = fileno (ctx->outbound.data.fp); #endif |