summaryrefslogtreecommitdiff
path: root/src/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/client.c')
-rw-r--r--src/client.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/client.c b/src/client.c
index 685a941..e74b73b 100644
--- a/src/client.c
+++ b/src/client.c
@@ -22,9 +22,16 @@
#include <config.h>
#endif
+#include <stdlib.h>
+
#include "assuan-defs.h"
#include "debug.h"
+#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
+ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
void
_assuan_client_finish (assuan_context_t ctx)
{
@@ -58,3 +65,243 @@ _assuan_client_release (assuan_context_t ctx)
_assuan_client_finish (ctx);
}
+
+
+/* This function also does deescaping for data lines. */
+gpg_error_t
+assuan_client_read_response (assuan_context_t ctx,
+ char **line_r, int *linelen_r)
+{
+ gpg_error_t rc;
+ char *line = NULL;
+ int linelen = 0;
+
+ *line_r = NULL;
+ *linelen_r = 0;
+
+ do
+ {
+ do
+ {
+ rc = _assuan_read_line (ctx);
+ }
+ while (_assuan_error_is_eagain (ctx, rc));
+ if (rc)
+ return rc;
+ line = ctx->inbound.line;
+ linelen = ctx->inbound.linelen;
+ }
+ while (*line == '#' || !linelen);
+
+ /* For data lines, we deescape immediately. The user will never
+ have to worry about it. */
+ if (linelen >= 1 && line[0] == 'D' && line[1] == ' ')
+ {
+ char *s, *d;
+ for (s=d=line; linelen; linelen--)
+ {
+ if (*s == '%' && linelen > 2)
+ { /* handle escaping */
+ s++;
+ *d++ = xtoi_2 (s);
+ s += 2;
+ linelen -= 2;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = 0; /* add a hidden string terminator */
+
+ ctx->inbound.linelen = linelen;
+ }
+
+ *line_r = line;
+ *linelen_r = linelen;
+
+ return 0;
+}
+
+
+gpg_error_t
+assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen,
+ assuan_response_t *response, int *off)
+{
+ *response = ASSUAN_RESPONSE_ERROR;
+ *off = 0;
+
+ if (linelen >= 1
+ && line[0] == 'D' && line[1] == ' ')
+ {
+ *response = ASSUAN_RESPONSE_DATA; /* data line */
+ *off = 2;
+ }
+ else if (linelen >= 1
+ && line[0] == 'S'
+ && (line[1] == '\0' || line[1] == ' '))
+ {
+ *response = ASSUAN_RESPONSE_STATUS;
+ *off = 1;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ *response = ASSUAN_RESPONSE_OK;
+ *off = 2;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ *response = ASSUAN_RESPONSE_ERROR;
+ *off = 3;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ *response = ASSUAN_RESPONSE_INQUIRE;
+ *off = 7;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ *response = ASSUAN_RESPONSE_END;
+ *off = 3;
+ }
+ else
+ return _assuan_error (ctx, GPG_ERR_ASS_INV_RESPONSE);
+
+ return 0;
+}
+
+
+gpg_error_t
+_assuan_read_from_server (assuan_context_t ctx, assuan_response_t *response,
+ int *off)
+{
+ gpg_error_t rc;
+ char *line;
+ int linelen;
+
+ *response = ASSUAN_RESPONSE_ERROR;
+ *off = 0;
+ rc = assuan_client_read_response (ctx, &line, &linelen);
+ if (!rc)
+ rc = assuan_client_parse_response (ctx, line, linelen, response, off);
+ return rc;
+}
+
+
+/**
+ * assuan_transact:
+ * @ctx: The Assuan context
+ * @command: Command line to be send to the server
+ * @data_cb: Callback function for data lines
+ * @data_cb_arg: first argument passed to @data_cb
+ * @inquire_cb: Callback function for a inquire response
+ * @inquire_cb_arg: first argument passed to @inquire_cb
+ * @status_cb: Callback function for a status response
+ * @status_cb_arg: first argument passed to @status_cb
+ *
+ * FIXME: Write documentation
+ *
+ * Return value: 0 on success or error code. The error code may be
+ * the one one returned by the server in error lines or from the
+ * callback functions. Take care: When a callback returns an error
+ * this function returns immediately with an error and thus the caller
+ * will altter return an Assuan error (write erro in most cases).
+ **/
+gpg_error_t
+assuan_transact (assuan_context_t ctx,
+ const char *command,
+ gpg_error_t (*data_cb)(void *, const void *, size_t),
+ void *data_cb_arg,
+ gpg_error_t (*inquire_cb)(void*, const char *),
+ void *inquire_cb_arg,
+ gpg_error_t (*status_cb)(void*, const char *),
+ void *status_cb_arg)
+{
+ gpg_error_t rc;
+ assuan_response_t response;
+ int off;
+ char *line;
+ int linelen;
+
+ rc = assuan_write_line (ctx, command);
+ if (rc)
+ return rc;
+
+ if (*command == '#' || !*command)
+ return 0; /* Don't expect a response for a comment line. */
+
+ again:
+ rc = _assuan_read_from_server (ctx, &response, &off);
+ if (rc)
+ return rc; /* error reading from server */
+
+ line = ctx->inbound.line + off;
+ linelen = ctx->inbound.linelen - off;
+
+ if (response == ASSUAN_RESPONSE_ERROR)
+ rc = atoi (line);
+ else if (response == ASSUAN_RESPONSE_DATA)
+ {
+ if (!data_cb)
+ rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
+ else
+ {
+ rc = data_cb (data_cb_arg, line, linelen);
+ if (!rc)
+ goto again;
+ }
+ }
+ else if (response == ASSUAN_RESPONSE_INQUIRE)
+ {
+ if (!inquire_cb)
+ {
+ assuan_write_line (ctx, "END"); /* get out of inquire mode */
+ _assuan_read_from_server (ctx, &response, &off); /* dummy read */
+ rc = _assuan_error (ctx, GPG_ERR_ASS_NO_INQUIRE_CB);
+ }
+ else
+ {
+ rc = inquire_cb (inquire_cb_arg, line);
+ if (!rc)
+ rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
+ if (!rc)
+ goto again;
+ }
+ }
+ else if (response == ASSUAN_RESPONSE_STATUS)
+ {
+ if (status_cb)
+ rc = status_cb (status_cb_arg, line);
+ if (!rc)
+ goto again;
+ }
+ else if (response == ASSUAN_RESPONSE_END)
+ {
+ if (!data_cb)
+ rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
+ else
+ {
+ rc = data_cb (data_cb_arg, NULL, 0);
+ if (!rc)
+ goto again;
+ }
+ }
+
+ return rc;
+}