[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