summaryrefslogtreecommitdiffstats
path: root/libtdenetwork/libgpgme-copy/assuan/assuan-handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtdenetwork/libgpgme-copy/assuan/assuan-handler.c')
-rw-r--r--libtdenetwork/libgpgme-copy/assuan/assuan-handler.c774
1 files changed, 774 insertions, 0 deletions
diff --git a/libtdenetwork/libgpgme-copy/assuan/assuan-handler.c b/libtdenetwork/libgpgme-copy/assuan/assuan-handler.c
new file mode 100644
index 00000000..866db225
--- /dev/null
+++ b/libtdenetwork/libgpgme-copy/assuan/assuan-handler.c
@@ -0,0 +1,774 @@
+/* assuan-handler.c - dispatch commands
+ * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "assuan-defs.h"
+
+
+
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+#define digitp(a) ((a) >= '0' && (a) <= '9')
+
+static int my_strcasecmp (const char *a, const char *b);
+
+
+
+static int
+dummy_handler (assuan_context_t ctx, char *line)
+{
+ return set_error (ctx, Server_Fault, "no handler registered");
+}
+
+
+static int
+std_handler_nop (assuan_context_t ctx, char *line)
+{
+ return 0; /* okay */
+}
+
+static int
+std_handler_cancel (assuan_context_t ctx, char *line)
+{
+ if (ctx->cancel_notify_fnc)
+ ctx->cancel_notify_fnc (ctx);
+ return set_error (ctx, Not_Implemented, NULL);
+}
+
+static int
+std_handler_option (assuan_context_t ctx, char *line)
+{
+ char *key, *value, *p;
+
+ for (key=line; spacep (key); key++)
+ ;
+ if (!*key)
+ return set_error (ctx, Syntax_Error, "argument required");
+ if (*key == '=')
+ return set_error (ctx, Syntax_Error, "no option name given");
+ for (value=key; *value && !spacep (value) && *value != '='; value++)
+ ;
+ if (*value)
+ {
+ if (spacep (value))
+ *value++ = 0; /* terminate key */
+ for (; spacep (value); value++)
+ ;
+ if (*value == '=')
+ {
+ *value++ = 0; /* terminate key */
+ for (; spacep (value); value++)
+ ;
+ if (!*value)
+ return set_error (ctx, Syntax_Error, "option argument expected");
+ }
+ if (*value)
+ {
+ for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
+ ;
+ if (p > value)
+ *++p = 0; /* strip trailing spaces */
+ }
+ }
+
+ if (*key == '-' && key[1] == '-' && key[2])
+ key += 2; /* the double dashes are optional */
+ if (*key == '-')
+ return set_error (ctx, Syntax_Error,
+ "option should not begin with one dash");
+
+ if (ctx->option_handler_fnc)
+ return ctx->option_handler_fnc (ctx, key, value);
+ return 0;
+}
+
+static int
+std_handler_bye (assuan_context_t ctx, char *line)
+{
+ if (ctx->bye_notify_fnc)
+ ctx->bye_notify_fnc (ctx);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return -1; /* pretty simple :-) */
+}
+
+static int
+std_handler_auth (assuan_context_t ctx, char *line)
+{
+ return set_error (ctx, Not_Implemented, NULL);
+}
+
+static int
+std_handler_reset (assuan_context_t ctx, char *line)
+{
+ if (ctx->reset_notify_fnc)
+ ctx->reset_notify_fnc (ctx);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ _assuan_uds_close_fds (ctx);
+ return 0;
+}
+
+static int
+std_handler_end (assuan_context_t ctx, char *line)
+{
+ return set_error (ctx, Not_Implemented, NULL);
+}
+
+assuan_error_t
+assuan_command_parse_fd (assuan_context_t ctx, char *line, int *rfd)
+{
+ char *endp;
+
+ if ( (strncmp (line, "FD", 2) && strncmp (line, "fd", 2))
+ || (line[2] != '=' && line[2] != '\0'))
+ return set_error (ctx, Syntax_Error, "FD[=<n>] expected");
+ line += 2;
+ if (*line == '=')
+ {
+ line ++;
+ if (!digitp (*line))
+ return set_error (ctx, Syntax_Error, "number required");
+ *rfd = strtoul (line, &endp, 10);
+ /* Remove that argument so that a notify handler won't see it. */
+ memset (line, ' ', endp? (endp-line):strlen(line));
+
+ if (*rfd == ctx->inbound.fd)
+ return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
+ if (*rfd == ctx->outbound.fd)
+ return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
+ return 0;
+ }
+ else
+ /* Our peer has sent the file descriptor. */
+ return assuan_receivefd (ctx, rfd);
+}
+
+/* Format is INPUT FD=<n> */
+static int
+std_handler_input (assuan_context_t ctx, char *line)
+{
+ int rc, fd;
+
+ rc = assuan_command_parse_fd (ctx, line, &fd);
+ if (rc)
+ return rc;
+ ctx->input_fd = fd;
+ if (ctx->input_notify_fnc)
+ ctx->input_notify_fnc (ctx, line);
+ return 0;
+}
+
+/* Format is OUTPUT FD=<n> */
+static int
+std_handler_output (assuan_context_t ctx, char *line)
+{
+ int rc, fd;
+
+ rc = assuan_command_parse_fd (ctx, line, &fd);
+ if (rc)
+ return rc;
+ ctx->output_fd = fd;
+ if (ctx->output_notify_fnc)
+ ctx->output_notify_fnc (ctx, line);
+ return 0;
+}
+
+
+
+
+
+/* This is a table with the standard commands and handler for them.
+ The table is used to initialize a new context and associate strings
+ with default handlers */
+static struct {
+ const char *name;
+ int (*handler)(assuan_context_t, char *line);
+ int always; /* always initialize this command */
+} std_cmd_table[] = {
+ { "NOP", std_handler_nop, 1 },
+ { "CANCEL", std_handler_cancel, 1 },
+ { "OPTION", std_handler_option, 1 },
+ { "BYE", std_handler_bye, 1 },
+ { "AUTH", std_handler_auth, 1 },
+ { "RESET", std_handler_reset, 1 },
+ { "END", std_handler_end, 1 },
+
+ { "INPUT", std_handler_input },
+ { "OUTPUT", std_handler_output },
+ { "OPTION", std_handler_option, 1 },
+ { NULL }
+};
+
+
+/**
+ * assuan_register_command:
+ * @ctx: the server context
+ * @cmd_name: A string with the command name
+ * @handler: The handler function to be called or NULL to use a default
+ * handler.
+ *
+ * Register a handler to be used for a given command. Note that
+ * several default handlers are already regsitered with a new context.
+ * This function however allows to override them.
+ *
+ * Return value: 0 on success or an error code
+ **/
+int
+assuan_register_command (assuan_context_t ctx,
+ const char *cmd_name,
+ int (*handler)(assuan_context_t, char *))
+{
+ int i;
+ const char *s;
+
+ if (cmd_name && !*cmd_name)
+ cmd_name = NULL;
+
+ if (!cmd_name)
+ return _assuan_error (ASSUAN_Invalid_Value);
+
+ if (!handler)
+ { /* find a default handler. */
+ for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++)
+ ;
+ if (!s)
+ { /* Try again but case insensitive. */
+ for (i=0; (s=std_cmd_table[i].name)
+ && my_strcasecmp (cmd_name, s); i++)
+ ;
+ }
+ if (s)
+ handler = std_cmd_table[i].handler;
+ if (!handler)
+ handler = dummy_handler; /* Last resort is the dummy handler. */
+ }
+
+ if (!ctx->cmdtbl)
+ {
+ ctx->cmdtbl_size = 50;
+ ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
+ if (!ctx->cmdtbl)
+ return _assuan_error (ASSUAN_Out_Of_Core);
+ ctx->cmdtbl_used = 0;
+ }
+ else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
+ {
+ struct cmdtbl_s *x;
+
+ x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
+ if (!x)
+ return _assuan_error (ASSUAN_Out_Of_Core);
+ ctx->cmdtbl = x;
+ ctx->cmdtbl_size += 50;
+ }
+
+ ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
+ ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
+ ctx->cmdtbl_used++;
+ return 0;
+}
+
+int
+assuan_register_post_cmd_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t, int))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->post_cmd_notify_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_bye_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->bye_notify_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_reset_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->reset_notify_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_cancel_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->cancel_notify_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_option_handler (assuan_context_t ctx,
+ int (*fnc)(assuan_context_t,
+ const char*, const char*))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->option_handler_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_input_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t, const char *))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->input_notify_fnc = fnc;
+ return 0;
+}
+
+int
+assuan_register_output_notify (assuan_context_t ctx,
+ void (*fnc)(assuan_context_t, const char *))
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ ctx->output_notify_fnc = fnc;
+ return 0;
+}
+
+
+/* Helper to register the standards commands */
+int
+_assuan_register_std_commands (assuan_context_t ctx)
+{
+ int i, rc;
+
+ for (i=0; std_cmd_table[i].name; i++)
+ {
+ if (std_cmd_table[i].always)
+ {
+ rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL);
+ if (rc)
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+
+/* Process the special data lines. The "D " has already been removed
+ from the line. As all handlers this function may modify the line. */
+static int
+handle_data_line (assuan_context_t ctx, char *line, int linelen)
+{
+ return set_error (ctx, Not_Implemented, NULL);
+}
+
+/* like ascii_strcasecmp but assume that B is already uppercase */
+static int
+my_strcasecmp (const char *a, const char *b)
+{
+ if (a == b)
+ return 0;
+
+ for (; *a && *b; a++, b++)
+ {
+ if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
+ break;
+ }
+ return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
+}
+
+/* Parse the line, break out the command, find it in the command
+ table, remove leading and white spaces from the arguments, call the
+ handler with the argument line and return the error */
+static int
+dispatch_command (assuan_context_t ctx, char *line, int linelen)
+{
+ char *p;
+ const char *s;
+ int shift, i;
+
+ if (*line == 'D' && line[1] == ' ') /* divert to special handler */
+ return handle_data_line (ctx, line+2, linelen-2);
+
+ for (p=line; *p && *p != ' ' && *p != '\t'; p++)
+ ;
+ if (p==line)
+ return set_error (ctx, Syntax_Error, "leading white-space");
+ if (*p)
+ { /* Skip over leading WS after the keyword */
+ *p++ = 0;
+ while ( *p == ' ' || *p == '\t')
+ p++;
+ }
+ shift = p - line;
+
+ for (i=0; (s=ctx->cmdtbl[i].name); i++)
+ {
+ if (!strcmp (line, s))
+ break;
+ }
+ if (!s)
+ { /* and try case insensitive */
+ for (i=0; (s=ctx->cmdtbl[i].name); i++)
+ {
+ if (!my_strcasecmp (line, s))
+ break;
+ }
+ }
+ if (!s)
+ return set_error (ctx, Unknown_Command, NULL);
+ line += shift;
+ linelen -= shift;
+
+/* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
+ return ctx->cmdtbl[i].handler (ctx, line);
+}
+
+
+
+
+static int
+process_request (assuan_context_t ctx)
+{
+ int rc;
+
+ if (ctx->in_inquire)
+ return _assuan_error (ASSUAN_Nested_Commands);
+
+ rc = _assuan_read_line (ctx);
+ if (rc)
+ return rc;
+ if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
+ return 0; /* comment line - ignore */
+
+ ctx->outbound.data.error = 0;
+ ctx->outbound.data.linelen = 0;
+ /* dispatch command and return reply */
+ rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
+ /* check from data write errors */
+ if (ctx->outbound.data.fp)
+ { /* Flush the data lines */
+ fclose (ctx->outbound.data.fp);
+ ctx->outbound.data.fp = NULL;
+ if (!rc && ctx->outbound.data.error)
+ rc = ctx->outbound.data.error;
+ }
+ else /* flush any data send w/o using the data fp */
+ {
+ assuan_send_data (ctx, NULL, 0);
+ if (!rc && ctx->outbound.data.error)
+ rc = ctx->outbound.data.error;
+ }
+ /* Error handling */
+ if (!rc)
+ {
+ rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK");
+ }
+ else if (err_is_eof (rc))
+ { /* No error checking because the peer may have already disconnect. */
+ assuan_write_line (ctx, "OK closing connection");
+ ctx->finish_handler (ctx);
+ }
+ else
+ {
+ char errline[300];
+
+ if (rc < 100)
+ sprintf (errline, "ERR %d server fault (%.50s)",
+ _assuan_error (ASSUAN_Server_Fault), assuan_strerror (rc));
+ else
+ {
+ const char *text = ctx->err_no == rc? ctx->err_str:NULL;
+
+#if defined(__GNUC__) && defined(__ELF__)
+ /* If we have weak symbol support we try to use the error
+ strings from libgpg-error without creating a dependency.
+ They are used for debugging purposes only, so there is no
+ problem if they are not available. We need to make sure
+ that we are using ELF because only this guarantees that
+ weak symbol support is available in case GNU ld is not
+ used. It seems that old gcc versions don't implement the
+ weak attribute properly but it works with the weak
+ pragma. */
+
+ unsigned int source, code;
+
+ int gpg_strerror_r (unsigned int err, char *buf, size_t buflen)
+ __attribute__ ((weak));
+ const char *gpg_strsource (unsigned int err)
+ __attribute__ ((weak));
+#if !defined(HAVE_W32_SYSTEM) && __GNUC__ < 3
+#pragma weak gpg_strerror_r
+#pragma weak gpg_strsource
+#endif
+
+ source = ((rc >> 24) & 0xff);
+ code = (rc & 0x00ffffff);
+ if (source && gpg_strsource && gpg_strerror_r)
+ {
+ /* Assume this is an libgpg-error. */
+ char ebuf[50];
+
+ gpg_strerror_r (rc, ebuf, sizeof ebuf );
+ sprintf (errline, "ERR %d %.50s <%.30s>%s%.100s",
+ rc,
+ ebuf,
+ gpg_strsource (rc),
+ text? " - ":"", text?text:"");
+ }
+ else
+#endif /* __GNUC__ && __ELF__ */
+ sprintf (errline, "ERR %d %.50s%s%.100s",
+ rc, assuan_strerror (rc), text? " - ":"", text?text:"");
+ }
+ rc = assuan_write_line (ctx, errline);
+ }
+
+ if (ctx->post_cmd_notify_fnc)
+ ctx->post_cmd_notify_fnc (ctx, rc);
+
+ ctx->confidential = 0;
+ if (ctx->okay_line)
+ {
+ xfree (ctx->okay_line);
+ ctx->okay_line = NULL;
+ }
+ return rc;
+}
+
+/**
+ * assuan_process:
+ * @ctx: assuan context
+ *
+ * This function is used to handle the assuan protocol after a
+ * connection has been established using assuan_accept(). This is the
+ * main protocol handler.
+ *
+ * Return value: 0 on success or an error code if the assuan operation
+ * failed. Note, that no error is returned for operational errors.
+ **/
+int
+assuan_process (assuan_context_t ctx)
+{
+ int rc;
+
+ do {
+ rc = process_request (ctx);
+ } while (!rc);
+
+ if (err_is_eof (rc))
+ rc = 0;
+
+ return rc;
+}
+
+
+/**
+ * assuan_process_next:
+ * @ctx: Assuan context
+ *
+ * Same as assuan_process() but the user has to provide the outer
+ * loop. He should loop as long as the return code is zero and stop
+ * otherwise; -1 is regular end.
+ *
+ * See also: assuan_get_active_fds()
+ * Return value: -1 for end of server, 0 on success or an error code
+ **/
+int
+assuan_process_next (assuan_context_t ctx)
+{
+ return process_request (ctx);
+}
+
+
+/**
+ * assuan_get_active_fds:
+ * @ctx: Assuan context
+ * @what: 0 for read fds, 1 for write fds
+ * @fdarray: Caller supplied array to store the FDs
+ * @fdarraysize: size of that array
+ *
+ * Return all active filedescriptors for the given context. This
+ * function can be used to select on the fds and call
+ * assuan_process_next() if there is an active one. The first fd in
+ * the array is the one used for the command connection.
+ *
+ * Note, that write FDs are not yet supported.
+ *
+ * Return value: number of FDs active and put into @fdarray or -1 on
+ * error which is most likely a too small fdarray.
+ **/
+int
+assuan_get_active_fds (assuan_context_t ctx, int what,
+ int *fdarray, int fdarraysize)
+{
+ int n = 0;
+
+ if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
+ return -1;
+
+ if (!what)
+ {
+ if (ctx->inbound.fd != -1)
+ fdarray[n++] = ctx->inbound.fd;
+ }
+ else
+ {
+ if (ctx->outbound.fd != -1)
+ fdarray[n++] = ctx->outbound.fd;
+ if (ctx->outbound.data.fp)
+ fdarray[n++] = fileno (ctx->outbound.data.fp);
+ }
+
+ return n;
+}
+
+
+/* Two simple wrappers to make the expected function types match. */
+#ifdef HAVE_FUNOPEN
+static int
+fun1_cookie_write (void *cookie, const char *buffer, int orig_size)
+{
+ return _assuan_cookie_write_data (cookie, buffer, orig_size);
+}
+#endif /*HAVE_FUNOPEN*/
+#ifdef HAVE_FOPENCOOKIE
+static ssize_t
+fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size)
+{
+ return _assuan_cookie_write_data (cookie, buffer, orig_size);
+}
+#endif /*HAVE_FOPENCOOKIE*/
+
+/* Return a FP to be used for data output. The FILE pointer is valid
+ until the end of a handler. So a close is not needed. Assuan does
+ all the buffering needed to insert the status line as well as the
+ required line wappping and quoting for data lines.
+
+ We use GNU's custom streams here. There should be an alternative
+ implementaion for systems w/o a glibc, a simple implementation
+ could use a child process */
+FILE *
+assuan_get_data_fp (assuan_context_t ctx)
+{
+#if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
+ if (ctx->outbound.data.fp)
+ return ctx->outbound.data.fp;
+
+#ifdef HAVE_FUNOPEN
+ ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write,
+ 0, _assuan_cookie_write_flush);
+#else
+ ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write,
+ 0, _assuan_cookie_write_flush);
+#endif
+
+ ctx->outbound.data.error = 0;
+ return ctx->outbound.data.fp;
+#else
+ errno = ENOSYS;
+ return NULL;
+#endif
+}
+
+
+/* Set the text used for the next OK reponse. This string is
+ automatically reset to NULL after the next command. */
+assuan_error_t
+assuan_set_okay_line (assuan_context_t ctx, const char *line)
+{
+ if (!ctx)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ if (!line)
+ {
+ xfree (ctx->okay_line);
+ ctx->okay_line = NULL;
+ }
+ else
+ {
+ /* FIXME: we need to use gcry_is_secure() to test whether
+ we should allocate the entire line in secure memory */
+ char *buf = xtrymalloc (3+strlen(line)+1);
+ if (!buf)
+ return _assuan_error (ASSUAN_Out_Of_Core);
+ strcpy (buf, "OK ");
+ strcpy (buf+3, line);
+ xfree (ctx->okay_line);
+ ctx->okay_line = buf;
+ }
+ return 0;
+}
+
+
+
+assuan_error_t
+assuan_write_status (assuan_context_t ctx,
+ const char *keyword, const char *text)
+{
+ char buffer[256];
+ char *helpbuf;
+ size_t n;
+ assuan_error_t ae;
+
+ if ( !ctx || !keyword)
+ return _assuan_error (ASSUAN_Invalid_Value);
+ if (!text)
+ text = "";
+
+ n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
+ if (n < sizeof (buffer))
+ {
+ strcpy (buffer, "S ");
+ strcat (buffer, keyword);
+ if (*text)
+ {
+ strcat (buffer, " ");
+ strcat (buffer, text);
+ }
+ ae = assuan_write_line (ctx, buffer);
+ }
+ else if ( (helpbuf = xtrymalloc (n)) )
+ {
+ strcpy (helpbuf, "S ");
+ strcat (helpbuf, keyword);
+ if (*text)
+ {
+ strcat (helpbuf, " ");
+ strcat (helpbuf, text);
+ }
+ ae = assuan_write_line (ctx, helpbuf);
+ xfree (helpbuf);
+ }
+ else
+ ae = 0;
+ return ae;
+}