Implemented file upload to agents
authorVictor Kirhenshtein <victor@netxms.org>
Wed, 29 Dec 2004 19:10:56 +0000 (19:10 +0000)
committerVictor Kirhenshtein <victor@netxms.org>
Wed, 29 Dec 2004 19:10:56 +0000 (19:10 +0000)
13 files changed:
TODO
include/nms_agent.h
include/nms_cscp.h
include/nxclapi.h
include/nxcscpapi.h
include/unicode.h
src/agent/core/comm.cpp
src/agent/core/nxagentd.cpp
src/agent/core/nxagentd.h
src/agent/core/session.cpp
src/server/include/nxsrvapi.h
src/server/libnxsrv/agent.cpp
src/server/libnxsrv/main.cpp

diff --git a/TODO b/TODO
index 44b658f..b576864 100644 (file)
--- a/TODO
+++ b/TODO
@@ -14,7 +14,6 @@ GENERAL:
 - Write README for NetWare
 - SMS-sender interface with drivers for cell-phones/SMPP
 - Add check for read/write locks support to configure script
-- Implement daemon() function for platforms without it
 - Allow converting counters to deltas in data collection
 - Add ability to clear collected DCI data
 - Add possibility to set different polling timeouts for different nodes
index 7d3b394..a0d8fae 100644 (file)
@@ -47,6 +47,7 @@
 #define ERR_SUCCESS                 ((DWORD)0)
 #define ERR_UNKNOWN_COMMAND         ((DWORD)400)
 #define ERR_AUTH_REQUIRED           ((DWORD)401)
+#define ERR_ACCESS_DENIED           ((DWORD)403)
 #define ERR_UNKNOWN_PARAMETER       ((DWORD)404)
 #define ERR_REQUEST_TIMEOUT         ((DWORD)408)
 #define ERR_AUTH_FAILED             ((DWORD)440)
@@ -58,6 +59,8 @@
 #define ERR_NOT_CONNECTED           ((DWORD)900)
 #define ERR_CONNECTION_BROKEN       ((DWORD)901)
 #define ERR_BAD_RESPONCE            ((DWORD)902)
+#define ERR_IO_FAILURE              ((DWORD)903)
+#define ERR_RESOURCE_BUSY           ((DWORD)904)
 
 
 //
index d4a1118..4b2a0c7 100644 (file)
@@ -157,6 +157,7 @@ typedef struct
 //
 
 #define MF_BINARY    0x0001
+#define MF_EOF       0x0002
 
 
 //
@@ -268,8 +269,7 @@ typedef struct
 #define CMD_GET_SERVER_INFO         0x0067
 #define CMD_SET_DCI_STATUS          0x0068
 #define CMD_FILE_DATA               0x0069
-#define CMD_WEBAPI_LOGIN            0x006A
-#define CMD_WEBAPI_ATTACH           0x006B
+#define CMD_TRANSFER_FILE           0x006A
 
 
 //
@@ -399,7 +399,8 @@ typedef struct
 #define VID_SERVER_VERSION          ((DWORD)121)
 #define VID_SUPPORTED_ENCRYPTION    ((DWORD)122)
 #define VID_EVENT_ID                ((DWORD)123)
-#define VID_WEBAPI_SID              ((DWORD)124)
+#define VID_AGENT_VERSION           ((DWORD)124)
+#define VID_FILE_NAME               ((DWORD)125)
 
 // Variable ranges for object's ACL
 #define VID_ACL_USER_BASE           ((DWORD)0x00001000)
index 6cf15f8..058cffe 100644 (file)
@@ -62,6 +62,7 @@ typedef void * NXC_SESSION;
 #define MAX_USER_DESCR           256
 #define MAX_ITEM_NAME            256
 #define MAX_STRING_VALUE         256
+#define MAX_AGENT_VERSION_LEN    64
 #define GROUP_FLAG               ((DWORD)0x80000000)
 #define GROUP_EVERYONE           ((DWORD)0x80000000)
 #define INVALID_UID              ((DWORD)0xFFFFFFFF)
