gpgme/complus/gpgcom.c
2001-07-31 09:44:22 +00:00

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, &reg );
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" );
}