File indexing completed on 2024-04-21 05:42:25

0001 /*
0002   SPDX-FileCopyrightText: 2003-2006, Sergey Zorin. All rights reserved.
0003   SPDX-FileCopyrightText:  2018-2020 Michael Reeves reeves.87@gmail.com
0004   SPDX-License-Identifier: BSD-2-Clause
0005 */
0006 
0007 #define _CRT_NON_CONFORMING_SWPRINTFS
0008 
0009 #include "server.h"
0010 
0011 #include <stdio.h>
0012 
0013 #include <tchar.h>
0014 
0015 #include <shlguid.h>
0016 #include <olectl.h>
0017 #include <objidl.h>
0018 
0019 #include <objbase.h>
0020 #include <initguid.h>
0021 
0022 #include <KLocalizedString>
0023 
0024 #include "class_factory.h"
0025 
0026 #define DllExport   __declspec( dllexport )
0027 
0028 // registry key util struct
0029 struct REGSTRUCT {
0030   LPCTSTR subkey;
0031   LPCTSTR name;
0032   LPCTSTR value;
0033 };
0034 
0035 SERVER* SERVER::_instance = 0;
0036 static HINSTANCE server_instance; // Handle to this DLL itself.
0037 
0038 //DEFINE_GUID(CLSID_DIFF_EXT, 0xA0482097, 0xC69D, 0x4DEC, 0x8A, 0xB6, 0xD3, 0xA2, 0x59, 0xAC, 0xC1, 0x51);
0039 // New class id for DIFF_EXT for KDiff3
0040 #ifdef Q_OS_WIN64
0041 // {34471FFB-4002-438b-8952-E4588D0C0FE9}
0042 DEFINE_GUID( CLSID_DIFF_EXT, 0x34471FFB, 0x4002, 0x438b, 0x89, 0x52, 0xE4, 0x58, 0x8D, 0x0C, 0x0F, 0xE9 );
0043 #else
0044 #error unsupported configuration
0045 #endif
0046 
0047 tstring SERVER::getRegistryKeyString( const tstring& subKey, const tstring& value )
0048 {
0049    tstring keyName = m_registryBaseName;
0050    if (!subKey.empty())
0051       keyName += TEXT("\\")+subKey;
0052 
0053    HKEY key;
0054    HKEY baseKey = HKEY_CURRENT_USER;
0055    tstring result;
0056    for(;;)
0057    {
0058       if( RegOpenKeyEx( baseKey, keyName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &key ) == ERROR_SUCCESS )
0059       {
0060          DWORD neededSizeInBytes = 0;
0061          if (RegQueryValueEx(key, value.c_str(), 0, 0, 0, &neededSizeInBytes) == ERROR_SUCCESS)
0062          {
0063             DWORD length = neededSizeInBytes / sizeof( TCHAR );
0064             result.resize( length );
0065             if ( RegQueryValueEx( key, value.c_str(), 0, 0, (LPBYTE)&result[0], &neededSizeInBytes ) == ERROR_SUCCESS)
0066             {
0067                //Everything is ok, but we want to cut off the terminating 0-character
0068                result.resize( length - 1 );
0069                RegCloseKey(key);
0070                return result;
0071             }
0072             else
0073             {
0074                result.resize(0);
0075             }
0076          }
0077 
0078          RegCloseKey(key);
0079       }
0080       if (baseKey==HKEY_LOCAL_MACHINE)
0081          break;
0082       baseKey = HKEY_LOCAL_MACHINE;
0083    }
0084 
0085    // Error
0086    {
0087       LPTSTR message;
0088       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
0089          GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &message, 0, 0);
0090       ERRORLOG( (tstring(TEXT("RegOpenKeyEx: ")+keyName+TEXT("->")+value) + TEXT(": ")) + message );                                        \
0091       LocalFree(message);
0092    }
0093    return result;
0094 }
0095 
0096 
0097 STDAPI
0098 DllCanUnloadNow(void) {
0099   HRESULT ret = S_FALSE;
0100 
0101   if(SERVER::instance()->reference_count() == 0) {
0102     ret = S_OK;
0103   }
0104 
0105   return ret;
0106 }
0107 
0108 extern "C" int APIENTRY
0109 DllMain(HINSTANCE instance, DWORD reason, LPVOID /* reserved */) {
0110 //  char str[1024];
0111 //  char* reason_string[] = {"DLL_PROCESS_DETACH", "DLL_PROCESS_ATTACH", "DLL_THREAD_ATTACH", "DLL_THREAD_DETACH"};
0112 //  sprintf(str, "instance: %x; reason: '%s'", instance, reason_string[reason]);
0113 //  MessageBox(0, str, TEXT("Info"), MB_OK);
0114   switch (reason) {
0115     case DLL_PROCESS_ATTACH:
0116       server_instance = instance;
0117       SERVER::instance()->save_history();
0118       MESSAGELOG(TEXT("DLL_PROCESS_ATTACH"));
0119       break;
0120 
0121     case DLL_PROCESS_DETACH:
0122       MESSAGELOG(TEXT("DLL_PROCESS_DETACH"));
0123       SERVER::instance()->save_history();
0124       break;
0125   }
0126 
0127   return 1;
0128 }
0129 
0130 STDAPI
0131 DllGetClassObject(REFCLSID rclsid, REFIID riid, void** class_object) {
0132   HRESULT ret = CLASS_E_CLASSNOTAVAILABLE;
0133   *class_object = 0;
0134 
0135   if (IsEqualIID(rclsid, CLSID_DIFF_EXT)) {
0136     CLASS_FACTORY* pcf = new CLASS_FACTORY();
0137 
0138     ret = pcf->QueryInterface(riid, class_object);
0139   }
0140 
0141   return ret;
0142 }
0143 
0144 /*extern "C" HRESULT STDAPICALLTYPE*/  STDAPI
0145 DllRegisterServer() {
0146   return SERVER::instance()->do_register();
0147 }
0148 
0149 STDAPI
0150 DllUnregisterServer() {
0151   return SERVER::instance()->do_unregister();
0152 }
0153 
0154 SERVER* SERVER::instance()
0155 {
0156    if(_instance == nullptr)
0157    {
0158       _instance = new SERVER();
0159       _instance->initLogging();
0160       MESSAGELOG(TEXT("New Server instance"));
0161    }
0162 
0163    return _instance;
0164 }
0165 
0166 SERVER::SERVER()  : _reference_count(0)
0167 {
0168    m_registryBaseName = TEXT("Software\\KDE e.V.\\KDiff3\\diff-ext");
0169    m_pRecentFiles = 0;
0170    m_pLogFile = 0;
0171 }
0172 
0173 void SERVER::initLogging()
0174 {
0175    tstring logFileName = getRegistryKeyString( TEXT(""), TEXT("LogFile") );
0176    if ( !logFileName.empty() )
0177    {
0178       m_pLogFile = _tfopen( logFileName.c_str(), TEXT("a+, ccs=UTF-8") );
0179       if (m_pLogFile)
0180       {
0181          _ftprintf( m_pLogFile, TEXT("\nSERVER::SERVER()\n") );
0182       }
0183    }
0184 }
0185 
0186 SERVER::~SERVER()
0187 {
0188    if ( m_pLogFile )
0189    {
0190       _ftprintf( m_pLogFile, TEXT("SERVER::~SERVER()\n\n") );
0191       fclose( m_pLogFile );
0192    }
0193 
0194    delete m_pRecentFiles;
0195 }
0196 
0197 HINSTANCE
0198 SERVER::handle() const
0199 {
0200    return server_instance;
0201 }
0202 
0203 void
0204 SERVER::lock() {
0205   InterlockedIncrement(&_reference_count);
0206 }
0207 
0208 void
0209 SERVER::release() {
0210   InterlockedDecrement(&_reference_count);
0211 
0212   //if(InterlockedDecrement((LPLONG)&_reference_count) == 0)
0213   //   delete this;
0214 }
0215 
0216 void SERVER::logMessage( const char* function, const char* file, int line, const tstring& msg )
0217 {
0218    SERVER* pServer = SERVER::instance();
0219    if ( pServer && pServer->m_pLogFile )
0220    {
0221       SYSTEMTIME st;
0222       GetSystemTime( &st );
0223       _ftprintf( pServer->m_pLogFile, TEXT("%04d/%02d/%02d %02d:%02d:%02d ")
0224          TEXT("%S (%S:%d) %s\n"), // integrate char-string into wchar_t string
0225          st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, function, file, line, msg.c_str() );
0226       fflush(pServer->m_pLogFile);
0227    }
0228 }
0229 
0230 std::list<tstring>&
0231 SERVER::recent_files()
0232 {
0233    LOG();
0234    if ( m_pRecentFiles==0 )
0235    {
0236       m_pRecentFiles = new std::list<tstring>;
0237    }
0238    else
0239    {
0240       m_pRecentFiles->clear();
0241    }
0242    MESSAGELOG(TEXT("Reading history from registry..."));
0243    for( int i=0; i<32; ++i )  // Max history size
0244    {
0245       TCHAR numAsString[10];
0246       _sntprintf( numAsString, 10, TEXT("%d"), i );
0247       tstring historyItem = getRegistryKeyString( TEXT("history"), numAsString );
0248       if ( ! historyItem.empty() )
0249          m_pRecentFiles->push_back( historyItem );
0250    }
0251    return *m_pRecentFiles;
0252 }
0253 
0254 void
0255 SERVER::save_history() const
0256 {
0257    if( m_pRecentFiles )
0258    {
0259       HKEY key;
0260       if( RegCreateKeyEx(HKEY_CURRENT_USER, (m_registryBaseName + TEXT("\\history")).c_str(), 0, 0,
0261                          REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_WOW64_64KEY, 0, &key, 0) == ERROR_SUCCESS )
0262       {
0263          LOG();
0264          //DWORD len = MAX_PATH;
0265          int n = 0;
0266 
0267          std::list<tstring>::const_iterator i;
0268 
0269          for(i = m_pRecentFiles->begin(); i!=m_pRecentFiles->end(); ++i, ++n )
0270          {
0271             tstring str = *i;
0272             TCHAR numAsString[10];
0273             _sntprintf( numAsString, 10, TEXT("%d"), n );
0274             if(RegSetValueEx(key, numAsString, 0, REG_SZ, (const BYTE*)str.c_str(), (DWORD)(str.size()+1)*sizeof(TCHAR) ) != ERROR_SUCCESS)
0275             {
0276                LPTSTR message;
0277                FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
0278                   GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
0279                   (LPTSTR) &message, 0, 0);
0280                MessageBox(0, message, TEXT("KDiff3-diff-ext: Save history failed"), MB_OK | MB_ICONINFORMATION);
0281                LocalFree(message);
0282             }
0283          }
0284          for(; n<32; ++n )
0285          {
0286             TCHAR numAsString[10];
0287             _sntprintf( numAsString, 10, TEXT("%d"), n );
0288             RegDeleteValue(key, numAsString );
0289          }
0290 
0291          RegCloseKey(key);
0292       }
0293       else
0294       {
0295          SYSERRORLOG(TEXT("RegOpenKeyEx"));
0296       }
0297    }
0298 }
0299 
0300 HRESULT
0301 SERVER::do_register() {
0302    LOG();
0303   TCHAR   class_id[MAX_PATH];
0304   LPWSTR  tmp_guid;
0305   HRESULT ret = SELFREG_E_CLASS;
0306 
0307   if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) {
0308     _tcsncpy(class_id, tmp_guid, MAX_PATH);
0309 
0310     CoTaskMemFree((void*)tmp_guid);
0311 
0312     TCHAR    subkey[MAX_PATH];
0313     TCHAR    server_path[MAX_PATH];
0314     HKEY     key;
0315     LRESULT  result = NOERROR;
0316     DWORD    dwDisp;
0317 
0318     GetModuleFileName(SERVER::instance()->handle(), server_path, MAX_PATH);
0319 
0320     REGSTRUCT entry[] = {
0321       {TEXT("Software\\Classes\\CLSID\\%s"), 0, TEXT("kdiff3ext")},
0322       {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, TEXT("%s")},
0323       {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), TEXT("ThreadingModel"), TEXT("Apartment")}
0324     };
0325 
0326     for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) {
0327       _sntprintf(subkey, MAX_PATH, entry[i].subkey, class_id);
0328       result = RegCreateKeyEx(HKEY_CURRENT_USER, subkey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp);
0329 
0330       if(result == NOERROR) {
0331         TCHAR szData[MAX_PATH];
0332 
0333         _sntprintf(szData, MAX_PATH, entry[i].value, server_path);
0334         szData[MAX_PATH-1]=0;
0335 
0336         result = RegSetValueEx(key, entry[i].name, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR)));
0337       }
0338 
0339       RegCloseKey(key);
0340     }
0341 
0342     if(result == NOERROR) {
0343       result = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\kdiff3ext"), 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp);
0344 
0345       if(result == NOERROR) {
0346 
0347         result = RegSetValueEx(key, 0, 0, REG_SZ, (LPBYTE)class_id, DWORD(_tcslen(class_id)*sizeof(TCHAR)));
0348 
0349         RegCloseKey(key);
0350 
0351         // NT needs to have shell extensions "approved".
0352          result = RegCreateKeyEx(HKEY_CURRENT_USER,
0353             TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"),
0354             0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp);
0355 
0356          if(result == NOERROR) {
0357          TCHAR szData[MAX_PATH];
0358 
0359          lstrcpy(szData, TEXT("diff-ext"));
0360 
0361          result = RegSetValueEx(key, class_id, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR)));
0362 
0363          RegCloseKey(key);
0364 
0365          ret = S_OK;
0366          } else if (result == ERROR_ACCESS_DENIED) {
0367       TCHAR msg[] = TEXT("Warning! You have unsufficient rights to write to a specific registry key.\n")
0368          TEXT("The application may work anyway, but it is advised to register this module ")
0369          TEXT("again while having administrator rights.");
0370 
0371         MessageBox(0, msg, TEXT("Warning"), MB_ICONEXCLAMATION);
0372 
0373         ret = S_OK;
0374           }
0375       }
0376     }
0377   }
0378 
0379   return ret;
0380 }
0381 
0382 HRESULT
0383 SERVER::do_unregister() {
0384    LOG();
0385   TCHAR class_id[MAX_PATH];
0386   LPWSTR tmp_guid;
0387   HRESULT ret = SELFREG_E_CLASS;
0388 
0389   if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) {
0390     _tcsncpy(class_id, tmp_guid, MAX_PATH);
0391 
0392     CoTaskMemFree((void*)tmp_guid);
0393 
0394     LRESULT result = NOERROR;
0395     TCHAR subkey[MAX_PATH];
0396 
0397     REGSTRUCT entry[] = {
0398       {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, 0},
0399       {TEXT("Software\\Classes\\CLSID\\%s"), 0, 0}
0400     };
0401 
0402     for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) {
0403       _stprintf(subkey, entry[i].subkey, class_id);
0404       result = RegDeleteKey(HKEY_CURRENT_USER, subkey);
0405     }
0406 
0407     if(result == NOERROR) {
0408       result = RegDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\kdiff3ext"));
0409 
0410       if(result == NOERROR) {
0411         // NT needs to have shell extensions "approved".
0412          HKEY key;
0413 
0414          RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), 0, KEY_ALL_ACCESS, &key);
0415 
0416          result = RegDeleteValue(key, class_id);
0417 
0418          RegCloseKey(key);
0419 
0420          if(result == ERROR_SUCCESS) {
0421          ret = S_OK;
0422          }
0423       }
0424     }
0425   }
0426 
0427   return ret;
0428 }