~ubuntu-branches/ubuntu/jaunty/gnupg2/jaunty

« back to all changes in this revision

Viewing changes to assuan/assuan-handler.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Urlichs
  • Date: 2006-01-24 04:31:42 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060124043142-pbg192or6qxv3yk2
Tags: 1.9.20-1
* New Upstream version. Closes:#306890,#344530
  * Closes:#320490: gpg-protect-tool fails to decrypt PKCS-12 files 
* Depend on libopensc2-dev, not -1-. Closes:#348106

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* assuan-handler.c - dispatch commands 
 
2
 *      Copyright (C) 2001, 2002 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., 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 <string.h>
 
25
 
 
26
#include "assuan-defs.h"
 
27
 
 
28
#define spacep(p)  (*(p) == ' ' || *(p) == '\t')
 
29
#define digitp(a) ((a) >= '0' && (a) <= '9')
 
30
 
 
31
 
 
32
#if !HAVE_FOPENCOOKIE
 
33
/* Provide structure for our dummy replacement function.  Usually this
 
34
   is defined in ../common/util.h but assuan should be self
 
35
   contained. */
 
36
/* Fixme: Remove fopencoookie :-(( */
 
37
typedef struct
 
38
{
 
39
  ssize_t (*read)(void*,char*,size_t);
 
40
  ssize_t (*write)(void*,const char*,size_t);
 
41
  int (*seek)(void*,off_t*,int);
 
42
  int (*close)(void*);
 
43
} _IO_cookie_io_functions_t;
 
44
typedef _IO_cookie_io_functions_t cookie_io_functions_t;
 
45
FILE *fopencookie (void *cookie, const char *opentype,
 
46
                   cookie_io_functions_t funclist);
 
47
#endif /*!HAVE_FOPENCOOKIE*/
 
48
 
 
49
 
 
50
 
 
51
 
 
52
static int
 
53
dummy_handler (ASSUAN_CONTEXT ctx, char *line)
 
54
{
 
55
  return set_error (ctx, Server_Fault, "no handler registered");
 
56
}
 
57
 
 
58
 
 
59
static int
 
60
std_handler_nop (ASSUAN_CONTEXT ctx, char *line)
 
61
{
 
62
  return 0; /* okay */
 
63
}
 
64
  
 
65
static int
 
66
std_handler_cancel (ASSUAN_CONTEXT ctx, char *line)
 
67
{
 
68
  if (ctx->cancel_notify_fnc)
 
69
    ctx->cancel_notify_fnc (ctx);
 
70
  return set_error (ctx, Not_Implemented, NULL); 
 
71
}
 
72
 
 
73
static int
 
74
std_handler_option (ASSUAN_CONTEXT ctx, char *line)
 
75
{
 
76
  char *key, *value, *p;
 
77
 
 
78
  for (key=line; spacep (key); key++)
 
79
    ;
 
80
  if (!*key)
 
81
    return set_error (ctx, Syntax_Error, "argument required");
 
82
  if (*key == '=')
 
83
    return set_error (ctx, Syntax_Error, "no option name given");
 
84
  for (value=key; *value && !spacep (value) && *value != '='; value++)
 
85
    ;
 
86
  if (*value)
 
87
    {
 
88
      if (spacep (value))
 
89
        *value++ = 0; /* terminate key */
 
90
      for (; spacep (value); value++)
 
91
        ;
 
92
      if (*value == '=')
 
93
        {
 
94
          *value++ = 0; /* terminate key */
 
95
          for (; spacep (value); value++)
 
96
            ;
 
97
          if (!*value)
 
98
            return set_error (ctx, Syntax_Error, "option argument expected");
 
99
        }
 
100
      if (*value)
 
101
        {
 
102
          for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
 
103
            ;
 
104
          if (p > value)
 
105
            *++p = 0; /* strip trailing spaces */
 
106
        }
 
107
    }
 
108
 
 
109
  if (*key == '-' && key[1] == '-' && key[2])
 
110
    key += 2; /* the double dashes are optional */
 
111
  if (*key == '-')
 
112
    return set_error (ctx, Syntax_Error,
 
113
                      "option should not begin with one dash");
 
114
 
 
115
  if (ctx->option_handler_fnc)
 
116
    return ctx->option_handler_fnc (ctx, key, value);
 
117
  return 0;
 
118
}
 
119
  
 
120
static int
 
121
std_handler_bye (ASSUAN_CONTEXT ctx, char *line)
 
