+int
+repo_add_keyring(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ unsigned char *buf, *p, *pbuf;
+ int bufl, l, pl, pbufl;
+
+ data = repo_add_repodata(repo, flags);
+ buf = (unsigned char *)solv_slurp(fp, &bufl);
+ if (buf && !is_pubkey_packet(buf, bufl))
+ {
+ /* assume ascii armored */
+ unsigned char *nbuf = 0, *ubuf;
+ int nl, ubufl;
+ bufl = 0;
+ for (l = 0; (nl = unarmor((char *)buf + l, &ubuf, &ubufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----")) != 0; l += nl)
+ {
+ /* found another block. concat. */
+ nbuf = solv_realloc(nbuf, bufl + ubufl);
+ if (ubufl)
+ memcpy(nbuf + bufl, ubuf, ubufl);
+ bufl += ubufl;
+ solv_free(ubuf);
+ }
+ solv_free(buf);
+ buf = nbuf;
+ }
+ /* now split into pubkey parts, ignoring the packets we don't know */
+ pbuf = 0;
+ pbufl = 0;
+ for (p = buf; bufl; p += pl, bufl -= pl)
+ {
+ int tag;
+ int hl = parsepkgheader(p, bufl, &tag, &pl);
+ if (!hl)
+ break;
+ pl += hl;
+ if (tag == 6)
+ {
+ /* found new pubkey! flush old */
+ if (pbufl)
+ {
+ add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+ pbuf = solv_free(pbuf);
+ pbufl = 0;
+ }
+ }
+ if (tag != 6 && !pbufl)
+ continue;
+ if (tag != 6 && tag != 2 && tag != 13 && tag != 14 && tag != 17)
+ continue;
+ /* we want that packet. concat. */
+ pbuf = solv_realloc(pbuf, pbufl + pl);
+ memcpy(pbuf + pbufl, p, pl);
+ pbufl += pl;
+ }
+ if (pbufl)
+ add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+ solv_free(pbuf);
+ solv_free(buf);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+int
+repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i, nent, sl;
+ struct dirent **namelist;
+ char *rkeydir;
+
+ data = repo_add_repodata(repo, flags);
+ nent = scandir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keydir) : keydir, &namelist, 0, alphasort);
+ if (nent == -1)
+ return pool_error(pool, -1, "%s: %s", keydir, strerror(errno));
+ rkeydir = pool_prepend_rootdir(pool, keydir);
+ sl = suffix ? strlen(suffix) : 0;
+ for (i = 0; i < nent; i++)
+ {
+ const char *dn = namelist[i]->d_name;
+ int l;
+ if (*dn == '.' && !(flags & ADD_KEYDIR_WITH_DOTFILES))
+ continue;
+ l = strlen(dn);
+ if (sl && (l < sl || strcmp(dn + l - sl, suffix) != 0))
+ continue;
+ repo_add_pubkey(repo, pool_tmpjoin(pool, rkeydir, "/", dn), flags | REPO_REUSE_REPODATA);
+ }
+ solv_free(rkeydir);
+ for (i = 0; i < nent; i++)
+ solv_free(namelist[i]);
+ solv_free(namelist);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+Solvsig *
+solvsig_create(FILE *fp)
+{
+ Solvsig *ss;
+ unsigned char *sig;
+ int sigl, hl, tag, pktl;
+ struct pgpsig pgpsig;
+
+ if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
+ return 0;
+ if (!is_sig_packet(sig, sigl))
+ {
+ /* not a raw sig, check armored */
+ unsigned char *nsig;
+ if (!unarmor((char *)sig, &nsig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----"))
+ {
+ solv_free(sig);
+ return 0;
+ }
+ solv_free(sig);
+ sig = nsig;
+ if (!is_sig_packet(sig, sigl))
+ {
+ solv_free(sig);
+ return 0;
+ }
+ }
+ hl = parsepkgheader(sig, sigl, &tag, &pktl);
+ if (!hl || tag != 2 || !pktl)
+ {
+ solv_free(sig);
+ return 0;
+ }
+ pgpsig_init(&pgpsig, sig + hl, pktl);
+ if (pgpsig.type != 0 || !pgpsig.haveissuer)
+ {
+ solv_free(sig);
+ return 0;
+ }
+ ss = solv_calloc(1, sizeof(*ss));
+ ss->sigpkt = solv_memdup(sig + hl, pktl);
+ ss->sigpktl = pktl;
+ solv_free(sig);
+ solv_bin2hex(pgpsig.issuer, 8, ss->keyid);
+ ss->htype = pgphashalgo2type(pgpsig.hashalgo);
+ ss->created = pgpsig.created;
+ ss->expires = pgpsig.expires;
+ return ss;
+}
+
+void
+solvsig_free(Solvsig *ss)
+{
+ solv_free(ss->sigpkt);
+ solv_free(ss);
+}
+
+static int
+repo_find_all_pubkeys_cmp(const void *va, const void *vb, void *dp)
+{
+ Pool *pool = dp;
+ Id a = *(Id *)va;
+ Id b = *(Id *)vb;
+ /* cannot use evrcmp, as rpm says '0' > 'a' */
+ return strcmp(pool_id2str(pool, pool->solvables[b].evr), pool_id2str(pool, pool->solvables[a].evr));
+}
+
+void
+repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q)
+{
+ Id p;
+ Solvable *s;
+
+ queue_empty(q);
+ if (!keyid)
+ return;
+ queue_init(q);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *kidstr, *evr = pool_id2str(s->repo->pool, s->evr);
+
+ if (!evr || strncmp(evr, keyid + 8, 8) != 0)
+ continue;
+ kidstr = solvable_lookup_str(s, PUBKEY_KEYID);
+ if (kidstr && !strcmp(kidstr, keyid))
+ queue_push(q, p);
+ }
+ if (q->count > 1)
+ solv_sort(q->elements, q->count, sizeof(Id), repo_find_all_pubkeys_cmp, repo->pool);
+}
+
+Id
+repo_find_pubkey(Repo *repo, const char *keyid)
+{
+ Queue q;
+ Id p;
+ queue_init(&q);
+ repo_find_all_pubkeys(repo, keyid, &q);
+ p = q.count ? q.elements[0] : 0;
+ queue_free(&q);
+ return p;
+}
+
+#ifdef ENABLE_PGPVRFY
+
+/* warning: does not check key expiry/revokation, same as with gpgv or rpm */
+/* returns the Id of the pubkey that verified the signature */
+Id
+repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid)
+{
+ Id p;
+ Queue q;
+ int i;
+
+ if (!sigdata || !keyid)
+ return 0;
+ queue_init(&q);
+ repo_find_all_pubkeys(repo, keyid, &q);
+ for (i = 0; i < q.count; i++)
+ {
+ int pubdatal;
+ const unsigned char *pubdata = repo_lookup_binary(repo, q.elements[i], PUBKEY_DATA, &pubdatal);
+ if (pubdata && solv_pgpvrfy(pubdata, pubdatal, sigdata, sigdatal))
+ break;
+ }
+ p = i < q.count? q.elements[i] : 0;
+ queue_free(&q);
+ return p;
+}
+
+Id
+solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk)
+{
+ struct pgpsig pgpsig;
+ void *chk2;
+ Id p;
+
+ if (!chk || solv_chksum_isfinished(chk))
+ return 0;
+ pgpsig_init(&pgpsig, ss->sigpkt, ss->sigpktl);
+ chk2 = solv_chksum_create_clone(chk);
+ pgpsig_makesigdata(&pgpsig, ss->sigpkt, ss->sigpktl, 0, 0, 0, 0, chk2);
+ solv_chksum_free(chk2, 0);
+ if (!pgpsig.sigdata)
+ return 0;
+ p = repo_verify_sigdata(repo, pgpsig.sigdata, pgpsig.sigdatal, ss->keyid);
+ solv_free(pgpsig.sigdata);
+ return p;
+}
+
+#endif
+