1/*
2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <ctype.h>
30#include <Windows.h>
31#include <tchar.h>
32
33// This is the default buffer size used for RegQueryValue values.
34#define DEFAULT_ALLOC MAX_PATH
35// only allocate a buffer as big as MAX_ALLOC for RegQueryValue values.
36#define MAX_ALLOC 262144
37
38static LPCTSTR ACCESSIBILITY_USER_KEY =
39    _T("Software\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility");
40static LPCTSTR ACCESSIBILITY_SYSTEM_KEY =
41    _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\Session");
42static LPCTSTR ACCESSIBILITY_CONFIG =
43    _T("Configuration");
44static LPCTSTR STR_ACCESSBRIDGE =
45    _T("oracle_javaaccessbridge");
46
47// Note: There are senarios where more than one extension can be specified on the
48// asssistive_technologies=
49// line but this code only deals with the case of
50// assistive_technologies=com.sun.java.accessibility.AccessBridge
51// assuming that if additional extensions are desired the user knows how edit the file.
52
53FILE* origFile;
54FILE* tempFile;
55
56bool isXP()
57{
58    static bool isXPFlag = false;
59    OSVERSIONINFO  osvi;
60
61    // Initialize the OSVERSIONINFO structure.
62    ZeroMemory( &osvi, sizeof( osvi ) );
63    osvi.dwOSVersionInfoSize = sizeof( osvi );
64
65    GetVersionEx( &osvi );
66
67    if ( osvi.dwMajorVersion == 5 ) // For Windows XP and Windows 2000
68        isXPFlag = true;
69
70    return isXPFlag ;
71}
72
73void enableJAB() {
74    // Copy lines from orig to temp modifying the line containing
75    // assistive_technologies=
76    // There are various scenarios:
77    // 1) If the line exists exactly as
78    //    #assistive_technologies=com.sun.java.accessibility.AccessBridge
79    //    replace it with
80    //    assistive_technologies=com.sun.java.accessibility.AccessBridge
81    // 2) else if the line exists exactly as
82    //    assistive_technologies=com.sun.java.accessibility.AccessBridge
83    //    use it as is
84    // 3) else if a line containing "assistive_technologies" exits
85    //    a) if it's already commented out, us it as is (jab will be enabled in step 4)
86    //    b) else if it's not commented out, comment it out and add a new line with
87    //       assistive_technologies=com.sun.java.accessibility.AccessBridge
88    // 4) If the line doesn't exist (or case 3a), add
89    //    assistive_technologies=com.sun.java.accessibility.AccessBridge
90    // Do the same for screen_magnifier_present=
91    char line[512];
92    char commentLine[512] = "#";
93    char jabLine[] = "assistive_technologies=com.sun.java.accessibility.AccessBridge\n";
94    char magLine[] = "screen_magnifier_present=true\n";
95    bool foundJabLine = false;
96    bool foundMagLine = false;
97    while (!feof(origFile)) {
98        if (fgets(line, 512, origFile) != NULL) {
99            if (_stricmp(line, "#assistive_technologies=com.sun.java.accessibility.AccessBridge\n") == 0) {
100                fputs(jabLine, tempFile);
101                foundJabLine = true;
102            } else if (_stricmp(line, jabLine) == 0) {
103                fputs(line, tempFile);
104                foundJabLine = true;
105            } else if (strstr(line, "assistive_technologies") != NULL) {
106                char* context;
107                char* firstNonSpaceChar = strtok_s(line, " ", &context);
108                if (*firstNonSpaceChar == '#') {
109                    fputs(line, tempFile);
110                } else {
111                    strcat_s(commentLine, line);
112                    fputs(commentLine, tempFile);
113                    fputs(jabLine, tempFile);
114                    foundJabLine = true;
115                }
116            } else if (_stricmp(line, "#screen_magnifier_present=true\n") == 0) {
117                fputs(magLine, tempFile);
118                foundMagLine = true;
119            } else if (_stricmp(line, magLine) == 0) {
120                fputs(line, tempFile);
121                foundMagLine = true;
122            } else if (strstr(line, "screen_magnifier_present") != NULL) {
123                char* context;
124                char* firstNonSpaceChar = strtok_s(line, " ", &context);
125                if (*firstNonSpaceChar == '#') {
126                    fputs(line, tempFile);
127                } else {
128                    strcat_s(commentLine, line);
129                    fputs(commentLine, tempFile);
130                    fputs(magLine, tempFile);
131                    foundMagLine = true;
132                }
133            } else {
134                fputs(line, tempFile);
135            }
136        }
137    }
138    if (!foundJabLine) {
139        fputs(jabLine, tempFile);
140    }
141    if (!foundMagLine) {
142        fputs(magLine, tempFile);
143    }
144}
145
146void disableJAB() {
147    // Copy lines from orig to temp modifying the line containing
148    // assistive_technologies=
149    // There are various scenarios:
150    // 1) If the uncommented line exists, comment it out
151    // 2) If the line exists but is preceeded by a #, nothing to do
152    // 3) If the line doesn't exist, nothing to do
153    // Do the same for screen_magnifier_present=
154    char line[512];
155    char commentLine[512];
156    while (!feof(origFile)) {
157        if (fgets(line, 512, origFile) != NULL) {
158            if (strstr(line, "assistive_technologies") != NULL) {
159                char* context;
160                char* firstNonSpaceChar = strtok_s(line, " ", &context);
161                if (*firstNonSpaceChar != '#') {
162                    strcpy_s(commentLine, "#");
163                    strcat_s(commentLine, line);
164                    fputs(commentLine, tempFile);
165                } else {
166                    fputs(line, tempFile);
167                }
168            } else if (strstr(line, "screen_magnifier_present") != NULL) {
169                char* context;
170                char* firstNonSpaceChar = strtok_s(line, " ", &context);
171                if (*firstNonSpaceChar != '#') {
172                    strcpy_s(commentLine, "#");
173                    strcat_s(commentLine, line);
174                    fputs(commentLine, tempFile);
175                } else {
176                    fputs(line, tempFile);
177                }
178            } else {
179                fputs(line, tempFile);
180            }
181        }
182    }
183}
184
185int modify(bool enable) {
186    errno_t error = 0;
187    char path[_MAX_PATH];
188    char tempPath[_MAX_PATH];
189    // Get the path for %USERPROFILE%
190    char *profilePath;
191    size_t len;
192    error = _dupenv_s(&profilePath, &len, "USERPROFILE" );
193    if (error) {
194        printf("Error fetching USERPROFILE.\n");
195        perror("Error");
196        return error;
197    }
198    const char acc_props1[] = "\\.accessibility.properties";
199    const char acc_props2[] = "\\.acce$$ibility.properties";
200    // len must be 234 or less (233 characters)
201    // sizeof(path) is 260 (room for 259 characters)
202    // sizeof(acc_props1) is 27 (26 characters)
203    // path will hold 233 path characters plus 26 file characters plus 1 null character)
204    // if len - 1 > 233 then error
205    if ( len - 1 > sizeof(path) - sizeof(acc_props1) ||
206         len - 1 > sizeof(tempPath) - sizeof(acc_props2) ) {
207        printf("The USERPROFILE environment variable is too long.\n");
208        printf("It must be no longer than 233 characters.\n");
209        return 123;
210     }
211    path[0] = 0;
212    strcat_s(path, _MAX_PATH, profilePath);
213    strcat_s(path, acc_props1);
214    tempPath[0] = 0;
215    strcat_s(tempPath, _MAX_PATH, profilePath);
216    strcat_s(tempPath, acc_props2);
217    free(profilePath);
218    profilePath = 0;
219    // Open the original file.  If it doesn't exist and this is an enable request then create it.
220    error = fopen_s(&origFile, path, "r");
221    if (error) {
222        if (enable) {
223            error = fopen_s(&origFile, path, "w");
224            if (error) {
225                printf("Couldn't create file: %s\n", path);
226                perror("Error");
227            } else {
228                char str[100] = "assistive_technologies=com.sun.java.accessibility.AccessBridge\n";
229                strcat_s(str, "screen_magnifier_present=true\n");
230                fprintf(origFile, str);
231                fclose(origFile);
232            }
233        } else {
234            // It's OK if the file isn't there for a -disable
235            error = 0;
236        }
237    } else {
238        // open a temp file
239        error = fopen_s(&tempFile, tempPath, "w");
240        if (error) {
241            printf("Couldn't open temp file: %s\n", tempPath);
242            perror("Error");
243            return error;
244        }
245        if (enable) {
246            enableJAB();
247        } else {
248            disableJAB();
249        }
250        fclose(origFile);
251        fclose(tempFile);
252        // delete the orig file and rename the temp file
253        if (remove(path) != 0) {
254            printf("Couldn't remove file: %s\n", path);
255            perror("Error");
256            return errno;
257        }
258        if (rename(tempPath, path) != 0) {
259            printf("Couldn't rename %s to %s.\n", tempPath, path);
260            perror("Error");
261            return errno;
262        }
263    }
264    return error;
265}
266
267void printUsage() {
268    printf("\njabswitch [/enable | /disable | /version | /?]\n\n");
269    printf("Description:\n");
270    printf("  jabswitch enables or disables the Java Access Bridge.\n\n");
271    printf("Parameters:\n");
272    printf("  /enable   Enable the Java Accessibility Bridge.\n");
273    printf("  /disable  Disable the Java Accessibility Bridge.\n");
274    printf("  /version  Display the version.\n");
275    printf("  /?        Display this usage information.\n");
276    printf("\nNote:\n");
277    printf("  The Java Access Bridge can also be enabled with the\n");
278    printf("  Windows Ease of Access control panel (which can be\n");
279    printf("  activated by pressing Windows + U).  The Ease of Access\n");
280    printf("  control panel has a Java Access Bridge checkbox.  Please\n");
281    printf("  be aware that unchecking the checkbox has no effect and\n");
282    printf("  in order to disable the Java Access Bridge you must run\n");
283    printf("  jabswitch.exe from the command line.\n");
284}
285
286void printVersion() {
287    TCHAR executableFileName[_MAX_PATH];
288    if (!GetModuleFileName(0, executableFileName, _MAX_PATH)) {
289        printf("Unable to get executable file name.\n");
290        return;
291    }
292    DWORD nParam;
293    DWORD nVersionSize = GetFileVersionInfoSize(executableFileName, &nParam);
294    if (!nVersionSize) {
295        printf("Unable to get version info size.\n");
296        return;
297    }
298    char* pVersionData = new char[nVersionSize];
299    if (!GetFileVersionInfo(executableFileName, 0, nVersionSize, pVersionData)) {
300        printf("Unable to get version info.\n");
301        return;
302    }
303    LPVOID pVersionInfo;
304    UINT nSize;
305    if (!VerQueryValue(pVersionData, _T("\\"), &pVersionInfo, &nSize)) {
306        printf("Unable to query version value.\n");
307        return;
308    }
309    VS_FIXEDFILEINFO *pVSInfo = (VS_FIXEDFILEINFO *)pVersionInfo;
310    char versionString[100];
311    sprintf_s( versionString, "version %i.%i.%i.%i",
312               pVSInfo->dwProductVersionMS >> 16,
313               pVSInfo->dwProductVersionMS & 0xFFFF,
314               pVSInfo->dwProductVersionLS >> 16,
315               pVSInfo->dwProductVersionLS & 0xFFFF );
316    char outputString[100];
317    strcpy_s(outputString, "jabswitch ");
318    strcat_s(outputString, versionString);
319    strcat_s(outputString, "\njabswitch enables or disables the Java Access Bridge.\n");
320    printf(outputString);
321}
322
323int regEnable() {
324    HKEY hKey;
325    DWORD retval = -1;
326    LSTATUS err;
327    err = RegOpenKeyEx(HKEY_CURRENT_USER, ACCESSIBILITY_USER_KEY, NULL, KEY_READ|KEY_WRITE, &hKey);
328    if (err == ERROR_SUCCESS) {
329        DWORD dataType = REG_SZ;
330        DWORD dataLength = DEFAULT_ALLOC;
331        TCHAR dataBuffer[DEFAULT_ALLOC];
332        TCHAR *data = dataBuffer;
333        bool freeData = false;
334        err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
335        if (err == ERROR_MORE_DATA) {
336            if (dataLength > 0 && dataLength < MAX_ALLOC) {
337                data = new TCHAR[dataLength];
338                err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
339            }
340        }
341        if (err == ERROR_SUCCESS) {
342            err = _tcslwr_s(dataBuffer, DEFAULT_ALLOC);
343            if (err) {
344                return -1;
345            }
346            if (_tcsstr(dataBuffer, STR_ACCESSBRIDGE) != NULL) {
347                return 0;  // This is OK, e.g. ran enable twice and the value is there.
348            } else {
349                // add oracle_javaaccessbridge to Config key for HKCU
350                dataLength = dataLength + (_tcslen(STR_ACCESSBRIDGE) + 1) * sizeof(TCHAR);
351                TCHAR *newStr = new TCHAR[dataLength];
352                if (newStr != NULL) {
353                    wsprintf(newStr, L"%s,%s", dataBuffer, STR_ACCESSBRIDGE);
354                    RegSetValueEx(hKey, ACCESSIBILITY_CONFIG, 0, REG_SZ, (BYTE *)newStr, dataLength);
355                }
356            }
357        }
358        RegCloseKey(hKey);
359    }
360    return err;
361}
362
363int regDeleteValue(HKEY hFamilyKey, LPCWSTR lpSubKey)
364{
365    HKEY hKey;
366    DWORD retval = -1;
367    LSTATUS err;
368    err = RegOpenKeyEx(hFamilyKey, lpSubKey, NULL, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, &hKey);
369    if (err != ERROR_SUCCESS)
370        err = RegOpenKeyEx(hFamilyKey, lpSubKey, NULL, KEY_READ|KEY_WRITE, &hKey);
371
372    if (err == ERROR_SUCCESS) {
373        DWORD dataType = REG_SZ;
374        DWORD dataLength = DEFAULT_ALLOC;
375        TCHAR dataBuffer[DEFAULT_ALLOC];
376        TCHAR searchBuffer[DEFAULT_ALLOC];
377        TCHAR *data = dataBuffer;
378        bool freeData = false;
379        err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
380        if (err == ERROR_MORE_DATA) {
381            if (dataLength > 0 && dataLength < MAX_ALLOC) {
382                data = new TCHAR[dataLength];
383                err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
384            }
385        }
386        if (err == ERROR_SUCCESS) {
387            err = _tcslwr_s(dataBuffer, DEFAULT_ALLOC);
388            if (err) {
389                return -1;
390            }
391            if (_tcsstr(dataBuffer, STR_ACCESSBRIDGE) == NULL) {
392                return 0;  // This is OK, e.g. ran disable twice and the value is not there.
393            } else {
394                // remove oracle_javaaccessbridge from Config key
395                TCHAR *newStr = new TCHAR[dataLength];
396                TCHAR *nextToken;
397                LPTSTR tok, beg1 = dataBuffer;
398                bool first = true;
399                _tcscpy_s(newStr, dataLength, L"");
400                tok = _tcstok_s(beg1, L",", &nextToken);
401                while (tok != NULL) {
402                    _tcscpy_s(searchBuffer, DEFAULT_ALLOC, tok);
403                    err = _tcslwr_s(searchBuffer, DEFAULT_ALLOC);
404                    if (err) {
405                        return -1;
406                    }
407                    if (_tcsstr(searchBuffer, STR_ACCESSBRIDGE) == NULL) {
408                        if (!first) {
409                           _tcscat_s(newStr, dataLength, L",");
410                        }
411                        first = false;
412                        _tcscat_s(newStr, dataLength, tok);
413                    }
414                    tok = _tcstok_s(NULL, L",", &nextToken);
415                }
416                dataLength = (_tcslen(newStr) + 1) * sizeof(TCHAR);
417                RegSetValueEx(hKey, ACCESSIBILITY_CONFIG, 0, REG_SZ, (BYTE *)newStr, dataLength);
418            }
419        }
420        RegCloseKey(hKey);
421    }
422    return err;
423}
424
425int regDisable()
426{
427    LSTATUS err;
428    // Update value for HKCU
429    err=regDeleteValue(HKEY_CURRENT_USER, ACCESSIBILITY_USER_KEY);
430    // Update value for HKLM for Session
431    TCHAR dataBuffer[DEFAULT_ALLOC];
432    DWORD dwSessionId ;
433    ProcessIdToSessionId(GetCurrentProcessId(),&dwSessionId ) ;
434    if( dwSessionId >= 0 )
435    {
436        wsprintf(dataBuffer, L"%s%d", ACCESSIBILITY_SYSTEM_KEY, dwSessionId);
437        err=regDeleteValue(HKEY_LOCAL_MACHINE, dataBuffer);
438    }
439    return err;
440}
441
442void main(int argc, char* argv[]) {
443    bool enableWasRequested = false;
444    bool disableWasRequested = false;
445    bool badParams = true;
446    int error = 0;
447    if (argc == 2) {
448        if (_stricmp(argv[1], "-?") == 0 || _stricmp(argv[1], "/?") == 0) {
449            printUsage();
450            badParams = false;
451        } else if (_stricmp(argv[1], "-version") == 0 || _stricmp(argv[1], "/version") == 0) {
452            printVersion();
453            badParams = false;
454        } else {
455            if (_stricmp(argv[1], "-enable") == 0 || _stricmp(argv[1], "/enable") == 0) {
456                badParams = false;
457                enableWasRequested = true;
458                error = modify(true);
459                if (error == 0) {
460                   if( !isXP() )
461                      regEnable();
462                }
463            } else if (_stricmp(argv[1], "-disable") == 0 || _stricmp(argv[1], "/disable") == 0) {
464                badParams = false;
465                disableWasRequested = true;
466                error = modify(false);
467                if (error == 0) {
468                   if( !isXP() )
469                      regDisable();
470                }
471            }
472        }
473    }
474    if (badParams) {
475        printUsage();
476    } else if (enableWasRequested || disableWasRequested) {
477        if (error != 0) {
478            printf("There was an error.\n\n");
479        }
480        printf("The Java Access Bridge has ");
481        if (error != 0) {
482            printf("not ");
483        }
484        printf("been ");
485        if (enableWasRequested) {
486            printf("enabled.\n");
487        } else {
488            printf("disabled.\n");
489        }
490        // Use exit so test case can sense for error.
491        if (error != 0) {
492            exit(error);
493        }
494    }
495}
496