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