Coverage Report

Created: 2026-01-09 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/g10/photoid.c
Line
Count
Source
1
/* photoid.c - photo ID handling code
2
 * Copyright (C) 2001, 2002, 2005, 2006, 2008, 2011 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * GnuPG is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GnuPG is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include <config.h>
21
#include <errno.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <unistd.h>
25
#ifdef _WIN32
26
# ifdef HAVE_WINSOCK2_H
27
#  include <winsock2.h>
28
# endif
29
# include <windows.h>
30
#endif
31
32
#include "gpg.h"
33
#include "../common/util.h"
34
#include "packet.h"
35
#include "../common/status.h"
36
#include "keydb.h"
37
#include "../common/i18n.h"
38
#include "../common/iobuf.h"
39
#include "options.h"
40
#include "main.h"
41
#include "photoid.h"
42
#include "../common/ttyio.h"
43
#include "trustdb.h"
44
45
#if defined (_WIN32)
46
/* This is a nicer system() for windows that waits for programs to
47
   return before returning control to the caller.  I hate helpful
48
   computers. */
49
static int
50
w32_system (const char *command)
51
{
52
  if (!strncmp (command, "!ShellExecute ", 14))
53
    {
54
      SHELLEXECUTEINFOW see;
55
      wchar_t *wname;
56
      int waitms;
57
58
      command = command + 14;
59
      while (spacep (command))
60
        command++;
61
      waitms = atoi (command);
62
      if (waitms < 0)
63
        waitms = 0;
64
      else if (waitms > 60*1000)
65
        waitms = 60000;
66
      while (*command && !spacep (command))
67
        command++;
68
      while (spacep (command))
69
        command++;
70
71
      wname = utf8_to_wchar (command);
72
      if (!wname)
73
        return -1;
74
75
      memset (&see, 0, sizeof see);
76
      see.cbSize = sizeof see;
77
      see.fMask = (SEE_MASK_NOCLOSEPROCESS
78
                   | SEE_MASK_NOASYNC
79
                   | SEE_MASK_FLAG_NO_UI
80
                   | SEE_MASK_NO_CONSOLE);
81
      see.lpVerb = L"open";
82
      see.lpFile = (LPCWSTR)wname;
83
      see.nShow = SW_SHOW;
84
85
      if (DBG_EXTPROG)
86
        log_debug ("running ShellExecuteEx(open,'%s')\n", command);
87
      if (!ShellExecuteExW (&see))
88
        {
89
          if (DBG_EXTPROG)
90
            log_debug ("ShellExecuteEx failed: rc=%d\n", (int)GetLastError ());
91
          xfree (wname);
92
          return -1;
93
        }
94
      if (DBG_EXTPROG)
95
        {
96
          /* hInstApp has HINSTANCE type.  The documentations says
97
             that it's not a true HINSTANCE and it can be cast only to
98
             an int.  */
99
          int hinstance = (intptr_t)see.hInstApp;
100
101
          log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n",
102
                     see.hProcess, hinstance);
103
        }
104
105
      if (!see.hProcess)
106
        {
107
          gnupg_usleep (waitms*1000);
108
          if (DBG_EXTPROG)
109
            log_debug ("ShellExecuteEx ready (wait=%dms)\n", waitms);
110
        }
111
      else
112
        {
113
          WaitForSingleObject (see.hProcess, INFINITE);
114
          if (DBG_EXTPROG)
115
            log_debug ("ShellExecuteEx ready\n");
116
        }
117
      CloseHandle (see.hProcess);
118
119
      xfree (wname);
120
    }
121
  else
