Index: src/rms.cpp
===================================================================
--- src/rms.cpp	(revision 6179)
+++ src/rms.cpp	(working copy)
@@ -22,6 +22,7 @@
 #include "licq_file.h"
 #include "licq_user.h"
 #include "licq_constants.h"
+#include "licq_history.h"
 
 extern "C" { const char *LP_Version(); }
 
@@ -60,6 +61,8 @@
 const unsigned short CODE_SECURExSTAT = 228;
 const unsigned short CODE_NOTIFYxON = 229;
 const unsigned short CODE_NOTIFYxOFF = 230;
+const unsigned short CODE_HISTORYxEVENT = 231;
+const unsigned short CODE_HISTORYxEND = 232;
 const unsigned short CODE_VIEWxUNKNOWN = 299;
 // 300 - further action required
 const unsigned short CODE_ENTERxUIN = 300;
@@ -95,6 +98,9 @@
 
 #define NEXT_WORD(s) while (*s != '\0' && *s == ' ') s++;
 
+#define FIRST_PARAM(s) s = strtok(s, " ");
+#define NEXT_PARAM(s) s = strtok(NULL, " ");
+
 struct Command
 {
   const char *name;
@@ -138,6 +144,8 @@
     "Send an sms { <uin> }." },
   { "NOTIFY", &CRMSClient::Process_NOTIFY,
     "Notify events" },
+  { "HISTORY", &CRMSClient::Process_HISTORY,
+    "View history of specific user { <id>[.<protocol>] [<lenght>] [<offset>]}." },
 };
 
 static const unsigned short NUM_COMMANDS = sizeof(commands)/sizeof(*commands);
@@ -480,6 +488,9 @@
   m_bNotify = false;
   m_szEventId = 0;
   m_nEventPPID = 0;
+  m_history = 0;
+  m_szHistoryLastId = 0;
+  
 }
 
 
@@ -1578,3 +1589,154 @@
   
   return fflush(fs);
 }
