50900c42566df3e41948ee917e9abe50269dd1e0
[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   if (!ctx)
176     return ASSUAN_Invalid_Value;
177   *line = ctx->inbound.line;
178   *linelen = ctx->inbound.linelen;
179   return _assuan_read_line (ctx);
180 }
181
182
183 /* Return true when a full line is pending for a read, without the need
184    for actual IO */
185 int
186 assuan_pending_line (ASSUAN_CONTEXT ctx)
187 {
188   return ctx && ctx->inbound.attic.pending;
189 }
190
191
192 AssuanError 
193 assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
194 {
195   int rc;
196   
197   if (!ctx)
198     return ASSUAN_Invalid_Value;
199
200   /* fixme: we should do some kind of line buffering */
201   rc = writen (ctx->outbound.fd, line, strlen(line));
202   if (rc)
203     rc = ASSUAN_Write_Error;
204   if (!rc)
205     {
206       rc = writen (ctx->outbound.fd, "\n", 1);
207       if (rc)
208         rc = ASSUAN_Write_Error;
209     }
210
211   return rc;
212 }
213
214
215 \f
216 /* Write out the data in buffer as datalines with line wrapping and
217    percent escaping.  This fucntion is used for GNU's custom streams */
218 int
219 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
220 {
221   ASSUAN_CONTEXT ctx = cookie;
222   char *line;
223   size_t linelen;
224
225   if (ctx->outbound.data.error)
226     return 0;
227
228   line = ctx->outbound.data.line;
229   linelen = ctx->outbound.data.linelen;
230   line += linelen;
231   while (size)
232     {
233       /* insert data line header */
234       if (!linelen)
235         {
236           *line++ = 'D';
237           *line++ = ' ';
238           linelen += 2;
239         }
240       
241       /* copy data, keep some space for the CRLF and to escape one character */
242       while (size && linelen < LINELENGTH-2-2)
243         {
244           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
245             {
246               sprintf (line, "%%%02X", *(unsigned char*)buffer);
247               line += 3;
248               linelen += 3;
249               buffer++;
250             }
251           else
252             {
253               *line++ = *buffer++;
254               linelen++;
255             }
256           size--;
257         }
258       
259       if (linelen >= LINELENGTH-2-2)
260         {
261           *line++ = '\n';
262           linelen++;
263           if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
264             {
265               ctx->outbound.data.error = ASSUAN_Write_Error;
266               return 0;
267             }
268           line = ctx->outbound.data.line;
269           linelen = 0;
270         }
271     }
272
273   ctx->outbound.data.linelen = linelen;
274   return 0;
275 }
276
277
278 /* Write out any buffered data 
279    This fucntion is used for GNU's custom streams */
280 int
281 _assuan_cookie_write_flush (void *cookie)
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   if (linelen)
294     {
295       *line++ = '\n';
296       linelen++;
297       if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
298         {
299           ctx->outbound.data.error = ASSUAN_Write_Error;
300           return 0;
301         }
302       ctx->outbound.data.linelen = 0;
303     }
304   return 0;
305 }
306
307
308 /**
309  * assuan_send_data:
310  * @ctx: An assuan context
311  * @buffer: Data to send or NULL to flush
312  * @length: length of the data to send/
313  * 
314  * This function may be used by the server or the client to send data
315  * lines.  The data will be escaped as required by the Assuan protocol
316  * and may get buffered until a line is full.  To force sending the
317  * data out @buffer may be passed as NULL (in which case @length must
318  * also be 0); however when used by a client this flush operation does
319  * also send the terminating "END" command to terminate the reponse on
320  * a INQUIRE response.  However, when assuan_transact() is used, this
321  * function takes care of sending END itself.
322  * 
323  * Return value: 0 on success or an error code
324  **/
325 \f
326 AssuanError
327 assuan_send_data (ASSUAN_CONTEXT ctx, const void *buffer, size_t length)
328 {
329   if (!ctx)
330     return ASSUAN_Invalid_Value;
331   if (!buffer && length)
332     return ASSUAN_Invalid_Value;
333
334   if (!buffer)
335     { /* flush what we have */
336       _assuan_cookie_write_flush (ctx);
337       if (ctx->outbound.data.error)
338         return ctx->outbound.data.error;
339       if (!ctx->is_server)
340         return assuan_write_line (ctx, "END");
341     }
342   else
343     {
344       _assuan_cookie_write_data (ctx, buffer, length);
345       if (ctx->outbound.data.error)
346         return ctx->outbound.data.error;
347     }
348
349   return 0;
350 }
351
352
353
354