]>
Commit | Line | Data |
---|---|---|
9e769c12 CP |
1 | from zope.interface import implements |
2 | ||
3 | from twisted.python import usage | |
1bccb3e6 CP |
4 | |
5 | from twisted.internet import task, protocol | |
6 | from twisted.protocols import basic, policies | |
9e769c12 CP |
7 | from twisted.plugin import IPlugin |
8 | from twisted.application.service import IServiceMaker | |
5a767fca | 9 | from twisted.application import internet, strports, service |
9e769c12 | 10 | from twisted.web import static, server |
1bccb3e6 CP |
11 | import urlparse |
12 | import urllib | |
9e769c12 | 13 | |
930be88a | 14 | from qwebirc.root import RootSite |
9e769c12 CP |
15 | |
16 | class Options(usage.Options): | |
17 | optParameters = [["port", "p", "9090","Port to start the server on."], | |
ef74f2c1 | 18 | ["ip", "i", "0.0.0.0", "IP address to listen on."], |
9e769c12 CP |
19 | ["logfile", "l", None, "Path to web CLF (Combined Log Format) log file."], |
20 | ["https", None, None, "Port to listen on for Secure HTTP."], | |
21 | ["certificate", "c", "server.pem", "SSL certificate to use for HTTPS. "], | |
22 | ["privkey", "k", "server.pem", "SSL certificate to use for HTTPS."], | |
9e93b6fe | 23 | ["certificate-chain", "C", None, "Chain SSL certificate"], |
9e769c12 | 24 | ["staticpath", "s", "static", "Path to static content"], |
5a767fca | 25 | ["flashPort", None, None, "Port to listen on for flash policy connections."], |
9e769c12 CP |
26 | ] |
27 | ||
28 | optFlags = [["notracebacks", "n", "Display tracebacks in broken web pages. " + | |
29 | "Displaying tracebacks to users may be security risk!"], | |
30 | ] | |
9e769c12 CP |
31 | |
32 | def postOptions(self): | |
33 | if self['https']: | |
34 | try: | |
9e93b6fe | 35 | get_ssl_factory_factory() |
9e769c12 CP |
36 | except ImportError: |
37 | raise usage.UsageError("SSL support not installed") | |
1bccb3e6 CP |
38 | |
39 | class FlashPolicyProtocol(protocol.Protocol, policies.TimeoutMixin): | |
9463277c CP |
40 | def connectionMade(self): |
41 | self.setTimeout(5) | |
1bccb3e6 CP |
42 | |
43 | def dataReceived(self, data): | |
44 | if data == '<policy-file-request/>\0': | |
45 | self.transport.write(self.factory.response_body) | |
46 | self.transport.loseConnection() | |
47 | return | |
5a767fca | 48 | elif self.factory.childProtocol: |
1bccb3e6 | 49 | self.setTimeout(None) |
5a767fca | 50 | p = self.factory.childProtocol.buildProtocol(self.transport.client) |
1bccb3e6 CP |
51 | p.transport = self.transport |
52 | self.transport.protocol = p | |
53 | p.connectionMade() | |
54 | p.dataReceived(data) | |
5a767fca CP |
55 | else: |
56 | self.transport.loseConnection() | |
1bccb3e6 | 57 | |
1bccb3e6 CP |
58 | class FlashPolicyFactory(protocol.ServerFactory): |
59 | protocol = FlashPolicyProtocol | |
60 | ||
5a767fca | 61 | def __init__(self, childProtocol=None): |
1bccb3e6 CP |
62 | import config |
63 | base_url = urlparse.urlparse(config.BASE_URL) | |
64 | port = base_url.port | |
65 | if port is None: | |
66 | if base_url.scheme == "http": | |
67 | port = 80 | |
68 | elif base_url.scheme == "https": | |
69 | port = 443 | |
70 | else: | |
71 | raise Exception("Unknown scheme: " + base_url.scheme) | |
72 | ||
5a767fca | 73 | self.childProtocol = childProtocol |
1bccb3e6 CP |
74 | self.response_body = """<cross-domain-policy> |
75 | <allow-access-from domain="%s" to-ports="%d" /> | |
76 | </cross-domain-policy>""" % (urllib.quote(base_url.hostname), port) + '\0' | |
77 | ||
9e769c12 CP |
78 | class QWebIRCServiceMaker(object): |
79 | implements(IServiceMaker, IPlugin) | |
80 | tapname = "qwebirc" | |
81 | description = "QuakeNet web-based IRC client" | |
82 | options = Options | |
9e769c12 CP |
83 | |
84 | def makeService(self, config): | |
85 | if config['logfile']: | |
86 | site = RootSite(config['staticpath'], logPath=config['logfile']) | |
87 | else: | |
88 | site = RootSite(config['staticpath']) | |
5a767fca CP |
89 | |
90 | s = service.MultiService() | |
9e769c12 CP |
91 | site.displayTracebacks = not config["notracebacks"] |
92 | if config['https']: | |
9e93b6fe | 93 | ssl_factory = get_ssl_factory_factory() |
5a767fca | 94 | i = internet.SSLServer(int(config['https']), site, ssl_factory(config['privkey'], config['certificate'], certificateChainFile=config["certificate-chain"]), interface=config['ip']) |
9e769c12 | 95 | else: |
1bccb3e6 | 96 | i = internet.TCPServer(int(config['port']), FlashPolicyFactory(site), interface=config['ip']) |
5a767fca CP |
97 | |
98 | i.setServiceParent(s) | |
99 | if config["flashPort"]: | |
100 | f = internet.TCPServer(int(config['flashPort']), FlashPolicyFactory(), interface=config['ip']) | |
101 | f.setServiceParent(s) | |
102 | ||
103 | return s | |
9e93b6fe CP |
104 | |
105 | def get_ssl_factory_factory(): | |
106 | from twisted.internet.ssl import DefaultOpenSSLContextFactory | |
107 | class ChainingOpenSSLContextFactory(DefaultOpenSSLContextFactory): | |
108 | def __init__(self, *args, **kwargs): | |
109 | self.chain = None | |
110 | if kwargs.has_key("certificateChainFile"): | |
111 | self.chain = kwargs["certificateChainFile"] | |
112 | del kwargs["certificateChainFile"] | |
113 | ||
114 | DefaultOpenSSLContextFactory.__init__(self, *args, **kwargs) | |
115 | ||
116 | def cacheContext(self): | |
117 | DefaultOpenSSLContextFactory.cacheContext(self) | |
118 | if self.chain: | |
119 | self._context.use_certificate_chain_file(self.chain) | |
120 | self._context.use_privatekey_file(self.privateKeyFileName) | |
121 | ||
122 | return ChainingOpenSSLContextFactory | |
123 | ||
ef74f2c1 | 124 | serviceMaker = QWebIRCServiceMaker() |