/*
 ============================================================================
 xoblite -> an alternative shell based on Blackbox for Windows
 Copyright  2002-2005 Karl-Henrik Henriksson [qwilk]
 Copyright  2001-2004 The Blackbox for Windows Development Team
 http://xoblite.net/ - #bb4win on irc.freenode.net
 ============================================================================

  Blackbox for Windows is free software, released under the
  GNU General Public License (GPL version 2 or later), with an extension
  that allows linking of proprietary modules under a controlled interface.
  What this means is that plugins etc. are allowed to be released
  under any license the author wishes. Please note, however, that the
  original Blackbox gradient math code used in Blackbox for Windows
  is available under the BSD license.

  http://www.fsf.org/licenses/gpl.html
  http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
  http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  For additional license information, please read the included license.html

 ============================================================================
*/

#include "BBApi.h" 
#include "PluginManager.h" 
#include "Slit.h" 
#include "Systembar.h" 
#include "Toolbar.h" 
#include "Desk.h" 
#include <windows.h> 
#include <vector> 
#include <string> 
#include <process.h> 

extern PluginManager *pPluginManager;
extern Settings *pSettings;
extern Slit *pSlit;
extern Systembar *pSystembar;
extern Toolbar *pToolbar;
extern Desk *pDesk;

//===========================================================================

PluginManager::PluginManager(HINSTANCE hMainInstance)
{
        char szBuffer[MAX_LINE_LENGTH]="";

        startupLock = true;

        // SAFE MODE: Hold down control to disable loading of plugins at startup
        // Beware! -> Do not load any plugins when running in safe mode or you
        // will overwrite the contents of your plugins.rc!
        if (!(GetAsyncKeyState(VK_CONTROL) & 0x8000))
        {
                FILE *f = FileOpen(plugrcPath());
                while (ReadNextCommand(f, szBuffer, sizeof(szBuffer))) loadPlugin(szBuffer);
                FileClose(f);
        }

        startupLock = false;
        pSettings->checkingForUpdates = false;

        _beginthread(periodicCheckForUpdates, 0, NULL);
}

//====================

PluginManager::~PluginManager()
{
        for (int i=0; i < (int)pluginList.size(); i++)
        {
                endPlugin = (endPluginFunc)GetProcAddress(pluginList[i].hModule, "endPlugin");
                endPlugin(pluginList[i].hModule);
                FreeLibrary(pluginList[i].hModule);
        }

        pluginList.clear();
}

//===========================================================================

