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