122
{
 
123
  if (ctx->bye_notify_fnc)
 
124
    ctx->bye_notify_fnc (ctx);
 
125
  assuan_close_input_fd (ctx);
 
126
  assuan_close_output_fd (ctx);
 
127
  return -1; /* pretty simple :-) */
 
128
}
 
129
  
 
130
static int
 
131
std_handler_auth (ASSUAN_CONTEXT ctx, char *line)
 
132
{
 
133
  return set_error (ctx, Not_Implemented, NULL); 
 
134
}
 
135
  
 
136
static int
 
137
std_handler_reset (ASSUAN_CONTEXT ctx, char *line)
 
138
{
 
139
  if (ctx->reset_notify_fnc)
 
140
    ctx->reset_notify_fnc (ctx);
 
141
  assuan_close_input_fd (ctx);
 
142
  assuan_close_output_fd (ctx);
 
143
  return 0;
 
144
}
 
145
  
 
146
static int
 
147
std_handler_end (ASSUAN_CONTEXT ctx, char *line)
 
148
{
 
149
  return set_error (ctx, Not_Implemented, NULL); 
 
150
}
 
151
 
 
152
static int
 
153
parse_cmd_input_output (ASSUAN_CONTEXT ctx, char *line, int *rfd)
 
154
{
 
155
  char *endp;
 
156
 
 
157
  if (strncmp (line, "FD=", 3))
 
158
    return set_error (ctx, Syntax_Error, "FD=<n> expected");
 
159
  line += 3;
 
160
  if (!digitp (*line))
 
161
    return set_error (ctx, Syntax_Error, "number required");
 
162
  *rfd = strtoul (line, &endp, 10);
 
163
  /* remove that argument so that a notify handler won't see it */
 
164
  memset (line, ' ', endp? (endp-line):strlen(line));
 
165
 
 
166
  if (*rfd == ctx->inbound.fd)
 
167
    return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
 
168
  if (*rfd == ctx->outbound.fd)
 
169
    return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
 
170
  return 0;
 
171
}
 
172
 
 
173
/* Format is INPUT FD=<n> */
 
174
static int
 
175
std_handler_input (ASSUAN_CONTEXT ctx, char *line)
 
176
{
 
177
  int rc, fd;
 
178
 
 
179
  rc = parse_cmd_input_output (ctx, line, &fd);
 
180
  if (rc)
 
181
    return rc;
 
182
  ctx->input_fd = fd;
 
183
  if (ctx->input_notify_fnc)
 
184
    ctx->input_notify_fnc (ctx, line);
 
185
  return 0;
 
186
}
 
187
 
 
188
/* Format is OUTPUT FD=<n> */
 
189
static int
 
190
std_handler_output (ASSUAN_CONTEXT ctx, char *line)
 
191
{
 
192
  int rc, fd;
 
193
 
 
194
  rc = parse_cmd_input_output (ctx, line, &fd);
 
195
  if (rc)
 
196
    return rc;
 
197
  ctx->output_fd = fd;
 
198
  if (ctx->output_notify_fnc)
 
199
    ctx->output_notify_fnc (ctx, line);
 
200
  return 0;
 
201
}
 
202
 
 
203
 
 
204
 
 
205
  
 
206
 
 
207
/* This is a table with the standard commands and handler for them.
 
208
   The table is used to initialize a new context and assuciate strings
 
209
   and handlers with cmd_ids */
 
