1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2000-2014 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "rms.h"
21 #include "pluginversion.h"
22 
23 #include <boost/foreach.hpp>
24 #include <cctype>
25 #include <climits>
26 #include <cstdio>
27 #include <cstring>
28 #include <sstream>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <cerrno>
33 
34 #include <licq/buffer.h>
35 #include <licq/contactlist/group.h>
36 #include <licq/contactlist/owner.h>
37 #include <licq/contactlist/user.h>
38 #include <licq/contactlist/usermanager.h>
39 #include <licq/daemon.h>
40 #include <licq/event.h>
41 #include <licq/icq/icq.h>
42 #include <licq/inifile.h>
43 #include <licq/logging/log.h>
44 #include <licq/logging/logservice.h>
45 #include <licq/logging/logutils.h>
46 #include <licq/mainloop.h>
47 #include <licq/plugin/pluginmanager.h>
48 #include <licq/pluginsignal.h>
49 #include <licq/protocolmanager.h>
50 #include <licq/translator.h>
51 #include <licq/userevents.h>
52 
53 using Licq::UserId;
54 using Licq::gLog;
55 using Licq::gPluginManager;
56 using Licq::gProtocolManager;
57 using Licq::gUserManager;
58 using std::string;
59 
60 CLicqRMS *licqRMS = NULL;
61 
62 // 100 - information
63 const unsigned short CODE_QUIT = 100;
64 const unsigned short CODE_HELP = 101;
65 const unsigned short CODE_COMMANDxSTART = 102;
66 const unsigned short CODE_LOG = 103;
67 // 200 - fulfilled
68 const unsigned short CODE_HELLO = 200;
69 const unsigned short CODE_USERxINFO = 201;
70 const unsigned short CODE_STATUS = 202;
71 const unsigned short CODE_RESULTxSUCCESS = 203;
72 const unsigned short CODE_LISTxUSER = 204;
73 const unsigned short CODE_LISTxGROUP = 205;
74 const unsigned short CODE_LISTxDONE = 206;
75 const unsigned short CODE_LOGxTYPE = 207;
76 const unsigned short CODE_VIEWxMSG = 208;
77 const unsigned short CODE_VIEWxURL = 209;
78 const unsigned short CODE_VIEWxCHAT= 210;
79 const unsigned short CODE_VIEWxFILE = 211;
80 const unsigned short CODE_STATUSxDONE = 212;
81 const unsigned short CODE_VIEWxTIME = 220;
82 const unsigned short CODE_VIEWxFLAGS = 221;
83 const unsigned short CODE_VIEWxTEXTxSTART = 222;
84 const unsigned short CODE_VIEWxTEXTxEND = 223;
85 const unsigned short CODE_ADDUSERxDONE = 224;
86 const unsigned short CODE_REMUSERxDONE = 225;
87 const unsigned short CODE_SECURExOPEN = 226;
88 const unsigned short CODE_SECURExCLOSE = 227;
89 const unsigned short CODE_SECURExSTAT = 228;
90 const unsigned short CODE_NOTIFYxON = 229;
91 const unsigned short CODE_NOTIFYxOFF = 230;
92 const unsigned short CODE_HISTORYxEND = 231;
93 const unsigned short CODE_VIEWxUNKNOWN = 299;
94 // 300 - further action required
95 const unsigned short CODE_ENTERxUIN = 300;
96 const unsigned short CODE_ENTERxPASSWORD = 301;
97 const unsigned short CODE_ENTERxTEXT = 302;
98 const unsigned short CODE_ENTERxLINE = 303;
99 // 400 - client error
100 const unsigned short CODE_INVALID = 400;
101 const unsigned short CODE_INVALIDxCOMMAND = 401;
102 const unsigned short CODE_INVALIDxUSER = 402;
103 const unsigned short CODE_INVALIDxSTATUS = 403;
104 const unsigned short CODE_EVENTxCANCELLED = 404;
105 const unsigned short CODE_VIEWxNONE = 405;
106 // 500 - server error
107 const unsigned short CODE_EVENTxTIMEDOUT = 500;
108 const unsigned short CODE_EVENTxFAILED = 501;
109 const unsigned short CODE_EVENTxERROR = 502;
110 const unsigned short CODE_ADDUSERxERROR = 503;
111 const unsigned short CODE_SECURExNOTCOMPILED = 504;
112 
113 const unsigned short CODE_NOTIFYxSTATUS = 600;
114 const unsigned short CODE_NOTIFYxMESSAGE = 601;
115 
116 const unsigned short STATE_UIN = 1;
117 const unsigned short STATE_PASSWORD = 2;
118 const unsigned short STATE_COMMAND = 3;
119 const unsigned short STATE_ENTERxMESSAGE = 4;
120 const unsigned short STATE_ENTERxURLxDESCRIPTION = 5;
121 const unsigned short STATE_ENTERxURL = 6;
122 const unsigned short STATE_ENTERxAUTOxRESPONSE = 7;
123 
124 #define NEXT_WORD(s) while (*s != '\0' && *s == ' ') s++;
125 
126 struct Command
127 {
128   const char *name;
129   int (CRMSClient::*fcn)();
130   const char *help;
131 };
132 
133 static struct Command commands[] =
134 {
135   { "ADDUSER", &CRMSClient::Process_ADDUSER,
136     "Add user to contact list { <id>[.<protocol>] }." },
137   { "AR", &CRMSClient::Process_AR,
138     "Set your (or a user custom) auto response { [ <id>[.<protocol>] ] }." },
139   { "GROUPS", &CRMSClient::Process_GROUPS,
140     "Show list of groups." },
141   { "HELP", &CRMSClient::Process_HELP,
142     "Print out help on commands." },
143   { "HISTORY", &CRMSClient::Process_HISTORY,
144     "View history of specific user { <id>[.<protocol>] [<length>] [<offset>]}." },
145   { "INFO", &CRMSClient::Process_INFO,
146     "Print out user information.  Argument is the id and protocol, or none for personal." },
147   { "LIST", &CRMSClient::Process_LIST,
148     "List users { [ <group #> ] [ <online|offline|all> ] [ <format> ] }." },
149   { "LOG", &CRMSClient::Process_LOG,
150     "Dump log messages { <log types> }." },
151   { "MESSAGE", &CRMSClient::Process_MESSAGE,
152     "Send a message { <id>[.<protocol>] }." },
153   { "QUIT", &CRMSClient::Process_QUIT,
154     "Close the connection.  With an argument of 1 causes the plugin to unload." },
155   { "REMUSER", &CRMSClient::Process_REMUSER,
156     "Remove user from contact list { <id>[.<protocol>] }." },
157   { "SECURE", &CRMSClient::Process_SECURE,
158     "Open/close/check secure channel { <uin> [ <open|close> ] } ." },
159   { "STATUS", &CRMSClient::Process_STATUS,
160     "Set or show status.  Argument is new status and protocol, or blank to display current. { [ <status>[.<protocol>] ] }." },
161   { "TERM", &CRMSClient::Process_TERM,
162     "Terminate the licq daemon." },
163   { "VIEW", &CRMSClient::Process_VIEW,
164     "View event (next or specific user) { [ <id>[.<protocol>] ] }." },
165   { "URL", &CRMSClient::Process_URL,
166     "Send a url { <id>[.<protocol>] }." },
167   { "NOTIFY", &CRMSClient::Process_NOTIFY,
168     "Notify events" },
169 };
170 
171 static const unsigned short NUM_COMMANDS = sizeof(commands)/sizeof(*commands);
172 
173 /*---------------------------------------------------------------------------
174  * CLicqRMS::Constructor
175  *-------------------------------------------------------------------------*/
CLicqRMS(const std::string & configFile)176 CLicqRMS::CLicqRMS(const std::string& configFile)
177   : m_bEnabled(true),
178     myPort(0),
179     myConfigFile(configFile)
180 {
181   licqRMS = this;
182   server = NULL;
183 }
184 
185 
186 /*---------------------------------------------------------------------------
187  * CLicqRMS::Destructor
188  *-------------------------------------------------------------------------*/
~CLicqRMS()189 CLicqRMS::~CLicqRMS()
190 {
191   delete server;
192   ClientList::iterator iter;
193   for (iter = clients.begin(); iter != clients.end(); iter++)
194     delete *iter;
195 }
196 
197 /*---------------------------------------------------------------------------
198  * CLicqRMS::Shutdown
199  *-------------------------------------------------------------------------*/
Shutdown()200 void CLicqRMS::Shutdown()
201 {
202   gLog.info("Shutting down remote manager server");
203 
204   if (myLogSink)
205     Licq::gLogService.unregisterLogSink(myLogSink);
206 }
207 
init(int argc,char ** argv)208 bool CLicqRMS::init(int argc, char** argv)
209 {
210   //char *LocaleVal = new char;
211   //LocaleVal = setlocale (LC_ALL, "");
212   //bindtextdomain (PACKAGE, LOCALEDIR);
213   //textdomain (PACKAGE);
214 
215   // parse command line for arguments
216   int i = 0;
217   while ( (i = getopt(argc, argv, "dp:")) > 0)
218   {
219     switch (i)
220     {
221     case 'd': // enable
222       m_bEnabled = false;
223       break;
224     case 'p':
225       myPort = atol(optarg);
226       break;
227     }
228   }
229   return true;
230 }
231 
232 /*---------------------------------------------------------------------------
233  * CLicqRMS::Run
234  *-------------------------------------------------------------------------*/
run()235 int CLicqRMS::run()
236 {
237   setSignalMask(Licq::PluginSignal::SignalAll);
238 
239   Licq::IniFile conf(myConfigFile);
240   if (conf.loadFile())
241   {
242     conf.setSection("RMS");
243 
244     // Ignore port in config if given on command line
245     if (myPort == 0)
246       conf.get("Port", myPort, 0);
247 
248     string protocolStr;
249     conf.get("AuthProtocol", protocolStr, "ICQ");
250     conf.get("AuthUser", myAuthUser);
251     if (protocolStr == "Config")
252     {
253       // Get password from config file
254       conf.get("AuthPassword", myAuthPassword);
255 
256       // Make sure we got something instead of opening a security problem
257       if (myAuthUser.empty() || myAuthPassword.empty())
258       {
259         gLog.warning("Missing value for AuthUser or AuthPassword in configuration, "
260             "login will not be possible.");
261       }
262     }
263     else
264     {
265       // Parse protocol id
266       unsigned long protocolId = Licq::protocolId_fromString(protocolStr);
267       if (protocolId == 0 || myAuthUser.empty())
268       {
269         // Invalid
270         gLog.warning("Invalid value for AuthProtocol or AuthUser in configuration, "
271             "login will not be possible");
272       }
273       else
274         myAuthOwnerId = Licq::UserId(protocolId, myAuthUser);
275     }
276   }
277 
278   server = new Licq::TCPSocket();
279 
280   if (Licq::gDaemon.tcpPortsLow() != 0 && myPort == 0)
281   {
282     if (!Licq::gDaemon.StartTCPServer(server))
283     {
284       Shutdown();
285       return 1;
286     }
287   }
288   else
289   {
290     if (!server->StartServer(myPort))
291     {
292       gLog.error("Could not start server on port %u, "
293           "maybe this port is already in use?", myPort);
294       Shutdown();
295       return 1;
296     };
297   }
298 
299   gLog.info("RMS server started on port %d", server->getLocalPort());
300   myMainLoop.addSocket(server, this);
301   myMainLoop.addRawFile(getReadPipe(), this);
302 
303   myMainLoop.run();
304 
305   Shutdown();
306   return 0;
307 }
308 
isEnabled() const309 bool CLicqRMS::isEnabled() const
310 {
311   return m_bEnabled;
312 }
313 
rawFileEvent(int,int fd,int)314 void CLicqRMS::rawFileEvent(int /*id*/, int fd, int /* revents */)
315 {
316   if (fd == getReadPipe())
317     ProcessPipe();
318   else if (myLogSink && fd == myLogSink->getReadPipe())
319     ProcessLog();
320 }
321 
setupLogSink()322 void CLicqRMS::setupLogSink()
323 {
324   if (!myLogSink)
325   {
326     myLogSink.reset(new Licq::PluginLogSink);
327     Licq::gLogService.registerLogSink(myLogSink);
328     myMainLoop.addRawFile(myLogSink->getReadPipe(), this);
329   }
330 
331   unsigned int mask = 0;
332   BOOST_FOREACH(CRMSClient* client, clients)
333     mask |= client->myLogLevelsBitmask;
334   myLogSink->setLogLevelsFromBitmask(mask);
335 }
336 
deleteClient(CRMSClient * client)337 void CLicqRMS::deleteClient(CRMSClient* client)
338 {
339   delete client;
340 
341   for (ClientList::iterator iter = clients.begin(); iter != clients.end(); ++iter)
342     if (*iter == client)
343     {
344       clients.erase(iter);
345       break;
346     }
347 
348   if (myLogSink)
349     setupLogSink();
350 }
351 
352 /*---------------------------------------------------------------------------
353  * CLicqRMS::ProcessPipe
354  *-------------------------------------------------------------------------*/
ProcessPipe()355 void CLicqRMS::ProcessPipe()
356 {
357   char buf;
358   read(getReadPipe(), &buf, 1);
359   switch (buf)
360   {
361     case PipeSignal:
362       if (m_bEnabled)
363         ProcessSignal(popSignal().get());
364       else
365         popSignal();
366       break;
367 
368     case PipeEvent:
369       // An event is pending (should never happen)
370       if (m_bEnabled)
371         ProcessEvent(popEvent().get());
372       else
373         popEvent();
374       break;
375 
376     case PipeShutdown:
377       gLog.info("Exiting");
378       myMainLoop.quit();
379       break;
380 
381     case PipeDisable:
382       gLog.info("Disabling");
383       m_bEnabled = false;
384       break;
385 
386     case PipeEnable:
387       gLog.info("Enabling");
388       m_bEnabled = true;
389       break;
390 
391     default:
392       gLog.warning("Unknown notification type from daemon: %c", buf);
393   }
394 }
395 
396 
397 /*---------------------------------------------------------------------------
398  * CLicqRMS::ProcessLog
399  *-------------------------------------------------------------------------*/
ProcessLog()400 void CLicqRMS::ProcessLog()
401 {
402   using namespace Licq::LogUtils;
403 
404   Licq::LogSink::Message::Ptr message = myLogSink->popMessage();
405   const char* level = levelToShortString(message->level);
406   const std::string time = timeToString(message->time);
407 
408   BOOST_FOREACH(CRMSClient* client, clients)
409   {
410     if (levelInBitmask(message->level, client->myLogLevelsBitmask))
411     {
412       if (packetInBitmask(client->myLogLevelsBitmask)
413           && !message->packet.empty())
414       {
415         ::fprintf(client->fs, "%d %s [%s] %s: %s\n%s\n",
416                   CODE_LOG, time.c_str(), level,
417                   message->sender.c_str(), message->text.c_str(),
418                   packetToString(message).c_str());
419       }
420       else
421       {
422         ::fprintf(client->fs, "%d %s [%s] %s: %s\n",
423                   CODE_LOG, time.c_str(), level,
424                   message->sender.c_str(), message->text.c_str());
425       }
426       ::fflush(client->fs);
427     }
428   }
429 }
430 
431 
432 /*---------------------------------------------------------------------------
433  * CLicqRMS::ProcessSignal
434  *-------------------------------------------------------------------------*/
ProcessSignal(const Licq::PluginSignal * s)435 void CLicqRMS::ProcessSignal(const Licq::PluginSignal* s)
436 {
437   switch (s->signal())
438   {
439     case Licq::PluginSignal::SignalUser:
440       if (s->subSignal() == Licq::PluginSignal::UserStatus)
441       {
442         Licq::UserReadGuard u(s->userId());
443         if (u.isLocked())
444         {
445         ClientList::iterator iter;
446         for (iter = clients.begin(); iter != clients.end(); iter++)
447         {
448           if ((*iter)->m_bNotify)
449           {
450             fprintf((*iter)->fs, "%d %s\n", CODE_NOTIFYxSTATUS, u->usprintf("%u %P %-20a %3m %s").c_str());
451             fflush((*iter)->fs);
452           }
453         }
454         }
455         break;
456       }
457       else if (s->subSignal() == Licq::PluginSignal::UserEvents)
458       {
459         Licq::UserReadGuard u(s->userId());
460         if (u.isLocked())
461         {
462         ClientList::iterator iter;
463         for (iter = clients.begin(); iter != clients.end(); iter++)
464         {
465           if ((*iter)->m_bNotify)
466           {
467             fprintf((*iter)->fs, "%d %s\n", CODE_NOTIFYxMESSAGE, u->usprintf("%u %P %3m").c_str());
468             fflush((*iter)->fs);
469           }
470         }
471       }
472     }
473   default:
474     break;
475 
476   }
477 }
478 
479 /*---------------------------------------------------------------------------
480  * CLicqRMS::ProcessEvent
481  *-------------------------------------------------------------------------*/
ProcessEvent(const Licq::Event * e)482 void CLicqRMS::ProcessEvent(const Licq::Event* e)
483 {
484   ClientList ::iterator iter;
485   for (iter = clients.begin(); iter != clients.end(); iter++)
486   {
487     if ((*iter)->ProcessEvent(e))
488       break;
489   }
490 }
491 
socketEvent(int,Licq::INetSocket * inetSocket,int)492 void CLicqRMS::socketEvent(int /*id*/, Licq::INetSocket* inetSocket, int /* revents */)
493 {
494   if (inetSocket == server)
495   {
496     server->Lock();
497     clients.push_back(new CRMSClient(server));
498     server->Unlock();
499   }
500 }
501 
502 /*---------------------------------------------------------------------------
503  * CRMSClient::constructor
504  *-------------------------------------------------------------------------*/
CRMSClient(Licq::TCPSocket * sin)505 CRMSClient::CRMSClient(Licq::TCPSocket* sin)
506   : myLogLevelsBitmask(0)
507 {
508   sin->RecvConnection(sock);
509   licqRMS->myMainLoop.addSocket(&sock, this);
510 
511   gLog.info("Client connected from %s", sock.getRemoteIpString().c_str());
512   fs = fdopen(sock.Descriptor(), "r+");
513   fprintf(fs, "Licq Remote Management Server v" PLUGIN_VERSION_STRING "\n"
514       "%d Enter your UIN:\n", CODE_ENTERxUIN);
515   fflush(fs);
516 
517   m_szCheckId = 0;
518   m_nState = STATE_UIN;
519   data_line_pos = 0;
520   m_bNotify = false;
521 }
522 
523 
524 /*---------------------------------------------------------------------------
525  * CRMSClient::destructor
526  *-------------------------------------------------------------------------*/
~CRMSClient()527 CRMSClient::~CRMSClient()
528 {
529   licqRMS->myMainLoop.removeSocket(&sock);
530   sock.CloseConnection();
531 
532   if (m_szCheckId)
533     free(m_szCheckId);
534 }
535 
socketEvent(int,Licq::INetSocket *,int)536 void CRMSClient::socketEvent(int /*id*/, Licq::INetSocket* /*inetSocket*/, int /*revents*/)
537 {
538   if (Activity() == -1)
539   {
540     licqRMS->deleteClient(this);
541   }
542 }
543 
544 /*---------------------------------------------------------------------------
545  * CRMSClient::ParseUser
546  *-------------------------------------------------------------------------*/
ParseUser(const string & strData)547 void CRMSClient::ParseUser(const string& strData)
548 {
549   myUserId = UserId();
550   unsigned long protocolId = 0;
551   string accountId;
552 
553   size_t pos = strData.rfind('.');
554   if (pos != string::npos)
555   {
556     // Protocol specified
557     protocolId = Licq::protocolId_fromString(strData.substr(pos+1));
558     accountId = strData.substr(0, pos-1);
559   }
560   else
561     accountId = strData;
562 
563   // Try and find an existing user that matches
564   Licq::UserListGuard userList(protocolId);
565   BOOST_FOREACH(const Licq::User* user, **userList)
566   {
567     if (user->accountId() == accountId)
568     {
569       myUserId = user->id();
570       return;
571     }
572   }
573 
574   if (protocolId != 0)
575   {
576     // Use first owner for protocol
577     Licq::OwnerListGuard ownerList(protocolId);
578     if (!ownerList->empty())
579     {
580       myUserId = Licq::UserId((*ownerList->begin())->id(), accountId);
581       return;
582     }
583   }
584 
585   // Failed
586   myUserId = Licq::UserId();
587 }
588 
589 /*---------------------------------------------------------------------------
590  * CRMSClient::ProcessEvent
591  *-------------------------------------------------------------------------*/
ProcessEvent(const Licq::Event * e)592 bool CRMSClient::ProcessEvent(const Licq::Event* e)
593 {
594   TagList::iterator iter;
595   for (iter = tags.begin(); iter != tags.end(); iter++)
596   {
597     if ( e->Equals(*iter) ) break;
598   }
599   if (iter == tags.end()) return false;
600 
601   unsigned long tag = *iter;
602   tags.erase(iter);
603 
604   unsigned short nCode = 0;
605   const char *szr = NULL;
606   switch(e->Result())
607   {
608     case Licq::Event::ResultAcked:
609     case Licq::Event::ResultSuccess:
610       nCode = CODE_RESULTxSUCCESS;
611       szr = "done";
612       break;
613     case Licq::Event::ResultTimedout:
614       nCode = CODE_EVENTxTIMEDOUT;
615       szr = "timed out";
616       break;
617     case Licq::Event::ResultFailed:
618     case Licq::Event::ResultUnsupported:
619       nCode = CODE_EVENTxFAILED;
620       szr = "failed";
621       break;
622     case Licq::Event::ResultError:
623       nCode = CODE_EVENTxERROR;
624       szr = "error";
625       break;
626     case Licq::Event::ResultCancelled:
627       nCode = CODE_EVENTxCANCELLED;
628       szr = "cancelled";
629       break;
630   }
631   fprintf(fs, "%d [%ld] Event %s.\n", nCode, tag, szr);
632   fflush(fs);
633 
634   return true;
635 }
636 
637 
638 /*---------------------------------------------------------------------------
639  * CRMSClient::Activity
640  *-------------------------------------------------------------------------*/
Activity()641 int CRMSClient::Activity()
642 {
643   Licq::Buffer buf;
644   if (!sock.receive(buf))
645   {
646     gLog.info("Client %s disconnected", sock.getRemoteIpString().c_str());
647     return -1;
648   }
649 
650   char* in = buf.getDataStart();
651   char* last = buf.getDataPosWrite();
652 
653   do
654   {
655     while (in != last && *in != '\n')
656     {
657       if (!iscntrl(*in) && data_line_pos < MAX_LINE_LENGTH)
658         data_line[data_line_pos++] = *in;
659       in++;
660     }
661 
662     if (in != last && *in == '\n')
663     {
664       data_line[data_line_pos] = '\0';
665       in++;
666       if (StateMachine() == -1) return -1;
667 
668       data_line_pos = 0;
669     }
670 
671   } while (in != last);
672 
673   data_line[data_line_pos] = '\0';
674 
675   return 0;
676 }
677 
678 
679 /*---------------------------------------------------------------------------
680  * CRMSClient::StateMachine
681  *-------------------------------------------------------------------------*/
StateMachine()682 int CRMSClient::StateMachine()
683 {
684   switch(m_nState)
685   {
686     case STATE_UIN:
687     {
688       myLoginUser = data_line;
689       fprintf(fs, "%d Enter your password:\n", CODE_ENTERxPASSWORD);
690       fflush(fs);
691       m_nState = STATE_PASSWORD;
692       break;
693     }
694     case STATE_PASSWORD:
695     {
696       bool ok = false;
697       string name;
698       if (licqRMS->myAuthOwnerId.isValid())
699       {
700         // Check against protocol owner
701         Licq::OwnerReadGuard o(licqRMS->myAuthOwnerId);
702         if (!o.isLocked())
703           return -1;
704         ok = (myLoginUser == o->accountId() && data_line == o->password());
705         name = o->getAlias();
706       }
707       else if (!licqRMS->myAuthUser.empty() && !licqRMS->myAuthPassword.empty())
708       {
709         // User and password specified in RMS config
710         ok = (myLoginUser == licqRMS->myAuthUser &&
711             data_line == licqRMS->myAuthPassword);
712         name = myLoginUser;
713       }
714 
715       if (!ok)
716       {
717         gLog.info("Client failed validation from %s",
718             sock.getRemoteIpString().c_str());
719         fprintf(fs, "%d Invalid ID/Password.\n", CODE_INVALID);
720         fflush(fs);
721         return -1;
722       }
723       gLog.info("Client validated from %s",
724           sock.getRemoteIpString().c_str());
725       fprintf(fs, "%d Hello %s.  Type HELP for assistance.\n", CODE_HELLO,
726          name.c_str());
727       fflush(fs);
728       m_nState = STATE_COMMAND;
729       break;
730     }
731     case STATE_COMMAND:
732     {
733       if (ProcessCommand() == -1) return -1;
734       break;
735     }
736     case STATE_ENTERxMESSAGE:
737     {
738       if (AddLineToText())
739          return Process_MESSAGE_text();
740       break;
741     }
742     case STATE_ENTERxURLxDESCRIPTION:
743     {
744       if (AddLineToText())
745          return Process_URL_text();
746       break;
747     }
748     case STATE_ENTERxURL:
749     {
750       return Process_URL_url();
751     }
752     case STATE_ENTERxAUTOxRESPONSE:
753     {
754       if (AddLineToText())
755          return Process_AR_text();
756       break;
757     }
758   }
759   return 0;
760 }
761 
762 
763 /*---------------------------------------------------------------------------
764  * CRMSClient::AddLineToText
765  *-------------------------------------------------------------------------*/
AddLineToText()766 bool CRMSClient::AddLineToText()
767 {
768   if (data_line[0] == '.' && data_line[1] == '\0') return true;
769 
770   myText += data_line;
771   myText += "\n";
772 
773   return false;
774 }
775 
776 
777 /*---------------------------------------------------------------------------
778  * CRMSClient::ProcessCommand
779  *-------------------------------------------------------------------------*/
ProcessCommand()780 int CRMSClient::ProcessCommand()
781 {
782   data_arg = data_line;
783   while (*data_arg != '\0' && *data_arg != ' ') data_arg++;
784   if (*data_arg == ' ')
785   {
786     *data_arg++ = '\0';
787     NEXT_WORD(data_arg);
788   }
789 
790   for (unsigned short i = 0; i < NUM_COMMANDS; i++)
791   {
792     if (strcasecmp(commands[i].name, data_line) == 0)
793       return  (this->*(commands[i].fcn))();
794   }
795 
796   fprintf(fs, "%d Invalid command.  Type HELP for assistance.\n",
797      CODE_INVALIDxCOMMAND);
798   return fflush(fs);
799 }
800 
801 
802 /*---------------------------------------------------------------------------
803  * CRMSClient::Process_INFO
804  *-------------------------------------------------------------------------*/
Process_INFO()805 int CRMSClient::Process_INFO()
806 {
807   ParseUser(data_arg);
808 
809   //XXX Handle the case when we have the owner
810 
811   // Print the user info
812   Licq::UserReadGuard u(myUserId);
813   if (!u.isLocked())
814   {
815     fprintf(fs, "%d No such user.\n", CODE_INVALIDxUSER);
816     return fflush(fs);
817   }
818 
819   fprintf(fs, "%d %s Alias: %s\n", CODE_USERxINFO, u->accountId().c_str(),
820       u->getAlias().c_str());
821   fprintf(fs, "%d %s Status: %s\n", CODE_USERxINFO, u->accountId().c_str(),
822       u->statusString().c_str());
823   fprintf(fs, "%d %s First Name: %s\n", CODE_USERxINFO, u->accountId().c_str(),
824     u->getFirstName().c_str());
825   fprintf(fs, "%d %s Last Name: %s\n", CODE_USERxINFO, u->accountId().c_str(),
826     u->getLastName().c_str());
827   fprintf(fs, "%d %s Email 1: %s\n", CODE_USERxINFO, u->accountId().c_str(),
828     u->getUserInfoString("Email1").c_str());
829   fprintf(fs, "%d %s Email 2: %s\n", CODE_USERxINFO, u->accountId().c_str(),
830     u->getUserInfoString("Email2").c_str());
831 
832   return fflush(fs);
833 }
834 
835 
836 /*---------------------------------------------------------------------------
837  * CRMSClient::Process_STATUS
838  *
839  * Command:
840  *   STATUS [ status | protocol ]
841  *
842  * Response:
843  *
844  *-------------------------------------------------------------------------*/
Process_STATUS()845 int CRMSClient::Process_STATUS()
846 {
847   // Show status
848   if (data_arg[0] == '\0')
849   {
850     Licq::OwnerListGuard ownerList;
851     BOOST_FOREACH(const Licq::Owner* owner, **ownerList)
852     {
853       Licq::ProtocolPlugin::Ptr protocol = Licq::gPluginManager.getProtocolPlugin(owner->protocolId());
854       Licq::OwnerReadGuard o(owner);
855       fprintf(fs, "%d %s %s %s\n", CODE_STATUS, o->accountId().c_str(),
856           protocol->name().c_str(), o->statusString().c_str());
857     }
858     fprintf(fs, "%d\n", CODE_STATUSxDONE);
859     return fflush(fs);
860   }
861 
862   // Set status
863   string strData(data_arg);
864   string::size_type nPos = strData.find_last_of(".");
865   string status;
866 
867   // Get a list of owners first since we can't call changeStatus with list locked
868   std::list<Licq::UserId> owners;
869   if (nPos == string::npos)
870   {
871     status = data_arg;
872 
873     Licq::OwnerListGuard ownerList;
874     BOOST_FOREACH(const Licq::Owner* o, **ownerList)
875       owners.push_back(o->id());
876   }
877   else
878   {
879     status = string(strData, 0, nPos);
880     string param(strData, nPos+1);
881     unsigned long protocolId = Licq::protocolId_fromString(param);
882 
883     Licq::OwnerListGuard ownerList;
884     BOOST_FOREACH(const Licq::Owner* o, **ownerList)
885       if (o->protocolId() == protocolId || o->accountId() == param)
886         owners.push_back(o->id());
887   }
888 
889   BOOST_FOREACH(const Licq::UserId& ownerId, owners)
890     changeStatus(ownerId, status);
891 
892   fprintf(fs, "%d Done setting status\n", CODE_STATUSxDONE);
893   return fflush(fs);
894 }
895 
changeStatus(const Licq::UserId & ownerId,const string & strStatus)896 int CRMSClient::changeStatus(const Licq::UserId& ownerId, const string& strStatus)
897 {
898   unsigned status;
899   if (!Licq::User::stringToStatus(strStatus, status))
900   {
901     fprintf(fs, "%d Invalid status.\n", CODE_INVALIDxSTATUS);
902     return -1;
903   }
904   if (status == Licq::User::OfflineStatus)
905   {
906     fprintf(fs, "%d [0] Logging off %s.\n", CODE_COMMANDxSTART, strStatus.c_str());
907     fflush(fs);
908     gProtocolManager.setStatus(ownerId, Licq::User::OfflineStatus);
909     fprintf(fs, "%d [0] Event done.\n", CODE_STATUSxDONE);
910     return 0;
911   }
912   else
913   {
914     bool b;
915     {
916       Licq::OwnerReadGuard o(ownerId);
917       if (!o.isLocked())
918       {
919         fprintf(fs, "%d Invalid protocol.\n", CODE_INVALIDxUSER);
920         return -1;
921       }
922       b = !o->isOnline();
923     }
924     unsigned long tag = gProtocolManager.setStatus(ownerId, status);
925     if (b)
926       fprintf(fs, "%d [%ld] Logging on to %s.\n", CODE_COMMANDxSTART, tag, strStatus.c_str());
927     else
928       fprintf(fs, "%d [%ld] Setting status for %s.\n", CODE_COMMANDxSTART, tag, strStatus.c_str());
929     tags.push_back(tag);
930   }
931   return 0;
932 }
933 
934 /*---------------------------------------------------------------------------
935  * CRMSClient::Process_QUIT
936  *-------------------------------------------------------------------------*/
Process_QUIT()937 int CRMSClient::Process_QUIT()
938 {
939   fprintf(fs, "%d Sayonara.\n", CODE_QUIT);
940   fflush(fs);
941   if (strtoul(data_arg, (char**)NULL, 10) > 0)
942     licqRMS->myMainLoop.quit();
943   return -1;
944 }
945 
946 
947 /*---------------------------------------------------------------------------
948  * CRMSClient::Process_TERM
949  *-------------------------------------------------------------------------*/
Process_TERM()950 int CRMSClient::Process_TERM()
951 {
952   Licq::gDaemon.Shutdown();
953   return -1;
954 }
955 
956 
957 /*---------------------------------------------------------------------------
958  * CRMSClient::Process_HELP
959  *-------------------------------------------------------------------------*/
Process_HELP()960 int CRMSClient::Process_HELP()
961 {
962   for (unsigned short i = 0; i < NUM_COMMANDS; i++)
963   {
964     fprintf(fs, "%d %s: %s\n", CODE_HELP, commands[i].name, commands[i].help);
965   }
966   return fflush(fs);
967 }
968 
969 
970 /*---------------------------------------------------------------------------
971  * CRMSClient::Process_GROUPS
972  *
973  * Command:
974  *  GROUPS
975  *    Prints out the list of groups as follows.
976  *
977  * Response:
978  *  CODE_LISTxGROUP 000 All Users
979  *  CODE_LISTxGROUP 001 First Group
980  *  ...
981  *  CODE_LISTxDONE
982  *
983  *-------------------------------------------------------------------------*/
Process_GROUPS()984 int CRMSClient::Process_GROUPS()
985 {
986   fprintf(fs, "%d 000 All Users\n", CODE_LISTxGROUP);
987   int i = 1;
988   Licq::GroupListGuard groupList;
989   BOOST_FOREACH(const Licq::Group* group, **groupList)
990   {
991     Licq::GroupReadGuard pGroup(group);
992     fprintf(fs, "%d %03d %s\n", CODE_LISTxGROUP, i, pGroup->name().c_str());
993     ++i;
994   }
995   fprintf(fs, "%d\n", CODE_LISTxDONE);
996 
997   return fflush(fs);
998 }
999 
Process_HISTORY()1000 int CRMSClient::Process_HISTORY()
1001 {
1002   char* s = strtok(data_arg, " ");
1003   if (s == NULL)
1004   {
1005     fprintf(fs, "%d Invalid User.\n", CODE_INVALIDxUSER);
1006     return fflush(fs);
1007   }
1008   ParseUser(s);
1009 
1010   int length = 10;
1011   s = strtok(NULL, " ");
1012   if (s != NULL)
1013     length = atoi(s);
1014 
1015   int offset = 0;
1016   s = strtok(NULL, " ");
1017   if (s != NULL)
1018     offset = atoi(s);
1019 
1020   Licq::HistoryList history;
1021   string userAlias;
1022   string ownerAlias = "me";
1023 
1024   {
1025     Licq::UserReadGuard u(myUserId);
1026     if (!u.isLocked())
1027     {
1028       fprintf(fs, "%d Invalid User (%s).\n", CODE_INVALIDxUSER, myUserId.toString().c_str());
1029       return fflush(fs);
1030     }
1031     if (!u->GetHistory(history))
1032     {
1033       fprintf(fs, "%d Cannot load history file.\n", CODE_EVENTxERROR);
1034       return fflush(fs);
1035     }
1036 
1037     if (u->isUser())
1038     {
1039       userAlias = u->getAlias();
1040       Licq::OwnerReadGuard o(myUserId.ownerId());
1041       if (o.isLocked())
1042         ownerAlias = o->getAlias();
1043     }
1044     else
1045     {
1046       userAlias = "system";
1047       ownerAlias = u->getAlias();
1048     }
1049   }
1050 
1051   int counter = 0;
1052   Licq::HistoryList::reverse_iterator it;
1053   for (it = history.rbegin(); it != history.rend(); ++it)
1054   {
1055     ++counter;
1056     if (counter < offset || counter > offset + length)
1057       continue;
1058 
1059     printUserEvent(*it, ((*it)->isReceiver() ? userAlias : ownerAlias));
1060   }
1061   fprintf(fs, "%d End.\n", CODE_HISTORYxEND);
1062   return fflush(fs);
1063 }
1064 
1065 
1066 /*---------------------------------------------------------------------------
1067  * CRMSClient::Process_LIST
1068  *
1069  * Command:
1070  *   LIST [ group ] [ online|offline|all ] [ format ]
1071  *     All options are optional and can be left out arbitrarily, ie
1072  *     "LIST all" is a valid call and will print all online and offline users.
1073  *     <format> is a printf style string using the user % symbols as
1074  *     documented in HINTS.  The default is "%u %P %-20a %3m %s"
1075  *     and prints out users as follows.
1076  *
1077  * Response:
1078  *   CODE_LISTxUSER   5550000 Licq            AnAlias   2 Online
1079  *     The default line contains the uin, protocol, alias, number of new
1080  *     messages and status all column and white space deliminated.  Note that
1081  *     the alias may contain white space.
1082  *   CODE_LISTxUSER ...
1083  *   ...
1084  *   CODE_LISTxDONE
1085  *
1086  *-------------------------------------------------------------------------*/
Process_LIST()1087 int CRMSClient::Process_LIST()
1088 {
1089   unsigned short nGroup = 0;
1090   if (isdigit(*data_arg))
1091   {
1092     nGroup = strtoul(data_arg, (char**)NULL, 10);
1093     while (*data_arg != '\0' && *data_arg != ' ') data_arg++;
1094     NEXT_WORD(data_arg);
1095   }
1096 
1097   unsigned short n = 3;
1098   if (strncasecmp(data_arg, "online", 6) == 0)
1099   {
1100     n = 1;
1101     data_arg += 6;
1102   }
1103   else if (strncasecmp(data_arg, "offline", 7) == 0)
1104   {
1105     n = 2;
1106     data_arg += 7;
1107   }
1108   else if (strncasecmp(data_arg, "all", 3) == 0)
1109   {
1110     n = 3;
1111     data_arg += 3;
1112   }
1113   NEXT_WORD(data_arg);
1114 
1115   string format;
1116   if (*data_arg == '\0')
1117     format = "%u %P %-20a %3m %s";
1118   else
1119     format = data_arg;
1120 
1121   Licq::UserListGuard userList;
1122   BOOST_FOREACH(const Licq::User* user, **userList)
1123   {
1124     Licq::UserReadGuard pUser(user);
1125     if (pUser->isInGroup(nGroup) &&
1126         ((!pUser->isOnline() && n&2) || (pUser->isOnline() && n&1)))
1127     {
1128       fprintf(fs, "%d %s\n", CODE_LISTxUSER, pUser->usprintf(format).c_str());
1129     }
1130   }
1131   fprintf(fs, "%d\n", CODE_LISTxDONE);
1132 
1133   return fflush(fs);
1134 }
1135 
1136 
1137 
1138 /*---------------------------------------------------------------------------
1139  * CRMSClient::Process_MESSAGE
1140  *
1141  * Command:
1142  *     MESSAGE <id>[.<protocol>]
1143  *
1144  * Response:
1145  *   CODE_ENTERxTEXT | CODE_INVALIDxUSER
1146  *     At which point the message should be entered line by line and
1147  *     terminated by entering a "." on a line by itself.  Invalid user
1148  *     means the uin was invalid (< 10000) and the message was aborted.
1149  *   CODE_COMMANDxSTART
1150  *     < ...time... >
1151  *   CODE_RESULTxSUCCESS | CODE_EVENTxTIMEDOUT | CODE_EVENTxERROR
1152  *-------------------------------------------------------------------------*/
Process_MESSAGE()1153 int CRMSClient::Process_MESSAGE()
1154 {
1155   fprintf(fs, "%d Enter message, terminate with a . on a line by itself:\n",
1156      CODE_ENTERxTEXT);
1157 
1158   ParseUser(data_arg);
1159 
1160   myText.clear();
1161 
1162   m_nState = STATE_ENTERxMESSAGE;
1163   return fflush(fs);
1164 }
1165 
Process_MESSAGE_text()1166 int CRMSClient::Process_MESSAGE_text()
1167 {
1168   //XXX Give a tag...
1169   myText.erase(myText.size() - 1);
1170   unsigned long tag = gProtocolManager.sendMessage(myUserId,
1171       Licq::gTranslator.toUtf8(myText));
1172 
1173   fprintf(fs, "%d [%ld] Sending message to %s.\n", CODE_COMMANDxSTART,
1174       tag, myUserId.toString().c_str());
1175 
1176   tags.push_back(tag);
1177   m_nState = STATE_COMMAND;
1178 
1179   return fflush(fs);
1180 }
1181 
1182 
1183 
1184 /*---------------------------------------------------------------------------
1185  * CRMSClient::Process_URL
1186  *
1187  * Command:
1188  *   URL <id>[.<protocol>]
1189  *
1190  * Response:
1191  *   CODE_ENTERxLINE | CODE_INVALIDxUSER
1192  *     At which point the url should be entered on a line by itself.
1193  *     Invalid user means the uin was invalid (< 10000) and the url
1194  *     was aborted.
1195  *   CODE_ENTERxTEXT
1196  *     Now the description should be entered and terminated by a "." on
1197  *     a line by itself.
1198  *   CODE_COMMANDxSTART
1199  *     < ...time... >
1200  *   CODE_RESULTxSUCCESS | CODE_EVENTxTIMEDOUT | CODE_EVENTxERROR
1201  *-------------------------------------------------------------------------*/
Process_URL()1202 int CRMSClient::Process_URL()
1203 {
1204   ParseUser(data_arg);
1205 
1206   myText.clear();
1207 
1208   m_nState = STATE_ENTERxURL;
1209   return fflush(fs);
1210 }
1211 
1212 
Process_URL_url()1213 int CRMSClient::Process_URL_url()
1214 {
1215   myLine = data_line;
1216 
1217   fprintf(fs, "%d Enter description, terminate with a . on a line by itself:\n",
1218      CODE_ENTERxTEXT);
1219 
1220   myText.clear();
1221 
1222   m_nState = STATE_ENTERxURLxDESCRIPTION;
1223   return fflush(fs);
1224 }
1225 
1226 
Process_URL_text()1227 int CRMSClient::Process_URL_text()
1228 {
1229   unsigned long tag = gProtocolManager.sendUrl(myUserId, myLine,
1230       Licq::gTranslator.toUtf8(myText));
1231 
1232   fprintf(fs, "%d [%ld] Sending URL to %s.\n", CODE_COMMANDxSTART,
1233       tag, myUserId.toString().c_str());
1234 
1235   tags.push_back(tag);
1236   m_nState = STATE_COMMAND;
1237 
1238   return fflush(fs);
1239 }
1240 
1241 
1242 /*---------------------------------------------------------------------------
1243  * CRMSClient::Process_AR
1244  *
1245  * Command:
1246  *     AR [ <id>[.<protocol>] ]
1247  *
1248  * Response:
1249  *   CODE_ENTERxTEXT | CODE_INVALIDxUIN
1250  *     At which point the auto response should be entered line by line and
1251  *     terminated by entering a "." on a line by itself.
1252  *   CODE_RESULTxSUCCESS
1253  *-------------------------------------------------------------------------*/
Process_AR()1254 int CRMSClient::Process_AR()
1255 {
1256   if (data_arg[0] == '\0')
1257   {
1258     // Clear user id to set general auto response
1259     myUserId = Licq::UserId();
1260   }
1261   else
1262   {
1263     // Parameter is user id to set custom autoresponse for
1264     ParseUser(data_arg);
1265 
1266     if (!myUserId.isValid())
1267     {
1268       fprintf(fs, "%d Invalid User.\n", CODE_INVALIDxUSER);
1269       return fflush(fs);
1270     }
1271   }
1272 
1273   fprintf(fs, "%d Enter %sauto response, terminate with a . on a line by itself:\n",
1274      CODE_ENTERxTEXT, myUserId.isValid() ? "custom " : "");
1275 
1276   myText.clear();
1277 
1278   m_nState = STATE_ENTERxAUTOxRESPONSE;
1279   return fflush(fs);
1280 }
1281 
Process_AR_text()1282 int CRMSClient::Process_AR_text()
1283 {
1284   string textUtf8 = Licq::gTranslator.toUtf8(myText);
1285 
1286   if (!myUserId.isValid())
1287   {
1288     Licq::OwnerListGuard ownerList;
1289     BOOST_FOREACH(Licq::Owner* owner, **ownerList)
1290     {
1291       Licq::OwnerWriteGuard o(owner);
1292       o->setAutoResponse(textUtf8);
1293       o->save(Licq::Owner::SaveOwnerInfo);
1294     }
1295   }
1296   else
1297   {
1298     Licq::UserWriteGuard u(myUserId);
1299     if (u.isLocked())
1300       u->setCustomAutoResponse(textUtf8);
1301   }
1302 
1303   fprintf(fs, "%d Auto response saved.\n", CODE_RESULTxSUCCESS);
1304   m_nState = STATE_COMMAND;
1305   return fflush(fs);
1306 }
1307 
1308 
1309 /*---------------------------------------------------------------------------
1310  * CRMSClient::Process_LOG
1311  *
1312  * Command:
1313  *   LOG <log types>
1314  *
1315  * Response:
1316  *   CODE_LOG 12:04:34.042 [TCP] Message from ...
1317  *   ...
1318  *-------------------------------------------------------------------------*/
Process_LOG()1319 int CRMSClient::Process_LOG()
1320 {
1321   unsigned short lt = strtoul(data_arg, (char**)NULL, 10);
1322   myLogLevelsBitmask = Licq::LogUtils::convertOldBitmaskToNew(lt);
1323 
1324   licqRMS->setupLogSink();
1325 
1326   fprintf(fs, "%d Log type set to %d.\n", CODE_LOGxTYPE, lt);
1327 
1328   return fflush(fs);
1329 }
1330 
1331 /*---------------------------------------------------------------------------
1332  * CRMSClient::Process_NOTIFY
1333  *
1334  * Command:
1335  *   NOTIFY
1336  *
1337  * Response:
1338  *   CODE_NOTIFYxON|CODE_NOTIFYxOFF
1339  *   ...
1340  *-------------------------------------------------------------------------*/
Process_NOTIFY()1341 int CRMSClient::Process_NOTIFY()
1342 {
1343   m_bNotify = !m_bNotify;
1344 
1345   if (m_bNotify)
1346     fprintf(fs, "%d Notify set ON.\n", CODE_NOTIFYxON);
1347   else
1348     fprintf(fs, "%d Notify set OFF.\n", CODE_NOTIFYxOFF);
1349 
1350   return fflush(fs);
1351 }
1352 
1353 /*---------------------------------------------------------------------------
1354  * CRMSClient::Process_VIEW
1355  *
1356  * Command:
1357  *   VIEW [ <id>[.<protocol> ]
1358  *
1359  * Response:
1360  *
1361  *-------------------------------------------------------------------------*/
Process_VIEW()1362 int CRMSClient::Process_VIEW()
1363 {
1364   if (*data_arg != '\0')
1365   {
1366     ParseUser(data_arg);
1367   }
1368   else
1369   {
1370     // XXX Check system messages first
1371 
1372     // Check user messages now
1373     Licq::UserListGuard userList;
1374     BOOST_FOREACH(const Licq::User* user, **userList)
1375     {
1376       Licq::UserReadGuard pUser(user);
1377       if(pUser->NewMessages() > 0)
1378       {
1379         myUserId = pUser->id();
1380         break;
1381       }
1382     }
1383 
1384     if (!myUserId.isValid())
1385     {
1386       fprintf(fs, "%d No new messages.\n", CODE_VIEWxNONE);
1387       return fflush(fs);
1388     }
1389   }
1390 
1391   Licq::UserWriteGuard u(myUserId);
1392   if (!u.isLocked())
1393   {
1394     fprintf(fs, "%d No such user.\n", CODE_INVALIDxUSER);
1395     return fflush(fs);
1396   }
1397 
1398   Licq::UserEvent* e = u->EventPop();
1399   printUserEvent(e, u->getAlias());
1400 
1401   return fflush(fs);
1402 }
1403 
printUserEvent(const Licq::UserEvent * e,const string & alias)1404 void CRMSClient::printUserEvent(const Licq::UserEvent* e, const string& alias)
1405 {
1406   if (e == NULL)
1407   {
1408     fprintf(fs, "%d Invalid event\n", CODE_EVENTxERROR);
1409     return;
1410   }
1411 
1412   std::ostringstream eventHeader;
1413   switch (e->eventType())
1414   {
1415     case Licq::UserEvent::TypeMessage:
1416       eventHeader << CODE_VIEWxMSG << " Message";
1417       break;
1418 
1419     case Licq::UserEvent::TypeUrl:
1420       eventHeader << CODE_VIEWxURL << " URL";
1421       break;
1422 
1423     case Licq::UserEvent::TypeChat:
1424       eventHeader << CODE_VIEWxCHAT << " Chat Request";
1425       break;
1426 
1427     case Licq::UserEvent::TypeFile:
1428       eventHeader << CODE_VIEWxFILE << " File Request";
1429       break;
1430 
1431     default:
1432       eventHeader << CODE_VIEWxUNKNOWN << " Unknown Event";
1433   }
1434 
1435   eventHeader << " from ";
1436   eventHeader << alias;
1437   eventHeader << "\n";
1438 
1439   // Write out the event header
1440   fputs(eventHeader.str().c_str(), fs);
1441 
1442   // Timestamp
1443   char szTime[25];
1444   time_t nMessageTime = e->Time();
1445   struct tm* pTM = localtime(&nMessageTime);
1446   strftime(szTime, 25, "%Y-%m-%d %H:%M:%S", pTM);
1447   fprintf(fs, "%d Sent At %s\n", CODE_VIEWxTIME, szTime);
1448 
1449   // Message
1450   fprintf(fs, "%d Message Start\n", CODE_VIEWxTEXTxSTART);
1451   fputs(e->textLoc().c_str(), fs);
1452   fprintf(fs, "\n%d Message Complete\n", CODE_VIEWxTEXTxEND);
1453 }
1454 
1455 /*---------------------------------------------------------------------------
1456  * CRMSClient::Process_ADDUSER
1457  *
1458  * Command:
1459  *   ADDUSER <id> <protocol>
1460  *
1461  * Response:
1462  *
1463  *-------------------------------------------------------------------------*/
Process_ADDUSER()1464 int CRMSClient::Process_ADDUSER()
1465 {
1466   ParseUser(data_arg);
1467 
1468   if (!myUserId.isValid())
1469   {
1470     fprintf(fs, "%d Invalid UIN.\n", CODE_INVALIDxUSER);
1471   }
1472   else if (gUserManager.addUser(myUserId) != 0)
1473   {
1474     fprintf(fs, "%d User added\n", CODE_ADDUSERxDONE);
1475   }
1476   else
1477   {
1478     fprintf(fs, "%d User not added\n", CODE_ADDUSERxERROR);
1479   }
1480 
1481   return fflush(fs);
1482 }
1483 
1484 /*---------------------------------------------------------------------------
1485  * CRMSClient::Process_REMUSER
1486  *
1487  * Command:
1488  *   REMUSER <uin>
1489  *
1490  * Response:
1491  *
1492  *-------------------------------------------------------------------------*/
Process_REMUSER()1493 int CRMSClient::Process_REMUSER()
1494 {
1495   ParseUser(data_arg);
1496 
1497   if (myUserId.isValid() && gUserManager.userExists(myUserId))
1498   {
1499     gUserManager.removeUser(myUserId);
1500     fprintf(fs, "%d User removed\n", CODE_REMUSERxDONE);
1501   }
1502   else
1503   {
1504     fprintf(fs, "%d Invalid UIN.\n", CODE_INVALIDxUSER);
1505   }
1506 
1507   return fflush(fs);
1508 }
1509 
1510 /*---------------------------------------------------------------------------
1511  * CRMSClient::Process_SECURE
1512  *
1513  * Command:
1514  *   SECURE <uin> <what>
1515  *
1516  * Response:
1517  *
1518  *-------------------------------------------------------------------------*/
Process_SECURE()1519 int CRMSClient::Process_SECURE()
1520 {
1521   if (!Licq::gDaemon.haveCryptoSupport())
1522   {
1523     fprintf(fs, "%d Licq secure channel not compiled. Please recompile with OpenSSL.\n", CODE_SECURExNOTCOMPILED);
1524     return fflush(fs);
1525   }
1526 
1527   ParseUser(data_arg);
1528 
1529   if (!myUserId.isValid())
1530   {
1531     fprintf(fs, "%d Invalid UIN.\n", CODE_INVALIDxUSER);
1532     return fflush(fs);
1533   }
1534   while (*data_arg != '\0' && *data_arg != ' ') data_arg++;
1535   NEXT_WORD(data_arg);
1536 
1537   if (strncasecmp(data_arg, "open", 4) == 0)
1538   {
1539     fprintf(fs, "%d Opening secure connection.\n", CODE_SECURExOPEN);
1540     gProtocolManager.secureChannelOpen(myUserId);
1541   }
1542   else
1543   if (strncasecmp(data_arg, "close", 5) == 0)
1544   {
1545     fprintf(fs, "%d Closing secure connection.\n", CODE_SECURExCLOSE);
1546     gProtocolManager.secureChannelClose(myUserId);
1547   }
1548   else
1549   {
1550     Licq::UserReadGuard u(myUserId);
1551     if (u.isLocked())
1552     {
1553       if (u->Secure() == 0)
1554         fprintf(fs, "%d Status: secure connection is closed.\n", CODE_SECURExSTAT);
1555       if (u->Secure() == 1)
1556         fprintf(fs, "%d Status: secure connection is open.\n", CODE_SECURExSTAT);
1557     }
1558   }
1559 
1560   return fflush(fs);
1561 }
1562