Updated FSF street address and preparations for a release candidate.
[gnupg.git] / util / 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 /* Please note that this is a stripped down and modified version of
23    the orginal Assuan code from libassuan. */
24
25
26 #include <config.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <assert.h>
33 #ifdef HAVE_W32_SYSTEM
34 #include <process.h>
35 #endif
36 #include "assuan-defs.h"
37
38 #ifndef HAVE_MEMRCHR
39 void *memrchr(const void *s, int c, size_t n);
40 #endif
41
42 static int
43 writen (assuan_context_t ctx, const char *buffer, size_t length)
44 {
45   while (length)
46     {
47       ssize_t nwritten = ctx->io->writefnc (ctx, buffer, length);
48       
49       if (nwritten < 0)
50         {
51           if (errno == EINTR)
52             continue;
53           return -1; /* write error */
54         }
55       length -= nwritten;
56       buffer += nwritten;
57     }
58   return 0;  /* okay */
59 }
60
61 /* Read an entire line.  */
62 static int
63 readaline (assuan_context_t ctx, char *buf, size_t buflen,
64           int *r_nread, int *r_eof)
65 {
66   size_t nleft = buflen;
67   char *p;
68
69   *r_eof = 0;
70   *r_nread = 0;
71   while (nleft > 0)
72     {
73       ssize_t n = ctx->io->readfnc (ctx, buf, nleft);
74
75       if (n < 0)
76         {
77           if (errno == EINTR)
78             continue;
79           return -1; /* read error */
80         }
81       else if (!n)
82         {
83           *r_eof = 1;
84           break; /* allow incomplete lines */
85         }
86       p = buf;
87       nleft -= n;
88       buf += n;
89       *r_nread += n;
90
91       p = memrchr (p, '\n', n);
92       if (p)
93         break; /* at least one full line available - that's enough for now */
94     }
95
96   return 0;
97 }
98
99
100 int
101 _assuan_read_line (assuan_context_t ctx)
102 {
103   char *line = ctx->inbound.line;
104   int nread, atticlen;
105   int rc;
106   char *endp = 0;
107
108   if (ctx->inbound.eof)
109     return -1;
110
111   atticlen = ctx->inbound.attic.linelen;
112   if (atticlen)
113     {
114       memcpy (line, ctx->inbound.attic.line, atticlen);
115       ctx->inbound.attic.linelen = 0;
116
117       endp = memchr (line, '\n', atticlen);
118       if (endp)
119         /* Found another line in the attic.  */
120         {
121           rc = 0;
122           nread = atticlen;
123           atticlen = 0;
124         }
125       else
126         /* There is pending data but not a full line.  */
127         {
128           assert (atticlen < LINELENGTH);
129           rc = readaline (ctx, line + atticlen,
130                          LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
131         }
132     }
133   else
134     /* No pending data.  */
135     rc = readaline (ctx, line, LINELENGTH,
136                    &nread, &ctx->inbound.eof);
137   if (rc)
138     {
139       if (ctx->log_fp)
140         fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Error: %s]\n",
141                  assuan_get_assuan_log_prefix (),
142                  (unsigned int)getpid (), ctx, strerror (errno));
143       return ASSUAN_Read_Error;
144     }
145   if (!nread)
146     {
147       assert (ctx->inbound.eof);
148       if (ctx->log_fp)
149         fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [EOF]\n",
150                  assuan_get_assuan_log_prefix (),
151                  (unsigned int)getpid (), ctx);
152       return -1;
153     }
154
155   ctx->inbound.attic.pending = 0;
156   nread += atticlen;
157
158   if (! endp)
159     endp = memchr (line, '\n', nread);
160
161   if (endp)
162     {
163       int n = endp - line + 1;
164       if (n < nread)
165         /* LINE contains more than one line.  We copy it to the attic
166            now as handlers are allowed to modify the passed
167            buffer.  */
168         {
169           int len = nread - n;
170           memcpy (ctx->inbound.attic.line, endp + 1, len);
171           ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
172           ctx->inbound.attic.linelen = len;
173         }
174
175       if (endp != line && endp[-1] == '\r')
176         endp --;
177       *endp = 0;
178
179       ctx->inbound.linelen = endp - line;
180       if (ctx->log_fp)
181         {
182           fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- ",
183                    assuan_get_assuan_log_prefix (),
184                    (unsigned int)getpid (), ctx);
185           if (ctx->confidential)
186             fputs ("[Confidential data not shown]", ctx->log_fp);
187           else
188             _assuan_log_print_buffer (ctx->log_fp,
189                                       ctx->inbound.line,
190                                       ctx->inbound.linelen);
191           putc ('\n', ctx->log_fp);
192         }
193       return 0;
194     }
195   else
196     {
197       if (ctx->log_fp)
198         fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Invalid line]\n",
199                  assuan_get_assuan_log_prefix (),
200                  (unsigned int)getpid (), ctx);
201       *line = 0;
202       ctx->inbound.linelen = 0;
203       return ctx->inbound.eof ? 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_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   int 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.%p] DBG: -> "
254                  "[supplied line too long -truncated]\n",
255                  assuan_get_assuan_log_prefix (),
256                  (unsigned int)getpid (), ctx);
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.%p] DBG: -> ",
267                assuan_get_assuan_log_prefix (),
268                (unsigned int)getpid (), ctx);
269       if (ctx->confidential)
270         fputs ("[Confidential data not shown]", ctx->log_fp);
271       else
272         _assuan_log_print_buffer (ctx->log_fp, line, len);
273       putc ('\n', ctx->log_fp);
274     }
275
276   if (prefixlen)
277     {
278       rc = writen (ctx, prefix, prefixlen);
279       if (rc)
280         rc = ASSUAN_Write_Error;
281     }
282   if (!rc)
283     {
284       rc = writen (ctx, line, len);
285       if (rc)
286         rc = ASSUAN_Write_Error;
287       if (!rc)
288         {
289           rc = writen (ctx, "\n", 1);
290           if (rc)
291             rc = ASSUAN_Write_Error;
292         }
293     }
294   return rc;
295 }
296
297
298 assuan_error_t 
299 assuan_write_line (assuan_context_t ctx, const char *line)
300 {
301   size_t len;
302   const char *s;
303
304   if (!ctx)
305     return ASSUAN_Invalid_Value;
306
307   /* Make sure that we never take a LF from the user - this might
308      violate the protocol. */
309   s = strchr (line, '\n');
310   len = s? (s-line) : strlen (line);
311
312   if (ctx->log_fp && s)
313     fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> "
314              "[supplied line contained a LF -truncated]\n",
315              assuan_get_assuan_log_prefix (),
316              (unsigned int)getpid (), ctx);
317
318   return _assuan_write_line (ctx, NULL, line, len);
319 }
320
321
322 \f
323 /* Write out the data in buffer as datalines with line wrapping and
324    percent escaping.  This function is used for GNU's custom streams */
325 int
326 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size)
327 {
328   assuan_context_t ctx = cookie;
329   size_t size = orig_size;
330   char *line;
331   size_t linelen;
332
333   if (ctx->outbound.data.error)
334     return 0;
335
336   line = ctx->outbound.data.line;
337   linelen = ctx->outbound.data.linelen;
338   line += linelen;
339   while (size)
340     {
341       /* insert data line header */
342       if (!linelen)
343         {
344           *line++ = 'D';
345           *line++ = ' ';
346           linelen += 2;
347         }
348       
349       /* copy data, keep some space for the CRLF and to escape one character */
350       while (size && linelen < LINELENGTH-2-2)
351         {
352           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
353             {
354               sprintf (line, "%%%02X", *(unsigned char*)buffer);
355               line += 3;
356               linelen += 3;
357               buffer++;
358             }
359           else
360             {
361               *line++ = *buffer++;
362               linelen++;
363             }
364           size--;
365         }
366       
367       if (linelen >= LINELENGTH-2-2)
368         {
369           if (ctx->log_fp)
370             {
371               fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
372                        assuan_get_assuan_log_prefix (),
373                        (unsigned int)getpid (), ctx);
374
375               if (ctx->confidential)
376                 fputs ("[Confidential data not shown]", ctx->log_fp);
377               else 
378                 _assuan_log_print_buffer (ctx->log_fp, 
379                                           ctx->outbound.data.line,
380                                           linelen);
381               putc ('\n', ctx->log_fp);
382             }
383           *line++ = '\n';
384           linelen++;
385           if (writen (ctx, ctx->outbound.data.line, linelen))
386             {
387               ctx->outbound.data.error = ASSUAN_Write_Error;
388               return 0;
389             }
390           line = ctx->outbound.data.line;
391           linelen = 0;
392         }
393     }
394
395   ctx->outbound.data.linelen = linelen;
396   return (int)orig_size;
397 }
398
399
400 /* Write out any buffered data 
401    This function is used for GNU's custom streams */
402 int
403 _assuan_cookie_write_flush (void *cookie)
404 {
405   assuan_context_t ctx = cookie;
406   char *line;
407   size_t linelen;
408
409   if (ctx->outbound.data.error)
410     return 0;
411
412   line = ctx->outbound.data.line;
413   linelen = ctx->outbound.data.linelen;
414   line += linelen;
415   if (linelen)
416     {
417       if (ctx->log_fp)
418         {
419           fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
420                    assuan_get_assuan_log_prefix (),
421                    (unsigned int)getpid (), ctx);
422           if (ctx->confidential)
423             fputs ("[Confidential data not shown]", ctx->log_fp);
424           else
425             _assuan_log_print_buffer (ctx->log_fp,
426                                       ctx->outbound.data.line, linelen);
427           putc ('\n', ctx->log_fp);
428         }
429       *line++ = '\n';
430       linelen++;
431       if (writen (ctx, ctx->outbound.data.line, linelen))
432         {
433           ctx->outbound.data.error = ASSUAN_Write_Error;
434           return 0;
435         }
436       ctx->outbound.data.linelen = 0;
437     }
438   return 0;
439 }
440
441
442 /**
443  * assuan_send_data:
444  * @ctx: An assuan context
445  * @buffer: Data to send or NULL to flush
446  * @length: length of the data to send/
447  * 
448  * This function may be used by the server or the client to send data
449  * lines.  The data will be escaped as required by the Assuan protocol
450  * and may get buffered until a line is full.  To force sending the
451  * data out @buffer may be passed as NULL (in which case @length must
452  * also be 0); however when used by a client this flush operation does
453  * also send the terminating "END" command to terminate the reponse on
454  * a INQUIRE response.  However, when assuan_transact() is used, this
455  * function takes care of sending END itself.
456  * 
457  * Return value: 0 on success or an error code
458  **/
459 \f
460 assuan_error_t
461 assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
462 {
463   if (!ctx)
464     return ASSUAN_Invalid_Value;
465   if (!buffer && length)
466     return ASSUAN_Invalid_Value;
467
468   if (!buffer)
469     { /* flush what we have */
470       _assuan_cookie_write_flush (ctx);
471       if (ctx->outbound.data.error)
472         return ctx->outbound.data.error;
473       if (!ctx->is_server)
474         return assuan_write_line (ctx, "END");
475     }
476   else
477     {
478       _assuan_cookie_write_data (ctx, buffer, length);
479       if (ctx->outbound.data.error)
480         return ctx->outbound.data.error;
481     }
482
483   return 0;
484 }
485