2001-12-14 Marcus Brinkmann <marcus@g10code.de>
[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 <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <assert.h>
27
28 #include "assuan-defs.h"
29
30
31 static int
32 writen ( int fd, const char *buffer, size_t length )
33 {
34   while (length)
35     {
36       int nwritten = write (fd, buffer, length);
37       
38       if (nwritten < 0)
39         {
40           if (errno == EINTR)
41             continue;
42           return -1; /* write error */
43         }
44       length -= nwritten;
45       buffer += nwritten;
46     }
47   return 0;  /* okay */
48 }
49
50 /* read an entire line */
51 static int
52 readline (int fd, char *buf, size_t buflen, 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       int n = read (fd, buf, nleft);
62       if (n < 0)
63         {
64           if (errno == EINTR)
65             continue;
66           return -1; /* read error */
67         }
68       else if (!n)
69         {
70           *eof = 1;
71           break; /* allow incomplete lines */
72         }
73       p = buf;
74       nleft -= n;
75       buf += n;
76       *r_nread += n;
77       
78       for (; n && *p != '\n'; n--, p++)
79         ;
80       if (n)
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 n, nread, atticlen;
93   int rc;
94
95   if (ctx->inbound.eof)
96     return -1;
97
98   atticlen = ctx->inbound.attic.linelen;
99   if (atticlen)
100     {
101       memcpy (line, ctx->inbound.attic.line, atticlen);
102       ctx->inbound.attic.linelen = 0;
103       for (n=0; n < atticlen && line[n] != '\n'; n++)
104         ;
105       if (n < atticlen)
106         {
107           rc = 0; /* found another line in the attic */
108           nread = atticlen;
109           atticlen = 0;
110         }
111       else
112         { /* read the rest */
113           assert (atticlen < LINELENGTH);
114           rc = readline (ctx->inbound.fd, line + atticlen,
115                          LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
116         }
117     }
118   else
119     rc = readline (ctx->inbound.fd, line, LINELENGTH,
120                    &nread, &ctx->inbound.eof);
121   if (rc)
122     return ASSUAN_Read_Error;
123   if (!nread)
124     {
125       assert (ctx->inbound.eof);
126       return -1; 
127     }
128
129   ctx->inbound.attic.pending = 0;
130   nread += atticlen;
131   for (n=0; n < nread; n++)
132     {
133       if (line[n] == '\n')
134         {
135           if (n+1 < nread)
136             {
137               char *s, *d;
138               int i;
139
140               n++;
141               /* we have to copy the rest because the handlers are
142                  allowed to modify the passed buffer */
143               for (d=ctx->inbound.attic.line, s=line+n, i=nread-n; i; i--)
144                 {
145                   if (*s=='\n')
146                     ctx->inbound.attic.pending = 1;
147                   *d++ = *s++;
148                 }
149               ctx->inbound.attic.linelen = nread-n;
150               n--;
151             }
152           if (n && line[n-1] == '\r')
153             n--;
154           line[n] = 0;
155           ctx->inbound.linelen = n;
156           return 0;
157         }
158     }
159
160   *line = 0;
161   ctx->inbound.linelen = 0;
162   return ctx->inbound.eof? ASSUAN_Line_Not_Terminated : ASSUAN_Line_Too_Long;
163 }
164
165
166 /* Read the next line from the client or server and return a pointer
167    to a buffer with holding that line.  linelen returns the length of
168    the line.  This buffer is valid until another read operation is
169    done on this buffer.  The caller is allowed to modify this buffer.
170    He should only use the buffer if the function returns without an
171    error.
172
173    Returns: 0 on success or an assuan error code
174    See also: assuan_pending_line().
175 */
176 AssuanError
177 assuan_read_line (ASSUAN_CONTEXT ctx, char **line, size_t *linelen)
178 {
179   AssuanError err;
180
181   if (!ctx)
182     return ASSUAN_Invalid_Value;
183
184   err = _assuan_read_line (ctx);
185   *line = ctx->inbound.line;
186   *linelen = ctx->inbound.linelen;
187   return err;
188 }
189
190
191 /* Return true when a full line is pending for a read, without the need
192    for actual IO */
193 int
194 assuan_pending_line (ASSUAN_CONTEXT ctx)
195 {
196   return ctx && ctx->inbound.attic.pending;
197 }
198
199
200 AssuanError 
201 assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
202 {
203   int rc;
204   
205   if (!ctx)
206     return ASSUAN_Invalid_Value;
207
208   /* fixme: we should do some kind of line buffering */
209   rc = writen (ctx->outbound.fd, line, strlen(line));
210   if (rc)
211     rc = ASSUAN_Write_Error;
212   if (!rc)
213     {
214       rc = writen (ctx->outbound.fd, "\n", 1);
215       if (rc)
216         rc = ASSUAN_Write_Error;
217     }
218
219   return rc;
220 }
221
222
223 \f
224 /* Write out the data in buffer as datalines with line wrapping and
225    percent escaping.  This fucntion is used for GNU's custom streams */
226 int
227 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
228 {
229   ASSUAN_CONTEXT ctx = cookie;
230   char *line;
231   size_t linelen;
232
233   if (ctx->outbound.data.error)
234     return 0;
235
236   line = ctx->outbound.data.line;
237   linelen = ctx->outbound.data.linelen;
238   line += linelen;
239   while (size)
240     {
241       /* insert data line header */
242       if (!linelen)
243         {
244           *line++ = 'D';
245           *line++ = ' ';
246           linelen += 2;
247         }
248       
249       /* copy data, keep some space for the CRLF and to escape one character */
250       while (size && linelen < LINELENGTH-2-2)
251         {
252           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
253             {
254               sprintf (line, "%%%02X", *(unsigned char*)buffer);
255               line += 3;
256               linelen += 3;
257               buffer++;
258             }
259           else
260             {
261               *line++ = *buffer++;
262               linelen++;
263             }
264           size--;
265         }
266       
267       if (linelen >= LINELENGTH-2-2)
268         {
269           *line++ = '\n';
270           linelen++;
271           if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
272             {
273               ctx->outbound.data.error = ASSUAN_Write_Error;
274               return 0;
275             }
276           line = ctx->outbound.data.line;
277           linelen = 0;
278         }
279     }
280
281   ctx->outbound.data.linelen = linelen;
282   return 0;
283 }
284
285
286 /* Write out any buffered data 
287    This fucntion is used for GNU's custom streams */
288 int
289 _assuan_cookie_write_flush (void *cookie)
290 {
291   ASSUAN_CONTEXT ctx = cookie;
292   char *line;
293   size_t linelen;
294
295   if (ctx->outbound.data.error)
296     return 0;
297
298   line = ctx->outbound.data.line;
299   linelen = ctx->outbound.data.linelen;
300   line += linelen;
301   if (linelen)
302     {
303       *line++ = '\n';
304       linelen++;
305       if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
306         {
307           ctx->outbound.data.error = ASSUAN_Write_Error;
308           return 0;
309         }
310       ctx->outbound.data.linelen = 0;
311     }
312   return 0;
313 }
314
315
316 /**
317  * assuan_send_data:
318  * @ctx: An assuan context
319  * @buffer: Data to send or NULL to flush
320  * @length: length of the data to send/
321  * 
322  * This function may be used by the server or the client to send data
323  * lines.  The data will be escaped as required by the Assuan protocol
324  * and may get buffered until a line is full.  To force sending the
325  * data out @buffer may be passed as NULL (in which case @length must
326  * also be 0); however when used by a client this flush operation does
327  * also send the terminating "END" command to terminate the reponse on
328  * a INQUIRE response.  However, when assuan_transact() is used, this
329  * function takes care of sending END itself.
330  * 
331  * Return value: 0 on success or an error code
332  **/
333 \f
334 AssuanError
335 assuan_send_data (ASSUAN_CONTEXT ctx, const void *buffer, size_t length)
336 {
337   if (!ctx)
338     return ASSUAN_Invalid_Value;
339   if (!buffer && length)
340     return ASSUAN_Invalid_Value;
341
342   if (!buffer)
343     { /* flush what we have */
344       _assuan_cookie_write_flush (ctx);
345       if (ctx->outbound.data.error)
346         return ctx->outbound.data.error;
347       if (!ctx->is_server)
348         return assuan_write_line (ctx, "END");
349     }
350   else
351     {
352       _assuan_cookie_write_data (ctx, buffer, length);
353       if (ctx->outbound.data.error)
354         return ctx->outbound.data.error;
355     }
356
357   return 0;
358 }
359
360
361
362