~leighman/ubuntu/raring/workrave/fix-desktop-file-2

« back to all changes in this revision

Viewing changes to backend/src/win32/W32ActiveSetup.cc

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2013-01-24 11:04:41 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: package-import@ubuntu.com-20130124110441-t06q3xlc7mp4406p
Tags: upstream-1.10
ImportĀ upstreamĀ versionĀ 1.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// W32ActiveSetup.cc --- Active Setup for Workrave
 
2
//
 
3
// Copyright (C) 2012 Ray Satiro
 
4
// All rights reserved.
 
5
//
 
6
// This program 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 3 of the License, or
 
9
// (at your option) any later version.
 
10
//
 
11
// This program 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, see <http://www.gnu.org/licenses/>.
 
18
//
 
19
 
 
20
/* Active Setup is post install data that's set by the Inno Setup installer.
 
21
 
 
22
After Active Setup data is set it will be run for each user the next time that user logs on.
 
23
However, in the case that the user hasn't yet logged off and then back on, any programs specified 
 
24
by Active Setup have not yet ran.
 
25
 
 
26
In order to ensure the proper behavior of Workrave, Workrave's Active Setup key(s) should always be 
 
27
checked before Workrave loads, and run if they haven't already. This module emulates the behavior 
 
28
of Microsoft's Active Setup.
 
29
 
 
30
##############################################WARNING##############################################
 
31
Active Setup is undocumented and can be dangerous if not used properly. Its behavior is blocking 
 
32
and there is no interaction available to the user in a typical case. In a typical case it will 
 
33
block the explorer shell, with explorer running the Active Setup programs hidden from view and 
 
34
waiting for them to complete before completing user initialization on logon. This emulation is very 
 
35
similar. It was designed for the Workrave application to block before continuing execution until 
 
36
Workrave's Active Setup programs have run and terminated. Therefore, it is extremely important that 
 
37
any program put under the purview of Active Setup does not require any user interaction, does not 
 
38
have any error message boxes, and has all its dependencies (so there are no message boxes about 
 
39
failure to load libraries). The Workrave Active Setup program (I've only written one so far -- to 
 
40
change a user's autorun settings) takes an extra step, to void user interaction during any 
 
41
potential crash.
 
42
*/
 
43
 
 
44
#ifdef HAVE_CONFIG_H
 
45
#include "config.h"
 
46
#endif
 
47
 
 
48
#include "debug.hh"
 
49
 
 
50
#include <windows.h>
 
51
#include <tchar.h>
 
52
#include <cstdlib>
 
53
#include <vector>
 
54
#include <sstream>
 
55
#include <iterator>
 
56
#include <string>
 
57
 
 
58
#include "W32ActiveSetup.hh"
 
59
 
 
60
using namespace std;
 
61
 
 
62
 
 
63
 
 
64
/* using 32-bit registry view on 32-bit OS, 64-bit registry view on 64-bit OS
 
65
http://support.microsoft.com/kb/907660
 
66
*/
 
67
const REGSAM W32ActiveSetup::registry_view = ( W32ActiveSetup::is_os_64() ? KEY_WOW64_64KEY : KEY_WOW64_32KEY );
 
68
 
 
69
/* The subkey path to HKLM/HKCU Active Setup GUIDs */
 
70
const wchar_t W32ActiveSetup::component_path[] = L"SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\";
 
71
 
 
72
/* Right now there is only one GUID key set by Inno and it's used by Workrave's 'ChangeAutorun' 
 
73
program, which adds/updates each user's Workrave autorun setting.
 
74
*/
 
75
const wchar_t W32ActiveSetup::guid_autorun[] = L"{180B0AC5-6FDA-438B-9466-C9894322B6BA}";
 
76
 
 
77
 
 
78
 
 
79
/* W32ActiveSetup::is_os_64()
 
80
 
 
81
returns true if the windows operating system is 64-bit
 
82
*/
 
83
bool W32ActiveSetup::is_os_64()
 
