1: /*
     2:  ============================================================================
     3:  xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
     4:  Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
     5:  Copyright © 2001-2004 The Blackbox for Windows Development Team
     6:  http://xoblite.net/ + https://github.com/xoblite/
     7:  ============================================================================
     8: 
     9:   Blackbox for Windows is free software, released under the
    10:   GNU General Public License (GPL version 2 or later), with an extension
    11:   that allows linking of proprietary modules under a controlled interface.
    12:   What this means is that plugins etc. are allowed to be released
    13:   under any license the author wishes. Please note, however, that the
    14:   original Blackbox gradient math code used in Blackbox for Windows
    15:   is available under the BSD license.
    16: 
    17:   http://www.fsf.org/licenses/gpl.html
    18:   http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
    19:   http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
    20: 
    21:   This program is distributed in the hope that it will be useful,
    22:   but WITHOUT ANY WARRANTY; without even the implied warranty of
    23:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    24:   GNU General Public License for more details.
    25: 
    26:   For additional license information, please read the included license.html.
    27: 
    28:  ============================================================================
    29: */
    30: 
    31: #include "..\API\BBApi.h" 
    32: #include "PluginManager.h" 
    33: #include "..\Dock\Dock.h" 
    34: #include "..\Toolbar\Toolbar.h" 
    35: #include "..\Desktop\Desktop.h" 
    36: #include <windows.h> 
    37: #include <vector> 
    38: #include <string> 
    39: #include <process.h> 
    40: 
    41: extern PluginManager *pPluginManager;
    42: extern Settings *pSettings;
    43: extern Dock *pDock;
    44: extern Toolbar *pToolbar;
    45: extern Desktop *pDesktop;
    46: 
    47: //===========================================================================
    48: 
    49: PluginManager::PluginManager(HINSTANCE hMainInstance)
    50: {
    51:     char szBuffer[MAX_LINE_LENGTH]="";
    52: 
    53:     startupLock = true;
    54: 
    55:     // SAFE MODE: Hold down control to disable loading of plugins at startup
    56:     // ### Beware! -> Do not load any plugins when running in safe mode or you
    57:     // will overwrite the contents of your plugins.rc! ###
    58:     if (!(GetAsyncKeyState(VK_CONTROL) & 0x8000))
    59:     {
    60:         FILE *f = FileOpen(plugrcPath());
    61:         while (ReadNextCommand(f, szBuffer, sizeof(szBuffer))) LoadPlugin(szBuffer);
    62:         FileClose(f);
    63:     }
    64:     else SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_WARNING_MESSAGE, (LPARAM)"Safe mode: Holding down the Ctrl key disables loading of plugins at startup.");
    65: 
    66:     //====================
    67: 
    68:     if (!pluginList.empty())
    69:     {
    70:         char szCurrentPlugin[MAX_LINE_LENGTH] = "", szPreviousPlugin[MAX_LINE_LENGTH] = "";
    71:         char szMultiplier[10];
    72:         int numberOfInstances = 1;
    73: 
    74:         // Display the names of all loaded plugins in the console...
    75:         // (nb. grouped together if more than one plugin of the same name is loaded in succession)
    76:         strcpy_s(szBuffer, sizeofArray(szBuffer), "Loading plugins -> ");
    77:         for (int i=0; i < (int)pluginList.size(); i++)
    78:         {
    79:             pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[i].hModule, "pluginInfo");
    80:             if (pluginInfo)
    81:             {
    82:                 strcpy_s(szCurrentPlugin, sizeofArray(szCurrentPlugin), pluginInfo(PLUGIN_NAME));
    83: 
    84:                 if (!_stricmp(szCurrentPlugin, szPreviousPlugin))
    85:                 {
    86:                     numberOfInstances++;
    87:                     continue;
    88:                 }
    89:                 else
    90:                 {
    91:                     if (numberOfInstances > 1)
    92:                     {
    93:                         sprintf_s(szMultiplier, sizeofArray(szMultiplier), " x %d", numberOfInstances);
    94:                         strcat_s(szBuffer, sizeofArray(szBuffer), szMultiplier);
    95:                         numberOfInstances = 1;
    96:                     }
    97: 
    98:                     if (i > 0) strcat_s(szBuffer, sizeofArray(szBuffer), " | ");
    99:                     strcat_s(szBuffer, sizeofArray(szBuffer), szCurrentPlugin);
   100: 
   101:                     strcpy_s(szPreviousPlugin, sizeofArray(szPreviousPlugin), szCurrentPlugin);
   102:                 }
   103:             }
   104:             else // Extract name from file name...
   105:             {
   106:                 char pluginName[MAX_PATH];
   107:                 strcpy_s(pluginName, sizeofArray(pluginName), pluginList[i].path);
   108:                 int n = GetParentFolder(pluginName);
   109:                 strcpy_s(pluginName, sizeofArray(pluginName), &pluginList[i].path[n]);
   110:                 PathRemoveExtension(pluginName);
   111:                 if (i > 0) strcat_s(szBuffer, sizeofArray(szBuffer), " | ");
   112:                 strcat_s(szBuffer, sizeofArray(szBuffer), pluginName);
   113:             }
   114:         }
   115: 
   116:         if (numberOfInstances > 1)
   117:         {
   118:             sprintf_s(szMultiplier, sizeofArray(szMultiplier), " x %d", numberOfInstances);
   119:             strcat_s(szBuffer, sizeofArray(szBuffer), szMultiplier);
   120:         }
   121: 
   122:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)szBuffer);
   123:     }
   124: 
   125:     //====================
   126: 
   127:     startupLock = false;
   128:     pSettings->checkingForUpdates = false;
   129: 
   130:     // Automatic checking for updates at startup? (note: call later moved to Blackbox.cpp, kept here only for reference)
   131: //  if (_stricmp(pSettings->checkedForUpdates, "*Disabled*")) _beginthread(periodicCheckForUpdates, 0, NULL);
   132: }
   133: 
   134: //====================
   135: 
   136: PluginManager::~PluginManager()
   137: {
   138:     for (int i=0; i < (int)pluginList.size(); i++)
   139:     {
   140:         endPlugin = (endPluginFunc)GetProcAddress(pluginList[i].hModule, "endPlugin");
   141:         endPlugin(pluginList[i].hModule);
   142:         FreeLibrary(pluginList[i].hModule);
   143:     }
   144: 
   145:     pluginList.clear();
   146: }
   147: 
   148: //===========================================================================
   149: 
   150: bool PluginManager::LoadPlugin(LPSTR pluginPath)
   151: {
   152:     char msg[MAX_LINE_LENGTH];
   153: 
   154:     //====================
   155:     
   156:     if (strchr(pluginPath, '\"')) StrRemoveEncap(pluginPath);
   157:     if (strchr(pluginPath, '$')) ReplaceShellFolders(pluginPath);
   158:     if (strchr(pluginPath, '%')) ReplaceEnvVars(pluginPath);
   159:     
   160:     if (IsInString(pluginPath, "BBSystemBar.dll") ||        // Do not load the BBSystemBar plugin (integrated functionality in xoblite)
   161:         IsInString(pluginPath, "BBSystemBarEx.dll") ||      // Do not load the BBSystemBarEx plugin (similar to integrated functionality in xoblite)
   162:         IsInString(pluginPath, "SystemBarEx.dll") ||        // Do not load the SystemBarEx plugin (similar to integrated functionality in xoblite)
   163:         IsInString(pluginPath, "BBSlit.dll") ||             // Do not load the BBSlit plugin (integrated functionality in xoblite)
   164:         IsInString(pluginPath, "BBKeys.dll") ||             // Do not load the BBKeys plugin (integrated functionality in xoblite)
   165:         IsInString(pluginPath, "RandomStyle.dll") ||        // Do not load the RandomStyle plugin (integrated functionality in xoblite)
   166:         IsInString(pluginPath, "BBDDE.dll"))                // Do not load the BBDDE plugin (integrated functionality in xoblite)
   167:     {
   168:         strcpy_s(msg, sizeofArray(msg), "The following plugins are automatically excluded   \n"
   169:             "because the functionality they provide (or similar   \n"
   170:             "functionality) is already part of xoblite itself:   \n\n"
   171:             "BBSystemBar\nBBSystemBarEx/SystemBarEx\nBBSlit\nBBKeys\nRandomStyle\nBBDDE   \n");
   172:         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
   173:         return false;
   174:     }
   175: 
   176:     //====================
   177: 
   178:     // Check if this particular plugin has already been loaded...
   179: 
   180:     char dllName[MAX_LINE_LENGTH], dllTempName[MAX_LINE_LENGTH];
   181:     strcpy_s(dllName, sizeofArray(dllName), pluginPath);
   182:     int nLen = strnlen_s(dllName, sizeofArray(dllName)) - 1;
   183:     if (IsInString(dllName, "/")) while (nLen >0 && dllName[nLen] != '/') nLen--;
   184:     else while (nLen >0 && dllName[nLen] != '\\') nLen--;
   185:     strcpy_s(dllName, sizeofArray(dllName), &pluginPath[nLen+1]);
   186: 
   187:     for (int i=0; i < (int)pluginList.size(); i++)
   188:     {
   189:         strcpy_s(dllTempName, sizeofArray(dllTempName), pluginList[i].path);
   190:         nLen = strnlen_s(dllTempName, sizeofArray(dllTempName)) - 1;
   191:         if (IsInString(dllTempName, "/")) while ((nLen > 0) && (dllTempName[nLen] != '/')) nLen--;
   192:         else while ((nLen > 0) && (dllTempName[nLen] != '\\')) nLen--;
   193:         strcpy_s(dllTempName, sizeofArray(dllTempName), &pluginList[i].path[nLen+1]);
   194: 
   195:         if (!_stricmp(dllName, dllTempName))
   196:         {
   197:             strcpy_s(msg, sizeofArray(msg), pluginPath);
   198:             strcat_s(msg, sizeofArray(msg), "   \n\nThis plugin is already loaded.   ");
   199:             MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
   200:             return false;
   201:         }
   202:     }
   203: 
   204:     //====================
   205: 
   206:     bool dockingEnabled;
   207: 
   208:     if (pluginPath[0] == '&') // Dock plugin?
   209:     {
   210:         dockingEnabled = true;
   211:         hModule = LoadLibrary(&pluginPath[1]);
   212:     }
   213:     else // Normal plugin
   214:     {
   215:         dockingEnabled = false;
   216:         hModule = LoadLibrary(pluginPath);
   217:     }
   218: 
   219:     if (hModule)
   220:     {
   221:         if (StartPlugin(hModule, pluginPath, dockingEnabled))
   222:         {
   223:             pluginItem plg;
   224:             plg.hModule = hModule;
   225:             plg.DockingEnabled = dockingEnabled;
   226:             strcpy_s(plg.path, sizeofArray(plg.path), pluginPath);
   227:             plg.UpdateAvailable = false;
   228:             pluginList.push_back(plg);
   229: 
   230:             if (IsInString(pluginPath, "xDesignerGUI.dll")) pSettings->designerModeEnabled = true; // Safeguard ;)
   231: 
   232: //          if (!startupLock) // ...writing to plugins.rc during startup is not a good idea... <g>
   233:             else if (!startupLock) // ...writing to plugins.rc during startup is not a good idea... <g>
   234:             {
   235:                 if (!WritePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   236:             }
   237: 
   238:             return true;
   239:         }
   240:     }
   241:     else
   242:     {
   243:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   244:         strcat_s(msg, sizeofArray(msg), " (LoadLibrary failed)");
   245:         Log("Plugin error", msg);
   246:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   247:         strcat_s(msg, sizeofArray(msg), "   \n\nThe plugin you are trying to load does not exist,   \nor is not compatible with your operating system.   \n\nPlease check your plugin configuration file.   ");
   248:         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   249:     }
   250: 
   251:     return false;
   252: }
   253: 
   254: //===========================================================================
   255: 
   256: bool PluginManager::StartPlugin(HMODULE hModule, LPSTR pluginPath, bool dockingEnabled)
   257: {
   258:     beginPluginEx = (beginPluginExFunc)GetProcAddress(hModule, "beginPluginEx");
   259:     beginPlugin = (beginPluginFunc)GetProcAddress(hModule, "beginPlugin");
   260:     beginSlitPlugin = (beginSlitPluginFunc)GetProcAddress(hModule, "beginSlitPlugin");
   261:     endPlugin = (endPluginFunc)GetProcAddress(hModule, "endPlugin");
   262: 
   263:     if (!beginPluginEx && !beginPlugin && !beginSlitPlugin)
   264:     {
   265:         FreeLibrary(hModule);
   266:         char msg[MAX_LINE_LENGTH];
   267:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   268:         strcat_s(msg, sizeofArray(msg), " (entry point missing)");
   269:         Log("Plugin failed", msg);
   270:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   271:         strcat_s(msg, sizeofArray(msg), "   \n\nThe plugin you are trying to load is   \nmissing the necessary entry point.   \n\nPlease contact the plugin author.   ");
   272:         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   273:         return false;
   274:     }
   275: 
   276:     if (!endPlugin)
   277:     {
   278:         FreeLibrary(hModule);
   279:         char msg[MAX_LINE_LENGTH];
   280:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   281:         strcat_s(msg, sizeofArray(msg), " (exit point missing)");
   282:         Log("Plugin failed", msg);
   283:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   284:         strcat_s(msg, sizeofArray(msg), "   \n\nThe plugin you are trying to load is   \nmissing the necessary exit point.   \n\nPlease contact the plugin author.   ");
   285:         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   286:         return false;
   287:     }
   288: 
   289:     // Show plugins if they are hidden and we're not starting up...
   290:     // (needed for visibility consistency when loading a new plugin)
   291:     if (pSettings->pluginsHidden && !startupLock && !IsInString(pluginPath, "xDesignerGUI.dll")) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);
   292: 
   293:     //====================
   294: 
   295:     try
   296:     {
   297:         if (pDock)
   298:         {
   299:             if (beginPluginEx) beginPluginEx(hModule, pDock->hDockWnd);
   300:             else if (beginSlitPlugin && dockingEnabled) beginSlitPlugin(hModule, pDock->hDockWnd);
   301:             else if (beginPlugin) beginPlugin(hModule);
   302:             else if (beginSlitPlugin) beginSlitPlugin(hModule, pDock->hDockWnd);
   303:         }
   304:         else if (beginPlugin) beginPlugin(hModule);
   305:     }
   306:     catch (...)
   307:     {
   308:         FreeLibrary(hModule);
   309:         char msg[MAX_LINE_LENGTH];
   310:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   311:         strcat_s(msg, sizeofArray(msg), " (try/catch exception)");
   312:         Log("Plugin failed", msg);
   313:         strcpy_s(msg, sizeofArray(msg), pluginPath);
   314:         strcat_s(msg, sizeofArray(msg), "   \n\nThe plugin you are trying to load has failed.   \n\nPlease contact the plugin author.   ");
   315:         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   316:         return false;
   317:     }
   318: 
   319:     //====================
   320: 
   321:     return true;
   322: }
   323: 
   324: //===========================================================================
   325: 
   326: void PluginManager::UnloadPlugin(LPSTR pluginPath, int pluginNumber)
   327: {
   328:     if (pluginNumber != -1)
   329:     {
   330:         endPlugin = (endPluginFunc)GetProcAddress(pluginList[pluginNumber].hModule, "endPlugin");
   331:         endPlugin(pluginList[pluginNumber].hModule);
   332:         FreeLibrary(pluginList[pluginNumber].hModule);
   333: 
   334:         if (IsInString(pluginList[pluginNumber].path, "xDesignerGUI.dll")) pSettings->designerModeEnabled = false; // Safeguard ;)
   335: 
   336:         pluginList.erase(pluginList.begin() + pluginNumber);
   337: 
   338:         if (!WritePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   339: 
   340:         return;
   341:     }
   342: 
   343:     //====================
   344: 
   345:     if (strchr(pluginPath, '\"')) StrRemoveEncap(pluginPath);
   346:     if (strchr(pluginPath, '$')) ReplaceShellFolders(pluginPath);
   347:     if (strchr(pluginPath, '%')) ReplaceEnvVars(pluginPath);
   348: 
   349:     char dllName[MAX_LINE_LENGTH], dllTempName[MAX_LINE_LENGTH];
   350:     int nLen;
   351: 
   352:     // Extract the name of the .dll to unload (disregard the full path)...
   353:     strcpy_s(dllName, sizeofArray(dllName), pluginPath);
   354:     nLen = strnlen_s(dllName, sizeofArray(dllName)) - 1;
   355:     if (strchr(dllName, '/')) while (nLen >0 && dllName[nLen] != '/') nLen--;
   356:     else while (nLen >0 && dllName[nLen] != '\\') nLen--;
   357:     strcpy_s(dllName, sizeofArray(dllName), &pluginPath[nLen+1]);
   358: 
   359:     //====================
   360: 
   361:     for (int i=0; i < (int)pluginList.size(); i++)
   362:     {
   363:         // Extract the name of the current .dll (disregard the full path)...
   364:         strcpy_s(dllTempName, sizeofArray(dllTempName), pluginList[i].path);
   365:         nLen = strnlen_s(dllTempName, sizeofArray(dllTempName)) - 1;
   366:         if (strchr(dllName, '/')) while (nLen >0 && dllTempName[nLen] != '/') nLen--;
   367:         else while (nLen >0 && dllTempName[nLen] != '\\') nLen--;
   368:         strcpy_s(dllTempName, sizeofArray(dllTempName), &pluginList[i].path[nLen+1]);
   369: 
   370:         // Is the current plugin the plugin we wish to unload?
   371:         if (!_stricmp(dllName, dllTempName))
   372:         {
   373:             endPlugin = (endPluginFunc)GetProcAddress(pluginList[i].hModule, "endPlugin");
   374:             endPlugin(pluginList[i].hModule);
   375:             FreeLibrary(pluginList[i].hModule);
   376: 
   377:             if (IsInString(pluginPath, "xDesignerGUI.dll")) pSettings->designerModeEnabled = false; // Safeguard ;)
   378: 
   379:             pluginList.erase(pluginList.begin() + i);
   380: 
   381:             if (!WritePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   382: 
   383:             return;
   384:         }
   385:     }
   386: /*
   387:     char msg[MAX_LINE_LENGTH];
   388:     strcpy_s(msg, sizeofArray(msg), pluginPath);
   389:     strcat_s(msg, sizeofArray(msg), "   \n\nThe plugin you were trying to unload was not found running.   ");
   390:     MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
   391: */
   392: }
   393: 
   394: //===========================================================================
   395: 
   396: void PluginManager::UpdatePlugin(LPSTR pluginPath, int pluginNumber, bool forceUpdate)
   397: {
   398:     if (pSettings->checkingForUpdates) return;
   399: 
   400:     char msg[MAX_LINE_LENGTH], temp[MAX_LINE_LENGTH], path[MAX_LINE_LENGTH];
   401: 
   402:     // Make sure the updates metafile exists...
   403:     GetBlackboxPath(path, sizeof(path));
   404:     strcat_s(path, sizeofArray(path), "update.log");
   405:     if (!FileExists(path) || forceUpdate)
   406:     {
   407:         bool silent = true;
   408:         _beginthread(checkForUpdates, 0, &silent);
   409:     }
   410: 
   411:     //====================
   412: 
   413:     // Display "<->" icon in toolbar.windowLabel -> update in progress!
   414:     pSettings->checkingForUpdates = true;
   415: //  if ((pSettings->toolbarMode == TOOLBAR_MODE_WINDOWLABEL) && pToolbar) pToolbar->UpdateToolbarWindow();
   416: 
   417:     //====================
   418: 
   419:     if (pluginNumber != -1)
   420:     {
   421:         pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[pluginNumber].hModule, "pluginInfo");
   422:         if (pluginInfo)
   423:         {
   424:             if (FileExists(path))
   425:             {
   426:                 char plugin[MAX_LINE_LENGTH], parameter[MAX_LINE_LENGTH];
   427:                 char onlineVersion[MAX_LINE_LENGTH], currentVersion[MAX_LINE_LENGTH];
   428:                 char onlineDate[MAX_LINE_LENGTH], currentDate[MAX_LINE_LENGTH];
   429: 
   430:                 strcpy_s(plugin, sizeofArray(plugin), pluginInfo(PLUGIN_NAME));
   431:                 strcpy_s(currentVersion, sizeofArray(currentVersion), pluginInfo(PLUGIN_VERSION));
   432:                 strcpy_s(currentDate, sizeofArray(currentDate), pluginInfo(PLUGIN_RELEASEDATE));
   433: 
   434:                 sprintf_s(parameter, sizeofArray(parameter), "%s.version:", plugin);
   435:                 strcpy_s(onlineVersion, sizeofArray(onlineVersion), ReadString(path, parameter, ""));
   436:                 if (!strnlen_s(onlineVersion, sizeofArray(onlineVersion)))
   437:                 {
   438:                     pluginList[pluginNumber].UpdateAvailable = false;
   439:                     strcpy_s(temp, sizeofArray(temp), pluginInfo(PLUGIN_UPDATE_URL));
   440:                     if (!IsInString(temp, "http://")) strcpy_s(temp, sizeofArray(temp), pSettings->pluginsRepository);
   441:                     sprintf_s(msg, sizeofArray(msg), "%s   \n\n%s is not supported by the specified repository!   ", temp, plugin);
   442:                     MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONERROR);
   443:                     return;
   444:                 }
   445:                 sprintf_s(parameter, sizeofArray(parameter), "%s.date:", plugin);
   446:                 strcpy_s(onlineDate, sizeofArray(onlineDate), ReadString(path, parameter, "xxxx-xx-xx"));
   447: 
   448:                 //====================
   449: 
   450:                 // Check if online version/date differs from the current version/date...
   451:                 if (!_stricmp(currentDate, onlineDate))
   452:                 {
   453:                     if (!_stricmp(currentVersion, onlineVersion))
   454:                     {
   455:                         pluginList[pluginNumber].UpdateAvailable = false;
   456:                         sprintf_s(msg, sizeofArray(msg), "You are already using the latest version of %s.   ", plugin);
   457:                         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
   458:                         return;
   459:                     }
   460:                 }
   461: 
   462:                 //====================
   463: 
   464:                 sprintf_s(msg, sizeofArray(msg), "%s update found!\n\nCurrent version:\t %s \t%s   \nOnline version:\t %s \t%s   ", plugin, currentVersion, currentDate, onlineVersion, onlineDate);
   465: 
   466:                 sprintf_s(parameter, sizeofArray(parameter), "%s.changes:", plugin);
   467:                 strcpy_s(temp, sizeofArray(temp), ReadString(path, parameter, ""));
   468:                 if (strnlen_s(temp, sizeofArray(temp)))
   469:                 {
   470:                     strcat_s(msg, sizeofArray(msg), "\n\nUpdate information:\n");
   471:                     strcat_s(msg, sizeofArray(msg), temp);
   472:                 }
   473: 
   474:                 strcat_s(msg, sizeofArray(msg), "\n\nDo you want to download this update?   ");
   475: 
   476:                 //====================
   477: 
   478:                 if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
   479:                 {
   480:                     int nLen;
   481:                     char token[MAX_LINE_LENGTH], extra[MAX_LINE_LENGTH], filename[MAX_LINE_LENGTH], pluginPath[MAX_LINE_LENGTH];
   482:                     char savedfiles[MAX_LINE_LENGTH] = "";
   483:                     LPSTR tokens[1];
   484:                     tokens[0] = token;
   485: 
   486:                     // Copy the path to the plugin + the URL...
   487:                     bool dllFound = false, zipFound = false;
   488: 
   489:                     if (!strstr(pluginList[pluginNumber].path, ":\\"))
   490:                     {
   491:                         // Make sure we use the full path of the plugin and
   492:                         // not just a path relative to the Blackbox folder...
   493:                         GetBlackboxPath(filename, MAX_LINE_LENGTH);
   494:                         strcat_s(filename, sizeofArray(filename), pluginList[pluginNumber].path);
   495:                     }
   496:                     else strcpy_s(filename, sizeofArray(filename), pluginList[pluginNumber].path);
   497: 
   498:                     sprintf_s(parameter, sizeofArray(parameter), "%s.files:", plugin);
   499:                     strcpy_s(temp, sizeofArray(temp), ReadString(path, parameter, ""));
   500:                     if (!strnlen_s(temp, sizeofArray(temp)))
   501:                     {
   502:                         // Download the plugin .zip archive if no
   503:                         // list of updated files was specified...
   504:                         sprintf_s(parameter, sizeofArray(parameter), "%s.archive:", plugin);
   505:                         strcpy_s(temp, sizeofArray(temp), ReadString(path, parameter, ""));
   506:                     }
   507: 
   508:                     if (IsInString(temp, ".dll")) dllFound = true;
   509:                     else if (IsInString(temp, ".zip")) zipFound = true;
   510: 
   511:                     if (!dllFound && !zipFound)
   512:                     {
   513:                         // Neither an archive (.zip) nor individual files (including at least one .dll)
   514:                         // was directly linked to -> Open the link in the user's default browser...
   515:                         if (IsInString(temp, "http")) BBSmartExecute(temp);
   516:                         else
   517:                         {
   518:                             sprintf_s(msg, sizeofArray(msg), "Invalid update link detected for %s:   \n\n%s   ", plugin, temp);
   519:                             if (strnlen_s(temp, sizeofArray(temp)) == 0) strcat_s(msg, sizeofArray(msg), "<empty>   ");
   520:                             strcat_s(msg, sizeofArray(msg), "\n\nUpdate request aborted. Click OK to continue.");
   521:                             MessageBox(GetBBWnd(), msg, "Error updating plugin!", MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
   522:                         }
   523:                         return;
   524:                     }
   525: 
   526:                     if (dllFound)
   527:                     {
   528:                         // Unload the plugin...
   529:                         strcpy_s(pluginPath, sizeofArray(pluginPath), pluginList[pluginNumber].path);
   530:                         endPlugin = (endPluginFunc)GetProcAddress(pluginList[pluginNumber].hModule, "endPlugin");
   531:                         endPlugin(pluginList[pluginNumber].hModule);
   532:                         FreeLibrary(pluginList[pluginNumber].hModule);
   533:                         pluginList.erase(pluginList.begin() + pluginNumber);
   534:                     }
   535: 
   536:                     // Download the updated files...
   537:                     while(strnlen_s(temp, sizeofArray(temp)))
   538:                     {
   539:                         token[0] = extra[0] = '\0';
   540:                         BBTokenize (temp, tokens, 1, extra);
   541:                         strcpy_s(temp, sizeofArray(temp), extra);
   542: 
   543:                         // Extract the plugin folder from the plugin path...
   544:                         nLen = strnlen_s(filename, sizeofArray(filename)) - 1;
   545:                         if (IsInString(filename, "/")) while (nLen >0 && filename[nLen] != '/') nLen--;
   546:                         else while (nLen >0 && filename[nLen] != '\\') nLen--;
   547:                         filename[nLen + 1] = 0;
   548: 
   549:                         // Extract the name of the file from the URL...
   550:                         nLen = strnlen_s(token, sizeofArray(token)) - 1;
   551:                         while (nLen >0 && token[nLen] != '/') nLen--;
   552:                         strcat_s(filename, sizeofArray(filename), &token[nLen+1]);
   553: 
   554:                         // Download the file and save it in the plugin folder...
   555:                         DownloadFile(token, filename);
   556: 
   557:                         // Copy the path of the saved file to the list of updated files...
   558:                         strcat_s(savedfiles, sizeofArray(savedfiles), "\n");
   559:                         strcat_s(savedfiles, sizeofArray(savedfiles), filename);
   560:                         strcat_s(savedfiles, sizeofArray(savedfiles), "   ");
   561:                     }
   562: 
   563:                     if (dllFound)
   564:                     {
   565:                         // Reload the plugin...
   566:                         bool dockingEnabled;
   567: 
   568:                         if (pluginPath[0] == '&') // dock plugin?
   569:                         {
   570:                             dockingEnabled = true;
   571:                             hModule = LoadLibrary(&pluginPath[1]);
   572:                         }
   573:                         else // normal plugin
   574:                         {
   575:                             dockingEnabled = false;
   576:                             hModule = LoadLibrary(pluginPath);
   577:                         }
   578: 
   579:                         if (hModule)
   580:                         {
   581:                             if (StartPlugin(hModule, pluginPath, dockingEnabled))
   582:                             {
   583:                                 pluginItem plg;
   584:                                 plg.hModule = hModule;
   585:                                 plg.DockingEnabled = dockingEnabled;
   586:                                 strcpy_s(plg.path, sizeofArray(plg.path), pluginPath);
   587:                                 plg.UpdateAvailable = false;
   588:                                 pluginList.insert(pluginList.begin() + pluginNumber, plg);
   589:                             }
   590:                         }
   591:                         else return;
   592: 
   593:                         sprintf_s(msg, sizeofArray(msg), "%s update downloaded and installed.   \n%s", plugin, savedfiles);
   594:                         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_ICONINFORMATION);
   595:                     }
   596:                     else if (zipFound)
   597:                     {
   598:                         sprintf_s(msg, sizeofArray(msg), "%s update in .zip format downloaded,   \nsee archive for further instructions.   \n%s", plugin, savedfiles);
   599:                         if (MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_ICONINFORMATION))
   600:                         {
   601:                             char arg[MAX_LINE_LENGTH];
   602:                             strcpy_s(arg, sizeofArray(arg), "/e,/select,");
   603:                             strcat_s(arg, sizeofArray(arg), savedfiles);
   604:                             BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, NULL, SW_SHOWNORMAL, false);
   605:                             return;
   606:                         }
   607:                     }
   608:                     else
   609:                     {
   610:                         sprintf_s(msg, sizeofArray(msg), "%s update downloaded.   \n%s", plugin, savedfiles);
   611:                         MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_ICONINFORMATION);
   612:                     }
   613:                 }
   614: 
   615:                 // Turn off the "update available" indicator...
   616:                 pluginList[pluginNumber].UpdateAvailable = false;
   617:             }
   618:             else MessageBox(GetBBWnd(), "Could not find the updates metafile! [update.log]   ", "xoblite", MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_ICONERROR);
   619:         }
   620:         else MessageBox(GetBBWnd(), "This plugin does not support automatic updating.   ", "xoblite", MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_ICONERROR);
   621:     }
   622: 
   623:     // Remove "<->" icon from toolbar.windowLabel -> update no longer in progress
   624:     pSettings->checkingForUpdates = false;
   625: //  if ((pSettings->toolbarMode == TOOLBAR_MODE_WINDOWLABEL) && pToolbar) pToolbar->UpdateToolbarWindow();
   626: }
   627: 
   628: //===========================================================================
   629: 
   630: void checkForUpdates(void *param)
   631: {
   632:     if (pSettings->checkingForUpdates) _endthread();
   633:     else
   634:     {
   635:         // Display "<->" icon in toolbar.windowLabel -> update in progress!
   636:         pSettings->checkingForUpdates = true;
   637: //      if ((pSettings->toolbarMode == TOOLBAR_MODE_WINDOWLABEL) && pToolbar) pToolbar->UpdateToolbarWindow();
   638: //      SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)"xoblite -> Checking for updates...");
   639:     }
   640: 
   641:     bool silent = *((bool*)param);
   642:     int updates = 0;
   643:     char listOfURLs[MAX_LINE_LENGTH] = "", path[MAX_LINE_LENGTH], msg[MAX_LINE_LENGTH], listOfUpdates[MAX_LINE_LENGTH] = "";
   644: 
   645:     //====================
   646: /*
   647:     // Extract distributed repository URLs from all
   648:     // plugins that support automatic updating...
   649:     for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
   650:     {
   651:         pPluginManager->pluginInfo = (pluginInfoFunc)GetProcAddress(pPluginManager->pluginList[i].hModule, "pluginInfo");
   652:         if (pPluginManager->pluginInfo)
   653:         {
   654:             strcpy_s(msg, sizeofArray(msg), pPluginManager->pluginInfo(PLUGIN_UPDATE_URL));
   655: 
   656:             // Make sure the string supplied by the plugin is a valid URL
   657:             // and that it isn't the same URL as for the central repository...
   658:             if (IsInString(msg, "http://") && !IsInString(msg, pSettings->pluginsRepository))
   659:             {
   660:                 if (!IsInString(listOfURLs, msg))
   661:                 {
   662:                     strcat_s(listOfURLs, sizeofArray(listOfURLs), msg);
   663:                     strcat_s(listOfURLs, sizeofArray(listOfURLs), " ");
   664:                 }
   665:             }
   666:         }
   667:     }
   668: */
   669:     // Finally, we add the central repository URL at the end of the list
   670:     // (i.e. if a plugin does not return a URL for automatic updating we fall
   671:     // back to the central plugin repository defined in xoblite.rc, which means
   672:     // that the information provided by the distributed repositories
   673:     // will have precedence in case of duplicate plugin update information! :D )
   674:     strcat_s(listOfURLs, sizeofArray(listOfURLs), pSettings->pluginsRepository);
   675: 
   676:     //====================
   677: 
   678:     if (IsInString(listOfURLs, "http://"))
   679:     {
   680:         GetBlackboxPath(path, sizeof(path));
   681:         strcat_s(path, sizeofArray(path), "update.log");
   682: 
   683:         if (FileExists(path)) remove(path); // Delete existing update.log file...
   684: 
   685:         //====================
   686: 
   687:         if (DownloadFile(listOfURLs, path))
   688:         {
   689:             char plugin[MAX_LINE_LENGTH], parameter[MAX_LINE_LENGTH];
   690:             char onlineVersion[MAX_LINE_LENGTH], currentVersion[MAX_LINE_LENGTH];
   691:             char onlineDate[MAX_LINE_LENGTH], currentDate[MAX_LINE_LENGTH];
   692:             char onlineAuthor[MAX_LINE_LENGTH];
   693: 
   694:             for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
   695:             {
   696:                 pPluginManager->pluginInfo = (pluginInfoFunc)GetProcAddress(pPluginManager->pluginList[i].hModule, "pluginInfo");
   697:                 if (pPluginManager->pluginInfo)
   698:                 {
   699:                     strcpy_s(plugin, sizeofArray(plugin), pPluginManager->pluginInfo(PLUGIN_NAME));
   700:                     strcpy_s(currentVersion, sizeofArray(currentVersion), pPluginManager->pluginInfo(PLUGIN_VERSION));
   701:                     strcpy_s(currentDate, sizeofArray(currentDate), pPluginManager->pluginInfo(PLUGIN_RELEASEDATE));
   702: 
   703:                     sprintf_s(parameter, sizeofArray(parameter), "%s.version:", plugin);
   704:                     strcpy_s(onlineVersion, sizeofArray(onlineVersion), ReadString(path, parameter, ""));
   705:                     if (!strnlen_s(onlineVersion, sizeofArray(onlineVersion)))
   706:                     {
   707:                         // The plugin was not not supported by the specified repository (or invalid syntax)...
   708:                         pPluginManager->pluginList[i].UpdateAvailable = false;
   709:                         continue;
   710:                     }
   711:                     sprintf_s(parameter, sizeofArray(parameter), "%s.date:", plugin);
   712:                     strcpy_s(onlineDate, sizeofArray(onlineDate), ReadString(path, parameter, "xxxx-xx-xx"));
   713: 
   714:                     //====================
   715: 
   716:                     // Check if online version/date differs from the current version/date...
   717:                     if (!_stricmp(currentDate, onlineDate))
   718:                     {
   719:                         if (!_stricmp(currentVersion, onlineVersion))
   720:                         {
   721:                             // If not, continue looking...
   722:                             pPluginManager->pluginList[i].UpdateAvailable = false;
   723:                             continue;
   724:                         }
   725:                     }
   726: 
   727:                     // ...but if it differs we have found an updated plugin!
   728:                     pPluginManager->pluginList[i].UpdateAvailable = true;
   729: 
   730:                     sprintf_s(parameter, sizeofArray(parameter), "%s.author:", plugin);
   731:                     strcpy_s(onlineAuthor, sizeofArray(onlineAuthor), ReadString(path, parameter, (LPSTR)pPluginManager->pluginInfo(PLUGIN_AUTHOR)));
   732: 
   733:                     sprintf_s(msg, sizeofArray(msg), "\n%s %s by %s (%s)   ", plugin, onlineVersion, onlineAuthor, onlineDate);
   734:                     strcat_s(listOfUpdates, sizeofArray(listOfUpdates), msg);
   735:                     updates++;
   736:                 }
   737:                 else pPluginManager->pluginList[i].UpdateAvailable = false;
   738:             }
   739:         }
   740:         else
   741:         {
   742:             strcpy_s(msg, sizeofArray(msg), "Note: Some repositories were not currently available,   \nwhich may affect the result of this check for updates.   ");
   743:             MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST);
   744:         }
   745:     }
   746: 
   747:     //====================
   748: 
   749:     if (!silent)
   750:     {
   751:         char xobVersion[MAX_LINE_LENGTH], xobOnlineVersion[MAX_LINE_LENGTH];
   752:         ULONG *size = 0;
   753:         void *versionPtr;
   754:         UINT bytes;
   755: 
   756:         // Extract currently running xoblite version...
   757:         // (make sure the correct version encoding is used, currently "000004b0" = language neutral)
   758:         DWORD number = GetFileVersionInfoSize("Blackbox.exe", size);
   759:         char temp[MAX_LINE_LENGTH];
   760:         GetFileVersionInfo("Blackbox.exe", NULL, number, (LPVOID)temp);
   761:         VerQueryValue(temp, TEXT("\\StringFileInfo\\000004b0\\FileVersion"), &versionPtr, &bytes);
   762:         strcpy_s(xobVersion, sizeofArray(xobVersion), (LPCSTR)versionPtr);
   763:         if ((xobVersion[strnlen_s(xobVersion, sizeofArray(xobVersion))-1] == '\n') || (xobVersion[strnlen_s(xobVersion, sizeofArray(xobVersion))-1] == '\r')) xobVersion[strnlen_s(xobVersion, sizeofArray(xobVersion))-1] = '\0'; // Failsafe
   764: 
   765:         // ...and compare that with the xoblite version available online...
   766:         strcpy_s(xobOnlineVersion, sizeofArray(xobOnlineVersion), ReadString(path, "xoblite.version:", ""));
   767:         if ((xobOnlineVersion[strnlen_s(xobOnlineVersion, sizeofArray(xobOnlineVersion))-1] == '\n') || (xobOnlineVersion[strnlen_s(xobOnlineVersion, sizeofArray(xobOnlineVersion))-1] == '\r')) xobOnlineVersion[strnlen_s(xobOnlineVersion, sizeofArray(xobOnlineVersion))-1] = '\0'; // Failsafe
   768: 
   769:         bool xobliteUpdateFound = false;
   770:         if (strnlen_s(xobOnlineVersion, sizeofArray(xobOnlineVersion)) && _stricmp(xobVersion, xobOnlineVersion)) xobliteUpdateFound = true;
   771: 
   772:         //====================
   773: 
   774:         // Display result of the check for updates in the console...
   775:         if (updates > 0)
   776:         {
   777:             if (xobliteUpdateFound) sprintf_s(msg, sizeofArray(msg), "Checking for updates -> xoblite %s and %d updated plugin", xobOnlineVersion, updates);
   778:             else sprintf_s(msg, sizeofArray(msg), "Checking for updates -> %d updated plugin", updates);
   779:             if (updates > 1) strcat_s(msg, sizeofArray(msg), "s");
   780:             strcat_s(msg, sizeofArray(msg), " available.");
   781:         }
   782:         else if (xobliteUpdateFound) sprintf_s(msg, sizeofArray(msg), "Checking for updates -> xoblite %s available.", xobOnlineVersion);
   783:         else strcpy_s(msg, sizeofArray(msg), "Checking for updates -> No updates were found.");
   784: 
   785:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)msg);
   786: 
   787:         //====================
   788: 
   789:         if (xobliteUpdateFound)
   790:         {
   791:             sprintf_s(msg, sizeofArray(msg), "Shell update found!   \n\nCurrent version:   \nxoblite %s   \n\nOnline version:   \nxoblite %s   \n\nDo you want to download this update?   \n(clicking \"Yes\" will take you to the xoblite website)   ", xobVersion, xobOnlineVersion);
   792: 
   793:             if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
   794:             {
   795:                 // Choosing to download an update will (for now) take the user to the xoblite
   796:                 // website to manually download the latest build (mostly to reduce maintenance
   797:                 // overhead, however this could possibly change in a future release...)
   798:                 BBExecute(GetDesktopWindow(), NULL, "http://xoblite.net/", NULL, NULL, SW_SHOWNORMAL, true);
   799:             }
   800: 
   801:             // Choosing to download an update will (for now) take the user to the xoblite
   802:             // website to manually download the latest build (mostly to reduce maintenance
   803:             // overhead, however this could possibly change in a future release...)
   804: //          SendMessage(GetBBWnd(), BB_POPUPMESSAGE, (WPARAM)msg, (LPARAM)"@GoToURL http://xoblite.net/");
   805:         }
   806: 
   807:         //====================
   808: 
   809:         if (updates > 0)
   810:         {
   811:             sprintf_s(msg, sizeofArray(msg), "Found %d updated plugins:   \n", updates);
   812:             strcat_s(msg, sizeofArray(msg), listOfUpdates);
   813:             if (updates == 1) strcat_s(msg, sizeofArray(msg), "\n\nDo you want to download this update?   ");
   814:             else strcat_s(msg, sizeofArray(msg), "\n\nDo you want to download any of these updates?   ");
   815: 
   816:             if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
   817:             {
   818:                 for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
   819:                 {
   820:                     if (pPluginManager->pluginList[i].UpdateAvailable)
   821:                     {
   822:                         pSettings->checkingForUpdates = false; // Disable update lock to access updatePlugin() ...
   823:                         pPluginManager->UpdatePlugin(NULL, i, false);
   824:                         pSettings->checkingForUpdates = true; // Re-enable update lock...
   825:                     }
   826:                 }
   827:             }
   828:         }
   829:         else
   830:         {
   831:             strcpy_s(msg, sizeofArray(msg), "No updated plugins were found.   ");
   832:             MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST);
   833:         }
   834:     }
   835: 
   836:     //====================
   837: 
   838:     // Finally, we update xoblite.checked.for.updates in xoblite.rc... (temporarily disable write protection if necessary!)
   839:     time_t systemTime;
   840:     struct tm localTime;
   841:     time(&systemTime);
   842:     localtime_s(&localTime, &systemTime);
   843: 
   844:     _locale_t timestampLocale = _create_locale(LC_TIME, "en-US"); // -> Always use en-US format for the "checked for updates" timestamp...
   845:     _strftime_l(pSettings->checkedForUpdates, 20, "#%j / %Y-%m-%d", &localTime, timestampLocale);
   846:     _free_locale(timestampLocale);
   847: 
   848:     bool tempBool = pSettings->writeProtection;
   849:     pSettings->writeProtection = false;
   850:     WriteString(pSettings->xobrcDefaultFile, "xoblite.checked.for.updates:", pSettings->checkedForUpdates);
   851:     pSettings->writeProtection = tempBool;
   852: 
   853:     //====================
   854: 
   855:     // Remove "<->" icon from toolbar.windowLabel -> update no longer in progress
   856:     pSettings->checkingForUpdates = false;
   857: //  if ((pSettings->toolbarMode == TOOLBAR_MODE_WINDOWLABEL) && pToolbar) pToolbar->UpdateToolbarWindow();
   858: 
   859:     _endthread();
   860: }
   861: 
   862: //===========================================================================
   863: 
   864: void periodicCheckForUpdates(void *)
   865: {
   866:     if (pSettings->checkingForUpdates) _endthread();
   867: 
   868:     char temp[MAX_LINE_LENGTH];// , windowLabel[25];
   869: 
   870:     // Get current day of the year...
   871:     time_t systemTime;
   872:     struct tm localTime;
   873:     time(&systemTime);
   874:     localtime_s(&localTime, &systemTime);
   875:     int currentDay = localTime.tm_yday + 1;
   876: 
   877:     // Extract day of the year for the last check...
   878: //  strncpy(temp, &pSettings->checkedForUpdates[1], 3);
   879:     strncpy_s(temp, sizeofArray(temp), &pSettings->checkedForUpdates[1], 3);
   880:     temp[3] = 0;
   881:     int checkedDay = atoi(temp);
   882: 
   883:     // Calculate the number of days since the last check for updates...
   884:     int interval;
   885:     if (currentDay >= checkedDay) interval = currentDay - checkedDay;
   886:     else interval = 366 - checkedDay + currentDay;
   887: 
   888:     //====================
   889: /*
   890:     // Is the last checked date in xoblite.rc == the default?
   891:     // If so, xoblite is running for the first time -> display a nice welcome screen! =]
   892:     if (!_stricmp(pSettings->checkedForUpdates, "#001 / 2006-01-01"))
   893:     {
   894:         interval = 365; // ...just to trigger the update messagebox below! <g>
   895:         strcpy_s(windowLabel, sizeofArray(windowLabel), "Welcome to xoblite!");
   896:         strcpy_s(temp, sizeofArray(temp),
   897:                 "You seem to be running xoblite for the first time.   \n\n"
   898:                 "xoblite features a mechanism to notify you of available updates to the core (Blackbox.exe) and your loaded plugins.   \n\n"
   899:                 "A check for updates can be triggered using:   \n"
   900:                 "- The \"Check for updates...\" item in the Configuration/Global menu   \n"
   901:                 "- The core broadcast message: @xoblite CheckForUpdates   \n"
   902:                 "- Monthly periodic checking (user is notified if/when more than 30 days have passed since the last check)   \n\n"
   903:                 "Do you want to check for updates now?   ");
   904:     }
   905:     else
   906:     {
   907:         strcpy_s(windowLabel, sizeofArray(windowLabel), "xoblite");
   908:         sprintf_s(temp, sizeofArray(temp), "%d days have passed since you last checked for updates.   \n\nDo you want to check for updates now?   ", interval);
   909:     }
   910: */
   911:     //====================
   912: 
   913:     if (interval > 30) // Has more than 30 days passed since the last check for updates?
   914:     {
   915: /*
   916:         // Ask the user if he/she wants to check for updates...
   917:         if (MessageBox(NULL, temp, windowLabel, MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
   918:         {
   919:             bool silent = false;
   920:             checkForUpdates(&silent); // We're already running in a separate thread! (NOTE: checkForUpdates will terminate the thread)
   921:         }
   922:         else
   923:         {
   924:             // Update xoblite.checked.for.updates in xoblite.rc... (temporarily disable write protection if necessary!)
   925:             _locale_t timestampLocale = _create_locale(LC_TIME, "en-US"); // -> Always use en-US format for the checked for updates timestamp...
   926:             _strftime_l(pSettings->checkedForUpdates, 20, "#%j / %Y-%m-%d", localTime, timestampLocale);
   927:             _free_locale(timestampLocale);
   928: 
   929:             bool tempBool = pSettings->writeProtection;
   930:             pSettings->writeProtection = false;
   931:             WriteString(pSettings->xobrcDefaultFile, "xoblite.checked.for.updates:", pSettings->checkedForUpdates);
   932:             pSettings->writeProtection = tempBool;
   933:         }
   934: */
   935:         bool silent = false;
   936:         checkForUpdates(&silent); // We're already running in a separate thread! (NOTE: checkForUpdates will terminate the thread)
   937:     }
   938: 
   939:     //====================
   940: 
   941:     _endthread();
   942: }
   943: 
   944: //===========================================================================
   945: 
   946: void PluginManager::AboutPlugins()
   947: {
   948:     char msg[MAX_LINE_LENGTH] = "", temp[MAX_LINE_LENGTH];
   949: 
   950:     for (int i=0; i < (int)pluginList.size(); i++)
   951:     {
   952:         pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[i].hModule, "pluginInfo");
   953:         if (pluginInfo)
   954:         {
   955:             sprintf_s(temp, sizeofArray(temp), "%s %s by %s (%s)", pluginInfo(PLUGIN_NAME), pluginInfo(PLUGIN_VERSION), pluginInfo(PLUGIN_AUTHOR), pluginInfo(PLUGIN_RELEASEDATE));
   956:             strcat_s(msg, sizeofArray(msg), temp);
   957:             if (pluginList[i].path[0] == '&') strcat_s(msg, sizeofArray(msg), " *\n"); // &-loaded plugin (beginSlitPlugin)
   958:             else strcat_s(msg, sizeofArray(msg), "\n"); // Normal plugin (beginPlugin, beginPluginEx)
   959:         }
   960:         else
   961:         {
   962:             strcat_s(msg, sizeofArray(msg), pluginList[i].path);
   963:             if (pluginList[i].path[0] == '&') strcat_s(msg, sizeofArray(msg), " *\n"); // &-loaded plugin (beginSlitPlugin)
   964:             else strcat_s(msg, sizeofArray(msg), "\n"); // Normal plugin (beginPlugin, beginPluginEx)
   965:         }
   966:     }
   967: 
   968:     if (strnlen_s(msg, sizeofArray(msg)))
   969:     {
   970:         msg[strnlen_s(msg, sizeofArray(msg))-1] = '\0'; // Remove the last line feed auto-generated above...
   971:         SendMessage(GetBBWnd(), BB_POPUPMESSAGE, (WPARAM)"About the loaded plugins...", (LPARAM)msg);
   972:     }
   973:     else SendMessage(GetBBWnd(), BB_POPUPMESSAGE, (WPARAM)"About the loaded plugins...", (LPARAM)"--- No plugins loaded ---   ");
   974: }
   975: 
   976: //===========================================================================
   977: 
   978: LPCSTR PluginManager::GetPluginInfo(int pluginNumber, int infoParam)
   979: {
   980:     pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[pluginNumber].hModule, "pluginInfo");
   981:     if (pluginInfo)
   982:     {
   983:         LPCSTR result = pluginInfo(infoParam); // e.g. PLUGIN_VERSION (see BBApi.h)
   984:         if (result) return result;
   985:         // Fetch default fallback string in case the result was NULL
   986:         // (i.e. if the requested field was not supported/defined)
   987: //      else return pluginInfo(-1);
   988:         else return pluginInfo(PLUGIN_NAME);
   989:     }
   990:     else
   991:     {
   992:         // Extract the name of the plugin...
   993:         strcpy_s(pluginName, sizeofArray(pluginName), pluginList[pluginNumber].path);
   994:         int nLen = strnlen_s(pluginName, sizeofArray(pluginName)) - 1;
   995:         if (IsInString(pluginName, "/")) while (nLen >0 && pluginName[nLen] != '/') nLen--;
   996:         else while (nLen >0 && pluginName[nLen] != '\\') nLen--;
   997:         strcpy_s(pluginName, sizeofArray(pluginName), &pluginList[pluginNumber].path[nLen+1]);
   998:         pluginName[strnlen_s(pluginName, sizeofArray(pluginName))-4] = 0; // Remove .dll at the end...
   999:         return pluginName;
  1000:     }
  1001: }
  1002: 
  1003: //===========================================================================
  1004: 
  1005: bool PluginManager::WritePluginsRC()
  1006: {
  1007:     if (pSettings->writeProtection) return true;
  1008:     
  1009:     HANDLE f = CreateFile(plugrcPath(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1010:     if (f)
  1011:     {
  1012:         char szTemp[MAX_LINE_LENGTH];
  1013:         DWORD retLength = 0;
  1014: 
  1015:         for (int i=0; i < (int)pluginList.size(); i++)
  1016:         {
  1017:             // If the full path to the plugin is specified (e.g. from drag'n'drop) and it is in
  1018:             // a subfolder to the Blackbox folder, extract the relative path to the plugin
  1019:             // -> full path minus Blackbox folder path.
  1020:             // Example: "C:\Blackbox\plugins\BBbin\BBbin.dll" becomes "plugins\BBbin\BBbin.dll"
  1021:             // This way the Blackbox folder can be moved, and the plugin path will still work! :D
  1022: 
  1023:             if (!pSettings->usingDefaultTheme && IsInString(pluginList[i].path, pSettings->SF_currentThemePath))
  1024:             {
  1025:                 strcpy_s(szTemp, sizeofArray(szTemp), "$CurrentTheme$");
  1026:                 strcat_s(szTemp, sizeofArray(szTemp), &pluginList[i].path[(strnlen_s(pSettings->SF_currentThemePath, sizeofArray(pSettings->SF_blackboxPath)))]);
  1027:             }
  1028:             else if (IsInString(pluginList[i].path, pSettings->SF_blackboxPath))
  1029:             {
  1030:                 strcpy_s(szTemp, sizeofArray(szTemp), &pluginList[i].path[(strnlen_s(pSettings->SF_blackboxPath, sizeofArray(pSettings->SF_blackboxPath))+1)]);
  1031:             }
  1032:             else strcpy_s(szTemp, sizeofArray(szTemp), pluginList[i].path);
  1033: 
  1034:             strcat_s(szTemp, sizeofArray(szTemp), "\r\n");
  1035:             WriteFile(f, szTemp, strnlen_s(szTemp, sizeofArray(szTemp)), &retLength, NULL);
  1036:         }
  1037: 
  1038:         CloseHandle(f);
  1039:         return true;
  1040:     }
  1041:     else
  1042:     {
  1043:         CloseHandle(f);
  1044:         return false;
  1045:     }
  1046: }
  1047: 
  1048: //===========================================================================
  1049: 
  1050: void PluginManager::LoadPluginDialog()
  1051: {
  1052:     char msg[MAX_LINE_LENGTH];
  1053:     strcpy_s(msg, sizeofArray(msg), "Always be careful when loading plugins and   \nrunning scripts downloaded from the internet.   \n\nEnsure they are from a trusted source.   \n\nDo you want to continue?   ");
  1054: 
  1055:     if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
  1056:     {
  1057:         char file[MAX_PATH] = "", path[MAX_PATH];
  1058: 
  1059:         if (pSettings->usingDefaultTheme)
  1060:         {
  1061:             strcpy_s(path, sizeofArray(path), pSettings->SF_blackboxPath);
  1062:             strcat_s(path, sizeofArray(path), "\\plugins");
  1063:         }
  1064:         else
  1065:         {
  1066:             strcpy_s(path, sizeofArray(path), pSettings->SF_currentThemePath);
  1067:             strcat_s(path, sizeofArray(path), "\\plugins");
  1068:             if (!FolderExists(path))
  1069:             {
  1070:                 strcpy_s(path, sizeofArray(path), pSettings->SF_blackboxPath);
  1071:                 strcat_s(path, sizeofArray(path), "\\plugins");
  1072:             }
  1073:         }
  1074: 
  1075:         // Set the required formats for the file dialog...
  1076:         OPENFILENAME ofn;
  1077:         ZeroMemory(&ofn, sizeof(ofn));
  1078:         ofn.lStructSize = sizeof(ofn);
  1079:         ofn.hwndOwner = NULL;
  1080:         ofn.lpstrFilter = "Plugins (*.dll)\0*.dll", NULL, NULL;
  1081:         ofn.lpstrFile = file;
  1082:         ofn.nMaxFile = MAX_PATH;
  1083:         ofn.lpstrInitialDir = path;
  1084:         ofn.lpstrTitle = "Load plugin...";
  1085:         ofn.lpstrDefExt = NULL;
  1086:         ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;
  1087: 
  1088:         if (GetOpenFileName(&ofn)) LoadPlugin(ofn.lpstrFile);
  1089:     }
  1090: }
  1091: 
  1092: //===========================================================================
  1093: 
  1094: bool PluginManager::IsPluginLoaded(LPSTR pluginPath)
  1095: {
  1096:     bool fileNameOnly = false;
  1097:     if (!strchr(pluginPath, '\\')) fileNameOnly = true;
  1098: 
  1099:     for (int i=0; i < (int)pluginList.size(); i++)
  1100:     {
  1101:         if (fileNameOnly)
  1102:         {
  1103:             if (IsInString(pluginList[i].path, pluginPath)) return true;
  1104:         }
  1105:         else
  1106:         {
  1107:             if (!_stricmp(pluginList[i].path, pluginPath)) return true;
  1108:         }
  1109:     }
  1110: 
  1111:     return false;
  1112: }
  1113: 
  1114: //===========================================================================
  1115: