2003-08-18 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   /* fixme: we should do some kind of line buffering.  */
242   if (ctx->log_fp)
243     {
244       fprintf (ctx->log_fp, "%s[%p] -> ",
245                assuan_get_assuan_log_prefix (), ctx);
246       if (s)
247         fputs ("[supplied line contained a LF]", ctx->log_fp);
248       if (ctx->confidential)
249         fputs ("[Confidential data not shown]", ctx->log_fp);
250       else
251         _assuan_log_print_buffer (ctx->log_fp, line, len);
252       putc ('\n', ctx->log_fp);
253     }
254
255   rc = writen (ctx, line, len);
256   if (rc)
257     rc = ASSUAN_Write_Error;
258   if (!rc)
259     {
260       rc = writen (ctx, "\n", 1);
261       if (rc)
262         rc = ASSUAN_Write_Error;
263     }
264
265   return rc;
266 }
267
268
269 \f
270 /* Write out the data in buffer as datalines with line wrapping and
271    percent escaping.  This fucntion is used for GNU's custom streams */
272 int
273 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
274 {
275   ASSUAN_CONTEXT ctx = cookie;
276   char *line;
277   size_t linelen;
278
279   if (ctx->outbound.data.error)
280     return 0;
281
282   line = ctx->outbound.data.line;
283   linelen = ctx->outbound.data.linelen;
284   line += linelen;
285   while (size)
286     {
287       /* insert data line header */
288       if (!linelen)
289         {
290           *line++ = 'D';
291           *line++ = ' ';
292           linelen += 2;
293         }
294       
295       /* copy data, keep some space for the CRLF and to escape one character */
296       while (size && linelen < LINELENGTH-2-2)
297         {
298           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
299             {
300               sprintf (line, "%%%02X", *(unsigned char*)buffer);
301               line += 3;
302               linelen += 3;
303               buffer++;
304             }
305           else
306             {
307               *line++ = *buffer++;
308               linelen++;
309             }
310           size--;
311         }
312       
313       if (linelen >= LINELENGTH-2-2)
314         {
315           if (ctx->log_fp)
316             {
317               fprintf (ctx->log_fp, "%s[%p] -> ",
318                        assuan_get_assuan_log_prefix (), ctx);
319
320               if (ctx->confidential)
321                 fputs ("[Confidential data not shown]", ctx->log_fp);
322               else 
323                 _assuan_log_print_buffer (ctx->log_fp, 
324                                           ctx->outbound.data.line,
325                                           linelen);
326               putc ('\n', ctx->log_fp);
327             }
328           *line++ = '\n';
329           linelen++;
330           if (writen (ctx, ctx->outbound.data.line, linelen))
331             {
332               ctx->outbound.data.error = ASSUAN_Write_Error;
333               return 0;
334             }
335           line = ctx->outbound.data.line;
336           linelen = 0;
337         }
338     }
339
340   ctx->outbound.data.linelen = linelen;
341   return 0;
342 }
343
344
345 /* Write out any buffered data 
346    This fucntion is used for GNU's custom streams */
347 int
348 _assuan_cookie_write_flush (void *cookie)
349 {
350   ASSUAN_CONTEXT ctx = cookie;
351   char *line;
352   size_t linelen;
353
354   if (ctx->outbound.data.error)
355     return 0;
356
357   line = ctx->outbound.data.line;
358   linelen = ctx->outbound.data.linelen;
359   line += linelen;
360   if (linelen)
361     {
362       if (ctx->log_fp)
363         {
364           fprintf (ctx->log_fp, "%s[%p] -> ",
365                    assuan_get_assuan_log_prefix (), ctx);
366           if (ctx->confidential)
367             fputs ("[Confidential data not shown]", ctx->log_fp);
368           else
369             _assuan_log_print_buffer (ctx->log_fp,
370                                       ctx->outbound.data.line, linelen);
371           putc ('\n', ctx->log_fp);
372         }
373       *line++ = '\n';
374       linelen++;
375       if (writen (ctx, ctx->outbound.data.line, linelen))
376         {
377           ctx->outbound.data.error = ASSUAN_Write_Error;
378           return 0;
379         }
380       ctx->outbound.data.linelen = 0;
381     }
382   return 0;
383 }
384
385
386 /**
387  * assuan_send_data:
388  * @ctx: An assuan context
389  * @buffer: Data to send or NULL to flush
390  * @length: length of the data to send/
391  * 
392  * This function may be used by the server or the client to send data
393  * lines.  The data will be escaped as required by the Assuan protocol
394  * and may get buffered until a line is full.  To force sending the
395  * data out @buffer may be passed as NULL (in which case @length must
396  * also be 0); however when used by a client this flush operation does
397  * also send the terminating "END" command to terminate the reponse on
398  * a INQUIRE response.  However, when assuan_transact() is used, this
399  * function takes care of sending END itself.
400  * 
401  * Return value: 0 on success or an error code
402  **/
403 \f
404 AssuanError
405 assuan_send_data (ASSUAN_CONTEXT ctx, const void *buffer, size_t length)
406 {
407   if (!ctx)
408     return ASSUAN_Invalid_Value;
409   if (!buffer && length)
410     return ASSUAN_Invalid_Value;
411
412   if (!buffer)
413     { /* flush what we have */
414       _assuan_cookie_write_flush (ctx);
415       if (ctx->outbound.data.error)
416         return ctx->outbound.data.error;
417       if (!ctx->is_server)
418         return assuan_write_line (ctx, "END");
419     }
420   else
421     {
422       _assuan_cookie_write_data (ctx, buffer, length);
423       if (ctx->outbound.data.error)
424         return ctx->outbound.data.error;
425     }
426
427   return 0;
428 }
429
430 AssuanError
431 assuan_sendfd (ASSUAN_CONTEXT ctx, int fd)
432 {
433   if (! ctx->io->sendfd)
434     return set_error (ctx, Not_Implemented,
435                       "server does not support sending and receiving "
436                       "of file descriptors");
437   return ctx->io->sendfd (ctx, fd);
438 }
439
440 AssuanError
441 assuan_receivefd (ASSUAN_CONTEXT ctx, int *fd)
442 {
443   if (! ctx->io->receivefd)
444     return set_error (ctx, Not_Implemented,
445                       "server does not support sending and receiving "
446                       "of file descriptors");
447   return ctx->io->receivefd (ctx, fd);
448 }