LCOV - code coverage report
Current view: top level - src - assuan-handler.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 406 26.8 %
Date: 2015-11-05 17:06:03 Functions: 10 36 27.8 %

          Line data    Source code
       1             : /* assuan-handler.c - dispatch commands
       2             :    Copyright (C) 2001, 2002, 2003, 2007, 2009,
       3             :                  2011 Free Software Foundation, Inc.
       4             : 
       5             :    This file is part of Assuan.
       6             : 
       7             :    Assuan is free software; you can redistribute it and/or modify it
       8             :    under the terms of the GNU Lesser General Public License as
       9             :    published by the Free Software Foundation; either version 2.1 of
      10             :    the License, or (at your option) any later version.
      11             : 
      12             :    Assuan is distributed in the hope that it will be useful, but
      13             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :    Lesser General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU Lesser General Public
      18             :    License along with this program; if not, see <http://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <stdlib.h>
      26             : #include <stdio.h>
      27             : #include <string.h>
      28             : #include <errno.h>
      29             : 
      30             : #include "assuan-defs.h"
      31             : #include "debug.h"
      32             : 
      33             : 
      34             : #define spacep(p)  (*(p) == ' ' || *(p) == '\t')
      35             : #define digitp(a) ((a) >= '0' && (a) <= '9')
      36             : 
      37             : static int my_strcasecmp (const char *a, const char *b);
      38             : 
      39             : 
      40             : #define PROCESS_DONE(ctx, rc) \
      41             :   ((ctx)->in_process_next ? assuan_process_done ((ctx), (rc)) : (rc))
      42             : 
      43             : static gpg_error_t
      44           0 : dummy_handler (assuan_context_t ctx, char *line)
      45             : {
      46           0 :   return
      47           0 :     PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT,
      48             :                                   "no handler registered"));
      49             : }
      50             : 
      51             : 
      52             : static const char std_help_nop[] =
      53             :   "NOP\n"
      54             :   "\n"
      55             :   "No operation.  Returns OK without any action.";
      56             : static gpg_error_t
      57           0 : std_handler_nop (assuan_context_t ctx, char *line)
      58             : {
      59           0 :   return PROCESS_DONE (ctx, 0); /* okay */
      60             : }
      61             : 
      62             : static const char std_help_cancel[] =
      63             :   "CANCEL\n"
      64             :   "\n"
      65             :   "Run the server's cancel handler if one has been registered.";
      66             : static gpg_error_t
      67           0 : std_handler_cancel (assuan_context_t ctx, char *line)
      68             : {
      69           0 :   if (ctx->cancel_notify_fnc)
      70             :     /* Return value ignored.  */
      71           0 :     ctx->cancel_notify_fnc (ctx, line);
      72           0 :   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
      73             : }
      74             : 
      75             : static const char std_help_option[] =
      76             :   "OPTION <NAME> [ [=] <VALUE> ]\n"
      77             :   "\n"
      78             :   "Set option <NAME> to configure server operation.  Leading and\n"
      79             :   "trailing spaces around <NAME> and <VALUE> are allowed but should be\n"
      80             :   "ignored.  For compatibility reasons, <NAME> may be prefixed with two\n"
      81             :   "dashes.  The use of the equal sign is optional but suggested if\n"
      82             :   "<VALUE> is given.";
      83             : static gpg_error_t
      84           0 : std_handler_option (assuan_context_t ctx, char *line)
      85             : {
      86             :   char *key, *value, *p;
      87             : 
      88           0 :   for (key=line; spacep (key); key++)
      89             :     ;
      90           0 :   if (!*key)
      91           0 :     return
      92           0 :       PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required"));
      93           0 :   if (*key == '=')
      94           0 :     return
      95           0 :       PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
      96             :                                     "no option name given"));
      97           0 :   for (value=key; *value && !spacep (value) && *value != '='; value++)
      98             :     ;
      99           0 :   if (*value)
     100             :     {
     101           0 :       if (spacep (value))
     102           0 :         *value++ = 0; /* terminate key */
     103           0 :       for (; spacep (value); value++)
     104             :         ;
     105           0 :       if (*value == '=')
     106             :         {
     107           0 :           *value++ = 0; /* terminate key */
     108           0 :           for (; spacep (value); value++)
     109             :             ;
     110           0 :           if (!*value)
     111           0 :             return
     112           0 :               PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
     113             :                                             "option argument expected"));
     114             :         }
     115           0 :       if (*value)
     116             :         {
     117           0 :           for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
     118             :             ;
     119           0 :           if (p > value)
     120           0 :             *++p = 0; /* strip trailing spaces */
     121             :         }
     122             :     }
     123             : 
     124           0 :   if (*key == '-' && key[1] == '-' && key[2])
     125           0 :     key += 2; /* the double dashes are optional */
     126           0 :   if (*key == '-')
     127           0 :     return PROCESS_DONE (ctx,
     128             :                          set_error (ctx, GPG_ERR_ASS_SYNTAX,
     129             :                                     "option should not begin with one dash"));
     130             : 
     131           0 :   if (ctx->option_handler_fnc)
     132           0 :     return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value));
     133           0 :   return PROCESS_DONE (ctx, 0);
     134             : }
     135             : 
     136             : static const char std_help_bye[] =
     137             :   "BYE\n"
     138             :   "\n"
     139             :   "Close the connection.  The server will reply with OK.";
     140             : static gpg_error_t
     141           2 : std_handler_bye (assuan_context_t ctx, char *line)
     142             : {
     143           2 :   if (ctx->bye_notify_fnc)
     144             :     /* Return value ignored.  */
     145           0 :     ctx->bye_notify_fnc (ctx, line);
     146           2 :   assuan_close_input_fd (ctx);
     147           2 :   assuan_close_output_fd (ctx);
     148             :   /* pretty simple :-) */
     149           2 :   ctx->process_complete = 1;
     150           2 :   return PROCESS_DONE (ctx, 0);
     151             : }
     152             : 
     153             : static const char std_help_auth[] =
     154             :   "AUTH\n"
     155             :   "\n"
     156             :   "Reserved for future extensions.";
     157             : static gpg_error_t
     158           0 : std_handler_auth (assuan_context_t ctx, char *line)
     159             : {
     160           0 :   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
     161             : }
     162             : 
     163             : static const char std_help_reset[] =
     164             :   "RESET\n"
     165             :   "\n"
     166             :   "Reset the connection but not any existing authentication.  The server\n"
     167             :   "should release all resources associated with the connection.";
     168             : static gpg_error_t
     169           0 : std_handler_reset (assuan_context_t ctx, char *line)
     170             : {
     171           0 :   gpg_error_t err = 0;
     172             : 
     173           0 :   if (ctx->reset_notify_fnc)
     174           0 :     err = ctx->reset_notify_fnc (ctx, line);
     175           0 :   if (! err)
     176             :     {
     177           0 :       assuan_close_input_fd (ctx);
     178           0 :       assuan_close_output_fd (ctx);
     179           0 :       _assuan_uds_close_fds (ctx);
     180             :     }
     181           0 :   return PROCESS_DONE (ctx, err);
     182             : }
     183             : 
     184             : static const char std_help_help[] =
     185             :   "HELP [<COMMAND>]\n"
     186             :   "\n"
     187             :   "Lists all commands that the server understands as comment lines on\n"
     188             :   "the status channel.  If <COMMAND> is given, list detailed help for\n"
     189             :   "that command.";
     190             : static gpg_error_t
     191           0 : std_handler_help (assuan_context_t ctx, char *line)
     192             : {
     193             :   unsigned int i;
     194             :   char buf[ASSUAN_LINELENGTH];
     195             :   const char *helpstr;
     196             :   size_t n;
     197             : 
     198           0 :   n = strcspn (line, " \t\n");
     199           0 :   if (!n)
     200             :     {
     201             :       /* Print all commands.  If a help string is available and that
     202             :          starts with the command name, print the first line of the
     203             :          help string.  */
     204           0 :       for (i = 0; i < ctx->cmdtbl_used; i++)
     205             :         {
     206           0 :           n = strlen (ctx->cmdtbl[i].name);
     207           0 :           helpstr = ctx->cmdtbl[i].helpstr;
     208           0 :           if (helpstr
     209           0 :               && !strncmp (ctx->cmdtbl[i].name, helpstr, n)
     210           0 :               && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ')
     211           0 :               && (n = strcspn (helpstr, "\n"))          )
     212           0 :             snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
     213             :           else
     214           0 :             snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name);
     215           0 :           buf[ASSUAN_LINELENGTH - 1] = '\0';
     216           0 :           assuan_write_line (ctx, buf);
     217             :         }
     218             :     }
     219             :   else
     220             :     {
     221             :       /* Print the help for the given command.  */
     222           0 :       int c = line[n];
     223           0 :       line[n] = 0;
     224           0 :       for (i=0; ctx->cmdtbl[i].name; i++)
     225           0 :         if (!my_strcasecmp (line, ctx->cmdtbl[i].name))
     226           0 :           break;
     227           0 :       line[n] = c;
     228           0 :       if (!ctx->cmdtbl[i].name)
     229           0 :         return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL));
     230           0 :       helpstr = ctx->cmdtbl[i].helpstr;
     231           0 :       if (!helpstr)
     232           0 :         return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL));
     233             :       do
     234             :         {
     235           0 :           n = strcspn (helpstr, "\n");
     236           0 :           snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
     237           0 :           helpstr += n;
     238           0 :           if (*helpstr == '\n')
     239           0 :             helpstr++;
     240           0 :           buf[ASSUAN_LINELENGTH - 1] = '\0';
     241           0 :           assuan_write_line (ctx, buf);
     242             :         }
     243           0 :       while (*helpstr);
     244             :     }
     245             : 
     246           0 :   return PROCESS_DONE (ctx, 0);
     247             : }
     248             : 
     249             : static const char std_help_end[] =
     250             :   "END\n"
     251             :   "\n"
     252             :   "Used by a client to mark the end of raw data.";
     253             : static gpg_error_t
     254           0 : std_handler_end (assuan_context_t ctx, char *line)
     255             : {
     256           0 :   return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
     257             : }
     258             : 
     259             : 
     260             : gpg_error_t
     261           6 : assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd)
     262             : {
     263             :   char *endp;
     264             : 
     265           6 :   if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2))
     266           6 :       || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2])))
     267           0 :     return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=<n>] expected");
     268           6 :   line += 2;
     269           6 :   if (*line == '=')
     270             :     {
     271           0 :       line ++;
     272           0 :       if (!digitp (*line))
     273           0 :         return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required");
     274             : #if HAVE_W64_SYSTEM
     275             :       *rfd = (void*)strtoull (line, &endp, 10);
     276             : #elif HAVE_W32_SYSTEM
     277             :       *rfd = (void*)strtoul (line, &endp, 10);
     278             : #else
     279           0 :       *rfd = strtoul (line, &endp, 10);
     280             : #endif
     281             :       /* Remove that argument so that a notify handler won't see it. */
     282           0 :       memset (line, ' ', endp? (endp-line):strlen(line));
     283             : 
     284           0 :       if (*rfd == ctx->inbound.fd)
     285           0 :         return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd");
     286           0 :       if (*rfd == ctx->outbound.fd)
     287           0 :         return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd");
     288           0 :       return 0;
     289             :     }
     290             :   else
     291             :     /* Our peer has sent the file descriptor.  */
     292           6 :     return assuan_receivefd (ctx, rfd);
     293             : }
     294             : 
     295             : 
     296             : static const char std_help_input[] =
     297             :   "INPUT FD[=<N>]\n"
     298             :   "\n"
     299             :   "Used by a client to pass an input file descriptor to the server.\n"
     300             :   "The server opens <N> as a local file descriptor.  Without <N>, the\n"
     301             :   "server opens the file descriptor just sent by the client using\n"
     302             :   "assuan_sendfd.";
     303             : static gpg_error_t
     304           6 : std_handler_input (assuan_context_t ctx, char *line)
     305             : {
     306             :   gpg_error_t rc;
     307             :   assuan_fd_t fd, oldfd;
     308             : 
     309           6 :   rc = assuan_command_parse_fd (ctx, line, &fd);
     310           6 :   if (rc)
     311           0 :     return PROCESS_DONE (ctx, rc);
     312             : 
     313             : #ifdef HAVE_W32CE_SYSTEM
     314             :   oldfd = fd;
     315             :   fd = _assuan_w32ce_finish_pipe ((int)fd, 0);
     316             :   if (fd == INVALID_HANDLE_VALUE)
     317             :     return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_PARAMETER,
     318             :                                          "rvid conversion failed"));
     319             :   TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_input", ctx,
     320             :           "turned RVID 0x%x into handle 0x%x", oldfd, fd);
     321             : #endif
     322             : 
     323           6 :   if (ctx->input_notify_fnc)
     324             :     {
     325           0 :       oldfd = ctx->input_fd;
     326           0 :       ctx->input_fd = fd;
     327           0 :       rc = ctx->input_notify_fnc (ctx, line);
     328           0 :       if (rc)
     329           0 :         ctx->input_fd = oldfd;
     330             :     }
     331           6 :   else if (!rc)
     332           6 :     ctx->input_fd = fd;
     333           6 :   return PROCESS_DONE (ctx, rc);
     334             : }
     335             : 
     336             : 
     337             : static const char std_help_output[] =
     338             :   "OUTPUT FD[=<N>]\n"
     339             :   "\n"
     340             :   "Used by a client to pass an output file descriptor to the server.\n"
     341             :   "The server opens <N> as a local file descriptor.  Without <N>, the\n"
     342             :   "server opens the file descriptor just sent by the client using\n"
     343             :   "assuan_sendfd.";
     344             : static gpg_error_t
     345           0 : std_handler_output (assuan_context_t ctx, char *line)
     346             : {
     347             :   gpg_error_t rc;
     348             :   assuan_fd_t fd, oldfd;
     349             : 
     350           0 :   rc = assuan_command_parse_fd (ctx, line, &fd);
     351           0 :   if (rc)
     352           0 :     return PROCESS_DONE (ctx, rc);
     353             : 
     354             : #ifdef HAVE_W32CE_SYSTEM
     355             :   oldfd = fd;
     356             :   fd = _assuan_w32ce_finish_pipe ((int)fd, 1);
     357             :   if (fd == INVALID_HANDLE_VALUE)
     358             :     return PROCESS_DONE (ctx, set_error (ctx, gpg_err_code_from_syserror (),
     359             :                                          "rvid conversion failed"));
     360             :   TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_output", ctx,
     361             :           "turned RVID 0x%x into handle 0x%x", oldfd, fd);
     362             : #endif
     363             : 
     364           0 :   if (ctx->output_notify_fnc)
     365             :     {
     366           0 :       oldfd = ctx->output_fd;
     367           0 :       ctx->output_fd = fd;
     368           0 :       rc = ctx->output_notify_fnc (ctx, line);
     369           0 :       if (rc)
     370           0 :         ctx->output_fd = oldfd;
     371             :     }
     372           0 :   else if (!rc)
     373           0 :     ctx->output_fd = fd;
     374           0 :   return PROCESS_DONE (ctx, rc);
     375             : }
     376             : 
     377             : 
     378             : /* This is a table with the standard commands and handler for them.
     379             :    The table is used to initialize a new context and associate strings
     380             :    with default handlers */
     381             : static struct {
     382             :   const char *name;
     383             :   gpg_error_t (*handler)(assuan_context_t, char *line);
     384             :   const char *help;
     385             :   int always; /* always initialize this command */
     386             : } std_cmd_table[] = {
     387             :   { "NOP",    std_handler_nop, std_help_nop, 1 },
     388             :   { "CANCEL", std_handler_cancel, std_help_cancel, 1 },
     389             :   { "OPTION", std_handler_option, std_help_option, 1 },
     390             :   { "BYE",    std_handler_bye, std_help_bye, 1 },
     391             :   { "AUTH",   std_handler_auth, std_help_auth, 1 },
     392             :   { "RESET",  std_handler_reset, std_help_reset, 1 },
     393             :   { "END",    std_handler_end, std_help_end, 1 },
     394             :   { "HELP",   std_handler_help, std_help_help, 1 },
     395             : 
     396             :   { "INPUT",  std_handler_input, std_help_input, 0 },
     397             :   { "OUTPUT", std_handler_output, std_help_output, 0 },
     398             :   { } };
     399             : 
     400             : 
     401             : /**
     402             :  * assuan_register_command:
     403             :  * @ctx: the server context
     404             :  * @cmd_name: A string with the command name
     405             :  * @handler: The handler function to be called or NULL to use a default
     406             :  *           handler.
     407             :  * HELPSTRING
     408             :  *
     409             :  * Register a handler to be used for a given command.  Note that
     410             :  * several default handlers are already regsitered with a new context.
     411             :  * This function however allows to override them.
     412             :  *
     413             :  * Return value: 0 on success or an error code
     414             :  **/
     415             : gpg_error_t
     416          23 : assuan_register_command (assuan_context_t ctx, const char *cmd_name,
     417             :                          assuan_handler_t handler, const char *help_string)
     418             : {
     419          23 :   int i, cmd_index = -1;
     420             :   const char *s;
     421             : 
     422          23 :   if (cmd_name && !*cmd_name)
     423           0 :     cmd_name = NULL;
     424             : 
     425          23 :   if (!cmd_name)
     426           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     427             : 
     428          23 :   if (!handler)
     429             :     { /* find a default handler. */
     430          20 :       for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++)
     431             :         ;
     432          20 :       if (!s)
     433             :         { /* Try again but case insensitive. */
     434           0 :           for (i=0; (s=std_cmd_table[i].name)
     435           0 :                     && my_strcasecmp (cmd_name, s); i++)
     436             :             ;
     437             :         }
     438          20 :       if (s)
     439          20 :         handler = std_cmd_table[i].handler;
     440          20 :       if (!handler)
     441           0 :         handler = dummy_handler; /* Last resort is the dummy handler. */
     442             :     }
     443             : 
     444          23 :   if (!ctx->cmdtbl)
     445             :     {
     446           2 :       ctx->cmdtbl_size = 50;
     447           2 :       ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
     448           2 :       if (!ctx->cmdtbl)
     449           0 :         return _assuan_error (ctx, gpg_err_code_from_syserror ());
     450           2 :       ctx->cmdtbl_used = 0;
     451             :     }
     452          21 :   else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
     453             :     {
     454             :       struct cmdtbl_s *x;
     455             : 
     456           0 :       x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
     457           0 :       if (!x)
     458           0 :         return _assuan_error (ctx, gpg_err_code_from_syserror ());
     459           0 :       ctx->cmdtbl = x;
     460           0 :       ctx->cmdtbl_size += 50;
     461             :     }
     462             : 
     463         144 :   for (i=0; i<ctx->cmdtbl_used; i++)
     464             :     {
     465         121 :       if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name))
     466             :         {
     467           0 :           cmd_index = i;
     468           0 :           break;
     469             :         }
     470             :     }
     471             : 
     472          23 :   if (cmd_index == -1)
     473          23 :     cmd_index = ctx->cmdtbl_used++;
     474             : 
     475          23 :   ctx->cmdtbl[cmd_index].name = cmd_name;
     476          23 :   ctx->cmdtbl[cmd_index].handler = handler;
     477          23 :   ctx->cmdtbl[cmd_index].helpstr = help_string;
     478          23 :   return 0;
     479             : }
     480             : 
     481             : /* Return the name of the command currently processed by a handler.
     482             :    The string returned is valid until the next call to an assuan
     483             :    function on the same context.  Returns NULL if no handler is
     484             :    executed or the command is not known.  */
     485             : const char *
     486           0 : assuan_get_command_name (assuan_context_t ctx)
     487             : {
     488           0 :   return ctx? ctx->current_cmd_name : NULL;
     489             : }
     490             : 
     491             : gpg_error_t
     492           0 : assuan_register_pre_cmd_notify (assuan_context_t ctx,
     493             :                                  gpg_error_t (*fnc)(assuan_context_t,
     494             :                                    const char *cmd))
     495             : {
     496           0 :   if (!ctx)
     497           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     498           0 :   ctx->pre_cmd_notify_fnc = fnc;
     499           0 :   return 0;
     500             : }
     501             : 
     502             : gpg_error_t
     503           0 : assuan_register_post_cmd_notify (assuan_context_t ctx,
     504             :                                  void (*fnc)(assuan_context_t, gpg_error_t))
     505             : {
     506           0 :   if (!ctx)
     507           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     508           0 :   ctx->post_cmd_notify_fnc = fnc;
     509           0 :   return 0;
     510             : }
     511             : 
     512             : gpg_error_t
     513           0 : assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc)
     514             : {
     515           0 :   if (!ctx)
     516           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     517           0 :   ctx->bye_notify_fnc = fnc;
     518           0 :   return 0;
     519             : }
     520             : 
     521             : gpg_error_t
     522           0 : assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc)
     523             : {
     524           0 :   if (!ctx)
     525           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     526           0 :   ctx->reset_notify_fnc = fnc;
     527           0 :   return 0;
     528             : }
     529             : 
     530             : gpg_error_t
     531           0 : assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc)
     532             : {
     533           0 :   if (!ctx)
     534           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     535           0 :   ctx->cancel_notify_fnc = fnc;
     536           0 :   return 0;
     537             : }
     538             : 
     539             : gpg_error_t
     540           0 : assuan_register_option_handler (assuan_context_t ctx,
     541             :                                 gpg_error_t (*fnc)(assuan_context_t,
     542             :                                                    const char*, const char*))
     543             : {
     544           0 :   if (!ctx)
     545           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     546           0 :   ctx->option_handler_fnc = fnc;
     547           0 :   return 0;
     548             : }
     549             : 
     550             : gpg_error_t
     551           0 : assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc)
     552             : {
     553           0 :   if (!ctx)
     554           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     555           0 :   ctx->input_notify_fnc = fnc;
     556           0 :   return 0;
     557             : }
     558             : 
     559             : gpg_error_t
     560           0 : assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc)
     561             : {
     562           0 :   if (!ctx)
     563           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     564           0 :   ctx->output_notify_fnc = fnc;
     565           0 :   return 0;
     566             : }
     567             : 
     568             : 
     569             : /* Helper to register the standards commands */
     570             : gpg_error_t
     571           2 : _assuan_register_std_commands (assuan_context_t ctx)
     572             : {
     573             :   gpg_error_t rc;
     574             :   int i;
     575             : 
     576          22 :   for (i = 0; std_cmd_table[i].name; i++)
     577             :     {
     578          20 :       if (std_cmd_table[i].always)
     579             :         {
     580          16 :           rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL);
     581          16 :           if (rc)
     582           0 :             return rc;
     583             :         }
     584             :     }
     585           2 :   return 0;
     586             : }
     587             : 
     588             : 
     589             : 
     590             : /* Process the special data lines.  The "D " has already been removed
     591             :    from the line.  As all handlers this function may modify the line.  */
     592             : static gpg_error_t
     593           0 : handle_data_line (assuan_context_t ctx, char *line, int linelen)
     594             : {
     595           0 :   return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL);
     596             : }
     597             : 
     598             : /* like ascii_strcasecmp but assume that B is already uppercase */
     599             : static int
     600         121 : my_strcasecmp (const char *a, const char *b)
     601             : {
     602         121 :     if (a == b)
     603           0 :         return 0;
     604             : 
     605         127 :     for (; *a && *b; a++, b++)
     606             :       {
     607         127 :         if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
     608         121 :             break;
     609             :       }
     610         121 :     return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
     611             : }
     612             : 
     613             : 
     614             : /* Parse the line, break out the command, find it in the command
     615             :    table, remove leading and white spaces from the arguments, call the
     616             :    handler with the argument line and return the error.  */
     617             : static gpg_error_t
     618          15 : dispatch_command (assuan_context_t ctx, char *line, int linelen)
     619             : {
     620             :   gpg_error_t err;
     621             :   char *p;
     622             :   const char *s;
     623             :   int shift, i;
     624             : 
     625             :   /* Note that as this function is invoked by assuan_process_next as
     626             :      well, we need to hide non-critical errors with PROCESS_DONE.  */
     627             : 
     628          15 :   if (*line == 'D' && line[1] == ' ') /* divert to special handler */
     629             :     /* FIXME: Depending on the final implementation of
     630             :        handle_data_line, this may be wrong here.  For example, if a
     631             :        user callback is invoked, and that callback is responsible for
     632             :        calling assuan_process_done, then this is wrong.  */
     633           0 :     return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2));
     634             : 
     635          15 :   for (p=line; *p && *p != ' ' && *p != '\t'; p++)
     636             :     ;
     637          15 :   if (p==line)
     638           0 :     return PROCESS_DONE
     639             :       (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space"));
     640          15 :   if (*p)
     641             :     { /* Skip over leading WS after the keyword */
     642           7 :       *p++ = 0;
     643          14 :       while ( *p == ' ' || *p == '\t')
     644           0 :         p++;
     645             :     }
     646          15 :   shift = p - line;
     647             : 
     648         131 :   for (i=0; (s=ctx->cmdtbl[i].name); i++)
     649             :     {
     650         131 :       if (!strcmp (line, s))
     651          15 :         break;
     652             :     }
     653          15 :   if (!s)
     654             :     { /* and try case insensitive */
     655           0 :       for (i=0; (s=ctx->cmdtbl[i].name); i++)
     656             :         {
     657           0 :           if (!my_strcasecmp (line, s))
     658           0 :             break;
     659             :         }
     660             :     }
     661          15 :   if (!s)
     662           0 :     return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL));
     663          15 :   line += shift;
     664          15 :   linelen -= shift;
     665             : 
     666          15 :   if (ctx->pre_cmd_notify_fnc) {
     667           0 :     err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name);
     668             : 
     669           0 :     if (err)
     670           0 :       return PROCESS_DONE(ctx, err);
     671             :   }
     672             : 
     673             : /*    fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
     674          15 :   ctx->current_cmd_name = ctx->cmdtbl[i].name;
     675          15 :   err = ctx->cmdtbl[i].handler (ctx, line);
     676          15 :   ctx->current_cmd_name = NULL;
     677          15 :   return err;
     678             : }
     679             : 
     680             : 
     681             : /* Call this to acknowledge the current command.  */
     682             : gpg_error_t
     683          15 : assuan_process_done (assuan_context_t ctx, gpg_error_t rc)
     684             : {
     685          15 :   if (!ctx->in_command)
     686           0 :     return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
     687             : 
     688          15 :   if (ctx->flags.force_close)
     689           0 :     ctx->process_complete = 1;
     690             : 
     691          15 :   ctx->in_command = 0;
     692             : 
     693             :   /* Check for data write errors.  */
     694          15 :   if (ctx->outbound.data.fp)
     695             :     {
     696             :       /* Flush the data lines.  */
     697           0 :       fclose (ctx->outbound.data.fp);
     698           0 :       ctx->outbound.data.fp = NULL;
     699           0 :       if (!rc && ctx->outbound.data.error)
     700           0 :         rc = ctx->outbound.data.error;
     701             :     }
     702             :   else
     703             :     {
     704             :       /* Flush any data send without using the data FP.  */
     705          15 :       assuan_send_data (ctx, NULL, 0);
     706          15 :       if (!rc && ctx->outbound.data.error)
     707           0 :         rc = ctx->outbound.data.error;
     708             :     }
     709             : 
     710             :   /* Error handling.  */
     711          15 :   if (!rc)
     712             :     {
     713          15 :       if (ctx->process_complete)
     714             :         {
     715             :           /* No error checking because the peer may have already
     716             :              disconnect. */
     717           2 :           assuan_write_line (ctx, "OK closing connection");
     718           2 :           ctx->finish_handler (ctx);
     719             :         }
     720             :       else
     721          13 :         rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK");
     722             :     }
     723             :   else
     724             :     {
     725             :       char errline[300];
     726           0 :       const char *text = ctx->err_no == rc ? ctx->err_str : NULL;
     727             :       char ebuf[50];
     728             : 
     729           0 :       if (ctx->flags.force_close)
     730           0 :         text = "[closing connection]";
     731             : 
     732           0 :       gpg_strerror_r (rc, ebuf, sizeof (ebuf));
     733           0 :       snprintf (errline, sizeof errline, "ERR %d %.50s <%.30s>%s%.100s",
     734             :                 rc, ebuf, gpg_strsource (rc),
     735             :                 text? " - ":"", text?text:"");
     736             : 
     737           0 :       rc = assuan_write_line (ctx, errline);
     738             : 
     739           0 :       if (ctx->flags.force_close)
     740           0 :         ctx->finish_handler (ctx);
     741             :     }
     742             : 
     743          15 :   if (ctx->post_cmd_notify_fnc)
     744           0 :     ctx->post_cmd_notify_fnc (ctx, rc);
     745             : 
     746          15 :   ctx->flags.confidential = 0;
     747          15 :   if (ctx->okay_line)
     748             :     {
     749           0 :       _assuan_free (ctx, ctx->okay_line);
     750           0 :       ctx->okay_line = NULL;
     751             :     }
     752             : 
     753          15 :   return rc;
     754             : }
     755             : 
     756             : 
     757             : static gpg_error_t
     758           0 : process_next (assuan_context_t ctx)
     759             : {
     760             :   gpg_error_t rc;
     761             : 
     762             :   /* What the next thing to do is depends on the current state.
     763             :      However, we will always first read the next line.  The client is
     764             :      required to write full lines without blocking long after starting
     765             :      a partial line.  */
     766           0 :   rc = _assuan_read_line (ctx);
     767           0 :   if (_assuan_error_is_eagain (ctx, rc))
     768           0 :     return 0;
     769           0 :   if (gpg_err_code (rc) == GPG_ERR_EOF)
     770             :     {
     771           0 :       ctx->process_complete = 1;
     772           0 :       return 0;
     773             :     }
     774           0 :   if (rc)
     775           0 :     return rc;
     776           0 :   if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
     777             :      /* Comment lines are ignored.  */
     778           0 :     return 0;
     779             : 
     780             :   /* Now we have a line that really means something.  It could be one
     781             :      of the following things: First, if we are not in a command
     782             :      already, it is the next command to dispatch.  Second, if we are
     783             :      in a command, it can only be the response to an INQUIRE
     784             :      reply.  */
     785             : 
     786           0 :   if (!ctx->in_command)
     787             :     {
     788           0 :       ctx->in_command = 1;
     789             : 
     790           0 :       ctx->outbound.data.error = 0;
     791           0 :       ctx->outbound.data.linelen = 0;
     792             :       /* Dispatch command and return reply.  */
     793           0 :       ctx->in_process_next = 1;
     794           0 :       rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
     795           0 :       ctx->in_process_next = 0;
     796             :     }
     797           0 :   else if (ctx->in_inquire)
     798             :     {
     799             :       /* FIXME: Pick up the continuation.  */
     800           0 :       rc = _assuan_inquire_ext_cb (ctx);
     801             :     }
     802             :   else
     803             :     {
     804             :       /* Should not happen.  The client is sending data while we are
     805             :          in a command and not waiting for an inquire.  We log an error
     806             :          and discard it.  */
     807           0 :       TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx,
     808             :               "unexpected client data");
     809           0 :       rc = 0;
     810             :     }
     811             : 
     812           0 :   return rc;
     813             : }
     814             : 
     815             : 
     816             : /* This function should be invoked when the assuan connected FD is
     817             :    ready for reading.  If the equivalent to EWOULDBLOCK is returned
     818             :    (this should be done by the command handler), assuan_process_next
     819             :    should be invoked the next time the connected FD is readable.
     820             :    Eventually, the caller will finish by invoking assuan_process_done.
     821             :    DONE is set to 1 if the connection has ended.  */
     822             : gpg_error_t
     823           0 : assuan_process_next (assuan_context_t ctx, int *done)
     824             : {
     825             :   gpg_error_t rc;
     826             : 
     827           0 :   if (done)
     828           0 :     *done = 0;
     829           0 :   ctx->process_complete = 0;
     830             :   do
     831             :     {
     832           0 :       rc = process_next (ctx);
     833             :     }
     834           0 :   while (!rc && !ctx->process_complete && assuan_pending_line (ctx));
     835             : 
     836           0 :   if (done)
     837           0 :     *done = !!ctx->process_complete;
     838             : 
     839           0 :   return rc;
     840             : }
     841             : 
     842             : 
     843             : 
     844             : static gpg_error_t
     845          21 : process_request (assuan_context_t ctx)
     846             : {
     847             :   gpg_error_t rc;
     848             : 
     849          21 :   if (ctx->in_inquire)
     850           0 :     return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS);
     851             : 
     852             :   do
     853             :     {
     854          21 :       rc = _assuan_read_line (ctx);
     855             :     }
     856          21 :   while (_assuan_error_is_eagain (ctx, rc));
     857          21 :   if (gpg_err_code (rc) == GPG_ERR_EOF)
     858             :     {
     859           0 :       ctx->process_complete = 1;
     860           0 :       return 0;
     861             :     }
     862          21 :   if (rc)
     863           0 :     return rc;
     864          21 :   if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
     865           6 :     return 0; /* comment line - ignore */
     866             : 
     867          15 :   ctx->in_command = 1;
     868          15 :   ctx->outbound.data.error = 0;
     869          15 :   ctx->outbound.data.linelen = 0;
     870             :   /* dispatch command and return reply */
     871          15 :   rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
     872             : 
     873          15 :   return assuan_process_done (ctx, rc);
     874             : }
     875             : 
     876             : /**
     877             :  * assuan_process:
     878             :  * @ctx: assuan context
     879             :  *
     880             :  * This function is used to handle the assuan protocol after a
     881             :  * connection has been established using assuan_accept().  This is the
     882             :  * main protocol handler.
     883             :  *
     884             :  * Return value: 0 on success or an error code if the assuan operation
     885             :  * failed.  Note, that no error is returned for operational errors.
     886             :  **/
     887             : gpg_error_t
     888           2 : assuan_process (assuan_context_t ctx)
     889             : {
     890             :   gpg_error_t rc;
     891             : 
     892           2 :   ctx->process_complete = 0;
     893             :   do {
     894          21 :     rc = process_request (ctx);
     895          21 :   } while (!rc && !ctx->process_complete);
     896             : 
     897           2 :   return rc;
     898             : }
     899             : 
     900             : 
     901             : /**
     902             :  * assuan_get_active_fds:
     903             :  * @ctx: Assuan context
     904             :  * @what: 0 for read fds, 1 for write fds
     905             :  * @fdarray: Caller supplied array to store the FDs
     906             :  * @fdarraysize: size of that array
     907             :  *
     908             :  * Return all active filedescriptors for the given context.  This
     909             :  * function can be used to select on the fds and call
     910             :  * assuan_process_next() if there is an active one.  The first fd in
     911             :  * the array is the one used for the command connection.
     912             :  *
     913             :  * Note, that write FDs are not yet supported.
     914             :  *
     915             :  * Return value: number of FDs active and put into @fdarray or -1 on
     916             :  * error which is most likely a too small fdarray.
     917             :  **/
     918             : int
     919           0 : assuan_get_active_fds (assuan_context_t ctx, int what,
     920             :                        assuan_fd_t *fdarray, int fdarraysize)
     921             : {
     922           0 :   int n = 0;
     923             : 
     924           0 :   if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
     925           0 :     return -1;
     926             : 
     927           0 :   if (!what)
     928             :     {
     929           0 :       if (ctx->inbound.fd != ASSUAN_INVALID_FD)
     930           0 :         fdarray[n++] = ctx->inbound.fd;
     931             :     }
     932             :   else
     933             :     {
     934           0 :       if (ctx->outbound.fd != ASSUAN_INVALID_FD)
     935           0 :         fdarray[n++] = ctx->outbound.fd;
     936           0 :       if (ctx->outbound.data.fp)
     937             : #if defined(HAVE_W32CE_SYSTEM)
     938             :         fdarray[n++] = (void*)fileno (ctx->outbound.data.fp);
     939             : #elif defined(HAVE_W32_SYSTEM)
     940             :         fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp));
     941             : #else
     942           0 :         fdarray[n++] = fileno (ctx->outbound.data.fp);
     943             : #endif
     944             :     }
     945             : 
     946           0 :   return n;
     947             : }
     948             : 
     949             : 
     950             : /* Two simple wrappers to make the expected function types match. */
     951             : #ifdef HAVE_FUNOPEN
     952             : static int
     953             : fun1_cookie_write (void *cookie, const char *buffer, int orig_size)
     954             : {
     955             :   return _assuan_cookie_write_data (cookie, buffer, orig_size);
     956             : }
     957             : #endif /*HAVE_FUNOPEN*/
     958             : #ifdef HAVE_FOPENCOOKIE
     959             : static ssize_t
     960           0 : fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size)
     961             : {
     962           0 :   return _assuan_cookie_write_data (cookie, buffer, orig_size);
     963             : }
     964             : #endif /*HAVE_FOPENCOOKIE*/
     965             : 
     966             : /* Return a FP to be used for data output.  The FILE pointer is valid
     967             :    until the end of a handler.  So a close is not needed.  Assuan does
     968             :    all the buffering needed to insert the status line as well as the
     969             :    required line wappping and quoting for data lines.
     970             : 
     971             :    We use GNU's custom streams here.  There should be an alternative
     972             :    implementaion for systems w/o a glibc, a simple implementation
     973             :    could use a child process */
     974             : FILE *
     975           0 : assuan_get_data_fp (assuan_context_t ctx)
     976             : {
     977             : #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
     978           0 :   if (ctx->outbound.data.fp)
     979           0 :     return ctx->outbound.data.fp;
     980             : 
     981             : #ifdef HAVE_FUNOPEN
     982             :   ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write,
     983             :                                    0, _assuan_cookie_write_flush);
     984             : #else
     985           0 :   ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write,
     986             :                                    0, _assuan_cookie_write_flush);
     987             : #endif
     988             : 
     989           0 :   ctx->outbound.data.error = 0;
     990           0 :   return ctx->outbound.data.fp;
     991             : #else
     992             :   gpg_err_set_errno (ENOSYS);
     993             :   return NULL;
     994             : #endif
     995             : }
     996             : 
     997             : 
     998             : /* Set the text used for the next OK reponse.  This string is
     999             :    automatically reset to NULL after the next command. */
    1000             : gpg_error_t
    1001           0 : assuan_set_okay_line (assuan_context_t ctx, const char *line)
    1002             : {
    1003           0 :   if (!ctx)
    1004           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
    1005           0 :   if (!line)
    1006             :     {
    1007           0 :       _assuan_free (ctx, ctx->okay_line);
    1008           0 :       ctx->okay_line = NULL;
    1009             :     }
    1010             :   else
    1011             :     {
    1012             :       /* FIXME: we need to use gcry_is_secure() to test whether
    1013             :          we should allocate the entire line in secure memory */
    1014           0 :       char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1);
    1015           0 :       if (!buf)
    1016           0 :         return _assuan_error (ctx, gpg_err_code_from_syserror ());
    1017           0 :       strcpy (buf, "OK ");
    1018           0 :       strcpy (buf+3, line);
    1019           0 :       _assuan_free (ctx, ctx->okay_line);
    1020           0 :       ctx->okay_line = buf;
    1021             :     }
    1022           0 :   return 0;
    1023             : }
    1024             : 
    1025             : 
    1026             : 
    1027             : gpg_error_t
    1028           0 : assuan_write_status (assuan_context_t ctx,
    1029             :                      const char *keyword, const char *text)
    1030             : {
    1031             :   char buffer[256];
    1032             :   char *helpbuf;
    1033             :   size_t n;
    1034             :   gpg_error_t ae;
    1035             : 
    1036           0 :   if ( !ctx || !keyword)
    1037           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
    1038           0 :   if (!text)
    1039           0 :     text = "";
    1040             : 
    1041           0 :   n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
    1042           0 :   if (n < sizeof (buffer))
    1043             :     {
    1044           0 :       strcpy (buffer, "S ");
    1045           0 :       strcat (buffer, keyword);
    1046           0 :       if (*text)
    1047             :         {
    1048           0 :           strcat (buffer, " ");
    1049           0 :           strcat (buffer, text);
    1050             :         }
    1051           0 :       ae = assuan_write_line (ctx, buffer);
    1052             :     }
    1053           0 :   else if ( (helpbuf = _assuan_malloc (ctx, n)) )
    1054             :     {
    1055           0 :       strcpy (helpbuf, "S ");
    1056           0 :       strcat (helpbuf, keyword);
    1057           0 :       if (*text)
    1058             :         {
    1059           0 :           strcat (helpbuf, " ");
    1060           0 :           strcat (helpbuf, text);
    1061             :         }
    1062           0 :       ae = assuan_write_line (ctx, helpbuf);
    1063           0 :       _assuan_free (ctx, helpbuf);
    1064             :     }
    1065             :   else
    1066           0 :     ae = 0;
    1067           0 :   return ae;
    1068             : }

Generated by: LCOV version 1.11