From 5a7f006e8aed6ad825c41926eb096459f41ec801 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 10 Nov 2000 17:50:24 +0000 Subject: [PATCH] Started with --list-key --- trunk/gpgme/Makefile.am | 2 + trunk/gpgme/context.h | 2 + trunk/gpgme/gpgme.c | 2 + trunk/gpgme/gpgme.h | 2 + trunk/gpgme/key.c | 135 ++++++++++++++++ trunk/gpgme/key.h | 59 +++++++ trunk/gpgme/keylist.c | 334 ++++++++++++++++++++++++++++++++++++++++ trunk/gpgme/ops.h | 5 + trunk/gpgme/rungpg.c | 171 +++++++++++++++++++- trunk/gpgme/rungpg.h | 5 + trunk/gpgme/types.h | 3 + trunk/tests/Makefile.am | 2 +- trunk/tests/t-keylist.c | 74 +++++++++ 13 files changed, 793 insertions(+), 3 deletions(-) create mode 100644 trunk/gpgme/key.c create mode 100644 trunk/gpgme/key.h create mode 100644 trunk/gpgme/keylist.c create mode 100644 trunk/tests/t-keylist.c diff --git a/trunk/gpgme/Makefile.am b/trunk/gpgme/Makefile.am index b47169c..547dcd4 100644 --- a/trunk/gpgme/Makefile.am +++ b/trunk/gpgme/Makefile.am @@ -19,6 +19,8 @@ libgpgme_la_SOURCES = \ wait.c wait.h \ encrypt.c \ verify.c \ + key.c key.h \ + keylist.c \ rungpg.c rungpg.h status-table.h \ gpgme.c errors.c diff --git a/trunk/gpgme/context.h b/trunk/gpgme/context.h index c45d1bf..ab5a2d3 100644 --- a/trunk/gpgme/context.h +++ b/trunk/gpgme/context.h @@ -53,6 +53,8 @@ struct gpgme_context_s { union { VerifyResult verify; } result; + + GpgmeKey tmp_key; /* used by keylist.c */ }; diff --git a/trunk/gpgme/gpgme.c b/trunk/gpgme/gpgme.c index 10df508..8a8cbb2 100644 --- a/trunk/gpgme/gpgme.c +++ b/trunk/gpgme/gpgme.c @@ -61,6 +61,7 @@ gpgme_release_context ( GpgmeCtx c ) _gpgme_gpg_release_object ( c->gpg ); _gpgme_release_result ( c ); + _gpgme_key_release ( c->tmp_key ); xfree ( c ); } @@ -75,6 +76,7 @@ _gpgme_release_result ( GpgmeCtx c ) _gpgme_release_verify_result ( c->result.verify ); break; } + c->result.verify = NULL; c->result_type = RESULT_TYPE_NONE; } diff --git a/trunk/gpgme/gpgme.h b/trunk/gpgme/gpgme.h index 88a8434..32a925f 100644 --- a/trunk/gpgme/gpgme.h +++ b/trunk/gpgme/gpgme.h @@ -95,6 +95,8 @@ GpgmeError gpgme_start_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text ); /* Key management functions */ +GpgmeError gpgme_keylist_start ( GpgmeCtx c, + const char *pattern, int secret_only ); diff --git a/trunk/gpgme/key.c b/trunk/gpgme/key.c new file mode 100644 index 0000000..92bb597 --- /dev/null +++ b/trunk/gpgme/key.c @@ -0,0 +1,135 @@ +/* key.c - Key and keyList objects + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include + +#include "util.h" +#include "ops.h" +#include "key.h" + +#define ALLOC_CHUNK 1024 +#define my_isdigit(a) ( (a) >='0' && (a) <= '9' ) + + +GpgmeError +_gpgme_key_new( GpgmeKey *r_key ) +{ + GpgmeKey key; + + *r_key = NULL; + key = xtrycalloc ( 1, sizeof *key ); + if (!key) + return mk_error (Out_Of_Core); + + *r_key = key; + return 0; +} + +void +_gpgme_key_release ( GpgmeKey key ) +{ + struct user_id_s *u, *u2; + + if (!key) + return; + + xfree (key->fingerprint); + for ( u = key->uids; u; u = u2 ) { + u2 = u->next; + xfree (u); + } + xfree (key); +} + +/* + * Take a name from the --with-colon listing, remove certain escape sequences + * sequences and put it into the list of UIDs + */ +GpgmeError +_gpgme_key_append_name ( GpgmeKey key, const char *s ) +{ + struct user_id_s *uid; + char *d; + + assert (key); + /* we can malloc a buffer of the same length, because the converted + * string will never be larger */ + uid = xtrymalloc ( sizeof *uid + strlen (s) ); + if ( !uid ) + return mk_error (Out_Of_Core); + uid->validity = 0; + d = uid->name; + + while ( *s ) { + if ( *s != '\\' ) + *d++ = *s++; + else if ( s[1] == '\\' ) { + s++; + *d++ = *s++; + } + else if ( s[1] == 'n' ) { + s += 2; + *d++ = '\n'; + } + else if ( s[1] == 'r' ) { + s += 2; + *d++ = '\r'; + } + else if ( s[1] == 'v' ) { + s += 2; + *d++ = '\v'; + } + else if ( s[1] == 'b' ) { + s += 2; + *d++ = '\b'; + } + else if ( s[1] == '0' ) { + /* Hmmm: no way to express this */ + s += 2; + *d++ = '\\'; + *d++ = '\0'; + } + else if ( s[1] == 'x' && my_isdigit (s[2]) && my_isdigit (s[3]) ) { + unsigned int val = (s[2]-'0')*16 + (s[3]-'0'); + if ( !val ) { + *d++ = '\\'; + *d++ = '\0'; + } + else + *(byte*)d++ = val; + s += 3; + } + else { /* should not happen */ + s++; + *d++ = '\\'; + *d++ = *s++; + } + } + + uid->next = key->uids; + key->uids = uid; + return 0; +} + + + diff --git a/trunk/gpgme/key.h b/trunk/gpgme/key.h new file mode 100644 index 0000000..bd32c4b --- /dev/null +++ b/trunk/gpgme/key.h @@ -0,0 +1,59 @@ +/* key.h + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef KEY_H +#define KEY_H + +#include +#include "types.h" + +struct user_id_s { + struct user_id_s *next; + int validity; /* 0 = undefined, 1 = not, 2 = marginal, + 3 = full, 4 = ultimate */ + char name[1]; +}; + +struct gpgme_key_s { + struct { + unsigned int revoked:1 ; + unsigned int expired:1 ; + unsigned int disabled:1 ; + } flags; + unsigned int key_algo; + unsigned int key_len; + char keyid[16+1]; + char *fingerprint; /* malloced hex digits */ + time_t timestamp; /* -1 for invalid, 0 for not available */ + struct user_id_s *uids; + +}; + + +GpgmeError _gpgme_key_append_name ( GpgmeKey key, const char *s ); + + + +#endif /* KEY_H */ + + + + + diff --git a/trunk/gpgme/keylist.c b/trunk/gpgme/keylist.c new file mode 100644 index 0000000..1e7d5b1 --- /dev/null +++ b/trunk/gpgme/keylist.c @@ -0,0 +1,334 @@ +/* keylist.c - key listing + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "context.h" +#include "ops.h" +#include "key.h" + +#define my_isdigit(a) ( (a) >='0' && (a) <= '9' ) + +static void finish_key ( GpgmeCtx ctx ); + + +static void +keylist_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) +{ + if ( ctx->out_of_core ) + return; + + switch (code) { + case STATUS_EOF: + if (ctx->tmp_key) + finish_key (ctx); + break; + + default: + /* ignore all other codes */ + fprintf (stderr, "keylist_status: code=%d not handled\n", code ); + break; + } +} + + +static time_t +parse_timestamp ( char *p ) +{ + struct tm tm; + int i; + + if (!*p ) + return 0; + + if (strlen(p) < 10 || p[4] != '-' || p[7] != '-' ) + return (time_t)-1; + p[4] = 0; + p[7] = 0; + p[10] = 0; /* just in case the time part follows */ + memset (&tm, 0, sizeof tm); + + i = atoi (p); + if ( i < 1900 ) + return (time_t)-1; + tm.tm_year = i - 1900; + + i = atoi (p+5); + if ( i < 1 || i > 12 ) + return (time_t)-1; + tm.tm_mon = i-1; + + i = atoi (p+8); + if ( i < 1 || i > 31 ) + return (time_t)-1; + tm.tm_mday = i; + + return mktime (&tm); +} + + +static void +set_trust_info ( GpgmeKey key, const char *s ) +{ + /* look at letters and stop at the first digit */ + for (; *s && !my_isdigit (*s); s++ ) { + switch (*s) { + case 'e': key->flags.expired = 1; break; + case 'r': key->flags.revoked = 1; break; + case 'd': key->flags.disabled = 1; break; + case 'n': key->uids->validity = 1; break; + case 'm': key->uids->validity = 2; break; + case 'f': key->uids->validity = 3; break; + case 'u': key->uids->validity = 4; break; + } + } +} + + +/* Note: we are allowed to modify line */ +static void +keylist_colon_handler ( GpgmeCtx ctx, char *line ) +{ + char *p, *pend; + int field = 0; + enum { + RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC + } rectype = RT_NONE; + GpgmeKey key = ctx->tmp_key; + int i; + const char *trust_info = NULL; + + + if ( ctx->out_of_core ) + return; + if (!line) + return; /* EOF */ + + fprintf (stderr, "line=`%s'\n", line ); + + for (p = line; p; p = pend) { + field++; + pend = strchr (p, ':'); + if (pend) + *pend++ = 0; + + if ( field == 1 ) { + if ( !strcmp ( p, "sig" ) ) + rectype = RT_SIG; + else if ( !strcmp ( p, "uid" ) && key ) { + rectype = RT_UID; + key = ctx->tmp_key; + } + else if ( !strcmp ( p, "sub" ) ) + rectype = RT_SUB; + else if ( !strcmp ( p, "pub" ) ) { + /* start a new keyblock */ + if ( _gpgme_key_new ( &key ) ) { + ctx->out_of_core=1; /* the only kind of error we can get */ + return; + } + rectype = RT_PUB; + if ( ctx->tmp_key ) + finish_key ( ctx ); + assert ( !ctx->tmp_key ); + ctx->tmp_key = key; + } + else if ( !strcmp ( p, "fpr" ) && key ) + rectype = RT_FPR; + else if ( !strcmp ( p, "ssb" ) ) + rectype = RT_SSB; + else if ( !strcmp ( p, "sec" ) ) + rectype = RT_SEC; + else + rectype = RT_NONE; + + } + else if ( rectype == RT_PUB /*|| rectype == RT_SUB*/ ) { + switch (field) { + case 2: /* trust info */ + if ( rectype == RT_PUB ) + trust_info = p; /*save for later */ + break; + case 3: /* key length */ + i = atoi (p); + if ( i > 1 ) /* ignore invalid values */ + key->key_len = i; + break; + case 4: /* pubkey algo */ + i = atoi (p); + if ( i > 1 && i < 128 ) + key->key_algo = i; + break; + case 5: /* long keyid */ + if ( strlen (p) == DIM(key->keyid)-1 ) + strcpy (key->keyid, p); + break; + case 6: /* timestamp (1998-02-28) */ + key->timestamp = parse_timestamp (p); + break; + case 7: /* valid for n days */ + break; + case 8: /* reserved (LID) */ + break; + case 9: /* ownertrust */ + break; + case 10: /* This is the first name listed */ + if ( rectype == RT_PUB ) { + if ( _gpgme_key_append_name ( key, p) ) + ctx->out_of_core = 1; + else { + if (trust_info) + set_trust_info (key, trust_info); + } + } + break; + case 11: /* signature class */ + break; + case 12: + pend = NULL; /* we can stop here */ + break; + } + } + else if ( rectype == RT_UID ) { + switch (field) { + case 2: /* trust info */ + trust_info = p; /*save for later */ + break; + case 10: /* the 2nd, 3rd,... user ID */ + if ( _gpgme_key_append_name ( key, p) ) + ctx->out_of_core = 1; + else { + if (trust_info) + set_trust_info (key, trust_info); + } + pend = NULL; /* we can stop here */ + break; + } + } + else if ( rectype == RT_FPR ) { + switch (field) { + case 10: /* fingerprint (take only the first one)*/ + if ( !key->fingerprint && *p ) { + key->fingerprint = xtrystrdup (p); + if ( !key->fingerprint ) + ctx->out_of_core = 1; + } + pend = NULL; /* that is all we want */ + break; + } + } + } + +} + + +/* + * We have read an entire key into ctx->tmp_key and should now finish + * it. It is assumed that this releases ctx->tmp_key. + */ +static void +finish_key ( GpgmeCtx ctx ) +{ + GpgmeKey key = ctx->tmp_key; + struct user_id_s *u; + + assert (key); + ctx->tmp_key = NULL; + + fprintf (stderr, "finish_key: keyid=`%s'\n", key->keyid ); + if ( key->fingerprint ) + fprintf (stderr, "finish_key: fpr=`%s'\n", key->fingerprint ); + for (u=key->uids; u; u = u->next ) + fprintf (stderr, "finish_key: uid=`%s'\n", u->name ); + + + /* fixme: call the callback or do something else with the key */ + + _gpgme_key_release (key); +} + + + + +GpgmeError +gpgme_keylist_start ( GpgmeCtx c, const char *pattern, int secret_only ) +{ + GpgmeError rc = 0; + int i; + + fail_on_pending_request( c ); + c->pending = 1; + + _gpgme_release_result (c); + c->out_of_core = 0; + + if ( c->gpg ) { + _gpgme_gpg_release_object ( c->gpg ); + c->gpg = NULL; + } + _gpgme_key_release (c->tmp_key); + c->tmp_key = NULL; + + rc = _gpgme_gpg_new_object ( &c->gpg ); + if (rc) + goto leave; + + _gpgme_gpg_set_status_handler ( c->gpg, keylist_status_handler, c ); + + rc = _gpgme_gpg_set_colon_line_handler ( c->gpg, + keylist_colon_handler, c ); + if (rc) + goto leave; + + /* build the commandline */ + for ( i=0; i < c->verbosity; i++ ) + _gpgme_gpg_add_arg ( c->gpg, "--verbose" ); + _gpgme_gpg_add_arg ( c->gpg, "--with-colons" ); + _gpgme_gpg_add_arg ( c->gpg, "--with-fingerprint" ); + _gpgme_gpg_add_arg ( c->gpg, secret_only? + "--list-secret-keys":"--list-keys" ); + + /* Tell the gpg object about the data */ + _gpgme_gpg_add_arg ( c->gpg, "--" ); + if (pattern && *pattern) + _gpgme_gpg_add_arg ( c->gpg, pattern ); + + /* and kick off the process */ + rc = _gpgme_gpg_spawn ( c->gpg, c ); + + leave: + if (rc) { + c->pending = 0; + _gpgme_gpg_release_object ( c->gpg ); c->gpg = NULL; + } + return rc; +} + + + + + + diff --git a/trunk/gpgme/ops.h b/trunk/gpgme/ops.h index 59efeff..020f0fa 100644 --- a/trunk/gpgme/ops.h +++ b/trunk/gpgme/ops.h @@ -39,6 +39,11 @@ void _gpgme_set_data_mode ( GpgmeData dh, GpgmeDataMode mode ); GpgmeError _gpgme_append_data ( GpgmeData dh, const char *buffer, size_t length ); +/*-- key.c --*/ +GpgmeError _gpgme_key_new( GpgmeKey *r_key ); +void _gpgme_key_release ( GpgmeKey key ); + + /*-- verify.c --*/ void _gpgme_release_verify_result ( VerifyResult res ); diff --git a/trunk/gpgme/rungpg.c b/trunk/gpgme/rungpg.c index ac6ba11..2926329 100644 --- a/trunk/gpgme/rungpg.c +++ b/trunk/gpgme/rungpg.c @@ -73,6 +73,17 @@ struct gpg_object_s { void *fnc_value; } status; + /* This is a kludge - see the comment at gpg_colon_line_handler */ + struct { + int fd[2]; + size_t bufsize; + char *buffer; + size_t readpos; + int eof; + GpgColonLineHandler fnc; /* this indicate use of this structrue */ + void *fnc_value; + } colon; + char **argv; struct fd_data_map_s *fd_data_map; @@ -86,12 +97,16 @@ struct gpg_object_s { static void kill_gpg ( GpgObject gpg ); static void free_argv ( char **argv ); static void free_fd_data_map ( struct fd_data_map_s *fd_data_map ); -static int gpg_status_handler ( void *opaque, pid_t pid, int fd ); + static int gpg_inbound_handler ( void *opaque, pid_t pid, int fd ); static int gpg_outbound_handler ( void *opaque, pid_t pid, int fd ); +static int gpg_status_handler ( void *opaque, pid_t pid, int fd ); static GpgmeError read_status ( GpgObject gpg ); +static int gpg_colon_line_handler ( void *opaque, pid_t pid, int fd ); +static GpgmeError read_colon_line ( GpgObject gpg ); + GpgmeError @@ -109,6 +124,8 @@ _gpgme_gpg_new_object ( GpgObject *r_gpg ) gpg->status.fd[0] = -1; gpg->status.fd[1] = -1; + gpg->colon.fd[0] = -1; + gpg->colon.fd[1] = -1; /* allocate the read buffer for the status pipe */ gpg->status.bufsize = 1024; @@ -150,12 +167,17 @@ _gpgme_gpg_release_object ( GpgObject gpg ) if ( !gpg ) return; xfree (gpg->status.buffer); + xfree (gpg->colon.buffer); if ( gpg->argv ) free_argv (gpg->argv); if (gpg->status.fd[0] != -1 ) close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1 ) close (gpg->status.fd[1]); + if (gpg->colon.fd[0] != -1 ) + close (gpg->colon.fd[0]); + if (gpg->colon.fd[1] != -1 ) + close (gpg->colon.fd[1]); free_fd_data_map (gpg->fd_data_map); kill_gpg (gpg); /* fixme: should be done asyncronously */ xfree (gpg); @@ -232,6 +254,30 @@ _gpgme_gpg_set_status_handler ( GpgObject gpg, gpg->status.fnc_value = fnc_value; } +/* Kludge to process --with-colon output */ +GpgmeError +_gpgme_gpg_set_colon_line_handler ( GpgObject gpg, + GpgColonLineHandler fnc, void *fnc_value ) +{ + assert (gpg); + + gpg->colon.bufsize = 1024; + gpg->colon.readpos = 0; + gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize); + if (!gpg->colon.buffer) { + return mk_error (Out_Of_Core); + } + if (pipe (gpg->colon.fd) == -1) { + xfree (gpg->colon.buffer); gpg->colon.buffer = NULL; + return mk_error (Pipe_Error); + } + gpg->colon.eof = 0; + gpg->colon.fnc = fnc; + gpg->colon.fnc_value = fnc_value; + return 0; +} + + static void free_argv ( char **argv ) { @@ -431,6 +477,17 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) int duped_stderr = 0; close (gpg->status.fd[0]); + + if (gpg->colon.fnc) { + /* dup it to stdout */ + if ( dup2 ( gpg->colon.fd[1], 1 ) == -1 ) { + fprintf (stderr,"dup2(colon, 1) failed: %s\n", + strerror (errno) ); + _exit (8); + } + close (gpg->colon.fd[0]); + close (gpg->colon.fd[1]); + } for (i=0; gpg->fd_data_map[i].data; i++ ) { close (gpg->fd_data_map[i].fd); @@ -485,14 +542,29 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ - if ( gpg->status.fd[1] != -1 ) + if ( gpg->status.fd[1] != -1 ) { close (gpg->status.fd[1]); + gpg->status.fd[1] = -1; + } if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler, gpg, pid, gpg->status.fd[0], 1 ) ) { /* FIXME: kill the child */ return mk_error (General_Error); } + + if ( gpg->colon.fd[1] != -1 ) { + close (gpg->colon.fd[1]); + gpg->colon.fd[1] = -1; + assert ( gpg->colon.fd[0] != -1 ); + if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler, + gpg, pid, gpg->colon.fd[0], 1 ) ) { + /* FIXME: kill the child */ + return mk_error (General_Error); + + } + } + for (i=0; gpg->fd_data_map[i].data; i++ ) { close (gpg->fd_data_map[i].peer_fd); gpg->fd_data_map[i].peer_fd = -1; @@ -724,3 +796,98 @@ read_status ( GpgObject gpg ) } +/* + * This colonline handler thing is not the clean way to do it. + * It might be better to enhance the GpgmeData object to act as + * a wrapper for a callback. Same goes for the status thing. + * For now we use this thing here becuase it is easier to implement. + */ +static int +gpg_colon_line_handler ( void *opaque, pid_t pid, int fd ) +{ + GpgObject gpg = opaque; + GpgmeError rc = 0; + + assert ( fd == gpg->colon.fd[0] ); + rc = read_colon_line ( gpg ); + if ( rc ) { + fprintf (stderr, "gpg_colon_line_handler: " + "read problem %d\n - stop", rc); + return 1; + } + + return gpg->status.eof; +} + +static GpgmeError +read_colon_line ( GpgObject gpg ) +{ + char *p; + int nread; + size_t bufsize = gpg->colon.bufsize; + char *buffer = gpg->colon.buffer; + size_t readpos = gpg->colon.readpos; + + assert (buffer); + if (bufsize - readpos < 256) { + /* need more room for the read */ + bufsize += 1024; + buffer = xtryrealloc (buffer, bufsize); + if ( !buffer ) + return mk_error (Out_Of_Core); + } + + + do { + nread = read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos ); + } while (nread == -1 && errno == EINTR); + + if (nread == -1) + return mk_error(Read_Error); + + if (!nread) { + gpg->colon.eof = 1; + assert (gpg->colon.fnc); + gpg->colon.fnc ( gpg->colon.fnc_value, NULL ); + return 0; + } + + while (nread > 0) { + for (p = buffer + readpos; nread; nread--, p++) { + if ( *p == '\n' ) { + /* (we require that the last line is terminated by a + * LF) and we skip empty lines. Note: we use UTF8 + * encoding and escaping of special characters + * We require at least one colon to cope with + * some other printed information. + */ + *p = 0; + if ( *buffer && strchr (buffer, ':') ) { + assert (gpg->colon.fnc); + gpg->colon.fnc ( gpg->colon.fnc_value, buffer ); + } + + /* To reuse the buffer for the next line we have to + * shift the remaining data to the buffer start and + * restart the loop Hmmm: We can optimize this + * function by looking forward in the buffer to see + * whether a second complete line is available and in + * this case avoid the memmove for this line. */ + nread--; p++; + if (nread) + memmove (buffer, p, nread); + readpos = 0; + break; /* the for loop */ + } + else + readpos++; + } + } + + /* Update the gpg object. */ + gpg->colon.bufsize = bufsize; + gpg->colon.buffer = buffer; + gpg->colon.readpos = readpos; + return 0; +} + diff --git a/trunk/gpgme/rungpg.h b/trunk/gpgme/rungpg.h index 5a27ce9..fd29ca4 100644 --- a/trunk/gpgme/rungpg.h +++ b/trunk/gpgme/rungpg.h @@ -82,6 +82,7 @@ typedef enum { } GpgStatusCode; typedef void (*GpgStatusHandler)( GpgmeCtx, GpgStatusCode code, char *args ); +typedef void (*GpgColonLineHandler)( GpgmeCtx, char *line ); GpgmeError _gpgme_gpg_new_object ( GpgObject *r_gpg ); void _gpgme_gpg_release_object ( GpgObject gpg ); @@ -90,6 +91,10 @@ GpgmeError _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to ); void _gpgme_gpg_set_status_handler ( GpgObject gpg, GpgStatusHandler fnc, void *fnc_value ); +GpgmeError _gpgme_gpg_set_colon_line_handler ( GpgObject gpg, + GpgColonLineHandler fnc, + void *fnc_value ); + GpgmeError _gpgme_gpg_spawn ( GpgObject gpg, void *opaque ); diff --git a/trunk/gpgme/types.h b/trunk/gpgme/types.h index 82f6b79..045fd7e 100644 --- a/trunk/gpgme/types.h +++ b/trunk/gpgme/types.h @@ -47,6 +47,9 @@ typedef struct gpg_object_s *GpgObject; struct verify_result_s; typedef struct verify_result_s *VerifyResult; +/*-- key.c --*/ +struct gpgme_key_s; +typedef struct gpgme_key_s *GpgmeKey; #endif /* TYPES_H */ diff --git a/trunk/tests/Makefile.am b/trunk/tests/Makefile.am index aced400..74709b1 100644 --- a/trunk/tests/Makefile.am +++ b/trunk/tests/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in -TESTS = t-encrypt t-verify +TESTS = t-encrypt t-verify t-keylist INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl diff --git a/trunk/tests/t-keylist.c b/trunk/tests/t-keylist.c new file mode 100644 index 0000000..43ad323 --- /dev/null +++ b/trunk/tests/t-keylist.c @@ -0,0 +1,74 @@ +/* t-keylist.c - regression test + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include + +#include "../gpgme/gpgme.h" + +#define fail_if_err(a) do { if(a) { \ + fprintf (stderr, "%s:%d: GpgmeError %s\n", \ + __FILE__, __LINE__, gpgme_strerror(a)); \ + exit (1); } \ + } while(0) + +static void +doit ( GpgmeCtx ctx, const char *pattern ) +{ + GpgmeError err; + + err = gpgme_keylist_start (ctx, pattern, 0 ); + fail_if_err (err); + gpgme_wait (ctx, 1); +} + + +int +main (int argc, char **argv ) +{ + GpgmeCtx ctx; + GpgmeError err; + int loop = 0; + const char *pattern; + + if( argc ) { + argc--; argv++; + } + + if (argc && !strcmp( *argv, "--loop" ) ) { + loop = 1; + argc--; argv++; + } + pattern = argc? *argv : NULL; + + err = gpgme_new_context (&ctx); + fail_if_err (err); + do { + doit ( ctx, pattern ); + } while ( loop ); + gpgme_release_context (ctx); + + return 0; +} + + + -- 2.26.2