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