Coverage Report

Created: 2026-01-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/g10/cpr.c
Line
Count
Source
1
/* status.c - Status message and command-fd interface
2
 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3
 *               2004, 2005, 2006, 2010 Free Software Foundation, Inc.
4
 *
5
 * This file is part of GnuPG.
6
 *
7
 * GnuPG is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * GnuPG is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
#include <config.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <errno.h>
26
#include <unistd.h>
27
#ifdef HAVE_SIGNAL_H
28
# include <signal.h>
29
#endif
30
31
#include "gpg.h"
32
#include "../common/util.h"
33
#include "../common/status.h"
34
#include "../common/ttyio.h"
35
#include "options.h"
36
#include "main.h"
37
#include "../common/i18n.h"
38
39
0
#define CONTROL_D ('D' - 'A' + 1)
40
41
42
/* The stream to output the status information.  Output is disabled if
43
   this is NULL.  */
44
static estream_t statusfp;
45
46
47
static void
48
progress_cb (void *ctx, const char *what, int printchar,
49
             int current, int total)
50
0
{
51
0
  char buf[50];
52
53
0
  (void)ctx;
54
55
0
  if ( printchar == '\n' && !strcmp (what, "primegen") )
56
0
    snprintf (buf, sizeof buf, "%.20s X 100 100", what );
57
0
  else
58
0
    snprintf (buf, sizeof buf, "%.20s %c %d %d",
59
0
              what, printchar=='\n'?'X':printchar, current, total );
60
0
  write_status_text (STATUS_PROGRESS, buf);
61
0
}
62
63
64
/* Return true if the status message NO may currently be issued.  We
65
   need this to avoid synchronization problem while auto retrieving a
66
   key.  There it may happen that a status NODATA is issued for a non
67
   available key and the user may falsely interpret this has a missing
68
   signature. */
69
static int
70
status_currently_allowed (int no)
71
0
{
72
0
  if (!glo_ctrl.in_auto_key_retrieve)
73
0
    return 1; /* Yes. */
74
75
  /* We allow some statis anyway, so that import statistics are
76
     correct and to avoid problems if the retrieval subsystem will
77
     prompt the user. */
78
0
  switch (no)
79
0
    {
80
0
    case STATUS_GET_BOOL:
81
0
    case STATUS_GET_LINE:
82
0
    case STATUS_GET_HIDDEN:
83
0
    case STATUS_GOT_IT:
84
0
    case STATUS_IMPORTED:
85
0
    case STATUS_IMPORT_OK:
86
0
    case STATUS_IMPORT_CHECK:
87
0
    case STATUS_IMPORT_RES:
88
0
      return 1; /* Yes. */
89
0
    default:
90
0
      break;
91
0
    }
92
0
  return 0; /* No. */
93
0
}
94
95
96
void
97
set_status_fd (int fd)
98
0
{
99
0
  static int last_fd = -1;
100
101
0
  if (fd != -1 && last_fd == fd)
102
0
    return;
103
104
0
  if (statusfp && statusfp != es_stdout && statusfp != es_stderr )
105
0
    es_fclose (statusfp);
106
0
  statusfp = NULL;
107
0
  if (fd == -1)
108
0
    return;
109
110
0
  if (! gnupg_fd_valid (fd))
111
0
    log_fatal ("status-fd is invalid: %s\n", strerror (errno));
112
113
0
  if (fd == 1)
114
0
    statusfp = es_stdout;
115
0
  else if (fd == 2)
116
0
    statusfp = es_stderr;
117
0
  else
118
0
    statusfp = es_fdopen (fd, "w");
119
0
  if (!statusfp)
120
0
    {
121
0
      log_fatal ("can't open fd %d for status output: %s\n",
122
0
                 fd, strerror (errno));
123
0
    }
124
0
  last_fd = fd;
125
126
0
  gcry_set_progress_handler (progress_cb, NULL);
127
0
}
128
129
130
int
131
is_status_enabled (void)
132
0
{
133
0
  return !!statusfp;
134
0
}
135
136
137
void
138
write_status ( int no )
139
8.40k
{
140
8.40k
    write_status_text( no, NULL );
141
8.40k
}
142
143
144
/* Write a status line with code NO followed by the string TEXT and
145
 * directly followed by the remaining strings up to a NULL.  Embedded
146
 * CR and LFs in the strings (but not in TEXT) are C-style escaped.*/
