security fix: replaced strcpy with safeStrCpy from backend.c
[xboard.git] / winboard / winboard.c
index 243ff29..6f5e32e 100644 (file)
@@ -277,7 +277,8 @@ int dialogItems[][40] = {
 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
-  OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,  OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag,  OPT_PopupMoveErrors,\r
+  OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
+  OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
@@ -329,17 +330,23 @@ int dialogItems[][40] = {
 { 0 }\r
 };\r
 \r
-char languageBuf[40000], *foreign[1000], *english[1000];\r
+static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
+static int lastChecked;\r
+static char oldLanguage[MSG_SIZ], *menuText[10][25];\r
+extern int tinyLayout;\r
+extern char * menuBarText[][8];\r
 \r
 void\r
 LoadLanguageFile(char *name)\r
 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
     FILE *f;\r
     int i=0, j=0, n=0, k;\r
-    static char oldLanguage[MSG_SIZ];\r
-    if(!strcmp(name, oldLanguage)) return;\r
+    char buf[MSG_SIZ];\r
+\r
     if(!name || name[0] == NULLCHAR) return;\r
-    if((f = fopen(name, "r")) == NULL) return;\r
+    sprintf(buf, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
+    if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
+    if((f = fopen(buf, "r")) == NULL) return;\r
     while((k = fgetc(f)) != EOF) {\r
         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
         languageBuf[i] = k;\r
@@ -351,7 +358,7 @@ LoadLanguageFile(char *name)
                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
                         english[j] = languageBuf + n + 1; *p = 0;\r
                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
-if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
+//if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
                     }\r
                 }\r
             }\r
@@ -368,7 +375,7 @@ if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", en
     }\r
     fclose(f);\r
     barbaric = (j != 0);\r
-    if(barbaric) strcpy(oldLanguage, name); else oldLanguage[0] = NULLCHAR;\r
+    safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
 }\r
 \r
 char *\r
@@ -387,7 +394,7 @@ if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
 }\r
 \r
 void\r
-Translate(HANDLE hDlg, int dialogID)\r
+Translate(HWND hDlg, int dialogID)\r
 {   // translate all text items in the given dialog\r
     int i=0, j, k;\r
     char buf[MSG_SIZ], *s;\r
@@ -408,24 +415,58 @@ if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
 }\r
 \r
 void\r
-TranslateMenus()\r
+TranslateMenus(int addLanguage)\r
 {\r
     int i, j;\r
-    if(barbaric) {\r
+    WIN32_FIND_DATA fileData;\r
+    HANDLE hFind;\r
+#define IDM_English 1895\r
+    if(1) {\r
         HMENU mainMenu = GetMenu(hwndMain);\r
         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
           HMENU subMenu = GetSubMenu(mainMenu, i);\r
+          ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
+                                                                  (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
             char buf[MSG_SIZ];\r
             UINT k = GetMenuItemID(subMenu, j);\r
-            GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
+             if(menuText[i][j]) 
+               safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
+                GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
+                menuText[i][j] = strdup(buf); // remember original on first change\r
+            }\r
             if(buf[0] == NULLCHAR) continue;\r
 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
-            ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION, \r
-            k, T_(buf));\r
+            ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
+                                   |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
+                                   |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
           }\r
         }\r
-    \r
+        DrawMenuBar(hwndMain);\r
+    }\r
+\r
+    if(!addLanguage) return;\r
+    if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
+        HMENU mainMenu = GetMenu(hwndMain);\r
+        HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
+        AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
+        AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
+        i = 0; lastChecked = IDM_English;\r
+        do {\r
+            char *p, *q = fileData.cFileName;\r
+            int checkFlag = MF_UNCHECKED;\r
+            languageFile[i] = strdup(q);\r
+            if(barbaric && !strcmp(oldLanguage, q)) {\r
+                checkFlag = MF_CHECKED;\r
+                lastChecked = IDM_English + i + 1;\r
+                CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
+            }\r
+            *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
+            p = strstr(fileData.cFileName, ".lng");\r
+            if(p) *p = 0;\r
+            AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
+        } while(FindNextFile(hFind, &fileData));\r
+        FindClose(hFind);\r
     }\r
 }\r
 \r
@@ -842,7 +883,7 @@ SetUserLogo()
          if(strcmp(curName, oldUserName)) {\r
                sprintf(oldUserName, "logos\\%s.bmp", curName);\r
                userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
-               strcpy(oldUserName, curName);\r
+               safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
          }\r
     }\r
 }\r
@@ -1007,7 +1048,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   }\r
 \r
   InitDrawingSizes(boardSize, 0);\r
-  TranslateMenus();\r
+  TranslateMenus(1);\r
   InitMenuChecks();\r
   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
 \r
@@ -1072,7 +1113,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
     ShowWindow(hwndConsole, nCmdShow);\r
     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
       char buf[MSG_SIZ], *p = buf, *q;\r
-      strcpy(buf, appData.chatBoxes);\r
+       safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
       do {\r
        q = strchr(p, ';');\r
        if(q) *q++ = 0;\r
@@ -1132,7 +1173,7 @@ LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
   lf->lfQuality = DEFAULT_QUALITY;\r
   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
-  strcpy(lf->lfFaceName, mfp->faceName);\r
+    safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
 }\r
 \r
 void\r
@@ -1435,7 +1476,7 @@ MySearchPath(char *installDir, char *name, char *fullname)
   if(name[0]== '%') {\r
     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
-      strcpy(buf, p+1);\r
+      safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
       *strchr(buf, '%') = 0;\r
       strcat(fullname, getenv(buf));\r
       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
@@ -2024,7 +2065,7 @@ DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
   if (gameInfo.event &&\r
       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
       strcmp(name, "k80s") == 0) {\r
-    strcpy(name, "tim");\r
+    safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
   }\r
   return LoadBitmap(hinst, name);\r
 }\r
