//This file is part of HTTPServer a Miranda IM plugin //Copyright (C)2002 Kennet Nielsen // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either //version 2 of the License, or (at your option) any later version. // //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. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "Glob.h" #define szConfigFile "HTTPServer.xml" const char szXmlHeader[] = "\r\n" "\r\n" "\r\n"; const char szXmlData[] = "\t\r\n" "\t\t%s\r\n" "\t\t%s\r\n" "\t\t%d\r\n" "\t\t%d.%d.%d.%d\r\n" "\t\t%d.%d.%d.%d\r\n" "\t\r\n"; const char szXmlTail[] = ""; const char* pszDefaultShares[] = { "htdocs\\@settings\\favicon.ico", "/favicon.ico", "htdocs\\@settings\\index.xsl", "/index.xsl", "htdocs\\@settings\\theme\\", "/theme/", "htdocs\\", "/", nullptr, nullptr }; static IconItem iconList[] = { { "Disable server", "disable", IDI_DISABLE_SERVER }, { "Enable server", "enable", IDI_SHARE_NEW_FILE }, }; int OptionsInitialize(WPARAM, LPARAM); int PreShutdown(WPARAM, LPARAM); HNETLIBUSER hNetlibUser; HANDLE hDirectBoundPort; string sLogFilePath; // static so they can not be used from other modules ( sourcefiles ) static HANDLE hEventSystemInit = nullptr; static HANDLE hPreShutdown = nullptr; static HANDLE hHttpAcceptConnectionsService = nullptr; static HANDLE hHttpAddChangeRemoveService = nullptr; static HANDLE hHttpGetShareService = nullptr; static HANDLE hHttpGetAllShares = nullptr; static HGENMENU hAcceptConnectionsMenuItem = nullptr; char szPluginPath[MAX_PATH] = { 0 }; int nPluginPathLen = 0; uint32_t dwLocalIpAddress = 0; uint32_t dwLocalPortUsed = 0; uint32_t dwExternalIpAddress = 0; int nMaxUploadSpeed = -1; int nMaxConnectionsTotal = -1; int nMaxConnectionsPerUser = -1; int nDefaultDownloadLimit = -1; bool bIsOnline = true; bool bLimitOnlyWhenOnline = true; bool bShutdownInProgress = false; CMPlugin g_plugin; extern HWND hwndStatsticView; ///////////////////////////////////////////////////////////////////// PLUGININFOEX pluginInfoEx = { sizeof(PLUGININFOEX), __PLUGIN_NAME, PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), __DESCRIPTION, __AUTHOR, __COPYRIGHT, __AUTHORWEB, UNICODE_AWARE, // {67848B07-83D2-49E9-8844-7E3DE268E304} {0x67848b07, 0x83d2, 0x49e9, {0x88, 0x44, 0x7e, 0x3d, 0xe2, 0x68, 0xe3, 0x4}} }; CMPlugin::CMPlugin() : PLUGIN(MODULENAME, pluginInfoEx) {} ///////////////////////////////////////////////////////////////////// // Member Function : bOpenLogFile // Type : Global // Parameters : None // Returns : Returns alway true // Description : Open the log file in default program assoaitated // with .log files // References : - // Remarks : - // Created : 031020, 20 oktober 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bOpenLogFile() { SHELLEXECUTEINFO st = { 0 }; st.cbSize = sizeof(st); st.fMask = SEE_MASK_INVOKEIDLIST; st.hwnd = nullptr; st.lpFile = sLogFilePath.c_str(); st.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&st); return true; } bool bWriteToFile(HANDLE hFile, const char * pszSrc, int nLen = -1) { if (nLen < 0) nLen = (int)mir_strlen(pszSrc); DWORD dwBytesWritten; return WriteFile(hFile, pszSrc, nLen, &dwBytesWritten, nullptr) && (dwBytesWritten == (uint32_t)nLen); } ///////////////////////////////////////////////////////////////////// // Member Function : LogEvent // Type : Global // Parameters : title - ? // logme - ? // Returns : void // Description : // // References : - // Remarks : - // Created : 030928, 28 september 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// void LogEvent(const char * pszTitle, const char * pszLog) { HANDLE hFile = CreateFile(sLogFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) return; if (SetFilePointer(hFile, 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER) { CloseHandle(hFile); return; } char szTmp[128]; time_t now; time(&now); int nLen = (int)strftime(szTmp, sizeof(szTmp), "%d-%m-%Y %H:%M:%S -- ", localtime(&now)); int nLogLen = (int)mir_strlen(pszLog); while (nLogLen > 0 && (pszLog[nLogLen - 1] == '\r' || pszLog[nLogLen - 1] == '\n')) nLogLen--; bWriteToFile(hFile, szTmp, nLen); bWriteToFile(hFile, pszTitle); bWriteToFile(hFile, " : "); bWriteToFile(hFile, pszLog, nLogLen); bWriteToFile(hFile, "\r\n"); CloseHandle(hFile); } ///////////////////////////////////////////////////////////////////// // Member Function : dwReadIPAddress // Type : Global // Parameters : pszStr - ? // Returns : uint32_t // Description : // // References : - // Remarks : - // Created : 030826, 26 august 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// uint32_t dwReadIPAddress(char * pszStr, bool &bError) { uint32_t ip = 0; char *pszEnd; for (int n = 0; n < 4; n++) { int nVal = strtol(pszStr, &pszEnd, 10); if (pszEnd <= pszStr || (n != 3 && pszEnd[0] != '.') || (nVal < 0 || nVal > 255)) { bError = true; return 0; } pszStr = pszEnd + 1; ip |= nVal << ((3 - n) * 8); } return ip; } ///////////////////////////////////////////////////////////////////// // Member Function : bReadConfigurationFile // Type : Global // Parameters : None // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 030823, 23 august 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bReadConfigurationFile() { mir_cslock lck(csFileShareListAccess); CLFileShareNode * pclLastNode = nullptr; char szBuf[1000]; mir_strcpy(szBuf, szPluginPath); mir_strcat(szBuf, szConfigFile); HANDLE hFile = CreateFile(szBuf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) return false; char *pszCurPos = szBuf; bool bEof = false; while (!bEof) { uint32_t dwBytesInBuffer = 0; // move rest of buffer to front if (pszCurPos && pszCurPos != szBuf) { dwBytesInBuffer = uint32_t(sizeof(szBuf) - (pszCurPos - szBuf)); memmove(szBuf, pszCurPos, dwBytesInBuffer * sizeof(wchar_t)); } // append data to buffer DWORD dwBytesRead = 0; bEof = !ReadFile(hFile, &szBuf[dwBytesInBuffer], sizeof(szBuf) - dwBytesInBuffer, &dwBytesRead, nullptr) || dwBytesRead <= 0; pszCurPos = szBuf; if (pszCurPos) { while (pszCurPos && (pszCurPos = strstr(pszCurPos, "")) != nullptr) { pszCurPos += 7; char * pszColData[5] = {}; for (int n = 0; n < 5; n++) { pszColData[n] = strstr(pszCurPos, ">"); if (!pszColData[n]) break; pszColData[n] += 1; pszCurPos = strstr(pszColData[n], ""); if (!pszCurPos) break; pszCurPos += 1; } if (!pszColData[4]) continue; CLFileShareNode * pcl = new CLFileShareNode(pszColData[0], pszColData[1]); bool bError = false; char * pszEnd; pcl->st.nMaxDownloads = strtol(pszColData[2], &pszEnd, NULL); if (!pszEnd || *pszEnd != NULL) bError = true; pcl->st.dwAllowedIP = dwReadIPAddress(pszColData[3], bError); pcl->st.dwAllowedMask = dwReadIPAddress(pszColData[4], bError); if (bError) { delete pcl; continue; } if (!pclLastNode) { pclLastNode = pclFirstNode = pcl; } else { pclLastNode->pclNext = pcl; pclLastNode = pcl; } // refill buffer if (!bEof && pszCurPos - szBuf > sizeof(szBuf) / 2) break; } } } CloseHandle(hFile); return true; } ///////////////////////////////////////////////////////////////////// // Member Function : bWriteConfigurationFile // Type : Global // Parameters : None // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 030826, 26 august 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bWriteConfigurationFile() { mir_cslock lck(csFileShareListAccess); char szBuf[1000]; mir_strcpy(szBuf, szPluginPath); mir_strcat(szBuf, szConfigFile); HANDLE hFile = CreateFile(szBuf, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) return false; DWORD dwBytesWriten = 0; if (WriteFile(hFile, szXmlHeader, sizeof(szXmlHeader) - 1, &dwBytesWriten, nullptr)) { CLFileShareNode *pclCur = pclFirstNode; while (pclCur) { uint32_t dwBytesToWrite = mir_snprintf(szBuf, szXmlData, pclCur->st.pszSrvPath, pclCur->pszOrigRealPath, pclCur->st.nMaxDownloads, SplitIpAddress(pclCur->st.dwAllowedIP), SplitIpAddress(pclCur->st.dwAllowedMask)); WriteFile(hFile, szBuf, dwBytesToWrite, &dwBytesWriten, nullptr); pclCur = pclCur->pclNext; } WriteFile(hFile, szXmlTail, sizeof(szXmlTail) - 1, &dwBytesWriten, nullptr); } SetEndOfFile(hFile); CloseHandle(hFile); return true; } ///////////////////////////////////////////////////////////////////// // Member Function : nAddChangeRemoveShare // Type : Global // Parameters : wParam - Contact handle // lParam - ? // Returns : static int // Description : // // References : - // Remarks : - // Created : 030826, 26 august 2003 // Developer : KN, Houdini, changed By Sérgio Vieira Rolanski ///////////////////////////////////////////////////////////////////// static INT_PTR nAddChangeRemoveShare(WPARAM wParam, LPARAM lParam) { if (!lParam) return 1001; STFileShareInfo * pclNew = (STFileShareInfo*)lParam; // make the server path lowercase char* pszPos = pclNew->pszSrvPath; while (*pszPos) { *pszPos = (char)tolower(*pszPos); pszPos++; } if (pclNew->lStructSize != sizeof(STFileShareInfo)) return 1002; mir_cslockfull lck(csFileShareListAccess); bool bIsDirectory = (pclNew->pszSrvPath[mir_strlen(pclNew->pszSrvPath) - 1] == '/'); CLFileShareNode **pclPrev = &pclFirstNode; CLFileShareNode * pclCur = pclFirstNode; // insert files after directories if (!bIsDirectory) { while (pclCur && pclCur->bIsDirectory()) { pclPrev = &pclCur->pclNext; pclCur = pclCur->pclNext; } } while (pclCur) { if (_stricmp(pclCur->st.pszSrvPath, pclNew->pszSrvPath) == 0) { if (pclCur->bAnyUsers()) { // Some downloads are in progress we will try an terminate them !! // we try for 5 sec. pclCur->CloseAllTransfers(); int nTryCount = 0; do { nTryCount++; if (nTryCount >= 100) return 1004; lck.unlock(); Sleep(50); lck.lock(); } while (pclCur->bAnyUsers()); } if (!pclNew->pszRealPath || pclNew->pszRealPath[0] == 0) { // remove this one *pclPrev = pclCur->pclNext; delete pclCur; } else { // update info !! if (!pclCur->bSetInfo(pclNew)) return 1003; } return !bWriteConfigurationFile(); } pclPrev = &pclCur->pclNext; pclCur = pclCur->pclNext; } // Node was not found we will add a new one. CLFileShareNode* pclNewNode = new CLFileShareNode(pclNew); pclNewNode->pclNext = *pclPrev; *pclPrev = pclNewNode; /* Add by Sérgio Vieira Rolanski */ if (pclNew->dwOptions & OPT_SEND_LINK) SendLinkToUser(wParam, pclNew->pszSrvPath); return !bWriteConfigurationFile(); } ///////////////////////////////////////////////////////////////////// // Member Function : nGetShare // Type : Global // Parameters : WPARAM - ? // lParam - ? // Returns : static int // Description : // // References : - // Remarks : - // Created : 031011, 11 oktober 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// static INT_PTR nGetShare(WPARAM /*wParam*/, LPARAM lParam) { if (!lParam) return 1001; mir_cslock lck(csFileShareListAccess); STFileShareInfo * pclShare = (STFileShareInfo*)lParam; CLFileShareNode * pclCur = pclFirstNode; while (pclCur) { if (mir_strcmp(pclCur->st.pszSrvPath, pclShare->pszSrvPath) == 0) { if (pclShare->dwMaxRealPath <= mir_strlen(pclCur->st.pszRealPath) + 1) return 1003; mir_strcpy(pclShare->pszRealPath, pclCur->st.pszRealPath); pclShare->dwAllowedIP = pclCur->st.dwAllowedIP; pclShare->dwAllowedMask = pclCur->st.dwAllowedMask; pclShare->nMaxDownloads = pclCur->st.nMaxDownloads; pclShare->dwOptions = pclCur->st.dwOptions; return 0; } pclCur = pclCur->pclNext; } return 1002; } ///////////////////////////////////////////////////////////////////// // Member Function : nAddChangeRemoveShare // Type : Global // Parameters : WPARAM - ? // lParam - ? // Returns : static int // Description : // // References : - // Remarks : - // Created : 030903, 03 september 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// static INT_PTR nHttpGetAllShares(WPARAM /*wParam*/, LPARAM /*lParam*/) {/* STFileShareInfo ** pTarget = (STFileShareInfo**)lParam; CLFileShareNode * pclCur; CLFileShareListAccess clCritSection; int nShareCount = 0; for( pclCur = pclFirstNode; pclCur ; pclCur = pclCur->pclNext) nShareCount++; int nCount = 0; *pTarget = (STFileShareInfo*)MirandaMalloc( sizeof( STFileShareInfo) * nShareCount ); for( pclCur = pclFirstNode; pclCur ; pclCur = pclCur->pclNext) { // this code is not good !!! // we need to alloc and copy psz strings also !! memcpy( &((*pTarget)[nCount]), pclCur, sizeof( STFileShareInfo)); nCount++; } */ return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : HandleNewConnection // Type : Global // Parameters : ch - ? // Returns : void // Description : // // References : - // Remarks : - // Created : 030903, 03 september 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// void __cdecl HandleNewConnection(CLHttpUser *pclUser) { pclUser->HandleNewConnection(); delete pclUser; } ///////////////////////////////////////////////////////////////////// // Member Function : ConnectionOpen // Type : Global // Parameters : hNewConnection - ? // dwRemoteIP - ? // Returns : void // Description : // // References : - // Remarks : - // Created : 030813, 13 august 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// void ConnectionOpen(HNETLIBCONN hNewConnection, uint32_t dwRemoteIP, void*) { in_addr stAddr; stAddr.S_un.S_addr = htonl(dwRemoteIP); CLHttpUser *pclUser = new CLHttpUser(hNewConnection, stAddr); mir_forkThread(HandleNewConnection, pclUser); } ///////////////////////////////////////////////////////////////////// // Member Function : nProtoAck // Type : Global // Parameters : wParam - ? // lParam - ? // Returns : static int // Description : // // References : - // Remarks : - // Created : 031213, 13 december 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// static int nProtoAck(WPARAM /*wParam*/, LPARAM lParam) { //todo: ignore weather protos ACKDATA *ack = (ACKDATA *)lParam; if (ack->type != ACKTYPE_STATUS || //only send for statuses ack->result != ACKRESULT_SUCCESS) //only successful ones return 0; bIsOnline = ((int)ack->lParam != ID_STATUS_AWAY && (int)ack->lParam != ID_STATUS_NA); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : nToggelAcceptConnections // Type : Global // Parameters : WPARAM - ? // LPARAM - ? // Returns : int // Description : // // References : - // Remarks : - // Created : 031011, 11 oktober 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// INT_PTR nToggelAcceptConnections(WPARAM wparam, LPARAM /*lparam*/) { if (!hDirectBoundPort) { NETLIBUSERSETTINGS nus = {}; nus.cbSize = sizeof(nus); Netlib_GetUserSettings(hNetlibUser, &nus); NETLIBBIND nlb = {}; nlb.pfnNewConnection = ConnectionOpen; if (nus.specifyIncomingPorts && nus.szIncomingPorts && nus.szIncomingPorts[0]) nlb.wPort = 0; else nlb.wPort = 80; hDirectBoundPort = Netlib_BindPort(hNetlibUser, &nlb); if (!hDirectBoundPort) { char szTemp[200]; mir_snprintf(szTemp, Translate("Failed to bind to port %s\r\nThis is most likely because another program or service is using this port"), nlb.wPort == 80 ? "80" : nus.szIncomingPorts); MessageBox(nullptr, szTemp, MSG_BOX_TITLE, MB_OK); return 1001; } dwLocalPortUsed = nlb.wPort; dwLocalIpAddress = nlb.dwInternalIP; Menu_ModifyItem(hAcceptConnectionsMenuItem, LPGENW("Disable HTTP server"), iconList[0].hIcolib); } else if (hDirectBoundPort && wparam == 0) { Netlib_CloseHandle(hDirectBoundPort); hDirectBoundPort = nullptr; Menu_ModifyItem(hAcceptConnectionsMenuItem, LPGENW("Enable HTTP server"), iconList[1].hIcolib); } else return 0; // no changes; if (!bShutdownInProgress) g_plugin.setByte("AcceptConnections", hDirectBoundPort != nullptr); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : MainInit // Type : Global // Parameters : wparam - ? // lparam - ? // Returns : int // Description : Called when system modules has been loaded // // References : - // Remarks : - // Created : 020422, 22 April 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// int MainInit(WPARAM /*wparam*/, LPARAM /*lparam*/) { if (!bReadConfigurationFile()) { char szRealPath[MAX_PATH]; char szSrvPath[MAX_PATH] = { 0 }; STFileShareInfo share; const char** p = pszDefaultShares; while (*p) { memset(&share, 0, sizeof(share)); share.lStructSize = sizeof(share); share.dwAllowedIP = 0; share.dwAllowedMask = 0; share.nMaxDownloads = -1; share.pszRealPath = szRealPath; share.dwMaxRealPath = sizeof(szRealPath); mir_strcpy(share.pszRealPath, p[0]); share.pszSrvPath = szSrvPath; share.dwMaxSrvPath = sizeof(szSrvPath); mir_strcpy(share.pszSrvPath, p[1]); if (CallService(MS_HTTP_ADD_CHANGE_REMOVE, 0, (LPARAM)&share)) break; p += 2; } bWriteConfigurationFile(); } NETLIBUSER nlu = {}; nlu.flags = NUF_OUTGOING | NUF_INCOMING; nlu.szSettingsModule = MODULENAME; nlu.szDescriptiveName.a = Translate("HTTP Server"); hNetlibUser = Netlib_RegisterUser(&nlu); if (g_plugin.getByte("AcceptConnections", 1)) nToggelAcceptConnections(0, 0); InitGuiElements(); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : PreShutdown // Type : Global // Parameters : WPARAM - ? // LPARAM - ? // Returns : int // Description : // // References : - // Remarks : - // Created : 030811, 11 august 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// int PreShutdown(WPARAM /*wparam*/, LPARAM /*lparam*/) { bShutdownInProgress = true; { mir_cslock lck(csFileShareListAccess); for (CLFileShareNode * pclCur = pclFirstNode; pclCur; pclCur = pclCur->pclNext) pclCur->CloseAllTransfers(); } if (hDirectBoundPort) nToggelAcceptConnections(0, 0); Netlib_CloseHandle(hNetlibUser); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : nSystemShutdown // Type : Global // Parameters : wparam - 0 // lparam - 0 // Returns : int // Description : // // References : - // Remarks : - // Created : 020428, 28 April 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// int nSystemShutdown(WPARAM /*wparam*/, LPARAM /*lparam*/) { while (pclFirstNode) { CLFileShareNode * pclCur = pclFirstNode; pclFirstNode = pclFirstNode->pclNext; delete pclCur; } pclFirstNode = nullptr; g_plugin.setByte("IndexCreationMode", (uint8_t)indexCreationMode); FreeIndexHTMLTemplate(); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : Load // Type : Global // Parameters : link - ? // Returns : int // Description : // // References : - // Remarks : - // Created : 020422, 22 April 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// int CMPlugin::Load() { g_plugin.registerIcon(MSG_BOX_TITLE, iconList, MODULENAME); hHttpAcceptConnectionsService = CreateServiceFunction(MS_HTTP_ACCEPT_CONNECTIONS, nToggelAcceptConnections); hHttpAddChangeRemoveService = CreateServiceFunction(MS_HTTP_ADD_CHANGE_REMOVE, nAddChangeRemoveShare); hHttpGetShareService = CreateServiceFunction(MS_HTTP_GET_SHARE, nGetShare); hHttpGetAllShares = CreateServiceFunction(MS_HTTP_GET_ALL_SHARES, nHttpGetAllShares); hEventSystemInit = HookEvent(ME_SYSTEM_MODULESLOADED, MainInit); hPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); Profile_GetPathA(MAX_PATH, szPluginPath); mir_strncat(szPluginPath, "\\HTTPServer\\", _countof(szPluginPath) - mir_strlen(szPluginPath)); int err = CreateDirectoryTree(szPluginPath); if ((err != 0) && (err != ERROR_ALREADY_EXISTS)) { MessageBoxW(nullptr, L"Failed to create HTTPServer directory.", TranslateW(_A2W(MSG_BOX_TITLE)), MB_OK); return 1; } nPluginPathLen = (int)mir_strlen(szPluginPath); sLogFilePath = szPluginPath; sLogFilePath += "HTTPServer.log"; if (!bInitMimeHandling()) MessageBoxW(nullptr, L"Failed to read configuration file : " szMimeTypeConfigFile, TranslateW(_A2W(MSG_BOX_TITLE)), MB_OK); nMaxUploadSpeed = g_plugin.getDword("MaxUploadSpeed", nMaxUploadSpeed); nMaxConnectionsTotal = g_plugin.getDword("MaxConnectionsTotal", nMaxConnectionsTotal); nMaxConnectionsPerUser = g_plugin.getDword("MaxConnectionsPerUser", nMaxConnectionsPerUser); bLimitOnlyWhenOnline = g_plugin.getByte("LimitOnlyWhenOnline", bLimitOnlyWhenOnline) != 0; indexCreationMode = (eIndexCreationMode)g_plugin.getByte("IndexCreationMode", 2); CMenuItem mi(&g_plugin); SET_UID(mi, 0xf0a68784, 0xc30e, 0x4245, 0xb6, 0x2b, 0xb8, 0x71, 0x7e, 0xe6, 0xe1, 0x73); mi.flags = CMIF_UNICODE; mi.hIcolibItem = g_plugin.getIconHandle(IDI_SHARE_NEW_FILE); mi.position = 1000085000; mi.name.a = LPGEN("Enable HTTP server"); mi.pszService = MS_HTTP_ACCEPT_CONNECTIONS; hAcceptConnectionsMenuItem = Menu_AddMainMenuItem(&mi); if (indexCreationMode == INDEX_CREATION_HTML || indexCreationMode == INDEX_CREATION_DETECT) if (!LoadIndexHTMLTemplate()) { indexCreationMode = INDEX_CREATION_DISABLE; g_plugin.setByte("IndexCreationMode", (uint8_t)indexCreationMode); } HookEvent(ME_OPT_INITIALISE, OptionsInitialize); HookEvent(ME_PROTO_ACK, nProtoAck); return 0; } ///////////////////////////////////////////////////////////////////// // Member Function : Unload // Type : Global // Parameters : none // Returns : // Description : // // References : - // Remarks : - // Created : 020422, 22 April 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// int CMPlugin::Unload() { nSystemShutdown(0, 0); if (hwndStatsticView) DestroyWindow(hwndStatsticView); return 0; }