210
static struct {
 
211
  const char *name;
 
212
  int cmd_id;
 
213
  int (*handler)(ASSUAN_CONTEXT, char *line);
 
214
  int always; /* always initialize this command */
 
215
} std_cmd_table[] = {
 
216
  { "NOP",    ASSUAN_CMD_NOP,    std_handler_nop, 1 },
 
217
  { "CANCEL", ASSUAN_CMD_CANCEL, std_handler_cancel, 1 },
 
218
  { "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
 
219
  { "BYE",    ASSUAN_CMD_BYE,    std_handler_bye, 1 },
 
220
  { "AUTH",   ASSUAN_CMD_AUTH,   std_handler_auth, 1 },
 
221
  { "RESET",  ASSUAN_CMD_RESET,  std_handler_reset, 1 },
 
222
  { "END",    ASSUAN_CMD_END,    std_handler_end, 1 },
 
223
 
 
224
  { "INPUT",  ASSUAN_CMD_INPUT,  std_handler_input },
 
225
  { "OUTPUT", ASSUAN_CMD_OUTPUT, std_handler_output },
 
226
  { "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
 
227
  { NULL }
 
228
};
 
229
 
 
230
 
 
231
/**
 
232
 * assuan_register_command:
 
233
 * @ctx: the server context
 
234
 * @cmd_id: An ID value for the command
 
235
 * @cmd_name: A string with the command name
 
236
 * @handler: The handler function to be called
 
237
 * 
 
238
 * Register a handler to be used for a given command.
 
239
 * 
 
240
 * The @cmd_name must be %NULL or an empty string for all @cmd_ids
 
241
 * below %ASSUAN_CMD_USER because predefined values are used.
 
242
 * 
 
243
 * Return value: 
 
244
 **/
 
245
int
 
246
assuan_register_command (ASSUAN_CONTEXT ctx,
 
247
                         int cmd_id, const char *cmd_name,
 
248
                         int (*handler)(ASSUAN_CONTEXT, char *))
 
249
{
 
250
  int i;
 
251
 
 
252
  if (cmd_name && !*cmd_name)
 
253
    cmd_name = NULL;
 
254
 
 
255
  if (cmd_id < ASSUAN_CMD_USER)
 
256
    { 
 
257
      if (cmd_name)
 
258
        return ASSUAN_Invalid_Value; /* must be NULL for these values*/
 
259
 
 
260
      for (i=0; std_cmd_table[i].name; i++)
 
261
        {
 
262
          if (std_cmd_table[i].cmd_id == cmd_id)
 
263
            {
 
264
              cmd_name = std_cmd_table[i].name;
 
265
              if (!handler)
 
266
                handler = std_cmd_table[i].handler;
 
267
              break;
 
268
            }
 
269
        }
 
270
      if (!std_cmd_table[i].name)
 
271
        return ASSUAN_Invalid_Value; /* not a pre-registered one */
 
272
    }
 
273
  
 
274
  if (!handler)
 
275
    handler = dummy_handler;
 
276
 
 
277
  if (!cmd_name)
 
278
    return ASSUAN_Invalid_Value;
 
279
 
 
280
/*    fprintf (stderr, "DBG-assuan: registering %d as `%s'\n", cmd_id, cmd_name); */
 
281
 
 
282
  if (!ctx->cmdtbl)
 
283
    {
 
284
      ctx->cmdtbl_size = 50;
 
285
      ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
 
286
      if (!ctx->cmdtbl)
 
287
        return ASSUAN_Out_Of_Core;
 
288
      ctx->cmdtbl_used = 0;
 
289
    }
 
290
  else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
 
291
    {
 
292
      struct cmdtbl_s *x;
 
293
 
 
294
      x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
 
295
      if (!x)
 
296
        return ASSUAN_Out_Of_Core;
 
297
      ctx->cmdtbl = x;
 
298
      ctx->cmdtbl_size += 50;
 
299
    }
 
300
 
 
301
  ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
 
302
  ctx->cmdtbl[ctx->cmdtbl_used].cmd_id = cmd_id;
 
303
  ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
 
304
  ctx->cmdtbl_used++;
 
305
  return 0;
 
306
}
 
307
 
 
308
int
 
309
assuan_register_bye_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
 
310
{
 
311
  if (!ctx)
 
312
    return ASSUAN_Invalid_Value;
 
313
  ctx->bye_notify_fnc = fnc;
 
314
  return 0;
 
315
}
 
316
 
 
317
int
 
318
assuan_register_reset_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
 
319
{
 
320
  if (!ctx)
 
321
    return ASSUAN_Invalid_Value;
 
322
  ctx->reset_notify_fnc = fnc;
 
323
  return 0;
 
324
}
 
325
 
 
326
int
 
327
assuan_register_cancel_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
 
328
{
 
329
  if (!ctx)
 
330
    return ASSUAN_Invalid_Value;
 
331
  ctx->cancel_notify_fnc = fnc;
 
332
  return 0;
 
333
}
 
334
 
 
335
int
 
336
assuan_register_option_handler (ASSUAN_CONTEXT ctx,
 
337
                               int (*fnc)(ASSUAN_CONTEXT,
 
338
                                          const char*, const char*))
 
339
{
 
340
  if (!ctx)
 
341
    return ASSUAN_Invalid_Value;
 
342
  ctx->option_handler_fnc = fnc;
 
343
  return 0;
 
344
}
 
345
 
 
346
int
 
347
assuan_register_input_notify (ASSUAN_CONTEXT ctx,
 
348
                              void (*fnc)(ASSUAN_CONTEXT, const char *))
 
349
{
 
350
  if (!ctx)
 
351
    return ASSUAN_Invalid_Value;
 
352
  ctx->input_notify_fnc = fnc;
 
353
  return 0;
 
354
}
 
355
 
 
356
int
 
357
assuan_register_output_notify (ASSUAN_CONTEXT ctx,
 
358
                              void (*fnc)(ASSUAN_CONTEXT, const char *))
 
359
{
 
360
  if (!ctx)
 
361
    return ASSUAN_Invalid_Value;
 
362
  ctx->output_notify_fnc = fnc;
 
363
  return 0;
 
364
}
 
365
 
 
366
 
 
367
/* Helper to register the standards commands */
 
368
int
 
369
_assuan_register_std_commands (ASSUAN_CONTEXT ctx)
 
370
{
 
371
  int i, rc;
 
372
 
 
373
  for (i=0; std_cmd_table[i].name; i++)
 
374
    {
 
375
      if (std_cmd_table[i].always)
 
376
        {
 
377
          rc = assuan_register_command (ctx, std_cmd_table[i].cmd_id,
 
378
                                        NULL, NULL);
 
379
          if (rc)
 
380
            return rc;
 
381
        }
 
382
    } 
 
383
  return 0;
 
384
}
 
385
 
 
386
 
 
387
 
 
388
/* Process the special data lines.  The "D " has already been removed
 
389
   from the line.  As all handlers this function may modify the line.  */
 
390
static int
 
391
handle_data_line (ASSUAN_CONTEXT ctx, char *line, int linelen)
 
392
{
 
393
  return set_error (ctx, Not_Implemented, NULL);
 
394
}
 
395
 
 
396
/* like ascii_strcasecmp but assume that B is already uppercase */
 
397
static int
 
398
my_strcasecmp (const char *a, const char *b)
 
399
{
 
400
    if (a == b)
 
401
        return 0;
 
402
 
 
403
    for (; *a && *b; a++, b++)
 
404
      {
 
405
        if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
 
406
            break;
 
407
      }
 
408
    return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
 
409
}
 
410
 
 
411
/* Parse the line, break out the command, find it in the command
 
412
   table, remove leading and white spaces from the arguments, all the
 
413
   handler with the argument line and return the error */
 
414
static int 
 
415
dispatch_command (ASSUAN_CONTEXT ctx, char *line, int linelen)
 
416
{
 
417
  char *p;
 
418
  const char *s;
 
419
  int shift, i;
 
420
 
 
421
  if (*line == 'D' && line[1] == ' ') /* divert to special handler */
 
422
    return handle_data_line (ctx, line+2, linelen-2);
 
423
 
 
424
  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
 
425
    ;
 
426
  if (p==line)
 
427
    return set_error (ctx, Invalid_Command, "leading white-space"); 
 
428
  if (*p) 
 
429
    { /* Skip over leading WS after the keyword */
 
430
      *p++ = 0;
 
431
      while ( *p == ' ' || *p == '\t')
 
432
        p++;
 
433
    }
 
434
  shift = p - line;
 
435
 
 
436
  for (i=0; (s=ctx->cmdtbl[i].name); i++)
 
437
    {
 
438
      if (!strcmp (line, s))
 
439
        break;
 
440
    }
 
441
  if (!s)
 
442
    { /* and try case insensitive */
 
443
      for (i=0; (s=ctx->cmdtbl[i].name); i++)
 
444
        {
 
445
          if (!my_strcasecmp (line, s))
 
446
            break;
 
447
        }
 
448
    }
 
449
  if (!s)
 
450
    return set_error (ctx, Unknown_Command, NULL);
 
451
  line += shift;
 
452
  linelen -= shift;
 
453
 
 
454
/*    fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
 
455
  return ctx->cmdtbl[i].handler (ctx, line);
 
456
}
 
457
 
 
458
 
 
459
 
 
460
 
 
461
static int
 
462
process_request (ASSUAN_CONTEXT ctx)
 
463
{
 
464
  int rc;
 
465
 
 
466
  if (ctx->in_inquire)
 
467
    return ASSUAN_Nested_Commands;
 
468
 
 
469
  rc = _assuan_read_line (ctx);
 
470
  if (rc)
 
471
    return rc;
 
472
  if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
 
473
    return 0; /* comment line - ignore */
 
474
 
 
475
  ctx->outbound.data.error = 0;
 
476
  ctx->outbound.data.linelen = 0;
 
477
  /* dispatch command and return reply */
 
478
  rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
 
479
  /* check from data write errors */
 
480
  if (ctx->outbound.data.fp)
 
481
    { /* Flush the data lines */
 
482
      fclose (ctx->outbound.data.fp);
 
483
      ctx->outbound.data.fp = NULL;
 
484
      if (!rc && ctx->outbound.data.error)
 
485
        rc = ctx->outbound.data.error;
 
486
    }
 
487
  else /* flush any data send w/o using the data fp */
 
488
    {
 
489
      assuan_send_data (ctx, NULL, 0);
 
490
      if (!rc && ctx->outbound.data.error)
 
491
        rc = ctx->outbound.data.error;
 
492
    }
 
493
  /* Error handling */
 
494
  if (!rc)
 
495
    {
 
496
      rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK");
 
497
    }
 
498
  else if (rc == -1)
 
499
    { /* No error checking because the peer may have already disconnect */ 
 
500
      assuan_write_line (ctx, "OK closing connection");
 
501
      ctx->finish_handler (ctx);
 
502
    }
 
503
  else 
 
504
    {
 
505
      char errline[256];
 
506
 
 
507
      if (rc < 100)
 
508
        sprintf (errline, "ERR %d server fault (%.50s)",
 
509
                 ASSUAN_Server_Fault, assuan_strerror (rc));
 
510
      else
 
511
        {
 
512
          const char *text = ctx->err_no == rc? ctx->err_str:NULL;
 
513
 
 
514
          sprintf (errline, "ERR %d %.50s%s%.100s",
 
515
                   rc, assuan_strerror (rc), text? " - ":"", text?text:"");
 
516
        }
 
517
      rc = assuan_write_line (ctx, errline);
 
518
    }
 
519
 
 
520
  ctx->confidential = 0;
 
521
  if (ctx->okay_line)
 
522
    {
 
523
      xfree (ctx->okay_line);
 
524
      ctx->okay_line = NULL;
 
525
    }
 
526
  return rc;
 
527
}
 
528
 
 
529
/**
 
530
 * assuan_process:
 
531
 * @ctx: assuan context
 
532
 * 
 
533
 * This fucntion is used to handle the assuan protocol after a
 
534
 * connection has been established using assuan_accept().  This is the
 
535
 * main protocol handler.
 
536
 * 
 
537
 * Return value: 0 on success or an error code if the assuan operation
 
538
 * failed.  Note, that no error is returned for operational errors.
 
539
 **/
 
540
int
 
541
assuan_process (ASSUAN_CONTEXT ctx)
 
542
{
 
543
  int rc;
 
544
 
 
545
  do {
 
546
    rc = process_request (ctx);
 
547
  } while (!rc);
 
548
 
 
549
  if (rc == -1)
 
550
    rc = 0;
 
551
 
 
552
  return rc;
 
553
}
 
554
 
 
555
 
 
556
/**
 
557
 * assuan_process_next:
 
558
 * @ctx: Assuan context
 
559
 * 
 
560
 * Same as assuan_process() but the user has to provide the outer
 
561
 * loop.  He should loop as long as the return code is zero and stop
 
562
 * otherwise; -1 is regular end.
 
563
 * 
 
564
 * See also: assuan_get_active_fds()
 
565
 * Return value: -1 for end of server, 0 on success or an error code
 
566
 **/
 
567
int 
 
568
assuan_process_next (ASSUAN_CONTEXT ctx)
 
569
{
 
570
  return process_request (ctx);
 
571
}
 
572
 
 
573
 
 
574
/**
 
575
 * assuan_get_active_fds:
 
576
 * @ctx: Assuan context
 
577
 * @what: 0 for read fds, 1 for write fds
 
578
 * @fdarray: Caller supplied array to store the FDs
 
579
 * @fdarraysize: size of that array
 
580
 * 
 
581
 * Return all active filedescriptors for the given context.  This
 
582
 * function can be used to select on the fds and call
 
583
 * assuan_process_next() if there is an active one.  The first fd in
 
584
 * the array is the one used for the command connection.
 
585
 *
 
586
 * Note, that write FDs are not yet supported.
 
587
 * 
 
588
 * Return value: number of FDs active and put into @fdarray or -1 on
 
589
 * error which is most likely a too small fdarray.
 
590
 **/
 
591
int 
 
592
assuan_get_active_fds (ASSUAN_CONTEXT ctx, int what,
 
593
                       int *fdarray, int fdarraysize)
 
594
{
 
595
  int n = 0;
 
596
 
 
597
  if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
 
598
    return -1;
 
599
 
 
600
  if (!what)
 
601
    {
 
602
      if (ctx->inbound.fd != -1)
 
603
        fdarray[n++] = ctx->inbound.fd;
 
604
    }
 
605
  else
 
606
    {
 
607
      if (ctx->outbound.fd != -1)
 
608
        fdarray[n++] = ctx->outbound.fd;
 
609
      if (ctx->outbound.data.fp)
 
610
        fdarray[n++] = fileno (ctx->outbound.data.fp);
 
611
    }
 
612
 
 
613
  return n;
 
614
}
 
615
 
 
616
/* Return a FP to be used for data output.  The FILE pointer is valid
 
617
   until the end of a handler.  So a close is not needed.  Assuan does
 
618
   all the buffering needed to insert the status line as well as the
 
619
   required line wappping and quoting for data lines.
 
620
 
 
621
   We use GNU's custom streams here.  There should be an alternative
 
622
   implementaion for systems w/o a glibc, a simple implementation
 
623
   could use a child process */
 
624
FILE *
 
625
assuan_get_data_fp (ASSUAN_CONTEXT ctx)
 
626
{
 
627
  cookie_io_functions_t cookie_fnc;
 
628
 
 
629
  if (ctx->outbound.data.fp)
 
630
    return ctx->outbound.data.fp;
 
631
  
 
632
  cookie_fnc.read = NULL; 
 
633
  cookie_fnc.write = _assuan_cookie_write_data;
 
634
  cookie_fnc.seek = NULL;
 
635
  cookie_fnc.close = _assuan_cookie_write_flush;
 
636
 
 
637
  ctx->outbound.data.fp = fopencookie (ctx, "wb", cookie_fnc);
 
638
  ctx->outbound.data.error = 0;
 
639
  return ctx->outbound.data.fp;
 
640
}
 
641
 
 
642
 
 
643
/* Set the text used for the next OK reponse.  This string is
 
644
   automatically reset to NULL after the next command. */
 
645
AssuanError
 
646
assuan_set_okay_line (ASSUAN_CONTEXT ctx, const char *line)
 
647
{
 
648
  if (!ctx)
 
649
    return ASSUAN_Invalid_Value;
 
650
  if (!line)
 
651
    {
 
652
      xfree (ctx->okay_line);
 
653
      ctx->okay_line = NULL;
 
654
    }
 
655
  else
 
656
    {
 
657
      /* FIXME: we need to use gcry_is_secure() to test whether
 
658
         we should allocate the entire line in secure memory */
 
659
      char *buf = xtrymalloc (3+strlen(line)+1);
 
660
      if (!buf)
 
661
        return ASSUAN_Out_Of_Core;
 
662
      strcpy (buf, "OK ");
 
663
      strcpy (buf+3, line);
 
664
      xfree (ctx->okay_line);
 
665
      ctx->okay_line = buf;
 
666
    }
 
667
  return 0;
 
668
}
 
669
 
 
670
 
 
671
 
 
672
void
 
673
assuan_write_status (ASSUAN_CONTEXT ctx, const char *keyword, const char *text)
 
674
{
 
675
  char buffer[256];
 
676
  char *helpbuf;
 
677
  size_t n;
 
678
 
 
679
  if ( !ctx || !keyword)
 
680
    return;
 
681
  if (!text)
 
682
    text = "";
 
683
 
 
684
  n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
 
685
  if (n < sizeof (buffer))
 
686
    {
 
687
      strcpy (buffer, "S ");
 
688
      strcat (buffer, keyword);
 
689
      if (*text)
 
690
        {
 
691
          strcat (buffer, " ");
 
692
          strcat (buffer, text);
 
693
        }
 
694
      assuan_write_line (ctx, buffer);
 
695
    }
 
696
  else if ( (helpbuf = xtrymalloc (n)) )
 
697
    {
 
698
      strcpy (helpbuf, "S ");
 
699
      strcat (helpbuf, keyword);
 
700
      if (*text)
 
701
        {
 
702
          strcat (helpbuf, " ");
 
703
          strcat (helpbuf, text);
 
704
        }
 
705
      assuan_write_line (ctx, helpbuf);
 
706
      xfree (helpbuf);
 
707
    }
 
708
}