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[512];
 188     char tempPath[512];
 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     strcpy_s(path, profilePath);
 199     strcat_s(path, "\\.accessibility.properties");
 200     strcpy_s(tempPath, profilePath);
 201     strcat_s(tempPath, "\\.acce$$ibility.properties");
 202     free(profilePath);
 203     // Open the original file.  If it doesn't exist and this is an enable request then create it.
 204     error = fopen_s(&origFile, path, "r");
 205     if (error) {
 206         if (enable) {
 207             error = fopen_s(&origFile, path, "w");
 208             if (error) {
 209                 printf("Couldn't create file: %s\n", path);
 210                 perror("Error");
 211             } else {
 212                 char str[100] = "assistive_technologies=com.sun.java.accessibility.AccessBridge\n";
 213                 strcat_s(str, "screen_magnifier_present=true\n");
 214                 fprintf(origFile, str);
 215                 fclose(origFile);
 216             }
 217         } else {
 218             // It's OK if the file isn't there for a -disable
 219             error = 0;
 220         }
 221     } else {
 222         // open a temp file
 223         error = fopen_s(&tempFile, tempPath, "w");
 224         if (error) {
 225             printf("Couldn't open temp file: %s\n", tempPath);
 226             perror("Error");
 227             return error;
 228         }
 229         if (enable) {
 230             enableJAB();
 231         } else {
 232             disableJAB();
 233         }
 234         fclose(origFile);
 235         fclose(tempFile);
 236         // delete the orig file and rename the temp file
 237         if (remove(path) != 0) {
 238             printf("Couldn't remove file: %s\n", path);
 239             perror("Error");
 240             return errno;
 241         }
 242         if (rename(tempPath, path) != 0) {
 243             printf("Couldn't rename %s to %s.\n", tempPath, path);
 244             perror("Error");
 245             return errno;
 246         }
 247     }
 248     return error;
 249 }
 250 
 251 void printUsage() {
 252     printf("\njabswitch [/enable | /disable | /version | /?]\n\n");
 253     printf("Description:\n");
 254     printf("  jabswitch enables or disables the Java Access Bridge.\n\n");
 255     printf("Parameters:\n");
 256     printf("  /enable   Enable the Java Accessibility Bridge.\n");
 257     printf("  /disable  Disable the Java Accessibility Bridge.\n");
 258     printf("  /version  Display the version.\n");
 259     printf("  /?        Display this usage information.\n");
 260     printf("\nNote:\n");
 261     printf("  The Java Access Bridge can also be enabled with the\n");
 262     printf("  Windows Ease of Access control panel (which can be\n");
 263     printf("  activated by pressing Windows + U).  The Ease of Access\n");
 264     printf("  control panel has a Java Access Bridge checkbox.  Please\n");
 265     printf("  be aware that unchecking the checkbox has no effect and\n");
 266     printf("  in order to disable the Java Access Bridge you must run\n");
 267     printf("  jabswitch.exe from the command line.\n");
 268 }
 269 
 270 void printVersion() {
 271     TCHAR executableFileName[_MAX_PATH];
 272     if (!GetModuleFileName(0, executableFileName, _MAX_PATH)) {
 273         printf("Unable to get executable file name.\n");
 274         return;
 275     }
 276     DWORD nParam;
 277     DWORD nVersionSize = GetFileVersionInfoSize(executableFileName, &nParam);
 278     if (!nVersionSize) {
 279         printf("Unable to get version info size.\n");
 280         return;
 281     }
 282     char* pVersionData = new char[nVersionSize];
 283     if (!GetFileVersionInfo(executableFileName, 0, nVersionSize, pVersionData)) {
 284         printf("Unable to get version info.\n");
 285         return;
 286     }
 287     LPVOID pVersionInfo;
 288     UINT nSize;
 289     if (!VerQueryValue(pVersionData, _T("\\"), &pVersionInfo, &nSize)) {
 290         printf("Unable to query version value.\n");
 291         return;
 292     }
 293     VS_FIXEDFILEINFO *pVSInfo = (VS_FIXEDFILEINFO *)pVersionInfo;
 294     char versionString[100];
 295     sprintf_s( versionString, "version %i.%i.%i.%i",
 296                pVSInfo->dwProductVersionMS >> 16,
 297                pVSInfo->dwProductVersionMS & 0xFFFF,
 298                pVSInfo->dwProductVersionLS >> 16,
 299                pVSInfo->dwProductVersionLS & 0xFFFF );
 300     char outputString[100];
 301     strcpy_s(outputString, "jabswitch ");
 302     strcat_s(outputString, versionString);
 303     strcat_s(outputString, "\njabswitch enables or disables the Java Access Bridge.\n");
 304     printf(outputString);
 305 }
 306 
 307 int regEnable() {
 308     HKEY hKey;
 309     DWORD retval = -1;
 310     LSTATUS err;
 311     err = RegOpenKeyEx(HKEY_CURRENT_USER, ACCESSIBILITY_USER_KEY, NULL, KEY_READ|KEY_WRITE, &hKey);
 312     if (err == ERROR_SUCCESS) {
 313         DWORD dataType = REG_SZ;
 314         DWORD dataLength = DEFAULT_ALLOC;
 315         TCHAR dataBuffer[DEFAULT_ALLOC];
 316         TCHAR *data = dataBuffer;
 317         bool freeData = false;
 318         err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
 319         if (err == ERROR_MORE_DATA) {
 320             if (dataLength > 0 && dataLength < MAX_ALLOC) {
 321                 data = new TCHAR[dataLength];
 322                 err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
 323             }
 324         }
 325         if (err == ERROR_SUCCESS) {
 326             err = _tcslwr_s(dataBuffer, DEFAULT_ALLOC);
 327             if (err) {
 328                 return -1;
 329             }
 330             if (_tcsstr(dataBuffer, STR_ACCESSBRIDGE) != NULL) {
 331                 return 0;  // This is OK, e.g. ran enable twice and the value is there.
 332             } else {
 333                 // add oracle_javaaccessbridge to Config key for HKCU
 334                 dataLength = dataLength + (_tcslen(STR_ACCESSBRIDGE) + 1) * sizeof(TCHAR);
 335                 TCHAR *newStr = new TCHAR[dataLength];
 336                 if (newStr != NULL) {
 337                     wsprintf(newStr, L"%s,%s", dataBuffer, STR_ACCESSBRIDGE);
 338                     RegSetValueEx(hKey, ACCESSIBILITY_CONFIG, 0, REG_SZ, (BYTE *)newStr, dataLength);
 339                 }
 340             }
 341         }
 342         RegCloseKey(hKey);
 343     }
 344     return err;
 345 }
 346 
 347 int regDeleteValue(HKEY hFamilyKey, LPCWSTR lpSubKey)
 348 {
 349     HKEY hKey;
 350     DWORD retval = -1;
 351     LSTATUS err;
 352     err = RegOpenKeyEx(hFamilyKey, lpSubKey, NULL, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, &hKey);
 353     if (err != ERROR_SUCCESS)
 354         err = RegOpenKeyEx(hFamilyKey, lpSubKey, NULL, KEY_READ|KEY_WRITE, &hKey);
 355 
 356     if (err == ERROR_SUCCESS) {
 357         DWORD dataType = REG_SZ;
 358         DWORD dataLength = DEFAULT_ALLOC;
 359         TCHAR dataBuffer[DEFAULT_ALLOC];
 360         TCHAR searchBuffer[DEFAULT_ALLOC];
 361         TCHAR *data = dataBuffer;
 362         bool freeData = false;
 363         err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
 364         if (err == ERROR_MORE_DATA) {
 365             if (dataLength > 0 && dataLength < MAX_ALLOC) {
 366                 data = new TCHAR[dataLength];
 367                 err = RegQueryValueEx(hKey, ACCESSIBILITY_CONFIG, 0, &dataType, (BYTE *)data, &dataLength);
 368             }
 369         }
 370         if (err == ERROR_SUCCESS) {
 371             err = _tcslwr_s(dataBuffer, DEFAULT_ALLOC);
 372             if (err) {
 373                 return -1;
 374             }
 375             if (_tcsstr(dataBuffer, STR_ACCESSBRIDGE) == NULL) {
 376                 return 0;  // This is OK, e.g. ran disable twice and the value is not there.
 377             } else {
 378                 // remove oracle_javaaccessbridge from Config key
 379                 TCHAR *newStr = new TCHAR[dataLength];
 380                 TCHAR *nextToken;
 381                 LPTSTR tok, beg1 = dataBuffer;
 382                 bool first = true;
 383                 _tcscpy_s(newStr, dataLength, L"");
 384                 tok = _tcstok_s(beg1, L",", &nextToken);
 385                 while (tok != NULL) {
 386                     _tcscpy_s(searchBuffer, DEFAULT_ALLOC, tok);
 387                     err = _tcslwr_s(searchBuffer, DEFAULT_ALLOC);
 388                     if (err) {
 389                         return -1;
 390                     }
 391                     if (_tcsstr(searchBuffer, STR_ACCESSBRIDGE) == NULL) {
 392                         if (!first) {
 393                            _tcscat_s(newStr, dataLength, L",");
 394                         }
 395                         first = false;
 396                         _tcscat_s(newStr, dataLength, tok);
 397                     }
 398                     tok = _tcstok_s(NULL, L",", &nextToken);
 399                 }
 400                 dataLength = (_tcslen(newStr) + 1) * sizeof(TCHAR);
 401                 RegSetValueEx(hKey, ACCESSIBILITY_CONFIG, 0, REG_SZ, (BYTE *)newStr, dataLength);
 402             }
 403         }
 404         RegCloseKey(hKey);
 405     }
 406     return err;
 407 }
 408 
 409 int regDisable()
 410 {
 411     LSTATUS err;
 412     // Update value for HKCU
 413     err=regDeleteValue(HKEY_CURRENT_USER, ACCESSIBILITY_USER_KEY);
 414     // Update value for HKLM for Session
 415     TCHAR dataBuffer[DEFAULT_ALLOC];
 416     DWORD dwSessionId ;
 417     ProcessIdToSessionId(GetCurrentProcessId(),&dwSessionId ) ;
 418     if( dwSessionId >= 0 )
 419     {
 420         wsprintf(dataBuffer, L"%s%d", ACCESSIBILITY_SYSTEM_KEY, dwSessionId);
 421         err=regDeleteValue(HKEY_LOCAL_MACHINE, dataBuffer);
 422     }
 423     return err;
 424 }
 425 
 426 void main(int argc, char* argv[]) {
 427     bool enableWasRequested = false;
 428     bool disableWasRequested = false;
 429     bool badParams = true;
 430     int error = 0;
 431     if (argc == 2) {
 432         if (_stricmp(argv[1], "-?") == 0 || _stricmp(argv[1], "/?") == 0) {
 433             printUsage();
 434             badParams = false;
 435         } else if (_stricmp(argv[1], "-version") == 0 || _stricmp(argv[1], "/version") == 0) {
 436             printVersion();
 437             badParams = false;
 438         } else {
 439             if (_stricmp(argv[1], "-enable") == 0 || _stricmp(argv[1], "/enable") == 0) {
 440                 badParams = false;
 441                 enableWasRequested = true;
 442                 error = modify(true);
 443                 if (error == 0) {
 444                    if( !isXP() )
 445                       regEnable();
 446                 }
 447             } else if (_stricmp(argv[1], "-disable") == 0 || _stricmp(argv[1], "/disable") == 0) {
 448                 badParams = false;
 449                 disableWasRequested = true;
 450                 error = modify(false);
 451                 if (error == 0) {
 452                    if( !isXP() )
 453                       regDisable();
 454                 }
 455             }
 456         }
 457     }
 458     if (badParams) {
 459         printUsage();
 460     } else if (enableWasRequested || disableWasRequested) {
 461         if (error != 0) {
 462             printf("There was an error.\n\n");
 463         }
 464         printf("The Java Access Bridge has ");
 465         if (error != 0) {
 466             printf("not ");
 467         }
 468         printf("been ");
 469         if (enableWasRequested) {
 470             printf("enabled.\n");
 471         } else {
 472             printf("disabled.\n");
 473         }
 474     }
 475 }