122
    {
123
      char *string;
124
      wchar_t *wstring;
125
      PROCESS_INFORMATION pi;
126
      STARTUPINFOW si;
127
128
      /* We must use a copy of the command as CreateProcess modifies
129
       * this argument. */
130
      string = xstrdup (command);
131
      wstring = utf8_to_wchar (string);
132
      xfree (string);
133
      if (!wstring)
134
        return -1;
135
136
      memset (&pi, 0, sizeof(pi));
137
      memset (&si, 0, sizeof(si));
138
      si.cb = sizeof (si);
139
140
      if (!CreateProcessW (NULL, wstring, NULL, NULL, FALSE,
141
                           DETACHED_PROCESS,
142
                           NULL, NULL, &si, &pi))
143
        {
144
          xfree (wstring);
145
          return -1;
146
        }
147
148
      /* Wait for the child to exit */
149
      WaitForSingleObject (pi.hProcess, INFINITE);
150
151
      CloseHandle (pi.hProcess);
152
      CloseHandle (pi.hThread);
153
      xfree (wstring);
154
    }
155
156
  return 0;
157
}
158
#endif /*_W32*/
159
160
/* Generate a new photo id packet, or return NULL if canceled.
161
   FIXME:  Should we add a duplicates check similar to generate_user_id? */
162
PKT_user_id *
163
generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name)
164
0
{
165
0
  PKT_user_id *uid;
166
0
  int error=1,i;
167
0
  uint64_t len;
168
0
  char *filename;
169
0
  byte *photo=NULL;
170
0
  byte header[16];
171
0
  IOBUF file;
172
173
0
  header[0]=0x10; /* little side of photo header length */
174
0
  header[1]=0;    /* big side of photo header length */
175
0
  header[2]=1;    /* 1 == version of photo header */
176
0
  header[3]=1;    /* 1 == JPEG */
177
178
0
  for(i=4;i<16;i++) /* The reserved bytes */
179
0
    header[i]=0;
180
181
0
#define EXTRA_UID_NAME_SPACE 71
182
0
  uid=xmalloc_clear(sizeof(*uid)+71);
183
184
0
  if(photo_name && *photo_name)
185
0
    filename=make_filename(photo_name,(void *)NULL);
186
0
  else
187
0
    {
188
0
      tty_printf(_("\nPick an image to use for your photo ID."
189
0
       "  The image must be a JPEG file.\n"
190
0
       "Remember that the image is stored within your public key."
191
0
       "  If you use a\n"
192
0
       "very large picture, your key will become very large"
193
0
       " as well!\n"
194
0
       "Keeping the image close to 240x288 is a good size"
195
0
       " to use.\n"));
196
0
      filename=NULL;
197
0
    }
198
199
0
  while(photo==NULL)
200
0
    {
201
0
      if(filename==NULL)
202
0
  {
203
0
    char *tempname;
204
205
0
    tty_printf("\n");
206
207
0
    tty_enable_completion(NULL);
208
209
0
    tempname=cpr_get("photoid.jpeg.add",
210
0
         _("Enter JPEG filename for photo ID: "));
211
212
0
    tty_disable_completion();
213
214
0
    filename=make_filename(tempname,(void *)NULL);
215
216
0
    xfree(tempname);
217
218
0
    if(strlen(filename)==0)
219
0
      goto scram;
220
0
  }
221
222
0
      file=iobuf_open(filename);
223
0
      if (file && is_secured_file (iobuf_get_fd (file)))
224
0
        {
225
0
          iobuf_close (file);
226
0
          file = NULL;
227
0
          gpg_err_set_errno (EPERM);
228
0
        }
229
0
      if(!file)
230
0
  {
231
0
    log_error(_("unable to open JPEG file '%s': %s\n"),
232
0
        filename,strerror(errno));
233
0
    xfree(filename);
234
0
    filename=NULL;
235
0
    continue;
236
0
  }
237
238
239
0
      len = iobuf_get_filelength(file);
240
0
      if(len>6144)
241
0
  {
242
          /* We silently skip JPEGs larger than 1MiB because we have a
243
           * 2MiB limit on the user ID packets and we need some limit
244
           * anyway because the returned u64 is larger than the u32 or
245
           * OpenPGP.  Note that the diagnostic may print a wrong
246
           * value if the value is really large; we don't fix this to
247
           * avoid a string change.  */
248
0
    tty_printf( _("This JPEG is really large (%d bytes) !\n"), (int)len);
249
0
    if(len > 1024*1024
250
0
             || !cpr_get_answer_is_yes("photoid.jpeg.size",
251
0
          _("Are you sure you want to use it? (y/N) ")))
252
0
    {
253
0
      iobuf_close(file);
254
0
      xfree(filename);
255
0
      filename=NULL;
256
0
      continue;
257
0
    }
258
0
  }
259
260
0
      photo=xmalloc(len);
261
0
      iobuf_read(file,photo,len);
262
0
      iobuf_close(file);
263
264
      /* Is it a JPEG? */
265
0
      if(photo[0]!=0xFF || photo[1]!=0xD8)
266
0
  {
267
0
    log_error(_("'%s' is not a JPEG file\n"),filename);
268
0
    xfree(photo);
269
0
    photo=NULL;
270
0
    xfree(filename);
271
0
    filename=NULL;
272
0
    continue;
273
0
  }
274
275
      /* Build the packet */
276
0
      build_attribute_subpkt(uid,1,photo,len,header,16);
277
0
      parse_attribute_subpkts(uid);
278
0
      make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE);
279
280
      /* Showing the photo is not safe when noninteractive since the
281
         "user" may not be able to dismiss a viewer window! */
282
0
      if(opt.command_fd==-1)
283
0
  {
284
0
    show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
285
0
    switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay",
286
0
           _("Is this photo correct (y/N/q)? ")))