void PluginManager::loadPlugin(LPSTR pluginPath)
{
        if (strchr(pluginPath, '\"')) StrRemoveEncap(pluginPath);
        if (strchr(pluginPath, '$')) ReplaceShellFolders(pluginPath);
        if (strchr(pluginPath, '%')) ReplaceEnvVars(pluginPath);
        
        if (IsInString(pluginPath, "BBSystemBar.dll") ||                // Do not load the BBSystemBar plugin (integrated functionality in xoblite)
                IsInString(pluginPath, "BBSystemBarEx.dll") ||          // Do not load the BBSystemBarEx plugin (similar to integrated functionality in xoblite)
                IsInString(pluginPath, "SystemBarEx.dll") ||            // Do not load the SystemBarEx plugin (similar to integrated functionality in xoblite)
                IsInString(pluginPath, "BBSlit.dll") ||                         // Do not load the BBSlit plugin (integrated functionality in xoblite)
                IsInString(pluginPath, "BBDDE.dll"))                            // Do not load the BBDDE plugin (integrated functionality in xoblite)
        {
                char msg[MAX_LINE_LENGTH];
                strcpy(msg, "The following plugins are automatically excluded   \n"
                        "because the functionality they provide (or similar   \n"
                        "functionality) is already part of xoblite itself:   \n\n"
                        "BBSystemBar\nBBSystemBarEx/SystemBarEx\nBBSlit\nBBDDE   \n");
                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
                return;
        }

        //====================

        // Check if this particular plugin has already been loaded...

        char dllName[MAX_LINE_LENGTH], dllTempName[MAX_LINE_LENGTH];
        int nLen;

        strcpy(dllName, pluginPath);
        nLen = strlen(dllName) - 1;
        if (IsInString(dllName, "/")) while (nLen >0 && dllName[nLen] != '/') nLen--;
        else while (nLen >0 && dllName[nLen] != '\\') nLen--;
        strcpy(dllName, &pluginPath[nLen+1]);

        for (int i=0; i < (int)pluginList.size(); i++)
        {
                strcpy(dllTempName, pluginList[i].path);
                nLen = strlen(dllTempName) - 1;
                if (IsInString(dllTempName, "/")) while (nLen >0 && dllTempName[nLen] != '/') nLen--;
                else while (nLen >0 && dllTempName[nLen] != '\\') nLen--;
                strcpy(dllTempName, &pluginList[i].path[nLen+1]);

                if (!stricmp(dllName, dllTempName))
                {
                        char msg[MAX_LINE_LENGTH];
                        strcpy(msg, pluginPath);
                        strcat(msg, "   \n\nThis plugin is already loaded.   ");
                        MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
                        return;
                }
        }

        //====================

        bool EnableDockToSlit, EnableDockToDesktop;

        if (pluginPath[0] == '&') // slit plugin?
        {
                EnableDockToSlit = true;
                EnableDockToDesktop = false;
                hModule = LoadLibrary(&pluginPath[1]);
        }
        else if (pluginPath[0] == '>') // desktop plugin?
        {
                EnableDockToSlit = false;
                EnableDockToDesktop = true;
                hModule = LoadLibrary(&pluginPath[1]);
        }
        else // normal plugin
        {
                EnableDockToSlit = false;
                EnableDockToDesktop = false;
                hModule = LoadLibrary(pluginPath);
        }

        if (hModule)
        {
                if (startPlugin(hModule, pluginPath, EnableDockToSlit, EnableDockToDesktop))
                {
                        pluginItem plg;
                        plg.hModule = hModule;
                        plg.EnableDockToSlit = EnableDockToSlit;
                        plg.EnableDockToDesktop = EnableDockToDesktop;
                        strcpy(plg.path, pluginPath);
                        plg.UpdateAvailable = false;
                        pluginList.push_back(plg);

                        // ...writing to plugins.rc during startup is not a good idea... <g>
                        if (!startupLock)
                        {
                                if (!writePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
                        }
                }
        }
        else
        {
                char msg[MAX_LINE_LENGTH];
                strcpy(msg, pluginPath);
                strcat(msg, " (LoadLibrary failed)");
                Log("Plugin error", msg);
                strcpy(msg, pluginPath);
                strcat(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.   ");
                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
        }
}

//===========================================================================

bool PluginManager::startPlugin(HMODULE hModule, LPSTR pluginPath, bool EnableDockToSlit, bool EnableDockToDesktop)
{
        beginPluginEx = (beginPluginExFunc)GetProcAddress(hModule, "beginPluginEx");
        beginPlugin = (beginPluginFunc)GetProcAddress(hModule, "beginPlugin");
        beginSlitPlugin = (beginSlitPluginFunc)GetProcAddress(hModule, "beginSlitPlugin");
        endPlugin = (endPluginFunc)GetProcAddress(hModule, "endPlugin");

        if (!beginPluginEx && !beginPlugin && !beginSlitPlugin)
        {
                char msg[MAX_LINE_LENGTH];
                strcpy(msg, pluginPath);
                strcat(msg, " (entry point missing)");
                Log("Plugin failed", msg);
                strcpy(msg, pluginPath);
                strcat(msg, "   \n\nThe plugin you are trying to load is missing   \nthe necessary entry point.   \n\nPlease contact the plugin author.   ");
                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
                FreeLibrary(hModule);
                return false;
        }

        if (!endPlugin)
        {
                char msg[MAX_LINE_LENGTH];
                strcpy(msg, pluginPath);
                strcat(msg, " (exit point missing)");
                Log("Plugin failed", msg);
                strcpy(msg, pluginPath);
                strcat(msg, "   \n\nThe plugin you are trying to load is missing   \nthe necessary exit point.   \n\nPlease contact the plugin author.   ");
                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
                FreeLibrary(hModule);
                return false;
        }

        // Show plugins if they are hidden and we're not starting up
        // (needed for visibility consistency when loading a new plugin)
        if (pSettings->pluginsHidden && !startupLock) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);

        //====================

        try
        {
                if (beginPluginEx)
                {
                        if (EnableDockToDesktop) beginPluginEx(hModule, pDesk->hDesktopWnd);
                        else beginPluginEx(hModule, pSlit->hSlitWnd);
                }
                else if (beginSlitPlugin && EnableDockToSlit) beginSlitPlugin(hModule, pSlit->hSlitWnd);
                else if (beginPlugin) beginPlugin(hModule);
                else if (beginSlitPlugin) beginSlitPlugin(hModule, pSlit->hSlitWnd);
        }
        catch (...)
        {
                FreeLibrary(hModule);
                char msg[MAX_LINE_LENGTH];
                strcpy(msg, pluginPath);
                strcat(msg, " (try/catch exception)");
                Log("Plugin failed", msg);
                strcpy(msg, pluginPath);
                strcat(msg, "   \n\nThe plugin you are trying to load has failed.   \n\nPlease contact the plugin author.   ");
                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
                return false;
        }

        //====================

        return true;
}

//===========================================================================

void PluginManager::unloadPlugin(LPSTR pluginPath, int pluginNumber)
{
        if (pluginNumber != -1)
        {
                endPlugin = (endPluginFunc)GetProcAddress(pluginList[pluginNumber].hModule, "endPlugin");
                endPlugin(pluginList[pluginNumber].hModule);
                FreeLibrary(pluginList[pluginNumber].hModule);

                pluginList.erase(pluginList.begin() + pluginNumber);

                if (!writePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);

                return;
        }

        //====================

        if (strchr(pluginPath, '\"')) StrRemoveEncap(pluginPath);
        if (strchr(pluginPath, '$')) ReplaceShellFolders(pluginPath);
        if (strchr(pluginPath, '%')) ReplaceEnvVars(pluginPath);

        char dllName[MAX_LINE_LENGTH], dllTempName[MAX_LINE_LENGTH];
        int nLen;

        // Extract the name of the .dll to unload (disregard the full path)...
        strcpy(dllName, pluginPath);
        nLen = strlen(dllName) - 1;
        if (strchr(dllName, '/')) while (nLen >0 && dllName[nLen] != '/') nLen--;
        else while (nLen >0 && dllName[nLen] != '\\') nLen--;
        strcpy(dllName, &pluginPath[nLen+1]);

        //====================

        for (int i=0; i < (int)pluginList.size(); i++)
        {
                // Extract the name of the current .dll (disregard the full path)...
                strcpy(dllTempName, pluginList[i].path);
                nLen = strlen(dllTempName) - 1;
                if (strchr(dllName, '/')) while (nLen >0 && dllTempName[nLen] != '/') nLen--;
                else while (nLen >0 && dllTempName[nLen] != '\\') nLen--;
                strcpy(dllTempName, &pluginList[i].path[nLen+1]);

                // Is the current plugin the plugin we wish to unload?
                if (!stricmp(dllName, dllTempName))
                {
                        endPlugin = (endPluginFunc)GetProcAddress(pluginList[i].hModule, "endPlugin");
                        endPlugin(pluginList[i].hModule);
                        FreeLibrary(pluginList[i].hModule);

                        pluginList.erase(pluginList.begin() + i);

                        if (!writePluginsRC()) MessageBox(GetBBWnd(), "Could not write to plugins.rc!", "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);

                        return;
                }
        }
/*
        char msg[MAX_LINE_LENGTH];
        strcpy(msg, pluginPath);
        strcat(msg, "   \n\nThe plugin you were trying to unload was not found running.   ");
        MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
*/
}

//===========================================================================

void PluginManager::updatePlugin(LPSTR pluginPath, int pluginNumber, bool forceUpdate)
{
        if (pSettings->checkingForUpdates) return;

        char msg[MAX_LINE_LENGTH], temp[MAX_LINE_LENGTH], path[MAX_LINE_LENGTH];

        // Make sure the updates metafile exists...
        GetBlackboxPath(path, sizeof(path));
        strcat(path, "update.log");
        if (!FileExists(path) || forceUpdate)
        {
                bool silent = true;
                _beginthread(checkForUpdates, 0, &silent);
        }

        //====================

        // Display "<->" icon in toolbar.windowLabel -> update in progress!
        pSettings->checkingForUpdates = true;
        if (pSettings->taskbarOnToolbar && pSystembar) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
        else if (pToolbar) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);

        //====================

        if (pluginNumber != -1)
        {
                pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[pluginNumber].hModule, "pluginInfo");
                if (pluginInfo)
                {
                        if (FileExists(path))
                        {
                                char plugin[MAX_LINE_LENGTH], parameter[MAX_LINE_LENGTH];
                                char onlineVersion[MAX_LINE_LENGTH], currentVersion[MAX_LINE_LENGTH];
                                char onlineDate[MAX_LINE_LENGTH], currentDate[MAX_LINE_LENGTH];

                                strcpy(plugin, pluginInfo(PLUGIN_NAME));
                                strcpy(currentVersion, pluginInfo(PLUGIN_VERSION));
                                strcpy(currentDate, pluginInfo(PLUGIN_RELEASEDATE));

                                sprintf(parameter, "%s.version:", plugin);
                                strcpy(onlineVersion, ReadString(path, parameter, ""));
                                if (!strlen(onlineVersion))
                                {
                                        pluginList[pluginNumber].UpdateAvailable = false;
                                        strcpy(temp, pluginInfo(PLUGIN_UPDATE_URL));
                                        if (!IsInString(temp, "http://")) strcpy(temp, pSettings->pluginsRepository);
                                        sprintf(msg, "%s   \n\n%s is not supported by the specified repository!   ", temp, plugin);
                                        MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONERROR);
                                        return;
                                }
                                sprintf(parameter, "%s.date:", plugin);
                                strcpy(onlineDate, ReadString(path, parameter, "xxxx-xx-xx"));

                                //====================

                                // Check if online version/date differs from the current version/date...
                                if (!stricmp(currentDate, onlineDate))
                                {
                                        if (!stricmp(currentVersion, onlineVersion))
                                        {
                                                pluginList[pluginNumber].UpdateAvailable = false;
                                                sprintf(msg, "You are already using the latest version of %s.   ", plugin);
                                                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
                                                return;
                                        }
                                }

                                //====================

                                sprintf(msg, "%s update found!\n\nCurrent version:\t %s \t\t%s   \nOnline version:\t %s \t\t%s   ", plugin, currentVersion, currentDate, onlineVersion, onlineDate);

                                sprintf(parameter, "%s.changes:", plugin);
                                strcpy(temp, ReadString(path, parameter, ""));
                                if (strlen(temp))
                                {
                                        strcat(msg, "\n\nUpdate information:\n");
                                        strcat(msg, temp);
                                }

                                strcat(msg, "\n\nDo you want to download this update?   ");

                                //====================

                                if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
                                {
                                        int nLen;
                                        char token[MAX_LINE_LENGTH], extra[MAX_LINE_LENGTH], filename[MAX_LINE_LENGTH], pluginPath[MAX_LINE_LENGTH];
                                        char savedfiles[MAX_LINE_LENGTH] = "";
                                        LPSTR tokens[1];
                                        tokens[0] = token;

                                        // Copy the path to the plugin + the URL...
                                        bool dllFound = false, zipFound = false;

                                        if (!strstr(pluginList[pluginNumber].path, ":\\"))
                                        {
                                                // Make sure we use the full path of the plugin and
                                                // not just a path relative to the Blackbox folder...
                                                GetBlackboxPath(filename, MAX_LINE_LENGTH);
                                                strcat(filename, pluginList[pluginNumber].path);
                                        }
                                        else strcpy(filename, pluginList[pluginNumber].path);

                                        sprintf(parameter, "%s.files:", plugin);
                                        strcpy(temp, ReadString(path, parameter, ""));
                                        if (!strlen(temp))
                                        {
                                                // Download the plugin .zip archive if no
                                                // list of updated files was specified...
                                                sprintf(parameter, "%s.archive:", plugin);
                                                strcpy(temp, ReadString(path, parameter, ""));
                                        }
                                        if (IsInString(temp, ".dll")) dllFound = true;
                                        else if (IsInString(temp, ".zip")) zipFound = true;

                                        if (dllFound)
                                        {
                                                // Unload the plugin...
                                                strcpy(pluginPath, pluginList[pluginNumber].path);
                                                endPlugin = (endPluginFunc)GetProcAddress(pluginList[pluginNumber].hModule, "endPlugin");
                                                endPlugin(pluginList[pluginNumber].hModule);
                                                FreeLibrary(pluginList[pluginNumber].hModule);
                                                pluginList.erase(pluginList.begin() + pluginNumber);
                                        }

                                        // Download the updated files...
                                        while(strlen(temp))
                                        {
                                                token[0] = extra[0] = '\0';
                                                BBTokenize (temp, tokens, 1, extra);
                                                strcpy(temp, extra);

                                                // Extract the plugin folder from the plugin path...
                                                nLen = strlen(filename) - 1;
                                                if (IsInString(filename, "/")) while (nLen >0 && filename[nLen] != '/') nLen--;
                                                else while (nLen >0 && filename[nLen] != '\\') nLen--;
                                                filename[nLen + 1] = 0;

                                                // Extract the name of the file from the URL...
                                                nLen = strlen(token) - 1;
                                                while (nLen >0 && token[nLen] != '/') nLen--;
                                                strcat(filename, &token[nLen+1]);

                                                // Download the file and save it in the plugin folder...
                                                DownloadFile(token, filename);

                                                // Copy the path of the saved file to the list of updated files...
                                                strcat(savedfiles, "\n");
                                                strcat(savedfiles, filename);
                                                strcat(savedfiles, "   ");
                                        }

                                        if (dllFound)
                                        {
                                                // Reload the plugin...
                                                bool EnableDockToSlit, EnableDockToDesktop;

                                                if (pluginPath[0] == '&') // slit plugin?
                                                {
                                                        EnableDockToSlit = true;
                                                        EnableDockToDesktop = false;
                                                        hModule = LoadLibrary(&pluginPath[1]);
                                                }
                                                else if (pluginPath[0] == '>') // desktop plugin?
                                                {
                                                        EnableDockToSlit = false;
                                                        EnableDockToDesktop = true;
                                                        hModule = LoadLibrary(&pluginPath[1]);
                                                }
                                                else // normal plugin
                                                {
                                                        EnableDockToSlit = false;
                                                        EnableDockToDesktop = false;
                                                        hModule = LoadLibrary(pluginPath);
                                                }

                                                if (hModule)
                                                {
                                                        if (startPlugin(hModule, pluginPath, EnableDockToSlit, EnableDockToDesktop))
                                                        {
                                                                pluginItem plg;
                                                                plg.hModule = hModule;
                                                                plg.EnableDockToSlit = EnableDockToSlit;
                                                                plg.EnableDockToDesktop = EnableDockToDesktop;
                                                                strcpy(plg.path, pluginPath);
                                                                plg.UpdateAvailable = false;
                                                                pluginList.insert(pluginList.begin() + pluginNumber, plg);
                                                        }
                                                }
                                                else return;

                                                sprintf(msg, "%s update downloaded and installed.   \n%s", plugin, savedfiles);
                                                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
                                        }
                                        else if (zipFound)
                                        {
                                                sprintf(msg, "%s update in .zip format downloaded,   \nsee archive for further instructions.   \n%s", plugin, savedfiles);
                                                if (MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION))
                                                {
                                                        char arg[MAX_LINE_LENGTH];
                                                        strcpy(arg, "/e,/select,");
                                                        strcat(arg, savedfiles);
                                                        BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, NULL, SW_SHOWNORMAL, false);
                                                        return;
                                                }
                                        }
                                        else
                                        {
                                                sprintf(msg, "%s update downloaded.   \n%s", plugin, savedfiles);
                                                MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
                                        }
                                }

                                // Turn off the "update available" indicator...
                                pluginList[pluginNumber].UpdateAvailable = false;
                        }
                        else MessageBox(GetBBWnd(), "Could not find the updates metafile! [update.log]   ", "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONERROR);
                }
                else MessageBox(GetBBWnd(), "This plugin does not support automatic updating.   ", "xoblite", MB_OK | MB_SETFOREGROUND | MB_ICONERROR);
        }

        // Remove "<->" icon from toolbar.windowLabel -> update no longer in progress
        pSettings->checkingForUpdates = false;
        if (pSettings->taskbarOnToolbar && pSystembar) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
        else if (pToolbar) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);
}

