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 
  38 static LPCTSTR ACCESSIBILITY_USER_KEY =
  39     _T("Software\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility");
  40 static LPCTSTR ACCESSIBILITY_SYSTEM_KEY =
  41     _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\Session");
  42 static LPCTSTR ACCESSIBILITY_CONFIG =
  43     _T("Configuration");
  44 static 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 
  53 FILE* origFile;
  54 FILE* tempFile;
  55 
  56 bool 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 
  73 void 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 
 146 void 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 
 185 int 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 
 267 void 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 
 286 void 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 
 323 int 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 
 363 int 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 
 425 int 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 
 442 void 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 }