]> code.delx.au - pymsnt/blob - src/main.py
Reimporting (0.9.5)
[pymsnt] / 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
3
4 import utils
5 import os
6 import shutil
7 if(os.name == "posix"):
8 import signal
9 import sys
10 reload(sys)
11 sys.setdefaultencoding("utf-8")
12 import types
13
14 # Must load config before everything else
15 import config
16 import xmlconfig
17 xmlconfig.reloadConfig()
18
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
24 pollreactor.install()
25 elif(config.reactor == "kqueue"):
26 from twisted.internet import kqreactor
27 kqreactor.install()
28 elif(len(config.reactor) > 0):
29 print "Unknown reactor: ", config.reactor, "Using default reactor"
30
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
37 else:
38 from tlib.jabber import component, jid
39 from tlib.domish import Element
40
41
42 import xdb
43 import session
44 import jabw
45 import disco
46 import register
47 import misciq
48 import lang
49 import debug
50 import legacy
51
52 #import gc
53 #gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS)
54
55
56 class PyTransport(component.Service):
57 def __init__(self):
58 debug.log("PyTransport: Service starting up")
59
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
65
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)
71
72 self.xmlstream = None
73 self.sessions = {}
74
75 # Groupchat ID handling
76 self.lastID = 0
77 self.reservedIDs = []
78
79 # Message IDs
80 self.messageID = 0
81
82 self.loopCheckSessions = task.LoopingCall(self.loopCheckSessionsCall)
83 self.loopCheckSessions.start(60.0) # call every ten seconds
84
85 # Display active sessions if debug mode is on
86 if(config.debugOn):
87 self.loop = task.LoopingCall(self.loopCall)
88 self.loop.start(60.0) # call every 60 seconds
89 twisted.python.log.addObserver(self.exceptionLogger)
90
91
92 def removeMe(self):
93 debug.log("PyTransport: Service shutting down")
94 dic = utils.copyDict(self.sessions)
95 for session in dic:
96 dic[session].removeMe()
97
98 def exceptionLogger(self, *kwargs):
99 if(len(config.debugLog) > 0):
100 kwargs = kwargs[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
104 if(config.debugLog):
105 debug.flushDebugSmart()
106 print "Exception occured! Check the log!"
107
108 def makeMessageID(self):
109 self.messageID += 1
110 return str(self.messageID)
111
112 def makeID(self):
113 newID = "r" + str(self.lastID)
114 self.lastID += 1
115 if(self.reservedIDs.count(newID) > 0):
116 # Ack, it's already used.. Try again
117 return self.makeID()
118 else:
119 return newID
120
121 def reserveID(self, ID):
122 self.reservedIDs.append(ID)
123
124 def loopCall(self):
125 if(len(self.sessions) > 0):
126 debug.log("Sessions:")
127 for key in self.sessions:
128 debug.log("\t" + self.sessions[key].jabberID)
129
130 def loopCheckSessionsCall(self):
131 if(len(self.sessions) > 0):
132 oldDict = utils.copyDict(self.sessions)
133 self.sessions = {}
134 for key in oldDict:
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
139 else:
140 self.sessions[key] = session
141
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)
149
150 def componentDisconnected(self):
151 debug.log("PyTransport: Disconnected from main Jabberd server")
152 self.xmlstream = None
153
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)
162
163 def onMessage(self, el):
164 fro = el.getAttribute("from")
165 froj = jid.JID(fro)
166 to = el.getAttribute("to")
167 # if(to.find('@') < 0): return
168 mtype = el.getAttribute("type")
169 ulang = utils.getLang(el)
170 body = None
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)
179
180 def onPresence(self, el):
181 fro = el.getAttribute("from")
182 ptype = el.getAttribute("type")
183 froj = jid.JID(fro)
184 to = el.getAttribute("to")
185 toj = jid.JID(to)
186 ulang = utils.getLang(el)
187 if(self.sessions.has_key(froj.userhost())):
188 self.sessions[froj.userhost()].onPresence(el)
189 else:
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)
195 if(s):
196 self.sessions[froj.userhost()] = s
197 debug.log("PyTransport: New session created \"%s\"" % (froj.userhost()))
198 # Send the first presence
199 s.onPresence(el)
200 else:
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)
203
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"
210 self.send(pres)
211 return
212
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)
218 if(s):
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
224 s.onPresence(el)
225
226
227 class App:
228 def __init__(self):
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()))
233 pf.close()
234 if(os.name == "posix"):
235 try:
236 os.kill(pid, signal.SIGHUP)
237 self.alreadyRunning()
238 except OSError:
239 # The process is still up
240 pass
241 else:
242 self.alreadyRunning()
243
244 # Create a PID file
245 pid = str(os.getpid())
246 pf = file(utils.doPath(config.pid), 'w')
247 pf.write("%s\n" % pid);
248 pf.close()
249
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)
255
256 def alreadyRunning(self):
257 print "There is already a transport instance running with this configuration."
258 print "Exiting..."
259 sys.exit(1)
260
261 def shuttingDown(self):
262 self.transportSvc.removeMe()
263 def cb(ignored=None):
264 os.remove(utils.doPath(config.pid))
265 d = Deferred()
266 d.addCallback(cb)
267 reactor.callLater(3.0, d.callback, None)
268 return d
269
270
271
272 def SIGHUPstuff(*args):
273 xmlconfig.reloadConfig()
274 debug.reopenFile()
275
276 def doSpoolPrepCheck():
277 pre = utils.doPath(config.spooldir) + "/" + config.jid + "/"
278 try:
279 f = open(pre + "notes_to_myself", "r")
280 for line in f.readlines():
281 if line == "doSpoolPrepCheck\n":
282 return
283 f.close()
284 except IOError:
285 pass
286
287 # New installation
288 if not os.path.exists(pre):
289 os.makedirs(pre)
290 f = open(pre + "notes_to_myself", "w")
291 f.write("doSpoolPrepCheck\n")
292 f.close()
293 return
294
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()
300 if(file != filej):
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!"
305 os.exit(1)
306 else:
307 shutil.move(utils.doPath(pre + file, pre + filej))
308 print "done"
309 f = open(pre + "notes_to_myself", "a")
310 f.write("doSpoolPrepCheck\n")
311 f.close()
312
313
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)
318
319 # Check that all the spool files stringprepped
320 doSpoolPrepCheck()
321
322 app = App()
323 reactor.run()
324
325