glooxd  0.3-svn
c2s.cpp
1 /*
2  Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
3  This file is part of the glooxd library. http://camaya.net/glooxd
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 #include "c2s.h"
15 #include "configmanager.h"
16 #include "sm.h"
17 #include "streambase.h"
18 #include "streamfeature.h"
19 #include "router.h"
20 
21 #include <gloox/connectiontcpserver.h>
22 #include <gloox/connectionbase.h>
23 #include <gloox/jid.h>
24 #include <gloox/logsink.h>
25 #include <gloox/mutexguard.h>
26 #include <gloox/util.h>
27 
28 #ifdef _WIN32
29 #ifndef WIN32_LEAN_AND_MEAN
30 #define WIN32_LEAN_AND_MEAN
31 #endif
32 #include <windows.h>
33 #else
34 #include <unistd.h>
35 #endif
36 
37 #include <cstdio>
38 
39 namespace glooxd
40 {
41 
42  C2S::C2S( Router& _router, ConfigManager& _cm, const gloox::LogSink& _logInstance,
43  SM& _sm, bool threading,
44  const gloox::JID& _name )
45  : ComponentBase( _router, _cm, _name, _logInstance ),
46  m_sm( _sm ), m_threading( threading ), m_strictXMPP( true ), m_running( true ),
47  m_supportRecipientUnavailable( true )
48  {
49  }
50 
52  {
53  if( m_running )
54  handleShutdown();
55 
56  gloox::util::clearList( m_servers );
57  gloox::util::clearList( m_clients );
58 // gloox::util::clearList( m_obsoleteClients );
59  printf( "m_obsoleteClients.size(): %d\n", m_obsoleteClients.size() );
60  gloox::util::clearList( m_features );
61  gloox::util::clearList( m_queue );
62  }
63 
64  void C2S::run()
65  {
66  do
67  {
68  if( m_queueSemaphore.trywait() )
69  {
70  gloox::Tag* t = m_queue.front();
71  m_queueMutex.lock();
72  m_queue.pop_front();
73  m_queueMutex.unlock();
74  handleTag( t );
75  }
76  recv( 10 );
77  } while( m_threading );
78  }
79 
80  void C2S::handleShutdown()
81  {
82  m_serverMutex.lock();
83  gloox::util::ForEach( m_servers, &gloox::ConnectionTCPServer::disconnect );
84  m_serverMutex.unlock();
85 
86  gloox::ConnectionError ce = gloox::ConnNoError;
87  gloox::StreamError se = gloox::StreamErrorSystemShutdown;
88  m_clientMutex.lock();
89  gloox::util::ForEach( m_clients, &StreamBase::disconnect, ce, se, gloox::EmptyString );
90  m_clientMutex.unlock();
91 
92  m_running = false;
93  m_threading = false;
94  m_queueMutex.lock();
95  m_queue.push_back( 0 );
96  m_queueMutex.unlock();
97  }
98 
99  gloox::ConnectionError C2S::recv( int timeout )
100  {
101  if( !m_clients.size() && !m_servers.size() )
102  {
103 #ifdef _WIN32
104  ::Sleep( timeout / 1000 );
105 #else
106  usleep( timeout );
107 #endif
108  return gloox::ConnNoError;
109  }
110 
111 // printf( "recv: locking\n" );
112  m_serverMutex.lock();
113  ServerList::const_iterator its = m_servers.begin();
114  for( ; its != m_servers.end(); ++its )
115  (*its)->recv( timeout );
116 
117  m_serverMutex.unlock();
118 // printf( "recv: unlocked\n" );
119 
120 // printf( "recv: client locking\n" );
121  m_clientMutex.lock();
122  StreamBaseList::const_iterator itc = m_clients.begin();
123  for( ; itc != m_clients.end(); ++itc )
124  {
125  (*itc)->recv( timeout );
126  (*itc)->run();
127  }
128 
129 // printf( "recv: obsolete client locking\n" );
130  m_obsoleteClientMutex.lock();
131 
132  itc = m_obsoleteClients.begin();
133  for( ; itc != m_obsoleteClients.end(); ++itc )
134  m_clients.remove( (*itc) );
135 
136  m_clientMutex.unlock();
137 // printf( "recv: client unlocked\n" );
138 
139  gloox::util::clearList( m_obsoleteClients );
140  m_obsoleteClientMutex.unlock();
141 // printf( "recv: obsolete client unlocked\n" );
142 
143  return gloox::ConnNoError;
144  }
145 
146  gloox::ConnectionError C2S::receive()
147  {
148  gloox::ConnectionError ce = gloox::ConnNoError;
149  while( ce == gloox::ConnNoError )
150  ce = recv( 10 );
151 
152  return ce;
153  }
154 
155  bool C2S::handleC2SInterfaceAdded( const std::string& interface,
156  int port )
157  {
158  if( port < 0 )
159  return false;
160 
161  ServerList::iterator it = m_servers.begin();
162  for( ; it != m_servers.end()
163  && ( (*it)->server() != gloox::EmptyString || (*it)->port() != port )
164  && ( (*it)->server() != interface || (*it)->port() != port ); ++it )
165  ;
166 
167  if( it == m_servers.end() )
168  {
169 
170  gloox::ConnectionTCPServer* s = new gloox::ConnectionTCPServer( this, m_logInstance, interface, port );
171  if( s->connect() != gloox::ConnNoError )
172  {
173  m_logInstance.warn( static_cast<gloox::LogArea>( LogAreaC2S ),
174  "Failed to listen on "
175  + ( interface.empty() ? "*" : interface ) + ":"
176  + gloox::util::int2string( port ) );
177  delete s;
178  return false;
179  }
180  else
181  {
182  m_logInstance.dbg( static_cast<gloox::LogArea>( LogAreaC2S ),
183  "Listening on " + ( interface.empty() ? "*" : interface ) + ":"
184  + gloox::util::int2string( port ) );
185 
186 #ifdef DEBUG
187  printf( "handleC2SInterfaceAdded 1: locking\n" );
188 #endif
189  m_serverMutex.lock();
190  m_servers.push_back( s );
191  m_serverMutex.unlock();
192 #ifdef DEBUG
193  printf( "handleC2SInterfaceAdded 1: unlocked\n" );
194 #endif
195  }
196  }
197  else
198  {
199  m_logInstance.dbg( static_cast<gloox::LogArea>( LogAreaC2S ),
200  "Already listening on " + ( interface.empty() ? "*" : interface ) + ":"
201  + gloox::util::int2string( port ) );
202  }
203 
204  if( m_servers.size() == 1 )
205  m_cm.handleC2SListening( true );
206 
207  return true;
208  }
209 
210  void C2S::handleDomainAdded( const std::string& domain )
211  {
212  if( domain.empty() )
213  return;
214 
215  m_router.registerDomain( this, domain, true );
216  }
217 
218  void C2S::handleDomainRemoved( const std::string& domain )
219  {
220  if( domain.empty() )
221  return;
222 
223  m_router.removeDomain( this, domain, true );
224  }
225 
226  bool C2S::handleC2SInterfaceRemoved( const std::string& interface,
227  int port )
228  {
229  gloox::util::MutexGuard mg( m_serverMutex );
230  ServerList::iterator it = m_servers.begin();
231  for( ; it != m_servers.end(); ++it )
232  {
233  if( (*it)->server() == interface && (*it)->port() == port )
234  {
235  delete (*it);
236  m_servers.erase( it );
237  if( !m_servers.size() )
238  m_cm.handleC2SListening( false );
239 
240  return true;
241  }
242  }
243 
244  return false;
245  }
246 
247  void C2S::disconnect( StreamBase* client )
248  {
249  if( !client )
250  return;
251 
252  m_sm.removeSession( client->jid().full() );
253 
254 #ifdef DEBUG
255  printf( "making client obsolete: %s\n", client->jid().full().c_str() );
256  printf( "disconnect: obsolete client locking\n" );
257 #endif
258  gloox::util::MutexGuard mg( m_obsoleteClientMutex );
259 #ifdef DEBUG
260  printf( "disconnect: obsolete client locked\n" );
261 #endif
262  m_obsoleteClients.push_back( client );
263  }
264 
265  void C2S::handleIncomingConnection( gloox::ConnectionBase* /*server*/,
266  gloox::ConnectionBase* connection )
267  {
268  if( !connection )
269  return;
270 
271  m_logInstance.dbg( static_cast<gloox::LogArea>( LogAreaC2S ),
272  "Incoming connection from "
273  + connection->server() + ":"
274  + gloox::util::int2string( connection->port() ) );
275 
276  StreamBase* client = new StreamBase( *this, m_cm, connection, m_logInstance );
277  client->setStrictXMPP( m_strictXMPP );
278  FeatureList fl;
279  FeatureList::const_iterator it = m_features.begin();
280  for( ; it != m_features.end(); ++it )
281  {
282  StreamFeature* f = (*it)->newInstance();
283  f->setParent( client );
284  fl.push_back( f );
285  }
286  client->setFeatures( fl );
287 
288 #ifdef DEBUG
289  printf( "handleIncomingConnection: client locking\n" );
290 #endif
291  gloox::util::MutexGuard mg( m_clientMutex );
292 #ifdef DEBUG
293  printf( "handleIncomingConnection: client locked\n" );
294 #endif
295  m_clients.push_back( client );
296  }
297 
298  void C2S::handleIncomingTag( gloox::Tag* tag )
299  {
300  if( !tag /*|| !tag->hasAttribute( "from" )*/ )
301  return;
302 
303 #ifdef DEBUG
304  printf( "C2S::handleIncomingTag()\n" );
305  printf( "passing to router directly\n" );
306 #endif
307  m_router.handleIncomingTag( tag );
308  }
309 
310  void C2S::handleOutgoingTag( gloox::Tag* tag )
311  {
312  if( !tag )
313  return;
314 
315  m_queueMutex.lock();
316  m_queue.push_back( tag );
317  m_queueMutex.unlock();
318  m_queueSemaphore.post();
319  }
320 
321  void C2S::handleTag( gloox::Tag* tag )
322  {
323  if( !tag )
324  return;
325 
326 #ifdef DEBUG
327  printf( "C2S::handleTag()\n" );
328 #endif
329 
330  const gloox::JID& to( tag->findAttribute( "to" ) );
331 
332 #ifdef DEBUG
333  printf( "handleTag: client locking\n" );
334 #endif
335  gloox::util::MutexGuard mg( m_clientMutex );
336 #ifdef DEBUG
337  printf( "handleTag: client locked\n" );
338 #endif
339  StreamBaseList::const_iterator it = m_clients.begin();
340  for( ; it != m_clients.end(); ++it )
341  {
342  if( (*it)->jid().full() == to.full() )
343  {
344  (*it)->handleOutgoingTag( tag );
345  return;
346  }
347  else if( (*it)->id() == to.resource() )
348  {
349  (*it)->handleIncomingTag( tag );
350  return;
351  }
352  }
353 
354  m_logInstance.warn( static_cast<gloox::LogArea>( LogAreaC2S ),
355  "C2S::handleTag unable to route stanza: "
356  + tag->xml() );
357 
358  if( m_supportRecipientUnavailable )
359  {
360  // NOTE: We will send back a <recipient-unavailable /> error response
361  // if we make it to here, but only on an IQ stanza (since that is the
362  // only one that is waiting for a resposne). If we did send it on
363  // a presence stanza, we'd end up in an infinite loop as it tries
364  // to send an unavailable presense to the disconnected client.
365  if( tag->name() == "iq" )
366  {
367  // We also could get in an infinite loop if we try to reply back
368  // to a sender that is not available. So check the from.
369  const gloox::JID& from( tag->findAttribute( "from" ) );
370  if( !from.full().empty() )
371  {
372  StreamBaseList::const_iterator it2 = m_clients.begin();
373  for( ; it2 != m_clients.end(); ++it2 )
374  {
375  if( (*it2)->jid().full() == from.full() )
376  {
377  gloox::Tag* t = returnError( tag, gloox::StanzaErrorRecipientUnavailable, gloox::StanzaErrorTypeWait );
378  handleOutgoingTag( t );
379  break;
380  }
381  }
382  }
383  }
384  }
385 
386  delete tag;
387  }
388 
389 }