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