]>
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
)
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."
12 reactors
= [( "epollreactor" , True ), ( "pollreactor" , True ), ( "selectreactor" , False ), ( "default" , False )]
13 for tryReactor
, good
in reactors
:
15 bestReactor
= __import__ ( "twisted.internet." + tryReactor
)
17 print >> sys
. stderr
, selectWarning
22 print >> sys
. stderr
, "Unable to find a reactor. Please make sure you have Twisted properly installed. \n Exiting..."
30 # Must load config before everything else
33 configFile
= "config.xml"
35 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtlp:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "pid=" , "help" ])
37 if o
in ( "-c" , "--config" ):
39 elif o
in ( "-b" , "--background" ):
40 config
. background
= True
41 elif o
in ( "-d" , "--debug" ):
42 config
. debugLevel
= "2"
43 elif o
in ( "-D" , "--Debug" ):
44 config
. debugLevel
= "3"
45 elif o
in ( "-g" , "--garbage" ):
47 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
48 elif o
in ( "-t" , "--traceback" ):
49 config
. debugLevel
= "1"
50 elif o
in ( "-l" , "--log" ):
52 elif o
in ( "-p" , "--pid" ):
54 elif o
in ( "-o" , "--option" ):
55 var
, setting
= v
. split ( "=" , 2 )
56 configOptions
[ var
] = setting
57 elif o
in ( "-h" , "--help" ):
58 print " %s [options]" % sys
. argv
[ 0 ]
59 print " -h print this help"
60 print " -b daemonize/background transport"
61 print " -c <file> read configuration from this file"
62 print " -d print debugging output"
63 print " -D print extended debugging output"
64 print " -g print garbage collection output"
65 print " -t print debugging only on traceback"
66 print " -l <file> write debugging output to file"
67 print " -p <file> write process ID to file"
68 print " -o <var>=<setting> set config var to setting"
71 xmlconfig
. reloadConfig ( configFile
, configOptions
)
74 # They picked their own reactor. Lets install it.
75 del sys
. modules
[ "twisted.internet.reactor" ]
76 if config
. reactor
== "epoll" :
77 from twisted
. internet
import epollreactor
78 epollreactor
. install ()
79 elif config
. reactor
== "poll" :
80 from twisted
. internet
import pollreactor
82 elif config
. reactor
== "kqueue" :
83 from twisted
. internet
import kqreactor
85 elif len ( config
. reactor
) > 0 :
86 print >> sys
. stderr
, "Unknown reactor: " , config
. reactor
, ". Using best available reactor."
89 from twisted
. internet
import reactor
, task
90 from twisted
. internet
. defer
import Deferred
91 from twisted
. words
. xish
. domish
import Element
92 from twisted
. words
. protocols
. jabber
import component
93 from twisted
. words
. protocols
. jabber
. jid
import internJID
95 from debug
import LogEvent
, INFO
, WARN
, ERROR
113 class PyTransport ( component
. Service
):
116 LogEvent ( INFO
, msg
= "Reactor: " + str ( reactor
))
118 # Discovery, as well as some builtin features
119 self
. discovery
= disco
. ServerDiscovery ( self
)
120 self
. discovery
. addIdentity ( "gateway" , legacy
. id , config
. discoName
, config
. jid
)
121 self
. discovery
. addIdentity ( "conference" , "text" , config
. discoName
+ " Chatrooms" , config
. jid
)
122 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
123 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
124 self
. discovery
. addIdentity ( "client" , "pc" , "MSN Messenger" , "USER" )
125 self
. discovery
. addIdentity ( "conference" , "text" , "MSN Groupchat" , "ROOM" )
127 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
128 self
. avatarCache
= avatar
. AvatarCache ()
129 self
. registermanager
= register
. RegisterManager ( self
)
130 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
131 self
. versionTeller
= misciq
. VersionTeller ( self
)
132 self
. pingService
= misciq
. PingService ( self
)
133 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
134 self
. vCardFactory
= misciq
. VCardFactory ( self
)
135 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
136 self
. connectUsers
= misciq
. ConnectUsers ( self
)
137 if config
. ftJabberPort
:
138 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
139 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
141 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
142 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
143 self
. statistics
= misciq
. Statistics ( self
)
144 self
. startTime
= int ( time
. time ())
146 self
. xmlstream
= None
149 # Groupchat ID handling
151 self
. reservedIDs
= []
156 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
157 self
. loopTask
. start ( 60.0 )
161 for session
in self
. sessions
. copy ():
162 self
. sessions
[ session
]. removeMe ()
164 def makeMessageID ( self
):
166 return str ( self
. messageID
)
169 newID
= "r" + str ( self
. lastID
)
171 if self
. reservedIDs
. count ( newID
) > 0 :
172 # Ack, it's already used.. Try again
177 def reserveID ( self
, ID
):
178 self
. reservedIDs
. append ( ID
)
181 numsessions
= len ( self
. sessions
)
183 #if config.debugOn and numsessions > 0:
185 # for key in self.sessions:
186 # print "\t" + self.sessions[key].jabberID
188 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
189 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
190 legacy
. updateStats ( self
. statistics
)
192 oldDict
= self
. sessions
. copy ()
197 LogEvent ( WARN
, "" , "Ghost session found." )
198 # Don't add it to the new dictionary. Effectively removing it
200 self
. sessions
[ key
] = s
202 def componentConnected ( self
, xmlstream
):
204 self
. xmlstream
= xmlstream
205 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
206 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
207 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
208 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
210 pres
= Element (( None , "presence" ))
211 pres
. attributes
[ "to" ] = "presence@-internal"
212 pres
. attributes
[ "from" ] = config
. compjid
213 x
= pres
. addElement ( "x" )
214 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
215 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
216 x
. attributes
[ "config:version" ] = "1"
217 x
. attributes
[ "protocol-version" ] = "1.0"
218 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
221 def componentDisconnected ( self
):
223 self
. xmlstream
= None
225 def onRouteMessage ( self
, el
):
226 for child
in el
. elements ():
227 if child
. name
== "message" :
228 self
. onMessage ( child
)
229 elif child
. name
== "presence" :
230 # Ignore any presence broadcasts about other XCP components
231 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
232 self
. onPresence ( child
)
233 elif child
. name
== "iq" :
234 self
. discovery
. onIq ( child
)
236 def onMessage ( self
, el
):
237 fro
= el
. getAttribute ( "from" )
239 froj
= internJID ( fro
)
241 LogEvent ( WARN
, "" , "Failed stringprep." )
243 mtype
= el
. getAttribute ( "type" )
244 s
= self
. sessions
. get ( froj
. userhost (), None )
245 if mtype
== "error" and s
:
246 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
250 elif mtype
!= "error" :
251 to
= el
. getAttribute ( "to" )
252 ulang
= utils
. getLang ( el
)
254 for child
in el
. elements ():
255 if child
. name
== "body" :
256 body
= child
.__ str
__ ()
257 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
258 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
259 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
261 def onPresence ( self
, el
):
262 fro
= el
. getAttribute ( "from" )
263 to
= el
. getAttribute ( "to" )
265 froj
= internJID ( fro
)
268 LogEvent ( WARN
, "" , "Failed stringprep." )
271 ptype
= el
. getAttribute ( "type" )
272 s
= self
. sessions
. get ( froj
. userhost ())
273 if ptype
== "error" and s
:
274 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
279 ulang
= utils
. getLang ( el
)
280 ptype
= el
. getAttribute ( "type" )
282 # If the presence packet is to the transport (not a user) and there isn't already a session
283 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
284 LogEvent ( INFO
, "" , "Attempting to create a new session." )
285 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
287 self
. statistics
. stats
[ "TotalUsers" ] += 1
288 self
. sessions
[ froj
. userhost ()] = s
289 LogEvent ( INFO
, "" , "New session created." )
290 # Send the first presence
293 LogEvent ( INFO
, "" , "Failed to create session" )
294 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
296 elif el
. getAttribute ( "type" ) != "error" :
297 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
298 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
301 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
302 # They haven't logged in, and are trying to change subscription to a user
303 # No, lets not log them in. Lets send an error :)
304 jabw
. sendPresence ( self
, fro
, to
, ptype
= "error" )
306 # Lets log them in and then do it
307 #LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
308 #s = session.makeSession(self, froj.userhost(), ulang)
310 # self.sessions[froj.userhost()] = s
311 # LogEvent(INFO, "", "New session created.")
312 # # Tell the session there's a new resource
313 # s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
314 # # Send this subscription
320 # Check for any other instances
321 if config
. pid
and os
. name
!= "posix" :
324 twistd
. checkPID ( config
. pid
)
326 # Do any auto-update stuff
329 # Daemonise the process and write the PID file
330 if config
. background
and os
. name
== "posix" :
335 # Initialise debugging, and do the other half of SIGHUPstuff
337 legacy
. reloadConfig ()
341 if config
. useXCP
and config
. compjid
:
343 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
344 self
. transportSvc
= PyTransport ()
345 self
. transportSvc
. setServiceParent ( self
. c
)
346 self
. c
. startService ()
347 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
351 pid
= str ( os
. getpid ())
352 pf
= open ( config
. pid
, "w" )
353 pf
. write ( " %s \n " % pid
)
356 def shuttingDown ( self
):
357 self
. transportSvc
. removeMe ()
358 # Keep the transport running for another 3 seconds
359 def cb ( ignored
= None ):
361 twistd
. removePID ( config
. pid
)
364 reactor
. callLater ( 3.0 , d
. callback
, None )
369 def SIGHUPstuff (* args
):
370 global configFile
, configOptions
371 xmlconfig
. reloadConfig ( configFile
, configOptions
)
372 if config
. pid
and os
. name
!= "posix" :
375 legacy
. reloadConfig ()
377 if os
. name
== "posix" :
379 # Set SIGHUP to reload the config file & close & open debug file
380 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
381 # Load some scripts for PID and daemonising
382 from twisted
. scripts
import _twistd_unix
as twistd
386 # Create the application
390 if __name__
== "__main__" :