diff options
-rw-r--r-- | doc/ChangeLog | 4 | ||||
-rw-r--r-- | doc/assuan.texi | 207 | ||||
-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 |
9 files changed, 560 insertions, 83 deletions
diff --git a/doc/ChangeLog b/doc/ChangeLog index 3f76d12..ad028eb 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2007-08-09 Marcus Brinkmann <[email protected]> + + * assuan.texi (External I/O Loop): New chapter. + 2007-07-12 Werner Koch <[email protected]> * assuan.texi (Utilities): Document that under W32 we return a diff --git a/doc/assuan.texi b/doc/assuan.texi index f22a7f9..f49cd4f 100644 --- a/doc/assuan.texi +++ b/doc/assuan.texi @@ -115,6 +115,7 @@ interprocess communcation library. * Generalities:: Initialization code and data types used. * Client code:: How to develop an Assuan client. * Server code:: How to develop an Assuan server. +* External I/O Loop:: How to use external I/O event loops. * Utilities:: Utility functions. Miscellaneous @@ -1085,13 +1086,6 @@ individual command handlers, i.e. operational error, are not seen here. @end deftypefun -@deftypefun assuan_error_t assuan_process_next (@w{assuan_context_t @var{ctx}}) - -This is the same as @code{assuan_process} but the caller has to provide -the outer loop. He should loop as long as the return code is zero and -stop otherwise; @code{-1} or @code{GPG_ERR_EOF} indicate a regular end. -@end deftypefun - @noindent After the loop has terminated, the Assuan context needs to be released: @@ -1110,6 +1104,205 @@ context and calls to Assuan functions may modify that line. You are also allowed to modify that line which makes parsing much easier. +@c +@c E x t e r n a l I / O L o o p s +@c +@node External I/O Loop +@chapter How to use external I/O event loops + +The above implementations of an Assuan client and server are +synchronous, insofar the main routines block until a request or client +connection is completely processed. In some programs, for example GUI +applications, this is undesirable. Instead, Assuan operations should +be non-blocking, and the caller should be able to poll all involved +file descriptors to determine when the next Assuan function can be +invoked without blocking. + +To make this possible, client and server have to adhere to some rules: +@itemize @bullet +@item +Either partner should always write full lines. If partial lines are +written, the remainder of the line should b sent without delay. +@item +Either partner should eagerly receive status messages. While +receiving and sending bulk data may be delayed, the status +communication channel is different: Both partners may send status +messages in blocking mode at any time the protocol allows them to send +such status messages. To ensure that these send operations do not +actually block the sender, the recipient must be ready to receive +without undue delay. +@item +If descriptor passing is used over a socket, the descriptor must be +sent after the corresponding command without undue delay. +@end itemize + +Together, these restrictions allow to limit the need for asynchronous +I/O operations to bulk data and the inbound status file descriptor. + +In addition to the above rules, client and server should adhere to the +following implementation guidelines. + +@menu +* External I/O Loop Client:: External I/O event loops in the client. +* External I/O Loop Server:: External I/O event loops in the server. +@end menu + +@node External I/O Loop Client +@section External I/O event loops in the client. + +The reference implementation for using external I/O event loops in the +client is the GPGME library, which exports its own external I/O event +loop mechanism and utilizes the Assuan library transparently for the +user. The following steps document how GPGME achieves this. + +@enumerate +@item +Before connecting, set up pipes for bulk data transfer (using the +INPUT/OUTPUT commands, for example). These are passed to the server +either by inheritance (using a pipe server) or by FD passing (using a +socket server). + +@item +Then you need to connect to the server. GPGME uses a pipe server, so +it just spawns a server process, which is a non-blocking operation. +FIXME: Currently, using a client with external event loop over a +socket connection is not supported. It is easy to support (we just +need a variation of @code{assuan_socket_connect} which takes an +already connected socket FD and turns it into an Assuan context), so +if you need this let us know. + +@item +After connecting, get the inbound status FD with +@code{assuan_get_active_fds} (the first one returned is the status +FD). This FD can be duplicated if it is convenient (GPGME does this +to be able to close this FD and associated callback handlers without +disrupting Assuan's internals). + +@item +Then register the Assuan inbound status FD and all bulk data FDs with +the I/O event mechanism. In general, this requires setting up +callback handlers for these FDs and registering them with the main +event loop. + +@item +When bulk data FDs become ready, you can simply perform the +corresponding read or write operations. When the inbound status FD +becomes ready, you can receive the next server line with +assuan_read_line(). + +@item +You should close and unregister the bulk data FDs when you wrote all +data (for outbound FDs) or receive an EOF (for inbound FDs). When you +receive an ERR from the server, or an OK for the final operation, you +can unregister the inbound status FD and call @code{assuan_disconnect} +to close it. + +@item +As noted above, all send operations on the outbound status FD are done +immediate with blocking. In GPGME, this has never caused any problems. + +@item +The @code{INQUIRE} function can be handled in two ways: If the +requested data is immediately available, the client can just send the +data blockingly. If the requested data needs to be fetched from a +blocking source, a callback handler can be registered for the FD with +the main event loop. GPGME does not support the @code{INQUIRE} +function, so we do not have any practical experience with this. +@end enumerate + +Currently, the client can not cancel a pending operation gracefully. +It can, however, disconnect from the server at any time. It is the +responsibility of the server to periodically send status messages to +the client to probe if the connection remains alive. + + +@node External I/O Loop Server +@section External I/O event loops in the server. + +Currently, no Assuan server exists which uses external I/O event +loops. However, the following guidelines should lead to a usable +implementation: + +@enumerate +@item +For socket servers: You can not use @code{assuan_accept}, so you +should just implement the bind/connect/listen/accept stage yourself. +You can register the listen FD with your main event loop, accept the +connection when it becomes ready, and finally call +@code{assuan_init_socket_server_ext} with the final argument being 2 +to create an Assuan context for this connection. This way you can +also handle multiple connections in parallel. The reference +implementation for this approach is DirMngr. + +For pipe servers: @code{assuan_init_pipe_server} creates an Assuan +context valid for the pipe FDs. + +@item +Once you have a context for a single connection, you can get the +inbound status FD with @code{assuan_get_active_fds} (the first one +returned is the status FD). This FD can be duplicated if it is +convenient. Every time the inbound status FD is readable, you should +invoke the function @code{assuan_process_next} (see below) to process +the next incoming message. @code{assuan_process_next} processes as +many status lines as can be received by a single @code{read} +operation. When it returns, the inbound status FD may still be +readable, but Assuan does not check this. + +The function @code{assuan_process_next} returns 0 if it can not make +progress reliably, and it returns an end of file error code if the +client closed the connection. See below for more information on this +function. + +@item +The command will be dispatched by @code{assuan_process_next} just as +with @code{assuan_process}, however, you will want to implement the +command handlers in such a way that they do not block. For example, +the command handler may just register the bulk data FDs with the main +event loop and return. + +When the command is finished, irregardless if this happens directly in +the command handler or later, you must call @code{assuan_process_done} +with an appropriate error code (or 0 for success) to return an +appropriate status line to the client. You can do this at the end of +the command handler, for example by ending it with @code{return +assuan_process_done (error_code);}. Another possibility is to invoke +@code{assuan_process_done} from the place in the code which closes the +last active bulk FD registered with the main event loop for this +operation. +@end enumerate + +It is not possible to use @code{assuan_inquire} in a command handler, +as this function blocks on receiving the inquired data from the +client. Instead, the asynchronous version @code{assuan_inquire_ext} +needs to be used (see below), which invokes a callback when the client +provided the inquired data. A typical usage would be for the command +handler to register a continuation with @code{assuan_inquire_ext} and +return 0. Eventually, the continuation would be invoked by +@code{assuan_process_next} when the client data arrived. The +continuation could complete the command and eventually call +@code{assuan_process_done}. + +Cancellation is supported by returning an appropriate error code to +the client with @code{assuan_process_done}. For long running +operations, the server should send progress status messages to the +client in regular intervals to notice when the client disconnects. + +@deftypefun assuan_error_t assuan_process_next (@w{assuan_context_t @var{ctx}}) +This is the same as @code{assuan_process} but the caller has to +provide the outer loop. He should loop as long as the return code is +zero and stop otherwise; @code{-1} or @code{GPG_ERR_EOF} indicate a +regular end. +@end deftypefun + +@deftypefun assuan_error_t assuan_inquire_ext (@w{assuan_context_t @var{ctx}}, @w{const char *@var{keyword}}, @w{unsigned char **@var{r_buffer}}, @w{size_t *@var{r_length}}, @w{size_t @var{maxlen}}, @w{int (*@var{cb}) (void *cb_data, int rc)}, @w{void *@var{cb_data}}) + +This is the same as @code{assuan_inquire} but the caller has to +provide the outer loop (using @code{assuan_process_next}). The caller +should specify a continuation with @var{cb}, which receives +@var{cb_data} as its first argument. +@end deftypefun + + @c @c U T I L I T I E S 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) { |