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