Coverage Report

Created: 2022-12-08 06:09

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