]>
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
114 class PyTransport ( component
. Service
):
118 LogEvent ( INFO
, msg
= "SVN r" + str ( svninfo
. getSVNVersion ()))
121 LogEvent ( INFO
, msg
= "Reactor: " + str ( reactor
))
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" )
130 self
. discovery
. addIdentity ( "conference" , "text" , "MSN Groupchat" , "ROOM" )
132 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
133 self
. avatarCache
= avatar
. AvatarCache ()
134 self
. registermanager
= register
. RegisterManager ( self
)
135 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
136 self
. versionTeller
= misciq
. VersionTeller ( self
)
137 self
. pingService
= misciq
. PingService ( self
)
138 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
139 self
. vCardFactory
= misciq
. VCardFactory ( self
)
140 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
141 self
. connectUsers
= misciq
. ConnectUsers ( self
)
142 if config
. ftJabberPort
:
143 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
144 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
146 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
147 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
148 self
. statistics
= misciq
. Statistics ( self
)
149 self
. startTime
= int ( time
. time ())
151 self
. xmlstream
= None
154 # Groupchat ID handling
156 self
. reservedIDs
= []
161 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
162 self
. loopTask
. start ( 60.0 )
166 for session
in self
. sessions
. copy ():
167 self
. sessions
[ session
]. removeMe ()
169 def makeMessageID ( self
):
171 return str ( self
. messageID
)
174 newID
= "r" + str ( self
. lastID
)
176 if self
. reservedIDs
. count ( newID
) > 0 :
177 # Ack, it's already used.. Try again
182 def reserveID ( self
, ID
):
183 self
. reservedIDs
. append ( ID
)
186 numsessions
= len ( self
. sessions
)
188 #if config.debugOn and numsessions > 0:
190 # for key in self.sessions:
191 # print "\t" + self.sessions[key].jabberID
193 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
194 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
195 legacy
. updateStats ( self
. statistics
)
197 oldDict
= self
. sessions
. copy ()
202 LogEvent ( WARN
, "" , "Ghost session found." )
203 # Don't add it to the new dictionary. Effectively removing it
205 self
. sessions
[ key
] = s
207 def componentConnected ( self
, xmlstream
):
209 self
. xmlstream
= xmlstream
210 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
211 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
212 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
213 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
215 pres
= Element (( None , "presence" ))
216 pres
. attributes
[ "to" ] = "presence@-internal"
217 pres
. attributes
[ "from" ] = config
. compjid
218 x
= pres
. addElement ( "x" )
219 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
220 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
221 x
. attributes
[ "config:version" ] = "1"
222 x
. attributes
[ "protocol-version" ] = "1.0"
223 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
226 def componentDisconnected ( self
):
228 self
. xmlstream
= None
230 def onRouteMessage ( self
, el
):
231 for child
in el
. elements ():
232 if child
. name
== "message" :
233 self
. onMessage ( child
)
234 elif child
. name
== "presence" :
235 # Ignore any presence broadcasts about other XCP components
236 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
237 self
. onPresence ( child
)
238 elif child
. name
== "iq" :
239 self
. discovery
. onIq ( child
)
241 def onMessage ( self
, el
):
242 fro
= el
. getAttribute ( "from" )
244 froj
= internJID ( fro
)
246 LogEvent ( WARN
, "" , "Failed stringprep." )
248 mtype
= el
. getAttribute ( "type" )
249 s
= self
. sessions
. get ( froj
. userhost (), None )
250 if mtype
== "error" and s
:
251 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
255 elif mtype
!= "error" :
256 to
= el
. getAttribute ( "to" )
257 ulang
= utils
. getLang ( el
)
259 for child
in el
. elements ():
260 if child
. name
== "body" :
261 body
= child
.__ str
__ ()
262 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
263 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
264 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
266 def onPresence ( self
, el
):
267 fro
= el
. getAttribute ( "from" )
268 to
= el
. getAttribute ( "to" )
270 froj
= internJID ( fro
)
273 LogEvent ( WARN
, "" , "Failed stringprep." )
276 ptype
= el
. getAttribute ( "type" )
277 s
= self
. sessions
. get ( froj
. userhost ())
278 if ptype
== "error" and s
:
279 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
284 ulang
= utils
. getLang ( el
)
285 ptype
= el
. getAttribute ( "type" )
287 # If the presence packet is to the transport (not a user) and there isn't already a session
288 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
289 LogEvent ( INFO
, "" , "Attempting to create a new session." )
290 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
292 self
. statistics
. stats
[ "TotalUsers" ] += 1
293 self
. sessions
[ froj
. userhost ()] = s
294 LogEvent ( INFO
, "" , "New session created." )
295 # Send the first presence
298 LogEvent ( INFO
, "" , "Failed to create session" )
299 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
301 elif el
. getAttribute ( "type" ) != "error" :
302 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
303 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
306 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
307 # They haven't logged in, and are trying to change subscription to a user
308 # No, lets not log them in. Lets send an error :)
309 jabw
. sendPresence ( self
, fro
, to
, ptype
= "error" )
311 # Lets log them in and then do it
312 #LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
313 #s = session.makeSession(self, froj.userhost(), ulang)
315 # self.sessions[froj.userhost()] = s
316 # LogEvent(INFO, "", "New session created.")
317 # # Tell the session there's a new resource
318 # s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
319 # # Send this subscription
325 # Check for any other instances
326 if config
. pid
and os
. name
!= "posix" :
329 twistd
. checkPID ( config
. pid
)
331 # Do any auto-update stuff
334 # Daemonise the process and write the PID file
335 if config
. background
and os
. name
== "posix" :
340 # Initialise debugging, and do the other half of SIGHUPstuff
342 legacy
. reloadConfig ()
346 if config
. useXCP
and config
. compjid
:
348 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
349 self
. transportSvc
= PyTransport ()
350 self
. transportSvc
. setServiceParent ( self
. c
)
351 self
. c
. startService ()
352 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
356 pid
= str ( os
. getpid ())
357 pf
= open ( config
. pid
, "w" )
358 pf
. write ( " %s \n " % pid
)
361 def shuttingDown ( self
):
362 self
. transportSvc
. removeMe ()
363 # Keep the transport running for another 3 seconds
364 def cb ( ignored
= None ):
366 twistd
. removePID ( config
. pid
)
369 reactor
. callLater ( 3.0 , d
. callback
, None )
374 def SIGHUPstuff (* args
):
375 global configFile
, configOptions
376 xmlconfig
. reloadConfig ( configFile
, configOptions
)
377 if config
. pid
and os
. name
!= "posix" :
380 legacy
. reloadConfig ()
382 if os
. name
== "posix" :
384 # Set SIGHUP to reload the config file & close & open debug file
385 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
386 # Load some scripts for PID and daemonising
387 from twisted
. scripts
import _twistd_unix
as twistd
391 # Create the application
395 if __name__
== "__main__" :