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