* assuan.h: Prototype assuan_pipe_connect and assuan_pipe_disconnect.
[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 <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;
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   rc = readline (ctx->inbound.fd, line, LINELENGTH, &nread, &ctx->inbound.eof);
99   if (rc)
100     return ASSUAN_Read_Error;
101   if (!nread)
102     {
103       assert (ctx->inbound.eof);
104       return -1; 
105     }
106
107   for (n=nread-1; n>=0 ; n--)
108     {
109       if (line[n] == '\n')
110         {
111           if (n != nread-1)
112             {
113               fprintf (stderr, "DBG-assuan: %d bytes left over after read\n",
114                        nread-1 - n);
115               /* fixme: store them for the next read */
116             }
117           if (n && line[n-1] == '\r')
118             n--;
119           line[n] = 0;
120           ctx->inbound.linelen = n;
121           return 0;
122         }
123     }
124
125   *line = 0;
126   ctx->inbound.linelen = 0;
127   return ctx->inbound.eof? ASSUAN_Line_Not_Terminated : ASSUAN_Line_Too_Long;
128 }
129
130
131
132
133 int 
134 _assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
135 {
136   int rc;
137
138   /* fixme: we should do some kind of line buffering */
139   rc = writen (ctx->outbound.fd, line, strlen(line));
140   if (rc)
141     rc = ASSUAN_Write_Error;
142   if (!rc)
143     {
144       rc = writen (ctx->outbound.fd, "\n", 1);
145       if (rc)
146         rc = ASSUAN_Write_Error;
147     }
148
149   return rc;
150 }
151
152
153 \f
154 /* Write out the data in buffer as datalines with line wrapping and
155    percent escaping.  This fucntion is used for GNU's custom streams */
156 int
157 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
158 {
159   ASSUAN_CONTEXT ctx = cookie;
160   char *line;
161   size_t linelen;
162
163   if (ctx->outbound.data.error)
164     return 0;
165
166   line = ctx->outbound.data.line;
167   linelen = ctx->outbound.data.linelen;
168   line += linelen;
169   while (size)
170     {
171       /* insert data line header */
172       if (!linelen)
173         {
174           *line++ = 'D';
175           *line++ = ' ';
176           linelen += 2;
177         }
178       
179       /* copy data, keep some space for the CRLF and to escape one character */
180       while (size && linelen < LINELENGTH-2-2)
181         {
182           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
183             {
184               sprintf (line, "%%%02X", *(unsigned char*)buffer);
185               line += 3;
186               linelen += 3;
187               buffer++;
188             }
189           else
190             {
191               *line++ = *buffer++;
192               linelen++;
193             }
194           size--;
195         }
196       
197       if (linelen >= LINELENGTH-2-2)
198         {
199           *line++ = '\n';
200           linelen++;
201           if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
202             {
203               ctx->outbound.data.error = ASSUAN_Write_Error;
204               return 0;
205             }
206           line = ctx->outbound.data.line;
207           linelen = 0;
208         }
209     }
210
211   ctx->outbound.data.linelen = linelen;
212   return 0;
213 }
214
215
216 /* Write out any buffered data 
217    This fucntion is used for GNU's custom streams */
218 int
219 _assuan_cookie_write_flush (void *cookie)
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   if (linelen)
232     {
233       *line++ = '\n';
234       linelen++;
235       if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
236         {
237           ctx->outbound.data.error = ASSUAN_Write_Error;
238           return 0;
239         }
240       ctx->outbound.data.linelen = 0;
241     }
242   return 0;
243 }
244
245
246
247