]>
code.delx.au - pymsnt/blob - src/main.py
305d549b18f254210134ce7d477a17ea743237e6
1 # Copyright 2004-2006 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
4 import os
, os
. path
, time
, sys
, codecs
, getopt
6 sys
. setdefaultencoding ( "utf-8" )
7 sys
. stdout
= codecs
. lookup ( 'utf-8' )[- 1 ]( sys
. stdout
)
10 # Find the best reactor
11 selectWarning
= "Unable to install any good reactors (kqueue, epoll, poll). \n We fell back to using select. You may have scalability problems. \n This reactor will not support more than 1024 connections at a time."
13 from twisted
. internet
import epollreactor
as bestreactor
16 from twisted
. internet
import kqreactor
as bestreactor
19 from twisted
. internet
import pollreactor
as bestreactor
22 from twisted
. internet
import selectreactor
as bestreactor
26 from twisted
. internet
import default
as bestreactor
29 print "Unable to find a reactor. Please make sure you have Twisted properly installed. \n Exiting..."
37 # Must load config before everything else
40 configFile
= "config.xml"
42 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtlp:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "pid=" , "help" ])
44 if o
in ( "-c" , "--config" ):
46 elif o
in ( "-b" , "--background" ):
47 config
. background
= True
48 elif o
in ( "-d" , "--debug" ):
49 config
. debugLevel
= "2"
50 elif o
in ( "-D" , "--Debug" ):
51 config
. debugLevel
= "3"
52 elif o
in ( "-g" , "--garbage" ):
54 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
55 elif o
in ( "-t" , "--traceback" ):
56 config
. debugLevel
= "1"
57 elif o
in ( "-l" , "--log" ):
59 elif o
in ( "-p" , "--pid" ):
61 elif o
in ( "-o" , "--option" ):
62 var
, setting
= v
. split ( "=" , 2 )
63 configOptions
[ var
] = setting
64 elif o
in ( "-h" , "--help" ):
65 print " %s [options]" % sys
. argv
[ 0 ]
66 print " -h print this help"
67 print " -b daemonize/background transport"
68 print " -c <file> read configuration from this file"
69 print " -d print debugging output"
70 print " -D print extended debugging output"
71 print " -g print garbage collection output"
72 print " -t print debugging only on traceback"
73 print " -l <file> write debugging output to file"
74 print " -p <file> write process ID to file"
75 print " -o <var>=<setting> set config var to setting"
78 xmlconfig
. reloadConfig ( configFile
, configOptions
)
81 # They picked their own reactor. Lets install it.
82 del sys
. modules
[ "twisted.internet.reactor" ]
83 if config
. reactor
== "epoll" :
84 from twisted
. internet
import epollreactor
85 epollreactor
. install ()
86 elif config
. reactor
== "poll" :
87 from twisted
. internet
import pollreactor
89 elif config
. reactor
== "kqueue" :
90 from twisted
. internet
import kqreactor
92 elif len ( config
. reactor
) > 0 :
93 print "Unknown reactor: " , config
. reactor
, ". Using select(), reactor."
96 from twisted
. internet
import reactor
, task
97 from twisted
. internet
. defer
import Deferred
98 from twisted
. words
. xish
. domish
import Element
99 from twisted
. words
. protocols
. jabber
import component
100 from twisted
. words
. protocols
. jabber
. jid
import internJID
102 from debug
import LogEvent
, INFO
, WARN
, ERROR
121 class PyTransport ( component
. Service
):
125 LogEvent ( INFO
, msg
= "SVN r" + str ( svninfo
. getSVNVersion ()))
129 # Discovery, as well as some builtin features
130 self
. discovery
= disco
. ServerDiscovery ( self
)
131 self
. discovery
. addIdentity ( "gateway" , legacy
. id , config
. discoName
, config
. jid
)
132 self
. discovery
. addIdentity ( "conference" , "text" , config
. discoName
+ " Chatrooms" , config
. jid
)
133 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
134 self
. discovery
. addFeature ( "jabber:iq:conference" , None , config
. jid
) # We don't actually support this, but Psi has a bug where it looks for this instead of the above
135 self
. discovery
. addIdentity ( "client" , "pc" , "MSN Messenger" , "USER" )
137 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
138 self
. avatarCache
= avatar
. AvatarCache ()
139 self
. registermanager
= register
. RegisterManager ( self
)
140 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
141 self
. versionTeller
= misciq
. VersionTeller ( self
)
142 self
. pingService
= misciq
. PingService ( self
)
143 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
144 self
. vCardFactory
= misciq
. VCardFactory ( self
)
145 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
146 self
. connectUsers
= misciq
. ConnectUsers ( self
)
147 if config
. ftJabberPort
:
148 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
149 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
151 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
152 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
153 self
. statistics
= misciq
. Statistics ( self
)
154 self
. startTime
= int ( time
. time ())
156 self
. xmlstream
= None
159 # Groupchat ID handling
161 self
. reservedIDs
= []
166 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
167 self
. loopTask
. start ( 60.0 )
171 for session
in self
. sessions
. copy ():
172 self
. sessions
[ session
]. removeMe ()
174 def makeMessageID ( self
):
176 return str ( self
. messageID
)
179 newID
= "r" + str ( self
. lastID
)
181 if self
. reservedIDs
. count ( newID
) > 0 :
182 # Ack, it's already used.. Try again
187 def reserveID ( self
, ID
):
188 self
. reservedIDs
. append ( ID
)
191 numsessions
= len ( self
. sessions
)
193 #if config.debugOn and numsessions > 0:
195 # for key in self.sessions:
196 # print "\t" + self.sessions[key].jabberID
198 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
199 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
200 legacy
. updateStats ( self
. statistics
)
202 oldDict
= self
. sessions
. copy ()
207 LogEvent ( WARN
, "" , "Ghost session found." )
208 # Don't add it to the new dictionary. Effectively removing it
210 self
. sessions
[ key
] = s
212 def componentConnected ( self
, xmlstream
):
214 self
. xmlstream
= xmlstream
215 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
216 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
217 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
218 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
220 pres
= Element (( None , "presence" ))
221 pres
. attributes
[ "to" ] = "presence@-internal"
222 pres
. attributes
[ "from" ] = config
. compjid
223 x
= pres
. addElement ( "x" )
224 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
225 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
226 x
. attributes
[ "config:version" ] = "1"
227 x
. attributes
[ "protocol-version" ] = "1.0"
228 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
231 def componentDisconnected ( self
):
233 self
. xmlstream
= None
235 def onRouteMessage ( self
, el
):
236 for child
in el
. elements ():
237 if child
. name
== "message" :
238 self
. onMessage ( child
)
239 elif child
. name
== "presence" :
240 # Ignore any presence broadcasts about other XCP components
241 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
242 self
. onPresence ( child
)
243 elif child
. name
== "iq" :
244 self
. discovery
. onIq ( child
)
246 def onMessage ( self
, el
):
247 fro
= el
. getAttribute ( "from" )
249 froj
= internJID ( fro
)
251 LogEvent ( WARN
, "" , "Failed stringprep." )
253 mtype
= el
. getAttribute ( "type" )
254 s
= self
. sessions
. get ( froj
. userhost (), None )
255 if mtype
== "error" and s
:
256 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
260 elif mtype
!= "error" :
261 to
= el
. getAttribute ( "to" )
262 ulang
= utils
. getLang ( el
)
264 for child
in el
. elements ():
265 if child
. name
== "body" :
266 body
= child
.__ str
__ ()
267 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
268 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
269 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
271 def onPresence ( self
, el
):
272 fro
= el
. getAttribute ( "from" )
273 to
= el
. getAttribute ( "to" )
275 froj
= internJID ( fro
)
278 LogEvent ( WARN
, "" , "Failed stringprep." )
281 ptype
= el
. getAttribute ( "type" )
282 s
= self
. sessions
. get ( froj
. userhost ())
283 if ptype
== "error" and s
:
284 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
289 ulang
= utils
. getLang ( el
)
290 ptype
= el
. getAttribute ( "type" )
292 # If the presence packet is to the transport (not a user) and there isn't already a session
293 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
294 LogEvent ( INFO
, "" , "Attempting to create a new session." )
295 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
297 self
. statistics
. stats
[ "TotalUsers" ] += 1
298 self
. sessions
[ froj
. userhost ()] = s
299 LogEvent ( INFO
, "" , "New session created." )
300 # Send the first presence
303 LogEvent ( INFO
, "" , "Failed to create session" )
304 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
306 elif el
. getAttribute ( "type" ) != "error" :
307 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
308 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
311 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
312 # They haven't logged in, and are trying to change subscription to a user
313 # No, lets not log them in. Lets send an error :)
314 jabw
. sendPresence ( self
, fro
, to
, ptype
= "error" )
316 # Lets log them in and then do it
317 #LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
318 #s = session.makeSession(self, froj.userhost(), ulang)
320 # self.sessions[froj.userhost()] = s
321 # LogEvent(INFO, "", "New session created.")
322 # # Tell the session there's a new resource
323 # s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
324 # # Send this subscription
330 # Check for any other instances
331 if config
. pid
and os
. name
!= "posix" :
334 twistd
. checkPID ( config
. pid
)
336 # Do any auto-update stuff
339 # Daemonise the process and write the PID file
340 if config
. background
and os
. name
== "posix" :
345 # Initialise debugging, and do the other half of SIGHUPstuff
347 legacy
. reloadConfig ()
351 if config
. useXCP
and config
. compjid
:
353 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
354 self
. transportSvc
= PyTransport ()
355 self
. transportSvc
. setServiceParent ( self
. c
)
356 self
. c
. startService ()
357 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
361 pid
= str ( os
. getpid ())
362 pf
= open ( config
. pid
, "w" )
363 pf
. write ( " %s \n " % pid
)
366 def shuttingDown ( self
):
367 self
. transportSvc
. removeMe ()
368 # Keep the transport running for another 3 seconds
369 def cb ( ignored
= None ):
371 twistd
. removePID ( config
. pid
)
374 reactor
. callLater ( 3.0 , d
. callback
, None )
379 def SIGHUPstuff (* args
):
380 global configFile
, configOptions
381 xmlconfig
. reloadConfig ( configFile
, configOptions
)
382 if config
. pid
and os
. name
!= "posix" :
385 legacy
. reloadConfig ()
387 if os
. name
== "posix" :
389 # Set SIGHUP to reload the config file & close & open debug file
390 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
391 # Load some scripts for PID and daemonising
392 from twisted
. scripts
import twistd
396 # Create the application
400 if __name__
== "__main__" :