summaryrefslogtreecommitdiffstats
path: root/agent/w32main.c
blob: d907fe597c1343985c273b1707cfb2f354fda968 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/* w32main.c - W32 main entry pint and taskbar support for the GnuPG Agent
 * Copyright (C) 2007 Free Software Foundation, Inc.
 * Copyright 1996, 1998 Alexandre Julliard
 *
 * This file is part of GnuPG.
 *
 * GnuPG 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 3 of the License, or
 * (at your option) any later version.
 *
 * GnuPG 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, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#ifndef HAVE_W32_SYSTEM
#error This module is only useful for the W32 version of gpg-agent
#endif

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <windows.h>

#include "util.h"
#include "w32main.h"

/* The instance handle has received by WinMain.  */
static HINSTANCE glob_hinst;
static HWND glob_hwnd;


/* Build an argv array from the command in CMDLINE.  RESERVED is the
   number of args to reserve before the first one.  This code is based
   on Alexandre Julliard's LGPLed wine-0.9.34/dlls/kernel32/process.c
   and modified to fit into our framework.  The function returns NULL
   on error; on success an arry with the argiments is returned.  This
   array has been allocaqted using a plain malloc (and not the usual
   xtrymalloc). */
static char **
build_argv (char *cmdline_arg, int reserved)
{
  int argc;
  char **argv;
  char *cmdline, *s, *arg, *d;
  int in_quotes, bs_count;

  cmdline = malloc (strlen (cmdline_arg) + 1);
  if (!cmdline)
    return NULL;
  strcpy (cmdline, cmdline_arg);

  /* First determine the required size of the array.  */
  argc = reserved + 1;
  bs_count = 0;
  in_quotes = 0;
  s = cmdline;
  for (;;)
    {
      if ( !*s || ((*s==' ' || *s=='\t') && !in_quotes)) /* A space.  */
        {
          argc++;
          /* Skip the remaining spaces.  */
          while (*s==' ' || *s=='\t')
            s++;
          if (!*s)
            break;
          bs_count = 0;
        }
      else if (*s=='\\')
        {
          bs_count++;
          s++;
        }
      else if ( (*s == '\"') && !(bs_count & 1))
        {
          /* Unescaped '\"' */
          in_quotes = !in_quotes;
          bs_count=0;
          s++;
        }
      else /* A regular character. */
        {
          bs_count = 0;
          s++;
        }
    }

  argv = xtrymalloc (argc * sizeof *argv);
  if (!argv)
    {
      xfree (cmdline);
      return NULL;
    }

  /* Now actually parse the command line.  */
  argc = reserved;
  bs_count = 0;
  in_quotes=0;
  arg = d = s = cmdline;
  while (*s)
    {
      if ((*s==' ' || *s=='\t') && !in_quotes)
        {
          /* Close the argument and copy it. */
          *d = 0;
          argv[argc++] = arg;

          /* Skip the remaining spaces. */
          do
            s++;
          while (*s==' ' || *s=='\t');

          /* Start with a new argument */
          arg = d = s;
          bs_count = 0;
        }
      else if (*s=='\\')
        {
          *d++ = *s++;
          bs_count++;
        }
      else if (*s=='\"')
        {
          if ( !(bs_count & 1) )
            {
              /* Preceded by an even number of backslashes, this is
                 half that number of backslashes, plus a '\"' which we
                 discard.  */
              d -= bs_count/2;
              s++;
              in_quotes = !in_quotes;
            }
          else
            {
              /* Preceded by an odd number of backslashes, this is
                 half that number of backslashes followed by a '\"'.  */
              d = d - bs_count/2 - 1;
              *d++ ='\"';
              s++;
            }
          bs_count=0;
        }
      else /* A regular character. */
        {
          *d++ = *s++;
          bs_count = 0;
        }
    }

  if (*arg)
    {
      *d = 0;
      argv[argc++] = arg;
    }
  argv[argc] = NULL;

  return argv;
}



