]>
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 # Must load config before everything else
12 configFile
= "config.xml"
14 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtl:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "help" ])
16 if o
in ( "-c" , "--config" ):
18 elif o
in ( "-b" , "--background" ):
19 config
. daemonise
= True
20 elif o
in ( "-d" , "--debug" ):
21 config
. debugLevel
= "2"
22 elif o
in ( "-D" , "--Debug" ):
23 config
. debugLevel
= "3"
24 elif o
in ( "-g" , "--garbage" ):
26 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
27 elif o
in ( "-t" , "--traceback" ):
28 config
. debugLevel
= "1"
29 elif o
in ( "-l" , "--log" ):
31 elif o
in ( "-o" , "--option" ):
32 var
, setting
= v
. split ( "=" , 2 )
33 configOptions
[ var
] = setting
34 elif o
in ( "-h" , "--help" ):
35 print " %s [options]" % sys
. argv
[ 0 ]
36 print " -h print this help"
37 print " -b daemonize/background transport"
38 print " -c <file> read configuration from this file"
39 print " -d print debugging output"
40 print " -D print extended debugging output"
41 print " -g print garbage collection output"
42 print " -t print debugging only on traceback"
43 print " -l <file> write debugging output to file"
44 print " -o <var>=<setting> set config var to setting"
47 xmlconfig
. reloadConfig ( configFile
, configOptions
)
49 del sys
. modules
[ "twisted.internet.reactor" ]
51 if config
. reactor
== "epoll" :
52 from twisted
. internet
import epollreactor
53 epollreactor
. install ()
54 elif config
. reactor
== "poll" :
55 from twisted
. internet
import pollreactor
57 elif config
. reactor
== "kqueue" :
58 from twisted
. internet
import kqreactor
60 elif len ( config
. reactor
) > 0 :
61 print "Unknown reactor: " , config
. reactor
, ". Using default, select(), reactor."
64 from twisted
. internet
import reactor
, task
65 from twisted
. internet
. defer
import Deferred
66 from tlib
. xmlw
import Element
, jid
, component
67 from debug
import LogEvent
, INFO
, WARN
, ERROR
85 class PyTransport ( component
. Service
):
89 # Discovery, as well as some builtin features
90 self
. discovery
= disco
. ServerDiscovery ( self
)
91 self
. discovery
. addIdentity ( "gateway" , legacy
. id , legacy
. name
, config
. jid
)
92 self
. discovery
. addIdentity ( "conference" , "text" , legacy
. name
+ " Chatrooms" , config
. jid
)
93 self
. discovery
. addFeature ( disco
. XCONFERENCE
, None , config
. jid
) # So that clients know you can create groupchat rooms on the server
94 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
96 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
97 self
. avatarCache
= avatar
. AvatarCache ()
98 self
. registermanager
= register
. RegisterManager ( self
)
99 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
100 self
. versionTeller
= misciq
. VersionTeller ( self
)
101 self
. pingService
= misciq
. PingService ( self
)
102 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
103 self
. vCardFactory
= misciq
. VCardFactory ( self
)
104 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
105 self
. connectUsers
= misciq
. ConnectUsers ( self
)
106 if config
. ftJabberPort
:
107 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
108 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
110 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
111 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
112 self
. statistics
= misciq
. Statistics ( self
)
113 self
. startTime
= int ( time
. time ())
115 self
. xmlstream
= None
118 # Groupchat ID handling
120 self
. reservedIDs
= []
125 self
. loopCall
= task
. LoopingCall ( self
. loopCall
)
126 self
. loopCall
. start ( 60.0 )
130 for session
in self
. sessions
. copy ():
131 self
. sessions
[ session
]. removeMe ()
133 def makeMessageID ( self
):
135 return str ( self
. messageID
)
138 newID
= "r" + str ( self
. lastID
)
140 if self
. reservedIDs
. count ( newID
) > 0 :
141 # Ack, it's already used.. Try again
146 def reserveID ( self
, ID
):
147 self
. reservedIDs
. append ( ID
)
150 numsessions
= len ( self
. sessions
)
152 #if config.debugOn and numsessions > 0:
154 # for key in self.sessions:
155 # print "\t" + self.sessions[key].jabberID
157 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
158 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
159 legacy
. updateStats ( self
. statistics
)
161 oldDict
= self
. sessions
. copy ()
164 session
= oldDict
[ key
]
165 if not session
. alive
:
166 LogEvent ( WARN
, "" , "Ghost session found." )
167 # Don't add it to the new dictionary. Effectively removing it
169 self
. sessions
[ key
] = session
171 def componentConnected ( self
, xmlstream
):
173 self
. xmlstream
= xmlstream
174 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
175 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
176 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
177 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
179 pres
= Element (( None , "presence" ))
180 pres
. attributes
[ "to" ] = "presence@-internal"
181 pres
. attributes
[ "from" ] = config
. compjid
182 x
= pres
. addElement ( "x" )
183 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
184 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
185 x
. attributes
[ "config:version" ] = "1"
186 x
. attributes
[ "protocol-version" ] = "1.0"
187 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
190 def componentDisconnected ( self
):
192 self
. xmlstream
= None
194 def onRouteMessage ( self
, el
):
195 for child
in el
. elements ():
196 if child
. name
== "message" :
197 self
. onMessage ( child
)
198 elif child
. name
== "presence" :
199 # Ignore any presence broadcasts about other XCP components
200 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
201 self
. onPresence ( child
)
202 elif child
. name
== "iq" :
203 self
. discovery
. onIq ( child
)
205 def onMessage ( self
, el
):
206 fro
= el
. getAttribute ( "from" )
208 froj
= jid
. intern ( fro
)
210 LogEvent ( WARN
, "" , "Failed stringprep." )
212 mtype
= el
. getAttribute ( "type" )
213 if self
. sessions
. has_key ( froj
. userhost ()):
214 self
. sessions
[ froj
. userhost ()]. onMessage ( el
)
215 elif mtype
!= "error" :
216 to
= el
. getAttribute ( "to" )
217 ulang
= utils
. getLang ( el
)
219 for child
in el
. elements ():
220 if child
. name
== "body" :
221 body
= child
.__ str
__ ()
222 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
223 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
225 def onPresence ( self
, el
):
226 fro
= el
. getAttribute ( "from" )
227 to
= el
. getAttribute ( "to" )
229 froj
= jid
. intern ( fro
)
232 LogEvent ( WARN
, "" , "Failed stringprep." )
235 if self
. sessions
. has_key ( froj
. userhost ()):
236 self
. sessions
[ froj
. userhost ()]. onPresence ( el
)
238 ulang
= utils
. getLang ( el
)
239 ptype
= el
. getAttribute ( "type" )
241 # If the presence packet is to the transport (not a user) and there isn't already a session
242 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
243 LogEvent ( INFO
, "" , "Attempting to create a new session." )
244 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
246 self
. statistics
. stats
[ "TotalUsers" ] += 1
247 self
. sessions
[ froj
. userhost ()] = s
248 LogEvent ( INFO
, "" , "New session created." )
249 # Send the first presence
252 LogEvent ( INFO
, "" , "Failed to create session" )
253 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
255 elif el
. getAttribute ( "type" ) != "error" :
256 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
257 pres
= Element (( None , "presence" ))
258 pres
. attributes
[ "from" ] = to
259 pres
. attributes
[ "to" ] = fro
260 pres
. attributes
[ "type" ] = "unavailable"
264 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
265 # They haven't logged in, and are trying to change subscription to a user
266 # Lets log them in and then do it
267 LogEvent ( INFO
, "" , "Attempting to create a session to do subscription stuff." )
268 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
270 self
. sessions
[ froj
. userhost ()] = s
271 LogEvent ( INFO
, "" , "New session created." )
272 # Tell the session there's a new resource
273 s
. handleResourcePresence ( froj
. userhost (), froj
. resource
, toj
. userhost (), toj
. resource
, 0 , None , None , None )
274 # Send this subscription
280 # Check for any other instances
284 # Do any auto-update stuff
287 # Daemonise the process and write the PID file
288 if config
. background
:
293 # Initialise debugging, and do the other half of SIGHUPstuff
295 legacy
. reloadConfig ()
299 if config
. useXCP
and config
. compjid
:
301 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
302 self
. transportSvc
= PyTransport ()
303 self
. transportSvc
. setServiceParent ( self
. c
)
304 self
. c
. startService ()
305 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
308 # Check that we're not already running
309 if os
. path
. isfile ( config
. pid
):
310 if os
. name
== "posix" :
311 pf
= open ( config
. pid
)
312 pid
= int ( str ( pf
. readline (). strip ()))
315 os
. kill ( pid
, signal
. SIGHUP
)
316 self
. alreadyRunning ()
320 self
. alreadyRunning ()
324 pid
= str ( os
. getpid ())
325 pf
= open ( config
. pid
, "w" )
326 pf
. write ( " %s \n " % pid
)
329 def alreadyRunning ( self
):
330 sys
.__ stdout
__ . write ( "There is already a transport instance running with this configuration. \n Exiting... \n\n " )
339 sys
. stderr
. write ( "Daemonise failed: ( %d ) %s \n " % ( e
. errno
, e
. strerror
))
342 def shuttingDown ( self
):
343 self
. transportSvc
. removeMe ()
344 # Keep the transport running for another 3 seconds
345 def cb ( ignored
= None ):
347 os
. remove ( config
. pid
)
350 reactor
. callLater ( 3.0 , d
. callback
, None )
355 def SIGHUPstuff (* args
):
356 global configFile
, configOptions
357 xmlconfig
. reloadConfig ( configFile
, configOptions
)
359 legacy
. reloadConfig ()
361 if os
. name
== "posix" :
363 # Set SIGHUP to reload the config file & close & open debug file
364 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
368 # Create the application
372 if __name__
== "__main__" :