/* (C) Copyright Microsoft Corporation 1991-1994.  All Rights Reserved */
/*-----------------------------------------------------------------------------+
| FIXREG.C                                                                     |
|                                                                              |
| Publisher and Video For Windows make evil changes to the registry            |
| when they are installed.  Look for these changes.  If they are spotted       |
| then put up a message box to warn the user and offer the user the chance to  |
| correct them (i.e. stuff our version back in)                                |
|                                                                              |
| (C) Copyright Microsoft Corporation 1994.  All rights reserved.              |
|                                                                              |
| Revision History                                                             |
|    10-Aug-1994 Lauriegr Created.                                             |
|                                                                              |
+-----------------------------------------------------------------------------*/

#include <windows.h>
#include <mmsystem.h>
#include <soundrec.h>
#include <reg.h>
#include <fixreg.h>
#include <string.h>
#include <tchar.h>

#define RC_INVOKED
#include <o2base.hxx>
#include <srs.hxx>
#undef RC_INVOKED


/* The idea is to call CheckRegValues(hinst) on a separate thread
   (sort of backgroundy thing) and have it just die
   quietly if there's no problem.  If on the other hand there is a problem
   then we need to get the message box up - and it's a VERY BAD IDEA to
   try to put a message box up on anything other than the thread that's doing
   all the UI (otherwise ScottLu will get you with a weasle word - guaranteed).

   So the background thread should PostMessage (Post, don't Send - more weasles)
   to the main thread a message to say "WM_BADREG".  The main thread should then
   wack up the dialog box by calling FixRegValues.

   Suggested coding in main thread:

       BackgroundRegCheck(hwndmain);

   in window proc for hwndmain:
       case WM_HEYUP:
           FixReg(hwndmain, title);
*/


/* These are the things we check up.

   First define them as static strings, since the compiler's not smart enough
   to spot common strings.

   NOTE - these values are NOT LOCALISED
*/
/* These are for Sound Recorder - Let's try to fix it all while we're here. */
TCHAR szSoundRec[]               = TEXT("SoundRec");
TCHAR szSoundRec_CLSID[]         = TEXT("SoundRec\\CLSID");
TCHAR szSROLE2GUID[]             = TEXT("{00020C01-0000-0000-C000-000000000046}");
TCHAR szSROLE1GUID[]             = TEXT("{0003000D-0000-0000-C000-000000000046}");
TCHAR szSRCLSID_OLE2GUID[]       = TEXT("CLSID\\{00020C01-0000-0000-C000-000000000046}");
TCHAR szSRStdExecute_Server[]    = TEXT("SoundRec\\protocol\\StdExecute\\server");
TCHAR szSR32[]                   = TEXT("sndrec32.exe");
TCHAR szSRStdFileEdit_Server[]   = TEXT("SoundRec\\protocol\\StdFileEditing\\server");
TCHAR szSRShell_Open_Command[]   = TEXT("SoundRec\\shell\\open\\command");
TCHAR szSR32Cmd[]                = TEXT("sndrec32.exe %1");
TCHAR szSRStdFileEdit_verb_0[]   = TEXT("SoundRec\\protocol\\StdFileEditing\\verb\\0");
TCHAR szSRStdFileEdit_verb_1[]   = TEXT("SoundRec\\protocol\\StdFileEditing\\verb\\1");
TCHAR szSRStdFileEdit_verb_2[]   = TEXT("SoundRec\\protocol\\StdFileEditing\\verb\\2");
TCHAR sz_Open[]                  = TEXT("&Open");

/* Array of registry value-data pairs to check:
 */
#define RES_STR_LEN 40  /* Should be enough as a maximum resource string. */
TCHAR szSound[RES_STR_LEN];      // IDS_CLASSROOT in resources
TCHAR sz_Play[RES_STR_LEN];      // IDS_PLAYVERB in resources
TCHAR sz_Edit[RES_STR_LEN];      // IDS_EDITVERB in resources

/*
 * Check for explicit equivalence.
 * These are absolutely necessary.
 */
LPTSTR RegValuesExplicit[] =
{
    szSoundRec,               szSound,          // Primary name for object
    szSoundRec_CLSID,         szSROLE2GUID,     // CLSID, very important
    szSRStdFileEdit_verb_0,   sz_Play,          // verb, very important
    szSRStdFileEdit_verb_1,   sz_Edit           // verb, very important
//    szSRCLSID_OLE2GUID,       szSound,        // not too important 
};

/*
 * Check for valid substring
 * These are OK if the substring exists, i.e:
 *
 * "ntsd.exe sndrec32.exe"
 *  or "sndrec32.exe /play" are OK.
 *
 * "qrecord.exe" is NOT ok.
 */