+
+/*---------------------------------------------------------------------------
+ * CRMSClient::Process_HISTORY
+ *
+ * Command:
+ *   HISTORY <uin> <lenght> <offset>
+ *
+ * Response:
+ *
+ *-------------------------------------------------------------------------*/
+int CRMSClient::Process_HISTORY()
+{
+
+  FIRST_PARAM(data_arg);
+
+  int lenght = 10;
+  int offset = 0;
+  ICQUser *u = NULL;
+  ICQOwner *o = gUserManager.FetchOwner(m_nPPID, LOCK_R);
+
+
+  if (data_arg == NULL)
+  {
+    fprintf(fs, "%d Invalid User.\n", CODE_INVALIDxUSER);
+    return fflush(fs);
+  }
+
+  ParseUser(data_arg);
+
+  if ((m_szId == NULL) || (m_szId && (u = gUserManager.FetchUser(m_szId, m_nPPID, LOCK_R)) == NULL))
+  {
+    fprintf(fs, "%d Invalid User (%s,%lu).\n", CODE_INVALIDxUSER, m_szId, m_nPPID);
+    return fflush(fs);
+  }
+
+  if( (o = gUserManager.FetchOwner(m_nPPID, LOCK_R)) == NULL)
+  {
+    gUserManager.DropUser(u);
+    fprintf(fs, "%d Cannot fetch owner (%lu).\n", CODE_EVENTxERROR, m_nPPID);
+    return fflush(fs);
+  }
+
+  NEXT_PARAM(data_arg);
+
+  if (data_arg != NULL)
+  {
+    lenght = atoi(data_arg);
+  }
+
+  NEXT_PARAM(data_arg);
+
+  if (data_arg != NULL)
+  {
+    offset = atoi(data_arg);
+  }
+
+  if(m_history == NULL)
+    m_history = new CUserHistory;
+
+  if( m_szHistoryLastId == NULL || (m_szHistoryLastId != NULL && strcmp(m_szHistoryLastId, m_szId) != 0) || (m_nHistoryLastPPID != m_nPPID) )
+  {
+    if(m_szHistoryLastId)
+      free(m_szHistoryLastId);
+    
+    m_szHistoryLastId = strdup(m_szId);
+    m_nHistoryLastPPID = m_nPPID;
+    m_history->Clear(m_historyList);
+
+    m_history->SetFile("default", m_szId, m_nPPID);
+    if(!m_history->Load(m_historyList)){
+      gUserManager.DropOwner(m_nPPID);
+      gUserManager.DropUser(u);      
+      fprintf(fs, "%d Cannot load history file.\n", CODE_EVENTxERROR);
+      return fflush(fs);
+    }
+
+  }
+
+      
+  int counter = -1;
+  HistoryListRIter it = m_historyList.rbegin();
+  while (it != m_historyList.rend())
+  {
+    counter++;
+    if(counter < offset || counter - offset >= lenght)
+    {
+      it++;
+      continue;
+    }
+
+    char szEventHeader[75]; // Allows 50 chars for a nick
+    CUserEvent *e = *it;
+
+    switch (e->SubCommand())
+    {
+      case ICQ_CMDxSUB_MSG:
+        sprintf(szEventHeader, "%d Message ", CODE_VIEWxMSG);
+        break;
+
+      case ICQ_CMDxSUB_URL:
+        sprintf(szEventHeader, "%d URL ", CODE_VIEWxURL);
+        break;
+
+      case ICQ_CMDxSUB_CHAT:
+        sprintf(szEventHeader, "%d Chat Request ", CODE_VIEWxCHAT);
+        break;
+
+      case ICQ_CMDxSUB_FILE:
+        sprintf(szEventHeader, "%d File Request ", CODE_VIEWxFILE);
+        break;
+
+      default:
+        sprintf(szEventHeader, "%d Unknown Event ", CODE_VIEWxUNKNOWN);
+    }
+
+    strcat(szEventHeader, "from ");
+    if(e->Direction() == D_RECEIVER)
+      strncat(szEventHeader, u->GetAlias(), 50);
+    else
+      strncat(szEventHeader, o->GetAlias(), 50);
+    strcat(szEventHeader, "\n\0");
+
+    // Write out the event header
+    fprintf(fs, szEventHeader);
+
+    // Timestamp
+    char szTimestamp[39];
+    char szTime[25];
+    time_t nMessageTime = e->Time();
+    struct tm *pTM = localtime(&nMessageTime);
+    strftime(szTime, 25, "%H:%M:%S", pTM);
+    sprintf(szTimestamp, "%d Sent At ", CODE_VIEWxTIME);
+    strncat(szTimestamp, szTime, 25);
+    strcat(szTimestamp, "\n\0");
+    fprintf(fs, szTimestamp);
+
+    // Message
+    fprintf(fs, "%d Message Start\n", CODE_VIEWxTEXTxSTART);
+    fprintf(fs, "%s", e->Text());
+    fprintf(fs, "\n");
+    fprintf(fs, "%d Message Complete\n", CODE_VIEWxTEXTxEND);
+
+    it++;
+  }
+
+  gUserManager.DropOwner(m_nPPID);
+  gUserManager.DropUser(u);
+
+  fprintf(fs, "%d End.\n", CODE_HISTORYxEND);
+  return fflush(fs);
+}
Index: src/rms.h
===================================================================
--- src/rms.h	(revision 6179)
+++ src/rms.h	(working copy)
@@ -16,6 +16,7 @@
 class CICQSignal;
 class ICQEvent;
 class CLogService_Plugin;
+class CUserHistory;
 
 using namespace std;
 
@@ -24,6 +25,8 @@
 
 typedef list<class CRMSClient *> ClientList;
 typedef list<unsigned long> TagList;
+typedef list<class CUserEvent *> HistoryList;
+typedef HistoryList::reverse_iterator HistoryListRIter;
 
 class CLicqRMS
 {
@@ -84,6 +87,7 @@
   int Process_REMUSER();
   int Process_SECURE();
   int Process_NOTIFY();
+  int Process_HISTORY();
 
 protected:
   TCPSocket sock;
@@ -107,6 +111,11 @@
   char *m_szEventId;
   unsigned long m_nEventPPID;
 
+  CUserHistory *m_history;
+  HistoryList m_historyList; 
+  char *m_szHistoryLastId;
+  unsigned long m_nHistoryLastPPID;
+
   int StateMachine();
   int ProcessCommand();
   bool ProcessEvent(ICQEvent *);
