Commit | Line | Data |
---|---|---|
bd299b17 | 1 | var url = require('url'), |
0fa2ca42 | 2 | fs = require('fs'), |
cbcd1a23 | 3 | crypto = require('crypto'), |
0fa2ca42 | 4 | node_static = require('node-static'), |
cbcd1a23 JA |
5 | _ = require('lodash'), |
6 | config = require('./configuration.js'); | |
a8bf3ea4 | 7 | |
a8bf3ea4 | 8 | |
a8bf3ea4 | 9 | |
186531ed D |
10 | var HttpHandler = function (config) { |
11 | var public_html = config.public_html || 'client/'; | |
12 | this.file_server = new node_static.Server(public_html); | |
a8bf3ea4 JA |
13 | }; |
14 | ||
186531ed D |
15 | module.exports.HttpHandler = HttpHandler; |
16 | ||
17 | ||
18 | ||
19 | HttpHandler.prototype.serve = function (request, response) { | |
b65ad8f1 | 20 | // The incoming requests base path (ie. /kiwiclient) |
b737610b | 21 | var base_path = global.config.http_base_path || '/kiwi', |
b65ad8f1 D |
22 | base_path_regex; |
23 | ||
24 | // Trim of any trailing slashes | |
25 | if (base_path.substr(base_path.length - 1) === '/') { | |
26 | base_path = base_path.substr(0, base_path.length - 1); | |
27 | } | |
0fa2ca42 | 28 | |
b65ad8f1 D |
29 | // Build the regex to match the base_path |
30 | base_path_regex = base_path.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); | |
0ae87edb D |
31 | |
32 | // Any asset request to head into the asset dir | |
b65ad8f1 | 33 | request.url = request.url.replace(base_path + '/assets/', '/assets/'); |
0ae87edb | 34 | |
b075a0d6 | 35 | // Any requests for /client to load the index file |
b65ad8f1 | 36 | if (request.url.match(new RegExp('^' + base_path_regex + '([/$]|$)', 'i'))) { |
b075a0d6 D |
37 | request.url = '/'; |
38 | } | |
39 | ||
0fa2ca42 | 40 | // If the 'magic' translation is requested, figure out the best language to use from |
b722f3c6 | 41 | // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation |
28cde487 JA |
42 | if (request.url.substr(0, 16) === '/assets/locales/') { |
43 | if (request.url === '/assets/locales/magic.json') { | |
44 | return serveMagicLocale.call(this, request, response); | |
45 | } else { | |
46 | response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16)); | |
47 | } | |
cbcd1a23 JA |
48 | } else if (request.url.substr(0, 21) === '/assets/settings.json') { |
49 | return serveSettings.call(this, request, response); | |
0fa2ca42 | 50 | } |
0ae87edb | 51 | |
186531ed | 52 | this.file_server.serve(request, response, function (err) { |
a8bf3ea4 | 53 | if (err) { |
bd299b17 JA |
54 | response.writeHead(err.status, err.headers); |
55 | response.end(); | |
a8bf3ea4 JA |
56 | } |
57 | }); | |
0fa2ca42 JA |
58 | }; |
59 | ||
60 | var serveMagicLocale = function (request, response) { | |
cbc8feae | 61 | var that = this; |
8e1ab29d | 62 | |
0fa2ca42 | 63 | if (request.headers['accept-language']) { |
28cde487 JA |
64 | fs.readdir('client/assets/locales', function (err, files) { |
65 | var available = [], | |
66 | i = 0, | |
cbc8feae | 67 | langs = request.headers['accept-language'].split(','); // Example: en-gb,en;q=0.5 |
28cde487 | 68 | |
8e1ab29d | 69 | // Get a list of the available translations we have |
0fa2ca42 | 70 | files.forEach(function (file) { |
28cde487 JA |
71 | if (file.substr(-5) === '.json') { |
72 | available.push(file.slice(0, -5)); | |
0fa2ca42 JA |
73 | } |
74 | }); | |
0fa2ca42 | 75 | |
8e1ab29d | 76 | // Sanitise the browsers accepted languages and the qualities |
28cde487 JA |
77 | for (i = 0; i < langs.length; i++) { |
78 | langs[i]= langs[i].split(';q='); | |
8e1ab29d | 79 | langs[i][0] = langs[i][0].toLowerCase(); |
28cde487 | 80 | langs[i][1] = (typeof langs[i][1] === 'string') ? parseFloat(langs[i][1]) : 1.0; |
0fa2ca42 | 81 | } |
8e1ab29d D |
82 | |
83 | // Sort the accepted languages by quality | |
28cde487 JA |
84 | langs.sort(function (a, b) { |
85 | return b[1] - a[1]; | |
86 | }); | |
8e1ab29d D |
87 | |
88 | // Serve the first language we have a translation for | |
28cde487 JA |
89 | for (i = 0; i < langs.length; i++) { |
90 | if (langs[i][0] === '*') { | |
91 | break; | |
92 | } else if (_.contains(available, langs[i][0])) { | |
cbc8feae | 93 | return that.file_server.serveFile('/assets/locales/' + langs[i][0] + '.json', 200, {Vary: 'Accept-Language', 'Content-Language': langs[i][0]}, request, response); |
28cde487 JA |
94 | } |
95 | } | |
8e1ab29d D |
96 | |
97 | serveFallbackLocale.call(that, request, response); | |
28cde487 | 98 | }); |
d2831ddd | 99 | } else { |
8e1ab29d | 100 | serveFallbackLocale.call(that, request, response); |
0fa2ca42 | 101 | } |
d2831ddd | 102 | }; |
cbc8feae | 103 | |
d2831ddd | 104 | var serveFallbackLocale = function (request, response) { |
b722f3c6 | 105 | //en-gb is our default language, so we serve this as the last possible answer for everything |
d2831ddd | 106 | this.file_server.serveFile('/assets/locales/en-gb.json', 200, {Vary: 'Accept-Language', 'Content-Language': 'en-gb'}, request, response); |
0fa2ca42 | 107 | }; |
cbcd1a23 | 108 | |
cbe80532 | 109 | var cached_settings = { |
498afd32 JA |
110 | debug: { |
111 | hash: '', | |
112 | settings: '' | |
113 | }, | |
114 | production: { | |
115 | hash: '', | |
116 | settings: '' | |
117 | } | |
cbe80532 JA |
118 | }; |
119 | ||
120 | config.on('loaded', function () { | |
498afd32 JA |
121 | cached_settings.debug.settings = cached_settings.production.settings = ''; |
122 | cached_settings.debug.hash = cached_settings.production.hash = ''; | |
cbe80532 JA |
123 | }); |
124 | ||
498afd32 | 125 | function generateSettings(request, debug, callback) { |
cbcd1a23 JA |
126 | var vars = { |
127 | server_settings: {}, | |
128 | client_plugins: [], | |
129 | translations: [], | |
130 | scripts: [ | |
131 | [ | |
132 | 'libs/lodash.min.js' | |
133 | ], | |
134 | 'libs/backbone.min.js', | |
135 | 'libs/jed.js' | |
136 | ] | |
498afd32 | 137 | }; |
cbcd1a23 | 138 | |
498afd32 | 139 | if (debug) { |
cbcd1a23 JA |
140 | vars.scripts = vars.scripts.concat([ |
141 | 'src/app.js', | |
142 | [ | |
143 | 'src/models/application.js', | |
144 | 'src/models/gateway.js' | |
145 | ], | |
146 | [ | |
147 | 'src/models/newconnection.js', | |
148 | 'src/models/panellist.js', | |
149 | 'src/models/networkpanellist.js', | |
150 | 'src/models/panel.js', | |
151 | 'src/models/member.js', | |
152 | 'src/models/memberlist.js', | |
153 | 'src/models/network.js' | |
154 | ], | |
155 | ||
156 | [ | |
157 | 'src/models/query.js', | |
158 | 'src/models/channel.js', | |
159 | 'src/models/server.js', | |
160 | 'src/models/applet.js' | |
161 | ], | |
162 | ||
163 | [ | |
164 | 'src/applets/settings.js', | |
165 | 'src/applets/chanlist.js', | |
166 | 'src/applets/scripteditor.js' | |
167 | ], | |
168 | ||
169 | [ | |
170 | 'src/models/pluginmanager.js', | |
171 | 'src/models/datastore.js', | |
172 | 'src/helpers/utils.js' | |
173 | ], | |
174 | ||
175 | // Some views extend these, so make sure they're loaded beforehand | |
176 | [ | |
177 | 'src/views/panel.js' | |
178 | ], | |
179 | ||
180 | [ | |
181 | 'src/views/channel.js', | |
182 | 'src/views/applet.js', | |
183 | 'src/views/application.js', | |
184 | 'src/views/apptoolbar.js', | |
185 | 'src/views/controlbox.js', | |
186 | 'src/views/favicon.js', | |
187 | 'src/views/mediamessage.js', | |
188 | 'src/views/member.js', | |
189 | 'src/views/memberlist.js', | |
190 | 'src/views/menubox.js', | |
191 | 'src/views/networktabs.js', | |
192 | 'src/views/nickchangebox.js', | |
193 | 'src/views/resizehandler.js', | |
194 | 'src/views/serverselect.js', | |
195 | 'src/views/statusmessage.js', | |
196 | 'src/views/tabs.js', | |
197 | 'src/views/topicbar.js', | |
198 | 'src/views/userbox.js' | |
199 | ] | |
200 | ]); | |
201 | } else { | |
202 | vars.scripts.push('kiwi.min.js'); | |
203 | } | |
204 | ||
205 | // Any restricted server mode set? | |
206 | if (config.get().restrict_server) { | |
207 | vars.server_settings = { | |
208 | connection: { | |
209 | server: config.get().restrict_server, | |
210 | port: config.get().restrict_server_port || 6667, | |
211 | ssl: config.get().restrict_server_ssl, | |
212 | channel: config.get().restrict_server_channel, | |
213 | nick: config.get().restrict_server_nick, | |
214 | allow_change: false | |
215 | } | |
216 | }; | |
217 | } | |
218 | ||
219 | // Any client default settings? | |
220 | if (config.get().client) { | |
221 | vars.server_settings.client = config.get().client; | |
222 | } | |
223 | ||
224 | // Any client plugins? | |
225 | if (config.get().client_plugins && config.get().client_plugins.length > 0) { | |
226 | vars.client_plugins = config.get().client_plugins; | |
227 | } | |
228 | ||
229 | fs.readFile(__dirname + '/../client/assets/src/translations/translations.json', function (err, translations) { | |
230 | if (err) { | |
cbe80532 | 231 | return callback(err); |
cbcd1a23 JA |
232 | } |
233 | ||
234 | var translation_files; | |
235 | translations = JSON.parse(translations); | |
236 | fs.readdir(__dirname + '/../client/assets/src/translations/', function (err, pofiles) { | |
498afd32 | 237 | var hash, settings; |
cbcd1a23 | 238 | if (err) { |
cbe80532 | 239 | return callback(err); |
cbcd1a23 JA |
240 | } |
241 | ||
242 | pofiles.forEach(function (file) { | |
243 | var locale = file.slice(0, -3); | |
244 | if ((file.slice(-3) === '.po') && (locale !== 'template')) { | |
245 | vars.translations.push({tag: locale, language: translations[locale]}); | |
246 | } | |
247 | }); | |
248 | ||
498afd32 JA |
249 | settings = cached_settings[debug?'debug':'production']; |
250 | settings.settings = JSON.stringify(vars); | |
251 | settings.hash = crypto.createHash('md5').update(cached_settings.settings).digest('hex'); | |
cbcd1a23 | 252 | |
498afd32 | 253 | return callback(null, settings); |
cbe80532 JA |
254 | }); |
255 | }); | |
256 | } | |
cbcd1a23 | 257 | |
cbe80532 | 258 | function serveSettings(request, response) { |
498afd32 JA |
259 | var referrer_url, |
260 | debug = false, | |
261 | settings; | |
262 | ||
263 | if (request.headers['referer']) { | |
264 | referrer_url = url.parse(request.headers['referer'], true); | |
265 | if (referrer_url.query && referrer_url.query.debug) { | |
266 | debug = true; | |
267 | } | |
268 | } | |
269 | ||
270 | settings = cached_settings[debug?'debug':'production']; | |
271 | if (settings.settings === '') { | |
272 | generateSettings(request, debug, function (err, settings) { | |
cbe80532 JA |
273 | if (err) { |
274 | response.statusCode = 500; | |
275 | response.end(); | |
276 | } else { | |
277 | sendSettings.call(this, request, response, settings); | |
278 | } | |
cbcd1a23 | 279 | }); |
cbe80532 | 280 | } else { |
498afd32 | 281 | sendSettings.call(this, request, response, settings); |
cbe80532 JA |
282 | } |
283 | } | |
284 | ||
285 | function sendSettings(request, response, settings) { | |
286 | if (request.headers['if-none-match'] && request.headers['if-none-match'] === settings.hash) { | |
287 | response.writeHead(304, 'Not Modified'); | |
288 | return response.end(); | |
289 | } | |
290 | ||
291 | response.writeHead(200, { | |
292 | 'ETag': settings.hash, | |
293 | 'Content-Type': 'application/json' | |
cbcd1a23 | 294 | }); |
cbe80532 | 295 | response.end(settings.settings); |
cbcd1a23 | 296 | } |