546 lines
15 KiB
C
546 lines
15 KiB
C
/* gpgcom.c - COM+ component to access GnuPG
|
|
* Copyright (C) 2001 g10 Code GmbH
|
|
*
|
|
* This file is part of GPGME.
|
|
*
|
|
* GPGME 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GPGME 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
#include <windows.h>
|
|
|
|
#include <ole2.h>
|
|
|
|
#include "argparse.h"
|
|
|
|
#include "main.h"
|
|
#include "igpgme.h"
|
|
|
|
static void register_server (void);
|
|
static void unregister_server (void);
|
|
static void enter_complus (void);
|
|
|
|
|
|
enum cmd_and_opt_values { aNull = 0,
|
|
oQuiet = 'q',
|
|
oVerbose = 'v',
|
|
|
|
oNoVerbose = 500,
|
|
oOptions,
|
|
oDebug,
|
|
oDebugAll,
|
|
oNoGreeting,
|
|
oNoOptions,
|
|
oHomedir,
|
|
oGPGBinary,
|
|
oRegServer,
|
|
oUnregServer,
|
|
oEmbedding,
|
|
aTest };
|
|
|
|
|
|
static ARGPARSE_OPTS opts[] = {
|
|
|
|
{ 301, NULL, 0, N_("@Options:\n ") },
|
|
|
|
{ oVerbose, "verbose", 0, N_("verbose") },
|
|
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
|
{ oOptions, "options" , 2, N_("read options from file")},
|
|
{ oDebug, "debug" ,4|16, N_("set debugging flags")},
|
|
{ oDebugAll, "debug-all" ,0, N_("enable full debugging")},
|
|
{ oGPGBinary, "gpg-program", 2 , "" },
|
|
{ oRegServer, "RegServer" , 0, "" },
|
|
{ oUnregServer, "UnregServer" , 0, "" },
|
|
{ oEmbedding, "Embedding" , 0, "" },
|
|
{0} };
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
my_strusage( int level )
|
|
{
|
|
const char *p;
|
|
switch( level ) {
|
|
case 11: p = "gpgcom";
|
|
break;
|
|
case 13: p = VERSION; break;
|
|
/*case 17: p = PRINTABLE_OS_NAME; break;*/
|
|
case 19: p =
|
|
_("Please report bugs to <gpgme-bugs@gnupg.org>.\n");
|
|
break;
|
|
case 1:
|
|
case 40: p =
|
|
_("Usage: gpgcom [options] (-h for help)");
|
|
break;
|
|
case 41: p =
|
|
_("Syntax: gpgcom [options]\n"
|
|
"GnuPG COM+ component\n");
|
|
break;
|
|
|
|
default: p = NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
int
|
|
main (int argc, char **argv )
|
|
{
|
|
ARGPARSE_ARGS pargs;
|
|
int orig_argc;
|
|
char **orig_argv;
|
|
FILE *configfp = NULL;
|
|
char *configname = NULL;
|
|
unsigned configlineno;
|
|
int parse_debug = 0;
|
|
int default_config =1;
|
|
int greeting = 0;
|
|
int nogreeting = 0;
|
|
int action = 0;
|
|
|
|
set_strusage( my_strusage );
|
|
/*log_set_name ("gpa"); not yet implemented in logging.c */
|
|
|
|
opt.homedir = getenv("GNUPGHOME");
|
|
if( !opt.homedir || !*opt.homedir ) {
|
|
#ifdef HAVE_DRIVE_LETTERS
|
|
opt.homedir = "c:/gnupg";
|
|
#else
|
|
opt.homedir = "~/.gnupg";
|
|
#endif
|
|
}
|
|
|
|
/* check whether we have a config file on the commandline */
|
|
orig_argc = argc;
|
|
orig_argv = argv;
|
|
pargs.argc = &argc;
|
|
pargs.argv = &argv;
|
|
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
|
|
while( arg_parse( &pargs, opts) ) {
|
|
if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll )
|
|
parse_debug++;
|
|
else if( pargs.r_opt == oOptions ) {
|
|
/* yes there is one, so we do not try the default one, but
|
|
* read the option file when it is encountered at the commandline
|
|
*/
|
|
default_config = 0;
|
|
}
|
|
else if( pargs.r_opt == oNoOptions )
|
|
default_config = 0; /* --no-options */
|
|
else if( pargs.r_opt == oHomedir )
|
|
opt.homedir = pargs.r.ret_str;
|
|
}
|
|
|
|
if( default_config )
|
|
configname = make_filename(opt.homedir, "gpgme.conf", NULL );
|
|
|
|
|
|
argc = orig_argc;
|
|
argv = orig_argv;
|
|
pargs.argc = &argc;
|
|
pargs.argv = &argv;
|
|
pargs.flags= 1 | (1<<5); /* do not remove the args, allow one dash */
|
|
next_pass:
|
|
if( configname ) {
|
|
configlineno = 0;
|
|
configfp = fopen( configname, "r" );
|
|
if( !configfp ) {
|
|
if( default_config ) {
|
|
if( parse_debug )
|
|
log_info(_("NOTE: no default option file `%s'\n"),
|
|
configname );
|
|
}
|
|
else {
|
|
log_error(_("option file `%s': %s\n"),
|
|
configname, strerror(errno) );
|
|
exit(2);
|
|
}
|
|
free(configname); configname = NULL;
|
|
}
|
|
if( parse_debug && configname )
|
|
log_info(_("reading options from `%s'\n"), configname );
|
|
default_config = 0;
|
|
}
|
|
|
|
while( optfile_parse( configfp, configname, &configlineno,
|
|
&pargs, opts) ) {
|
|
switch( pargs.r_opt ) {
|
|
case oQuiet: opt.quiet = 1; break;
|
|
case oVerbose: opt.verbose++; break;
|
|
|
|
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
|
case oDebugAll: opt.debug = ~0; break;
|
|
|
|
case oOptions:
|
|
/* config files may not be nested (silently ignore them) */
|
|
if( !configfp ) {
|
|
free(configname);
|
|
configname = xstrdup(pargs.r.ret_str);
|
|
goto next_pass;
|
|
}
|
|
break;
|
|
case oNoGreeting: nogreeting = 1; break;
|
|
case oNoVerbose: opt.verbose = 0; break;
|
|
case oNoOptions: break; /* no-options */
|
|
case oHomedir: opt.homedir = pargs.r.ret_str; break;
|
|
case oGPGBinary: break;
|
|
|
|
case oRegServer: action = 1; break;
|
|
case oUnregServer: action = 2; break;
|
|
case oEmbedding: action = 3; break;
|
|
|
|
default : pargs.err = configfp? 1:2; break;
|
|
}
|
|
}
|
|
if( configfp ) {
|
|
fclose( configfp );
|
|
configfp = NULL;
|
|
free(configname); configname = NULL;
|
|
goto next_pass;
|
|
}
|
|
free( configname ); configname = NULL;
|
|
if( log_get_errorcount(0) )
|
|
exit(2);
|
|
if( nogreeting )
|
|
greeting = 0;
|
|
|
|
if( greeting ) {
|
|
fprintf(stderr, "%s %s; %s\n",
|
|
strusage(11), strusage(13), strusage(14) );
|
|
fprintf(stderr, "%s\n", strusage(15) );
|
|
}
|
|
#ifdef IS_DEVELOPMENT_VERSION
|
|
log_info("NOTE: this is a development version!\n");
|
|
#endif
|
|
|
|
if ( action == 1 )
|
|
register_server ();
|
|
else if (action == 2 )
|
|
unregister_server ();
|
|
else if (action == 3 )
|
|
enter_complus ();
|
|
else {
|
|
fprintf (stderr, "This is a COM+ component with no user interface.\n"
|
|
"gpgme --help will give you a list of options\n" );
|
|
exit (1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
register_progid ( const char *name )
|
|
{
|
|
HKEY hk = 0;
|
|
char buf[500];
|
|
|
|
/* Create a ProgID entry to point to the ClassID */
|
|
sprintf (buf, "%.400s", name);
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
sprintf (buf, "g10 Code's GnuPG made easy COMponent" );
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, 0)) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
sprintf (buf, "%.400s\\CLSID", name);
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
sprintf (buf, "%.100s", debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
register_typelib (void)
|
|
{
|
|
ITypeLib *pTypeLib;
|
|
HRESULT hr;
|
|
char name[500];
|
|
wchar_t *wname;
|
|
size_t n;
|
|
|
|
if ( !GetModuleFileNameA (0, name, sizeof (name)-10) ) {
|
|
fprintf (stderr,"GetModuleFileName() failed: %d\n",
|
|
(int)GetLastError());
|
|
exit (1);
|
|
}
|
|
n = mbstowcs (NULL, name, strlen(name)+1);
|
|
wname = xmalloc ((n+1)*sizeof *wname);
|
|
mbstowcs (wname, name, strlen (name)+1);
|
|
|
|
hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
|
|
if (hr)
|
|
fprintf (stderr, "CoInitializeEx() failed: hr=%lu\n", hr);
|
|
|
|
hr = LoadTypeLibEx (wname, REGKIND_REGISTER, &pTypeLib);
|
|
if (hr)
|
|
fprintf (stderr, "LoadTypeLibEx() failed: hr=%lx\n", hr);
|
|
|
|
ITypeLib_Release (pTypeLib);
|
|
CoUninitialize ();
|
|
free (wname);
|
|
}
|
|
|
|
static void
|
|
unregister_typelib (void)
|
|
{
|
|
UnRegisterTypeLib (&TLBID_Gpgcom, 1, 0, LANG_NEUTRAL, SYS_WIN32);
|
|
}
|
|
|
|
static void
|
|
register_server ()
|
|
{
|
|
HKEY hk = 0;
|
|
char buf[500];
|
|
|
|
|
|
register_typelib ();
|
|
|
|
/* Create a key for the CLSID */
|
|
sprintf (buf, "CLSID\\%.100s", debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
/* Store our class name as default value */
|
|
strcpy (buf, "Gpgme");
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
|
|
/* Set the application ID */
|
|
sprintf (buf, "%.100s", debugstr_guid (&APPID_Gpgcom) );
|
|
if (RegSetValueExA (hk, "AppID", 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
|
|
/* Create the LocalServer32 subkey under the CLSID key */
|
|
sprintf (buf, "CLSID\\%.100s\\LocalServer32",
|
|
debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
/* retrieve the module name and add it under the key */
|
|
if ( !GetModuleFileNameA (0, buf, sizeof (buf)-10) ) {
|
|
fprintf (stderr,"GetModuleFileName() failed\n");
|
|
exit (1);
|
|
}
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
|
|
/* Create the ProgID subkey under the CLSID key */
|
|
sprintf (buf, "CLSID\\%.100s\\ProgID",
|
|
debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, "Gpgcom.Gpgme.1", 0)) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
/* Create the VersionIndependentProgID subkey under the CLSID key */
|
|
sprintf (buf, "CLSID\\%.100s\\VersionIndependentProgID",
|
|
debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, "Gpgcom.Gpgme", 0)) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
|
|
|
|
/* Create a key to store AppID info */
|
|
sprintf (buf, "AppID\\%.100s", debugstr_guid (&APPID_Gpgcom) );
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
/* Store the name as default value */
|
|
strcpy (buf, "Gpgcom");
|
|
if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
|
|
register_progid ("Gpgcom.Gpgme");
|
|
register_progid ("Gpgcom.Gpgme.1");
|
|
|
|
/* Create a convenience cross reference to the AppID */
|
|
sprintf (buf, "AppID\\gpgcom.exe");
|
|
if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
|
|
fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
sprintf (buf, "%.100s", debugstr_guid (&APPID_Gpgcom) );
|
|
if (RegSetValueExA (hk, "AppID", 0, REG_SZ, buf, strlen (buf))) {
|
|
fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
|
|
exit (1);
|
|
}
|
|
if (RegCloseKey (hk)) {
|
|
fprintf (stderr,"RegCloseKey() failed\n");
|
|
exit (1);
|
|
}
|
|
hk = 0;
|
|
|
|
fprintf (stderr,"*** Component registered\n");
|
|
}
|
|
|
|
static void
|
|
unregister_server ()
|
|
{
|
|
char buf[500];
|
|
|
|
unregister_typelib ();
|
|
sprintf (buf, "CLSID\\%.100s\\LocalServer32",
|
|
debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
sprintf (buf, "CLSID\\%.100s\\ProgID", debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
sprintf (buf, "CLSID\\%.100s", debugstr_guid (&CLSID_Gpgme) );
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
sprintf (buf, "Gpgcom.Gpgme.1\\CLSID");
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
sprintf (buf, "Gpgcom.Gpgme.1");
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
sprintf (buf, "Gpgcom.Gpgme\\CLSID");
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
sprintf (buf, "Gpgcom.Gpgme");
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
|
|
sprintf (buf, "AppID\\%.100s", debugstr_guid (&APPID_Gpgcom) );
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
sprintf (buf, "AppID\\gpgcom.exe" );
|
|
if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
|
|
fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
|
|
|
|
fprintf (stderr,"*** component unregistered\n");
|
|
}
|
|
|
|
|
|
static void
|
|
enter_complus ()
|
|
{
|
|
HANDLE running;
|
|
DWORD reg;
|
|
IClassFactory *factory;
|
|
CLSID clsid;
|
|
HRESULT hr;
|
|
|
|
fprintf (stderr,"*** enter enter_complus()\n");
|
|
CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
|
running = CreateEvent (NULL, FALSE, FALSE, NULL );
|
|
fprintf (stderr,"*** CoInitialize() done; event=%lx\n", (unsigned long)running );
|
|
|
|
igpgme_register_exit_event (running);
|
|
factory = igpgme_factory_new ( &clsid );
|
|
fprintf (stderr,"*** igpgme_factory_new() done; got=%p\n", factory );
|
|
hr = CoRegisterClassObject (&clsid, (IUnknown*)factory,
|
|
CLSCTX_LOCAL_SERVER,
|
|
REGCLS_SUSPENDED|REGCLS_MULTIPLEUSE, ® );
|
|
if (hr) {
|
|
fprintf (stderr, "CoRegisterClassObject() failed: hr=%lx\n", hr);
|
|
exit (1);
|
|
}
|
|
hr = CoResumeClassObjects ();
|
|
if (hr)
|
|
fprintf (stderr, "CoRegisterClassObject() failed: hr=%lx\n", hr);
|
|
fprintf (stderr,"*** class object registered; waiting\n" );
|
|
|
|
WaitForSingleObject ( running, INFINITE );
|
|
fprintf (stderr,"*** shutting down\n" );
|
|
igpgme_register_exit_event (NULL);
|
|
CloseHandle (running);
|
|
CoRevokeClassObject ( reg );
|
|
fprintf (stderr,"*** class object revoked\n" );
|
|
igpgme_factory_release (factory);
|
|
fprintf (stderr,"*** factory released\n" );
|
|
CoUninitialize ();
|
|
fprintf (stderr,"*** leave enter_complus()\n" );
|
|
}
|
|
|