--- trunk/Src/wptKeyCache.cpp 2005/12/14 09:01:45 123 +++ trunk/Src/wptKeyCache.cpp 2006/01/27 10:08:10 168 @@ -1,5 +1,5 @@ /* wptKeyCache.cpp- Caching for the pub- and the secring - * Copyright (C) 2001-2005 Timo Schulz + * Copyright (C) 2001-2006 Timo Schulz * * This file is part of MyGPGME. * @@ -35,24 +35,137 @@ #include "wptNLS.h" #include "wptErrors.h" #include "wptW32API.h" +#include "wptGPG.h" -#if 0 -/* convert a binary buffer into its hex representation. */ -static void -buffer_to_string (char *dst, size_t dlen, const byte *buf, size_t nbytes) -{ - char dig[3]; - size_t i; +/* Attribute list which holds the image data. */ +struct attr_list_s { + struct attr_list_s *next; + char *fpr; /* fingerprint of the key */ + unsigned char *d; /* actual JPEG data. */ + unsigned long octets; /* length of the data. */ + unsigned int flags; /* status of the attribute. */ +}; +typedef struct attr_list_s *attr_list_t; - memset (dst, 0, dlen); - for (i = 0; i < nbytes && dlen > 0; i++) { - sprintf (dig, "%02X", buf[i]); - strcat (dst, dig); - dlen -= 2; + +void +free_attr_list (attr_list_t ctx) +{ + attr_list_t n; + while (ctx) { + n = ctx->next; + free (ctx->fpr); + free (ctx->d); + ctx = n; } } -#endif + +/* Parse the attribute list in @fp and store it into @ctx. + Return value: number of parsed items. */ +int +parse_attr_list (FILE *fp, const BYTE *data, DWORD datlen, attr_list_t *ctx) +{ + attr_list_t c, t; + char buf[512], *p, *buffer; + int pos, n=0; + + *ctx = NULL; + while (fgets (buf, 511, fp)) { + if (strstr (buf, "\r\n")) + buf[strlen (buf)-2]=0; + if (strstr (buf, "\n")) + buf[strlen (buf)-1]=0; + if (strlen (buf) < 2 || !strstr (buf, "ATTRIBUTE")) + continue; + buffer = buf+9+10; + pos = 0; + c = (attr_list_t)calloc (1, sizeof *c); + p = strtok (buffer, " "); + while (p != NULL) { + switch (pos) { + case 0: + c->fpr = strdup (p); + break; + + case 1: + c->octets = strtoul (p, NULL, 10); + break; + + case 7: + c->flags = strtoul (p, NULL, 10); + break; + + default: + break; + } + pos++; + p = strtok (NULL, " "); + } + /*printf ("id=%s octets=%d flags=%d\n", c->fpr, c->octets, c->flags);*/ + if (!*ctx) + *ctx = c; + else { + for (t = *ctx; t->next; t=t->next) + ; + t->next = c; + } + c->d = (unsigned char*)malloc (c->octets); + memcpy (c->d, data, c->octets); + data += c->octets; + datlen -= c->octets; + n++; + } + /*assert (datlen == 0); */ + return n; +} + + +/* Always use the $users temp folder. */ +static FILE* +w32_tmpfile (char *tmp, DWORD tmplen) +{ + char id[16]; + + if (!GetTempPath (tmplen-17, tmp)) + return NULL; + if (tmp[strlen (tmp)-1] != '\\') + strcat (tmp, "\\"); + _snprintf (id, sizeof (id)-1, "%lu", GetTickCount ()); + strcat (tmp, id); + return fopen (tmp, "w+b"); +} + + +static int +parse_attr_data (const char *keyid, attr_list_t *list) +{ + gpgme_error_t err; + FILE *tmp; + char *status, tmpnam[MAX_PATH+1]; + BYTE *data; + DWORD ndata; + + err = gpg_get_photoid_data (keyid, &status, &data, &ndata); + if (err) + return err; + + if (ndata > 0 && (tmp = w32_tmpfile (tmpnam, MAX_PATH)) != NULL) { + fwrite (status, 1, strlen (status), tmp); + fflush (tmp); + rewind (tmp); + + ndata = parse_attr_list (tmp, data, ndata, list); + fclose (tmp); + DeleteFile (tmpnam); + } + else + *list = NULL; + + safe_free (status); + safe_free (data); + return ndata; +} /* Parse the secret keyring and retrieve some additional information @@ -60,7 +173,7 @@ static void parse_secring (gpg_keycache_t cache, const char *kid, const char *secring) { - PACKET *pkt = (PACKET*)calloc (1, sizeof *pkt); + PACKET *pkt; PKT_secret_key *sk; gpg_iobuf_t inp; gpgme_error_t err; @@ -69,11 +182,11 @@ char keyid[16+1]; inp = gpg_iobuf_open (secring); - if (!inp) { - safe_free (pkt); + if (!inp) return; - } + gpg_iobuf_ioctl (inp, 3, 1, NULL); + pkt = (PACKET*)calloc (1, sizeof *pkt); gpg_init_packet (pkt); while (gpg_parse_packet (inp, pkt) != -1) { if (pkt->pkttype == PKT_SECRET_KEY) { @@ -102,6 +215,46 @@ } +/* Update the photo image of a single key with the fingerprint + @fpr. The @dat struct contains the new item data. */ +static gpgme_error_t +keycache_update_photo (gpg_keycache_t ctx, const char *fpr, attr_list_t dat) +{ + struct keycache_s *fnd = NULL; + gpgme_key_t key; + + gpg_keycache_find_key2 (ctx, fpr, 0, &key, &fnd); + if (!fnd) + return gpg_error (GPG_ERR_NOT_FOUND); + safe_free (fnd->attrib.d); + fnd->attrib.flags = dat->flags; + fnd->attrib.len = dat->octets; + fnd->attrib.d = (unsigned char*)malloc (dat->octets); + memcpy (fnd->attrib.d, dat->d, dat->octets); + return 0; +} + + +/* Update all photo images in the cache. */ +static gpgme_error_t +keycache_update_photos (gpg_keycache_t ctx) +{ + attr_list_t list=NULL, n; + DWORD ndata; + + ndata = parse_attr_data (NULL, &list); + if (ndata < 1) { + free_attr_list (list); + return 0; + } + + for (n=list; n; n=n->next) + keycache_update_photo (ctx, n->fpr, n); + free_attr_list (list); + return 0; +} + + /* Merge the information from the keyrings into the key cache structure. */ gpgme_error_t keycache_prepare2 (gpg_keycache_t ctx, const char *kid, @@ -110,27 +263,24 @@ gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR); gpgme_key_t key = NULL; gpg_iobuf_t inp; - PACKET *pkt = (PACKET*)calloc (1, sizeof * pkt); + PACKET *pkt; struct keycache_s *c; const byte *sym_prefs; - char keyid[16+1], *id = NULL; + char keyid[16+1]; int key_seen = 0; size_t nsym =0; if (secring) { parse_secring (ctx, kid, secring); - if (!pubring) { - safe_free (pkt); + if (!pubring) return 0; - } } inp = gpg_iobuf_open (pubring); - if (!inp) { - safe_free (pkt); + if (!inp) return gpg_error (GPG_ERR_KEYRING_OPEN); - } gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */ + pkt = (PACKET*)calloc (1, sizeof * pkt); gpg_init_packet (pkt); while (gpg_parse_packet (inp, pkt) != -1) { if (pkt->pkttype == PKT_PUBLIC_KEY) { @@ -138,10 +288,12 @@ key_seen = 1; } - if (pkt->pkttype == PKT_SIGNATURE && pkt->pkt.signature->sig_class == 0x1F) { + if (pkt->pkttype == PKT_SIGNATURE && + pkt->pkt.signature->sig_class == 0x1F) { if (pkt->pkt.signature->numrevkeys == 0) goto next; - _snprintf (keyid, sizeof keyid -1, "%08X", pkt->pkt.signature->keyid[1]); + _snprintf (keyid, sizeof (keyid) -1, "%08X", + pkt->pkt.signature->keyid[1]); if (kid && strcmp (kid, keyid) != 0) goto next; err = gpg_keycache_find_key2 (ctx, keyid, 0, &key, &c); @@ -150,11 +302,12 @@ c->gloflags.has_desig_rev = 1; } if (pkt->pkttype == PKT_SIGNATURE && key_seen == 1 ) { - sym_prefs=gpg_parse_sig_subpkt (pkt->pkt.signature->hashed, - SIGSUBPKT_PREF_SYM, &nsym); - if (sym_prefs == NULL) + sym_prefs = gpg_parse_sig_subpkt (pkt->pkt.signature->hashed, + SIGSUBPKT_PREF_SYM, &nsym); + if (!sym_prefs) goto next; - _snprintf (keyid, sizeof keyid - 1, "%08X", pkt->pkt.signature->keyid[1]); + _snprintf (keyid, sizeof (keyid) - 1, "%08X", + pkt->pkt.signature->keyid[1]); if (kid && strcmp (kid, keyid) != 0) goto next; err = gpg_keycache_find_key2 (ctx, keyid, 0, &key, &c); @@ -167,36 +320,11 @@ memcpy (c->sym_prefs, sym_prefs, nsym); } } - if (pkt->pkttype == PKT_USER_ID) { - if (id) - free (id); - id = strdup (pkt->pkt.user_id->name); - if (!id) { - err = gpg_error (GPG_ERR_ENOMEM); - goto fail; - } - } - if ((pkt->pkttype == PKT_USER_ID || pkt->pkttype == PKT_ATTRIBUTE) - && pkt->pkt.user_id->attrib_data && key) { - PKT_user_id *uid = pkt->pkt.user_id; - c->attrib.used = 1; - c->attrib.len = uid->attrib_len; - c->attrib.d = (unsigned char*)calloc (1, uid->attrib_len + 1); - if (!c->attrib.d) { - err = gpg_error (GPG_ERR_ENOMEM); - goto fail; - } - memcpy (c->attrib.d, uid->attrib_data, uid->attrib_len); - key = NULL; - c = NULL; - } next: gpg_free_packet (pkt); gpg_init_packet(pkt); } -fail: - safe_free (id); safe_free (pkt); gpg_iobuf_close (inp); return err; @@ -251,18 +379,12 @@ c2 = c->next; gpgme_key_release (c->key); c->key = NULL; - if (c->sym_prefs) - free (c->sym_prefs); - c->sym_prefs = NULL; - if (c->attrib.d) - free (c->attrib.d); - c->attrib.d = NULL; - if (c->card_type) - free (c->card_type); + safe_free (c->sym_prefs); + safe_free (c->attrib.d); + safe_free (c->card_type); free (c); } - if (ctx) - free (ctx); + safe_free (ctx); } @@ -367,7 +489,7 @@ } *r_key = NULL; return gpg_error (GPG_ERR_INTERNAL); -} /* keycache_find_key */ +} gpgme_error_t @@ -378,6 +500,47 @@ } +/* Reload a photo image of a single key with the keyid @keyid. + Return value: 0 on success. */ +static gpgme_error_t +keycache_reload_photo (gpg_keycache_t ctx, const char *keyid) +{ + attr_list_t list; + + if (parse_attr_data (keyid, &list) < 1) { + free_attr_list (list); + return 0; + } + keycache_update_photo (ctx, list->fpr, list); + free_attr_list (list); + return 0; +} + + +/* Return the next key which was updated. Before it is + returned the update flag is cleared. + @r_status is 1 for a new key and 2 for an updated key. + Return value: 0 on success. */ +gpgme_error_t +gpg_keycache_next_updated_key (gpg_keycache_t ctx, + struct keycache_s **r_obj, + int *r_status) +{ + struct keycache_s *c; + + for (c = ctx->item; c; c = c->next) { + if (c->flags != 0) { + *r_status = c->flags; + c->flags = 0; + *r_obj = c; + return 0; + } + } + return gpg_error (GPG_ERR_NOT_FOUND); +} + + + gpgme_error_t gpg_keycache_update_key (gpg_keycache_t ctx, int is_sec, void *opaque, const char *keyid) @@ -391,6 +554,7 @@ err = gpgme_new (&gctx); if (err) return err; + gpgme_set_keylist_mode (gctx, GPGME_KEYLIST_MODE_SIGS); err = gpgme_get_key (gctx, keyid, &key, is_sec); gpgme_release (gctx); if (err) @@ -400,12 +564,14 @@ log_debug ("keycache update: keyid=%s %p\r\n", keyid, pub); gpgme_key_release (fndkey); c->key = key; - c->flags = 0; + c->flags = KC_FLAG_UPD; if (is_sec && pub != NULL && !gpg_keycache_find_key (pub, keyid, 0, &fndkey)) { log_debug ("keycache update: set public part %p\r\n", fndkey); c->pubpart->key = fndkey; } + /* XXX: this is also called for keys without a photo-id. */ + keycache_reload_photo (ctx, keyid); } else { log_debug ("keycache add: sync public part\r\n"); @@ -421,6 +587,8 @@ c->gloflags.divert_to_card = c_new->gloflags.divert_to_card; } } + if (c) + c->flags = KC_FLAG_ADD; } return 0; } @@ -490,6 +658,7 @@ } if (gpgme_err_code (err) == GPG_ERR_EOF) err = gpg_error (GPG_ERR_NO_ERROR); + keycache_update_photos (ctx); /* XXX: make sure the progress dialog is closed. */ gpgme_op_keylist_end (c); gpgme_release (c); @@ -548,7 +717,8 @@ c_sec->gloflags.divert_to_card = c->gloflags.divert_to_card; if (!c->gloflags.divert_to_card) c->gloflags.divert_to_card = key_divert_to_card (key); - c->sym_prefs = copy_uid_prefs (c_sec->sym_prefs); + if (c_sec->sym_prefs) + c->sym_prefs = copy_uid_prefs (c_sec->sym_prefs); c->pubpart = c_sec; c->pubpart->key = key; } @@ -597,7 +767,11 @@ *r_key = NULL; return gpg_error (GPG_ERR_EOF); } - + if (ctx->tmp->flags != 0) + ctx->tmp->flags = 0; /* reset the 'updated' status. */ + /* it might be possible there is no public key. */ + if (flags && ctx->tmp->pubpart == NULL) + flags = 0; *r_key = flags? ctx->tmp->pubpart->key : ctx->tmp->key; *c = ctx->tmp = ctx->tmp->next; ctx->pos++; @@ -618,3 +792,47 @@ err = keycache_next_key (ctx, flags, &c, r_key); return err; } + +/* Search for a key with the pattern @pattern and mark + this key as the default signing key if found. + Return value: 0 on success. */ +gpgme_error_t +gpg_keycache_set_default_key (gpg_keycache_t ctx, + const char *pattern) +{ + gpgme_error_t err; + gpgme_key_t key; + struct keycache_s *itm; + + err = gpg_keycache_find_key2 (ctx, pattern, 0, &key, &itm); + if (err) + return err; + + if (itm) + itm->default_key = 1; + return 0; +} + +/* Return the default key from the cache. If no was + marked before, NULL is returned in @r_key. + Return value: 0 on success. */ +gpgme_error_t +gpg_keycache_get_default_key (gpg_keycache_t ctx, + gpgme_key_t *r_key) +{ + struct keycache_s *itm; + + *r_key = NULL; + for (itm = ctx->item; itm; itm = itm->next) { + if (itm->default_key) { + *r_key = itm->key; + break; + } + } + if (!*r_key) + return gpgme_error (GPG_ERR_NOT_FOUND); + return 0; +} + + +