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