~ubuntu-branches/ubuntu/hardy/gnupg/hardy-updates

« back to all changes in this revision

Viewing changes to util/assuan-buffer.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2005-12-16 16:57:39 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20051216165739-v0m2d1you6hd8jho
Tags: upstream-1.4.2
ImportĀ upstreamĀ versionĀ 1.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* assuan-buffer.c - read and send data
 
2
 *      Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
3
 *
 
4
 * This file is part of Assuan.
 
5
 *
 
6
 * Assuan is free software; you can redistribute it and/or modify it
 
7
 * under the terms of the GNU Lesser General Public License as
 
8
 * published by the Free Software Foundation; either version 2.1 of
 
9
 * the License, or (at your option) any later version.
 
10
 *
 
11
 * Assuan is distributed in the hope that it will be useful, but
 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 
19
 * USA. 
 
20
 */
 
21
 
 
22
/* Please note that this is a stripped down and modified version of
 
23
   the orginal Assuan code from libassuan. */
 
24
 
 
25
 
 
26
#include <config.h>
 
27
#include <stdlib.h>
 
28
#include <string.h>
 
29
#include <stdio.h>
 
30
#include <errno.h>
 
31
#include <unistd.h>
 
32
#include <assert.h>
 
33
#ifdef HAVE_W32_SYSTEM
 
34
#include <process.h>
 
35
#endif
 
36
#include "assuan-defs.h"
 
37
 
 
38
#ifndef HAVE_MEMRCHR
 
39
void *memrchr(const void *s, int c, size_t n);
 
40
#endif
 
41
 
 
42
static int
 
43
writen (assuan_context_t ctx, const char *buffer, size_t length)
 
44
{
 
45
  while (length)
 
46
    {
 
47
      ssize_t nwritten = ctx->io->writefnc (ctx, buffer, length);
 
48
      
 
49
      if (nwritten < 0)
 
50
        {
 
51
          if (errno == EINTR)
 
52
            continue;
 
53
          return -1; /* write error */
 
54
        }
 
55
      length -= nwritten;
 
56
      buffer += nwritten;
 
57
    }
 
58
  return 0;  /* okay */
 
59
}
 
60
 
 
61
/* Read an entire line.  */
 
62
static int
 
63
readaline (assuan_context_t ctx, char *buf, size_t buflen,
 
64
          int *r_nread, int *r_eof)
 
65
{
 
66
  size_t nleft = buflen;
 
67
  char *p;
 
68
 
 
69
  *r_eof = 0;
 
70
  *r_nread = 0;
 
71
  while (nleft > 0)
 
72
    {
 
73
      ssize_t n = ctx->io->readfnc (ctx, buf, nleft);
 
74
 
 
75
      if (n < 0)
 
76
        {
 
77
          if (errno == EINTR)
 
78
            continue;
 
79
          return -1; /* read error */
 
80
        }
 
81
      else if (!n)
 
82
        {
 
83
          *r_eof = 1;
 
84
          break; /* allow incomplete lines */
 
85
        }
 
86
      p = buf;
 
87
      nleft -= n;
 
88
      buf += n;
 
89
      *r_nread += n;
 
90
 
 
91
      p = memrchr (p, '\n', n);
 
92
      if (p)
 
93
        break; /* at least one full line available - that's enough for now */
 
94
    }
 
95
 
 
96
  return 0;
 
97
}
 
98
 
 
99
 
 
100
int
 
101
_assuan_read_line (assuan_context_t ctx)
 