147
void
148
write_status_strings (int no, const char *text, ...)
149
748k
{
150
748k
  va_list arg_ptr;
151
748k
  const char *s;
152
153
748k
  if (!statusfp || !status_currently_allowed (no) )
154
748k
    return;  /* Not enabled or allowed. */
155
156
748k
  es_fputs ("[GNUPG:] ", statusfp);
157
0
  es_fputs (get_status_string (no), statusfp);
158
0
  if ( text )
159
0
    {
160
0
      es_putc ( ' ', statusfp);
161
0
      va_start (arg_ptr, text);
162
0
      s = text;
163
0
      do
164
0
        {
165
0
          for (; *s; s++)
166
0
            {
167
0
              if (*s == '\n')
168
0
                es_fputs ("\\n", statusfp);
169
0
              else if (*s == '\r')
170
0
                es_fputs ("\\r", statusfp);
171
0
              else
172
0
                es_fputc (*(const byte *)s, statusfp);
173
0
            }
174
0
        }
175
0
      while ((s = va_arg (arg_ptr, const char*)));
176
0
      va_end (arg_ptr);
177
0
    }
178
0
  es_putc ('\n', statusfp);
179
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
180
0
    g10_exit (0);
181
0
}
182
183
184
/* Write a status line with code NO followed by the remaining
185
 * arguments which must be a list of strings terminated by a NULL.
186
 * Embedded CR and LFs in the strings are C-style escaped.  All
187
 * strings are printed with a space as delimiter.  */
188
gpg_error_t
189
write_status_strings2 (ctrl_t dummy, int no, ...)
190
0
{
191
0
  va_list arg_ptr;
192
0
  const char *s;
193
194
0
  (void)dummy;
195
196
0
  if (!statusfp || !status_currently_allowed (no) )
197
0
    return 0;  /* Not enabled or allowed. */
198
199
0
  va_start (arg_ptr, no);
200
201
0
  es_fputs ("[GNUPG:] ", statusfp);
202
0
  es_fputs (get_status_string (no), statusfp);
203
0
  while ((s = va_arg (arg_ptr, const char*)))
204
0
    {
205
0
      if (*s)
206
0
        es_putc (' ', statusfp);
207
0
      for (; *s; s++)
208
0
        {
209
0
          if (*s == '\n')
210
0
            es_fputs ("\\n", statusfp);
211
0
          else if (*s == '\r')
212
0
            es_fputs ("\\r", statusfp);
213
0
          else
214
0
            es_fputc (*(const byte *)s, statusfp);
215
0
        }
216
0
    }
217
0
  es_putc ('\n', statusfp);
218
219
0
  va_end (arg_ptr);
220
221
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
222
0
    g10_exit (0);
223
224
0
  return 0;
225
0
}
226
227
228
void
229
write_status_text (int no, const char *text)
230
748k
{
231
748k
  write_status_strings (no, text, NULL);
232
748k
}
233
234
235
/* Write a status line with code NO followed by the output of the
236
 * printf style FORMAT.  Embedded CR and LFs are C-style escaped.  */
237
void
238
write_status_printf (int no, const char *format, ...)
239
127k
{
240
127k
  va_list arg_ptr;
241
127k
  char *buf;
242
243
127k
  if (!statusfp || !status_currently_allowed (no) )
244
127k
    return;  /* Not enabled or allowed. */
245
246
127k
  es_fputs ("[GNUPG:] ", statusfp);
247
0
  es_fputs (get_status_string (no), statusfp);
248
0
  if (format)
249
0
    {
250
0
      es_putc ( ' ', statusfp);
251
0
      va_start (arg_ptr, format);
252
0
      buf = gpgrt_vbsprintf (format, arg_ptr);
253
0
      if (!buf)
254
0
        log_error ("error printing status line: %s\n",
255
0
                   gpg_strerror (gpg_err_code_from_syserror ()));
256
0
      else
257
0
        {
258
0
          if (strpbrk (buf, "\r\n"))
259
0
            {
260
0
              const byte *s;
261
0
              for (s=buf; *s; s++)
262
0
                {
263
0
                  if (*s == '\n')
264
0
                    es_fputs ("\\n", statusfp);
265
0
                  else if (*s == '\r')
266
0
                    es_fputs ("\\r", statusfp);
267
0
                  else
268
0
                    es_fputc (*s, statusfp);
269
0
                }
270
0
            }
271
0
          else
272
0
            es_fputs (buf, statusfp);
273
0
          gpgrt_free (buf);
274
0
        }
275
276
0
      va_end (arg_ptr);
277
0
    }
278
0
  es_putc ('\n', statusfp);
279
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
280
0
    g10_exit (0);
281
0
}
282
283
/* Write a WARNING status line using a full gpg-error error value.  */
284
void
285
write_status_warning (const char *where, gpg_error_t err)
286
0
{
287
0
  if (!statusfp || !status_currently_allowed (STATUS_WARNING))
288
0
    return;  /* Not enabled or allowed. */
289
290
0
  es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
291
0
              get_status_string (STATUS_WARNING), where, err);
