2005-03-24 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / assuan / assuan-buffer.c
index bd08817..d9163b7 100644 (file)
@@ -1,21 +1,21 @@
 /* assuan-buffer.c - read and send data
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
  *
- * This file is part of GnuPG.
+ * This file is part of Assuan.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
  *
- * GnuPG is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 
  */
 
 #include <config.h>
 #include <errno.h>
 #include <unistd.h>
 #include <assert.h>
-
 #include "assuan-defs.h"
 
-
 static int
-writen ( int fd, const char *buffer, size_t length )
+writen (ASSUAN_CONTEXT ctx, const char *buffer, size_t length)
 {
   while (length)
     {
-      int nwritten = write (fd, buffer, length);
+      ssize_t nwritten = ctx->io->writefnc (ctx, buffer, length);
       
       if (nwritten < 0)
         {
@@ -48,9 +46,10 @@ writen ( int fd, const char *buffer, size_t length )
   return 0;  /* okay */
 }
 
-/* read an entire line */
+/* Read an entire line.  */
 static int
-readline (int fd, char *buf, size_t buflen, int *r_nread, int *eof)
+readline (ASSUAN_CONTEXT ctx, char *buf, size_t buflen,
+         int *r_nread, int *eof)
 {
   size_t nleft = buflen;
   char *p;
@@ -59,7 +58,8 @@ readline (int fd, char *buf, size_t buflen, int *r_nread, int *eof)
   *r_nread = 0;
   while (nleft > 0)
     {
-      int n = read (fd, buf, nleft);
+      ssize_t n = ctx->io->readfnc (ctx, buf, nleft);
+
       if (n < 0)
         {
           if (errno == EINTR)
@@ -75,10 +75,9 @@ readline (int fd, char *buf, size_t buflen, int *r_nread, int *eof)
       nleft -= n;
       buf += n;
       *r_nread += n;
-      
-      for (; n && *p != '\n'; n--, p++)
-        ;
-      if (n)
+
+      p = memrchr (p, '\n', n);
+      if (p)
         break; /* at least one full line available - that's enough for now */
     }
 
@@ -90,8 +89,9 @@ int
 _assuan_read_line (ASSUAN_CONTEXT ctx)
 {
   char *line = ctx->inbound.line;
-  int n, nread, atticlen;
+  int nread, atticlen;
   int rc;
+  char *endp = 0;
 
   if (ctx->inbound.eof)
     return -1;
@@ -101,97 +101,102 @@ _assuan_read_line (ASSUAN_CONTEXT ctx)
     {
       memcpy (line, ctx->inbound.attic.line, atticlen);
       ctx->inbound.attic.linelen = 0;
-      for (n=0; n < atticlen && line[n] != '\n'; n++)
-        ;
-      if (n < atticlen)
+
+      endp = memchr (line, '\n', atticlen);
+      if (endp)
+       /* Found another line in the attic.  */
        {
-         rc = 0; /* found another line in the attic */
+         rc = 0;
          nread = atticlen;
          atticlen = 0;
        }
       else
-        { /* read the rest */
+       /* There is pending data but not a full line.  */
+        {
           assert (atticlen < LINELENGTH);
-          rc = readline (ctx->inbound.fd, line + atticlen,
+          rc = readline (ctx, line + atticlen,
                         LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
         }
     }
   else
-    rc = readline (ctx->inbound.fd, line, LINELENGTH,
+    /* No pending data.  */
+    rc = readline (ctx, line, LINELENGTH,
                    &nread, &ctx->inbound.eof);
   if (rc)
     {
       if (ctx->log_fp)
-        fprintf (ctx->log_fp, "%p <- [Error: %s]\n",
-                 ctx, strerror (errno)); 
+       fprintf (ctx->log_fp, "%s[%p] <- [Error: %s]\n",
+                assuan_get_assuan_log_prefix (), ctx, strerror (errno));
       return ASSUAN_Read_Error;
     }
   if (!nread)
     {
       assert (ctx->inbound.eof);
       if (ctx->log_fp)
-        fprintf (ctx->log_fp, "%p <- [EOF]\n", ctx); 
-      return -1; 
+       fprintf (ctx->log_fp, "%s[%p] <- [EOF]\n",
+                assuan_get_assuan_log_prefix (), ctx);
+      return -1;
     }
 
   ctx->inbound.attic.pending = 0;
   nread += atticlen;
-  for (n=0; n < nread; n++)
+
+  if (! endp)
+    endp = memchr (line, '\n', nread);
+
+  if (endp)
     {
-      if (line[n] == '\n')
-        {
-          if (n+1 < nread)
-            {
-              char *s, *d;
-              int i;
-
-              n++;
-              /* we have to copy the rest because the handlers are
-                 allowed to modify the passed buffer */
-              for (d=ctx->inbound.attic.line, s=line+n, i=nread-n; i; i--)
-                {
-                  if (*s=='\n')
-                    ctx->inbound.attic.pending = 1;
-                  *d++ = *s++;
-                }
-              ctx->inbound.attic.linelen = nread-n;
-              n--;
-            }
-          if (n && line[n-1] == '\r')
-            n--;
-          line[n] = 0;
-          ctx->inbound.linelen = n;
-          if (ctx->log_fp)
-            {
-              fprintf (ctx->log_fp, "%p <- ", ctx); 
-              if (ctx->confidential)
-                fputs ("[Confidential data not shown]", ctx->log_fp);
-              else
-                _assuan_log_print_buffer (ctx->log_fp, 
-                                          ctx->inbound.line,
-                                          ctx->inbound.linelen);
-              putc ('\n', ctx->log_fp);
-            }
-          return 0;
-        }
-    }
+      int n = endp - line + 1;
+      if (n < nread)
+       /* LINE contains more than one line.  We copy it to the attic
+          now as handlers are allowed to modify the passed
+          buffer.  */
+       {
+         int len = nread - n;
+         memcpy (ctx->inbound.attic.line, endp + 1, len);
+         ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
+         ctx->inbound.attic.linelen = len;
+       }
 
-  if (ctx->log_fp)
-    fprintf (ctx->log_fp, "%p <- [Invalid line]\n", ctx);
-  *line = 0;
-  ctx->inbound.linelen = 0;
-  return ctx->inbound.eof? ASSUAN_Line_Not_Terminated : ASSUAN_Line_Too_Long;
+      if (endp != line && endp[-1] == '\r')
+       endp --;
+      *endp = 0;
+
+      ctx->inbound.linelen = endp - line;
+      if (ctx->log_fp)
+       {
+         fprintf (ctx->log_fp, "%s[%p] <- ",
+                  assuan_get_assuan_log_prefix (), ctx);
+         if (ctx->confidential)
+           fputs ("[Confidential data not shown]", ctx->log_fp);
+         else
+           _assuan_log_print_buffer (ctx->log_fp,
+                                     ctx->inbound.line,
+                                     ctx->inbound.linelen);
+         putc ('\n', ctx->log_fp);
+       }
+      return 0;
+    }
+  else
+    {
+      if (ctx->log_fp)
+       fprintf (ctx->log_fp, "%s[%p] <- [Invalid line]\n",
+                assuan_get_assuan_log_prefix (), ctx);
+      *line = 0;
+      ctx->inbound.linelen = 0;
+      return ctx->inbound.eof ? ASSUAN_Line_Not_Terminated
+       : ASSUAN_Line_Too_Long;
+    }
 }
 
 
 /* Read the next line from the client or server and return a pointer
-   to a buffer with holding that line.  linelen returns the length of
-   the line.  This buffer is valid until another read operation is
-   done on this buffer.  The caller is allowed to modify this buffer.
-   He should only use the buffer if the function returns without an
-   error.
+   in *LINE to a buffer holding the line.  LINELEN is the length of
+   *LINE.  The buffer is valid until the next read operation on it.
+   The caller may modify the buffer.  The buffer is invalid (i.e. must
+   not be used) if an error is returned.
 
-   Returns: 0 on success or an assuan error code
+   Returns 0 on success or an assuan error code.
    See also: assuan_pending_line().
 */
 AssuanError
@@ -209,8 +214,8 @@ assuan_read_line (ASSUAN_CONTEXT ctx, char **line, size_t *linelen)
 }
 
 
-/* Return true when a full line is pending for a read, without the need
-   for actual IO */
+/* Return true if a full line is buffered (i.e. an entire line may be
+   read without any I/O).  */
 int
 assuan_pending_line (ASSUAN_CONTEXT ctx)
 {
@@ -219,31 +224,43 @@ assuan_pending_line (ASSUAN_CONTEXT ctx)
 
 
 AssuanError 
-assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
+assuan_write_line (ASSUAN_CONTEXT ctx, const char *line)
 {
   int rc;
-  
+  size_t len;
+  const char *s;
+
   if (!ctx)
     return ASSUAN_Invalid_Value;
 
-  /* fixme: we should do some kind of line buffering */
+  /* Make sure that we never take a LF from the user - this might
+     violate the protocol. */
+  s = strchr (line, '\n');
+  len = s? (s-line) : strlen (line);
+
+  if (len > LINELENGTH - 2)
+    return ASSUAN_Line_Too_Long;
+
+  /* fixme: we should do some kind of line buffering.  */
   if (ctx->log_fp)
     {
-      fprintf (ctx->log_fp, "%p -> ", ctx); 
+      fprintf (ctx->log_fp, "%s[%p] -> ",
+              assuan_get_assuan_log_prefix (), ctx);
+      if (s)
+       fputs ("[supplied line contained a LF]", ctx->log_fp);
       if (ctx->confidential)
-        fputs ("[Confidential data not shown]", ctx->log_fp);
+       fputs ("[Confidential data not shown]", ctx->log_fp);
       else
-        _assuan_log_print_buffer (ctx->log_fp, 
-                                  line, strlen (line));
+       _assuan_log_print_buffer (ctx->log_fp, line, len);
       putc ('\n', ctx->log_fp);
     }
 
-  rc = writen (ctx->outbound.fd, line, strlen(line));
+  rc = writen (ctx, line, len);
   if (rc)
     rc = ASSUAN_Write_Error;
   if (!rc)
     {
-      rc = writen (ctx->outbound.fd, "\n", 1);
+      rc = writen (ctx, "\n", 1);
       if (rc)
         rc = ASSUAN_Write_Error;
     }
@@ -300,7 +317,9 @@ _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
         {
           if (ctx->log_fp)
             {
-              fprintf (ctx->log_fp, "%p -> ", ctx); 
+             fprintf (ctx->log_fp, "%s[%p] -> ",
+                      assuan_get_assuan_log_prefix (), ctx);
+
               if (ctx->confidential)
                 fputs ("[Confidential data not shown]", ctx->log_fp);
               else 
@@ -311,7 +330,7 @@ _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
             }
           *line++ = '\n';
           linelen++;
-          if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
+          if (writen (ctx, ctx->outbound.data.line, linelen))
             {
               ctx->outbound.data.error = ASSUAN_Write_Error;
               return 0;
@@ -344,19 +363,19 @@ _assuan_cookie_write_flush (void *cookie)
   if (linelen)
     {
       if (ctx->log_fp)
-        {
-          fprintf (ctx->log_fp, "%p -> ", ctx); 
-          if (ctx->confidential)
-            fputs ("[Confidential data not shown]", ctx->log_fp);
-          else
-            _assuan_log_print_buffer (ctx->log_fp, 
-                                      ctx->outbound.data.line,
-                                      linelen);
-          putc ('\n', ctx->log_fp);
-            }
+       {
+         fprintf (ctx->log_fp, "%s[%p] -> ",
+                  assuan_get_assuan_log_prefix (), ctx);
+         if (ctx->confidential)
+           fputs ("[Confidential data not shown]", ctx->log_fp);
+         else
+           _assuan_log_print_buffer (ctx->log_fp,
+                                     ctx->outbound.data.line, linelen);
+         putc ('\n', ctx->log_fp);
+       }
       *line++ = '\n';
       linelen++;
-      if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
+      if (writen (ctx, ctx->outbound.data.line, linelen))
         {
           ctx->outbound.data.error = ASSUAN_Write_Error;
           return 0;
@@ -411,6 +430,22 @@ assuan_send_data (ASSUAN_CONTEXT ctx, const void *buffer, size_t length)
   return 0;
 }
 
+AssuanError
+assuan_sendfd (ASSUAN_CONTEXT ctx, int fd)
+{
+  if (! ctx->io->sendfd)
+    return set_error (ctx, Not_Implemented,
+                     "server does not support sending and receiving "
+                     "of file descriptors");
+  return ctx->io->sendfd (ctx, fd);
+}
 
-
-
+AssuanError
+assuan_receivefd (ASSUAN_CONTEXT ctx, int *fd)
+{
+  if (! ctx->io->receivefd)
+    return set_error (ctx, Not_Implemented,
+                     "server does not support sending and receiving "
+                     "of file descriptors");
+  return ctx->io->receivefd (ctx, fd);
+}