@@ -275,8 +276,9 @@ typedef void * NXC_SESSION;
 #define SYSTEM_ACCESS_EDIT_EVENT_DB       0x0020
 #define SYSTEM_ACCESS_EPP                 0x0040
 #define SYSTEM_ACCESS_MANAGE_ACTIONS      0x0080
+#define SYSTEM_ACCESS_DELETE_ALARMS       0x0100
 
-#define SYSTEM_ACCESS_FULL                0x00FF
+#define SYSTEM_ACCESS_FULL                0x01FF
 
 
 //
@@ -583,6 +585,7 @@ typedef struct
          WORD wAgentPort;     // Listening TCP port for native agent
          WORD wAuthMethod;    // Native agent's authentication method
          TCHAR *pszDescription;
+         TCHAR szAgentVersion[MAX_AGENT_VERSION_LEN];
          WORD wSNMPVersion;
       } node;
       struct
@@ -958,8 +961,6 @@ DWORD LIBNXCL_EXPORTABLE NXCCreateTrap(NXC_SESSION hSession, DWORD *pdwTrapId);
 DWORD LIBNXCL_EXPORTABLE NXCModifyTrap(NXC_SESSION hSession, NXC_TRAP_CFG_ENTRY *pTrap);
 DWORD LIBNXCL_EXPORTABLE NXCDeleteTrap(NXC_SESSION hSession, DWORD dwTrapId);
 
-DWORD LIBNXCL_EXPORTABLE NXCAttachWebSession(NXC_SESSION hSession, QWORD qwSID);
-
 #ifdef __cplusplus
 }
 #endif
index b71ff17..79028f6 100644 (file)
@@ -179,6 +179,7 @@ CSCP_MESSAGE LIBNXCSCP_EXPORTABLE *CreateRawCSCPMessage(WORD wCode, DWORD dwId,
                                                         DWORD dwDataSize, void *pData,
                                                         CSCP_MESSAGE *pBuffer);
 TCHAR LIBNXCSCP_EXPORTABLE *CSCPMessageCodeName(WORD wCode, TCHAR *pszBuffer);
+BOOL LIBNXCSCP_EXPORTABLE SendFileOverCSCP(SOCKET hSocket, DWORD dwId, TCHAR *pszFile);
    
 #ifdef __cplusplus
 }
index 21f2176..94e8dd7 100644 (file)
@@ -75,6 +75,7 @@
 #define _tcsdup   strdup
 #define _tcsupr   strupr
 #define _tcsspn   strspn
+#define _topen    open
 
 #endif
 
index 1197058..5c5dfb1 100644 (file)
@@ -51,13 +51,16 @@ static MUTEX m_hSessionListAccess;
 // Validates server's address
 //
 
-static BOOL IsValidServerAddr(DWORD dwAddr)
+static BOOL IsValidServerAddr(DWORD dwAddr, BOOL *pbInstallationServer)
 {
    DWORD i;
 
    for(i=0; i < g_dwServerCount; i++)
-      if (dwAddr == g_dwServerAddr[i])
+      if (dwAddr == g_pServerList[i].dwIpAddr)
+      {
+         *pbInstallationServer = g_pServerList[i].bInstallationServer;
          return TRUE;
+      }
    return FALSE;
 }
 
@@ -110,6 +113,7 @@ THREAD_RESULT THREAD_CALL ListenerThread(void *)
    socklen_t iSize;
    CommSession *pSession;
    char szBuffer[256];