@@ -4428,7 +4469,8 @@ ChangedConsoleFont()
 \r
   cfmt.cbSize = sizeof(CHARFORMAT);\r
   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
-  strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
+    safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
+              sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
    * size.  This was undocumented in the version of MSVC++ that I had\r
    * when I wrote the code, but is apparently documented now.\r
@@ -5298,7 +5340,23 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       fromX = fromY = -1;\r
       break;\r
 \r
+    case IDM_English:\r
+      barbaric = 0;\r
+      TranslateMenus(0);\r
+      CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
+      CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
+      lastChecked = wmId;\r
+      break;\r
+\r
     default:\r
+      if(wmId > IDM_English && wmId < IDM_English+5) {\r
+          LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
+          TranslateMenus(0);\r
+          CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
+          CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
+          lastChecked = wmId;\r
+          break;\r
+      }\r
       return (DefWindowProc(hwnd, message, wParam, lParam));\r
     }\r
     break;\r
@@ -5603,6 +5661,7 @@ OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       number = NULL;\r
     }\r
     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
+    Translate(hDlg, 1536);\r
     return FALSE;  /* Allow for further processing */\r
 \r
   case WM_COMMAND:\r
@@ -5622,6 +5681,7 @@ OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
   OFNOTIFY *ofnot;\r
   switch (uiMsg) {\r
   case WM_INITDIALOG:\r
+    Translate(hdlg, DLG_IndexNumber);\r
     ofname = (OPENFILENAME *)lParam;\r
     number = (UINT *)(ofname->lCustData);\r
     break;\r
@@ -5647,12 +5707,12 @@ OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] dia
 \r
   if (fileName == NULL) fileName = buf1;\r
   if (defName == NULL) {\r
-    strcpy(fileName, "*.");\r
+    safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
     strcat(fileName, defExt);\r
   } else {\r
-    strcpy(fileName, defName);\r
+    safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
   }\r
-  if (fileTitle) strcpy(fileTitle, "");\r
+    if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
   if (number) *number = 0;\r
 \r
   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
@@ -5851,7 +5911,7 @@ InitComboStrings(HANDLE hwndCombo, char **cd)
   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
 \r
   while (*cd != NULL) {\r
-    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
     cd++;\r
   }\r
 }\r
@@ -5994,23 +6054,23 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     switch (LOWORD(wParam)) {\r
     case IDOK:\r
       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
-        strcpy(buf, "/fcp=");\r
+        safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
-        strcpy(buf, "/scp=");\r
+       safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
        appData.noChessProgram = FALSE;\r
        appData.icsActive = FALSE;\r
       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
-        strcpy(buf, "/ics /icshost=");\r
+        safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
        if (appData.zippyPlay) {\r
-         strcpy(buf, "/fcp=");\r
+         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
          GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
          p = buf;\r
          ParseArgs(StringGet, &p);\r
@@ -6067,6 +6127,7 @@ About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     /* Center the dialog over the application window */\r
     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
+    Translate(hDlg, ABOUTBOX);\r
     JAWS_COPYRIGHT\r
     return (TRUE);\r
 \r
@@ -7989,7 +8050,7 @@ DisplayTitle(char *str)
 {\r
   char title[MSG_SIZ], *host;\r
   if (str[0] != NULLCHAR) {\r
-    strcpy(title, str);\r
+    safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
   } else if (appData.icsActive) {\r
     if (appData.icsCommPort[0] != NULLCHAR)\r
       host = "ICS";\r
@@ -7997,9 +8058,9 @@ DisplayTitle(char *str)
       host = appData.icsHost;\r
     sprintf(title, "%s: %s", szTitle, host);\r
   } else if (appData.noChessProgram) {\r
-    strcpy(title, szTitle);\r
+    safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
   } else {\r
-    strcpy(title, szTitle);\r
+    safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
     strcat(title, ": ");\r
     strcat(title, first.tidy);\r
   }\r
@@ -8054,7 +8115,7 @@ DisplayError(char *str, int error)
   int len;\r
 \r
   if (error == 0) {\r
-    strcpy(buf, str);\r
+    safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
   } else {\r
     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
                        NULL, error, LANG_NEUTRAL,\r
@@ -8164,7 +8225,7 @@ QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_COMMAND:\r
     switch (LOWORD(wParam)) {\r
     case IDOK:\r
-      strcpy(reply, qp->replyPrefix);\r
+      safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
       if (*reply) strcat(reply, " ");\r
       len = strlen(reply);\r
       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
@@ -8375,7 +8436,7 @@ int GameListOptions()
     int result;\r
     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
 \r
-    strcpy( lpUserGLT, appData.gameListTags );\r
+      safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
 \r
     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
 \r
@@ -8531,7 +8592,7 @@ UserName()
   }\r
   if (!GetUserName(buf, &bufsiz)) {\r
     /*DisplayError("Error getting user name", GetLastError());*/\r
-    strcpy(buf, _("User"));\r
+    safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
   }\r
   return buf;\r
 }\r
@@ -8544,7 +8605,7 @@ HostName()
 \r
   if (!GetComputerName(buf, &bufsiz)) {\r
     /*DisplayError("Error getting host name", GetLastError());*/\r
-    strcpy(buf, _("Unknown"));\r
+    safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
   }\r
   return buf;\r
 }\r
@@ -9061,7 +9122,7 @@ OpenCommPort(char *name, ProcRef *pr)
   if (*name != '\\')\r
     sprintf(fullname, "\\\\.\\%s", name);\r
   else\r
-    strcpy(fullname, name);\r
+    safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
 \r
   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
                 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r