102
{
 
103
  char *line = ctx->inbound.line;
 
104
  int nread, atticlen;
 
105
  int rc;
 
106
  char *endp = 0;
 
107
 
 
108
  if (ctx->inbound.eof)
 
109
    return -1;
 
110
 
 
111
  atticlen = ctx->inbound.attic.linelen;
 
112
  if (atticlen)
 
113
    {
 
114
      memcpy (line, ctx->inbound.attic.line, atticlen);
 
115
      ctx->inbound.attic.linelen = 0;
 
116
 
 
117
      endp = memchr (line, '\n', atticlen);
 
118
      if (endp)
 
119
        /* Found another line in the attic.  */
 
120
        {
 
121
          rc = 0;
 
122
          nread = atticlen;
 
123
          atticlen = 0;
 
124
        }
 
125
      else
 
126
        /* There is pending data but not a full line.  */
 
127
        {
 
128
          assert (atticlen < LINELENGTH);
 
129
          rc = readaline (ctx, line + atticlen,
 
130
                         LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
 
131
        }
 
132
    }
 
133
  else
 
134
    /* No pending data.  */
 
135
    rc = readaline (ctx, line, LINELENGTH,
 
136
                   &nread, &ctx->inbound.eof);
 
137
  if (rc)
 
138
    {
 
139
      if (ctx->log_fp)
 
140
        fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Error: %s]\n",
 
141
                 assuan_get_assuan_log_prefix (),
 
142
                 (unsigned int)getpid (), ctx, strerror (errno));
 
143
      return ASSUAN_Read_Error;
 
144
    }
 
145
  if (!nread)
 
146
    {
 
147
      assert (ctx->inbound.eof);
 
148
      if (ctx->log_fp)
 
149
        fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [EOF]\n",
 
150
                 assuan_get_assuan_log_prefix (),
 
151
                 (unsigned int)getpid (), ctx);
 
152
      return -1;
 
153
    }
 
154
 
 
155
  ctx->inbound.attic.pending = 0;
 
156
  nread += atticlen;
 
157
 
 
158
  if (! endp)
 
159
    endp = memchr (line, '\n', nread);
 
160
 
 
161
  if (endp)
 
162
    {
 
163
      int n = endp - line + 1;
 
164
      if (n < nread)
 
165
        /* LINE contains more than one line.  We copy it to the attic
 
166
           now as handlers are allowed to modify the passed
 
167
           buffer.  */
 
168
        {
 
169
          int len = nread - n;
 
170
          memcpy (ctx->inbound.attic.line, endp + 1, len);
 
171
          ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
 
172
          ctx->inbound.attic.linelen = len;
 
173
        }
 
174
 
 
175
      if (endp != line && endp[-1] == '\r')
 
176
        endp --;
 
177
      *endp = 0;
 
178
 
 
179
      ctx->inbound.linelen = endp - line;
 
180
      if (ctx->log_fp)
 
181
        {
 
182
          fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- ",
 
183
                   assuan_get_assuan_log_prefix (),
 
184
                   (unsigned int)getpid (), ctx);
 
185
          if (ctx->confidential)
 
186
            fputs ("[Confidential data not shown]", ctx->log_fp);
 
187
          else
 
188
            _assuan_log_print_buffer (ctx->log_fp,
 
189
                                      ctx->inbound.line,
 
190
                                      ctx->inbound.linelen);
 
191
          putc ('\n', ctx->log_fp);
 
192
        }
 
193
      return 0;
 
194
    }
 
195
  else
 
196
    {
 
197
      if (ctx->log_fp)
 
198
        fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Invalid line]\n",
 
199
                 assuan_get_assuan_log_prefix (),
 
200
                 (unsigned int)getpid (), ctx);
 
201
      *line = 0;
 
202
      ctx->inbound.linelen = 0;
 
203
      return ctx->inbound.eof ? ASSUAN_Line_Not_Terminated
 
204
        : ASSUAN_Line_Too_Long;
 
205
    }
 
206
}
 
207
 
 
208
 
 
209
/* Read the next line from the client or server and return a pointer
 
210
   in *LINE to a buffer holding the line.  LINELEN is the length of
 
211
   *LINE.  The buffer is valid until the next read operation on it.
 
212
   The caller may modify the buffer.  The buffer is invalid (i.e. must
 
213
   not be used) if an error is returned.
 
214
 
 
215
   Returns 0 on success or an assuan error code.
 
216
   See also: assuan_pending_line().
 
217
*/
 
218
assuan_error_t
 
219
assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen)
 
220
{
 
221
  assuan_error_t err;
 
222
 
 
223
  if (!ctx)
 
224
    return ASSUAN_Invalid_Value;
 
225
 
 
226
  err = _assuan_read_line (ctx);
 
227
  *line = ctx->inbound.line;
 
228
  *linelen = ctx->inbound.linelen;
 
229
  return err;
 
230
}
 