84
{
 
85
    SYSTEM_INFO si = { 0, };
 
86
 
 
87
    GetNativeSystemInfo( &si );
 
88
    
 
89
    return( ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
 
90
        || ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
 
91
        );
 
92
}
 
93
 
 
94
 
 
95
 
 
96
/* W32ActiveSetup::get_user_profile_dir()
 
97
 
 
98
returns the path contained in environment variable %USERPROFILE%
 
99
*/
 
100
const WCHAR *W32ActiveSetup::get_user_profile_dir()
 
101
{
 
102
    static vector<WCHAR> buffer;
 
103
 
 
104
    if( buffer.size() && *buffer.begin() )
 
105
        return &buffer[ 0 ];
 
106
    
 
107
    WCHAR *env_var = L"USERPROFILE";
 
108
 
 
109
    DWORD ret = GetEnvironmentVariableW( env_var, NULL, 0 );
 
110
    if( !ret || ( ret > 32767 ) )
 
111
        return NULL;
 
112
 
 
113
    buffer.resize( ret );
 
114
 
 
115
    ret  = GetEnvironmentVariableW( env_var, &buffer[ 0 ], buffer.size() );
 
116
    if( !ret || ( ret >= buffer.size() ) || !*buffer.begin() )
 
117
        return NULL;
 
118
 
 
119
    return &buffer[ 0 ];
 
120
}
 
121
 
 
122
 
 
123
 
 
124
/* W32ActiveSetup::check_guid()
 
125
 
 
126
Check for the existence of an Active Setup GUID key (HKLM or HKCU).
 
127
 
 
128
returns true if the key exists
 
129
*/
 
130
bool W32ActiveSetup::check_guid( 
 
131
    const enum reg reg, // HKLM or HKCU
 
132
    const wstring &guid
 
133
    )
 