287
0
      {
288
0
      case -1:
289
0
        goto scram;
290
0
      case 0:
291
0
        free_attributes(uid);
292
0
        xfree(photo);
293
0
        photo=NULL;
294
0
        xfree(filename);
295
0
        filename=NULL;
296
0
        continue;
297
0
      }
298
0
  }
299
0
    }
300
301
0
  error=0;
302
0
  uid->ref=1;
303
304
0
 scram:
305
0
  xfree(filename);
306
0
  xfree(photo);
307
308
0
  if(error)
309
0
    {
310
0
      free_attributes(uid);
311
0
      xfree(uid);
312
0
      return NULL;
313
0
    }
314
315
0
  return uid;
316
0
}
317
318
/* Returns 0 for error, 1 for valid */
319
int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len)
320
1.56k
{
321
1.56k
  u16 headerlen;
322
323
1.56k
  if(attr->len<3)
324
76
    return 0;
325
326
  /* For historical reasons (i.e. "oops!"), the header length is
327
     little endian. */
328
1.48k
  headerlen=(attr->data[1]<<8) | attr->data[0];
329
330
1.48k
  if(headerlen>attr->len)
331
521
    return 0;
332
333
963
  if(type && attr->len>=4)
334
927
    {
335
927
      if(attr->data[2]==1) /* header version 1 */
336
793
  *type=attr->data[3];
337
134
      else
338
134
  *type=0;
339
927
    }
340
341
963
  *len=attr->len-headerlen;
342
343
963
  if(*len==0)
344
101
    return 0;
345
346
862
  return 1;
347
963
}
348
349
/* style==0 for extension, 1 for name, 2 for MIME type.  Remember that
350
   the "name" style string could be used in a user ID name field, so
351
   make sure it is not too big (see parse-packet.c:parse_attribute).
352
   Extensions should be 3 characters long for the best cross-platform
353
   compatibility. */