LPTSTR RegValuesSubstring[] =
{
    szSRStdExecute_Server,    szSR32,   szSR32,     
    szSRStdFileEdit_Server,   szSR32,   szSR32
//    szSRShell_Open_Command,   szSR32Cmd,szSR32    // user can change this
};

/*
 * Check that a REG_SZ value in the registry has the value that it should do
 * Return TRUE if it does, FALSE if it doesn't.
 */
BOOL CheckRegValue(HKEY RootKey, LPTSTR KeyName, LPTSTR ShouldBe, LPTSTR CouldBe)
{
    DWORD Type;
    TCHAR Data[100];
    DWORD cData = sizeof(Data);
    LONG lRet;
    HKEY hkey;


    if (ERROR_SUCCESS!=RegOpenKeyEx( RootKey
                                   , KeyName
                                   , 0  /* reserved */
                                   , KEY_QUERY_VALUE
                                   , &hkey
                                   )
       )
        return FALSE;  /* couldn't even open the key */


    lRet=RegQueryValueEx( hkey
                        , NULL /* ValueName */
                        , NULL  /* reserved */
                        , &Type
                        , (LPBYTE)Data
                        , &cData
                        );

    RegCloseKey(hkey);  /* no idea what to do if this fails */

    if (ERROR_SUCCESS!=lRet) return FALSE;  /* couldn't query it */

    /*  Data, cData and Type give the data, length and type */
    if (Type!=REG_SZ) return FALSE;
    //
    // if Data == ShouldBe, then lRet = 0
    //
    lRet = lstrcmp(Data, ShouldBe); /* capture lRet to make debug easier */
    if (lRet && CouldBe != NULL)
    {
        //
        // if Couldbe in Data, lRet = 0
        //
        lRet = (_tcsstr(Data, CouldBe) == NULL);
    }
    
    return 0==lRet;

} /* CheckRegValue */

#define ARRAY_SIZE(x)   (sizeof((x))/sizeof((x)[0]))

/* check the registry for anything evil.  Return TRUE if it's OK else FALSE */
BOOL CheckRegValues(void)
{
    HKEY HCL = HKEY_CLASSES_ROOT;  /* save typing! */
    DWORD i;
    
    if( !( LoadString( ghInst, IDS_USERTYPESHORT, szSound, SIZEOF(szSound) )
        && LoadString( ghInst, IDS_PLAYVERB, sz_Play, SIZEOF(sz_Play))
        && LoadString( ghInst, IDS_EDITVERB, sz_Edit, SIZEOF(sz_Edit) ) ) )
        /* If any of the strings fails to load, forget it:
         */
        return TRUE;

    for( i = 0; i < ARRAY_SIZE(RegValuesExplicit); i+=2 )
    {
        if( !CheckRegValue( HCL
                            , RegValuesExplicit[i]
                            , RegValuesExplicit[i+1]
                            , NULL ) )
            return FALSE;
    }
    for(i = 0; i < ARRAY_SIZE(RegValuesSubstring); i+=3)
    {
        if( !CheckRegValue( HCL
                            , RegValuesSubstring[i]
                            , RegValuesSubstring[i+1]
                            , RegValuesSubstring[i+2] ) )
            return FALSE;
    }

    return TRUE;

} /* CheckRegValues */


/* start this thread to get the registry checked out.
 * hwnd is typed as a LPVOID because that's what CreateThread wants.
 */
DWORD RegCheckThread(LPVOID hwnd)
{
   if (!CheckRegValues())
       PostMessage((HWND)hwnd, WM_BADREG, 0, 0);

   return 0;   /* end of thread! */
}


/* Call this with the hwnd that you want a WM_BADREG message posted to
 * It will check the registry.  No news is good news.
 * It does the work on a separate thread, so this should return quickly.
 */
void BackgroundRegCheck(HWND hwnd)
{
    HANDLE hThread;
    DWORD thid;
    hThread = CreateThread( NULL /* no special security */
                          , 0    /* default stack size */
                          , (LPTHREAD_START_ROUTINE)RegCheckThread
                          , (LPVOID)hwnd
                          , 0 /* start running at once */
                          , &thid
                          );
    if (hThread!=NULL) CloseHandle(hThread);  /* we don't need this any more */

    /* Else we're in some sort of trouble - dunno what to do.
       Can't think of an intelligible message to give to the user.
       Too bad.  Creep home quietly.
    */

} /* BackgroundRegCheck */


