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