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