Made percent_escape more general.
[gnupg.git] / agent / w32main.c
1 /* w32main.c - W32 main entry pint and taskbar support for the GnuPG Agent
2  * Copyright (C) 2007 Free Software Foundation, Inc.
3  * Copyright 1996, 1998 Alexandre Julliard
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20  * USA.
21  */
22
23 #include <config.h>
24 #ifndef HAVE_W32_SYSTEM
25 #error This module is only useful for the W32 version of gpg-agent
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <windows.h>
32
33 #include "util.h"
34 #include "w32main.h"
35
36 /* The instance handle has received by WinMain.  */
37 static HINSTANCE glob_hinst;
38 static HWND glob_hwnd;
39
40
41 /* Build an argv array from the command in CMDLINE.  RESERVED is the
42    number of args to reserve before the first one.  This code is based
43    on Alexandre Julliard's LGPLed wine-0.9.34/dlls/kernel32/process.c
44    and modified to fit into our framework.  The function returns NULL
45    on error; on success an arry with the argiments is returned.  This
46    array has been allocaqted using a plain malloc (and not the usual
47    xtrymalloc). */
48 static char **
49 build_argv (char *cmdline_arg, int reserved)
50 {
51   int argc;
52   char **argv;
53   char *cmdline, *s, *arg, *d;
54   int in_quotes, bs_count;
55
56   cmdline = malloc (strlen (cmdline_arg) + 1);
57   if (!cmdline)
58     return NULL;
59   strcpy (cmdline, cmdline_arg);
60
61   /* First determine the required size of the array.  */
62   argc = reserved + 1;
63   bs_count = 0;
64   in_quotes = 0;
65   s = cmdline;
66   for (;;)
67     {
68       if ( !*s || ((*s==' ' || *s=='\t') && !in_quotes)) /* A space.  */
69         {
70           argc++;
71           /* Skip the remaining spaces.  */
72           while (*s==' ' || *s=='\t') 
73             s++;
74           if (!*s)
75             break;
76           bs_count = 0;
77         } 
78       else if (*s=='\\')
79         {
80           bs_count++;
81           s++;
82         }
83       else if ( (*s == '\"') && !(bs_count & 1))
84         {
85           /* Unescaped '\"' */
86           in_quotes = !in_quotes;
87           bs_count=0;
88           s++;
89         } 
90       else /* A regular character. */
91         {
92           bs_count = 0;
93           s++;
94         }
95     }
96
97   argv = malloc (argc * sizeof *argv);
98   if (!argv)
99     {
100       free (cmdline);
101       return NULL;
102     }
103
104   /* Now actually parse the command line.  */
105   argc = reserved;
106   bs_count = 0;
107   in_quotes=0;
108   arg = d = s = cmdline;
109   while (*s)
110     {
111       if ((*s==' ' || *s=='\t') && !in_quotes)
112         {
113           /* Close the argument and copy it. */
114           *d = 0;
115           argv[argc++] = arg;
116
117           /* Skip the remaining spaces. */
118           do 
119             s++;
120           while (*s==' ' || *s=='\t');
121
122           /* Start with a new argument */
123           arg = d = s;
124           bs_count = 0;
125         } 
126       else if (*s=='\\') 
127         {
128           *d++ = *s++;
129           bs_count++;
130         } 
131       else if (*s=='\"') 
132         {
133           if ( !(bs_count & 1) )
134             {
135               /* Preceded by an even number of backslashes, this is
136                  half that number of backslashes, plus a '\"' which we
137                  discard.  */
138               d -= bs_count/2;
139               s++;
140               in_quotes = !in_quotes;
141             }
142           else 
143             {
144               /* Preceded by an odd number of backslashes, this is
145                  half that number of backslashes followed by a '\"'.  */
146               d = d - bs_count/2 - 1;
147               *d++ ='\"';
148               s++;
149             }
150           bs_count=0;
151         } 
152       else /* A regular character. */
153         {
154           *d++ = *s++;
155           bs_count = 0;
156         }
157     }
158
159   if (*arg)
160     {
161       *d = 0;
162       argv[argc++] = arg;
163     }
164   argv[argc] = NULL;
165
166   return argv;
167 }
168
169
170
171 /* Our window message processing function.  */
172 static LRESULT CALLBACK 
173 wndw_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
174 {               
175
176   switch (msg)
177     {
178     case WM_USER:
179       fprintf (stderr,"%s: received WM_%s\n", __func__, "USER" );
180       break;
181
182     }
183
184   return DefWindowProc (hwnd, msg, wparam, lparam);
185 }
186
187
188 /* This function is called to do some fast event polling and
189    processing.  */
190 void
191 w32_poll_events (void)
192 {
193 /*   MSG msg; */
194
195 /*   fprintf (stderr,"%s: enter\n", __func__); */
196 /*   while (PeekMessage (&msg, glob_hwnd,  0, 0, PM_REMOVE))  */
197 /*     {  */
198 /*       DispatchMessage (&msg); */
199 /*     } */
200 /*   fprintf (stderr,"%s: leave\n", __func__); */
201 }
202
203
204
205 static void *
206 handle_taskbar (void *ctx)
207 {
208   WNDCLASS wndwclass = {0, wndw_proc, 0, 0, glob_hinst,
209                         0, 0, 0, 0, "gpg-agent"};
210   NOTIFYICONDATA nid;
211   HWND hwnd;
212   MSG msg;
213   int rc;
214
215   if (!RegisterClass (&wndwclass))
216     {
217       log_error ("error registering window class\n");
218       ExitThread (0);
219     }
220   hwnd = CreateWindow ("gpg-agent", "gpg-agent",
221                        0, 0, 0, 0, 0,
222                        NULL, NULL, glob_hinst, NULL);
223   if (!hwnd)
224     {
225       log_error ("error creating main window\n");
226       ExitThread (0);
227     }
228   glob_hwnd = hwnd;
229   UpdateWindow (hwnd);
230
231   memset (&nid, 0, sizeof nid);
232   nid.cbSize = sizeof (nid);
233   nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
234   nid.uCallbackMessage = WM_USER;
235   nid.hWnd = glob_hwnd;
236   nid.uID = 1;
237   nid.hIcon = LoadIcon (glob_hinst, MAKEINTRESOURCE (1));
238   mem2str (nid.szTip, "GnuPG Agent version "PACKAGE_VERSION,
239            sizeof nid.szTip);
240   Shell_NotifyIcon (NIM_ADD, &nid);
241   DestroyIcon (nid.hIcon);
242
243   fprintf (stderr, "%s: enter\n", __func__);
244   while ( (rc=GetMessage (&msg, hwnd,  0, 0)) ) 
245     { 
246       if (rc == -1)
247         {
248           log_error ("getMessage failed: %s\n", w32_strerror (-1));
249           break;
250         }
251       TranslateMessage (&msg);
252       DispatchMessage (&msg);
253     }
254   fprintf (stderr,"%s: leave\n", __func__);
255   ExitThread (0);
256   return NULL;
257 }
258
259
260
261 /* This function initializes the Window system and sets up the taskbar
262    icon.  We only have very limited GUI support just to give the
263    taskbar icon a little bit of life.  This fucntion is called once to
264    fire up the icon.  */
265 int
266 w32_setup_taskbar (void)
267 {
268   SECURITY_ATTRIBUTES sa;
269   DWORD tid;
270   HANDLE th;
271
272   memset (&sa, 0, sizeof sa);
273   sa.nLength = sizeof sa;
274   sa.bInheritHandle = FALSE;
275
276   fprintf (stderr,"creating thread for the taskbar_event_loop...\n");
277   th = CreateThread (&sa, 128*1024,
278                      (LPTHREAD_START_ROUTINE)handle_taskbar,
279                      NULL, 0, &tid);
280   fprintf (stderr,"created thread %p tid=%d\n", th, (int)tid);
281
282   CloseHandle (th);
283
284   return 0;
285 }
286
287
288 /* The main entry point for the Windows version.  We save away all GUI
289    related stuff, parse the command line and finally call the real
290    main.  */
291 int WINAPI
292 WinMain (HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
293 {
294   char **argv;
295   int argc;
296
297   /* We use the GetCommandLine function because that also includes the
298      program name in contrast to the CMDLINE arg. */
299   argv = build_argv (GetCommandLineA (), 0);
300   if (!argv)
301     return 2; /* Can't do much about a malloc failure.  */
302   for (argc=0; argv[argc]; argc++)
303     ;
304
305   glob_hinst = hinst;
306
307   return w32_main (argc, argv);
308 }