From 8f2287ef5c05d496fcb9b012629af007fe56f897 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Thu, 3 Oct 2019 14:33:36 +0100 Subject: [PATCH] key/key-io.c: Add low-level `key_mergeline' and `key_extractline' functions. Python 3 doesn't use C `stdio' streams. In order to integrate properly, we need to provide some other way to do I/O of key material. Introduce new functions which work in terms of lines in the keyring, which a Python wrapper can transfer between us and a Python file. --- debian/catacomb2.symbols | 2 + key/key-io.c | 381 +++++++++++++++++++++++++++-------------------- key/key.h | 42 +++++- 3 files changed, 260 insertions(+), 165 deletions(-) diff --git a/debian/catacomb2.symbols b/debian/catacomb2.symbols index ceec3f2f..23ca209b 100644 --- a/debian/catacomb2.symbols +++ b/debian/catacomb2.symbols @@ -4427,7 +4427,9 @@ libcatacomb.so.2 catacomb2 #MINVER# key_close@Base 2.1.1 key_discard@Base 2.1.1 key_extract@Base 2.3.1 + key_extractline@Base 2.5.99~ key_merge@Base 2.1.1 + key_mergeline@Base 2.5.99~ ## key-misc key_byid@Base 2.1.1 diff --git a/key/key-io.c b/key/key-io.c index 5a7ce5c3..bf3bf95f 100644 --- a/key/key-io.c +++ b/key/key-io.c @@ -150,226 +150,286 @@ static time_t exptime(const char *p) return (atol(p)); } -/* --- @key_merge@ --- * +/* --- @merge_core@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @const char *file@ = name of file (for error messages) - * @FILE *fp@ = file handle to read from + * @int lno@ = line number + * @char *p@ = pointer into the line buffer (which will be + * clobbered) * @key_reporter *rep@ = error reporting function * @void *arg@ = argument for function + * @dstr *n, *v@ = scratch dynamic strings, which must be empty + * on entry and are left empty on exit * - * Returns: Error code (one of the @KERR@ constants). + * Returns: --- + * + * Use: This is the common core of @key_merge@ and @key_mergeline@. * - * Use: Reads keys from a file, and inserts them into the file. + * It is assumed that the caller has already verified that the + * keyring is writable. */ -int key_merge(key_file *f, const char *file, FILE *fp, - key_reporter *rep, void *arg) +static void merge_core(key_file *f, const char *file, int lno, char *p, + key_reporter *rep, void *arg, dstr *n, dstr *v) { - int line = 0; - dstr l = DSTR_INIT; - dstr n = DSTR_INIT, v = DSTR_INIT; + char *vf[6]; + key *k = 0; - if (!(f->f & KF_WRITE)) - return (KERR_READONLY); + /* --- Skip blank lines and comments --- * + * + * Quite what they're doing in what ought to be an automatically- + * maintained file I don't know. + */ + + while (isspace((unsigned char)*p)) + p++; + if (!*p || *p == '#') + goto skip; + + /* --- Break the line into fields --- * + * + * There are currently six fields of interest: + * + * * The key's identification (id, tag and type). + * * The actual key data itself. + * * The key expiry time. + * * The key deletion time. + * * The attributes field. + * * Any further comments. + * + * All but the last field can contain no spaces. + */ - for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) { - char *vf[6]; - char *p = l.buf; - key *k; - - /* --- Skip blank lines and comments --- * - * - * Quite what they're doing in what ought to be an automatically- - * maintained file I don't know. - */ - - line++; - while (isspace((unsigned char)*p)) - p++; - if (!*p || *p == '#') - continue; - - /* --- Break the line into fields --- * - * - * There are currently six fields of interest: - * - * * The key's identification (id, tag and type). - * * The actual key data itself. - * * The key expiry time. - * * The key deletion time. - * * The attributes field. - * * Any further comments. - * - * All but the last field can contain no spaces. - */ - - { - int n = str_split(p, vf, 5, &vf[5]); - if (n < 4) { - if (rep) - rep(file, line, "too few fields", arg); - goto skip_0; - } + { + int n = str_split(p, vf, 5, &vf[5]); + if (n < 4) { + if (rep) + rep(file, lno, "too few fields", arg); + goto skip; } + } - /* --- Allocate a new key block --- */ + /* --- Allocate a new key block --- */ - k = CREATE(key); + k = CREATE(key); + k->k = 0; + k->tag = 0; + k->type = 0; - /* --- Extract the key data into the block --- */ + /* --- Extract the key data into the block --- */ - if ((k->k = key_read(vf[1], 0)) == 0) { - if (rep) - rep(file, line, "bad key data", arg); - goto skip_1; - } + if ((k->k = key_read(vf[1], 0)) == 0) { + if (rep) + rep(file, lno, "bad key data", arg); + goto skip; + } - /* --- Decode the identification field --- * - * - * For compatibility, derive a keyid from the key data. This can only be - * done if the key encoding is binary (and presumably old-encoding binary - * at that). - */ - - { - char *q = strchr(vf[0], ':'); - char *qq; - - if (!q) { - if (k->k->e != KENC_BINARY) { - if (rep) - rep(file, line, "new-style key encoding but no keyid", arg); - goto skip_2; - } - k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz); - k->type = xstrdup(vf[0]); + /* --- Decode the identification field --- * + * + * For compatibility, derive a keyid from the key data. This can only be + * done if the key encoding is binary (and presumably old-encoding binary + * at that). + */ + + { + char *q = strchr(vf[0], ':'); + char *qq; + + if (!q) { + if (k->k->e != KENC_BINARY) { + if (rep) + rep(file, lno, "new-style key encoding but no keyid", arg); + goto skip; + } + k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz); + k->type = xstrdup(vf[0]); + k->tag = 0; + } else { + *q++ = 0; + k->id = strtoul(p, 0, 16); + if ((qq = strchr(q, ':')) == 0 || !qq[1]) { + if (qq) + *qq = 0; k->tag = 0; } else { - *q++ = 0; - k->id = strtoul(p, 0, 16); - if ((qq = strchr(q, ':')) == 0 || !qq[1]) { - if (qq) - *qq = 0; - k->tag = 0; - } else { - *qq++ = 0; - k->tag = xstrdup(qq); - } - k->type = xstrdup(q); + *qq++ = 0; + k->tag = xstrdup(qq); } + k->type = xstrdup(q); } + } - /* --- Get a key block for the new key --- */ + /* --- Get a key block for the new key --- */ - k->exp = exptime(vf[2]); - k->del = exptime(vf[3]); + k->exp = exptime(vf[2]); + k->del = exptime(vf[3]); - /* --- Insert the key block into the table --- */ + /* --- Insert the key block into the table --- */ - { - int err; + { + int err; - again: - if ((err = insert(f, k)) < 0) { - if (err == KERR_DUPTAG) { - if (rep) - rep(file, line, "duplicate key tag stripped", arg); - xfree(k->tag); - k->tag = 0; - goto again; - } + again: + if ((err = insert(f, k)) < 0) { + if (err == KERR_DUPTAG) { if (rep) - rep(file, line, key_strerror(err), arg); - goto skip_3; + rep(file, lno, "duplicate key tag stripped", arg); + xfree(k->tag); + k->tag = 0; + goto again; } + if (rep) + rep(file, lno, key_strerror(err), arg); + goto skip; } + } - /* --- Parse up the attributes, if specified --- */ + /* --- Parse up the attributes, if specified --- */ - sym_create(&k->a); - if (vf[4] && strcmp(vf[4], "-") != 0) { - url_dctx uc; - for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) { - key_putattr(f, k, n.buf, v.buf); - DRESET(&n); DRESET(&v); - } + sym_create(&k->a); + if (vf[4] && strcmp(vf[4], "-") != 0) { + url_dctx uc; + for (url_initdec(&uc, vf[4]); url_dec(&uc, n, v); ) { + key_putattr(f, k, n->buf, v->buf); + DRESET(n); DRESET(v); } + } - /* --- Insert the comment --- */ + /* --- Insert the comment --- */ - if (vf[5]) - k->c = xstrdup(vf[5]); - else - k->c = 0; - continue; + if (vf[5]) + k->c = xstrdup(vf[5]); + else + k->c = 0; - /* --- Tidy up after something going wrong --- */ + /* --- Done --- */ - skip_3: - if (k->tag) - xfree(k->tag); - xfree(k->type); - skip_2: - key_drop(k->k); - skip_1: + f->f |= KF_MODIFIED; + return; + + /* --- Tidy up after something going wrong --- */ + +skip: + if (k) { + if (k->tag) xfree(k->tag); + if (k->type) xfree(k->type); + if (k->k) key_drop(k->k); DESTROY(k); - skip_0:; } +} + +/* --- @key_merge@, @key_mergeline@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @const char *file@ = name of file (for error messages) + * @int lno@ = line number (for error messages, @key_mergeline@) + * @FILE *fp@ = file handle to read from (@key_merge@) + * @const char *line@ = line from the input (@key_mergeline@) + * @key_reporter *rep@ = error reporting function + * @void *arg@ = argument for function + * + * Returns: Error code (one of the @KERR@ constants). + * + * Use: The @key_merge@ function reads keys from a file, and inserts + * them into the keyring. + * + * The @key_mergeline@ function reads a key from a single input + * line (which may, but need not, have a final newline), and + * adds it to the keyring. + * + * The @key_mergeline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. + */ - /* --- Extensive tidying up now required --- */ +int key_merge(key_file *f, const char *file, FILE *fp, + key_reporter *rep, void *arg) +{ + dstr n = DSTR_INIT, v = DSTR_INIT; + dstr l = DSTR_INIT; + int lno = 1; + + if (!(f->f & KF_WRITE)) + return (KERR_READONLY); + + for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) + merge_core(f, file, lno++, l.buf, rep, arg, &n, &v); dstr_destroy(&l); - dstr_destroy(&n); - dstr_destroy(&v); - f->f |= KF_MODIFIED; + dstr_destroy(&n); dstr_destroy(&v); return (0); } -/* --- @key_extract@ --- * +int key_mergeline(key_file *f, const char *file, int lno, const char *line, + key_reporter *rep, void *arg) +{ + dstr n = DSTR_INIT, v = DSTR_INIT; + size_t len = strlen(line); + char *p; + + if (!(f->f & KF_WRITE)) return (KERR_READONLY); + + if (len && line[len - 1] == '\n') len--; + p = xmalloc(len); memcpy(p, line, len); p[len] = 0; + merge_core(f, file, lno, p, rep, arg, &n, &v); + xfree(p); dstr_destroy(&n); dstr_destroy(&v); + return (0); +} + +/* --- @key_extract@, @key_extractline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @key *k@ = key to extract - * @FILE *fp@ = file to write on + * @FILE *fp@ = file to write on (@key_extract@) + * @dstr *d@ = string to write on (@key_extractline@) * @const key_filter *kf@ = pointer to key selection block * - * Returns: Zero if OK, EOF on error. + * Returns: @key_extract@ returns zero if OK, EOF on error. + * @key_extractline@ does not return a value. + * + * Use: Extracts a key to an ouptut file or buffer. + * + * The @key_extractline@ includes a final newline in its output. * - * Use: Extracts a key to an ouptut file. + * The @key_extractline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ -int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) +void key_extractline(key_file *f, key *k, dstr *d, const key_filter *kf) { - dstr d = DSTR_INIT; time_t t = time(0); /* --- Skip the key if it's deleted or unselected--- */ if (KEY_EXPIRED(t, k->del) || !key_match(k->k, kf)) - return (0); + return; /* --- Encode the key and write the easy stuff --- */ - key_fulltag(k, &d); - DPUTC(&d, ' '); - if (!key_write(k->k, &d, kf)) dstr_puts(&d, "struct:[]"); - DPUTC(&d, ' '); - dstr_write(&d, fp); - DRESET(&d); + key_fulltag(k, d); + DPUTC(d, ' '); + if (!key_write(k->k, d, kf)) dstr_puts(d, "struct:[]"); + DPUTC(d, ' '); /* --- Write out the expiry and deletion times --- */ if (KEY_EXPIRED(t, k->exp)) - fputs("expired ", fp); + dstr_puts(d, "expired "); else if (k->exp == KEXP_FOREVER) - fputs("forever ", fp); + dstr_puts(d, "forever "); else - fprintf(fp, "%li ", (long)k->exp); + dstr_putf(d, "%li ", (long)k->exp); if (k->del == KEXP_FOREVER) - fputs("forever ", fp); + dstr_puts(d, "forever "); else - fprintf(fp, "%li ", (long)k->del); + dstr_putf(d, "%li ", (long)k->del); /* --- Output the attributes --- */ @@ -382,19 +442,24 @@ int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) url_initenc(&uc); for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) { none = 0; - url_enc(&uc, &d, SYM_NAME(a), a->p); + url_enc(&uc, d, SYM_NAME(a), a->p); } if (none) - DPUTS(&d, "-"); - DWRITE(&d, fp); + DPUTC(d, '-'); } - dstr_destroy(&d); - if (k->c) { - putc(' ', fp); - fputs(k->c, fp); - } - putc('\n', fp); + if (k->c) + dstr_putf(d, " %s", k->c); + + DPUTC(d, '\n'); DPUTZ(d); +} + +int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) +{ + dstr d = DSTR_INIT; + + key_extractline(f, k, &d, kf); + dstr_write(&d, fp); return (ferror(fp) ? EOF : 0); } diff --git a/key/key.h b/key/key.h index bed36325..96a2b5a2 100644 --- a/key/key.h +++ b/key/key.h @@ -177,36 +177,64 @@ typedef void key_reporter(const char */*file*/, int /*line*/, /*----- Reading and writing keys and files --------------------------------*/ -/* --- @key_merge@ --- * +/* --- @key_merge@, @key_mergeline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @const char *file@ = name of file (for error messages) - * @FILE *fp@ = file handle to read from + * @int lno@ = line number (for error messages, @key_mergeline@) + * @FILE *fp@ = file handle to read from (@key_merge@) + * @const char *line@ = line from the input (@key_mergeline@) * @key_reporter *rep@ = error reporting function * @void *arg@ = argument for function * * Returns: Error code (one of the @KERR@ constants). * - * Use: Reads keys from a file, and inserts them into the file. + * Use: The @key_merge@ function reads keys from a file, and inserts + * them into the keyring. + * + * The @key_mergeline@ function reads a key from a single input + * line (which may, but need not, have a final newline), and + * adds it to the keyring. + * + * The @key_mergeline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ extern int key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/, key_reporter */*rep*/, void */*arg*/); +extern int key_mergeline(key_file */*f*/, const char */*file*/, int /*lno*/, + const char */*line*/, + key_reporter */*rep*/, void */*arg*/); -/* --- @key_extract@ --- * +/* --- @key_extract@, @key_extractline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @key *k@ = key to extract - * @FILE *fp@ = file to write on + * @FILE *fp@ = file to write on (@key_extract@) + * @dstr *d@ = string to write on (@key_extractline@) * @const key_filter *kf@ = pointer to key selection block * - * Returns: Zero if OK, EOF on error. + * Returns: @key_extract@ returns zero if OK, EOF on error. + * @key_extractline@ does not return a value. + * + * Use: Extracts a key to an ouptut file or buffer. + * + * The @key_extractline@ includes a final newline in its output. * - * Use: Extracts a key to an ouptut file. + * The @key_extractline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/, const key_filter */*kf*/); +extern void key_extractline(key_file */*f*/, key */*k*/, + dstr */*d*/, const key_filter */*kf*/); /* --- @key_open@ --- * * -- 2.11.0