292
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
293
0
    g10_exit (0);
294
0
}
295
296
297
/* Write an ERROR status line using a full gpg-error error value.  */
298
void
299
write_status_error (const char *where, gpg_error_t err)
300
0
{
301
0
  if (!statusfp || !status_currently_allowed (STATUS_ERROR))
302
0
    return;  /* Not enabled or allowed. */
303
304
0
  es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
305
0
              get_status_string (STATUS_ERROR), where, err);
306
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
307
0
    g10_exit (0);
308
0
}
309
310
311
/* Same as above but outputs the error code only.  */
312
void
313
write_status_errcode (const char *where, int errcode)
314
0
{
315
0
  if (!statusfp || !status_currently_allowed (STATUS_ERROR))
316
0
    return;  /* Not enabled or allowed. */
317
318
0
  es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
319
0
              get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
320
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
321
0
    g10_exit (0);
322
0
}
323
324
325
/* Write a FAILURE status line.  */
326
void
327
write_status_failure (const char *where, gpg_error_t err)
328
0
{
329
0
  static int any_failure_printed;
330
331
0
  if (!statusfp || !status_currently_allowed (STATUS_FAILURE))
332
0
    return;  /* Not enabled or allowed. */
333
0
  if (any_failure_printed)
334
0
    return;
335
0
  any_failure_printed = 1;
336
0
  es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
337
0
              get_status_string (STATUS_FAILURE), where, err);
338
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
339
0
    g10_exit (0);
340
0
}
341
342
343
/*
344
 * Write a status line with a buffer using %XX escapes.  If WRAP is >
345
 * 0 wrap the line after this length.  If STRING is not NULL it will
346
 * be prepended to the buffer, no escaping is done for string.
347
 * A wrap of -1 forces spaces not to be encoded as %20.
348
 */
349
void
350
write_status_text_and_buffer (int no, const char *string,
351
                              const char *buffer, size_t len, int wrap)
352
10.5k
{
353
10.5k
  const char *s, *text;
354
10.5k
  int esc, first;
355
10.5k
  int lower_limit = ' ';
356
10.5k
  int escape_more;
357
10.5k
  size_t n, count, dowrap;
358
359
10.5k
  if (!statusfp || !status_currently_allowed (no))
360
10.5k
    return;  /* Not enabled or allowed. */
361
362
0
  if (wrap == -1)
363
0
    {
364
0
      lower_limit--;
365
0
      wrap = 0;
366
0
    }
367
368
0
  escape_more = (no == STATUS_NOTATION_NAME || no == STATUS_NOTATION_DATA);
369
370
0
  text = get_status_string (no);
371
0
  count = dowrap = first = 1;
372
0
  do
373
0
    {
374
0
      if (dowrap)
375
0
        {
376
0
          es_fprintf (statusfp, "[GNUPG:] %s ", text);
377
0
          count = dowrap = 0;
378
0
          if (first && string)
379
0
            {
380
0
              es_fputs (string, statusfp);
381
0
              count += strlen (string);
382
              /* Make sure that there is a space after the string.  */
383
0
              if (*string && string[strlen (string)-1] != ' ')
384
0
                {
385
0
                  es_putc (' ', statusfp);
386
0
                  count++;
387
0
                }
388
0
            }
389
0
          first = 0;
390
0
        }
391
0
      for (esc=0, s=buffer, n=len; n; s++, n--)
392
0
        {
393
0
          if (*s == '%' || *(const byte*)s <= lower_limit
394
0
              || *(const byte*)s == 127 || (escape_more && (*s & 0x80)))
395
0
            esc = 1;
396
0
          if (wrap && ++count > wrap)
397
0
            dowrap=1;
398
0
          if (esc || dowrap)
399
0
            break;
400
0
        }
401
0
      if (s != buffer)
402
0
        es_fwrite (buffer, s-buffer, 1, statusfp);
403
0
      if ( esc )
404
0
        {
405
0
          es_fprintf (statusfp, "%%%02X", *(const byte*)s );
406
0
          s++; n--;
407
0
        }
408
0
      buffer = s;
409
0
      len = n;
410
0
      if (dowrap && len)
411
0
        es_putc ('\n', statusfp);
412
0
    }
413
0
  while (len);
414
415
0
  es_putc ('\n',statusfp);
416
0
  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
417
0
    g10_exit (0);
418
0
}
419
420
421
void
422
write_status_buffer (int no, const char *buffer, size_t len, int wrap)
423
10.5k
{
424
10.5k
  write_status_text_and_buffer (no, NULL, buffer, len, wrap);
425
10.5k
}
426
427
428
/* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
429
   used to retrieve the hash algorithms used for the message. */