354
const char *
355
image_type_to_string(byte type,int style)
356
862
{
357
862
  const char *string;
358
359
862
  switch(type)
360
862
    {
361
525
    case 1: /* jpeg */
362
525
      if(style==0)
363
0
  string="jpg";
364
525
      else if(style==1)
365
525
  string="jpeg";
366
0
      else
367
0
  string="image/jpeg";
368
525
      break;
369
370
337
    default:
371
337
      if(style==0)
372
0
  string="bin";
373
337
      else if(style==1)
374
337
  string="unknown";
375
0
      else
376
0
  string="image/x-unknown";
377
337
      break;
378
862
    }
379
380
862
  return string;
381
862
}
382
383
#if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER)
384
static const char *
385
get_default_photo_command(void)
386
0
{
387
#if defined(_WIN32)
388
  return "!ShellExecute 400 %i";
389
#elif defined(__APPLE__)
390
  /* OS X.  This really needs more than just __APPLE__. */
391
  return "open %I";
392
#else
393
0
  if (!path_access ("xloadimage", X_OK))
394
0
    return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin";
395
0
  else if (!path_access ("display",X_OK))
396
0
    return "display -title 'KeyID 0x%k' %i";
397
0
  else if (getuid () && !path_access ("xdg-open", X_OK))
398
0
    {
399
      /* xdg-open spawns the actual program and exits so we need to
400
       * keep the temp file */
401
0
      return "xdg-open %I";
402
0
    }
403
0
  else
404
0
    return "/bin/true";
405
0
#endif
406
0
}
407
#endif
408
409
#ifndef DISABLE_PHOTO_VIEWER
410
struct spawn_info
411
{
412
  unsigned int keep_temp_file;
413
  char *command;
414
  char *tempdir;
415
  char *tempfile;
416
};
417
418
#ifdef NO_EXEC
419
static void
420
show_photo (const char *command, const char *name, const void *image, u32 len)
421
{
422
  log_error(_("no remote program execution supported\n"));
423
}
424
#else /* ! NO_EXEC */
425
#include "../common/membuf.h"
426
427
/* Makes a temp directory and filenames */
428
static int
429
setup_input_file (struct spawn_info *info, const char *name)
430
0
{
431
0
  char *tmp = opt.temp_dir;
432
0
  int len;
433
0
#define TEMPLATE "gpg-XXXXXX"
434
435
  /* Initialize by the length of last part in the path + 1 */
436
0
  len = strlen (DIRSEP_S) + strlen (TEMPLATE) + 1;
437
438
  /* Make up the temp dir and file in case we need them */
439
0
  if (tmp)
440
0
    {
441
0
      len += strlen (tmp);
442
0
      info->tempdir = xmalloc (len);
443
0
    }
444
0
  else
445
0
    {
446
#if defined (_WIN32)
447
      int ret;
448
449
      tmp = xmalloc (MAX_PATH+1);
450
      if (!tmp)
451
        return -1;
452
453
      ret = GetTempPath (MAX_PATH-len, tmp);
454
      if (ret == 0 || ret >= MAX_PATH-len)
455
  strcpy (tmp, "c:\\windows\\temp");
456
      else
457
  {
458
    /* GetTempPath may return with \ on the end */
459
    while (ret > 0 && tmp[ret-1] == '\\')
460
      {
461
        tmp[ret-1]='\0';
462
        ret--;
463
      }
464
  }
465
466
      len += ret;
467
      info->tempdir = tmp;
468
#else /* More unixish systems */
469
0
      if (!(tmp = getenv ("TMPDIR"))
470
0
          && !(tmp = getenv ("TMP")))
471
0
        tmp = "/tmp";
472
473
0
      len += strlen (tmp);
474
0
      info->tempdir = xmalloc (len);
475
0
#endif
476
0
    }
477
478
0
  if (info->tempdir == NULL)
479
0
    return -1;
480
481
0
  sprintf (info->tempdir, "%s" DIRSEP_S TEMPLATE, tmp);
482
483
0
  if (gnupg_mkdtemp (info->tempdir) == NULL)
484
0
    {
485
0
      log_error (_("can't create directory '%s': %s\n"),
486
0
                 info->tempdir, strerror (errno));
487
0
      return -1;
488
0
    }
489
490
0
  info->tempfile = xmalloc (strlen (info->tempdir) + strlen (DIRSEP_S)
491
0
                               + strlen (name) + 1);
492
0
  if (info->tempfile == NULL)
493
0
    {
494
0
      xfree (info->tempdir);
495
0
      info->tempdir = NULL;
496
0
      return -1;
497
0
    }
498
0
  sprintf (info->tempfile, "%s" DIRSEP_S "%s", info->tempdir, name);
499
0
  return 0;
500
0
}
501
502
/* Expands %i or %I in the args to the full temp file within the temp
503
   directory. */
