MSI: Fix possible use of unintialized variable
[gpg4win.git] / src / mkportable.c
1 /* mkportable.c - Tool to create portable version of Gpg4win
2  * Copyright (C) 2013 g10 Code GmbH
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 3, or (at your option) any
7  * later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /* History:
19    2013-08-07 wk  Written.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <dirent.h>
29 #include <unistd.h>
30 #if _WIN32
31 # include <windows.h>
32 # define MKDIR_MODE_PRIVATE
33 # define MKDIR_MODE_PERMISSIVE
34 #else
35 # include <sys/stat.h>
36 # include <sys/types.h>
37 # define MKDIR_MODE_PRIVATE     , 0700
38 # define MKDIR_MODE_PERMISSIVE  , 0755
39 #endif
40
41
42 #define VERSION "1.0"
43 #define PGM "mkportable"
44
45 /* We use 3 complete lists for the 3 installation types.  Given that
46    gcc only writes one copy of a constant string, this doesn't
47    require too much extra space.
48
49    To generate the lists install Gpg4win foll and take only the
50    GnuPG part to create the vanilla file. Then use GnuPG
51    and the Gpg4win directory together to create the full
52    file.
53
54    The lists have initially been created by running
55
56      find . -type f | sed 's,^./,  ",' | awk '{print $0 "\","}'
57
58    in a Unix mounted Windows installation directory.  Non-required
59    files have then been removed and some common pattern replaced by
60    wildcards.  Note that using a wildcard makes the presence of a
61    source file optional.  */
62 #include "mkportable-vanilla.h"
63 #include "mkportable-full.h"
64
65 static int verbose;
66 static enum { iVANILLA = 0, iFULL } install_type;
67 static const char *install_name;
68 static const char *target_dir;
69 static const char * const *filelist;
70
71 static void
72 die (const char *format, ...)
73 {
74   va_list arg_ptr;
75
76   fflush (stdout);
77   fprintf (stderr, "%s: ", PGM);
78
79   va_start (arg_ptr, format);
80   vfprintf (stderr, format, arg_ptr);
81   va_end (arg_ptr);
82   putc ('\n', stderr);
83
84   exit (1);
85 }
86
87
88 static void
89 err (const char *format, ...)
90 {
91   va_list arg_ptr;
92
93   fflush (stdout);
94   fprintf (stderr, "%s: ", PGM);
95
96   va_start (arg_ptr, format);
97   vfprintf (stderr, format, arg_ptr);
98   va_end (arg_ptr);
99   putc ('\n', stderr);
100 }
101
102
103 static void
104 inf (const char *format, ...)
105 {
106   va_list arg_ptr;
107
108   if (!verbose)
109     return;
110
111   fflush (stdout);
112   fprintf (stdout, "%s: ", PGM);
113
114   va_start (arg_ptr, format);
115   vfprintf (stdout, format, arg_ptr);
116   va_end (arg_ptr);
117   putc ('\n', stdout);
118 }
119
120 #if _WIN32
121 static const char *
122 w32_strerror (int ec)
123 {
124   static char strerr[256];
125
126   if (ec == -1)
127     ec = (int)GetLastError ();
128   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
129                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
130                  strerr, sizeof (strerr)-1, NULL);
131   return strerr;
132 }
133 #endif /*_WIN32*/
134
135 static void *
136 xmalloc (size_t n)
137 {
138   void *p = malloc (n);
139   if (!p)
140     die ("out of core");
141   return p;
142 }
143
144
145 static char *
146 fix_backslashes (char *buffer)
147 {
148 #if _WIN32
149   char *p;
150
151   for (p=buffer; *p; p++)
152     if (*p == '\\')
153       *p = '/';
154 #endif
155   return buffer;
156 }
157
158
159 #if _WIN32
160 static char *
161 unfix_backslashes (char *buffer)
162 {
163   char *p;
164
165   for (p=buffer; *p; p++)
166     if (*p == '/')
167       *p = '\\';
168   return buffer;
169 }
170 #endif
171
172
173 /* Determine the root directory of the gnupg installation on Windows.  */
174 static const char *
175 get_sourcedir (void)
176 {
177 #if _WIN32
178   static int got_dir;
179   static char dir[MAX_PATH+5];
180
181   if (!got_dir)
182     {
183       char *p;
184       int rc;
185
186       rc = GetModuleFileName (NULL, dir, MAX_PATH);
187       if (!rc)
188         die ("GetModuleFileName failed: %s\n", w32_strerror (-1));
189       got_dir = 1;
190       p = strrchr (dir, '\\');
191       if (p)
192         {
193           *p = 0;
194
195           /* If we are installed below "bin" we strip that and use
196              the top directory instead.  */
197           p = strrchr (dir, '\\');
198           if (p && !strcmp (p+1, "bin"))
199             *p = 0;
200         }
201       if (!p)
202         die ("bad filename '%s' returned for this process\n", dir);
203       fix_backslashes (dir);
204     }
205
206   if (*dir)
207     return dir;
208 #endif /*_WIN32*/
209   /* Fallback to the current directory. */
210   return ".";
211 }
212
213
214 /* Return 0 if the target directory is suitable.  */
215 static int
216 check_target_dir (int force)
217 {
218   int count = 0;
219
220 #if _WIN32
221   {
222     char *fname;
223     HANDLE hd = INVALID_HANDLE_VALUE;
224     WIN32_FIND_DATAA fi;
225
226     fname = xmalloc (strlen (target_dir) + 2 + 2 + 1);
227     if (!strcmp (target_dir, "/"))
228       strcpy (fname, "/*"); /* Trailing slash is not allowed.  */
229     else if (!strcmp (target_dir, "."))
230       strcpy (fname, "*");
231     else if (*target_dir && target_dir[strlen (target_dir)-1] == '/')
232       {
233         strcpy (fname, target_dir);
234         strcat (fname, "*");
235       }
236     else if (*target_dir && target_dir[strlen (target_dir)-1] != '*')
237       {
238         strcpy (fname, target_dir);
239         strcat (fname, "/*");
240       }
241     else
242       strcpy (fname, target_dir);
243
244     inf ("finding files in '%s'", fname);
245
246     unfix_backslashes (fname);
247     hd = FindFirstFileA (fname, &fi);
248     if (hd == INVALID_HANDLE_VALUE)
249       {
250         err ("error reading target directory '%s': %s",
251              target_dir, w32_strerror (-1));
252         free (fname);
253         return 1;
254       }
255     do
256       {
257         if (!strcmp (fi.cFileName, "." ) || !strcmp (fi.cFileName, ".."))
258           ;
259         else
260           count++;
261       }
262     while (FindNextFileA (hd, &fi));
263     FindClose (hd);
264     free (fname);
265   }
266 #else /*!_WIN32*/
267   {
268     DIR *dir;
269     struct dirent *de;
270
271     dir = opendir (target_dir);
272     if (!dir)
273       {
274         err ("error reading read target directory '%s': %s",
275              target_dir, strerror (errno));
276         return 1;
277       }
278     while ((de = readdir (dir)))
279       {
280         if (!strcmp (de->d_name, "." ) || !strcmp (de->d_name, ".."))
281           continue; /* Skip self and parent dir entry.  */
282         count++;
283       }
284     closedir (dir);
285   }
286 #endif /*!_WIN32*/
287
288   if (count)
289     inf ("number of files in target directory: %d", count);
290   if (count)
291     {
292       err ("target directory '%s' is not empty%s",
293            target_dir, force? " - continuing anyway":"");
294       if (!force)
295         return 1;
296     }
297
298   return 0;
299 }
300
301
302 static char *
303 make_sourcename (const char *name)
304 {
305   const char *sdir = get_sourcedir ();
306   char *fname;
307
308   fname = xmalloc (strlen (sdir) + 1 + strlen (name) + 1);
309   strcpy (fname, sdir);
310   if (*fname && fname[strlen (fname)-1] != '/')
311     strcat (fname, "/");
312   strcat (fname, name);
313   return fname;
314 }
315
316 static char *
317 make_targetname (const char *name)
318 {
319   const char *tdir = target_dir;
320   char *fname;
321   int offset = 0;
322
323   fname = xmalloc (strlen (tdir) + 1 + strlen (name) + 1);
324   strcpy (fname, tdir);
325   if (*fname && fname[strlen (fname)-1] != '/')
326     strcat (fname, "/");
327   if (!strncmp (name, "../GnuPG/", 9))
328     offset = 9;
329   strcat (fname, name + offset);
330   return fname;
331 }
332
333
334 /* Call access for a file NAME in the installation directory.  */
335 static int
336 access_sourcename (const char *name)
337 {
338   char *fname = make_sourcename (name);
339   int rc = access (fname, F_OK);
340   free (fname);
341   return rc;
342 }
343
344
345 /* Check that all source files are available.  Return 0 on success.
346    We do this so that we don't start to copy files and only later
347    realize that some files are missing.  This is in particular useful
348    because we require an empty target directory.  */
349 static int
350 check_all_files (void)
351 {
352   const char *name;
353   int bad = 0;
354   int i;
355
356   /* First a quick check for gpgconf.  */
357   name = "../GnuPG/bin/gpgconf.exe";
358   if (access_sourcename (name))
359     {
360       err ("file '%s' not found in the source directory", name);
361       return 1;
362     }
363
364   for (i=0; (name = filelist[i]); i++)
365     {
366       if (strchr (filelist[i], '*'))
367         continue;
368       if (access_sourcename (name))
369         {
370           err ("file '%s' not found in the source directory", name);
371           bad++;
372         }
373     }
374
375   if (bad)
376     inf ("number of missing files: %d", bad);
377
378   return !!bad;
379 }
380
381 /* Try to make intermediate directories.  */
382 static void
383 make_dirs (const char *name)
384 {
385   char *fname, *p;
386
387   fname = make_targetname (name);
388   p = fname + strlen (target_dir) + 1;
389   while ((p = strchr (p, '/')))
390     {
391       *p = 0;
392       mkdir (fname MKDIR_MODE_PERMISSIVE);
393       *p++ = '/';
394     }
395   free (fname);
396 }
397
398
399 /****************
400  * Copy the option file skeleton to the given directory.  If NAME2 is
401  * not NULL it is used as the destination name.
402  */
403 static int
404 copy_file (const char *name, const char *name2)
405 {
406   char *srcname, *dstname;
407   FILE *srcfp, *dstfp;
408   int tried_mkdir = 0;
409   char buffer[4096];
410
411   if (verbose > 1)
412     {
413       if (name2)
414         inf ("copying '%s' as '%s'", name, name2);
415       else
416         inf ("copying '%s'", name);
417     }
418
419   srcname = make_sourcename (name);
420   dstname = make_targetname (name2? name2:name);
421
422   if (verbose > 2)
423     {
424       inf ("srcname '%s' target '%s'", srcname, dstname);
425     }
426
427   srcfp = fopen (srcname, "rb");
428   if (!srcfp)
429     {
430       err ("failed to open '%s': %s\n", srcname, strerror (errno));
431       free (srcname);
432       free (dstname);
433       return 1;
434     }
435
436  again:
437   dstfp = fopen (dstname, "wb");
438   if (!dstfp)
439     {
440       if (!tried_mkdir && errno == ENOENT && strchr (name, '/'))
441         {
442           tried_mkdir = 1;
443           make_dirs (name);
444           goto again;
445         }
446
447       err ("failed to create '%s': %s\n", dstname, strerror (errno));
448       fclose (srcfp);
449       free (srcname);
450       free (dstname);
451       return 1;
452     }
453
454   while (!feof (srcfp))
455     {
456       size_t n;
457
458       n = fread (buffer, 1, sizeof buffer, srcfp);
459       if (n < sizeof buffer && ferror (srcfp))
460         {
461           err ("error reading '%s'\n", srcname);
462           fclose (srcfp);
463           fclose (dstfp);
464           free (srcname);
465           free (dstname);
466           return 1;
467         }
468
469       errno = 0;
470       if (fwrite (buffer, 1, n, dstfp) != n)
471         {
472           err ("error writing to '%s': %s\n", dstname, strerror (errno));
473           fclose (srcfp);
474           fclose (dstfp);
475           free (srcname);
476           free (dstname);
477           return 1;
478         }
479     }
480
481   if (fflush (dstfp) == EOF)
482     {
483       err ("error writing to '%s': %s\n", dstname, strerror (errno));
484       fclose (srcfp);
485       fclose (dstfp);
486       free (srcname);
487       free (dstname);
488       return 1;
489     }
490   if (fclose (dstfp) == EOF)
491     {
492       err ("error closing '%s': %s\n", dstname, strerror (errno));
493       fclose (srcfp);
494       fclose (dstfp);
495       free (srcname);
496       free (dstname);
497       return 1;
498     }
499
500   fclose  (srcfp);
501
502   free (srcname);
503   free (dstname);
504   return 0;
505 }
506
507
508 /* Copy files which contain one '*' wildcard. */
509 static int
510 wildcard_copy_file (const char *name)
511 {
512   int res = 0;
513   char *fname;
514   size_t srcpos;
515
516   if (verbose > 1)
517     inf ("globing '%s'", name);
518
519   fname = make_sourcename (name);
520   srcpos = strlen (fname) - strlen (name);
521
522 #if _WIN32
523   {
524     HANDLE hd = INVALID_HANDLE_VALUE;
525     WIN32_FIND_DATAA fi;
526     char *p, *tail;
527     char *buffer = NULL;
528
529     p = strchr (fname, '*');
530     assert (p);
531     tail = strchr (p, '/');
532     if (tail)
533       *tail++ = 0;
534
535     unfix_backslashes (fname);
536     hd = FindFirstFileA (fname, &fi);
537     fix_backslashes (fname);
538
539     p = strrchr (fname, '/');
540     if (p)
541       *p = 0;
542
543     if (hd != INVALID_HANDLE_VALUE)
544       {
545         do
546           {
547             if (!strcmp (fi.cFileName, "." ) || !strcmp (fi.cFileName, ".."))
548               ;
549             else
550               {
551                 free (buffer);
552                 buffer = xmalloc (strlen (fname) + 1
553                                   + strlen (fi.cFileName) + 1
554                                   + (tail? strlen (tail):0) + 1);
555                 strcpy (buffer, fname);
556                 strcat (buffer, "/");
557                 strcat (buffer, fi.cFileName);
558                 if (tail)
559                   {
560                     strcat (buffer, "/");
561                     strcat (buffer, tail);
562                   }
563                 if (!access (buffer, F_OK))
564                   if (copy_file (buffer + srcpos, NULL))
565                     {
566                       res = 1;
567                       goto leave;
568                     }
569
570               }
571         }
572         while (FindNextFileA (hd, &fi));
573       leave:
574         free (buffer);
575         FindClose (hd);
576       }
577   }
578 #endif /*_WIN32*/
579
580   free (fname);
581   return res;
582 }
583
584
585 /* Copy all files to the target directory.  */
586 static int
587 copy_all_files (void)
588 {
589   int idx;
590   const char *name;
591
592   inf ("copying files to '%s'", target_dir);
593
594   for (idx=0; (name = filelist[idx]); idx++)
595     {
596       if (strchr (name, '*'))
597         {
598           if (wildcard_copy_file (name))
599             return 1;
600         }
601       else if (copy_file (name, NULL))
602         return 1;
603     }
604
605   return 0;
606 }
607
608
609 /* Create the new home directory.  */
610 static int
611 make_home_dir (void)
612 {
613   char *name;
614
615   name = make_targetname ("home");
616   if (mkdir (name MKDIR_MODE_PRIVATE))
617     {
618       if (errno == EEXIST)
619         inf ("portable home directory '%s' already exists - fine", name);
620       else
621         {
622           err ("error creating portable home directory '%s': %s",
623                name, strerror (errno));
624           free (name);
625           return 1;
626         }
627     }
628   else
629     inf ("portable home directory '%s' created", name);
630   free (name);
631   return 0;
632 }
633
634 /* Write the kde.conf file which tells kleopatra where to put its data.  */
635 static int
636 write_kde_conf (void)
637 {
638   char *name;
639   FILE *fp;
640
641   if (install_type != iFULL)
642     {
643       return 0;
644     }
645
646   name = make_targetname ("bin/kde.conf");
647   fp = fopen (name, "wb");
648   if (!fp)
649     {
650       err ("failed to create '%s': %s\n", name, strerror (errno));
651       free (name);
652       return 1;
653     }
654
655   fprintf (fp,
656            "[KDE]\n"
657            "KDEHOME=home/kleopatra\n"
658            "[XDG]\n"
659            "XDG_DATA_HOME=home/kleopatra\n"
660            "XDG_CONFIG_HOME=home/kleopatra\n");
661
662    if (fflush (fp) == EOF)
663     {
664       err ("error writing to '%s': %s\n", name, strerror (errno));
665       fclose (fp);
666       free (name);
667       return 1;
668     }
669   if (fclose (fp) == EOF)
670     {
671       err ("error closing '%s': %s\n", name, strerror (errno));
672       free (name);
673       return 1;
674     }
675
676   free (name);
677   return 0;
678 }
679
680
681 /* Write the gpgconf.ctl file which is used by GnuPG to put itself
682    into portable mode.  */
683 static int
684 write_ctl_file (void)
685 {
686   char *name;
687   FILE *fp;
688
689   name = make_targetname ("bin/gpgconf.ctl");
690   fp = fopen (name, "wb");
691   if (!fp)
692     {
693       err ("failed to create '%s': %s\n", name, strerror (errno));
694       free (name);
695       return 1;
696     }
697
698   fprintf (fp,
699            "# The presence of this file switches GnuPG into portable mode.\n"
700            "#\n"
701            "# Install type is: %s\n"
702            "# (created by %s version %s)\n",
703            install_name, PGM, VERSION);
704
705    if (fflush (fp) == EOF)
706     {
707       err ("error writing to '%s': %s\n", name, strerror (errno));
708       fclose (fp);
709       free (name);
710       return 1;
711     }
712   if (fclose (fp) == EOF)
713     {
714       err ("error closing '%s': %s\n", name, strerror (errno));
715       free (name);
716       return 1;
717     }
718
719   free (name);
720   return 0;
721 }
722
723
724 static void
725 usage (int mode)
726 {
727   FILE *fp = mode > 0? stderr : stdout;
728
729   if (mode <= 0)
730     fputs (PGM " "VERSION"\n"
731            "Copyright (C) 2013 g10 Code GmbH\n"
732            "License GPLv3+: GNU GPL version 3 or later "
733            "<http://gnu.org/licenses/gpl.html>.\n"
734            "This is free software: you are free to change "
735            "and redistribute it.\n"
736            "There is NO WARRANTY, to the extent permitted by law.\n\n",
737            fp);
738
739   if (mode < 0)
740     exit (0);
741
742   fputs ("Usage: mkportable [OPTIONS] TARGETDIR\n", fp);
743   if (mode > 0)
744     exit (1);
745
746   fputs ("Create a portable version of Gpg4win by copying the required\n"
747          "files to TARGETDIR\n"
748          "\n"
749          "Options:\n"
750          "  --vanilla   create a bare GnuPG version [default]\n"
751          "  --full      create a full version\n"
752          "  --verbose   enable extra informational output\n"
753          "  --force     force installation to a non-empty directory\n"
754          "  --version   print version of this program and exit\n"
755          "  --help      print this help and exit\n",
756          fp);
757
758   exit (0);
759 }
760
761 int
762 main (int argc, char **argv)
763 {
764   int last_argc = -1;
765   int force = 0;
766
767   if (argc)
768     {
769       argc--; argv++;
770     }
771
772   while (argc && last_argc != argc )
773     {
774       last_argc = argc;
775       if (!strcmp (*argv, "--"))
776         {
777           argc--; argv++;
778           break;
779         }
780       else if (!strcmp (*argv, "--version"))
781         usage (-1);
782       else if (!strcmp (*argv, "--help"))
783         usage (0);
784       else if (!strcmp (*argv, "--verbose"))
785         {
786           verbose++;
787           argc--; argv++;
788         }
789       else if (!strcmp (*argv, "--force"))
790         {
791           force = 1;
792           argc--; argv++;
793         }
794       else if (!strcmp (*argv, "--vanilla"))
795         {
796           install_type = iVANILLA;
797           argc--; argv++;
798         }
799       else if (!strcmp (*argv, "--full"))
800         {
801           install_type = iFULL;
802           argc--; argv++;
803         }
804       else if (!strncmp (*argv, "--", 2))
805         die ("unknown option '%s'", *argv);
806     }
807
808   if (argc != 1 || !*argv[0])
809     usage (1);
810   target_dir = fix_backslashes (argv[0]);
811
812   inf ("source directory is '%s'", get_sourcedir ());
813   inf ("target directory is '%s'", target_dir);
814
815   switch (install_type)
816     {
817     case iVANILLA: filelist = vanilla_files; install_name = "vanilla"; break;
818     case iFULL:    filelist = full_files;    install_name = "full"; break;
819     default:  assert (!"bug");
820     }
821
822   if (check_target_dir (force))
823     return 1;
824
825   if (check_all_files ())
826     return 1;
827
828   if (copy_all_files ())
829     return 1;
830
831   if (make_home_dir ())
832     return 1;
833
834   if (write_ctl_file ())
835     return 1;
836
837   if (write_kde_conf ())
838     return 1;
839
840   inf ("ready");
841
842   return 0;
843 }