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