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