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