134
{
 
135
    HKEY root_hkey = NULL;
 
136
 
 
137
    switch( reg )
 
138
    {
 
139
    case HKLM:
 
140
        root_hkey = HKEY_LOCAL_MACHINE;
 
141
        break;
 
142
    case HKCU:
 
143
        root_hkey = HKEY_CURRENT_USER;
 
144
        break;
 
145
    default:
 
146
        return false;
 
147
    }
 
148
 
 
149
    wstring keyname( W32ActiveSetup::component_path );
 
150
    keyname += guid;
 
151
 
 
152
    HKEY hkey = NULL;
 
153
    LONG ret = RegOpenKeyExW(
 
154
        root_hkey,
 
155
        keyname.c_str(),
 
156
        0,
 
157
        ( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
 
158
        &hkey
 
159
        );
 
160
 
 
161
    if( ret )
 
162
        return false;
 
163
 
 
164
    CloseHandle( hkey );
 
165
    return true;
 
166
}
 
167
 
 
168
 
 
169
 
 
170
/* W32ActiveSetup::is_guid_enabled()
 
171
 
 
172
returns true if the HKLM Active Setup GUID key exists and the key value "IsInstalled" either does 
 
173
not exist or does exist and does not have both a REG_DWORD type and data 0.
 
174
 
 
175
Those conditions are based on the my testing in Windows XP SP3 x86. In most cases for GUID keys 
 
176
"IsInstalled" exists with a REG_DWORD type and data 1, in which case this function returns true.
 
177
*/
 
178
bool W32ActiveSetup::is_guid_enabled(
 
179
    const wstring &guid
 
180
    )
 
181
{
 
182
    if( guid.empty() )
 
183
        return false;
 
184
 
 
185
    wstring keyname( W32ActiveSetup::component_path );
 
186
    keyname += guid;
 
187
 
 
188
    HKEY hkey = NULL;
 
189
    LONG ret = RegOpenKeyExW(
 
190
        HKEY_LOCAL_MACHINE,
 
191
        keyname.c_str(),
 
192
        0,
 
193
        ( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
 
194
        &hkey
 
195
        );
 
196
 
 
197
    if( ret )
 
198
        return false;
 
199
 
 
200
    DWORD type = REG_NONE;
 
201
    DWORD data = 0;
 
202
    DWORD bytelen = sizeof( data );
 
203
    ret = RegQueryValueExW(
 
204
        hkey,
 
205
        L"IsInstalled",
 
206
        NULL,
 
207
        &type,
 
208
        (LPBYTE)&data,
 
209
        &bytelen
 
210
        );
 
211
 
 
212
    CloseHandle( hkey );
 
213
 
 
214
    if( !ret
 
215
        && ( type == REG_DWORD )
 
216
        && ( bytelen == sizeof( data ) )
 
217
        && !data
 
218
        )
 
219
        return false;
 
220
 
 
221
    return true;
 
222
}
 
223
 
 
224
 
 
225
 
 
226
/* W32ActiveSetup::read_from_registry_value()
 
227
 
 
228
Get the REG_SZ data from a registry value in an Active Setup GUID key (HKLM or HKCU).
 
229
 
 
230
Before this function does anything else 'data' is cleared. 'data' may be empty even on success.
 
231
 
 
232
returns true if the value exists in the registry. 'data' receives the value's data, if any.
 
233
*/
 
234
bool W32ActiveSetup::read_from_registry_value( 
 
235
    const enum reg reg, // HKLM or HKCU
 
236
    const wstring &guid,
 
237
    const wstring &value,
 
238
    wstring &data // out
 
239
    )
 
240
{
 
241
    data.clear();
 
242
 
 
243
    if( guid.empty() || value.empty() )
 
244
        return false;
 
245
 
 
246
    HKEY root_hkey = NULL;
 
247
 
 
248
    switch( reg )
 
249
    {
 
250
    case HKLM:
 
251
        root_hkey = HKEY_LOCAL_MACHINE;
 
252
        break;
 
253
    case HKCU:
 
254
        root_hkey = HKEY_CURRENT_USER;
 
255
        break;
 
256
    default:
 
257
        return false;
 
258
    }
 
259
 
 
260
    wstring keyname( W32ActiveSetup::component_path );
 
261
    keyname += guid;
 
262
 
 
263
    HKEY hkey = NULL;
 
264
    LONG ret = RegOpenKeyExW(
 
265
        root_hkey,
 
266
        keyname.c_str(),
 
267
        0,
 
268
        ( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
 
269
        &hkey
 
270
        );
 
271
 
 
272
    if( ret )
 
273
        return false;
 
274
 
 
275
    DWORD type = REG_NONE, bytelen = 0;
 
276
    ret = RegQueryValueExW(
 
277
        hkey,
 
278
        value.c_str(),
 
279
        NULL,
 
280
        &type,
 
281
        NULL,
 
282
        &bytelen
 
283
        );
 
284
 
 
285
    if( ret || !bytelen || ( type != REG_SZ ) )
 
286
    {
 
287
        CloseHandle( hkey );
 
288
        return false;
 
289
    }
 
290
 
 
291
    vector<BYTE> buffer( bytelen + sizeof( wchar_t ) );
 
292
 
 
293
    ret = RegQueryValueExW(
 
294
        hkey,
 
295
        value.c_str(),
 
296
        NULL,
 
297
        &type,
 
298
        &buffer[ 0 ],
 
299
        &bytelen
 
300
        );
 
301
 
 
302
    CloseHandle( hkey );
 
303
 
 
304
    if( ret || ( type != REG_SZ ) )
 
305
        return false;
 
306
 
 
307
    data = (wchar_t *)&buffer[ 0 ];
 
308
    return true;
 
309
}
 
310
 
 
311
 
 
312
 
 
313
/* W32ActiveSetup::write_to_registry_value()
 
314
 
 
315
Set a value and its REG_SZ data in an Active Setup GUID key (HKCU only).
 
316
 
 
317
If the HKCU Active Setup GUID key does not exist this function will create it.
 
318
If 'value' is empty this function sets the GUID key's default value.
 
319
'data' can also be an empty string.
 
320
 
 
321
returns true if the value and its data (if any) were set in the registry.
 
322
*/
 
323
bool W32ActiveSetup::write_to_registry_value(
 
324
    const wstring &guid,
 
325
    const wstring &value,
 
326
    const wstring &data
 
327
    )
 
328
{
 
329
    if( guid.empty() )
 
330
        return false;
 
331
 
 
332
    wstring keyname( W32ActiveSetup::component_path );
 
333
    keyname += guid;
 
334
 
 
335
    HKEY hkey = NULL;
 
336
 
 
337
    LONG ret = RegCreateKeyExW(
 
338
        HKEY_CURRENT_USER,
 
339
        keyname.c_str(),
 
340
        0,
 
341
        NULL,
 
342
        0,
 
343
        ( KEY_READ | KEY_WRITE | W32ActiveSetup::registry_view ),
 
344
        NULL,
 
345
        &hkey,
 
346
        NULL
 
347
        );
 
348
        
 
349
    if( ret )
 
350
        return false;
 
351
 
 
352
    ret = RegSetValueExW(
 
353
        hkey,
 
354
        value.c_str(),
 
355
        0,
 
356
        REG_SZ,
 
357
        (const BYTE *)data.c_str(),
 
358
        ( ( data.length() + 1 ) * sizeof( wchar_t ) )
 
359
    );
 
360
 
 
361
    CloseHandle( hkey );
 
362
 
 
363
    return !ret;
 
364
}
 
365
 
 
366
 
 
367
 
 
368
/* W32ActiveSetup::get_version()
 
369
Converts the REG_SZ data from value "Version" in an Active Setup GUID key (HKLM or HKCU) into a 
 
370
vector of 4 DWORDs.
 
371
 
 
372
Active Setup observations:
 
373
A valid "Version" value string of a GUID key has four parts or less, separated by comma. If a 
 
374
number is greater than dword max the version is still valid, and that number is wrapped around.
 
375
 
 
376
"2012,05,10,023701" returns DWORDs 2012, 5, 10, 23701
 
377
"1" returns DWORDs 1, 0, 0, 0
 
378
"" or "0" returns DWORDs 0, 0, 0, 0
 
379
"1,2,3,4,5" (invalid) returns DWORDs 0, 0, 0, 0
 
380
"25,4294967299,77,4" returns DWORDs 25, 3, 77, 4
 
381
 
 
382
Before this function does anything else 'version' is cleared and then resized to 4 DWORDs.
 
383
 
 
384
returns true if the "Version" value exists in the registry and it's valid. 'version' receives the 
 
385
version as a vector of 4 DWORDs.
 
386
*/
 
387
bool W32ActiveSetup::get_version(
 
388
    const enum reg reg, // HKLM or HKCU
 
389
    const wstring &guid,
 
390
    vector<DWORD> &version // out
 
391
    )
 
392
{
 
393
    version.clear();
 
394
    version.resize( 4, 0 );
 
395
 
 
396
    wstring data;
 
397
    if( !read_from_registry_value( reg, guid, L"Version", data ) )
 
398
        return false;
 
399
    
 
400
    /* testing shows anything other than these characters invalidates the version string.
 
401
    Active Setup treats it the same as a version where all parts are zeroes
 
402
    */
 
403
    size_t pos = data.find_first_not_of( L"0123456789," );
 
404
    if( pos != wstring::npos )
 
405
        return false;
 
406
 
 
407
    /* testing shows if there is more than 3 commas that invalidates the version string.
 
408
    Active Setup treats it the same as a version where all parts are zeroes
 
409
    */
 
410
    for( unsigned i = 0, count = 0; i < data.length(); ++i )
 
411
    {
 
412
        if( data[ i ] == L',' )
 
413
        {
 
414
            ++count;
 
415
            if( count > 3 )
 
416
                return false;
 
417
        }
 
418
    }
 
419
 
 
420
    wstringstream ss( data );
 
421
 
 
422
    for( unsigned i = 0; i < 4; ++i )
 
423
    {
 
424
        wstring s;
 
425
 
 
426
        if( !getline( ss, s, L',' ) )
 
427
            break;
 
428
 
 
429
        for( unsigned j = 0; j < s.length(); ++j )
 
430
        {
 
431
            version[ i ] *= 10;
 
432
            version[ i ] += s[ j ] - 48;
 
433
        }
 
434
    }
 
435
 
 
436
    return true;
 
437
}
 
438
 
 
439
 
 
440
 
 
441
/* W32ActiveSetup::set_version()
 
442
 
 
443
Converts a vector of 4 DWORDs to a comma separated string of unsigned numbers and sets it as the 
 
444
REG_SZ data for the "Version" value of an Active Setup GUID key.
 
445
 
 
446
returns true on success
 
447
*/
 
448
bool W32ActiveSetup::set_version(
 
449
    const wstring &guid,
 
450
    const vector<DWORD> &version
 
451
    )
 
452
{
 
453
    if( version.size() != 4 )
 
454
        return false;
 
455
 
 
456
    wstringstream ss;
 
457
    ss << version[ 0 ] << "," << version[ 1 ] << "," << version[ 2 ] << "," << version[ 3 ];
 
458
    
 
459
    return write_to_registry_value( guid, L"Version", ss.str() );
 
460
}
 
461
 
 
462
 
 
463
 
 
464
/* W32ActiveSetup::update()
 
465
 
 
466
This function emulates Active Setup behavior. It should be called synchronously by the main thread 
 
467
before Workrave is initialized.
 
468
 
 
469
The way Active Setup basically works is it checks the HKLM GUID key for a "Version" REG_SZ value 
 
470
and compares it to the same HKCU GUID key's "Version" value. If the HKCU GUID key does not exist, 
 
471
or its "Version" value does not exist, or its version is less than the HKLM version then Active 
 
472
Setup will add/update the HKCU GUID key by copying any HKLM "Version" value to the HKCU "Version" 
 
473
value, and then execute the string contained in HKLM GUID key's "StubPath" value, if any.
 
474
http://www.sepago.de/helge/2010/04/22/active-setup-explained/
 
475
 
 
476
returns true if the HKLM Active Setup GUID key needed to be added/updated in HKCU Active Setup.
 
477
*/
 
478
bool W32ActiveSetup::update(
 
479
    const wstring &guid
 
480
    )
 
481
{
 
482
    if( guid.empty()
 
483
        || !check_guid( HKLM, guid )
 
484
        || !is_guid_enabled( guid )
 
485
        )
 
486
        return false;
 
487
 
 
488
    vector<DWORD> hklm_version;
 
489
    get_version( HKLM, guid, hklm_version );
 
490
    
 
491
    if( check_guid( HKCU, guid ) )
 
492
    {
 
493
        vector<DWORD> hkcu_version;
 
494
        get_version( HKCU, guid, hkcu_version );
 
495
 
 
496
        if( hkcu_version >= hklm_version )
 
497
            return false;
 
498
    }
 
499
 
 
500
    if( !set_version( guid, hklm_version ) )
 
501
        return false;
 
502
 
 
503
    // At this point HKCU Active Setup has been updated so any return should be true
 
504
 
 
505
    wstring data;
 
506
 
 
507
    if( read_from_registry_value( HKLM, guid, L"Locale", data ) )
 
508
        write_to_registry_value( guid, L"Locale", data );
 
509
 
 
510
    if( !read_from_registry_value( HKLM, guid, L"StubPath", data )
 
511
        || data.empty()
 
512
        )
 
513
        return true;
 
514
 
 
515
    vector<WCHAR> buffer( data.begin(), data.end() );
 
516
    buffer.push_back( L'\0' );
 
517
 
 
518
    HANDLE thread = CreateThread( NULL, 0, create_process, &buffer[ 0 ], 0, NULL );
 
519
    if( thread )
 
520
    {
 
521
        WaitForSingleObject( thread, INFINITE );
 
522
        CloseHandle( thread );
 
523
        thread = NULL;
 
524
    }
 
525
 
 
526
    /* Active Setup only attempts to run the StubPath, it doesn't record whether or not it was 
 
527
    successful in doing so.
 
528
    */
 
529
 
 
530
    return true;
 
531
}
 
532
 
 
533
 
 
534
 
 
535
/* W32ActiveSetup::update_all()
 
536
 
 
537
returns true if any of Workrave's HKLM Active Setup GUID keys needed to be installed in HKCU 
 
538
Active Setup.
 
539
*/
 
540
bool W32ActiveSetup::update_all()
 
541
{
 
542
    /* For now there's only one entry, for setting up Workrave's autorun for each user */
 
543
    return update( W32ActiveSetup::guid_autorun );
 
544
}
 
545
 
 
546
 
 
547
 
 
548
/* W32ActiveSetup::create_process()
 
549
 
 
550
This is the starting function for a thread that creates a process and waits for it to terminate.
 
551
*/
 
552
DWORD WINAPI W32ActiveSetup::create_process(
 
553
    LPVOID lpParam
 
554
    )
 
555
{
 
556
    DWORD exit_code = ((DWORD) -1);
 
557
    WCHAR *command = (WCHAR *)lpParam;
 
558
    STARTUPINFOW si = { sizeof( si ), };
 
559
    PROCESS_INFORMATION pi = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, };
 
560
 
 
561
    if( !command || !*command )
 
562
        return exit_code;
 
563
 
 
564
 
 
565
    void *OldValue = NULL;
 
566
    bool wow64_disabled = false;
 
567
    BOOL ( WINAPI *pWow64DisableWow64FsRedirection )( void ** ) = NULL;
 
568
    BOOL ( WINAPI *pWow64RevertWow64FsRedirection )( void * ) = NULL;
 
569
 
 
570
    if( W32ActiveSetup::is_os_64() )
 
571
    {
 
572
        pWow64DisableWow64FsRedirection = ( BOOL ( WINAPI * )( void ** ) )
 
573
            GetProcAddress( GetModuleHandleA( "kernel32" ), "Wow64DisableWow64FsRedirection" );
 
574
 
 
575
        pWow64RevertWow64FsRedirection = ( BOOL ( WINAPI * )( void * ) )
 
576
            GetProcAddress( GetModuleHandleA( "kernel32" ), "Wow64RevertWow64FsRedirection" );
 
577
    }
 
578
 
 
579
 
 
580
    if( pWow64DisableWow64FsRedirection && pWow64RevertWow64FsRedirection )
 
581
        wow64_disabled = !!pWow64DisableWow64FsRedirection( &OldValue );
 
582
 
 
583
    BOOL ret = CreateProcessW(
 
584
        NULL,
 
585
        command,
 
586
        NULL,
 
587
        NULL,
 
588
        FALSE,
 
589
        0,
 
590
        NULL,
 
591
        get_user_profile_dir(),
 
592
        &si,
 
593
        &pi
 
594
        );
 
595
 
 
596
    if( wow64_disabled && pWow64DisableWow64FsRedirection && pWow64RevertWow64FsRedirection )
 
597
        wow64_disabled = !pWow64RevertWow64FsRedirection( OldValue );
 
598
 
 
599
 
 
600
    if( ret )
 
601
    {
 
602
        if( pi.hProcess != INVALID_HANDLE_VALUE )
 
603
        {
 
604
            WaitForSingleObject( pi.hProcess, INFINITE );
 
605
 
 
606
            if( !GetExitCodeProcess( pi.hProcess, &exit_code ) )
 
607
                exit_code = ((DWORD) -1);
 
608
 
 
609
            CloseHandle( pi.hProcess );
 
610
        }
 
611
 
 
612
        if( pi.hThread != INVALID_HANDLE_VALUE )
 
613
            CloseHandle( pi.hThread );
 
614
    }
 
615
 
 
616
    return exit_code;
 
617
}