]>
code.delx.au - pymsnt/blob - src/main.py
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
)
9 # Find the best reactor
10 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."
12 from twisted
. internet
import epollreactor
as bestreactor
15 from twisted
. internet
import kqreactor
as bestreactor
18 from twisted
. internet
import pollreactor
as bestreactor
21 from twisted
. internet
import selectreactor
as bestreactor
25 from twisted
. internet
import default
as bestreactor
28 print "Unable to find a reactor. \n Exiting..."
34 # Must load config before everything else
37 configFile
= "config.xml"
39 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtlp:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "pid=" , "help" ])
41 if o
in ( "-c" , "--config" ):
43 elif o
in ( "-b" , "--background" ):
44 config
. background
= True
45 elif o
in ( "-d" , "--debug" ):
46 config
. debugLevel
= "2"
47 elif o
in ( "-D" , "--Debug" ):
48 config
. debugLevel
= "3"
49 elif o
in ( "-g" , "--garbage" ):
51 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
52 elif o
in ( "-t" , "--traceback" ):
53 config
. debugLevel
= "1"
54 elif o
in ( "-l" , "--log" ):
56 elif o
in ( "-p" , "--pid" ):
58 elif o
in ( "-o" , "--option" ):
59 var
, setting
= v
. split ( "=" , 2 )
60 configOptions
[ var
] = setting
61 elif o
in ( "-h" , "--help" ):
62 print " %s [options]" % sys
. argv
[ 0 ]
63 print " -h print this help"
64 print " -b daemonize/background transport"
65 print " -c <file> read configuration from this file"
66 print " -d print debugging output"
67 print " -D print extended debugging output"
68 print " -g print garbage collection output"
69 print " -t print debugging only on traceback"
70 print " -l <file> write debugging output to file"
71 print " -p <file> write process ID to file"
72 print " -o <var>=<setting> set config var to setting"
75 xmlconfig
. reloadConfig ( configFile
, configOptions
)
78 # They picked their own reactor. Lets install it.
79 del sys
. modules
[ "twisted.internet.reactor" ]
80 if config
. reactor
== "epoll" :
81 from twisted
. internet
import epollreactor
82 epollreactor
. install ()
83 elif config
. reactor
== "poll" :
84 from twisted
. internet
import pollreactor
86 elif config
. reactor
== "kqueue" :
87 from twisted
. internet
import kqreactor
89 elif len ( config
. reactor
) > 0 :
90 print "Unknown reactor: " , config
. reactor
, ". Using select(), reactor."
93 from twisted
. internet
import reactor
, task
94 from twisted
. internet
. defer
import Deferred
95 from tlib
. xmlw
import Element
, jid
, component
96 from debug
import LogEvent
, INFO
, WARN
, ERROR
115 class PyTransport ( component
. Service
):
119 LogEvent ( INFO
, msg
= "SVN r" + str ( svninfo
. getSVNVersion ()))
123 # Discovery, as well as some builtin features
124 self
. discovery
= disco
. ServerDiscovery ( self
)
125 self
. discovery
. addIdentity ( "gateway" , legacy
. id , config
. discoName
, config
. jid
)
126 self
. discovery
. addIdentity ( "conference" , "text" , config
. discoName
+ " Chatrooms" , config
. jid
)
127 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
128 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
129 self
. discovery
. addIdentity ( "client" , "pc" , "MSN Messenger" , "USER" )
131 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
132 self
. avatarCache
= avatar
. AvatarCache ()
133 self
. registermanager
= register
. RegisterManager ( self
)
134 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
135 self
. versionTeller
= misciq
. VersionTeller ( self
)
136 self
. pingService
= misciq
. PingService ( self
)
137 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
138 self
. vCardFactory
= misciq
. VCardFactory ( self
)
139 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
140 self
. connectUsers
= misciq
. ConnectUsers ( self
)
141 if config
. ftJabberPort
:
142 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
143 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
145 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
146 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
147 self
. statistics
= misciq
. Statistics ( self
)
148 self
. startTime
= int ( time
. time ())
150 self
. xmlstream
= None
153 # Groupchat ID handling
155 self
. reservedIDs
= []
160 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
161 self
. loopTask
. start ( 60.0 )
165 for session
in self
. sessions
. copy ():
166 self
. sessions
[ session
]. removeMe ()
168 def makeMessageID ( self
):
170 return str ( self
. messageID
)
173 newID
= "r" + str ( self
. lastID
)
175 if self
. reservedIDs
. count ( newID
) > 0 :
176 # Ack, it's already used.. Try again
181 def reserveID ( self
, ID
):
182 self
. reservedIDs
. append ( ID
)
185 numsessions
= len ( self
. sessions
)
187 #if config.debugOn and numsessions > 0:
189 # for key in self.sessions:
190 # print "\t" + self.sessions[key].jabberID
192 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
193 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
194 legacy
. updateStats ( self
. statistics
)
196 oldDict
= self
. sessions
. copy ()
201 LogEvent ( WARN
, "" , "Ghost session found." )
202 # Don't add it to the new dictionary. Effectively removing it
204 self
. sessions
[ key
] = s
206 def componentConnected ( self
, xmlstream
):
208 self
. xmlstream
= xmlstream
209 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
210 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
211 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
212 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
214 pres
= Element (( None , "presence" ))
215 pres
. attributes
[ "to" ] = "presence@-internal"
216 pres
. attributes
[ "from" ] = config
. compjid
217 x
= pres
. addElement ( "x" )
218 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
219 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
220 x
. attributes
[ "config:version" ] = "1"
221 x
. attributes
[ "protocol-version" ] = "1.0"
222 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
225 def componentDisconnected ( self
):
227 self
. xmlstream
= None
229 def onRouteMessage ( self
, el
):
230 for child
in el
. elements ():
231 if child
. name
== "message" :
232 self
. onMessage ( child
)
233 elif child
. name
== "presence" :
234 # Ignore any presence broadcasts about other XCP components
235 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
236 self
. onPresence ( child
)
237 elif child
. name
== "iq" :
238 self
. discovery
. onIq ( child
)
240 def onMessage ( self
, el
):
241 fro
= el
. getAttribute ( "from" )
243 froj
= jid
. intern ( fro
)
245 LogEvent ( WARN
, "" , "Failed stringprep." )
247 mtype
= el
. getAttribute ( "type" )
248 s
= self
. sessions
. get ( froj
. userhost (), None )
249 if mtype
== "error" and s
:
250 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
254 elif mtype
!= "error" :
255 to
= el
. getAttribute ( "to" )
256 ulang
= utils
. getLang ( el
)
258 for child
in el
. elements ():
259 if child
. name
== "body" :
260 body
= child
.__ str
__ ()
261 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
262 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
264 def onPresence ( self
, el
):
265 fro
= el
. getAttribute ( "from" )
266 to
= el
. getAttribute ( "to" )
268 froj
= jid
. intern ( fro
)
271 LogEvent ( WARN
, "" , "Failed stringprep." )
274 ptype
= el
. getAttribute ( "type" )
275 s
= self
. sessions
. get ( froj
. userhost ())
276 if ptype
== "error" and s
:
277 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
282 ulang
= utils
. getLang ( el
)
283 ptype
= el
. getAttribute ( "type" )
285 # If the presence packet is to the transport (not a user) and there isn't already a session
286 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
287 LogEvent ( INFO
, "" , "Attempting to create a new session." )
288 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
290 self
. statistics
. stats
[ "TotalUsers" ] += 1
291 self
. sessions
[ froj
. userhost ()] = s
292 LogEvent ( INFO
, "" , "New session created." )
293 # Send the first presence
296 LogEvent ( INFO
, "" , "Failed to create session" )
297 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
299 elif el
. getAttribute ( "type" ) != "error" :
300 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
301 pres
= Element (( None , "presence" ))
302 pres
. attributes
[ "from" ] = to
303 pres
. attributes
[ "to" ] = fro
304 pres
. attributes
[ "type" ] = "unavailable"
308 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
309 # They haven't logged in, and are trying to change subscription to a user
310 # Lets log them in and then do it
311 LogEvent ( INFO
, "" , "Attempting to create a session to do subscription stuff." )
312 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
314 self
. sessions
[ froj
. userhost ()] = s
315 LogEvent ( INFO
, "" , "New session created." )
316 # Tell the session there's a new resource
317 s
. handleResourcePresence ( froj
. userhost (), froj
. resource
, toj
. userhost (), toj
. resource
, 0 , None , None , None )
318 # Send this subscription
324 # Check for any other instances
325 if config
. pid
and os
. name
!= "posix" :
328 twistd
. checkPID ( config
. pid
)
330 # Do any auto-update stuff
333 # Daemonise the process and write the PID file
334 if config
. background
and os
. name
== "posix" :
339 # Initialise debugging, and do the other half of SIGHUPstuff
341 legacy
. reloadConfig ()
345 if config
. useXCP
and config
. compjid
:
347 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
348 self
. transportSvc
= PyTransport ()
349 self
. transportSvc
. setServiceParent ( self
. c
)
350 self
. c
. startService ()
351 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
355 pid
= str ( os
. getpid ())
356 pf
= open ( config
. pid
, "w" )
357 pf
. write ( " %s \n " % pid
)
360 def shuttingDown ( self
):
361 self
. transportSvc
. removeMe ()
362 # Keep the transport running for another 3 seconds
363 def cb ( ignored
= None ):
365 twistd
. removePID ( config
. pid
)
368 reactor
. callLater ( 3.0 , d
. callback
, None )
373 def SIGHUPstuff (* args
):
374 global configFile
, configOptions
375 xmlconfig
. reloadConfig ( configFile
, configOptions
)
376 if config
. pid
and os
. name
!= "posix" :
379 legacy
. reloadConfig ()
381 if os
. name
== "posix" :
383 # Set SIGHUP to reload the config file & close & open debug file
384 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
385 # Load some scripts for PID and daemonising
386 from twisted
. scripts
import twistd
390 # Create the application
394 if __name__
== "__main__" :