2009-10-20 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / complus / gpgcom.c
1 /* gpgcom.c - COM+ component to access GnuPG
2  *      Copyright (C) 2001 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <time.h>
29 #include <windows.h>
30
31 #include <ole2.h>
32
33 #include "argparse.h"
34
35 #include "main.h"
36 #include "igpgme.h"
37
38 static void register_server (void);
39 static void unregister_server (void);
40 static void enter_complus (void);
41
42
43 enum cmd_and_opt_values { aNull = 0,
44     oQuiet        = 'q',
45     oVerbose      = 'v',
46
47     oNoVerbose = 500,
48     oOptions,
49     oDebug,
50     oDebugAll,
51     oNoGreeting,
52     oNoOptions,
53     oHomedir,
54     oGPGBinary,
55     oRegServer,
56     oUnregServer,
57     oEmbedding,
58 aTest };
59
60
61 static ARGPARSE_OPTS opts[] = {
62
63     { 301, NULL, 0, N_("@Options:\n ") },
64
65     { oVerbose, "verbose",   0, N_("verbose") },
66     { oQuiet,   "quiet",     0, N_("be somewhat more quiet") },
67     { oOptions, "options"  , 2, N_("read options from file")},
68     { oDebug,   "debug"     ,4|16, N_("set debugging flags")},
69     { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
70     { oGPGBinary, "gpg-program", 2 , "" },
71     { oRegServer, "RegServer" , 0, "" },
72     { oUnregServer, "UnregServer" , 0, "" },
73     { oEmbedding, "Embedding" , 0, "" },
74 {0} };
75
76
77
78
79 static const char *
80 my_strusage( int level )
81 {
82     const char *p;
83     switch( level ) {
84       case 11: p = "gpgcom";
85         break;
86       case 13: p = VERSION; break;
87       /*case 17: p = PRINTABLE_OS_NAME; break;*/
88       case 19: p =
89             _("Please report bugs to <gpgme-bugs@gnupg.org>.\n");
90         break;
91       case 1:
92       case 40:  p =
93             _("Usage: gpgcom [options] (-h for help)");
94         break;
95       case 41:  p =
96             _("Syntax: gpgcom [options]\n"
97               "GnuPG COM+ component\n");
98         break;
99
100       default:  p = NULL;
101     }
102     return p;
103 }
104
105
106 int
107 main (int argc, char **argv )
108 {
109     ARGPARSE_ARGS pargs;
110     int orig_argc;
111     char **orig_argv;
112     FILE *configfp = NULL;
113     char *configname = NULL;
114     unsigned configlineno;
115     int parse_debug = 0;
116     int default_config =1;
117     int greeting = 0;
118     int nogreeting = 0;
119     int action = 0;
120
121     set_strusage( my_strusage );
122     /*log_set_name ("gpa"); not yet implemented in logging.c */
123
124     opt.homedir = getenv("GNUPGHOME");
125     if( !opt.homedir || !*opt.homedir ) {
126       #ifdef HAVE_DOSISH_SYSTEM
127         opt.homedir = "c:/gnupg";
128       #else
129         opt.homedir = "~/.gnupg";
130       #endif
131     }
132
133     /* check whether we have a config file on the commandline */
134     orig_argc = argc;
135     orig_argv = argv;
136     pargs.argc = &argc;
137     pargs.argv = &argv;
138     pargs.flags= 1|(1<<6);  /* do not remove the args, ignore version */
139     while( arg_parse( &pargs, opts) ) {
140         if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll )
141             parse_debug++;
142         else if( pargs.r_opt == oOptions ) {
143             /* yes there is one, so we do not try the default one, but
144              * read the option file when it is encountered at the commandline
145              */
146             default_config = 0;
147         }
148         else if( pargs.r_opt == oNoOptions )
149             default_config = 0; /* --no-options */
150         else if( pargs.r_opt == oHomedir )
151             opt.homedir = pargs.r.ret_str;
152     }
153
154     if( default_config )
155         configname = make_filename(opt.homedir, "gpgme.conf", NULL );
156
157
158     argc = orig_argc;
159     argv = orig_argv;
160     pargs.argc = &argc;
161     pargs.argv = &argv;
162     pargs.flags=  1 | (1<<5);  /* do not remove the args, allow one dash */
163   next_pass:
164     if( configname ) {
165         configlineno = 0;
166         configfp = fopen( configname, "r" );
167         if( !configfp ) {
168             if( default_config ) {
169                 if( parse_debug )
170                     log_info(_("NOTE: no default option file `%s'\n"),
171                                                             configname );
172             }
173             else {
174                 log_error(_("option file `%s': %s\n"),
175                                     configname, strerror(errno) );
176                 exit(2);
177             }
178             free(configname); configname = NULL;
179         }
180         if( parse_debug && configname )
181             log_info(_("reading options from `%s'\n"), configname );
182         default_config = 0;
183     }
184
185     while( optfile_parse( configfp, configname, &configlineno,
186                                                 &pargs, opts) ) {
187         switch( pargs.r_opt ) {
188           case oQuiet: opt.quiet = 1; break;
189           case oVerbose: opt.verbose++; break;
190
191           case oDebug: opt.debug |= pargs.r.ret_ulong; break;
192           case oDebugAll: opt.debug = ~0; break;
193
194           case oOptions:
195             /* config files may not be nested (silently ignore them) */
196             if( !configfp ) {
197                 free(configname);
198                 configname = xstrdup(pargs.r.ret_str);
199                 goto next_pass;
200             }
201             break;
202           case oNoGreeting: nogreeting = 1; break;
203           case oNoVerbose: opt.verbose = 0; break;
204           case oNoOptions: break; /* no-options */
205           case oHomedir: opt.homedir = pargs.r.ret_str; break;
206           case oGPGBinary:  break;
207
208           case oRegServer: action = 1; break;
209           case oUnregServer: action = 2; break;
210           case oEmbedding: action = 3; break;
211
212           default : pargs.err = configfp? 1:2; break;
213         }
214     }
215     if( configfp ) {
216         fclose( configfp );
217         configfp = NULL;
218         free(configname); configname = NULL;
219         goto next_pass;
220     }
221     free( configname ); configname = NULL;
222     if( log_get_errorcount(0) )
223         exit(2);
224     if( nogreeting )
225         greeting = 0;
226
227     if( greeting ) {
228         fprintf(stderr, "%s %s; %s\n",
229                         strusage(11), strusage(13), strusage(14) );
230         fprintf(stderr, "%s\n", strusage(15) );
231     }
232   #ifdef IS_DEVELOPMENT_VERSION
233     log_info("NOTE: this is a development version!\n");
234   #endif
235
236     if ( action == 1 )
237         register_server ();
238     else if (action == 2 )
239         unregister_server ();
240     else if (action == 3 )
241         enter_complus ();
242     else {
243         fprintf (stderr, "This is a COM+ component with no user interface.\n"
244                  "gpgme --help will give you a list of options\n" );
245         exit (1);
246     }
247
248     return 0;
249 }
250
251
252 static void
253 register_progid ( const char *name )
254 {
255     HKEY hk = 0;
256     char buf[500];
257
258     /* Create a ProgID entry to point to the ClassID */
259     sprintf (buf, "%.400s", name);
260     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
261         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
262         exit (1);
263     }
264     sprintf (buf, "g10 Code's GnuPG made easy COMponent" );
265     if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, 0)) {
266         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
267         exit (1);
268     }
269     if (RegCloseKey (hk)) {
270         fprintf (stderr,"RegCloseKey() failed\n");
271         exit (1);
272     }
273     sprintf (buf, "%.400s\\CLSID", name);
274     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
275         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
276         exit (1);
277     }
278     sprintf (buf, "%.100s", debugstr_guid (&CLSID_Gpgme) );
279     if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
280         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
281         exit (1);
282     }
283     if (RegCloseKey (hk)) {
284         fprintf (stderr,"RegCloseKey() failed\n");
285         exit (1);
286     }
287     hk = 0;
288 }
289
290
291 static void
292 register_typelib (void)
293 {
294     ITypeLib  *pTypeLib;
295     HRESULT hr;
296     char name[500];
297     wchar_t *wname;
298     size_t n;
299
300     if ( !GetModuleFileNameA (0, name, sizeof (name)-10) ) {
301         fprintf (stderr,"GetModuleFileName() failed: %d\n",
302                  (int)GetLastError());
303         exit (1);
304     }
305     n = mbstowcs (NULL, name, strlen(name)+1);
306     wname = xmalloc ((n+1)*sizeof *wname);
307     mbstowcs (wname, name, strlen (name)+1);
308
309     hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); 
310     if (hr)
311         fprintf (stderr, "CoInitializeEx() failed: hr=%lu\n", hr);
312
313     hr = LoadTypeLibEx (wname, REGKIND_REGISTER, &pTypeLib);
314     if (hr)
315         fprintf (stderr, "LoadTypeLibEx() failed: hr=%lx\n", hr);
316
317     ITypeLib_Release (pTypeLib);
318     CoUninitialize ();
319     free (wname);
320 }
321
322 static void
323 unregister_typelib (void)
324 {
325     UnRegisterTypeLib (&TLBID_Gpgcom, 1, 0, LANG_NEUTRAL, SYS_WIN32);
326 }
327
328 static void
329 register_server ()
330 {
331     HKEY hk = 0;
332     char buf[500];
333
334
335     register_typelib ();
336
337     /* Create a key for the CLSID */
338     sprintf (buf, "CLSID\\%.100s", debugstr_guid (&CLSID_Gpgme) );
339     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
340         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
341         exit (1);
342     }
343     /* Store our class name as default value */
344     strcpy (buf, "Gpgme");
345     if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
346         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
347         exit (1);
348     }
349     
350     /* Set the application ID */
351     sprintf (buf, "%.100s", debugstr_guid (&APPID_Gpgcom) );
352     if (RegSetValueExA (hk, "AppID", 0, REG_SZ, buf, strlen (buf))) {
353         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
354         exit (1);
355     }
356     if (RegCloseKey (hk)) {
357         fprintf (stderr,"RegCloseKey() failed\n");
358         exit (1);
359     }
360     hk = 0;
361     
362     /* Create the LocalServer32 subkey under the CLSID key */
363     sprintf (buf, "CLSID\\%.100s\\LocalServer32",
364              debugstr_guid (&CLSID_Gpgme) );
365     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
366         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
367         exit (1);
368     }
369     /* retrieve the module name and add it under the key */
370     if ( !GetModuleFileNameA (0, buf, sizeof (buf)-10) ) {
371         fprintf (stderr,"GetModuleFileName() failed\n");
372         exit (1);
373     }
374     if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
375         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
376         exit (1);
377     }
378     if (RegCloseKey (hk)) {
379         fprintf (stderr,"RegCloseKey() failed\n");
380         exit (1);
381     }
382     hk = 0;
383
384     /* Create the ProgID subkey under the CLSID key */
385     sprintf (buf, "CLSID\\%.100s\\ProgID",
386              debugstr_guid (&CLSID_Gpgme) );
387     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
388         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
389         exit (1);
390     }
391     if (RegSetValueExA (hk, 0, 0, REG_SZ, "Gpgcom.Gpgme.1", 0)) {
392         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
393         exit (1);
394     }
395     if (RegCloseKey (hk)) {
396         fprintf (stderr,"RegCloseKey() failed\n");
397         exit (1);
398     }
399     hk = 0;
400     /* Create the VersionIndependentProgID subkey under the CLSID key */
401     sprintf (buf, "CLSID\\%.100s\\VersionIndependentProgID",
402              debugstr_guid (&CLSID_Gpgme) );
403     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
404         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
405         exit (1);
406     }
407     if (RegSetValueExA (hk, 0, 0, REG_SZ, "Gpgcom.Gpgme", 0)) {
408         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
409         exit (1);
410     }
411     if (RegCloseKey (hk)) {
412         fprintf (stderr,"RegCloseKey() failed\n");
413         exit (1);
414     }
415     hk = 0;
416
417     
418     /* Create a key to store AppID info */
419     sprintf (buf, "AppID\\%.100s",  debugstr_guid (&APPID_Gpgcom) );
420     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
421         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
422         exit (1);
423     }
424     /* Store the name as default value */
425     strcpy (buf, "Gpgcom");
426     if (RegSetValueExA (hk, 0, 0, REG_SZ, buf, strlen (buf))) {
427         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
428         exit (1);
429     }
430     if (RegCloseKey (hk)) {
431         fprintf (stderr,"RegCloseKey() failed\n");
432         exit (1);
433     }
434     hk = 0;
435
436     register_progid ("Gpgcom.Gpgme");
437     register_progid ("Gpgcom.Gpgme.1");
438
439     /* Create a convenience cross reference to the AppID */
440     sprintf (buf, "AppID\\gpgcom.exe");
441     if (RegCreateKeyA (HKEY_CLASSES_ROOT, buf, &hk)) {
442         fprintf (stderr,"RegCreateKey(`%s') failed\n", buf);
443         exit (1);
444     }
445     sprintf (buf, "%.100s", debugstr_guid (&APPID_Gpgcom) );
446     if (RegSetValueExA (hk, "AppID", 0, REG_SZ, buf, strlen (buf))) {
447         fprintf (stderr,"RegSetValueEx(`%s') failed\n", buf);
448         exit (1);
449     }
450     if (RegCloseKey (hk)) {
451         fprintf (stderr,"RegCloseKey() failed\n");
452         exit (1);
453     }
454     hk = 0;
455
456     fprintf (stderr,"*** Component registered\n");
457 }
458
459 static void
460 unregister_server ()
461 {
462     char buf[500];
463
464     unregister_typelib ();
465     sprintf (buf, "CLSID\\%.100s\\LocalServer32",
466              debugstr_guid (&CLSID_Gpgme) );
467     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
468         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
469
470     sprintf (buf, "CLSID\\%.100s\\ProgID",  debugstr_guid (&CLSID_Gpgme) );
471     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
472         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
473
474     sprintf (buf, "CLSID\\%.100s", debugstr_guid (&CLSID_Gpgme) );
475     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
476         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
477
478     sprintf (buf, "Gpgcom.Gpgme.1\\CLSID");
479     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
480         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
481     sprintf (buf, "Gpgcom.Gpgme.1");
482     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
483         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
484
485     sprintf (buf, "Gpgcom.Gpgme\\CLSID");
486     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
487         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
488     sprintf (buf, "Gpgcom.Gpgme");
489     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
490         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
491
492
493     sprintf (buf, "AppID\\%.100s", debugstr_guid (&APPID_Gpgcom) );
494     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
495         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
496
497     sprintf (buf, "AppID\\gpgcom.exe" );
498     if (RegDeleteKey (HKEY_CLASSES_ROOT, buf))
499         fprintf (stderr,"RegDeleteKey(`%s') failed\n", buf);
500
501     fprintf (stderr,"*** component unregistered\n");
502 }
503
504
505 static void
506 enter_complus ()
507 {
508     HANDLE running;
509     DWORD reg;
510     IClassFactory *factory;
511     CLSID clsid;
512     HRESULT hr;
513
514     fprintf (stderr,"*** enter enter_complus()\n");
515     CoInitializeEx (NULL, COINIT_MULTITHREADED); 
516     running = CreateEvent (NULL, FALSE, FALSE, NULL );
517     fprintf (stderr,"*** CoInitialize() done; event=%lx\n", (unsigned long)running );
518
519     igpgme_register_exit_event (running);
520     factory = igpgme_factory_new ( &clsid ); 
521     fprintf (stderr,"*** igpgme_factory_new() done; got=%p\n", factory );
522     hr = CoRegisterClassObject (&clsid, (IUnknown*)factory, 
523                            CLSCTX_LOCAL_SERVER,
524                            REGCLS_SUSPENDED|REGCLS_MULTIPLEUSE, &reg );
525     if (hr) {
526         fprintf (stderr, "CoRegisterClassObject() failed: hr=%lx\n", hr);
527         exit (1);
528     }
529     hr = CoResumeClassObjects ();
530     if (hr) 
531         fprintf (stderr, "CoRegisterClassObject() failed: hr=%lx\n", hr);
532     fprintf (stderr,"*** class object registered; waiting\n" );
533
534     WaitForSingleObject ( running, INFINITE );
535     fprintf (stderr,"*** shutting down\n" );
536     igpgme_register_exit_event (NULL);
537     CloseHandle (running);
538     CoRevokeClassObject ( reg );
539     fprintf (stderr,"*** class object revoked\n" );
540     igpgme_factory_release (factory);
541     fprintf (stderr,"*** factory released\n" );
542     CoUninitialize (); 
543     fprintf (stderr,"*** leave enter_complus()\n" );
544 }
545