//===========================================================================

void checkForUpdates(void *param)
{
        if (pSettings->checkingForUpdates) _endthread();
        else
        {
                // Display "<->" icon in toolbar.windowLabel -> update in progress!
                pSettings->checkingForUpdates = true;
                if (pSettings->taskbarOnToolbar && pSystembar) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                else if (pToolbar) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);
        }

        bool silent = *((bool*)param);
        int updates = 0;
        char listOfURLs[MAX_LINE_LENGTH] = "", path[MAX_LINE_LENGTH], msg[MAX_LINE_LENGTH], listOfUpdates[MAX_LINE_LENGTH] = "";

        //====================
/*
        // Extract distributed repository URLs from all
        // plugins that support automatic updating...
        for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
        {
                pPluginManager->pluginInfo = (pluginInfoFunc)GetProcAddress(pPluginManager->pluginList[i].hModule, "pluginInfo");
                if (pPluginManager->pluginInfo)
                {
                        strcpy(msg, pPluginManager->pluginInfo(PLUGIN_UPDATE_URL));

                        // Make sure the string supplied by the plugin is a valid URL
                        // and that it isn't the same URL as for the central repository...
                        if (IsInString(msg, "http://") && !IsInString(msg, pSettings->pluginsRepository))
                        {
                                if (!IsInString(listOfURLs, msg))
                                {
                                        strcat(listOfURLs, msg);
                                        strcat(listOfURLs, " ");
                                }
                        }
                }
        }
*/
        // Finally, we add the central repository URL at the end of the list
        // (i.e. if a plugin does not return a URL for automatic updating we fall
        // back to the central plugin repository defined in extensions.rc, which means
        // that the information provided by the distributed repositories
        // will have precedence in case of duplicate plugin update information! :D )
        strcat(listOfURLs, pSettings->pluginsRepository);

        //====================

        if (IsInString(listOfURLs, "http://"))
        {
                GetBlackboxPath(path, sizeof(path));
                strcat(path, "update.log");

                if (FileExists(path)) remove(path); // Delete existing update.log file...

                //====================

                if (DownloadFile(listOfURLs, path))
                {
                        for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
                        {
                                pPluginManager->pluginInfo = (pluginInfoFunc)GetProcAddress(pPluginManager->pluginList[i].hModule, "pluginInfo");
                                if (pPluginManager->pluginInfo)
                                {
                                        char plugin[MAX_LINE_LENGTH], parameter[MAX_LINE_LENGTH];
                                        char onlineVersion[MAX_LINE_LENGTH], currentVersion[MAX_LINE_LENGTH];
                                        char onlineDate[MAX_LINE_LENGTH], currentDate[MAX_LINE_LENGTH];
                                        char onlineAuthor[MAX_LINE_LENGTH];

                                        strcpy(plugin, pPluginManager->pluginInfo(PLUGIN_NAME));
                                        strcpy(currentVersion, pPluginManager->pluginInfo(PLUGIN_VERSION));
                                        strcpy(currentDate, pPluginManager->pluginInfo(PLUGIN_RELEASEDATE));

                                        sprintf(parameter, "%s.version:", plugin);
                                        strcpy(onlineVersion, ReadString(path, parameter, ""));
                                        if (!strlen(onlineVersion))
                                        {
                                                // The plugin was not not supported by the specified repository (or invalid syntax)...
                                                pPluginManager->pluginList[i].UpdateAvailable = false;
                                                continue;
                                        }
                                        sprintf(parameter, "%s.date:", plugin);
                                        strcpy(onlineDate, ReadString(path, parameter, "xxxx-xx-xx"));

                                        //====================

                                        // Check if online version/date differs from the current version/date...
                                        if (!stricmp(currentDate, onlineDate))
                                        {
                                                if (!stricmp(currentVersion, onlineVersion))
                                                {
                                                        // If not, continue looking...
                                                        pPluginManager->pluginList[i].UpdateAvailable = false;
                                                        continue;
                                                }
                                        }

                                        // ...but if it differs we have found an updated plugin!
                                        pPluginManager->pluginList[i].UpdateAvailable = true;

                                        sprintf(parameter, "%s.author:", plugin);
                                        strcpy(onlineAuthor, ReadString(path, parameter, (LPSTR)pPluginManager->pluginInfo(PLUGIN_AUTHOR)));

                                        sprintf(msg, "\n%s %s by %s (%s)   ", plugin, onlineVersion, onlineAuthor, onlineDate);
                                        strcat(listOfUpdates, msg);
                                        updates++;
                                }
                                else pPluginManager->pluginList[i].UpdateAvailable = false;
                        }
                }
                else
                {
                        strcpy(msg, "Note: Some plugin repositories were not currently available,   \nwhich may affect the result of this check for updates.   ");
                        MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST);
                }
        }

        //====================

        if (!silent)
        {
                char buffer[MAX_LINE_LENGTH];
                ULONG *size = 0;
                void *xobVersion;
                UINT bytes;

                // Extract currently running xoblite version...
                // (make sure the correct version encoding is used, currently "000004b0" = language neutral)
                DWORD number = GetFileVersionInfoSize("Blackbox.exe", size);
                GetFileVersionInfo("Blackbox.exe", NULL, number, (LPVOID)buffer);
                VerQueryValue(buffer, TEXT("\\StringFileInfo\\000004b0\\FileVersion"), &xobVersion, &bytes);

                // ...and compare that with the xoblite version available online...
                char xobOnlineVersion[MAX_LINE_LENGTH];
                strcpy(xobOnlineVersion, ReadString(path, "xoblite.version:", ""));
        
                if (strlen(xobOnlineVersion) && stricmp((LPCSTR)xobVersion, xobOnlineVersion))
                {
                        strcpy(msg, "xoblite -> Found xoblite update!");
                        SendMessage(GetBBWnd(), BB_SETTOOLBARLABEL, 0, (LPARAM)msg);

                        sprintf(msg, "Shell update found:   \n\nxoblite %s   \n\nDo you want to download this update?   \n(clicking \"Yes\" will take you to the xoblite website)   ", xobOnlineVersion);
                        if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
                        {
                                // Choosing to download an update will (for now) take the user to the xoblite
                                // website to manually download the latest build (mostly to reduce maintenance
                                // overhead, however this could possibly change in a future release...)
                                BBExecute(GetDesktopWindow(), NULL, "http://xoblite.net/", NULL, NULL, SW_SHOWNORMAL, true);
                        }
                }
        }

        //====================

        sprintf(msg, "xoblite -> Found %d updated plugins!", updates);
        SendMessage(GetBBWnd(), BB_SETTOOLBARLABEL, 0, (LPARAM)msg);

        if (!silent)
        {
                if (updates > 0)
                {
                        sprintf(msg, "Found %d updated plugins:   \n", updates);
                        strcat(msg, listOfUpdates);
                        if (updates == 1) strcat(msg, "\n\nDo you want to download this update?   ");
                        else strcat(msg, "\n\nDo you want to download any of these updates?   ");

                        if (MessageBox(GetBBWnd(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
                        {
                                for (int i=0; i < (int)pPluginManager->pluginList.size(); i++)
                                {
                                        if (pPluginManager->pluginList[i].UpdateAvailable)
                                        {
                                                pSettings->checkingForUpdates = false; // Disable update lock to access updatePlugin() ...
                                                pPluginManager->updatePlugin(NULL, i, false);
                                                pSettings->checkingForUpdates = true; // Re-enable update lock...
                                        }
                                }
                        }
                }
                else
                {
                        strcpy(msg, "No updated plugins were found.   ");
                        MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST);
                }
        }

        //====================

        // Finally, we update xoblite.checked.for.updates in extensions.rc... (temporarily disable write protection if necessary!)
        time_t systemTime;
        struct tm *localTime;
        time(&systemTime);
        localTime = localtime(&systemTime);
        strftime(pSettings->checkedForUpdates, 20, "#%j / %Y-%m-%d", localTime);

        bool tempBool = pSettings->writeProtection;
        pSettings->writeProtection = false;
        WriteString(pSettings->extrcDefaultFile, "xoblite.checked.for.updates:", pSettings->checkedForUpdates);
        pSettings->writeProtection = tempBool;

        //====================

        // Remove "<->" icon from toolbar.windowLabel -> update no longer in progress
        pSettings->checkingForUpdates = false;
        if (pSettings->taskbarOnToolbar && pSystembar) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
        else if (pToolbar) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);

        _endthread();
}

