]>
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
11 from twisted
. internet
import epollreactor
as bestreactor
14 from twisted
. internet
import kqreactor
as bestreactor
17 from twisted
. internet
import pollreactor
as bestreactor
20 from twisted
. internet
import default
as bestreactor
21 print "Unable to install any good reactors (kqueue, epoll, poll)."
22 print "We fell back to using select. You may have scalability problems."
23 print "This reactor will not support more than 1024 connections at a time."
25 print "Unable to find a reactor. \n Exiting..."
31 # Must load config before everything else
34 configFile
= "config.xml"
36 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtlp:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "pid=" , "help" ])
38 if o
in ( "-c" , "--config" ):
40 elif o
in ( "-b" , "--background" ):
41 config
. daemonise
= True
42 elif o
in ( "-d" , "--debug" ):
43 config
. debugLevel
= "2"
44 elif o
in ( "-D" , "--Debug" ):
45 config
. debugLevel
= "3"
46 elif o
in ( "-g" , "--garbage" ):
48 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
49 elif o
in ( "-t" , "--traceback" ):
50 config
. debugLevel
= "1"
51 elif o
in ( "-l" , "--log" ):
53 elif o
in ( "-p" , "--pid" ):
55 elif o
in ( "-o" , "--option" ):
56 var
, setting
= v
. split ( "=" , 2 )
57 configOptions
[ var
] = setting
58 elif o
in ( "-h" , "--help" ):
59 print " %s [options]" % sys
. argv
[ 0 ]
60 print " -h print this help"
61 print " -b daemonize/background transport"
62 print " -c <file> read configuration from this file"
63 print " -d print debugging output"
64 print " -D print extended debugging output"
65 print " -g print garbage collection output"
66 print " -t print debugging only on traceback"
67 print " -l <file> write debugging output to file"
68 print " -p <file> write process ID to file"
69 print " -o <var>=<setting> set config var to setting"
72 xmlconfig
. reloadConfig ( configFile
, configOptions
)
75 # They picked their own reactor. Lets install it.
76 del sys
. modules
[ "twisted.internet.reactor" ]
77 if config
. reactor
== "epoll" :
78 from twisted
. internet
import epollreactor
79 epollreactor
. install ()
80 elif config
. reactor
== "poll" :
81 from twisted
. internet
import pollreactor
83 elif config
. reactor
== "kqueue" :
84 from twisted
. internet
import kqreactor
86 elif len ( config
. reactor
) > 0 :
87 print "Unknown reactor: " , config
. reactor
, ". Using select(), reactor."
90 from twisted
. internet
import reactor
, task
91 from twisted
. internet
. defer
import Deferred
92 from tlib
. xmlw
import Element
, jid
, component
93 from debug
import LogEvent
, INFO
, WARN
, ERROR
111 class PyTransport ( component
. Service
):
115 # Discovery, as well as some builtin features
116 self
. discovery
= disco
. ServerDiscovery ( self
)
117 self
. discovery
. addIdentity ( "gateway" , legacy
. id , legacy
. name
, config
. jid
)
118 self
. discovery
. addIdentity ( "conference" , "text" , legacy
. name
+ " Chatrooms" , config
. jid
)
119 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
120 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
121 self
. discovery
. addIdentity ( "client" , "pc" , "MSN Messenger" , "USER" )
123 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
124 self
. avatarCache
= avatar
. AvatarCache ()
125 self
. registermanager
= register
. RegisterManager ( self
)
126 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
127 self
. versionTeller
= misciq
. VersionTeller ( self
)
128 self
. pingService
= misciq
. PingService ( self
)
129 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
130 self
. vCardFactory
= misciq
. VCardFactory ( self
)
131 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
132 self
. connectUsers
= misciq
. ConnectUsers ( self
)
133 if config
. ftJabberPort
:
134 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
135 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
137 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
138 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
139 self
. statistics
= misciq
. Statistics ( self
)
140 self
. startTime
= int ( time
. time ())
142 self
. xmlstream
= None
145 # Groupchat ID handling
147 self
. reservedIDs
= []
152 self
. loopCall
= task
. LoopingCall ( self
. loopCall
)
153 self
. loopCall
. start ( 60.0 )
157 for session
in self
. sessions
. copy ():
158 self
. sessions
[ session
]. removeMe ()
160 def makeMessageID ( self
):
162 return str ( self
. messageID
)
165 newID
= "r" + str ( self
. lastID
)
167 if self
. reservedIDs
. count ( newID
) > 0 :
168 # Ack, it's already used.. Try again
173 def reserveID ( self
, ID
):
174 self
. reservedIDs
. append ( ID
)
177 numsessions
= len ( self
. sessions
)
179 #if config.debugOn and numsessions > 0:
181 # for key in self.sessions:
182 # print "\t" + self.sessions[key].jabberID
184 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
185 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
186 legacy
. updateStats ( self
. statistics
)
188 oldDict
= self
. sessions
. copy ()
193 LogEvent ( WARN
, "" , "Ghost session found." )
194 # Don't add it to the new dictionary. Effectively removing it
196 self
. sessions
[ key
] = s
198 def componentConnected ( self
, xmlstream
):
200 self
. xmlstream
= xmlstream
201 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
202 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
203 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
204 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
206 pres
= Element (( None , "presence" ))
207 pres
. attributes
[ "to" ] = "presence@-internal"
208 pres
. attributes
[ "from" ] = config
. compjid
209 x
= pres
. addElement ( "x" )
210 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
211 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
212 x
. attributes
[ "config:version" ] = "1"
213 x
. attributes
[ "protocol-version" ] = "1.0"
214 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
217 def componentDisconnected ( self
):
219 self
. xmlstream
= None
221 def onRouteMessage ( self
, el
):
222 for child
in el
. elements ():
223 if child
. name
== "message" :
224 self
. onMessage ( child
)
225 elif child
. name
== "presence" :
226 # Ignore any presence broadcasts about other XCP components
227 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
228 self
. onPresence ( child
)
229 elif child
. name
== "iq" :
230 self
. discovery
. onIq ( child
)
232 def onMessage ( self
, el
):
233 fro
= el
. getAttribute ( "from" )
235 froj
= jid
. intern ( fro
)
237 LogEvent ( WARN
, "" , "Failed stringprep." )
239 mtype
= el
. getAttribute ( "type" )
240 s
= self
. sessions
. get ( froj
. userhost (), None )
241 if mtype
== "error" and s
:
246 to
= el
. getAttribute ( "to" )
247 ulang
= utils
. getLang ( el
)
249 for child
in el
. elements ():
250 if child
. name
== "body" :
251 body
= child
.__ str
__ ()
252 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
253 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
255 def onPresence ( self
, el
):
256 fro
= el
. getAttribute ( "from" )
257 to
= el
. getAttribute ( "to" )
259 froj
= jid
. intern ( fro
)
262 LogEvent ( WARN
, "" , "Failed stringprep." )
265 s
= self
. sessions
. get ( froj
. userhost ())
269 ulang
= utils
. getLang ( el
)
270 ptype
= el
. getAttribute ( "type" )
272 # If the presence packet is to the transport (not a user) and there isn't already a session
273 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
274 LogEvent ( INFO
, "" , "Attempting to create a new session." )
275 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
277 self
. statistics
. stats
[ "TotalUsers" ] += 1
278 self
. sessions
[ froj
. userhost ()] = s
279 LogEvent ( INFO
, "" , "New session created." )
280 # Send the first presence
283 LogEvent ( INFO
, "" , "Failed to create session" )
284 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
286 elif el
. getAttribute ( "type" ) != "error" :
287 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
288 pres
= Element (( None , "presence" ))
289 pres
. attributes
[ "from" ] = to
290 pres
. attributes
[ "to" ] = fro
291 pres
. attributes
[ "type" ] = "unavailable"
295 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
296 # They haven't logged in, and are trying to change subscription to a user
297 # Lets log them in and then do it
298 LogEvent ( INFO
, "" , "Attempting to create a session to do subscription stuff." )
299 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
301 self
. sessions
[ froj
. userhost ()] = s
302 LogEvent ( INFO
, "" , "New session created." )
303 # Tell the session there's a new resource
304 s
. handleResourcePresence ( froj
. userhost (), froj
. resource
, toj
. userhost (), toj
. resource
, 0 , None , None , None )
305 # Send this subscription
311 # Check for any other instances
312 if config
. pid
and os
. name
!= "posix" :
315 twistd
. checkPID ( config
. pid
)
317 # Do any auto-update stuff
320 # Daemonise the process and write the PID file
321 if config
. background
and os
. name
== "posix" :
326 # Initialise debugging, and do the other half of SIGHUPstuff
328 legacy
. reloadConfig ()
332 if config
. useXCP
and config
. compjid
:
334 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
335 self
. transportSvc
= PyTransport ()
336 self
. transportSvc
. setServiceParent ( self
. c
)
337 self
. c
. startService ()
338 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
342 pid
= str ( os
. getpid ())
343 pf
= open ( config
. pid
, "w" )
344 pf
. write ( " %s \n " % pid
)
347 def shuttingDown ( self
):
348 self
. transportSvc
. removeMe ()
349 # Keep the transport running for another 3 seconds
350 def cb ( ignored
= None ):
352 twistd
. removePID ( config
. pid
)
355 reactor
. callLater ( 3.0 , d
. callback
, None )
360 def SIGHUPstuff (* args
):
361 global configFile
, configOptions
362 xmlconfig
. reloadConfig ( configFile
, configOptions
)
363 if config
. pid
and os
. name
!= "posix" :
366 legacy
. reloadConfig ()
368 if os
. name
== "posix" :
370 # Set SIGHUP to reload the config file & close & open debug file
371 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
372 # Load some scripts for PID and daemonising
373 from twisted
. scripts
import twistd
377 # Create the application
381 if __name__
== "__main__" :