[svn] assuan - r247 - in trunk: doc src
svn author marcus
cvs at cvs.gnupg.org
Thu Aug 9 12:44:32 CEST 2007
Author: marcus
Date: 2007-08-09 12:44:02 +0200 (Thu, 09 Aug 2007)
New Revision: 247
Modified:
trunk/doc/ChangeLog
trunk/doc/assuan.texi
trunk/src/ChangeLog
trunk/src/assuan-defs.h
trunk/src/assuan-handler.c
trunk/src/assuan-inquire.c
trunk/src/assuan-pipe-server.c
trunk/src/assuan.h
trunk/src/mkerrors
Log:
doc/
2007-08-09 Marcus Brinkmann <marcus at g10code.de>
* assuan.texi (External I/O Loop): New chapter.
src/
2007-08-09 Marcus Brinkmann <marcus at g10code.de>
* 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.
Modified: trunk/doc/ChangeLog
===================================================================
--- trunk/doc/ChangeLog 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/doc/ChangeLog 2007-08-09 10:44:02 UTC (rev 247)
@@ -1,3 +1,7 @@
+2007-08-09 Marcus Brinkmann <marcus at g10code.de>
+
+ * assuan.texi (External I/O Loop): New chapter.
+
2007-07-12 Werner Koch <wk at g10code.com>
* assuan.texi (Utilities): Document that under W32 we return a
Modified: trunk/doc/assuan.texi
===================================================================
--- trunk/doc/assuan.texi 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/doc/assuan.texi 2007-08-09 10:44:02 UTC (rev 247)
@@ -115,6 +115,7 @@
* 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 @@
@end deftypefun
- at 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.
- at end deftypefun
-
@noindent
After the loop has terminated, the Assuan context needs to be released:
@@ -1110,7 +1104,206 @@
also allowed to modify that line which makes parsing much easier.
+ at c
+ at c E x t e r n a l I / O L o o p s
+ at c
+ at node External I/O Loop
+ at 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:
+ at itemize @bullet
+ at item
+Either partner should always write full lines. If partial lines are
+written, the remainder of the line should b sent without delay.
+ at 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.
+ at item
+If descriptor passing is used over a socket, the descriptor must be
+sent after the corresponding command without undue delay.
+ at 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.
+
+ at 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.
+ at end menu
+
+ at node External I/O Loop Client
+ at 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.
+
+ at enumerate
+ at 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).
+
+ at 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.
+
+ at item
+After connecting, get the inbound status FD with
+ at 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).
+
+ at 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.
+
+ at 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().
+
+ at 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.
+
+ at 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.
+
+ at 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.
+ at 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.
+
+
+ at node External I/O Loop Server
+ at 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:
+
+ at enumerate
+ at 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
+ at 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.
+
+ at 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.
+
+ at 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
+ at 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.
+ at 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
+ at code{assuan_process_next} when the client data arrived. The
+continuation could complete the command and eventually call
+ at 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.
+
+ at 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.
+ at end deftypefun
+
+ at 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
+ at var{cb_data} as its first argument.
+ at end deftypefun
+
+
+
@c
@c U T I L I T I E S
@c
Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/ChangeLog 2007-08-09 10:44:02 UTC (rev 247)
@@ -1,3 +1,26 @@
+2007-08-09 Marcus Brinkmann <marcus at g10code.de>
+
+ * 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 <wk at g10code.com>
* assuan.h (assuan_fd_t): New.
Modified: trunk/src/assuan-defs.h
===================================================================
--- trunk/src/assuan-defs.h 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/assuan-defs.h 2007-08-09 10:44:02 UTC (rev 247)
@@ -103,6 +103,16 @@
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.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)
Modified: trunk/src/assuan-handler.c
===================================================================
--- trunk/src/assuan-handler.c 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/assuan-handler.c 2007-08-09 10:44:02 UTC (rev 247)
@@ -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 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
+ ctx->in_command = 0;
- rc = _assuan_read_line (ctx);
- if (rc)
- return rc;
- if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
- return 0; /* comment line - ignore */
-
- 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 @@
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 @@
{
/* 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 @@
}
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_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
Modified: trunk/src/assuan-inquire.c
===================================================================
--- trunk/src/assuan-inquire.c 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/assuan-inquire.c 2007-08-09 10:44:02 UTC (rev 247)
@@ -42,7 +42,7 @@
-/* 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 @@
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;
+}
Modified: trunk/src/assuan-pipe-server.c
===================================================================
--- trunk/src/assuan-pipe-server.c 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/assuan-pipe-server.c 2007-08-09 10:44:02 UTC (rev 247)
@@ -167,6 +167,7 @@
{
if (ctx)
{
+ _assuan_inquire_release (ctx);
xfree (ctx->hello_line);
xfree (ctx->okay_line);
xfree (ctx->cmdtbl);
Modified: trunk/src/assuan.h
===================================================================
--- trunk/src/assuan.h 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/assuan.h 2007-08-09 10:44:02 UTC (rev 247)
@@ -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_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_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);
Modified: trunk/src/mkerrors
===================================================================
--- trunk/src/mkerrors 2007-07-12 15:26:33 UTC (rev 246)
+++ trunk/src/mkerrors 2007-08-09 10:44:02 UTC (rev 247)
@@ -56,8 +56,13 @@
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)
{
More information about the Gnupg-commits
mailing list