diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 23 | ||||
-rw-r--r-- | src/assuan-defs.h | 23 | ||||
-rw-r--r-- | src/assuan-handler.c | 216 | ||||
-rw-r--r-- | src/assuan-inquire.c | 151 | ||||
-rw-r--r-- | src/assuan-pipe-server.c | 1 | ||||
-rw-r--r-- | src/assuan.h | 9 | ||||
-rwxr-xr-x | src/mkerrors | 9 |
7 files changed, 356 insertions, 76 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index ad04bb9..68905b6 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,26 @@ +2007-08-09 Marcus Brinkmann <[email protected]> + + * assuan.h (assuan_process_done, assuan_inquire_ext): New + prototypes. + * assuan-defs.h (struct assuan_context_s): New members + in_process_next, in_command, inquire_cb, inquire_cb_data, + inquire_r_buffer, inquire_r_buffer_len, inquire_membuf. + (_assuan_inquire_ext_cb, _assuan_inquire_release): New prototypes. + * assuan-handler.c (PROCESS_DONE): New macro. + (dummy_handler, std_handler_nop, std_handler_cancel) + (std_handler_option, std_handler_bye, std_handler_auth) + (std_handler_reset, std_handler_end): Use PROCESS_DONE to + optionally call assuan_process_done if CTX->in_process_next is + true. + (assuan_process_done, process_next): New functions. + (assuan_process_next): Rewritten to support external event + handling. + * mkerrors: Do not clear high bits of -1 for old style EOF. + * assuan-inquire.c (_assuan_inquire_release) + (_assuan_inquire_ext_cb, assuan_inquire_ext): New functions. + * assuan-pipe-server.c (_assuan_release_context): Call + _assuan_inquire_release. + 2007-07-12 Werner Koch <[email protected]> * assuan.h (assuan_fd_t): New. diff --git a/src/assuan-defs.h b/src/assuan-defs.h index d1037c8..52b0d8b 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -103,6 +103,16 @@ struct assuan_context_s int confidential; int is_server; /* Set if this is context belongs to a server */ int in_inquire; + int in_process_next; + int in_command; + + /* The following members are used by assuan_inquire_ext. */ + int (*inquire_cb) (void *cb_data, int rc); + void *inquire_cb_data; + unsigned char **inquire_r_buffer; + size_t *inquire_r_buffer_len; + void *inquire_membuf; + char *hello_line; char *okay_line; /* See assuan_set_okay_line() */ @@ -229,17 +239,20 @@ assuan_error_t _assuan_read_from_server (assuan_context_t ctx, /*-- assuan-error.c --*/ +/*-- assuan-inquire.c --*/ +int _assuan_inquire_ext_cb (assuan_context_t ctx); +void _assuan_inquire_release (assuan_context_t ctx); -/* Map error codes as used in this implementaion to the libgpg-error +/* Map error codes as used in this implementation to the libgpg-error codes. */ assuan_error_t _assuan_error (int oldcode); -/* Extrac the erro code from A. This works for both the old and the - new style error codes. This needs to be whenever an error code is - compared. */ +/* Extract the error code from A. This works for both the old and the + new style error codes. This needs to be used whenever an error + code is compared. */ #define err_code(a) ((a) & 0x00ffffff) -/* Check whether A is the erro code for EOF. We allow forold and new +/* Check whether A is the erro code for EOF. We allow for old and new style EOF error codes here. */ #define err_is_eof(a) ((a) == (-1) || err_code (a) == 16383) diff --git a/src/assuan-handler.c b/src/assuan-handler.c index c940c36..1cb514a 100644 --- a/src/assuan-handler.c +++ b/src/assuan-handler.c @@ -33,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 @@ -52,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 @@ -63,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) @@ -80,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) { @@ -94,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 @@ -109,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 @@ -126,13 +135,13 @@ 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_end (assuan_context_t ctx, char *line) { - return set_error (ctx, Not_Implemented, NULL); + return PROCESS_DONE (ctx, set_error (ctx, Not_Implemented, NULL)); } @@ -181,11 +190,11 @@ std_handler_input (assuan_context_t ctx, char *line) 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> */ @@ -197,11 +206,11 @@ std_handler_output (assuan_context_t ctx, char *line) 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); } @@ -414,9 +423,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) { @@ -461,42 +471,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_command) + return _assuan_error (ASSUAN_General_Error); - if (ctx->in_inquire) - return _assuan_error (ASSUAN_Nested_Commands); - - 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"); @@ -509,26 +511,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:""); @@ -562,7 +564,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, @@ -577,19 +579,117 @@ 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 (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); + + rc = _assuan_read_line (ctx); + 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 @@ -618,24 +718,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 diff --git a/src/assuan-inquire.c b/src/assuan-inquire.c index e81ecb3..080f4ef 100644 --- a/src/assuan-inquire.c +++ b/src/assuan-inquire.c @@ -42,7 +42,7 @@ struct membuf -/* A simple implemnation of a dynamic buffer. Use init_membuf() to +/* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter @@ -232,8 +232,157 @@ assuan_inquire (assuan_context_t ctx, const char *keyword, return rc; } + +void +_assuan_inquire_release (assuan_context_t ctx) +{ + if (ctx->in_inquire) + { + if (ctx->inquire_membuf) + { + free_membuf (ctx->inquire_membuf); + free (ctx->inquire_membuf); + } + ctx->in_inquire = 0; + } +} + + +int +_assuan_inquire_ext_cb (assuan_context_t ctx) +{ + int rc; + unsigned char *line; + int linelen; + struct membuf *mb; + unsigned char *p; + line = (unsigned char *) ctx->inbound.line; + linelen = ctx->inbound.linelen; + mb = ctx->inquire_membuf; + if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') + { + rc = _assuan_error (ASSUAN_Canceled); + goto leave; + } + if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' + && (!line[3] || line[3] == ' ')) + { + rc = 0; + goto leave; + } + + if (line[0] != 'D' || line[1] != ' ' || mb == NULL) + { + rc = _assuan_error (ASSUAN_Unexpected_Command); + goto leave; + } + + if (linelen < 3) + return 0; + line += 2; + linelen -= 2; + + p = line; + while (linelen) + { + for (;linelen && *p != '%'; linelen--, p++) + ; + put_membuf (mb, line, p-line); + if (linelen > 2) + { /* handle escaping */ + unsigned char tmp[1]; + p++; + *tmp = xtoi_2 (p); + p += 2; + linelen -= 3; + put_membuf (mb, tmp, 1); + } + line = p; + } + if (mb->too_large) + { + rc = _assuan_error (ASSUAN_Too_Much_Data); + goto leave; + } + return 0; + leave: + if (mb) + { + *(ctx->inquire_r_buffer) = get_membuf (mb, ctx->inquire_r_buffer_len); + if (!*(ctx->inquire_r_buffer)) + rc = _assuan_error (ASSUAN_Out_Of_Core); + free_membuf (mb); + free (mb); + } + (ctx->inquire_cb) (ctx->inquire_cb_data, rc); + return rc; +} +/** + * assuan_inquire_ext: + * @ctx: An assuan context + * @keyword: The keyword used for the inquire + * @r_buffer: Returns an allocated buffer + * @r_length: Returns the length of this buffer + * @maxlen: If not 0, the size limit of the inquired data. + * @cb: A callback handler which is invoked after the operation completed. + * @cb_data: A user-provided value passed to the callback handler. + * + * A Server may use this to Send an inquire. r_buffer, r_length and + * maxlen may all be NULL/0 to indicate that no real data is expected. + * When this function returns, + * + * Return value: 0 on success or an ASSUAN error code + **/ +assuan_error_t +assuan_inquire_ext (assuan_context_t ctx, const char *keyword, + unsigned char **r_buffer, size_t *r_length, size_t maxlen, + int (*cb) (void *cb_data, int rc), void *cb_data) +{ + assuan_error_t rc; + struct membuf *mb = NULL; + char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ + int nodataexpected; + + if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) + return _assuan_error (ASSUAN_Invalid_Value); + nodataexpected = !r_buffer && !r_length && !maxlen; + if (!nodataexpected && (!r_buffer || !r_length)) + return _assuan_error (ASSUAN_Invalid_Value); + if (!ctx->is_server) + return _assuan_error (ASSUAN_Not_A_Server); + if (ctx->in_inquire) + return _assuan_error (ASSUAN_Nested_Commands); + + if (!nodataexpected) + { + mb = malloc (sizeof (struct membuf)); + if (!mb) + return _assuan_error (ASSUAN_Out_Of_Core); + init_membuf (mb, maxlen ? maxlen : 1024, maxlen); + } + + strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); + rc = assuan_write_line (ctx, cmdbuf); + if (rc) + { + free_membuf (mb); + free (mb); + return rc; + } + + ctx->in_inquire = 1; + + /* Set up the continuation. */ + ctx->inquire_cb = cb; + ctx->inquire_cb_data = cb_data; + ctx->inquire_membuf = mb; + ctx->inquire_r_buffer = r_buffer; + ctx->inquire_r_buffer_len = r_length; + + return 0; +} diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c index 96bdfcc..e5dc15f 100644 --- a/src/assuan-pipe-server.c +++ b/src/assuan-pipe-server.c @@ -167,6 +167,7 @@ _assuan_release_context (assuan_context_t ctx) { if (ctx) { + _assuan_inquire_release (ctx); xfree (ctx->hello_line); xfree (ctx->okay_line); xfree (ctx->cmdtbl); diff --git a/src/assuan.h b/src/assuan.h index 5acf08c..3105602 100644 --- a/src/assuan.h +++ b/src/assuan.h @@ -75,6 +75,7 @@ _ASSUAN_PREFIX(assuan_register_option_handler) #define assuan_process _ASSUAN_PREFIX(assuan_process) #define assuan_process_next _ASSUAN_PREFIX(assuan_process_next) +#define assuan_process_done _ASSUAN_PREFIX(assuan_process_done) #define assuan_get_active_fds _ASSUAN_PREFIX(assuan_get_active_fds) #define assuan_get_data_fp _ASSUAN_PREFIX(assuan_get_data_fp) #define assuan_set_okay_line _ASSUAN_PREFIX(assuan_set_okay_line) @@ -102,6 +103,7 @@ #define assuan_get_peercred _ASSUAN_PREFIX(assuan_get_peercred) #define assuan_transact _ASSUAN_PREFIX(assuan_transact) #define assuan_inquire _ASSUAN_PREFIX(assuan_inquire) +#define assuan_inquire_ext _ASSUAN_PREFIX(assuan_inquire_ext) #define assuan_read_line _ASSUAN_PREFIX(assuan_read_line) #define assuan_pending_line _ASSUAN_PREFIX(assuan_pending_line) #define assuan_write_line _ASSUAN_PREFIX(assuan_write_line) @@ -372,6 +374,7 @@ int assuan_register_option_handler (assuan_context_t ctx, int assuan_process (assuan_context_t ctx); int assuan_process_next (assuan_context_t ctx); +int assuan_process_done (assuan_context_t ctx, int rc); int assuan_get_active_fds (assuan_context_t ctx, int what, assuan_fd_t *fdarray, int fdarraysize); @@ -462,7 +465,11 @@ assuan_transact (assuan_context_t ctx, assuan_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen); - +assuan_error_t assuan_inquire_ext (assuan_context_t ctx, const char *keyword, + unsigned char **r_buffer, size_t *r_length, + size_t maxlen, + int (*cb) (void *cb_data, int rc), + void *cb_data); /*-- assuan-buffer.c --*/ assuan_error_t assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen); diff --git a/src/mkerrors b/src/mkerrors index 4f37e06..7087fdb 100755 --- a/src/mkerrors +++ b/src/mkerrors @@ -56,8 +56,13 @@ _assuan_error (int oldcode) unsigned int n; if (!err_source) - return (oldcode & 0x00ffffff); /* Make sure that the gpg-error - source part is cleared. */ + { + if (oldcode == -1) + return -1; + else + return (oldcode & 0x00ffffff); /* Make sure that the gpg-error + source part is cleared. */ + } switch (oldcode) { |