/* 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 #include #include #include #include #include #include #include #include #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 .\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" ); }