/* returns TRUE if it worked.  Dunno what to do if it didn't

*/
BOOL SetRegValue(HKEY RootKey, LPTSTR KeyName, LPTSTR ValueName, LPTSTR ShouldBe)
{
    HKEY hkey;

    if (ERROR_SUCCESS!=RegOpenKeyEx( RootKey
                                   , KeyName
                                   , 0  /* reserved */
                                   , KEY_SET_VALUE
                                   , &hkey
                                   )
       ) {
        /* Maybe the key has been DELETED - we've seen that */
        DWORD dwDisp;
        if (ERROR_SUCCESS!=RegCreateKeyEx( RootKey
                                         , KeyName
                                         , 0  /* reserved */
                                         , TEXT("") /* class */
                                         , REG_OPTION_NON_VOLATILE
                                         , KEY_SET_VALUE
                                         , NULL   /* SecurityAttributes */
                                         , &hkey
                                         , &dwDisp
                                       )
           ) /* well we're really in trouble */
           return FALSE;
        else /* So now it exists, but we now have to open it */
            if (ERROR_SUCCESS!=RegOpenKeyEx( RootKey
                                           , KeyName
                                           , 0  /* reserved */
                                           , KEY_SET_VALUE
                                           , &hkey
                                           )
               ) /* Give up */
                   return FALSE;

    }


    if (ERROR_SUCCESS!=RegSetValueEx( hkey
                                    , ValueName
                                    , 0  // reserved 
                                    , REG_SZ
                                    , (LPBYTE)ShouldBe
                                    , (lstrlen(ShouldBe)+1)*sizeof(TCHAR)  //BYTES
                                    )
       )
        return FALSE;    /* couldn't set it */

    if ( ERROR_SUCCESS!=RegCloseKey(hkey) )
        /* no idea what to do!*/   ;    // couldn't set it

    // I'm NOT calling RegFlushKey.  They'll get there eventually 

    return TRUE;

} /* SetRegValue */


/*
 * SetRegValues
 *  Update the registry with the correct values.  Return TRUE if everything
 *  succeeds
 * */
BOOL SetRegValues(void)
{
    HKEY HCL = HKEY_CLASSES_ROOT;  /* save typing! */
    DWORD i;

    for( i = 0; i < ARRAY_SIZE(RegValuesExplicit); i+=2 )
    {
        // Do another check to see whether this one needs changing,
        // to avoid gratuitous changes, and to avoid the slim chance
        // that an unnecessary SetRegValue might fail:
        //
        if( !CheckRegValue( HCL
                            , RegValuesExplicit[i]
                            , RegValuesExplicit[i+1]
                            , NULL ) )
        {
            if( !SetRegValue( HCL
                              , RegValuesExplicit[i]
                              , NULL
                              , RegValuesExplicit[i+1] ) )
                return FALSE;
        }
    }
    for( i = 0; i < ARRAY_SIZE(RegValuesSubstring); i+=3 )
    {
        // Do another check to see whether this one needs changing,
        // to avoid gratuitous changes, and to avoid the slim chance
        // that an unnecessary SetRegValue might fail:
        //
        if( !CheckRegValue( HCL
                            , RegValuesSubstring[i]
                            , RegValuesSubstring[i+1]
                            , RegValuesSubstring[i+2] ) )
        {
            if( !SetRegValue( HCL
                              , RegValuesSubstring[i]
                              , NULL
                              , RegValuesSubstring[i+1] ) )
                return FALSE;
        }
    }
    return TRUE;
} /* SetRegValues */

/*
 * FixReg
 * */
void FixReg(HWND hwnd)
{
    int r;

    // Error is confusing and can be caused simply by incomplete localization
    // (see bug # 34330). I removed the error so that we fix the registry
    // automatically and fixed this bug.
    r = IDYES;
//    r = ErrorResBox(hwnd
//                    , NULL
//                    , MB_ICONEXCLAMATION | MB_YESNO
//                    , IDS_APPTITLE
//                    , IDS_BADREG) ;
    switch (r)
    {
        case IDYES:
            if (!SetRegValues())
                ErrorResBox(ghwndApp
                            , ghInst
                            , MB_ICONEXCLAMATION | MB_OK
                            , IDS_APPTITLE
                            , IDS_FIXREGERROR
                            , FALSE );
            break;
        case IDNO:
        case IDCANCEL:
            /* else sneak away quietly */            
        default:
            break;
    }


}  /* FixReg */

const TCHAR aszOptionsSection[]  = TEXT("Options");
const TCHAR aszIgnoreRegistryCheck[]   = TEXT("Ignore Registry Check");
        
BOOL IgnoreRegCheck()
{
    DWORD fIgnore = 0L;
    
    ReadRegistryData((LPTSTR)aszOptionsSection
                     , (LPTSTR)aszIgnoreRegistryCheck
                     , NULL
                     , (LPBYTE)&fIgnore
                     , sizeof fIgnore);
    
    return (fIgnore != 0L);
}