* assuan-socket-connect.c: Include sys/types.h
[gpgme.git] / assuan / assuan-buffer.c
index ee085d0..59518f2 100644 (file)
@@ -1,39 +1,38 @@
 /* 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 <stdlib.h>
+#include <string.h>
 #include <stdio.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->write (ctx, buffer, length);
       
       if (nwritten < 0)
         {
@@ -47,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;
@@ -58,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->read (ctx, buf, nleft);
+
       if (n < 0)
         {
           if (errno == EINTR)
@@ -74,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 */
     }
 
@@ -89,84 +89,114 @@ int
 _assuan_read_line (ASSUAN_CONTEXT ctx)
 {
   char *line = ctx->inbound.line;
-  int n, nread;
+  int nread, atticlen;
   int rc;
-  
+  char *endp = 0;
+
   if (ctx->inbound.eof)
     return -1;
 
-  if (ctx->inbound.attic.linelen)
+  atticlen = ctx->inbound.attic.linelen;
+  if (atticlen)
     {
-      memcpy (line, ctx->inbound.attic.line, ctx->inbound.attic.linelen);
-      nread = ctx->inbound.attic.linelen;
+      memcpy (line, ctx->inbound.attic.line, atticlen);
       ctx->inbound.attic.linelen = 0;
-      for (n=0; n < nread && line[n] != '\n'; n++)
-        ;
-      if (n < nread)
-        rc = 0; /* found another line in the attic */
+
+      endp = memchr (line, '\n', atticlen);
+      if (endp)
+       /* Found another line in the attic.  */
+       {
+         rc = 0;
+         nread = atticlen;
+         atticlen = 0;
+       }
       else
-        { /* read the rest */
-          n = nread;
-          assert (n < LINELENGTH);
-          rc = readline (ctx->inbound.fd, line + n, LINELENGTH - n,
-                         &nread, &ctx->inbound.eof);
+       /* There is pending data but not a full line.  */
+        {
+          assert (atticlen < LINELENGTH);
+          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)
-    return ASSUAN_Read_Error;
+    {
+      if (ctx->log_fp)
+       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);
-      return -1; 
+      if (ctx->log_fp)
+       fprintf (ctx->log_fp, "%s[%p] <- [EOF]\n",
+                assuan_get_assuan_log_prefix (), ctx);
+      return -1;
     }
 
   ctx->inbound.attic.pending = 0;
-  for (n=0; n < nread; n++)
+  nread += atticlen;
+
+  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;
-          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 (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;
     }
-
-  *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
@@ -184,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)
 {
@@ -194,20 +224,40 @@ 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 */
-  rc = writen (ctx->outbound.fd, line, strlen(line));
+  /* 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);
+
+  /* fixme: we should do some kind of line buffering.  */
+  if (ctx->log_fp)
+    {
+      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);
+      else
+       _assuan_log_print_buffer (ctx->log_fp, line, len);
+      putc ('\n', ctx->log_fp);
+    }
+
+  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;
     }
@@ -262,9 +312,22 @@ _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
       
       if (linelen >= LINELENGTH-2-2)
         {
+          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->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;
@@ -296,9 +359,20 @@ _assuan_cookie_write_flush (void *cookie)
   line += linelen;
   if (linelen)
     {
+      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->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;
@@ -353,6 +427,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);
+}