504
static int
505
expand_args (struct spawn_info *info, const char *args_in, const char *name)
506
0
{
507
0
  const char *ch = args_in;
508
0
  membuf_t command;
509
510
0
  info->keep_temp_file = 0;
511
512
0
  if (DBG_EXTPROG)
513
0
    log_debug ("expanding string \"%s\"\n", args_in);
514
515
0
  init_membuf (&command, 100);
516
517
0
  while (*ch != '\0')
518
0
    {
519
0
      if (*ch == '%')
520
0
  {
521
0
    const char *append = NULL;
522
523
0
    ch++;
524
525
0
    switch (*ch)
526
0
      {
527
0
      case 'I':
528
0
        info->keep_temp_file = 1;
529
        /* fall through */
530
531
0
      case 'i': /* in */
532
0
        if (info->tempfile == NULL)
533
0
    {
534
0
      if (setup_input_file (info, name) < 0)
535
0
        goto fail;
536
0
    }
537
0
        append = info->tempfile;
538
0
        break;
539
540
0
      case '%':
541
0
        append = "%";
542
0
        break;
543
0
      }
544
545
0
    if (append)
546
0
            put_membuf_str (&command, append);
547
0
  }
548
0
      else
549
0
        put_membuf (&command, ch, 1);
550
551
0
      ch++;
552
0
    }
553
554
0
  put_membuf (&command, "", 1);  /* Terminate string.  */
555
556
0
  info->command = get_membuf (&command, NULL);
557
0
  if (!info->command)
558
0
    return -1;
559
560
0
  if(DBG_EXTPROG)
561
0
    log_debug("args expanded to \"%s\", use %s, keep %u\n", info->command,
562
0
        info->tempfile, info->keep_temp_file);
563
564
0
  return 0;
565
566
0
 fail:
567
0
  xfree (get_membuf (&command, NULL));
568
0
  return -1;
569
0
}
570
571
#ifndef EXEC_TEMPFILE_ONLY
572
static void
573
fill_command_argv (const char *argv[4], const char *command)
574
0
{
575
0
  argv[0] = getenv ("SHELL");
576
0
  if (argv[0] == NULL)
577
0
    argv[0] = "/bin/sh";
578
579
0
  argv[1] = "-c";
580
0
  argv[2] = command;
581
0
  argv[3] = NULL;
582
0
}
583
#endif
584
585
static void
586
run_with_pipe (struct spawn_info *info, const void *image, u32 len)
587
0
{
588
#ifdef EXEC_TEMPFILE_ONLY
589
  (void)info;
590
  (void)image;
591
  (void)len;
592
  log_error (_("this platform requires temporary files when calling"
593
               " external programs\n"));
594
  return;
595
#else /* !EXEC_TEMPFILE_ONLY */
596
0
  gpg_error_t err;
597
0
  const char *argv[4];
598
0
  gpgrt_process_t proc;
599
600
0
  fill_command_argv (argv, info->command);
601
0
  err = gpgrt_process_spawn (argv[0], argv+1, GPGRT_PROCESS_STDIN_PIPE,
602
0
                             NULL, &proc);
603
0
  if (err)
604
0
    log_error (_("unable to execute shell '%s': %s\n"),
605
0
               argv[0], gpg_strerror (err));
606
0
  else
607
0
    {
608
0
      int fd_in;
609
610
0
      err = gpgrt_process_get_fds (proc, 0, &fd_in, NULL, NULL);
611
0
      if (err)
612
0
        log_error ("unable to get pipe connection '%s': %s\n",
613
0
                   argv[2], gpg_strerror (err));
614
0
      else
615
0
        {
616
0
          write (fd_in, image, len);
617
0
          close (fd_in);
618
0
        }
619
620
0
      err = gpgrt_process_wait (proc, 1);
621
0
      if (err)
622
0
        log_error (_("unnatural exit of external program\n"));
623
624
0
      gpgrt_process_release (proc);
625
0
    }
626
0
#endif /* !EXEC_TEMPFILE_ONLY */
627
0
}
628
629
static int
630
create_temp_file (struct spawn_info *info, const void *ptr, u32 len)
631
0
{
632
0
  if (DBG_EXTPROG)
633
0
    log_debug ("using temp file '%s'\n", info->tempfile);
634
635
  /* It's not fork/exec/pipe, so create a temp file */
636
0
  if ( is_secured_filename (info->tempfile) )
637
0
    {
638
0
      log_error (_("can't create '%s': %s\n"),
639
0
                 info->tempfile, strerror (EPERM));
640
0
      gpg_err_set_errno (EPERM);
641
0
      return -1;
642
0
    }
643
0
  else
644
0
    {
645
0
      estream_t fp = es_fopen (info->tempfile, "wb");
646
647
0
      if (fp)
648
0
        {
649
0
          es_fwrite (ptr, len, 1, fp);
650
0
          es_fclose (fp);
651
0
          return 0;
652
0
        }
653
0
      else
654
0
        {
655
0
          int save = errno;
656
0
          log_error (_("can't create '%s': %s\n"),
657
0
                     info->tempfile, strerror(errno));
658
0
          gpg_err_set_errno (save);
659
0
          return -1;
660
0
        }
661
0
    }
662
0
}
663
664
static void
665
show_photo (const char *command, const char *name, const void *image, u32 len)
666
0
{
667
0
  struct spawn_info *spawn;
668
669
0
  spawn = xmalloc_clear (sizeof (struct spawn_info));
670
0
  if (!spawn)
671
0
    return;
672
673
  /* Expand the args */
674
0
  if (expand_args (spawn, command, name) < 0)
675
0
    {
676
0
      xfree (spawn);
677
0
      return;
678
0
    }
679
680
0
  if (DBG_EXTPROG)
681
0
    log_debug ("running command: %s\n", spawn->command);
682
683
0
  if (spawn->tempfile == NULL)
684
0
    run_with_pipe (spawn, image, len);
685
0
  else if (create_temp_file (spawn, image, len) == 0)
686
0
    {
687
#if defined (_WIN32)
688
      if (w32_system (spawn->command) < 0)
689
        log_error (_("system error while calling external program: %s\n"),
690
                   strerror (errno));
691
#else
692
0
      gpg_error_t err;
693
0
      const char *argv[4];
694
695
0
      fill_command_argv (argv, spawn->command);
696
0
      err = gpgrt_process_spawn (argv[0], argv+1, 0, NULL, NULL);
697
0
      if (err)
698
0
        log_error (_("unnatural exit of external program\n"));
699
0
#endif
700
701
0
      if (!spawn->keep_temp_file)
702
0
        {
703
0
          if (unlink (spawn->tempfile) < 0)
704
0
            log_info (_("WARNING: unable to remove tempfile (%s) '%s': %s\n"),
705
0
                      "in", spawn->tempfile, strerror(errno));
706
707
0
          if (rmdir (spawn->tempdir) < 0)
708
0
            log_info (_("WARNING: unable to remove temp directory '%s': %s\n"),
709
0
                      spawn->tempdir, strerror(errno));
710
0
        }
711
0
    }
712
713
0
  xfree(spawn->command);
714
0
  xfree(spawn->tempdir);
715
0
  xfree(spawn->tempfile);
716
0
  xfree(spawn);
717
0
}
718
#endif
719
#endif
720
721
722
void
723
show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count,
724
             PKT_public_key *pk, PKT_user_id *uid)
