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