]> code.delx.au - pymsnt/blob - src/main.py
Small fixes to xmlw.
[pymsnt] / src / main.py
1 # Copyright 2004-2005 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 os.path
7 import shutil
8 import time
9 import sys
10 reload(sys)
11 sys.setdefaultencoding("utf-8")
12
13 # Must load config before everything else
14 import config
15 import xmlconfig
16 xmlconfig.reloadConfig()
17
18 from twisted.internet import reactor, task
19 from twisted.internet.defer import Deferred
20 from tlib.xmlw import Element, jid, component
21 from debug import LogEvent, INFO, WARN, ERROR
22
23 import xdb
24 import avatar
25 import session
26 import jabw
27 import disco
28 import register
29 import misciq
30 import lang
31 import legacy
32 import housekeep
33
34 #import gc
35 #gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS)
36
37
38 class PyTransport(component.Service):
39 def __init__(self):
40 LogEvent(INFO)
41
42 # Do any auto-update stuff
43 housekeep.init()
44
45 # Discovery, as well as some builtin features
46 self.discovery = disco.ServerDiscovery(self)
47 self.discovery.addIdentity("gateway", legacy.id, legacy.name, config.jid)
48 self.discovery.addIdentity("conference", "text", legacy.name + " Chatrooms", config.jid)
49 self.discovery.addFeature(disco.XCONFERENCE, None, config.jid) # So that clients know you can create groupchat rooms on the server
50 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
51
52 self.xdb = xdb.XDB(config.jid, legacy.mangle)
53 self.avatarCache = avatar.AvatarCache()
54 self.registermanager = register.RegisterManager(self)
55 self.gatewayTranslator = misciq.GatewayTranslator(self)
56 self.versionTeller = misciq.VersionTeller(self)
57 self.pingService = misciq.PingService(self)
58 self.adHocCommands = misciq.AdHocCommands(self)
59 self.vCardFactory = misciq.VCardFactory(self)
60 self.iqAvatarFactor = misciq.IqAvatarFactory(self)
61 self.connectUsers = misciq.ConnectUsers(self)
62 self.statistics = misciq.Statistics(self)
63 self.startTime = int(time.time())
64
65 self.xmlstream = None
66 self.sessions = {}
67
68 # Groupchat ID handling
69 self.lastID = 0
70 self.reservedIDs = []
71
72 # Message IDs
73 self.messageID = 0
74
75 self.loopCall = task.LoopingCall(self.loopCall)
76 self.loopCall.start(60.0)
77
78 def removeMe(self):
79 LogEvent(INFO)
80 for session in self.sessions.copy():
81 self.sessions[session].removeMe()
82
83 def makeMessageID(self):
84 self.messageID += 1
85 return str(self.messageID)
86
87 def makeID(self):
88 newID = "r" + str(self.lastID)
89 self.lastID += 1
90 if self.reservedIDs.count(newID) > 0:
91 # Ack, it's already used.. Try again
92 return self.makeID()
93 else:
94 return newID
95
96 def reserveID(self, ID):
97 self.reservedIDs.append(ID)
98
99 def loopCall(self):
100 numsessions = len(self.sessions)
101
102 #if config.debugOn and numsessions > 0:
103 # print "Sessions:"
104 # for key in self.sessions:
105 # print "\t" + self.sessions[key].jabberID
106
107 self.statistics.stats["Uptime"] = int(time.time()) - self.startTime
108 self.statistics.stats["OnlineUsers"] = numsessions
109 legacy.updateStats(self.statistics)
110 if numsessions > 0:
111 oldDict = self.sessions.copy()
112 self.sessions = {}
113 for key in oldDict:
114 session = oldDict[key]
115 if not session.alive:
116 LogEvent(WARN, "", "Ghost session found.")
117 # Don't add it to the new dictionary. Effectively removing it
118 else:
119 self.sessions[key] = session
120
121 def componentConnected(self, xmlstream):
122 LogEvent(INFO)
123 self.xmlstream = xmlstream
124 self.xmlstream.addObserver("/iq", self.discovery.onIq)
125 self.xmlstream.addObserver("/presence", self.onPresence)
126 self.xmlstream.addObserver("/message", self.onMessage)
127 self.xmlstream.addObserver("/route", self.onRouteMessage)
128 if config.useXCP:
129 pres = Element((None, "presence"))
130 pres.attributes["to"] = "presence@-internal"
131 pres.attributes["from"] = config.compjid
132 x = pres.addElement("x")
133 x.attributes["xmlns"] = "http://www.jabber.com/schemas/component-presence.xsd"
134 x.attributes["xmlns:config"] = "http://www.jabber.com/config"
135 x.attributes["config:version"] = "1"
136 x.attributes["protocol-version"] = "1.0"
137 x.attributes["config-ns"] = legacy.url + "/component"
138 self.send(pres)
139
140 def componentDisconnected(self):
141 LogEvent(INFO)
142 self.xmlstream = None
143
144 def onRouteMessage(self, el):
145 for child in el.elements():
146 if child.name == "message":
147 self.onMessage(child)
148 elif child.name == "presence":
149 # Ignore any presence broadcasts about other XCP components
150 if child.getAttribute("to") and child.getAttribute("to").find("@-internal") > 0: return
151 self.onPresence(child)
152 elif child.name == "iq":
153 self.discovery.onIq(child)
154
155 def onMessage(self, el):
156 fro = el.getAttribute("from")
157 try:
158 froj = utils.jid(fro)
159 except Exception, e:
160 LogEvent(WARN, "", "Failed stringprep.")
161 return
162 mtype = el.getAttribute("type")
163 if self.sessions.has_key(froj.userhost()):
164 self.sessions[froj.userhost()].onMessage(el)
165 elif mtype != "error":
166 to = el.getAttribute("to")
167 ulang = utils.getLang(el)
168 body = None
169 for child in el.elements():
170 if child.name == "body":
171 body = child.__str__()
172 LogEvent(INFO, "", "Sending error response to a message outside of session.")
173 jabw.sendErrorMessage(self, fro, to, "auth", "not-authorized", lang.get(ulang).notLoggedIn, body)
174
175 def onPresence(self, el):
176 fro = el.getAttribute("from")
177 to = el.getAttribute("to")
178 try:
179 froj = utils.jid(fro)
180 toj = utils.jid(to)
181 except Exception, e:
182 LogEvent(WARN, "", "Failed stringprep.")
183 return
184
185 if self.sessions.has_key(froj.userhost()):
186 self.sessions[froj.userhost()].onPresence(el)
187 else:
188 ulang = utils.getLang(el)
189 ptype = el.getAttribute("type")
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 not el.getAttribute("type"): # Don't create a session unless they're sending available presence
193 LogEvent(INFO, "", "Attempting to create a new session.")
194 s = session.makeSession(self, froj.userhost(), ulang)
195 if s:
196 self.statistics.stats["TotalUsers"] += 1
197 self.sessions[froj.userhost()] = s
198 LogEvent(INFO, "", "New session created.")
199 # Send the first presence
200 s.onPresence(el)
201 else:
202 LogEvent(INFO, "", "Failed to create session")
203 jabw.sendMessage(self, to=froj.userhost(), fro=config.jid, body=lang.get(ulang).notRegistered)
204
205 elif el.getAttribute("type") != "error":
206 LogEvent(INFO, "", "Sending unavailable presence to non-logged in user.")
207 pres = Element((None, "presence"))
208 pres.attributes["from"] = to
209 pres.attributes["to"] = fro
210 pres.attributes["type"] = "unavailable"
211 self.send(pres)
212 return
213
214 elif ptype and (ptype.startswith("subscribe") or ptype.startswith("unsubscribe")):
215 # They haven't logged in, and are trying to change subscription to a user
216 # Lets log them in and then do it
217 LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
218 s = session.makeSession(self, froj.userhost(), ulang)
219 if s:
220 self.sessions[froj.userhost()] = s
221 LogEvent(INFO, "", "New session created.")
222 # Tell the session there's a new resource
223 s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
224 # Send this subscription
225 s.onPresence(el)
226
227
228 class App:
229 def __init__(self):
230 jid = config.jid
231 if config.compjid: jid = config.compjid
232 self.c = component.buildServiceManager(jid, config.secret, "tcp:%s:%s" % (config.mainServer, config.port))
233 self.transportSvc = PyTransport()
234 self.transportSvc.setServiceParent(self.c)
235 self.c.startService()
236 reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
237
238 def alreadyRunning(self):
239 print "There is already a transport instance running with this configuration."
240 print "Exiting..."
241 sys.exit(1)
242
243 def shuttingDown(self):
244 self.transportSvc.removeMe()
245 # Keep the transport running for another 3 seconds
246 def cb(ignored=None):
247 pass
248 d = Deferred()
249 d.addCallback(cb)
250 reactor.callLater(3.0, d.callback, None)
251 return d
252
253
254
255 def SIGHUPstuff(*args):
256 xmlconfig.reloadConfig()
257 debug.reopenFile()
258
259 if os.name == "posix":
260 import signal
261 # Set SIGHUP to reload the config file & close & open debug file
262 signal.signal(signal.SIGHUP, SIGHUPstuff)
263
264
265 if __name__ == "__main__":
266 app = App()
267 reactor.run()
268