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  * 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 "config.h"
21 
22 #include <licq/socketmanager.h>
23 
24 #include <licq/contactlist/user.h>
25 #include <licq/socket.h>
26 #include <licq/thread/mutexlocker.h>
27 
28 using Licq::INetSocket;
29 using Licq::SocketHashTable;
30 using Licq::SocketManager;
31 using Licq::SocketSet;
32 using Licq::UserId;
33 using std::list;
34 
35 static const unsigned short SOCKET_HASH_SIZE = 128;
36 
SocketSet()37 SocketSet::SocketSet()
38 {
39   FD_ZERO(&sFd);
40 }
41 
~SocketSet()42 SocketSet::~SocketSet()
43 {
44   // Empty
45 }
46 
Set(int _nSD)47 void SocketSet::Set(int _nSD)
48 {
49   MutexLocker lock(myMutex);
50   FD_SET(_nSD, &sFd);
51   list<int>::iterator i = lFd.begin();
52   while (i != lFd.end() && _nSD < *i)
53     ++i;
54   lFd.insert(i, _nSD);
55 }
56 
Clear(int _nSD)57 void SocketSet::Clear(int _nSD)
58 {
59   MutexLocker lock(myMutex);
60   FD_CLR(_nSD, &sFd);
61   list<int>::iterator i = lFd.begin();
62   while (i != lFd.end() && *i != _nSD)
63     ++i;
64   if (i != lFd.end())
65     lFd.erase(i);
66 }
67 
Num()68 unsigned short SocketSet::Num()
69 {
70   MutexLocker lock(myMutex);
71   return lFd.size();
72 }
73 
Largest()74 int SocketSet::Largest()
75 {
76   MutexLocker lock(myMutex);
77   if (lFd.empty())
78     return 0;
79   else
80     return *lFd.begin();
81 }
82 
socketSet()83 fd_set SocketSet::socketSet()
84 {
85   MutexLocker lock(myMutex);
86   return sFd;
87 }
88 
89 
SocketHashTable(unsigned short _nSize)90 SocketHashTable::SocketHashTable(unsigned short _nSize)
91   : m_vlTable(_nSize)
92 {
93   // Empty
94 }
95 
~SocketHashTable()96 SocketHashTable::~SocketHashTable()
97 {
98   // Empty
99 }
100 
Retrieve(int _nSd)101 INetSocket* SocketHashTable::Retrieve(int _nSd)
102 {
103   myMutex.lockRead();
104 
105   INetSocket *s = NULL;
106   list <INetSocket *> &l = m_vlTable[HashValue(_nSd)];
107 
108   int nSd;
109   list<INetSocket *>::iterator iter;
110   for (iter = l.begin(); iter != l.end(); ++iter)
111   {
112     (*iter)->Lock();
113     nSd = (*iter)->Descriptor();
114     (*iter)->Unlock();
115     if (nSd == _nSd)
116     {
117       s = (*iter);
118       break;
119     }
120   }
121   if (iter == l.end()) s = NULL;
122 
123   myMutex.unlockRead();
124   return s;
125 }
126 
Store(INetSocket * s,int _nSd)127 void SocketHashTable::Store(INetSocket *s, int _nSd)
128 {
129   myMutex.lockWrite();
130   list<INetSocket *> &l = m_vlTable[HashValue(_nSd)];
131   l.push_front(s);
132   myMutex.unlockWrite();
133 }
134 
Remove(int _nSd)135 void SocketHashTable::Remove(int _nSd)
136 {
137   myMutex.lockWrite();
138   list<INetSocket *> &l = m_vlTable[HashValue(_nSd)];
139   int nSd;
140   list<INetSocket *>::iterator iter;
141   for (iter = l.begin(); iter != l.end(); ++iter)
142   {
143     (*iter)->Lock();
144     nSd = (*iter)->Descriptor();
145     (*iter)->Unlock();
146     if (nSd == _nSd)
147     {
148       l.erase(iter);
149       break;
150     }
151   }
152   myMutex.unlockWrite();
153 }
154 
HashValue(int _nSd)155 unsigned short SocketHashTable::HashValue(int _nSd)
156 {
157   //return _nSd % m_vlTable.size();
158   return _nSd & (unsigned long)(SOCKET_HASH_SIZE - 1);
159 }
160 
161 
SocketManager()162 SocketManager::SocketManager()
163   : m_hSockets(SOCKET_HASH_SIZE)
164 {
165   // Empty
166 }
167 
~SocketManager()168 SocketManager::~SocketManager()
169 {
170   myMutex.lock();
171   myMutex.unlock();
172 }
173 
FetchSocket(int _nSd)174 INetSocket* SocketManager::FetchSocket(int _nSd)
175 {
176   MutexLocker lock(myMutex);
177   INetSocket *s = m_hSockets.Retrieve(_nSd);
178   if (s != NULL)
179     s->Lock();
180   return s;
181 }
182 
DropSocket(INetSocket * s)183 void SocketManager::DropSocket(INetSocket *s)
184 {
185   if (s != NULL)
186     s->Unlock();
187 }
188 
AddSocket(INetSocket * s)189 void SocketManager::AddSocket(INetSocket *s)
190 {
191   s->Lock();
192   m_hSockets.Store(s, s->Descriptor());
193   m_sSockets.Set(s->Descriptor());
194 }
195 
CloseSocket(int nSd,bool bClearUser,bool bDelete)196 void SocketManager::CloseSocket(int nSd, bool bClearUser, bool bDelete)
197 {
198   // Quick check that the socket is valid
199   if (nSd == -1) return;
200 
201   MutexLocker lock(myMutex);
202 
203   // Clear from the socket list
204   m_sSockets.Clear(nSd);
205 
206   // Fetch the actual socket
207   INetSocket *s = m_hSockets.Retrieve(nSd);
208   if (s == NULL)
209     return;
210 
211   // First remove the socket from the hash table so it won't be fetched anymore
212   m_hSockets.Remove(nSd);
213 
214 
215   // Now close the connection (we don't have to lock it first, because the
216   // Remove function above guarantees that no one has a lock on the socket
217   // before removing it from the hash table, and once removed from the has
218   // table, no one can get a lock again.
219   s->CloseConnection();
220 
221   if (bClearUser)
222   {
223     Licq::UserWriteGuard u(s->userId());
224     if (u.isLocked())
225     {
226       u->clearSocketDesc(s);
227       if (u->OfflineOnDisconnect())
228         u->statusChanged(Licq::User::OfflineStatus);
229     }
230   }
231 
232   if (bDelete)
233     delete s;
234 }
235