231
 
 
232
 
 
233
/* Return true if a full line is buffered (i.e. an entire line may be
 
234
   read without any I/O).  */
 
235
int
 
236
assuan_pending_line (assuan_context_t ctx)
 
237
{
 
238
  return ctx && ctx->inbound.attic.pending;
 
239
}
 
240
 
 
241
 
 
242
assuan_error_t 
 
243
_assuan_write_line (assuan_context_t ctx, const char *prefix,
 
244
                    const char *line, size_t len)
 
245
{
 
246
  int rc = 0;
 
247
  size_t prefixlen = prefix? strlen (prefix):0;
 
248
 
 
249
  /* Make sure that the line is short enough. */
 
250
  if (len + prefixlen + 2 > ASSUAN_LINELENGTH)
 
251
    {
 
252
      if (ctx->log_fp)
 
253
        fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> "
 
254
                 "[supplied line too long -truncated]\n",
 
255
                 assuan_get_assuan_log_prefix (),
 
256
                 (unsigned int)getpid (), ctx);
 
257
      if (prefixlen > 5)
 
258
        prefixlen = 5;
 
259
      if (len > ASSUAN_LINELENGTH - prefixlen - 2)
 
260
        len = ASSUAN_LINELENGTH - prefixlen - 2 - 1;
 
261
    }
 
262
 
 
263
  /* Fixme: we should do some kind of line buffering.  */
 
264
  if (ctx->log_fp)
 
265
    {
 
266
      fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
 
267
               assuan_get_assuan_log_prefix (),
 
268
               (unsigned int)getpid (), ctx);
 
269
      if (ctx->confidential)
 
270
        fputs ("[Confidential data not shown]", ctx->log_fp);
 
271
      else
 
272
        _assuan_log_print_buffer (ctx->log_fp, line, len);
 
273
      putc ('\n', ctx->log_fp);
 
274
    }
 
275
 
 
276
  if (prefixlen)
 
277
    {
 
278
      rc = writen (ctx, prefix, prefixlen);
 
279
      if (rc)
 
280
        rc = ASSUAN_Write_Error;
 
281
    }
 
282
  if (!rc)
 
283
    {
 
284
      rc = writen (ctx, line, len);
 
285
      if (rc)
 
286
        rc = ASSUAN_Write_Error;
 
287
      if (!rc)
 
288
        {
 
289
          rc = writen (ctx, "\n", 1);
 
290
          if (rc)
 
291
            rc = ASSUAN_Write_Error;
 
292
        }
 
293
    }
 
294
  return rc;
 
295
}
 
296
 
 
297
 
 
298
assuan_error_t 
 
299
assuan_write_line (assuan_context_t ctx, const char *line)
 
300
{
 
301
  size_t len;
 
302
  const char *s;
 
303
 
 
304
  if (!ctx)
 
305
    return ASSUAN_Invalid_Value;
 
306
 
 
307
  /* Make sure that we never take a LF from the user - this might
 
308
     violate the protocol. */
 
309
  s = strchr (line, '\n');
 
310
  len = s? (s-line) : strlen (line);
 
311
 
 
312
  if (ctx->log_fp && s)
 
313
    fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> "
 
314
             "[supplied line contained a LF -truncated]\n",
 
315
             assuan_get_assuan_log_prefix (),
 
316
             (unsigned int)getpid (), ctx);
 
317
 
 
318
  return _assuan_write_line (ctx, NULL, line, len);
 
319
}
 
320
 
 
321
 
 
322
 
 
323
/* Write out the data in buffer as datalines with line wrapping and
 
324
   percent escaping.  This function is used for GNU's custom streams */
 
325
int
 
326
_assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size)
 
327
{
 
328
  assuan_context_t ctx = cookie;
 
329
  size_t size = orig_size;
 
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[%u.%p] DBG: -> ",
 
372
                       assuan_get_assuan_log_prefix (),
 
373
                       (unsigned int)getpid (), ctx);
 
374
 
 
375
              if (ctx->confidential)
 
