From ce0d3238f07e83f2fc08c193cd57b6bc57a83aa9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 15 Jun 2023 10:37:07 +0200 Subject: [PATCH] gpgsm: Print PROGRESS status lines. * common/ksba-io-support.c (struct writer_cb_parm_s): Add field progress. (struct gnupg_ksba_io_s): Add field is_writer. (update_write_progress): New. (base64_writer_cb, plain_writer_cb): Call update_write_progress. (base64_finish_write): Ditto. (gnupg_ksba_create_writer): Set is_writer. (gnupg_ksba_set_progress_cb): New. (gnupg_ksba_set_total): New. * common/ksba-io-support.h (gnupg_ksba_progress_cb_t): New type. * sm/server.c (gpgsm_status2): Return error from statusfp writes. (gpgsm_progress_cb): New. * sm/decrypt.c (gpgsm_decrypt): Set progress handler. * sm/encrypt.c (gpgsm_encrypt): Ditto. * sm/sign.c (gpgsm_sign): Ditto. * sm/verify.c (gpgsm_verify): Ditto. -- GnuPG-bug-id: 6534 Backported-from: c58067415fe93fbd5d3de2594ccca4761ad25103 Backported-from: a88aeee12990478c218abff7f38728e47ee824bc --- NEWS | 2 + common/ksba-io-support.c | 98 +++++++++++++++++++++++++++++++++++++--- common/ksba-io-support.h | 10 +++- sm/decrypt.c | 2 + sm/encrypt.c | 4 +- sm/gpgsm.h | 1 + sm/server.c | 48 +++++++++++++++++++- sm/sign.c | 4 +- sm/verify.c | 2 + 9 files changed, 161 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index e12e295e9..c13006e9c 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,8 @@ Noteworthy changes in version 2.2.42 (unreleased) * gpgsm: Support ECC certificates. [T6253] + * gpg: Make progress work for large files on Windows. [T6534] + * gpgsm: Also announce AES256-CBC in signatures. [rGaa397fdcdb21] Release-info: https://dev.gnupg.org/T6307 diff --git a/common/ksba-io-support.c b/common/ksba-io-support.c index 2832a4f3d..a23608164 100644 --- a/common/ksba-io-support.c +++ b/common/ksba-io-support.c @@ -1,6 +1,6 @@ /* kska-io-support.c - Supporting functions for ksba reader and writer * Copyright (C) 2001-2005, 2007, 2010-2011, 2017 Werner Koch - * Copyright (C) 2006 g10 Code GmbH + * Copyright (C) 2006, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -26,6 +26,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #include @@ -89,6 +90,15 @@ struct writer_cb_parm_s char *pem_name; /* Malloced. */ + struct { + gnupg_ksba_progress_cb_t cb; + ctrl_t ctrl; + u32 last_time; /* last time reported */ + uint64_t last; /* last amount reported */ + uint64_t current; /* current amount */ + uint64_t total; /* total amount */ + } progress; + int wrote_begin; int did_finish; @@ -103,6 +113,7 @@ struct writer_cb_parm_s /* Context for this module's functions. */ struct gnupg_ksba_io_s { + int is_writer; /* True if this context refers a writer object. */ union { struct reader_cb_parm_s rparm; struct writer_cb_parm_s wparm; @@ -424,6 +435,33 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) +/* Call the progress callback if its time. We do this very 2 seconds + * or if FORCE is set. However, we also require that at least 64KiB + * have been written to avoid unnecessary progress lines for small + * files. */ +static gpg_error_t +update_write_progress (struct writer_cb_parm_s *parm, size_t count, int force) +{ + gpg_error_t err = 0; + u32 timestamp; + + parm->progress.current += count; + if (parm->progress.current >= (64*1024)) + { + timestamp = make_timestamp (); + if (force || (timestamp - parm->progress.last_time > 1)) + { + parm->progress.last = parm->progress.current; + parm->progress.last_time = timestamp; + err = parm->progress.cb (parm->progress.ctrl, + parm->progress.current, + parm->progress.total); + } + } + return err; +} + + static int base64_writer_cb (void *cb_value, const void *buffer, size_t count) { @@ -432,6 +470,8 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) int i, c, idx, quad_count; const unsigned char *p; estream_t stream = parm->stream; + int rc; + size_t nleft; if (!count) return 0; @@ -454,7 +494,7 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) for (i=0; i < idx; i++) radbuf[i] = parm->base64.radbuf[i]; - for (p=buffer; count; p++, count--) + for (p=buffer, nleft = count; nleft; p++, nleft--) { radbuf[idx++] = *p; if (idx > 2) @@ -480,7 +520,11 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) parm->base64.idx = idx; parm->base64.quad_count = quad_count; - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + /* Note that we use the unencoded count for the progress. */ + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, count, 0); + return rc; } @@ -491,13 +535,16 @@ plain_writer_cb (void *cb_value, const void *buffer, size_t count) { struct writer_cb_parm_s *parm = cb_value; estream_t stream = parm->stream; + int rc; if (!count) return 0; es_write (stream, buffer, count, NULL); - - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, count, 0); + return rc; } @@ -507,6 +554,7 @@ base64_finish_write (struct writer_cb_parm_s *parm) unsigned char *radbuf; int c, idx, quad_count; estream_t stream = parm->stream; + int rc; if (!parm->wrote_begin) return 0; /* Nothing written or we are not called in base-64 mode. */ @@ -553,7 +601,10 @@ base64_finish_write (struct writer_cb_parm_s *parm) es_fputs ("-----\n", stream); } - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, 0, 1); + return rc; } @@ -683,6 +734,7 @@ gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, unsigned int flags, *ctx = xtrycalloc (1, sizeof **ctx); if (!*ctx) return gpg_error_from_syserror (); + (*ctx)->is_writer = 1; rc = ksba_writer_new (&w); if (rc) @@ -760,3 +812,37 @@ gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx) xfree (ctx->u.wparm.pem_name); xfree (ctx); } + + +/* Set a callback to the writer object. CTRL will be bassed to the + * callback. */ +void +gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx, + gnupg_ksba_progress_cb_t cb, ctrl_t ctrl) +{ + struct writer_cb_parm_s *parm; + + if (!ctx || !ctx->is_writer) + return; /* Currently only supported for writer objects. */ + parm = &ctx->u.wparm; + + parm->progress.cb = cb; + parm->progress.ctrl = ctrl; + parm->progress.last_time = 0; + parm->progress.last = 0; + parm->progress.current = 0; + parm->progress.total = 0; +} + + +/* Update the total count for the progress thingy. */ +void +gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total) +{ + struct writer_cb_parm_s *parm; + + if (!ctx || !ctx->is_writer) + return; /* Currently only supported for writer objects. */ + parm = &ctx->u.wparm; + parm->progress.total = total; +} diff --git a/common/ksba-io-support.h b/common/ksba-io-support.h index e33e0ed74..f309e6647 100644 --- a/common/ksba-io-support.h +++ b/common/ksba-io-support.h @@ -25,6 +25,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #ifndef GNUPG_KSBA_IO_SUPPORT_H @@ -41,6 +42,10 @@ /* Context object. */ typedef struct gnupg_ksba_io_s *gnupg_ksba_io_t; +/* Progress callback type. */ +typedef gpg_error_t (*gnupg_ksba_progress_cb_t)(ctrl_t ctrl, + uint64_t current, + uint64_t total); gpg_error_t gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, @@ -56,10 +61,13 @@ gpg_error_t gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, const char *pem_name, estream_t stream, ksba_writer_t *r_writer); - gpg_error_t gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx); void gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx); +void gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx, + gnupg_ksba_progress_cb_t cb, ctrl_t ctrl); +void gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total); + diff --git a/sm/decrypt.c b/sm/decrypt.c index 01260f599..9f8175c75 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1116,6 +1116,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + rc = ksba_cms_new (&cms); if (rc) goto leave; diff --git a/sm/encrypt.c b/sm/encrypt.c index fb36977af..ed5cd2f90 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -672,6 +672,8 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + err = ksba_cms_new (&cms); if (err) { @@ -847,7 +849,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) err = ksba_cms_build (cms, &stopreason); if (err) { - log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); + log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index e1be5ca55..5790a64f4 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -270,6 +270,7 @@ gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); +gpg_error_t gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); diff --git a/sm/server.c b/sm/server.c index a0cd652e0..dc1bce246 100644 --- a/sm/server.c +++ b/sm/server.c @@ -1450,7 +1450,14 @@ gpgsm_status2 (ctrl_t ctrl, int no, ...) } } putc ('\n', statusfp); - fflush (statusfp); + if (ferror (statusfp)) + err = gpg_error_from_syserror (); + else + { + fflush (statusfp); + if (ferror (statusfp)) + err = gpg_error_from_syserror (); + } } else { @@ -1495,6 +1502,45 @@ gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, } +/* This callback is used to emit progress status lines. */ +gpg_error_t +gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total) +{ + char buffer[60]; + char units[] = "BKMGTPEZY?"; + int unitidx = 0; + + if (total) + { + if (current > total) + current = total; + + while (total > 1024*1024) + { + total /= 1024; + current /= 1024; + unitidx++; + } + } + else + { + while (current > 1024*1024) + { + current /= 1024; + unitidx++; + } + } + + if (unitidx > 9) + unitidx = 9; + + snprintf (buffer, sizeof buffer, "? %lu %lu %c%s", + (unsigned long)current, (unsigned long)total, + units[unitidx], unitidx? "iB" : ""); + return gpgsm_status2 (ctrl, STATUS_PROGRESS, "?", buffer, NULL); +} + + /* Helper to notify the client about Pinentry events. Because that might disturb some older clients, this is only done when enabled via an option. Returns an gpg error code. */ diff --git a/sm/sign.c b/sm/sign.c index db8ef0226..5b442b9a7 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -361,6 +361,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + err = ksba_cms_new (&cms); if (err) { @@ -684,7 +686,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, err = ksba_cms_build (cms, &stopreason); if (err) { - log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); + log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } diff --git a/sm/verify.c b/sm/verify.c index 0e94463f3..8fc073c2f 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -153,6 +153,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + rc = ksba_cms_new (&cms); if (rc) goto leave;