]>
code.delx.au - pymsnt/blob - src/main.py
1 # Copyright 2004 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
7 if(os
.name
== "posix"):
11 sys
.setdefaultencoding("utf-8")
14 # Must load config before everything else
17 xmlconfig
.reloadConfig()
19 if(config
.reactor
== "epoll"):
20 from twisted
.internet
import epollreactor
21 epollreactor
.install()
22 elif(config
.reactor
== "poll"):
23 from twisted
.internet
import pollreactor
25 elif(config
.reactor
== "kqueue"):
26 from twisted
.internet
import kqreactor
28 elif(len(config
.reactor
) > 0):
29 print "Unknown reactor: ", config
.reactor
, "Using default reactor"
31 from twisted
.internet
import reactor
, task
32 from twisted
.internet
.defer
import Deferred
33 import twisted
.python
.log
34 if(utils
.checkTwisted()):
35 from twisted
.words
.protocols
.jabber
import component
, jid
36 from twisted
.xish
.domish
import Element
38 from tlib
.jabber
import component
, jid
39 from tlib
.domish
import Element
53 #gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS)
56 class PyTransport(component
.Service
):
58 debug
.log("PyTransport: Service starting up")
60 # Discovery, as well as some builtin features
61 self
.discovery
= disco
.ServerDiscovery(self
)
62 self
.discovery
.addIdentity("gateway", legacy
.id, legacy
.name
)
63 self
.discovery
.addIdentity("conference", "text", legacy
.name
+ " Chatrooms")
64 self
.discovery
.addFeature("http://jabber.org/protocol/muc", None) # So that clients know you can create groupchat rooms on the server
66 self
.xdb
= xdb
.XDB(config
.jid
, legacy
.mangle
)
67 self
.registermanager
= register
.RegisterManager(self
)
68 self
.gatewayTranslator
= misciq
.GatewayTranslator(self
)
69 self
.versionTeller
= misciq
.VersionTeller(self
)
70 self
.pingService
= misciq
.PingService(self
)
75 # Groupchat ID handling
82 self
.loopCheckSessions
= task
.LoopingCall(self
.loopCheckSessionsCall
)
83 self
.loopCheckSessions
.start(60.0) # call every ten seconds
85 # Display active sessions if debug mode is on
87 self
.loop
= task
.LoopingCall(self
.loopCall
)
88 self
.loop
.start(60.0) # call every 60 seconds
89 twisted
.python
.log
.addObserver(self
.exceptionLogger
)
93 debug
.log("PyTransport: Service shutting down")
94 dic
= utils
.copyDict(self
.sessions
)
96 dic
[session
].removeMe()
98 def exceptionLogger(self
, *kwargs
):
99 if(len(config
.debugLog
) > 0):
101 if(kwargs
.has_key("failure")):
102 failure
= kwargs
["failure"]
103 failure
.printTraceback(debug
) # Pass debug as a pretend file object because it implements the write method
105 debug
.flushDebugSmart()
106 print "Exception occured! Check the log!"
108 def makeMessageID(self
):
110 return str(self
.messageID
)
113 newID
= "r" + str(self
.lastID
)
115 if(self
.reservedIDs
.count(newID
) > 0):
116 # Ack, it's already used.. Try again
121 def reserveID(self
, ID
):
122 self
.reservedIDs
.append(ID
)
125 if(len(self
.sessions
) > 0):
126 debug
.log("Sessions:")
127 for key
in self
.sessions
:
128 debug
.log("\t" + self
.sessions
[key
].jabberID
)
130 def loopCheckSessionsCall(self
):
131 if(len(self
.sessions
) > 0):
132 oldDict
= utils
.copyDict(self
.sessions
)
135 session
= oldDict
[key
]
136 if(not session
.alive
):
137 debug
.log("Ghost session %s found. This shouldn't happen. Trace" % (session
.jabberID
))
138 # Don't add it to the new dictionary. Effectively removing it
140 self
.sessions
[key
] = session
142 def componentConnected(self
, xmlstream
):
143 debug
.log("PyTransport: Connected to main Jabberd server")
144 self
.xmlstream
= xmlstream
145 self
.xmlstream
.addObserver("/iq", self
.discovery
.onIq
)
146 self
.xmlstream
.addObserver("/presence", self
.onPresence
)
147 self
.xmlstream
.addObserver("/message", self
.onMessage
)
148 self
.xmlstream
.addObserver("/route", self
.onRouteMessage
)
150 def componentDisconnected(self
):
151 debug
.log("PyTransport: Disconnected from main Jabberd server")
152 self
.xmlstream
= None
154 def onRouteMessage(self
, el
):
155 for child
in el
.elements():
156 if(child
.name
== "message"):
157 self
.onMessage(child
)
158 elif(child
.name
== "presence"):
159 self
.onPresence(child
)
160 elif(child
.name
== "iq"):
161 self
.discovery
.onIq(child
)
163 def onMessage(self
, el
):
164 fro
= el
.getAttribute("from")
166 to
= el
.getAttribute("to")
167 # if(to.find('@') < 0): return
168 mtype
= el
.getAttribute("type")
169 ulang
= utils
.getLang(el
)
171 for child
in el
.elements():
172 if(child
.name
== "body"):
173 body
= child
.__str
__()
174 if(self
.sessions
.has_key(froj
.userhost())):
175 self
.sessions
[froj
.userhost()].onMessage(el
)
176 elif(mtype
!= "error"):
177 debug
.log("PyTrans: Sending error response to a message outside of session.")
178 jabw
.sendErrorMessage(self
, fro
, to
, "auth", "not-authorized", lang
.get(ulang
).notLoggedIn
, body
)
180 def onPresence(self
, el
):
181 fro
= el
.getAttribute("from")
182 ptype
= el
.getAttribute("type")
184 to
= el
.getAttribute("to")
186 ulang
= utils
.getLang(el
)
187 if(self
.sessions
.has_key(froj
.userhost())):
188 self
.sessions
[froj
.userhost()].onPresence(el
)
190 if(to
.find('@') < 0):
191 # If the presence packet is to the transport (not a user) and there isn't already a session
192 if(el
.getAttribute("type") in [None, ""]): # Don't create a session unless they're sending available presence
193 debug
.log("PyTransport: Attempting to create a new session \"%s\"" % (froj
.userhost()))
194 s
= session
.makeSession(self
, froj
.userhost(), ulang
)
196 self
.sessions
[froj
.userhost()] = s
197 debug
.log("PyTransport: New session created \"%s\"" % (froj
.userhost()))
198 # Send the first presence
201 debug
.log("PyTransport: Failed to create session \"%s\"" % (froj
.userhost()))
202 jabw
.sendMessage(self
, to
=froj
.userhost(), fro
=config
.jid
, body
=lang
.get(ulang
).notRegistered
)
204 elif(el
.getAttribute("type") != "error"):
205 debug
.log("PyTransport: Sending unavailable presence to non-logged in user \"%s\"" % (froj
.userhost()))
206 pres
= Element((None, "presence"))
207 pres
.attributes
["from"] = to
208 pres
.attributes
["to"] = fro
209 pres
.attributes
["type"] = "unavailable"
213 elif(ptype
in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]):
214 # They haven't logged in, and are trying to change subscription to a user
215 # Lets log them in and then do it
216 debug
.log("PyTransport: Attempting to create a session to do subscription stuff %s" % (froj
.userhost()))
217 s
= session
.makeSession(self
, froj
.userhost(), ulang
)
219 self
.sessions
[froj
.userhost()] = s
220 debug
.log("PyTransport: New session created \"%s\"" % (froj
.userhost()))
221 # Tell the session there's a new resource
222 s
.handleResourcePresence(froj
.userhost(), froj
.resource
, toj
.userhost(), toj
.resource
, 0, None, None, None)
223 # Send this subscription
229 # Check that there isn't already a PID file
230 if(os
.path
.isfile(utils
.doPath(config
.pid
))):
231 pf
= open(utils
.doPath(config
.pid
))
232 pid
= int(str(pf
.readline().strip()))
234 if(os
.name
== "posix"):
236 os
.kill(pid
, signal
.SIGHUP
)
237 self
.alreadyRunning()
239 # The process is still up
242 self
.alreadyRunning()
245 pid
= str(os
.getpid())
246 pf
= file(utils
.doPath(config
.pid
), 'w')
247 pf
.write("%s\n" % pid
);
250 self
.c
= component
.buildServiceManager(config
.jid
, config
.secret
, "tcp:%s:%s" % (config
.mainServer
, config
.port
))
251 self
.transportSvc
= PyTransport()
252 self
.transportSvc
.setServiceParent(self
.c
)
253 self
.c
.startService()
254 reactor
.addSystemEventTrigger('before', 'shutdown', self
.shuttingDown
)
256 def alreadyRunning(self
):
257 print "There is already a transport instance running with this configuration."
261 def shuttingDown(self
):
262 self
.transportSvc
.removeMe()
263 def cb(ignored
=None):
264 os
.remove(utils
.doPath(config
.pid
))
267 reactor
.callLater(3.0, d
.callback
, None)
272 def SIGHUPstuff(*args
):
273 xmlconfig
.reloadConfig()
276 def doSpoolPrepCheck():
277 pre
= utils
.doPath(config
.spooldir
) + "/" + config
.jid
+ "/"
279 f
= open(pre
+ "notes_to_myself", "r")
280 for line
in f
.readlines():
281 if line
== "doSpoolPrepCheck\n":
288 if not os
.path
.exists(pre
):
290 f
= open(pre
+ "notes_to_myself", "w")
291 f
.write("doSpoolPrepCheck\n")
295 print "Checking spool files and stringprepping any if necessary...",
296 for file in os
.listdir(pre
):
297 if(file == "notes_to_myself"): return
298 file = file.replace("%", "@")
299 filej
= jid
.JID(file).full()
301 file = file.replace("@", "%")
302 filej
= filej
.replace("@", "%")
303 if(os
.path
.exists(filej
)):
304 print "Need to move", file, "to", filej
, "but the latter exists!\nAborting!"
307 shutil
.move(utils
.doPath(pre
+ file, pre
+ filej
))
309 f
= open(pre
+ "notes_to_myself", "a")
310 f
.write("doSpoolPrepCheck\n")
314 if(__name__
== "__main__"):
315 # Set SIGHUP to reload the config file & close & open debug file
316 if(os
.name
== "posix"):
317 signal
.signal(signal
.SIGHUP
, SIGHUPstuff
)
319 # Check that all the spool files stringprepped