725
0
{
726
#ifdef DISABLE_PHOTO_VIEWER
727
  (void)attrs;
728
  (void)count;
729
  (void)pk;
730
  (void)uid;
731
#else /*!DISABLE_PHOTO_VIEWER*/
732
0
  int i;
733
0
  struct expando_args args;
734
0
  u32 len;
735
0
  u32 kid[2]={0,0};
736
737
0
  if (opt.exec_disable && !opt.no_perm_warn)
738
0
    {
739
0
      log_info (_("external program calls are disabled due to unsafe "
740
0
                  "options file permissions\n"));
741
0
      return;
742
0
    }
743
744
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
745
  /* There should be no way to get to this spot while still carrying
746
     setuid privs.  Just in case, bomb out if we are. */
747
  if ( getuid () != geteuid ())
748
    BUG ();
749
#endif
750
751
0
  memset (&args, 0, sizeof(args));
752
0
  args.pk = pk;
753
0
  args.validity_info = get_validity_info (ctrl, NULL, pk, uid);
754
0
  args.validity_string = get_validity_string (ctrl, pk, uid);
755
0
  namehash_from_uid (uid);
756
0
  args.namehash = uid->namehash;
757
758
0
  if (pk)
759
0
    keyid_from_pk (pk, kid);
760
761
0
  es_fflush (es_stdout);
762
763
#ifdef FIXED_PHOTO_VIEWER
764
  opt.photo_viewer = FIXED_PHOTO_VIEWER;
765
#else
766
0
  if (!opt.photo_viewer)
767
0
    opt.photo_viewer = get_default_photo_command ();
768
0
#endif
769
770
0
  for (i=0; i<count; i++)
771
0
    if (attrs[i].type == ATTRIB_IMAGE
772
0
        && parse_image_header (&attrs[i], &args.imagetype, &len))
773
0
      {
774
0
        char *command, *name;
775
0
        int offset = attrs[i].len-len;
776
777
  /* make command grow */
778
0
  command = pct_expando (ctrl, opt.photo_viewer,&args);
779
0
  if(!command)
780
0
    goto fail;
781
0
        if (!*command)
782
0
          {
783
0
            xfree (command);
784
0
            goto fail;
785
0
          }
786
787
0
  name = xmalloc (1 + 16 + strlen(EXTSEP_S)
788
0
                        + strlen (image_type_to_string (args.imagetype, 0)));
789
790
0
  if (!name)
791
0
          {
792
0
            xfree (command);
793
0
            goto fail;
794
0
          }
795
796
  /* Make the filename.  Notice we are not using the image
797
           encoding type for more than cosmetics.  Most external image
798
           viewers can handle a multitude of types, and even if one
799
           cannot understand a particular type, we have no way to know
800
           which.  The spec permits this, by the way. -dms */
801
802
#ifdef USE_ONLY_8DOT3
803
  sprintf (name,"%08lX" EXTSEP_S "%s", (ulong)kid[1],
804
                 image_type_to_string (args.imagetype, 0));
805
#else
806
0
  sprintf (name, "%08lX%08lX" EXTSEP_S "%s",
807
0
                 (ulong)kid[0], (ulong)kid[1],
808
0
                 image_type_to_string (args.imagetype, 0));
809
0
#endif
810
811
0
        show_photo (command, name, &attrs[i].data[offset], len);
812
0
        xfree (name);
813
0
        xfree (command);
814
0
      }
815
816
0
  return;
817
818
0
 fail:
819
0
  log_error(_("unable to display photo ID!\n"));
820
0
#endif /*!DISABLE_PHOTO_VIEWER*/
821
0
}