1 var url
= require('url'),
3 crypto
= require('crypto'),
4 node_static
= require('node-static'),
6 config
= require('./configuration.js');
10 var HttpHandler = function (config
) {
11 var public_html
= config
.public_html
|| 'client/';
12 this.file_server
= new node_static
.Server(public_html
);
15 module
.exports
.HttpHandler
= HttpHandler
;
19 HttpHandler
.prototype.serve = function (request
, response
) {
20 // The incoming requests base path (ie. /kiwiclient)
21 var base_path
= global
.config
.http_base_path
|| '/kiwi',
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);
29 // Build the regex to match the base_path
30 base_path_regex
= base_path
.replace(/[-/\\^$*+?.()|[\]{}]/g
, '\\$&');
32 // Any asset request to head into the asset dir
33 request
.url
= request
.url
.replace(base_path
+ '/assets/', '/assets/');
35 // Any requests for /client to load the index file
36 if (request
.url
.match(new RegExp('^' + base_path_regex
+ '([/$]|$)', 'i'))) {
40 // If the 'magic' translation is requested, figure out the best language to use from
41 // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
42 if (request
.url
.substr(0, 16) === '/assets/locales/') {
43 if (request
.url
=== '/assets/locales/magic.json') {
44 return serveMagicLocale
.call(this, request
, response
);
46 response
.setHeader('Content-Language', request
.url
.substr(16, request
.url
.indexOf('.') - 16));
48 } else if (request
.url
.substr(0, 21) === '/assets/settings.json') {
49 return serveSettings
.call(this, request
, response
);
52 this.file_server
.serve(request
, response
, function (err
) {
54 response
.writeHead(err
.status
, err
.headers
);
60 var serveMagicLocale = function (request
, response
) {
63 if (request
.headers
['accept-language']) {
64 fs
.readdir('client/assets/locales', function (err
, files
) {
67 langs
= request
.headers
['accept-language'].split(','); // Example: en-gb,en;q=0.5
69 // Get a list of the available translations we have
70 files
.forEach(function (file
) {
71 if (file
.substr(-5) === '.json') {
72 available
.push(file
.slice(0, -5));
76 // Sanitise the browsers accepted languages and the qualities
77 for (i
= 0; i
< langs
.length
; i
++) {
78 langs
[i
]= langs
[i
].split(';q=');
79 langs
[i
][0] = langs
[i
][0].toLowerCase();
80 langs
[i
][1] = (typeof langs
[i
][1] === 'string') ? parseFloat(langs
[i
][1]) : 1.0;
83 // Sort the accepted languages by quality
84 langs
.sort(function (a
, b
) {
88 // Serve the first language we have a translation for
89 for (i
= 0; i
< langs
.length
; i
++) {
90 if (langs
[i
][0] === '*') {
92 } else if (_
.contains(available
, langs
[i
][0])) {
93 return that
.file_server
.serveFile('/assets/locales/' + langs
[i
][0] + '.json', 200, {Vary
: 'Accept-Language', 'Content-Language': langs
[i
][0]}, request
, response
);
97 serveFallbackLocale
.call(that
, request
, response
);
100 serveFallbackLocale
.call(that
, request
, response
);
104 var serveFallbackLocale = function (request
, response
) {
105 //en-gb is our default language, so we serve this as the last possible answer for everything
106 this.file_server
.serveFile('/assets/locales/en-gb.json', 200, {Vary
: 'Accept-Language', 'Content-Language': 'en-gb'}, request
, response
);
109 var cached_settings
= {
120 config
.on('loaded', function () {
121 cached_settings
.debug
.settings
= cached_settings
.production
.settings
= '';
122 cached_settings
.debug
.hash
= cached_settings
.production
.hash
= '';
125 function generateSettings(request
, debug
, callback
) {
134 'libs/backbone.min.js',
140 vars
.scripts
= vars
.scripts
.concat([
143 'src/models/application.js',
144 'src/models/gateway.js'
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'
157 'src/models/query.js',
158 'src/models/channel.js',
159 'src/models/server.js',
160 'src/models/applet.js'
164 'src/applets/settings.js',
165 'src/applets/chanlist.js',
166 'src/applets/scripteditor.js'
170 'src/models/pluginmanager.js',
171 'src/models/datastore.js',
172 'src/helpers/utils.js'
175 // Some views extend these, so make sure they're loaded beforehand
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',
197 'src/views/topicbar.js',
198 'src/views/userbox.js'
202 vars
.scripts
.push('kiwi.min.js');
205 // Any restricted server mode set?
206 if (config
.get().restrict_server
) {
207 vars
.server_settings
= {
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
,
219 // Any client default settings?
220 if (config
.get().client
) {
221 vars
.server_settings
.client
= config
.get().client
;
224 // Any client plugins?
225 if (config
.get().client_plugins
&& config
.get().client_plugins
.length
> 0) {
226 vars
.client_plugins
= config
.get().client_plugins
;
229 fs
.readFile(__dirname
+ '/../client/assets/src/translations/translations.json', function (err
, translations
) {
231 return callback(err
);
234 var translation_files
;
235 translations
= JSON
.parse(translations
);
236 fs
.readdir(__dirname
+ '/../client/assets/src/translations/', function (err
, pofiles
) {
239 return callback(err
);
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
]});
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');
253 return callback(null, settings
);
258 function serveSettings(request
, response
) {
263 if (request
.headers
['referer']) {
264 referrer_url
= url
.parse(request
.headers
['referer'], true);
265 if (referrer_url
.query
&& referrer_url
.query
.debug
) {
270 settings
= cached_settings
[debug
?'debug':'production'];
271 if (settings
.settings
=== '') {
272 generateSettings(request
, debug
, function (err
, settings
) {
274 response
.statusCode
= 500;
277 sendSettings
.call(this, request
, response
, settings
);
281 sendSettings
.call(this, request
, response
, settings
);
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();
291 response
.writeHead(200, {
292 'ETag': settings
.hash
,
293 'Content-Type': 'application/json'
295 response
.end(settings
.settings
);