430
void
431
write_status_begin_signing (gcry_md_hd_t md)
432
0
{
433
0
  if (md)
434
0
    {
435
0
      char buf[100];
436
0
      size_t buflen;
437
0
      int i, ga;
438
439
0
      buflen = 0;
440
0
      for (i=1; i <= 110; i++)
441
0
        {
442
0
          ga = map_md_openpgp_to_gcry (i);
443
0
          if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf))
444
0
            {
445
0
              snprintf (buf+buflen, DIM(buf) - buflen,
446
0
                        "%sH%d", buflen? " ":"",i);
447
0
              buflen += strlen (buf+buflen);
448
0
            }
449
0
        }
450
0
      write_status_text (STATUS_BEGIN_SIGNING, buf);
451
0
    }
452
0
  else
453
0
    write_status ( STATUS_BEGIN_SIGNING );
454
0
}
455
456
457
static int
458
myread(int fd, void *buf, size_t count)
459
0
{
460
0
  int rc;
461
0
  do
462
0
    {
463
0
      rc = read( fd, buf, count );
464
0
    }
465
0
  while (rc == -1 && errno == EINTR);
466
467
0
  if (!rc && count)
468
0
    {
469
0
      static int eof_emmited=0;
470
0
      if ( eof_emmited < 3 )
471
0
        {
472
0
          *(char*)buf = CONTROL_D;
473
0
          rc = 1;
474
0
          eof_emmited++;
475
0
        }
476
0
      else /* Ctrl-D not caught - do something reasonable */
477
0
        {
478
#ifdef HAVE_DOSISH_SYSTEM
479
          raise (SIGINT); /* Nothing to hangup under DOS.  */
480
#else
481
0
          raise (SIGHUP); /* No more input data.  */
482
0
#endif
483
0
        }
484
0
    }
485
0
  return rc;
486
0
}
487
488
489
490
/* Request a string from the client over the command-fd.  If GETBOOL
491
   is set the function returns a static string (do not free) if the
492
   entered value was true or NULL if the entered value was false.  */
