assuan/
[gpgme.git] / assuan / assuan-buffer.c
1 /* assuan-buffer.c - read and send data
2  * Copyright (C) 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
6  * Assuan is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Assuan is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <assert.h>
27 #ifdef HAVE_W32_SYSTEM
28 #include <process.h>
29 #endif
30 #include "assuan-defs.h"
31
32
33 /* Extended version of write(2) to guarantee that all bytes are
34    written.  Returns 0 on success or -1 and ERRNO on failure. */
35 static int
36 writen (assuan_context_t ctx, const char *buffer, size_t length)
37 {
38   while (length)
39     {
40       ssize_t nwritten = ctx->io->writefnc (ctx, buffer, length);
41       
42       if (nwritten < 0)
43         {
44           if (errno == EINTR)
45             continue;
46           return -1; /* write error */
47         }
48       length -= nwritten;
49       buffer += nwritten;
50     }
51   return 0;  /* okay */
52 }
53
54 /* Read an entire line. Returns 0 on success or -1 and ERRNO on
55    failure.  EOF is indictated by setting the integer at address
56    R_EOF.  Note: BUF, R_NREAD and R_EOF contain a valid result even if
57    an error is returned.  */
58 static int
59 readline (assuan_context_t ctx, char *buf, size_t buflen,
60           int *r_nread, int *r_eof)
61 {
62   size_t nleft = buflen;
63   char *p;
64
65   *r_eof = 0;
66   *r_nread = 0;
67   while (nleft > 0)
68     {
69       ssize_t n = ctx->io->readfnc (ctx, buf, nleft);
70
71       if (n < 0)
72         {
73           if (errno == EINTR)
74             continue;
75           return -1; /* read error */
76         }
77       else if (!n)
78         {
79           *r_eof = 1;
80           break; /* allow incomplete lines */
81         }
82       p = buf;
83       nleft -= n;
84       buf += n;
85       *r_nread += n;
86
87       p = memrchr (p, '\n', n);
88       if (p)
89         break; /* at least one full line available - that's enough for now */
90     }
91
92   return 0;
93 }
94
95
96 /* Function returns an Assuan error.  */
97 assuan_error_t
98 _assuan_read_line (assuan_context_t ctx)
99 {
100   char *line = ctx->inbound.line;
101   int nread, atticlen;
102   int rc;
103   char *endp = 0;
104
105   if (ctx->inbound.eof)
106     return _assuan_error (-1);
107
108   atticlen = ctx->inbound.attic.linelen;
109   if (atticlen)
110     {
111       memcpy (line, ctx->inbound.attic.line, atticlen);
112       ctx->inbound.attic.linelen = 0;
113
114       endp = memchr (line, '\n', atticlen);
115       if (endp)
116         /* Found another line in the attic.  */
117         {
118           rc = 0;
119           nread = atticlen;
120           atticlen = 0;
121         }
122       else
123         /* There is pending data but not a full line.  */
124         {
125           assert (atticlen < LINELENGTH);
126           rc = readline (ctx, line + atticlen,
127                          LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
128         }
129     }
130   else
131     /* No pending data.  */
132     rc = readline (ctx, line, LINELENGTH,
133                    &nread, &ctx->inbound.eof);
134   if (rc)
135     {
136       int saved_errno = errno;
137
138       if (ctx->log_fp)
139         fprintf (ctx->log_fp, "%s[%u.%d] DBG: <- [Error: %s]\n",
140                  assuan_get_assuan_log_prefix (),
141                  (unsigned int)getpid (), (int)ctx->inbound.fd,
142                  strerror (errno));
143
144       if (saved_errno == EAGAIN)
145         {
146           /* We have to save a partial line.  */
147           memcpy (ctx->inbound.attic.line, line, atticlen + nread);
148           ctx->inbound.attic.pending = 0;
149           ctx->inbound.attic.linelen = atticlen + nread;
150         }
151
152       errno = saved_errno;
153       return _assuan_error (ASSUAN_Read_Error);
154     }
155   if (!nread)
156     {
157       assert (ctx->inbound.eof);
158       if (ctx->log_fp)
159         fprintf (ctx->log_fp, "%s[%u.%d] DBG: <- [EOF]\n",
160                  assuan_get_assuan_log_prefix (),
161                  (unsigned int)getpid (), (int)ctx->inbound.fd);
162       return _assuan_error (-1);
163     }
164
165   ctx->inbound.attic.pending = 0;
166   nread += atticlen;
167
168   if (! endp)
169     endp = memchr (line, '\n', nread);
170
171   if (endp)
172     {
173       unsigned monitor_result;
174       int n = endp - line + 1;
175
176       if (n < nread)
177         /* LINE contains more than one line.  We copy it to the attic
178            now as handlers are allowed to modify the passed
179            buffer.  */
180         {
181           int len = nread - n;
182           memcpy (ctx->inbound.attic.line, endp + 1, len);
183           ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
184           ctx->inbound.attic.linelen = len;
185         }
186
187       if (endp != line && endp[-1] == '\r')
188         endp --;
189       *endp = 0;
190
191       ctx->inbound.linelen = endp - line;
192
193       monitor_result = (ctx->io_monitor
194                         ? ctx->io_monitor (ctx, 0,
195                                            ctx->inbound.line,
196                                            ctx->inbound.linelen)
197                         : 0);
198       if ( (monitor_result & 2) )
199         ctx->inbound.linelen = 0;
200       
201       if (ctx->log_fp && !(monitor_result & 1))
202         {
203           fprintf (ctx->log_fp, "%s[%u.%d] DBG: <- ",
204                    assuan_get_assuan_log_prefix (),
205                    (unsigned int)getpid (), (int)ctx->inbound.fd);
206           if (ctx->confidential)
207             fputs ("[Confidential data not shown]", ctx->log_fp);
208           else
209             _assuan_log_print_buffer (ctx->log_fp,
210                                       ctx->inbound.line,
211                                       ctx->inbound.linelen);
212           putc ('\n', ctx->log_fp);
213         }
214       return 0;
215     }
216   else
217     {
218       if (ctx->log_fp)
219         fprintf (ctx->log_fp, "%s[%u.%d] DBG: <- [Invalid line]\n",
220                  assuan_get_assuan_log_prefix (),
221                  (unsigned int)getpid (), (int)ctx->inbound.fd);
222       *line = 0;
223       ctx->inbound.linelen = 0;
224       return _assuan_error (ctx->inbound.eof 
225                             ? ASSUAN_Line_Not_Terminated
226                             : ASSUAN_Line_Too_Long);
227     }
228 }
229
230
231 /* Read the next line from the client or server and return a pointer
232    in *LINE to a buffer holding the line.  LINELEN is the length of
233    *LINE.  The buffer is valid until the next read operation on it.
234    The caller may modify the buffer.  The buffer is invalid (i.e. must
235    not be used) if an error is returned.
236
237    Returns 0 on success or an assuan error code.
238    See also: assuan_pending_line().
239 */
240 assuan_error_t
241 assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen)
242 {
243   assuan_error_t err;
244
245   if (!ctx)
246     return _assuan_error (ASSUAN_Invalid_Value);
247
248   do
249     {
250       err = _assuan_read_line (ctx);
251     }
252   while (_assuan_error_is_eagain (err));
253
254   *line = ctx->inbound.line;
255   *linelen = ctx->inbound.linelen;
256   return err;
257 }
258
259
260 /* Return true if a full line is buffered (i.e. an entire line may be
261    read without any I/O).  */
262 int
263 assuan_pending_line (assuan_context_t ctx)
264 {
265   return ctx && ctx->inbound.attic.pending;
266 }
267
268
269 assuan_error_t 
270 _assuan_write_line (assuan_context_t ctx, const char *prefix,
271                     const char *line, size_t len)
272 {
273   assuan_error_t rc = 0;
274   size_t prefixlen = prefix? strlen (prefix):0;
275   unsigned int monitor_result;
276
277   /* Make sure that the line is short enough. */
278   if (len + prefixlen + 2 > ASSUAN_LINELENGTH)
279     {
280       if (ctx->log_fp)
281         fprintf (ctx->log_fp, "%s[%u.%d] DBG: -> "
282                  "[supplied line too long -truncated]\n",
283                  assuan_get_assuan_log_prefix (),
284                  (unsigned int)getpid (), (int)ctx->inbound.fd);
285       if (prefixlen > 5)
286         prefixlen = 5;
287       if (len > ASSUAN_LINELENGTH - prefixlen - 2)
288         len = ASSUAN_LINELENGTH - prefixlen - 2 - 1;
289     }
290
291   monitor_result = (ctx->io_monitor
292                     ? ctx->io_monitor (ctx, 1, line, len)
293                     : 0);
294
295   /* Fixme: we should do some kind of line buffering.  */
296   if (ctx->log_fp && !(monitor_result & 1))
297     {
298       fprintf (ctx->log_fp, "%s[%u.%d] DBG: -> ",
299                assuan_get_assuan_log_prefix (),
300                (unsigned int)getpid (), (int)ctx->inbound.fd);
301       if (ctx->confidential)
302         fputs ("[Confidential data not shown]", ctx->log_fp);
303       else
304         {
305           if (prefixlen)
306             _assuan_log_print_buffer (ctx->log_fp, prefix, prefixlen);
307           _assuan_log_print_buffer (ctx->log_fp, line, len);
308         }
309       putc ('\n', ctx->log_fp);
310     }
311
312   if (prefixlen && !(monitor_result & 2))
313     {
314       rc = writen (ctx, prefix, prefixlen);
315       if (rc)
316         rc = _assuan_error (ASSUAN_Write_Error);
317     }
318   if (!rc && !(monitor_result & 2))
319     {
320       rc = writen (ctx, line, len);
321       if (rc)
322         rc = _assuan_error (ASSUAN_Write_Error);
323       if (!rc)
324         {
325           rc = writen (ctx, "\n", 1);
326           if (rc)
327             rc = _assuan_error (ASSUAN_Write_Error);
328         }
329     }
330   return rc;
331 }
332
333
334 assuan_error_t 
335 assuan_write_line (assuan_context_t ctx, const char *line)
336 {
337   size_t len;
338   const char *s;
339
340   if (!ctx)
341     return _assuan_error (ASSUAN_Invalid_Value);
342
343   /* Make sure that we never take a LF from the user - this might
344      violate the protocol. */
345   s = strchr (line, '\n');
346   len = s? (s-line) : strlen (line);
347
348   if (ctx->log_fp && s)
349     fprintf (ctx->log_fp, "%s[%u.%d] DBG: -> "
350              "[supplied line contained a LF - truncated]\n",
351              assuan_get_assuan_log_prefix (),
352              (unsigned int)getpid (), (int)ctx->inbound.fd);
353
354   return _assuan_write_line (ctx, NULL, line, len);
355 }
356
357
358 \f
359 /* Write out the data in buffer as datalines with line wrapping and
360    percent escaping.  This function is used for GNU's custom streams. */
361 int
362 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size)
363 {
364   assuan_context_t ctx = cookie;
365   size_t size = orig_size;
366   char *line;
367   size_t linelen;
368
369   if (ctx->outbound.data.error)
370     return 0;
371
372   line = ctx->outbound.data.line;
373   linelen = ctx->outbound.data.linelen;
374   line += linelen;
375   while (size)
376     {
377       unsigned int monitor_result;
378
379       /* Insert data line header. */
380       if (!linelen)
381         {
382           *line++ = 'D';
383           *line++ = ' ';
384           linelen += 2;
385         }
386       
387       /* Copy data, keep space for the CRLF and to escape one character. */
388       while (size && linelen < LINELENGTH-2-2)
389         {
390           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
391             {
392               sprintf (line, "%%%02X", *(unsigned char*)buffer);
393               line += 3;
394               linelen += 3;
395               buffer++;
396             }
397           else
398             {
399               *line++ = *buffer++;
400               linelen++;
401             }
402           size--;
403         }
404       
405       
406       monitor_result = (ctx->io_monitor
407                         ? ctx->io_monitor (ctx, 1,
408                                            ctx->outbound.data.line, linelen)
409                         : 0);
410
411       if (linelen >= LINELENGTH-2-2)
412         {
413           if (ctx->log_fp && !(monitor_result & 1))
414             {
415               fprintf (ctx->log_fp, "%s[%u.%d] DBG: -> ",
416                        assuan_get_assuan_log_prefix (),
417                        (unsigned int)getpid (), (int)ctx->inbound.fd);
418
419               if (ctx->confidential)
420                 fputs ("[Confidential data not shown]", ctx->log_fp);
421               else 
422                 _assuan_log_print_buffer (ctx->log_fp, 
423                                           ctx->outbound.data.line,
424                                           linelen);
425               putc ('\n', ctx->log_fp);
426             }
427           *line++ = '\n';
428           linelen++;
429           if ( !(monitor_result & 2)
430                && writen (ctx, ctx->outbound.data.line, linelen))
431             {
432               ctx->outbound.data.error = _assuan_error (ASSUAN_Write_Error);
433               return 0;
434             }
435           line = ctx->outbound.data.line;
436           linelen = 0;
437         }
438     }
439
440   ctx->outbound.data.linelen = linelen;
441   return (int)orig_size;
442 }
443
444
445 /* Write out any buffered data 
446    This function is used for GNU's custom streams */
447 int
448 _assuan_cookie_write_flush (void *cookie)
449 {
450   assuan_context_t ctx = cookie;
451   char *line;
452   size_t linelen;
453   unsigned int monitor_result;
454
455   if (ctx->outbound.data.error)
456     return 0;
457
458   line = ctx->outbound.data.line;
459   linelen = ctx->outbound.data.linelen;
460   line += linelen;
461
462   monitor_result = (ctx->io_monitor
463                     ? ctx->io_monitor (ctx, 1,
464                                        ctx->outbound.data.line, linelen)
465                     : 0);
466   
467   if (linelen)
468     {
469       if (ctx->log_fp && !(monitor_result & 1))
470         {
471           fprintf (ctx->log_fp, "%s[%u.%d] DBG: -> ",
472                    assuan_get_assuan_log_prefix (),
473                    (unsigned int)getpid (), (int)ctx->inbound.fd);
474           if (ctx->confidential)
475             fputs ("[Confidential data not shown]", ctx->log_fp);
476           else
477             _assuan_log_print_buffer (ctx->log_fp,
478                                       ctx->outbound.data.line, linelen);
479           putc ('\n', ctx->log_fp);
480         }
481       *line++ = '\n';
482       linelen++;
483       if ( !(monitor_result & 2)
484            && writen (ctx, ctx->outbound.data.line, linelen))
485         {
486           ctx->outbound.data.error = _assuan_error (ASSUAN_Write_Error);
487           return 0;
488         }
489       ctx->outbound.data.linelen = 0;
490     }
491   return 0;
492 }
493
494
495 /**
496  * assuan_send_data:
497  * @ctx: An assuan context
498  * @buffer: Data to send or NULL to flush
499  * @length: length of the data to send/
500  * 
501  * This function may be used by the server or the client to send data
502  * lines.  The data will be escaped as required by the Assuan protocol
503  * and may get buffered until a line is full.  To force sending the
504  * data out @buffer may be passed as NULL (in which case @length must
505  * also be 0); however when used by a client this flush operation does
506  * also send the terminating "END" command to terminate the reponse on
507  * a INQUIRE response.  However, when assuan_transact() is used, this
508  * function takes care of sending END itself.
509  * 
510  * If BUFFER is NULL and LENGTH is 1 and we are a client, a "CAN" is
511  * send instead of an "END".
512  * 
513  * Return value: 0 on success or an error code
514  **/
515 \f
516 assuan_error_t
517 assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
518 {
519   if (!ctx)
520     return _assuan_error (ASSUAN_Invalid_Value);
521   if (!buffer && length > 1)
522     return _assuan_error (ASSUAN_Invalid_Value);
523
524   if (!buffer)
525     { /* flush what we have */
526       _assuan_cookie_write_flush (ctx);
527       if (ctx->outbound.data.error)
528         return ctx->outbound.data.error;
529       if (!ctx->is_server)
530         return assuan_write_line (ctx, length == 1? "CAN":"END");
531     }
532   else
533     {
534       _assuan_cookie_write_data (ctx, buffer, length);
535       if (ctx->outbound.data.error)
536         return ctx->outbound.data.error;
537     }
538
539   return 0;
540 }
541
542 assuan_error_t
543 assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd)
544 {
545   /* It is explicitly allowed to use (NULL, -1) as a runtime test to
546      check whether descriptor passing is available. */
547   if (!ctx && fd == ASSUAN_INVALID_FD)
548 #ifdef USE_DESCRIPTOR_PASSING
549     return 0;
550 #else
551     return _assuan_error (ASSUAN_Not_Implemented);
552 #endif
553
554   if (! ctx->io->sendfd)
555     return set_error (ctx, Not_Implemented,
556                       "server does not support sending and receiving "
557                       "of file descriptors");
558   return ctx->io->sendfd (ctx, fd);
559 }
560
561 assuan_error_t
562 assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd)
563 {
564   if (! ctx->io->receivefd)
565     return set_error (ctx, Not_Implemented,
566                       "server does not support sending and receiving "
567                       "of file descriptors");
568   return ctx->io->receivefd (ctx, fd);
569 }