]>
code.delx.au - pymsnt/blob - src/main.py
4b4d906022860449743b86ee76ba46884a9418f9
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
10 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 from twisted
. internet
import epollreactor
as bestreactor
15 from twisted
. internet
import kqreactor
as bestreactor
18 from twisted
. internet
import pollreactor
as bestreactor
21 from twisted
. internet
import selectreactor
as bestreactor
25 from twisted
. internet
import default
as bestreactor
28 print "Unable to find a reactor. \n Exiting..."
34 # Must load config before everything else
37 configFile
= "config.xml"
39 opts
, args
= getopt
. getopt ( sys
. argv
[ 1 :], "bc:o:dDgtlp:h" , [ "background" , "config=" , "option=" , "debug" , "Debug" , "garbage" , "traceback" , "log=" , "pid=" , "help" ])
41 if o
in ( "-c" , "--config" ):
43 elif o
in ( "-b" , "--background" ):
44 config
. background
= True
45 elif o
in ( "-d" , "--debug" ):
46 config
. debugLevel
= "2"
47 elif o
in ( "-D" , "--Debug" ):
48 config
. debugLevel
= "3"
49 elif o
in ( "-g" , "--garbage" ):
51 gc
. set_debug ( gc
. DEBUG_LEAK|gc
. DEBUG_STATS
)
52 elif o
in ( "-t" , "--traceback" ):
53 config
. debugLevel
= "1"
54 elif o
in ( "-l" , "--log" ):
56 elif o
in ( "-p" , "--pid" ):
58 elif o
in ( "-o" , "--option" ):
59 var
, setting
= v
. split ( "=" , 2 )
60 configOptions
[ var
] = setting
61 elif o
in ( "-h" , "--help" ):
62 print " %s [options]" % sys
. argv
[ 0 ]
63 print " -h print this help"
64 print " -b daemonize/background transport"
65 print " -c <file> read configuration from this file"
66 print " -d print debugging output"
67 print " -D print extended debugging output"
68 print " -g print garbage collection output"
69 print " -t print debugging only on traceback"
70 print " -l <file> write debugging output to file"
71 print " -p <file> write process ID to file"
72 print " -o <var>=<setting> set config var to setting"
75 xmlconfig
. reloadConfig ( configFile
, configOptions
)
78 # They picked their own reactor. Lets install it.
79 del sys
. modules
[ "twisted.internet.reactor" ]
80 if config
. reactor
== "epoll" :
81 from twisted
. internet
import epollreactor
82 epollreactor
. install ()
83 elif config
. reactor
== "poll" :
84 from twisted
. internet
import pollreactor
86 elif config
. reactor
== "kqueue" :
87 from twisted
. internet
import kqreactor
89 elif len ( config
. reactor
) > 0 :
90 print "Unknown reactor: " , config
. reactor
, ". Using select(), reactor."
93 from twisted
. internet
import reactor
, task
94 from twisted
. internet
. defer
import Deferred
95 from tlib
. xmlw
import Element
, jid
, component
96 from debug
import LogEvent
, INFO
, WARN
, ERROR
114 class PyTransport ( component
. Service
):
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" )
126 self
. xdb
= xdb
. XDB ( config
. jid
, legacy
. mangle
)
127 self
. avatarCache
= avatar
. AvatarCache ()
128 self
. registermanager
= register
. RegisterManager ( self
)
129 self
. gatewayTranslator
= misciq
. GatewayTranslator ( self
)
130 self
. versionTeller
= misciq
. VersionTeller ( self
)
131 self
. pingService
= misciq
. PingService ( self
)
132 self
. adHocCommands
= misciq
. AdHocCommands ( self
)
133 self
. vCardFactory
= misciq
. VCardFactory ( self
)
134 self
. iqAvatarFactor
= misciq
. IqAvatarFactory ( self
)
135 self
. connectUsers
= misciq
. ConnectUsers ( self
)
136 if config
. ftJabberPort
:
137 self
. ftSOCKS5Receive
= ft
. Proxy65 ( int ( config
. ftJabberPort
))
138 self
. ftSOCKS5Send
= misciq
. Socks5FileTransfer ( self
)
140 self
. ftOOBReceive
= ft
. FileTransferOOBReceive ( int ( config
. ftOOBPort
))
141 self
. ftOOBSend
= misciq
. FileTransferOOBSend ( self
)
142 self
. statistics
= misciq
. Statistics ( self
)
143 self
. startTime
= int ( time
. time ())
145 self
. xmlstream
= None
148 # Groupchat ID handling
150 self
. reservedIDs
= []
155 self
. loopTask
= task
. LoopingCall ( self
. loopFunc
)
156 self
. loopTask
. start ( 60.0 )
160 for session
in self
. sessions
. copy ():
161 self
. sessions
[ session
]. removeMe ()
163 def makeMessageID ( self
):
165 return str ( self
. messageID
)
168 newID
= "r" + str ( self
. lastID
)
170 if self
. reservedIDs
. count ( newID
) > 0 :
171 # Ack, it's already used.. Try again
176 def reserveID ( self
, ID
):
177 self
. reservedIDs
. append ( ID
)
180 numsessions
= len ( self
. sessions
)
182 #if config.debugOn and numsessions > 0:
184 # for key in self.sessions:
185 # print "\t" + self.sessions[key].jabberID
187 self
. statistics
. stats
[ "Uptime" ] = int ( time
. time ()) - self
. startTime
188 self
. statistics
. stats
[ "OnlineUsers" ] = numsessions
189 legacy
. updateStats ( self
. statistics
)
191 oldDict
= self
. sessions
. copy ()
196 LogEvent ( WARN
, "" , "Ghost session found." )
197 # Don't add it to the new dictionary. Effectively removing it
199 self
. sessions
[ key
] = s
201 def componentConnected ( self
, xmlstream
):
203 self
. xmlstream
= xmlstream
204 self
. xmlstream
. addObserver ( "/iq" , self
. discovery
. onIq
)
205 self
. xmlstream
. addObserver ( "/presence" , self
. onPresence
)
206 self
. xmlstream
. addObserver ( "/message" , self
. onMessage
)
207 self
. xmlstream
. addObserver ( "/route" , self
. onRouteMessage
)
209 pres
= Element (( None , "presence" ))
210 pres
. attributes
[ "to" ] = "presence@-internal"
211 pres
. attributes
[ "from" ] = config
. compjid
212 x
= pres
. addElement ( "x" )
213 x
. attributes
[ "xmlns" ] = "http://www.jabber.com/schemas/component-presence.xsd"
214 x
. attributes
[ "xmlns:config" ] = "http://www.jabber.com/config"
215 x
. attributes
[ "config:version" ] = "1"
216 x
. attributes
[ "protocol-version" ] = "1.0"
217 x
. attributes
[ "config-ns" ] = legacy
. url
+ "/component"
220 def componentDisconnected ( self
):
222 self
. xmlstream
= None
224 def onRouteMessage ( self
, el
):
225 for child
in el
. elements ():
226 if child
. name
== "message" :
227 self
. onMessage ( child
)
228 elif child
. name
== "presence" :
229 # Ignore any presence broadcasts about other XCP components
230 if child
. getAttribute ( "to" ) and child
. getAttribute ( "to" ). find ( "@-internal" ) > 0 : return
231 self
. onPresence ( child
)
232 elif child
. name
== "iq" :
233 self
. discovery
. onIq ( child
)
235 def onMessage ( self
, el
):
236 fro
= el
. getAttribute ( "from" )
238 froj
= jid
. intern ( fro
)
240 LogEvent ( WARN
, "" , "Failed stringprep." )
242 mtype
= el
. getAttribute ( "type" )
243 s
= self
. sessions
. get ( froj
. userhost (), None )
244 if mtype
== "error" and s
:
245 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
249 elif mtype
!= "error" :
250 to
= el
. getAttribute ( "to" )
251 ulang
= utils
. getLang ( el
)
253 for child
in el
. elements ():
254 if child
. name
== "body" :
255 body
= child
.__ str
__ ()
256 LogEvent ( INFO
, "" , "Sending error response to a message outside of session." )
257 jabw
. sendErrorMessage ( self
, fro
, to
, "auth" , "not-authorized" , lang
. get ( ulang
). notLoggedIn
, body
)
259 def onPresence ( self
, el
):
260 fro
= el
. getAttribute ( "from" )
261 to
= el
. getAttribute ( "to" )
263 froj
= jid
. intern ( fro
)
266 LogEvent ( WARN
, "" , "Failed stringprep." )
269 ptype
= el
. getAttribute ( "type" )
270 s
= self
. sessions
. get ( froj
. userhost ())
271 if ptype
== "error" and s
:
272 LogEvent ( INFO
, s
. jabberID
, "Removing session because of message type=error" )
277 ulang
= utils
. getLang ( el
)
278 ptype
= el
. getAttribute ( "type" )
280 # If the presence packet is to the transport (not a user) and there isn't already a session
281 if not el
. getAttribute ( "type" ): # Don't create a session unless they're sending available presence
282 LogEvent ( INFO
, "" , "Attempting to create a new session." )
283 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
285 self
. statistics
. stats
[ "TotalUsers" ] += 1
286 self
. sessions
[ froj
. userhost ()] = s
287 LogEvent ( INFO
, "" , "New session created." )
288 # Send the first presence
291 LogEvent ( INFO
, "" , "Failed to create session" )
292 jabw
. sendMessage ( self
, to
= froj
. userhost (), fro
= config
. jid
, body
= lang
. get ( ulang
). notRegistered
)
294 elif el
. getAttribute ( "type" ) != "error" :
295 LogEvent ( INFO
, "" , "Sending unavailable presence to non-logged in user." )
296 pres
= Element (( None , "presence" ))
297 pres
. attributes
[ "from" ] = to
298 pres
. attributes
[ "to" ] = fro
299 pres
. attributes
[ "type" ] = "unavailable"
303 elif ptype
and ( ptype
. startswith ( "subscribe" ) or ptype
. startswith ( "unsubscribe" )):
304 # They haven't logged in, and are trying to change subscription to a user
305 # Lets log them in and then do it
306 LogEvent ( INFO
, "" , "Attempting to create a session to do subscription stuff." )
307 s
= session
. makeSession ( self
, froj
. userhost (), ulang
)
309 self
. sessions
[ froj
. userhost ()] = s
310 LogEvent ( INFO
, "" , "New session created." )
311 # Tell the session there's a new resource
312 s
. handleResourcePresence ( froj
. userhost (), froj
. resource
, toj
. userhost (), toj
. resource
, 0 , None , None , None )
313 # Send this subscription
319 # Check for any other instances
320 if config
. pid
and os
. name
!= "posix" :
323 twistd
. checkPID ( config
. pid
)
325 # Do any auto-update stuff
328 # Daemonise the process and write the PID file
329 if config
. background
and os
. name
== "posix" :
334 # Initialise debugging, and do the other half of SIGHUPstuff
336 legacy
. reloadConfig ()
340 if config
. useXCP
and config
. compjid
:
342 self
. c
= component
. buildServiceManager ( jid
, config
. secret
, "tcp: %s : %s " % ( config
. mainServer
, config
. port
))
343 self
. transportSvc
= PyTransport ()
344 self
. transportSvc
. setServiceParent ( self
. c
)
345 self
. c
. startService ()
346 reactor
. addSystemEventTrigger ( 'before' , 'shutdown' , self
. shuttingDown
)
350 pid
= str ( os
. getpid ())
351 pf
= open ( config
. pid
, "w" )
352 pf
. write ( " %s \n " % pid
)
355 def shuttingDown ( self
):
356 self
. transportSvc
. removeMe ()
357 # Keep the transport running for another 3 seconds
358 def cb ( ignored
= None ):
360 twistd
. removePID ( config
. pid
)
363 reactor
. callLater ( 3.0 , d
. callback
, None )
368 def SIGHUPstuff (* args
):
369 global configFile
, configOptions
370 xmlconfig
. reloadConfig ( configFile
, configOptions
)
371 if config
. pid
and os
. name
!= "posix" :
374 legacy
. reloadConfig ()
376 if os
. name
== "posix" :
378 # Set SIGHUP to reload the config file & close & open debug file
379 signal
. signal ( signal
. SIGHUP
, SIGHUPstuff
)
380 # Load some scripts for PID and daemonising
381 from twisted
. scripts
import twistd
385 # Create the application
389 if __name__
== "__main__" :