376
                fputs ("[Confidential data not shown]", ctx->log_fp);
 
377
              else 
 
378
                _assuan_log_print_buffer (ctx->log_fp, 
 
379
                                          ctx->outbound.data.line,
 
380
                                          linelen);
 
381
              putc ('\n', ctx->log_fp);
 
382
            }
 
383
          *line++ = '\n';
 
384
          linelen++;
 
385
          if (writen (ctx, ctx->outbound.data.line, linelen))
 
386
            {
 
387
              ctx->outbound.data.error = ASSUAN_Write_Error;
 
388
              return 0;
 
389
            }
 
390
          line = ctx->outbound.data.line;
 
391
          linelen = 0;
 
392
        }
 
393
    }
 
394
 
 
395
  ctx->outbound.data.linelen = linelen;
 
396
  return (int)orig_size;
 
397
}
 
398
 
 
399
 
 
400
/* Write out any buffered data 
 
401
   This function is used for GNU's custom streams */
 
402
int
 
403
_assuan_cookie_write_flush (void *cookie)
 
404
{
 
405
  assuan_context_t ctx = cookie;
 
406
  char *line;
 
407
  size_t linelen;
 
408
 
 
409
  if (ctx->outbound.data.error)
 
410
    return 0;
 
411
 
 
412
  line = ctx->outbound.data.line;
 
413
  linelen = ctx->outbound.data.linelen;
 
414
  line += linelen;
 
415
  if (linelen)
 
416
    {
 
417
      if (ctx->log_fp)
 
418
        {
 
419
          fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
 
420
                   assuan_get_assuan_log_prefix (),
 
421
                   (unsigned int)getpid (), ctx);
 
422
          if (ctx->confidential)
 
423
            fputs ("[Confidential data not shown]", ctx->log_fp);
 
424
          else
 
425
            _assuan_log_print_buffer (ctx->log_fp,
 
426
                                      ctx->outbound.data.line, linelen);
 
427
          putc ('\n', ctx->log_fp);
 
428
        }
 
429
      *line++ = '\n';
 
430
      linelen++;
 
431
      if (writen (ctx, ctx->outbound.data.line, linelen))
 
432
        {
 
433
          ctx->outbound.data.error = ASSUAN_Write_Error;
 
434
          return 0;
 
435
        }
 
436
      ctx->outbound.data.linelen = 0;
 
437
    }
 
438
  return 0;
 
439
}
 
440
 
 
441
 
 
442
/**
 
443
 * assuan_send_data:
 
444
 * @ctx: An assuan context
 
445
 * @buffer: Data to send or NULL to flush
 
446
 * @length: length of the data to send/
 
447
 * 
 
448
 * This function may be used by the server or the client to send data
 
449
 * lines.  The data will be escaped as required by the Assuan protocol
 
450
 * and may get buffered until a line is full.  To force sending the
 
451
 * data out @buffer may be passed as NULL (in which case @length must
 
452
 * also be 0); however when used by a client this flush operation does
 
453
 * also send the terminating "END" command to terminate the reponse on
 
454
 * a INQUIRE response.  However, when assuan_transact() is used, this
 
455
 * function takes care of sending END itself.
 
456
 * 
 
457
 * Return value: 0 on success or an error code
 
458
 **/
 
459
 
 
460
assuan_error_t
 
461
assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
 
462
{
 
463
  if (!ctx)
 
464
    return ASSUAN_Invalid_Value;
 
465
  if (!buffer && length)
 
466
    return ASSUAN_Invalid_Value;
 
467
 
 
468
  if (!buffer)
 
469
    { /* flush what we have */
 
470
      _assuan_cookie_write_flush (ctx);
 
471
      if (ctx->outbound.data.error)
 
472
        return ctx->outbound.data.error;
 
473
      if (!ctx->is_server)
 
474
        return assuan_write_line (ctx, "END");
 
475
    }
 
476
  else
 
477
    {
 
478
      _assuan_cookie_write_data (ctx, buffer, length);
 
479
      if (ctx->outbound.data.error)
 
480
        return ctx->outbound.data.error;
 
481
    }
 
482
 
 
483
  return 0;
 
484
}
 
485