+   BOOL bInstallationServer;
 
    // Create socket
    if ((hSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
@@ -160,13 +164,14 @@ THREAD_RESULT THREAD_CALL ListenerThread(void *)
       iNumErrors = 0;     // Reset consecutive errors counter
       DebugPrintf("Incoming connection from %s", IpToStr(ntohl(servAddr.sin_addr.s_addr), szBuffer));
 
-      if (IsValidServerAddr(servAddr.sin_addr.s_addr))
+      if (IsValidServerAddr(servAddr.sin_addr.s_addr, &bInstallationServer))
       {
          g_dwAcceptedConnections++;
          DebugPrintf("Connection from %s accepted", szBuffer);
 
          // Create new session structure and threads
-         pSession = new CommSession(hClientSocket, ntohl(servAddr.sin_addr.s_addr));
+         pSession = new CommSession(hClientSocket, ntohl(servAddr.sin_addr.s_addr), 
+                                    bInstallationServer);
          if (!RegisterSession(pSession))
          {
             delete pSession;
index 154a458..2f4209f 100644 (file)
@@ -72,8 +72,9 @@ DWORD g_dwFlags = 0;
 char g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
 char g_szSharedSecret[MAX_SECRET_LENGTH] = "admin";
 char g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
+char g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
 WORD g_wListenPort = AGENT_LISTEN_PORT;
-DWORD g_dwServerAddr[MAX_SERVERS];
+SERVER_INFO g_pServerList[MAX_SERVERS];
 DWORD g_dwServerCount = 0;
 DWORD g_dwTimeOut = 5000;     // Request timeout in milliseconds
 time_t g_dwAgentStartTime;
@@ -97,6 +98,7 @@ DWORD (__stdcall *imp_HrLanConnectionNameFromGuidOrPath)(LPWSTR, LPWSTR, LPWSTR,
 
 static char *m_pszActionList = NULL;
 static char *m_pszServerList = NULL;
+static char *m_pszInstallServerList = NULL;
 static char *m_pszSubagentList = NULL;
 static char *m_pszExtParamList = NULL;
 static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
@@ -112,6 +114,8 @@ static NX_CFG_TEMPLATE cfgTemplate[] =
    { "Action", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList },
    { "EnableActions", CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags },
    { "ExternalParameter", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtParamList },
+   { "FileStore", CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore },
+   { "InstallationServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszInstallServerList },
    { "ListenPort", CT_WORD, 0, 0, 0, 0, &g_wListenPort },
    { "LogFile", CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile },
    { "LogUnresolvedSymbols", CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags },
@@ -276,21 +280,62 @@ BOOL Initialize(void)
          if (pEnd != NULL)
             *pEnd = 0;
          StrStrip(pItem);
-         g_dwServerAddr[g_dwServerCount] = inet_addr(pItem);
-         if ((g_dwServerAddr[g_dwServerCount] == INADDR_NONE) ||
-             (g_dwServerAddr[g_dwServerCount] == INADDR_ANY))
+         g_pServerList[g_dwServerCount].dwIpAddr = inet_addr(pItem);
+         if ((g_pServerList[g_dwServerCount].dwIpAddr == INADDR_NONE) ||
+             (g_pServerList[g_dwServerCount].dwIpAddr == INADDR_ANY))
          {
             if (!(g_dwFlags & AF_DAEMON))
                printf("Invalid server address '%s'\n", pItem);
          }
          else
          {
+            g_pServerList[g_dwServerCount].bInstallationServer = FALSE;
             g_dwServerCount++;
          }
       }
       free(m_pszServerList);
    }
 
+   // Parse installation server list
+   if (m_pszInstallServerList != NULL)
+   {
+      DWORD i, dwAddr;
+
+      for(pItem = m_pszInstallServerList; *pItem != 0; pItem = pEnd + 1)
+      {
+         pEnd = strchr(pItem, ',');
+         if (pEnd != NULL)
+            *pEnd = 0;
+         StrStrip(pItem);
+
+         dwAddr = inet_addr(pItem);
+         if ((dwAddr == INADDR_NONE) ||
+             (dwAddr == INADDR_ANY))
+         {
+            if (!(g_dwFlags & AF_DAEMON))
+               printf("Invalid server address '%s'\n", pItem);
+         }
+         else
+         {
+            for(i = 0; i < g_dwServerCount; i++)
+               if (g_pServerList[i].dwIpAddr == dwAddr)
+                  break;
+
+            if (i == g_dwServerCount)
+            {
+               g_pServerList[g_dwServerCount].dwIpAddr = dwAddr;
+               g_pServerList[g_dwServerCount].bInstallationServer = TRUE;
+               g_dwServerCount++;
+            }
+            else
+            {
+               g_pServerList[i].bInstallationServer = TRUE;
+            }
+         }
+      }
+      free(m_pszInstallServerList);
+   }
+
    // Load subagents
    if (m_pszSubagentList != NULL)
    {
index 05495f5..7f48cf4 100644 (file)
@@ -33,6 +33,7 @@
 #include "messages.h"
 
 #ifdef _WIN32
+#include <io.h>
 #include <psapi.h>
 #endif
 
 //
 
 #if defined(_WIN32)
-#define AGENT_DEFAULT_CONFIG  "C:\\nxagentd.conf"
-#define AGENT_DEFAULT_LOG     "C:\\nxagentd.log"
+#define AGENT_DEFAULT_CONFIG     "C:\\nxagentd.conf"
+#define AGENT_DEFAULT_LOG        "C:\\nxagentd.log"
+#define AGENT_DEFAULT_FILE_STORE "C:\\"
 #elif defined(_NETWARE)
-#define AGENT_DEFAULT_CONFIG  "SYS:ETC/nxagentd.conf"
-#define AGENT_DEFAULT_LOG     "SYS:ETC/nxagentd.log"
+#define AGENT_DEFAULT_CONFIG     "SYS:ETC/nxagentd.conf"
+#define AGENT_DEFAULT_LOG        "SYS:ETC/nxagentd.log"
+#define AGENT_DEFAULT_FILE_STORE "SYS:\\"
 #else
-#define AGENT_DEFAULT_CONFIG  "/etc/nxagentd.conf"
-#define AGENT_DEFAULT_LOG     "/var/log/nxagentd"
+#define AGENT_DEFAULT_CONFIG     "/etc/nxagentd.conf"
+#define AGENT_DEFAULT_LOG        "/var/log/nxagentd"
+#define AGENT_DEFAULT_FILE_STORE "/tmp"
 #endif
 
 
@@ -200,6 +204,17 @@ struct SUBAGENT
 
 
 //
+// Server information
+//
+
+struct SERVER_INFO
+{
+   DWORD dwIpAddr;
+   BOOL bInstallationServer;
+};
+
+
+//
 // Communication session
 //
 
@@ -215,11 +230,15 @@ private:
    DWORD m_dwHostAddr;        // IP address of connected host (network byte order)
    DWORD m_dwIndex;
    BOOL m_bIsAuthenticated;
+   BOOL m_bInstallationServer;
+   int m_hCurrFile;
+   DWORD m_dwFileRqId;
 
    void Authenticate(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void GetParameter(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void GetList(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void Action(CSCPMessage *pRequest, CSCPMessage *pMsg);
+   void RecvFile(CSCPMessage *pRequest, CSCPMessage *pMsg);
 
    void ReadThread(void);
    void WriteThread(void);
@@ -230,7 +249,7 @@ private:
    static THREAD_RESULT THREAD_CALL ProcessingThreadStarter(void *);
 
 public:
-   CommSession(SOCKET hSocket, DWORD dwHostAddr);
+   CommSession(SOCKET hSocket, DWORD dwHostAddr, BOOL bInstallServer);
    ~CommSession();
 
    void Run(void);
@@ -296,8 +315,9 @@ extern DWORD g_dwFlags;
 extern char g_szLogFile[];
 extern char g_szSharedSecret[];
 extern char g_szConfigFile[];
+extern char g_szFileStore[];
 extern WORD g_wListenPort;
-extern DWORD g_dwServerAddr[];
+extern SERVER_INFO g_pServerList[];
 extern DWORD g_dwServerCount;
 extern time_t g_dwAgentStartTime;
 
index f5ed392..394a7b3 100644 (file)
@@ -80,7 +80,7 @@ THREAD_RESULT THREAD_CALL CommSession::ProcessingThreadStarter(void *pArg)
 // Client session class constructor
 //
 
-CommSession::CommSession(SOCKET hSocket, DWORD dwHostAddr)
+CommSession::CommSession(SOCKET hSocket, DWORD dwHostAddr, BOOL bInstallationServer)
 {
    m_pSendQueue = new Queue;
    m_pMessageQueue = new Queue;
@@ -91,6 +91,8 @@ CommSession::CommSession(SOCKET hSocket, DWORD dwHostAddr)
    m_hProcessingThread = INVALID_THREAD_HANDLE;
    m_dwHostAddr = dwHostAddr;
    m_bIsAuthenticated = (g_dwFlags & AF_REQUIRE_AUTH) ? FALSE : TRUE;
+   m_bInstallationServer = bInstallationServer;
+   m_hCurrFile = -1;
 }
 
 
@@ -105,6 +107,8 @@ CommSession::~CommSession()
    delete m_pSendQueue;
    delete m_pMessageQueue;
    safe_free(m_pMsgBuffer);
+   if (m_hCurrFile != -1)
+      close(m_hCurrFile);
 }
 
 
@@ -129,7 +133,7 @@ void CommSession::ReadThread(void)
    CSCP_MESSAGE *pRawMsg;
    CSCPMessage *pMsg;
    int iErr;
-   char szBuffer[32];
+   char szBuffer[256];
    WORD wFlags;
 
    // Initialize raw message receiving function
@@ -159,8 +163,41 @@ void CommSession::ReadThread(void)
          pRawMsg->dwId = ntohl(pRawMsg->dwId);
          pRawMsg->wCode = ntohs(pRawMsg->wCode);
          pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
+         DebugPrintf("Received raw message %s", CSCPMessageCodeName(pRawMsg->wCode, szBuffer));
+
          if (pRawMsg->wCode == CMD_FILE_DATA)
          {
+            if ((m_hCurrFile != -1) && (m_dwFileRqId == pRawMsg->dwId))
+            {
+               if (write(m_hCurrFile, pRawMsg->df, pRawMsg->dwNumVars) == (int)pRawMsg->dwNumVars)
+               {
+                  if (wFlags & MF_EOF)
+                  {
+                     CSCPMessage msg;
+
+                     close(m_hCurrFile);
+                     m_hCurrFile = -1;
+                  
+                     msg.SetCode(CMD_REQUEST_COMPLETED);
+                     msg.SetId(pRawMsg->dwId);
+                     msg.SetVariable(VID_RCC, ERR_SUCCESS);
+                     SendMessage(&msg);
+                  }
+               }
+               else
+               {
+                  // I/O error
+                  CSCPMessage msg;
+
+                  close(m_hCurrFile);
+                  m_hCurrFile = -1;
+               
+                  msg.SetCode(CMD_REQUEST_COMPLETED);
+                  msg.SetId(pRawMsg->dwId);
+                  msg.SetVariable(VID_RCC, ERR_IO_FAILURE);
+                  SendMessage(&msg);
+               }
+            }
          }
       }
       else
@@ -257,6 +294,9 @@ void CommSession::ProcessingThread(void)
             case CMD_ACTION:
                Action(pMsg, &msg);
                break;
+            case CMD_TRANSFER_FILE:
+               RecvFile(pMsg, &msg);
+               break;
             default:
                msg.SetVariable(VID_RCC, ERR_UNKNOWN_COMMAND);
                break;
@@ -412,3 +452,59 @@ void CommSession::Action(CSCPMessage *pRequest, CSCPMessage *pMsg)
       safe_free(args.ppStringList[i]);
    safe_free(args.ppStringList);
 }
+
+
+//
+// Prepare for receiving file
+//
+
+void CommSession::RecvFile(CSCPMessage *pRequest, CSCPMessage *pMsg)
+{
+   int i;
+   size_t nLen;
+   TCHAR szFileName[MAX_PATH], szFullPath[MAX_PATH];
+
+   if (m_bInstallationServer)
+   {
+      szFileName[0] = 0;
+      pRequest->GetVariableStr(VID_FILE_NAME, szFileName, MAX_PATH);
+      DebugPrintf("Preparing for receiving file \"%s\"", szFileName);
+      for(i = _tcslen(szFileName) - 1; 
+          (i >= 0) && (szFileName[i] != '\\') && (szFileName[i] != '/'); i--);
+
+      _tcscpy(szFullPath, g_szFileStore);
+      nLen = _tcslen(szFullPath);
+      if ((szFullPath[nLen - 1] != '\\') &&
+          (szFullPath[nLen - 1] != '/'))
+      {
+         _tcscat(szFullPath, FS_PATH_SEPARATOR);
+         nLen++;
+      }
+      _tcsncpy(&szFullPath[nLen], szFileName, MAX_PATH - nLen);
+
+      // Check if for some reason we have already opened file
+      if (m_hCurrFile != -1)
+      {
+         pMsg->SetVariable(VID_RCC, ERR_RESOURCE_BUSY);
+      }
+      else
+      {
+         DebugPrintf("Writing to local file \"%s\"", szFullPath);
+         m_hCurrFile = _topen(szFullPath, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0600);
+         if (m_hCurrFile == -1)
+         {
+            DebugPrintf("Error opening file for writing: %s", strerror(errno));
+            pMsg->SetVariable(VID_RCC, ERR_IO_FAILURE);
+         }
+         else
+         {
+            m_dwFileRqId = pRequest->GetId();
+            pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
+         }
+      }
+   }
+   else
+   {
+      pMsg->SetVariable(VID_RCC, ERR_ACCESS_DENIED);
+   }
+}
index 6ce3e49..ed85200 100644 (file)
@@ -182,6 +182,7 @@ public:
    DWORD GetList(TCHAR *pszParam);
    DWORD Nop(void);
    DWORD ExecAction(TCHAR *pszAction, int argc, TCHAR **argv);
+   DWORD UploadFile(TCHAR *pszFile);
 
    DWORD GetNumDataLines(void) { return m_dwNumDataLines; }
    const TCHAR *GetDataLine(DWORD dwIndex) { return dwIndex < m_dwNumDataLines ? m_ppDataLines[dwIndex] : _T("(error)"); }
index 821381f..4930800 100644 (file)
@@ -695,3 +695,45 @@ DWORD AgentConnection::ExecAction(TCHAR *pszAction, int argc, TCHAR **argv)
    else
       return ERR_CONNECTION_BROKEN;
 }
+
+
+//
+// Upload file to agent
+//
+
+DWORD AgentConnection::UploadFile(TCHAR *pszFile)
+{
+   DWORD dwRqId, dwResult;
+   CSCPMessage msg;
+   int i;
+
+   if (!m_bIsConnected)
+      return ERR_NOT_CONNECTED;
+
+   dwRqId = m_dwRequestId++;
+
+   msg.SetCode(CMD_TRANSFER_FILE);
+   msg.SetId(dwRqId);
+   for(i = _tcslen(pszFile) - 1; 
+       (i >= 0) && (pszFile[i] != '\\') && (pszFile[i] != '/'); i--);
+   msg.SetVariable(VID_FILE_NAME, &pszFile[i + 1]);
+
+   if (SendMessage(&msg))
+   {
+      dwResult = WaitForRCC(dwRqId, m_dwCommandTimeout);
+   }
+   else
+   {
+      dwResult = ERR_CONNECTION_BROKEN;
+   }
+
+   if (dwResult == ERR_SUCCESS)
+   {
+      if (SendFileOverCSCP(m_hSocket, dwRqId, pszFile))
+         dwResult = WaitForRCC(dwRqId, m_dwCommandTimeout);
+      else
+         dwResult = ERR_IO_FAILURE;
+   }
+
+   return dwResult;
+}
index 4edb033..2979a27 100644 (file)
@@ -37,6 +37,7 @@ static struct
    { ERR_SUCCESS, _T("Success") },
    { ERR_UNKNOWN_COMMAND, _T("Unknown command") },
    { ERR_AUTH_REQUIRED, _T("Authentication required") },
+   { ERR_ACCESS_DENIED, _T("Access denied") },
    { ERR_UNKNOWN_PARAMETER, _T("Unknown parameter") },
    { ERR_REQUEST_TIMEOUT, _T("Request timeout") },
    { ERR_AUTH_FAILED, _T("Authentication failed") },
@@ -48,6 +49,8 @@ static struct
    { ERR_NOT_CONNECTED, _T("Not connected") },
    { ERR_CONNECTION_BROKEN, _T("Connection broken") },
    { ERR_BAD_RESPONCE, _T("Bad responce") },
+   { ERR_IO_FAILURE, _T("I/O failure") },
+   { ERR_RESOURCE_BUSY, _T("Resource busy") },
    { -1, NULL }
 };