]> code.delx.au - pymsnt/blob - src/main.py
OOB file sending nearly done.
[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 if config.ftJabberPort: self.ftSOCKS5 = ft.Proxy65(int(config.ftJabberPort))
64 if config.ftOOBPort:
65 self.ftOOBReceive = ft.FileTransferOOBReceive(int(config.ftOOBPort))
66 self.ftOOBSend = misciq.FileTransferOOBSend(self)
67 self.statistics = misciq.Statistics(self)
68 self.startTime = int(time.time())
69
70 self.xmlstream = None
71 self.sessions = {}
72
73 # Groupchat ID handling
74 self.lastID = 0
75 self.reservedIDs = []
76
77 # Message IDs
78 self.messageID = 0
79
80 self.loopCall = task.LoopingCall(self.loopCall)
81 self.loopCall.start(60.0)
82
83 def removeMe(self):
84 LogEvent(INFO)
85 for session in self.sessions.copy():
86 self.sessions[session].removeMe()
87
88 def makeMessageID(self):
89 self.messageID += 1
90 return str(self.messageID)
91
92 def makeID(self):
93 newID = "r" + str(self.lastID)
94 self.lastID += 1
95 if self.reservedIDs.count(newID) > 0:
96 # Ack, it's already used.. Try again
97 return self.makeID()
98 else:
99 return newID
100
101 def reserveID(self, ID):
102 self.reservedIDs.append(ID)
103
104 def loopCall(self):
105 numsessions = len(self.sessions)
106
107 #if config.debugOn and numsessions > 0:
108 # print "Sessions:"
109 # for key in self.sessions:
110 # print "\t" + self.sessions[key].jabberID
111
112 self.statistics.stats["Uptime"] = int(time.time()) - self.startTime
113 self.statistics.stats["OnlineUsers"] = numsessions
114 legacy.updateStats(self.statistics)
115 if numsessions > 0:
116 oldDict = self.sessions.copy()
117 self.sessions = {}
118 for key in oldDict:
119 session = oldDict[key]
120 if not session.alive:
121 LogEvent(WARN, "", "Ghost session found.")
122 # Don't add it to the new dictionary. Effectively removing it
123 else:
124 self.sessions[key] = session
125
126 def componentConnected(self, xmlstream):
127 LogEvent(INFO)
128 self.xmlstream = xmlstream
129 self.xmlstream.addObserver("/iq", self.discovery.onIq)
130 self.xmlstream.addObserver("/presence", self.onPresence)
131 self.xmlstream.addObserver("/message", self.onMessage)
132 self.xmlstream.addObserver("/route", self.onRouteMessage)
133 if config.useXCP:
134 pres = Element((None, "presence"))
135 pres.attributes["to"] = "presence@-internal"
136 pres.attributes["from"] = config.compjid
137 x = pres.addElement("x")
138 x.attributes["xmlns"] = "http://www.jabber.com/schemas/component-presence.xsd"
139 x.attributes["xmlns:config"] = "http://www.jabber.com/config"
140 x.attributes["config:version"] = "1"
141 x.attributes["protocol-version"] = "1.0"
142 x.attributes["config-ns"] = legacy.url + "/component"
143 self.send(pres)
144
145 def componentDisconnected(self):
146 LogEvent(INFO)
147 self.xmlstream = None
148
149 def onRouteMessage(self, el):
150 for child in el.elements():
151 if child.name == "message":
152 self.onMessage(child)
153 elif child.name == "presence":
154 # Ignore any presence broadcasts about other XCP components
155 if child.getAttribute("to") and child.getAttribute("to").find("@-internal") > 0: return
156 self.onPresence(child)
157 elif child.name == "iq":
158 self.discovery.onIq(child)
159
160 def onMessage(self, el):
161 fro = el.getAttribute("from")
162 try:
163 froj = jid.intern(fro)
164 except Exception, e:
165 LogEvent(WARN, "", "Failed stringprep.")
166 return
167 mtype = el.getAttribute("type")
168 if self.sessions.has_key(froj.userhost()):
169 self.sessions[froj.userhost()].onMessage(el)
170 elif mtype != "error":
171 to = el.getAttribute("to")
172 ulang = utils.getLang(el)
173 body = None
174 for child in el.elements():
175 if child.name == "body":
176 body = child.__str__()
177 LogEvent(INFO, "", "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 to = el.getAttribute("to")
183 try:
184 froj = jid.intern(fro)
185 toj = jid.intern(to)
186 except Exception, e:
187 LogEvent(WARN, "", "Failed stringprep.")
188 return
189
190 if self.sessions.has_key(froj.userhost()):
191 self.sessions[froj.userhost()].onPresence(el)
192 else:
193 ulang = utils.getLang(el)
194 ptype = el.getAttribute("type")
195 if to.find('@') < 0:
196 # If the presence packet is to the transport (not a user) and there isn't already a session
197 if not el.getAttribute("type"): # Don't create a session unless they're sending available presence
198 LogEvent(INFO, "", "Attempting to create a new session.")
199 s = session.makeSession(self, froj.userhost(), ulang)
200 if s:
201 self.statistics.stats["TotalUsers"] += 1
202 self.sessions[froj.userhost()] = s
203 LogEvent(INFO, "", "New session created.")
204 # Send the first presence
205 s.onPresence(el)
206 else:
207 LogEvent(INFO, "", "Failed to create session")
208 jabw.sendMessage(self, to=froj.userhost(), fro=config.jid, body=lang.get(ulang).notRegistered)
209
210 elif el.getAttribute("type") != "error":
211 LogEvent(INFO, "", "Sending unavailable presence to non-logged in user.")
212 pres = Element((None, "presence"))
213 pres.attributes["from"] = to
214 pres.attributes["to"] = fro
215 pres.attributes["type"] = "unavailable"
216 self.send(pres)
217 return
218
219 elif ptype and (ptype.startswith("subscribe") or ptype.startswith("unsubscribe")):
220 # They haven't logged in, and are trying to change subscription to a user
221 # Lets log them in and then do it
222 LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.")
223 s = session.makeSession(self, froj.userhost(), ulang)
224 if s:
225 self.sessions[froj.userhost()] = s
226 LogEvent(INFO, "", "New session created.")
227 # Tell the session there's a new resource
228 s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None)
229 # Send this subscription
230 s.onPresence(el)
231
232
233 class App:
234 def __init__(self):
235 jid = config.jid
236 if config.compjid: jid = config.compjid
237 self.c = component.buildServiceManager(jid, config.secret, "tcp:%s:%s" % (config.mainServer, config.port))
238 self.transportSvc = PyTransport()
239 self.transportSvc.setServiceParent(self.c)
240 self.c.startService()
241 reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
242
243 def alreadyRunning(self):
244 print "There is already a transport instance running with this configuration."
245 print "Exiting..."
246 sys.exit(1)
247
248 def shuttingDown(self):
249 self.transportSvc.removeMe()
250 # Keep the transport running for another 3 seconds
251 def cb(ignored=None):
252 pass
253 d = Deferred()
254 d.addCallback(cb)
255 reactor.callLater(3.0, d.callback, None)
256 return d
257
258
259
260 def SIGHUPstuff(*args):
261 xmlconfig.reloadConfig()
262
263 if os.name == "posix":
264 import signal
265 # Set SIGHUP to reload the config file & close & open debug file
266 signal.signal(signal.SIGHUP, SIGHUPstuff)
267
268
269 if __name__ == "__main__":
270 app = App()
271 reactor.run()
272