Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18824739
mkportable.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
18 KB
Subscribers
None
mkportable.c
View Options
/* mkportable.c - Tool to create portable version of Gpg4win
* Copyright (C) 2013 g10 Code GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 3, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* History:
2013-08-07 wk Written.
*/
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<stdarg.h>
#include
<assert.h>
#include
<errno.h>
#include
<dirent.h>
#include
<unistd.h>
#if _WIN32
# include <windows.h>
# define MKDIR_MODE_PRIVATE
# define MKDIR_MODE_PERMISSIVE
#else
# include <sys/stat.h>
# include <sys/types.h>
# define MKDIR_MODE_PRIVATE , 0700
# define MKDIR_MODE_PERMISSIVE , 0755
#endif
#define VERSION "1.0"
#define PGM "mkportable"
/* We use 3 complete lists for the 3 installation types. Given that
gcc only writes one copy of a constant string, this doesn't
require too much extra space.
The lists have initially been created by running
find . -type f | sed 's,^./, ",' | awk '{print $0 "\","}'
in a Unix mounted Windows installation directory. Non-required
files have then been removed and some common pattern replaced by
wildcards. Note that using a wildcard makes the presence of a
source file optional. */
#include
"mkportable-vanilla.h"
#include
"mkportable-light.h"
#include
"mkportable-full.h"
static
int
verbose
;
static
enum
{
iVANILLA
=
0
,
iLIGHT
,
iFULL
}
install_type
;
static
const
char
*
install_name
;
static
const
char
*
target_dir
;
static
const
char
*
const
*
filelist
;
static
void
die
(
const
char
*
format
,
...)
{
va_list
arg_ptr
;
fflush
(
stdout
);
fprintf
(
stderr
,
"%s: "
,
PGM
);
va_start
(
arg_ptr
,
format
);
vfprintf
(
stderr
,
format
,
arg_ptr
);
va_end
(
arg_ptr
);
putc
(
'\n'
,
stderr
);
exit
(
1
);
}
static
void
err
(
const
char
*
format
,
...)
{
va_list
arg_ptr
;
fflush
(
stdout
);
fprintf
(
stderr
,
"%s: "
,
PGM
);
va_start
(
arg_ptr
,
format
);
vfprintf
(
stderr
,
format
,
arg_ptr
);
va_end
(
arg_ptr
);
putc
(
'\n'
,
stderr
);
}
static
void
inf
(
const
char
*
format
,
...)
{
va_list
arg_ptr
;
if
(
!
verbose
)
return
;
fflush
(
stdout
);
fprintf
(
stdout
,
"%s: "
,
PGM
);
va_start
(
arg_ptr
,
format
);
vfprintf
(
stdout
,
format
,
arg_ptr
);
va_end
(
arg_ptr
);
putc
(
'\n'
,
stdout
);
}
#if _WIN32
static
const
char
*
w32_strerror
(
int
ec
)
{
static
char
strerr
[
256
];
if
(
ec
==
-1
)
ec
=
(
int
)
GetLastError
();
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM
,
NULL
,
ec
,
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
strerr
,
sizeof
(
strerr
)
-1
,
NULL
);
return
strerr
;
}
#endif
/*_WIN32*/
static
void
*
xmalloc
(
size_t
n
)
{
void
*
p
=
malloc
(
n
);
if
(
!
p
)
die
(
"out of core"
);
return
p
;
}
static
char
*
fix_backslashes
(
char
*
buffer
)
{
#if _WIN32
char
*
p
;
for
(
p
=
buffer
;
*
p
;
p
++
)
if
(
*
p
==
'\\'
)
*
p
=
'/'
;
#endif
return
buffer
;
}
#if _WIN32
static
char
*
unfix_backslashes
(
char
*
buffer
)
{
char
*
p
;
for
(
p
=
buffer
;
*
p
;
p
++
)
if
(
*
p
==
'/'
)
*
p
=
'\\'
;
return
buffer
;
}
#endif
/* Determine the root directory of the gnupg installation on Windows. */
static
const
char
*
get_sourcedir
(
void
)
{
#if _WIN32
static
int
got_dir
;
static
char
dir
[
MAX_PATH
+
5
];
if
(
!
got_dir
)
{
char
*
p
;
int
rc
;
rc
=
GetModuleFileName
(
NULL
,
dir
,
MAX_PATH
);
if
(
!
rc
)
die
(
"GetModuleFileName failed: %s
\n
"
,
w32_strerror
(
-1
));
got_dir
=
1
;
p
=
strrchr
(
dir
,
'\\'
);
if
(
p
)
{
*
p
=
0
;
/* If we are installed below "bin" we strip that and use
the top directory instead. */
p
=
strrchr
(
dir
,
'\\'
);
if
(
p
&&
!
strcmp
(
p
+
1
,
"bin"
))
*
p
=
0
;
}
if
(
!
p
)
die
(
"bad filename '%s' returned for this process
\n
"
,
dir
);
fix_backslashes
(
dir
);
}
if
(
*
dir
)
return
dir
;
#endif
/*_WIN32*/
/* Fallback to the current directory. */
return
"."
;
}
/* Return 0 if the target directory is suitable. */
static
int
check_target_dir
(
int
force
)
{
int
count
=
0
;
#if _WIN32
{
char
*
fname
;
HANDLE
hd
=
INVALID_HANDLE_VALUE
;
WIN32_FIND_DATAA
fi
;
fname
=
xmalloc
(
strlen
(
target_dir
)
+
2
+
2
+
1
);
if
(
!
strcmp
(
target_dir
,
"/"
))
strcpy
(
fname
,
"/*"
);
/* Trailing slash is not allowed. */
else
if
(
!
strcmp
(
target_dir
,
"."
))
strcpy
(
fname
,
"*"
);
else
if
(
*
target_dir
&&
target_dir
[
strlen
(
target_dir
)
-1
]
==
'/'
)
{
strcpy
(
fname
,
target_dir
);
strcat
(
fname
,
"*"
);
}
else
if
(
*
target_dir
&&
target_dir
[
strlen
(
target_dir
)
-1
]
!=
'*'
)
{
strcpy
(
fname
,
target_dir
);
strcat
(
fname
,
"/*"
);
}
else
strcpy
(
fname
,
target_dir
);
inf
(
"finding files in '%s'"
,
fname
);
unfix_backslashes
(
fname
);
hd
=
FindFirstFileA
(
fname
,
&
fi
);
if
(
hd
==
INVALID_HANDLE_VALUE
)
{
err
(
"error reading target directory '%s': %s"
,
target_dir
,
w32_strerror
(
-1
));
free
(
fname
);
return
1
;
}
do
{
if
(
!
strcmp
(
fi
.
cFileName
,
"."
)
||
!
strcmp
(
fi
.
cFileName
,
".."
))
;
else
count
++
;
}
while
(
FindNextFileA
(
hd
,
&
fi
));
FindClose
(
hd
);
free
(
fname
);
}
#else
/*!_WIN32*/
{
DIR
*
dir
;
struct
dirent
*
de
;
dir
=
opendir
(
target_dir
);
if
(
!
dir
)
{
err
(
"error reading read target directory '%s': %s"
,
target_dir
,
strerror
(
errno
));
return
1
;
}
while
((
de
=
readdir
(
dir
)))
{
if
(
!
strcmp
(
de
->
d_name
,
"."
)
||
!
strcmp
(
de
->
d_name
,
".."
))
continue
;
/* Skip self and parent dir entry. */
count
++
;
}
closedir
(
dir
);
}
#endif
/*!_WIN32*/
if
(
count
)
inf
(
"number of files in target directory: %d"
,
count
);
if
(
count
)
{
err
(
"target directory '%s' is not empty%s"
,
target_dir
,
force
?
" - continuing anyway"
:
""
);
if
(
!
force
)
return
1
;
}
return
0
;
}
static
char
*
make_sourcename
(
const
char
*
name
)
{
const
char
*
sdir
=
get_sourcedir
();
char
*
fname
;
fname
=
xmalloc
(
strlen
(
sdir
)
+
1
+
strlen
(
name
)
+
1
);
strcpy
(
fname
,
sdir
);
if
(
*
fname
&&
fname
[
strlen
(
fname
)
-1
]
!=
'/'
)
strcat
(
fname
,
"/"
);
strcat
(
fname
,
name
);
return
fname
;
}
static
char
*
make_targetname
(
const
char
*
name
)
{
const
char
*
tdir
=
target_dir
;
char
*
fname
;
fname
=
xmalloc
(
strlen
(
tdir
)
+
1
+
strlen
(
name
)
+
1
);
strcpy
(
fname
,
tdir
);
if
(
*
fname
&&
fname
[
strlen
(
fname
)
-1
]
!=
'/'
)
strcat
(
fname
,
"/"
);
strcat
(
fname
,
name
);
return
fname
;
}
/* Call access for a file NAME in the installation directory. */
static
int
access_sourcename
(
const
char
*
name
)
{
char
*
fname
=
make_sourcename
(
name
);
int
rc
=
access
(
fname
,
F_OK
);
free
(
fname
);
return
rc
;
}
/* Check that all source files are available. Return 0 on success.
We do this so that we don't start to copy files and only later
realize that some files are missing. This is in particular useful
because we require an empty target directory. */
static
int
check_all_files
(
void
)
{
const
char
*
name
;
int
bad
=
0
;
int
i
;
/* First a quick check for gpgconf. */
name
=
"../GnuPG/bin/gpgconf.exe"
;
if
(
access_sourcename
(
name
))
{
err
(
"file '%s' not found in the source directory"
,
name
);
return
1
;
}
for
(
i
=
0
;
(
name
=
filelist
[
i
]);
i
++
)
{
if
(
strchr
(
filelist
[
i
],
'*'
))
continue
;
if
(
access_sourcename
(
name
))
{
err
(
"file '%s' not found in the source directory"
,
name
);
bad
++
;
}
}
if
(
bad
)
inf
(
"number of missing files: %d"
,
bad
);
return
!!
bad
;
}
/* Try to make intermediate directories. */
static
void
make_dirs
(
const
char
*
name
)
{
char
*
fname
,
*
p
;
fname
=
make_targetname
(
name
);
p
=
fname
+
strlen
(
fname
)
-
strlen
(
name
);
while
((
p
=
strchr
(
p
,
'/'
)))
{
*
p
=
0
;
mkdir
(
fname
MKDIR_MODE_PERMISSIVE
);
*
p
++
=
'/'
;
}
free
(
fname
);
}
/****************
* Copy the option file skeleton to the given directory. If NAME2 is
* not NULL it is used as the destination name.
*/
static
int
copy_file
(
const
char
*
name
,
const
char
*
name2
)
{
char
*
srcname
,
*
dstname
;
FILE
*
srcfp
,
*
dstfp
;
int
tried_mkdir
=
0
;
char
buffer
[
4096
];
if
(
verbose
>
1
)
{
if
(
name2
)
inf
(
"copying '%s' as '%s'"
,
name
,
name2
);
else
inf
(
"copying '%s'"
,
name
);
}
srcname
=
make_sourcename
(
name
);
dstname
=
make_targetname
(
name2
?
name2
:
name
);
srcfp
=
fopen
(
srcname
,
"rb"
);
if
(
!
srcfp
)
{
err
(
"failed to open '%s': %s
\n
"
,
srcname
,
strerror
(
errno
));
free
(
srcname
);
free
(
dstname
);
return
1
;
}
again
:
dstfp
=
fopen
(
dstname
,
"wb"
);
if
(
!
dstfp
)
{
if
(
!
tried_mkdir
&&
errno
==
ENOENT
&&
strchr
(
name
,
'/'
))
{
tried_mkdir
=
1
;
make_dirs
(
name
);
goto
again
;
}
err
(
"failed to create '%s': %s
\n
"
,
dstname
,
strerror
(
errno
));
fclose
(
srcfp
);
free
(
srcname
);
free
(
dstname
);
return
1
;
}
while
(
!
feof
(
srcfp
))
{
size_t
n
;
n
=
fread
(
buffer
,
1
,
sizeof
buffer
,
srcfp
);
if
(
n
<
sizeof
buffer
&&
ferror
(
srcfp
))
{
err
(
"error reading '%s'
\n
"
,
srcname
);
fclose
(
srcfp
);
fclose
(
dstfp
);
free
(
srcname
);
free
(
dstname
);
return
1
;
}
errno
=
0
;
if
(
fwrite
(
buffer
,
1
,
n
,
dstfp
)
!=
n
)
{
err
(
"error writing to '%s': %s
\n
"
,
dstname
,
strerror
(
errno
));
fclose
(
srcfp
);
fclose
(
dstfp
);
free
(
srcname
);
free
(
dstname
);
return
1
;
}
}
if
(
fflush
(
dstfp
)
==
EOF
)
{
err
(
"error writing to '%s': %s
\n
"
,
dstname
,
strerror
(
errno
));
fclose
(
srcfp
);
fclose
(
dstfp
);
free
(
srcname
);
free
(
dstname
);
return
1
;
}
if
(
fclose
(
dstfp
)
==
EOF
)
{
err
(
"error closing '%s': %s
\n
"
,
dstname
,
strerror
(
errno
));
fclose
(
srcfp
);
fclose
(
dstfp
);
free
(
srcname
);
free
(
dstname
);
return
1
;
}
fclose
(
srcfp
);
free
(
srcname
);
free
(
dstname
);
return
0
;
}
/* Copy files which contain one '*' wildcard. */
static
int
wildcard_copy_file
(
const
char
*
name
)
{
int
res
=
0
;
char
*
fname
;
size_t
srcpos
;
if
(
verbose
>
1
)
inf
(
"globing '%s'"
,
name
);
fname
=
make_sourcename
(
name
);
srcpos
=
strlen
(
fname
)
-
strlen
(
name
);
#if _WIN32
{
HANDLE
hd
=
INVALID_HANDLE_VALUE
;
WIN32_FIND_DATAA
fi
;
char
*
p
,
*
tail
;
char
*
buffer
=
NULL
;
p
=
strchr
(
fname
,
'*'
);
assert
(
p
);
tail
=
strchr
(
p
,
'/'
);
if
(
tail
)
*
tail
++
=
0
;
unfix_backslashes
(
fname
);
hd
=
FindFirstFileA
(
fname
,
&
fi
);
fix_backslashes
(
fname
);
p
=
strrchr
(
fname
,
'/'
);
if
(
p
)
*
p
=
0
;
if
(
hd
!=
INVALID_HANDLE_VALUE
)
{
do
{
if
(
!
strcmp
(
fi
.
cFileName
,
"."
)
||
!
strcmp
(
fi
.
cFileName
,
".."
))
;
else
{
free
(
buffer
);
buffer
=
xmalloc
(
strlen
(
fname
)
+
1
+
strlen
(
fi
.
cFileName
)
+
1
+
(
tail
?
strlen
(
tail
)
:
0
)
+
1
);
strcpy
(
buffer
,
fname
);
strcat
(
buffer
,
"/"
);
strcat
(
buffer
,
fi
.
cFileName
);
if
(
tail
)
{
strcat
(
buffer
,
"/"
);
strcat
(
buffer
,
tail
);
}
if
(
!
access
(
buffer
,
F_OK
))
if
(
copy_file
(
buffer
+
srcpos
,
NULL
))
{
res
=
1
;
goto
leave
;
}
}
}
while
(
FindNextFileA
(
hd
,
&
fi
));
leave
:
free
(
buffer
);
FindClose
(
hd
);
}
}
#endif
/*_WIN32*/
free
(
fname
);
return
res
;
}
/* Copy all files to the target directory. */
static
int
copy_all_files
(
void
)
{
int
idx
;
const
char
*
name
;
inf
(
"copying files to '%s'"
,
target_dir
);
for
(
idx
=
0
;
(
name
=
filelist
[
idx
]);
idx
++
)
{
if
(
strchr
(
name
,
'*'
))
{
if
(
wildcard_copy_file
(
name
))
return
1
;
}
else
if
(
copy_file
(
name
,
NULL
))
return
1
;
}
/* Pinentry is special. Depending on the installation type we need
to install a copy under the name pinentry.exe. */
switch
(
install_type
)
{
case
iFULL
:
copy_file
(
"pinentry-qt.exe"
,
"pinentry.exe"
);
break
;
case
iLIGHT
:
copy_file
(
"pinentry-gtk-2.exe"
,
"pinentry.exe"
);
break
;
case
iVANILLA
:
copy_file
(
"pinentry-w32.exe"
,
"pinentry.exe"
);
break
;
}
return
0
;
}
/* Create the new home directory. */
static
int
make_home_dir
(
void
)
{
char
*
name
;
name
=
make_targetname
(
"home"
);
if
(
mkdir
(
name
MKDIR_MODE_PRIVATE
))
{
if
(
errno
==
EEXIST
)
inf
(
"portable home directory '%s' already exists - fine"
,
name
);
else
{
err
(
"error creating portable home directory '%s': %s"
,
name
,
strerror
(
errno
));
free
(
name
);
return
1
;
}
}
else
inf
(
"portable home directory '%s' created"
,
name
);
free
(
name
);
return
0
;
}
/* Write the kde.conf file which tells kleopatra where to put its data. */
static
int
write_kde_conf
(
void
)
{
char
*
name
;
FILE
*
fp
;
if
(
install_type
!=
iFULL
)
{
return
0
;
}
name
=
make_targetname
(
"bin/kde.conf"
);
fp
=
fopen
(
name
,
"wb"
);
if
(
!
fp
)
{
err
(
"failed to create '%s': %s
\n
"
,
name
,
strerror
(
errno
));
free
(
name
);
return
1
;
}
fprintf
(
fp
,
"[KDE]
\n
"
"KDEHOME=home/kleopatra
\n
"
"[XDG]
\n
"
"XDG_DATA_HOME=home/kleopatra
\n
"
"XDG_CONFIG_HOME=home/kleopatra
\n
"
);
if
(
fflush
(
fp
)
==
EOF
)
{
err
(
"error writing to '%s': %s
\n
"
,
name
,
strerror
(
errno
));
fclose
(
fp
);
free
(
name
);
return
1
;
}
if
(
fclose
(
fp
)
==
EOF
)
{
err
(
"error closing '%s': %s
\n
"
,
name
,
strerror
(
errno
));
free
(
name
);
return
1
;
}
free
(
name
);
return
0
;
}
/* Write the gpgconf.ctl file which is used by GnuPG to put itself
into portable mode. */
static
int
write_ctl_file
(
void
)
{
char
*
name
;
FILE
*
fp
;
name
=
make_targetname
(
"gpgconf.ctl"
);
fp
=
fopen
(
name
,
"wb"
);
if
(
!
fp
)
{
err
(
"failed to create '%s': %s
\n
"
,
name
,
strerror
(
errno
));
free
(
name
);
return
1
;
}
fprintf
(
fp
,
"# The presence of this file switches GnuPG into portable mode.
\n
"
"#
\n
"
"# Install type is: %s
\n
"
"# (created by %s version %s)
\n
"
,
install_name
,
PGM
,
VERSION
);
if
(
fflush
(
fp
)
==
EOF
)
{
err
(
"error writing to '%s': %s
\n
"
,
name
,
strerror
(
errno
));
fclose
(
fp
);
free
(
name
);
return
1
;
}
if
(
fclose
(
fp
)
==
EOF
)
{
err
(
"error closing '%s': %s
\n
"
,
name
,
strerror
(
errno
));
free
(
name
);
return
1
;
}
free
(
name
);
return
0
;
}
static
void
usage
(
int
mode
)
{
FILE
*
fp
=
mode
>
0
?
stderr
:
stdout
;
if
(
mode
<=
0
)
fputs
(
PGM
" "
VERSION
"
\n
"
"Copyright (C) 2013 g10 Code GmbH
\n
"
"License GPLv3+: GNU GPL version 3 or later "
"<http://gnu.org/licenses/gpl.html>.
\n
"
"This is free software: you are free to change "
"and redistribute it.
\n
"
"There is NO WARRANTY, to the extent permitted by law.
\n\n
"
,
fp
);
if
(
mode
<
0
)
exit
(
0
);
fputs
(
"Usage: mkportable [OPTIONS] TARGETDIR
\n
"
,
fp
);
if
(
mode
>
0
)
exit
(
1
);
fputs
(
"Create a portable version of Gpg4win by copying the required
\n
"
"files to TARGETDIR
\n
"
"
\n
"
"Options:
\n
"
" --vanilla create a bare GnuPG version [default]
\n
"
" --light create a light version
\n
"
" --full create a full version
\n
"
" --verbose enable extra informational output
\n
"
" --force force installation to a non-empty directory
\n
"
" --version print version of this program and exit
\n
"
" --help print this help and exit
\n
"
,
fp
);
exit
(
0
);
}
int
main
(
int
argc
,
char
**
argv
)
{
int
last_argc
=
-1
;
int
force
=
0
;
if
(
argc
)
{
argc
--
;
argv
++
;
}
while
(
argc
&&
last_argc
!=
argc
)
{
last_argc
=
argc
;
if
(
!
strcmp
(
*
argv
,
"--"
))
{
argc
--
;
argv
++
;
break
;
}
else
if
(
!
strcmp
(
*
argv
,
"--version"
))
usage
(
-1
);
else
if
(
!
strcmp
(
*
argv
,
"--help"
))
usage
(
0
);
else
if
(
!
strcmp
(
*
argv
,
"--verbose"
))
{
verbose
++
;
argc
--
;
argv
++
;
}
else
if
(
!
strcmp
(
*
argv
,
"--force"
))
{
force
=
1
;
argc
--
;
argv
++
;
}
else
if
(
!
strcmp
(
*
argv
,
"--vanilla"
))
{
install_type
=
iVANILLA
;
argc
--
;
argv
++
;
}
else
if
(
!
strcmp
(
*
argv
,
"--light"
))
{
install_type
=
iLIGHT
;
argc
--
;
argv
++
;
}
else
if
(
!
strcmp
(
*
argv
,
"--full"
))
{
install_type
=
iFULL
;
argc
--
;
argv
++
;
}
else
if
(
!
strncmp
(
*
argv
,
"--"
,
2
))
die
(
"unknown option '%s'"
,
*
argv
);
}
if
(
argc
!=
1
||
!*
argv
[
0
])
usage
(
1
);
target_dir
=
fix_backslashes
(
argv
[
0
]);
inf
(
"source directory is '%s'"
,
get_sourcedir
());
inf
(
"target directory is '%s'"
,
target_dir
);
switch
(
install_type
)
{
case
iVANILLA
:
filelist
=
vanilla_files
;
install_name
=
"vanilla"
;
break
;
case
iLIGHT
:
filelist
=
light_files
;
install_name
=
"light"
;
break
;
case
iFULL
:
filelist
=
full_files
;
install_name
=
"full"
;
break
;
default
:
assert
(
!
"bug"
);
}
if
(
check_target_dir
(
force
))
return
1
;
if
(
check_all_files
())
return
1
;
if
(
copy_all_files
())
return
1
;
if
(
make_home_dir
())
return
1
;
if
(
write_ctl_file
())
return
1
;
if
(
write_kde_conf
())
return
1
;
inf
(
"ready"
);
return
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Dec 23, 1:15 PM (21 m, 50 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
11/f0/c6c7034e24f8ec8cceb86207f5f4
Attached To
rW Gpg4win
Event Timeline
Log In to Comment