493
static char *
494
do_get_from_fd ( const char *keyword, int hidden, int getbool )
495
0
{
496
0
  int i, len;
497
0
  char *string;
498
499
0
  if (statusfp != es_stdout)
500
0
    es_fflush (es_stdout);
501
502
0
  write_status_text (getbool? STATUS_GET_BOOL :
503
0
                     hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
504
505
0
  for (string = NULL, i = len = 200; ; i++ )
506
0
    {
507
0
      if (i >= len-1 )
508
0
        {
509
          /* On the first iteration allocate a new buffer.  If that
510
           * buffer is too short at further iterations do a poor man's
511
           * realloc.  */
512
0
          char *save = string;
513
0
          len += 100;
514
0
          string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
515
0
          if (save)
516
0
            {
517
0
              memcpy (string, save, i);
518
0
              xfree (save);
519
0
            }
520
0
          else
521
0
            i = 0;
522
0
  }
523
      /* Fixme: why not use our read_line function here? */
524
0
      if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
525
0
        break;
526
0
      else if ( string[i] == CONTROL_D )
527
0
        {
528
          /* Found ETX - Cancel the line and return a sole ETX.  */
529
0
          string[0] = CONTROL_D;
530
0
          i = 1;
531
0
          break;
532
0
        }
533
0
    }
534
0
  string[i] = 0;
535
536
0
  write_status (STATUS_GOT_IT);
537
538
0
  if (getbool)  /* Fixme: is this correct??? */
539
0
    {
540
0
      char *rv = (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
541
0
      xfree (string);
542
0
      return rv;
543
0
    }
544
545
0
  return string;
546
0
}
547
548
549
550
int
551
cpr_enabled(void)
552
0
{
553
0
    if( opt.command_fd != -1 )
554
0
  return 1;
555
0
    return 0;
556
0
}
557
558
char *
559
cpr_get_no_help( const char *keyword, const char *prompt )
560
0
{
561
0
    char *p;
562
563
0
    if( opt.command_fd != -1 )
564
0
  return do_get_from_fd ( keyword, 0, 0 );
565
0
    for(;;) {
566
0
  p = tty_get( prompt );
567
0
        return p;
568
0
    }
569
0
}
570
571
char *
572
cpr_get( const char *keyword, const char *prompt )
573
0
{
574
0
    char *p;
575
576
0
    if( opt.command_fd != -1 )
577
0
  return do_get_from_fd ( keyword, 0, 0 );
578
0
    for(;;) {
579
0
  p = tty_get( prompt );
580
0
  if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
581
0
      xfree(p);
582
0
      display_online_help( keyword );
583
0
  }
584
0
  else
585
0
      return p;
586
0
    }
587
0
}
588
589
590
char *
591
cpr_get_utf8( const char *keyword, const char *prompt )
592
0
{
593
0
    char *p;
594
0
    p = cpr_get( keyword, prompt );
595
0
    if( p ) {
596
0
  char *utf8 = native_to_utf8( p );
597
0
  xfree( p );
598
0
  p = utf8;
599
0
    }
600
0
    return p;
601
0
}
602
603
char *
604
cpr_get_hidden( const char *keyword, const char *prompt )
605
0
{
606
0
    char *p;
607
608
0
    if( opt.command_fd != -1 )
609
0
  return do_get_from_fd ( keyword, 1, 0 );
610
0
    for(;;) {
611
0
  p = tty_get_hidden( prompt );
612
0
  if( *p == '?' && !p[1] ) {
613
0
      xfree(p);
614
0
      display_online_help( keyword );
615
0
  }
616
0
  else
617
0
      return p;
618
0
    }
619
0
}
620
621
void
622
cpr_kill_prompt(void)
623
0
{
624
0
    if( opt.command_fd != -1 )
625
0
  return;
626
0
    tty_kill_prompt();
627
0
    return;
628
0
}
629
630
int
631
cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes)
632
0
{
633
0
    int yes;
634
0
    char *p;
635
636
0
    if( opt.command_fd != -1 )
637
0
  return !!do_get_from_fd ( keyword, 0, 1 );
638
0
    for(;;) {
639
0
  p = tty_get( prompt );
640
0
  trim_spaces(p); /* it is okay to do this here */
641
0
  if( *p == '?' && !p[1] ) {
642
0
      xfree(p);
643
0
      display_online_help( keyword );
644
0
  }
645
0
  else {
646
0
      tty_kill_prompt();
647
0
      yes = answer_is_yes_no_default (p, def_yes);
648
0
      xfree(p);
649
0
      return yes;
650
0
  }
651
0
    }
652
0
}
653
654
int
655
cpr_get_answer_is_yes (const char *keyword, const char *prompt)
656
0
{
657
0
  return cpr_get_answer_is_yes_def (keyword, prompt, 0);
658
0
}
659
660
int
661
cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
662
0
{
663
0
    int yes;
664
0
    char *p;
665
666
0
    if( opt.command_fd != -1 )
667
0
  return !!do_get_from_fd ( keyword, 0, 1 );
668
0
    for(;;) {
669
0
  p = tty_get( prompt );
670
0
  trim_spaces(p); /* it is okay to do this here */
671
0
  if( *p == '?' && !p[1] ) {
672
0
      xfree(p);
673
0
      display_online_help( keyword );
674
0
  }
675
0
  else {
676
0
      tty_kill_prompt();
677
0
      yes = answer_is_yes_no_quit(p);
678
0
      xfree(p);
679
0
      return yes;
680
0
  }
681
0
    }
682
0
}
683
684
685
int
686
cpr_get_answer_okay_cancel (const char *keyword,
687
                            const char *prompt,
688
                            int def_answer)
689
0
{
690
0
  int yes;
691
0
  char *answer = NULL;
692
0
  char *p;
693
694
0
  if( opt.command_fd != -1 )
695
0
    answer = do_get_from_fd ( keyword, 0, 0 );
696
697
0
  if (answer)
698
0
    {
699
0
      yes = answer_is_okay_cancel (answer, def_answer);
700
0
      xfree (answer);
701
0
      return yes;
702
0
    }
703
704
0
  for(;;)
705
0
    {
706
0
      p = tty_get( prompt );
707
0
      trim_spaces(p); /* it is okay to do this here */
708
0
      if (*p == '?' && !p[1])
709
0
        {
710
0
          xfree(p);
711
0
          display_online_help (keyword);
712
0
  }
713
0
      else
714
0
        {
715
0
          tty_kill_prompt();
716
0
          yes = answer_is_okay_cancel (p, def_answer);
717
0
          xfree(p);
718
0
          return yes;
719
0
  }
720
0
    }
721
0
}