1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2010-2013 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Please refer to the COPYRIGHT file distributed with this source
6  * distribution for the names of the individual contributors.
7  *
8  * Licq is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Licq is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Licq; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include <boost/foreach.hpp>
24 
25 #include "client.h"
26 #include "owner.h"
27 #include "plugin.h"
28 #include "sessionmanager.h"
29 #include "user.h"
30 #include "vcard.h"
31 
32 #include <licq/contactlist/usermanager.h>
33 #include <licq/event.h>
34 #include <licq/logging/log.h>
35 #include <licq/oneventmanager.h>
36 #include <licq/plugin/pluginmanager.h>
37 #include <licq/protocolsignal.h>
38 #include <licq/statistics.h>
39 #include <licq/userevents.h>
40 
41 using namespace LicqJabber;
42 
43 using Licq::OnEventData;
44 using Licq::gOnEventManager;
45 using Licq::gLog;
46 using std::string;
47 
Plugin()48 Plugin::Plugin()
49   : myClient(NULL)
50 {
51   gLog.debug("Using gloox version %s", gloox::GLOOX_VERSION.c_str());
52 }
53 
~Plugin()54 Plugin::~Plugin()
55 {
56   delete myClient;
57 }
58 
run()59 int Plugin::run()
60 {
61   myMainLoop.addRawFile(getReadPipe(), this);
62   myMainLoop.run();
63   return 0;
64 }
65 
rawFileEvent(int,int fd,int)66 void Plugin::rawFileEvent(int /*id*/, int fd, int /*revents*/)
67 {
68   char ch;
69   ::read(fd, &ch, sizeof(ch));
70 
71   switch (ch)
72   {
73     case PipeSignal:
74       processSignal(popSignal().get());
75       break;
76     case PipeShutdown:
77       doLogoff();
78       myMainLoop.quit();
79       break;
80     default:
81       gLog.error("Unknown command %c", ch);
82       break;
83   }
84 }
85 
processSignal(const Licq::ProtocolSignal * signal)86 void Plugin::processSignal(const Licq::ProtocolSignal* signal)
87 {
88   assert(signal != NULL);
89 
90   switch (signal->signal())
91   {
92     case Licq::ProtocolSignal::SignalLogon:
93       doLogon(dynamic_cast<const Licq::ProtoLogonSignal*>(signal));
94       break;
95     case Licq::ProtocolSignal::SignalLogoff:
96       doLogoff();
97       break;
98     case Licq::ProtocolSignal::SignalChangeStatus:
99       doChangeStatus(
100           dynamic_cast<const Licq::ProtoChangeStatusSignal*>(signal));
101       break;
102     case Licq::ProtocolSignal::SignalAddUser:
103       doAddUser(dynamic_cast<const Licq::ProtoAddUserSignal*>(signal));
104       break;
105     case Licq::ProtocolSignal::SignalRemoveUser:
106       doRemoveUser(dynamic_cast<const Licq::ProtoRemoveUserSignal*>(signal));
107       break;
108     case Licq::ProtocolSignal::SignalRenameUser:
109       doRenameUser(dynamic_cast<const Licq::ProtoRenameUserSignal*>(signal));
110       break;
111     case Licq::ProtocolSignal::SignalChangeUserGroups:
112       doChangeUserGroups(
113           dynamic_cast<const Licq::ProtoChangeUserGroupsSignal*>(signal));
114       break;
115     case Licq::ProtocolSignal::SignalSendMessage:
116       doSendMessage(dynamic_cast<const Licq::ProtoSendMessageSignal*>(signal));
117       break;
118     case Licq::ProtocolSignal::SignalNotifyTyping:
119       doNotifyTyping(
120           dynamic_cast<const Licq::ProtoTypingNotificationSignal*>(signal));
121       break;
122     case Licq::ProtocolSignal::SignalGrantAuth:
123       doGrantAuth(dynamic_cast<const Licq::ProtoGrantAuthSignal*>(signal));
124       break;
125     case Licq::ProtocolSignal::SignalRefuseAuth:
126       doRefuseAuth(dynamic_cast<const Licq::ProtoRefuseAuthSignal*>(signal));
127       break;
128     case Licq::ProtocolSignal::SignalRequestInfo:
129       doGetInfo(dynamic_cast<const Licq::ProtoRequestInfo*>(signal));
130       break;
131     case Licq::ProtocolSignal::SignalUpdateInfo:
132       doUpdateInfo(dynamic_cast<const Licq::ProtoUpdateInfoSignal*>(signal));
133       break;
134     case Licq::ProtocolSignal::SignalRequestPicture:
135       doGetPicture(dynamic_cast<const Licq::ProtoRequestPicture*>(signal));
136       break;
137     case Licq::ProtocolSignal::SignalRequestAuth:
138       doRequestAuth(dynamic_cast<const Licq::ProtoRequestAuthSignal*>(signal));
139       break;
140     case Licq::ProtocolSignal::SignalRenameGroup:
141       doRenameGroup(dynamic_cast<const Licq::ProtoRenameGroupSignal*>(signal));
142       break;
143     default:
144       gLog.error("Unknown signal %u", signal->signal());
145       /* Unsupported action, if it has an eventId, cancel it */
146       if (signal->eventId() != 0)
147         Licq::gPluginManager.pushPluginEvent(
148             new Licq::Event(signal, Licq::Event::ResultUnsupported));
149       break;
150   }
151 }
152 
doLogon(const Licq::ProtoLogonSignal * signal)153 void Plugin::doLogon(const Licq::ProtoLogonSignal* signal)
154 {
155   unsigned status = signal->status();
156   if (status == User::OfflineStatus)
157     return;
158 
159   string username;
160   string password;
161   string host;
162   int port;
163   string resource;
164   gloox::TLSPolicy tlsPolicy;
165   {
166     OwnerReadGuard owner(signal->userId());
167     if (!owner.isLocked())
168     {
169       gLog.error("No owner set");
170       return;
171     }
172 
173     username = owner->accountId();
174     password = owner->password();
175     host = owner->serverHost();
176     port = owner->serverPort();
177     resource = owner->resource();
178     tlsPolicy = owner->tlsPolicy();
179   }
180 
181   if (myClient == NULL)
182     myClient = new Client(myMainLoop, signal->userId(), username, password,
183                           host, port, resource, tlsPolicy);
184   else
185     myClient->setPassword(password);
186 
187   if (!myClient->isConnected())
188   {
189     if (!myClient->connect(status))
190     {
191       delete myClient;
192       myClient = NULL;
193       return;
194     }
195   }
196 }
197 
doChangeStatus(const Licq::ProtoChangeStatusSignal * signal)198 void Plugin::doChangeStatus(const Licq::ProtoChangeStatusSignal* signal)
199 {
200   assert(myClient != NULL);
201   myClient->changeStatus(signal->status());
202 }
203 
doLogoff()204 void Plugin::doLogoff()
205 {
206   if (myClient == NULL)
207     return;
208 
209   delete myClient;
210   myClient = NULL;
211 }
212 
doSendMessage(const Licq::ProtoSendMessageSignal * signal)213 void Plugin::doSendMessage(const Licq::ProtoSendMessageSignal* signal)
214 {
215   assert(myClient != NULL);
216 
217   bool isUrgent = (signal->flags() & Licq::ProtocolSignal::SendUrgent);
218 
219   myClient->getSessionManager()->sendMessage(
220       signal->userId().accountId(), signal->message(), isUrgent);
221 
222   Licq::EventMsg* message = new Licq::EventMsg(
223       signal->message().c_str(), Licq::EventMsg::TimeNow,
224       Licq::EventMsg::FlagSender);
225 
226   Licq::Event* event =
227       new Licq::Event(signal, Licq::Event::ResultAcked, message);
228   event->myCommand = Licq::Event::CommandMessage;
229 
230   if (event->m_pUserEvent)
231   {
232     UserWriteGuard user(signal->userId());
233     if (user.isLocked())
234     {
235       event->m_pUserEvent->AddToHistory(*user, false);
236       user->SetLastSentEvent();
237       gOnEventManager.performOnEvent(OnEventData::OnEventMsgSent, *user);
238     }
239     Licq::gStatistics.increase(Licq::Statistics::EventsSentCounter);
240   }
241 
242   Licq::gPluginManager.pushPluginEvent(event);
243 }
244 
doNotifyTyping(const Licq::ProtoTypingNotificationSignal * signal)245 void Plugin::doNotifyTyping(const Licq::ProtoTypingNotificationSignal* signal)
246 {
247   assert(myClient != NULL);
248 
249   myClient->getSessionManager()->notifyTyping(
250       signal->userId().accountId(), signal->active());
251 }
252 
doGetInfo(const Licq::ProtoRequestInfo * signal)253 void Plugin::doGetInfo(const Licq::ProtoRequestInfo* signal)
254 {
255   assert(myClient != NULL);
256   myClient->getVCard(signal->userId().accountId());
257 
258   Licq::gPluginManager.pushPluginEvent(new Licq::Event(signal));
259 }
260 
doUpdateInfo(const Licq::ProtoUpdateInfoSignal * signal)261 void Plugin::doUpdateInfo(const Licq::ProtoUpdateInfoSignal* signal)
262 {
263   assert(myClient != NULL);
264   OwnerReadGuard owner(signal->userId());
265   if (!owner.isLocked())
266   {
267     gLog.error("No owner set");
268     return;
269   }
270 
271   UserToVCard vcard(*owner);
272   myClient->setOwnerVCard(vcard);
273 
274   Licq::gPluginManager.pushPluginEvent(new Licq::Event(signal));
275 }
276 
doGetPicture(const Licq::ProtoRequestPicture * signal)277 void Plugin::doGetPicture(const Licq::ProtoRequestPicture* signal)
278 {
279   assert(myClient != NULL);
280   myClient->getVCard(signal->userId().accountId());
281 
282   Licq::gPluginManager.pushPluginEvent(new Licq::Event(signal));
283 }
284 
doAddUser(const Licq::ProtoAddUserSignal * signal)285 void Plugin::doAddUser(const Licq::ProtoAddUserSignal* signal)
286 {
287   assert(myClient != NULL);
288   const Licq::UserId userId = signal->userId();
289   gloox::StringList groupNames;
290   getUserGroups(userId, groupNames);
291   myClient->addUser(userId.accountId(), groupNames, true);
292 }
293 
doChangeUserGroups(const Licq::ProtoChangeUserGroupsSignal * signal)294 void Plugin::doChangeUserGroups(
295     const Licq::ProtoChangeUserGroupsSignal* signal)
296 {
297   assert(myClient != NULL);
298   const Licq::UserId userId = signal->userId();
299   gloox::StringList groupNames;
300   getUserGroups(userId, groupNames);
301   myClient->changeUserGroups(userId.accountId(), groupNames);
302 }
303 
doRemoveUser(const Licq::ProtoRemoveUserSignal * signal)304 void Plugin::doRemoveUser(const Licq::ProtoRemoveUserSignal* signal)
305 {
306   assert(myClient != NULL);
307   myClient->removeUser(signal->userId().accountId());
308   Licq::gUserManager.removeLocalUser(signal->userId());
309 }
310 
doRenameUser(const Licq::ProtoRenameUserSignal * signal)311 void Plugin::doRenameUser(const Licq::ProtoRenameUserSignal* signal)
312 {
313   assert(myClient != NULL);
314   string newName;
315   {
316     UserReadGuard u(signal->userId());
317     if (!u.isLocked())
318       return;
319     newName = u->getAlias();
320   }
321 
322   myClient->renameUser(signal->userId().accountId(), newName);
323 }
324 
doGrantAuth(const Licq::ProtoGrantAuthSignal * signal)325 void Plugin::doGrantAuth(const Licq::ProtoGrantAuthSignal* signal)
326 {
327   assert(myClient != NULL);
328   myClient->grantAuthorization(signal->userId().accountId());
329 
330   Licq::gPluginManager.pushPluginEvent(new Licq::Event(signal));
331 }
332 
doRefuseAuth(const Licq::ProtoRefuseAuthSignal * signal)333 void Plugin::doRefuseAuth(const Licq::ProtoRefuseAuthSignal* signal)
334 {
335   assert(myClient != NULL);
336   myClient->refuseAuthorization(signal->userId().accountId());
337 
338   Licq::gPluginManager.pushPluginEvent(new Licq::Event(signal));
339 }
340 
doRequestAuth(const Licq::ProtoRequestAuthSignal * signal)341 void Plugin::doRequestAuth(const Licq::ProtoRequestAuthSignal* signal)
342 {
343   assert(myClient != NULL);
344   myClient->requestAuthorization(
345       signal->userId().accountId(), signal->message());
346 }
347 
doRenameGroup(const Licq::ProtoRenameGroupSignal * signal)348 void Plugin::doRenameGroup(const Licq::ProtoRenameGroupSignal* signal)
349 {
350   Licq::UserListGuard userList(signal->userId());
351   BOOST_FOREACH(Licq::User* licqUser, **userList)
352   {
353     Licq::UserReadGuard user(licqUser);
354 
355     if (!user->isInGroup(signal->groupId()))
356       continue;
357 
358     // User is member of renamed group, get complete group list and update
359     // server
360     gloox::StringList groupNames;
361     const Licq::UserGroupList& groups = user->GetGroups();
362     BOOST_FOREACH(int groupId, groups)
363     {
364       string groupName = Licq::gUserManager.GetGroupNameFromGroup(groupId);
365       if (!groupName.empty())
366         groupNames.push_back(groupName);
367     }
368     myClient->changeUserGroups(user->id().accountId(), groupNames);
369   }
370 }
371 
getUserGroups(const Licq::UserId & userId,gloox::StringList & retGroupNames)372 void Plugin::getUserGroups(const Licq::UserId& userId,
373                            gloox::StringList& retGroupNames)
374 {
375   UserReadGuard user(userId);
376   if (!user.isLocked())
377     return;
378 
379   const Licq::UserGroupList& groups = user->GetGroups();
380   BOOST_FOREACH(int groupId, groups)
381   {
382     string groupName = Licq::gUserManager.GetGroupNameFromGroup(groupId);
383     if (!groupName.empty())
384       retGroupNames.push_back(groupName);
385   }
386 }
387