]>
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."
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 ()))
128 LogEvent ( INFO
, msg
= "Reactor: " + str ( reactor
))
130 # Discovery, as well as some builtin features
131 self
. discovery
= disco
. ServerDiscovery ( self
)
132 self
. discovery
. addIdentity ( "gateway" , legacy
. id , config
. discoName
, config
. jid
)
133 self
. discovery
. addIdentity ( "conference" , "text" , config
. discoName
+ " Chatrooms" , config
. jid
)
134 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
135 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
136 self
. discovery
. addIdentity ( "client" , "pc" , "MSN Messenger" , "USER" )
138 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
139 self
. avatarCache
= avatar
. AvatarCache ()
140 self
. registermanager
= register
. RegisterManager ( self
)
141 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
142 self
. versionTeller
= misciq
. VersionTeller ( self
)
143 self
. pingService
= misciq
. PingService ( self
)
144 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
145 self
. vCardFactory
= misciq
. VCardFactory ( self
)
146 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
147 self
. connectUsers
= misciq
. ConnectUsers ( self
)
148 if config
. ftJabberPort
:
149 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
150 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
152 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
153 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
154 self
. statistics
= misciq
. Statistics ( self
)
155 self
. startTime
= int ( time
. time ())
157 self
. xmlstream
= None
160 # Groupchat ID handling
162 self
. reservedIDs
= []
167 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
168 self
. loopTask
. start ( 60.0 )
172 for session
in self
. sessions
. copy ():
173 self
. sessions
[ session
]. removeMe ()
175 def makeMessageID ( self
):
177 return str ( self
. messageID
)
180 newID
= "r" + str ( self
. lastID
)
182 if self
. reservedIDs
. count ( newID
) > 0 :
183 # Ack, it's already used.. Try again
188 def reserveID ( self
, ID
):
189 self
. reservedIDs
. append ( ID
)
192 numsessions
= len ( self
. sessions
)
194 #if config.debugOn and numsessions > 0:
196 # for key in self.sessions:
197 # print "\t" + self.sessions[key].jabberID
199 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
200 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
201 legacy
. updateStats ( self
. statistics
)
203 oldDict
= self
. sessions
. copy ()
208 LogEvent ( WARN
, "" , "Ghost session found." )
209 # Don't add it to the new dictionary. Effectively removing it
211 self
. sessions
[ key
] = s
213 def componentConnected ( self
, xmlstream
):
215 self
. xmlstream
= xmlstream
216 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
217 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
218 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
219 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
221 pres
= Element (( None , "presence" ))
222 pres
. attributes
[ "to" ] = "presence@-internal"
223 pres
. attributes
[ "from" ] = config
. compjid
224 x
= pres
. addElement ( "x" )
225 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
226 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
227 x
. attributes
[ "config:version" ] = "1"
228 x
. attributes
[ "protocol-version" ] = "1.0"
229 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
232 def componentDisconnected ( self
):
234 self
. xmlstream
= None
236 def onRouteMessage ( self
, el
):
237 for child
in el
. elements ():
238 if child
. name
== "message" :
239 self
. onMessage ( child
)
240 elif child
. name
== "presence" :
241 # Ignore any presence broadcasts about other XCP components
242 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
243 self
. onPresence ( child
)
244 elif child
. name
== "iq" :
245 self
. discovery
. onIq ( child
)
247 def onMessage ( self
, el
):
248 fro
= el
. getAttribute ( "from" )
250 froj
= internJID ( fro
)
252 LogEvent ( WARN
, "" , "Failed stringprep." )
254 mtype
= el
. getAttribute ( "type" )
255 s
= self
. sessions
. get ( froj
. userhost (), None )
256 if mtype
== "error" and s
:
257 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
261 elif mtype
!= "error" :
262 to
= el
. getAttribute ( "to" )
263 ulang
= utils
. getLang ( el
)
265 for child
in el
. elements ():
266 if child
. name
== "body" :
267 body
= child
.__ str
__ ()
268 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
269 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
270 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
272 def onPresence ( self
, el
):
273 fro
= el
. getAttribute ( "from" )
274 to
= el
. getAttribute ( "to" )
276 froj
= internJID ( fro
)
279 LogEvent ( WARN
, "" , "Failed stringprep." )
282 ptype
= el
. getAttribute ( "type" )
283 s
= self
. sessions
. get ( froj
. userhost ())
284 if ptype
== "error" and s
:
285 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
290 ulang
= utils
. getLang ( el
)
291 ptype
= el
. getAttribute ( "type" )
293 # If the presence packet is to the transport (not a user) and there isn't already a session
294 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
295 LogEvent ( INFO
, "" , "Attempting to create a new session." )
296 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
298 self
. statistics
. stats
[ "TotalUsers" ] += 1
299 self
. sessions
[ froj
. userhost ()] = s
300 LogEvent ( INFO
, "" , "New session created." )
301 # Send the first presence
304 LogEvent ( INFO
, "" , "Failed to create session" )
305 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
307 elif el
. getAttribute ( "type" ) != "error" :
308 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
309 jabw
. sendPresence ( self
, fro
, to
, ptype
= "unavailable" )
312 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
313 # They haven't logged in, and are trying to change subscription to a user
314 # No, lets not log them in. Lets send an error :)
315 jabw
. sendPresence ( self
, fro
, to
, ptype
= "error" )
317 # Lets log them in and then do it
318 #LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
319 #s = session.makeSession(self, froj.userhost(), ulang)
321 # self.sessions[froj.userhost()] = s
322 # LogEvent(INFO, "", "New session created.")
323 # # Tell the session there's a new resource
324 # s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
325 # # Send this subscription
331 # Check for any other instances
332 if config
. pid
and os
. name
!= "posix" :
335 twistd
. checkPID ( config
. pid
)
337 # Do any auto-update stuff
340 # Daemonise the process and write the PID file
341 if config
. background
and os
. name
== "posix" :
346 # Initialise debugging, and do the other half of SIGHUPstuff
348 legacy
. reloadConfig ()
352 if config
. useXCP
and config
. compjid
:
354 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
355 self
. transportSvc
= PyTransport ()
356 self
. transportSvc
. setServiceParent ( self
. c
)
357 self
. c
. startService ()
358 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
362 pid
= str ( os
. getpid ())
363 pf
= open ( config
. pid
, "w" )
364 pf
. write ( " %s \n " % pid
)
367 def shuttingDown ( self
):
368 self
. transportSvc
. removeMe ()
369 # Keep the transport running for another 3 seconds
370 def cb ( ignored
= None ):
372 twistd
. removePID ( config
. pid
)
375 reactor
. callLater ( 3.0 , d
. callback
, None )
380 def SIGHUPstuff (* args
):
381 global configFile
, configOptions
382 xmlconfig
. reloadConfig ( configFile
, configOptions
)
383 if config
. pid
and os
. name
!= "posix" :
386 legacy
. reloadConfig ()
388 if os
. name
== "posix" :
390 # Set SIGHUP to reload the config file & close & open debug file
391 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
392 # Load some scripts for PID and daemonising
393 from twisted
. scripts
import _twistd_unix
as twistd
397 # Create the application
401 if __name__
== "__main__" :