//===========================================================================

void periodicCheckForUpdates(void *)
{
        if (pSettings->checkingForUpdates) _endthread();

        char temp[MAX_LINE_LENGTH], windowLabel[25];

        // Get current day of the year...
        time_t systemTime;
        struct tm *localTime;
        time(&systemTime);
        localTime = localtime(&systemTime);
        int currentDay = localTime->tm_yday + 1;

        // Extract day of the year for the last check...
        strncpy(temp, &pSettings->checkedForUpdates[1], 3);
        temp[3] = 0;
        int checkedDay = atoi(temp);

        // Calculate the number of days since the last check for updates...
        int interval;
        if (currentDay >= checkedDay) interval = currentDay - checkedDay;
        else interval = 366 - checkedDay + currentDay;

        //====================

        // Is the last checked date in extensions.rc == the default?
        // If so, xoblite is running for the first time -> display a nice welcome screen! =]
        if (!stricmp(pSettings->checkedForUpdates, "#001 / 2005-01-01"))
        {
                interval = 365; // ...just to trigger the update messagebox below! <g>
                strcpy(windowLabel, "Welcome to xoblite!");
                strcpy(temp,
                                "You seem to be running xoblite bb3 for the first time.   \n\n"
                                "xoblite features a mechanism to notify you of available updates to the core (xoblite.exe) and your loaded plugins.   \n\n"
                                "A check for updates can be triggered using:   \n"
                                "- The \"Check for updates...\" item in the Configuration/Plugins menu   \n"
                                "- The core broadcast message: @xoblite CheckForUpdates   \n"
                                "- Monthly periodic checking (user is notified if/when more than 30 days have passed since the last check)   \n\n"
                                "Do you want to check for updates now?   ");
        }
        else
        {
                strcpy(windowLabel, "xoblite");
                sprintf(temp, "%d days have passed since you last checked for updates.   \n\nDo you want to check for updates now?   ", interval);
        }

        //====================

        if (interval > 30) // Has more than 30 days passed since the last check for updates?
        {
                // Ask the user if he/she wants to check for updates...
                if (MessageBox(NULL, temp, windowLabel, MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
                {
                        bool silent = false;
                        checkForUpdates(&silent); // We're already running in a separate thread! (NOTE: checkForUpdates will terminate the thread)
                }
                else
                {
                        // Update xoblite.checked.for.updates in extensions.rc... (temporarily disable write protection if necessary!)
                        strftime(pSettings->checkedForUpdates, 20, "#%j / %Y-%m-%d", localTime);

                        bool tempBool = pSettings->writeProtection;
                        pSettings->writeProtection = false;
                        WriteString(pSettings->extrcDefaultFile, "xoblite.checked.for.updates:", pSettings->checkedForUpdates);
                        pSettings->writeProtection = tempBool;
                }
        }

        //====================

        _endthread();
}

//===========================================================================

void PluginManager::aboutPlugins()
{
        char msg[MAX_LINE_LENGTH] = "", temp[MAX_LINE_LENGTH];

        for (int i=0; i < (int)pluginList.size(); i++)
        {
                pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[i].hModule, "pluginInfo");
                if (pluginInfo)
                {
                        sprintf(temp, "%s %s by %s (%s)", pluginInfo(PLUGIN_NAME), pluginInfo(PLUGIN_VERSION), pluginInfo(PLUGIN_AUTHOR), pluginInfo(PLUGIN_RELEASEDATE));
                        strcat(msg, temp);
                        if (pluginList[i].path[0] == '&') strcat(msg, " *   \n"); // &-loaded plugin (beginSlitPlugin)
                        else strcat(msg, "   \n"); // normal plugin (beginPlugin, beginPluginEx)
                }
                else
                {
                        strcat(msg, pluginList[i].path);
                        if (pluginList[i].path[0] == '&') strcat(msg, " *   \n"); // &-loaded plugin (beginSlitPlugin)
                        else strcat(msg, "   \n"); // normal plugin (beginPlugin, beginPluginEx)
                }
        }

        if (strlen(msg)) MessageBox(GetBBWnd(), msg, "xoblite: About loaded plugins", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
        else MessageBox(GetBBWnd(), "--- No plugins loaded ---   ", "xoblite: About loaded plugins", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
}

//===========================================================================

LPCSTR PluginManager::getPluginInfo(int pluginNumber, int infoParam)
{
        pluginInfo = (pluginInfoFunc)GetProcAddress(pluginList[pluginNumber].hModule, "pluginInfo");
        if (pluginInfo) return pluginInfo(infoParam); // e.g. PLUGIN_NAME
        else
        {
                // Extract the name of the plugin...
                strcpy(pluginName, pluginList[pluginNumber].path);
                int nLen = strlen(pluginName) - 1;
                if (IsInString(pluginName, "/")) while (nLen >0 && pluginName[nLen] != '/') nLen--;
                else while (nLen >0 && pluginName[nLen] != '\\') nLen--;
                strcpy(pluginName, &pluginList[pluginNumber].path[nLen+1]);
                pluginName[strlen(pluginName)-4] = 0; // Remove .dll at the end...
                return pluginName;
        }
}

//===========================================================================

bool PluginManager::writePluginsRC()
{
        if (pSettings->writeProtection) return true;
        
        HANDLE f = CreateFile(plugrcPath(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (f)
        {
                char szTemp[MAX_LINE_LENGTH];
                DWORD retLength = 0;

                for (int i=0; i < (int)pluginList.size(); i++)
                {
                        // If the full path to the plugin is specified (e.g. from drag'n'drop) and it is in
                        // a subfolder to the Blackbox folder, extract the relative path to the plugin
                        // -> full path minus Blackbox folder path.
                        // Example: "C:\Blackbox\plugins\BBbin\BBbin.dll" becomes "plugins\BBbin\BBbin.dll"
                        // This way the Blackbox folder can be moved, and the plugin path will still work! :D

                        if (!pSettings->usingDefaultTheme && IsInString(pluginList[i].path, pSettings->SF_currentThemePath))
                        {
                                if (pluginList[i].path[0] == '>') // desktop plugin?
                                {
                                        strcpy(szTemp, ">$CurrentTheme$");
                                        strcat(szTemp, &pluginList[i].path[(strlen(pSettings->SF_currentThemePath)+1)]);
                                }
                                else // normal or slit plugin
                                {
                                        strcpy(szTemp, "$CurrentTheme$");
                                        strcat(szTemp, &pluginList[i].path[(strlen(pSettings->SF_currentThemePath))]);
                                }
                        }
                        else if (IsInString(pluginList[i].path, pSettings->SF_blackboxPath))
                        {
                                if (pluginList[i].path[0] == '>') // desktop plugin?
                                {
                                        strcpy(szTemp, ">");
                                        strcat(szTemp, &pluginList[i].path[(strlen(pSettings->SF_blackboxPath)+2)]);
                                }
                                else strcpy(szTemp, &pluginList[i].path[(strlen(pSettings->SF_blackboxPath)+1)]);
                        }
                        else strcpy(szTemp, pluginList[i].path);

                        strcat(szTemp, "\r\n");
                        WriteFile(f, szTemp, strlen(szTemp), &retLength, NULL);
                }

                CloseHandle(f);
                return true;
        }
        else
        {
                CloseHandle(f);
                return false;
        }
}

//===========================================================================

void PluginManager::loadPluginDialog()
{
        char file[MAX_PATH] = "", path[MAX_PATH];

        if (pSettings->usingDefaultTheme)
        {
                strcpy(path, pSettings->SF_blackboxPath);
                strcat(path, "\\plugins");
        }
        else
        {
                strcpy(path, pSettings->SF_currentThemePath);
                strcat(path, "\\plugins");
                if (!FolderExists(path))
                {
                        strcpy(path, pSettings->SF_blackboxPath);
                        strcat(path, "\\plugins");
                }
        }

        // Set the required formats for the file dialog...
        OPENFILENAME ofn;
        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = NULL;
        ofn.lpstrFilter = "Plugins (*.dll)\0*.dll", NULL, NULL;
        ofn.lpstrFile = file;
        ofn.nMaxFile = MAX_PATH;
        ofn.lpstrInitialDir = path;
        ofn.lpstrTitle = "Load plugin...";
        ofn.lpstrDefExt = NULL;
        ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;

        if (GetOpenFileName(&ofn)) loadPlugin(ofn.lpstrFile);
}

//===========================================================================





syntax highlighting by

w e b c p p
web c plus plus