/* Our window message processing function.  */
static LRESULT CALLBACK
wndw_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{

  switch (msg)
    {
    case WM_USER:
      fprintf (stderr,"%s: received WM_%s\n", __func__, "USER" );
      break;

    }

  return DefWindowProc (hwnd, msg, wparam, lparam);
}


/* This function is called to do some fast event polling and
   processing.  */
void
w32_poll_events (void)
{
/*   MSG msg; */

/*   fprintf (stderr,"%s: enter\n", __func__); */
/*   while (PeekMessage (&msg, glob_hwnd,  0, 0, PM_REMOVE))  */
/*     {  */
/*       DispatchMessage (&msg); */
/*     } */
/*   fprintf (stderr,"%s: leave\n", __func__); */
}



static void *
handle_taskbar (void *ctx)
{
  WNDCLASS wndwclass = {0, wndw_proc, 0, 0, glob_hinst,
                        0, 0, 0, 0, "gpg-agent"};
  NOTIFYICONDATA nid;
  HWND hwnd;
  MSG msg;
  int rc;

  if (!RegisterClass (&wndwclass))
    {
      log_error ("error registering window class\n");
      ExitThread (0);
    }
  hwnd = CreateWindow ("gpg-agent", "gpg-agent",
                       0, 0, 0, 0, 0,
                       NULL, NULL, glob_hinst, NULL);
  if (!hwnd)
    {
      log_error ("error creating main window\n");
      ExitThread (0);
    }
  glob_hwnd = hwnd;
  UpdateWindow (hwnd);

  memset (&nid, 0, sizeof nid);
  nid.cbSize = sizeof (nid);
  nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  nid.uCallbackMessage = WM_USER;
  nid.hWnd = glob_hwnd;
  nid.uID = 1;
  nid.hIcon = LoadIcon (glob_hinst, MAKEINTRESOURCE (1));
  mem2str (nid.szTip, GPG_AGENT_NAME " version "PACKAGE_VERSION,
           sizeof nid.szTip);
  Shell_NotifyIcon (NIM_ADD, &nid);
  DestroyIcon (nid.hIcon);

  fprintf (stderr, "%s: enter\n", __func__);
  while ( (rc=GetMessage (&msg, hwnd,  0, 0)) )
    {
      if (rc == -1)
        {
          log_error ("getMessage failed: %s\n", w32_strerror (-1));
          break;
        }
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
  fprintf (stderr,"%s: leave\n", __func__);
  ExitThread (0);
  return NULL;
}



/* This function initializes the Window system and sets up the taskbar
   icon.  We only have very limited GUI support just to give the
   taskbar icon a little bit of life.  This function is called once to
   fire up the icon.  */
int
w32_setup_taskbar (void)
{
  SECURITY_ATTRIBUTES sa;
  DWORD tid;
  HANDLE th;

  memset (&sa, 0, sizeof sa);
  sa.nLength = sizeof sa;
  sa.bInheritHandle = FALSE;

  fprintf (stderr,"creating thread for the taskbar_event_loop...\n");
  th = CreateThread (&sa, 128*1024,
                     (LPTHREAD_START_ROUTINE)handle_taskbar,
                     NULL, 0, &tid);
  fprintf (stderr,"created thread %p tid=%d\n", th, (int)tid);

  CloseHandle (th);

  return 0;
}


/* The main entry point for the Windows version.  We save away all GUI
   related stuff, parse the command line and finally call the real
   main.  */
int WINAPI
WinMain (HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
{
  char **argv;
  int argc;

  /* We use the GetCommandLine function because that also includes the
     program name in contrast to the CMDLINE arg. */
  argv = build_argv (GetCommandLineA (), 0);
  if (!argv)
    return 2; /* Can't do much about a malloc failure.  */
  for (argc=0; argv[argc]; argc++)
    ;

  glob_hinst = hinst;

  return w32_main (argc, argv);
}