var sys = require('sys'), http = require('http'), fs = require('fs'), url = require('url'), events = require('events'); var DEFAULT_PORT = 8000; function main(argv) { new HttpServer({ 'GET': createServlet(StaticServlet), 'HEAD': createServlet(StaticServlet) }).start(Number(argv[2]) || DEFAULT_PORT); } function escapeHtml(value) { return value.toString(). replace('<', '<'). replace('>', '>'). replace('"', '"'); } function createServlet(Class) { var servlet = new Class(); return servlet.handleRequest.bind(servlet); } /** * An Http server implementation that uses a map of methods to decide * action routing. * * @param {Object} Map of method => Handler function */ function HttpServer(handlers) { this.handlers = handlers; this.server = http.createServer(this.handleRequest_.bind(this)); } HttpServer.prototype.start = function(port) { this.port = port; this.server.listen(port); sys.puts('Http Server running at http://127.0.0.1:' + port + '/'); }; HttpServer.prototype.parseUrl_ = function(urlString) { var parsed = url.parse(urlString); parsed.pathname = url.resolve('/', parsed.pathname); return url.parse(url.format(parsed), true); }; HttpServer.prototype.handleRequest_ = function(req, res) { var logEntry = req.method + ' ' + req.url; if (req.headers['user-agent']) { logEntry += ' ' + req.headers['user-agent']; } sys.puts(logEntry); req.url = this.parseUrl_(req.url); var handler = this.handlers[req.method]; if (!handler) { res.writeHead(501); res.end(); } else { handler.call(this, req, res); } }; /** * Handles static content. */ function StaticServlet() {} StaticServlet.MimeMap = { 'txt': 'text/plain', 'html': 'text/html', 'css': 'text/css', 'xml': 'application/xml', 'json': 'application/json', 'js': 'application/javascript', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'png': 'image/png', 'manifest': 'text/cache-manifest', // it should be application/font-woff // but only this silences chrome warnings 'woff': 'font/opentype' }; StaticServlet.prototype.handleRequest = function(req, res) { var self = this; var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){ return String.fromCharCode(parseInt(hex, 16)); }); var parts = path.split('/'); if (parts[parts.length-1].charAt(0) === '.') return self.sendForbidden_(req, res, path); // favicon rewriting if (path === './favicon.ico') return self.sendFile_(req, res, './lib/nodeserver/favicon.ico'); // docs rewriting var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/, IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/, match; if (!IGNORED.test(path) && (match = path.match(REWRITE))) { path = path.replace(match[0], '/index.html'); sys.puts('Rewrite to ' + path); } // rewrite : to _ to be GAE-friendly path = path.replace(':', '_'); // end of docs rewriting fs.stat(path, function(err, stat) { if (err) return self.sendMissing_(req, res, path); if (stat.isDirectory()) return fs.stat(path + 'index.html', function(err, stat) { // send index.html if exists if (!err) return self.sendFile_(req, res, path + 'index.html'); // list files otherwise return self.sendDirectory_(req, res, path); }); return self.sendFile_(req, res, path); }); }; StaticServlet.prototype.sendError_ = function(req, res, error) { res.writeHead(500, { 'Content-Type': 'text/html' }); res.write('\n'); res.write('
' + escapeHtml(sys.inspect(error)) + ''); sys.puts('500 Internal Server Error'); sys.puts(sys.inspect(error)); }; StaticServlet.prototype.sendMissing_ = function(req, res, path) { path = path.substring(1); res.writeHead(404, { 'Content-Type': 'text/html' }); res.write('\n'); res.write('
The requested URL ' + escapeHtml(path) + ' was not found on this server.
' ); res.end(); sys.puts('404 Not Found: ' + path); }; StaticServlet.prototype.sendForbidden_ = function(req, res, path) { path = path.substring(1); res.writeHead(403, { 'Content-Type': 'text/html' }); res.write('\n'); res.write('You do not have permission to access ' + escapeHtml(path) + ' on this server.
' ); res.end(); sys.puts('403 Forbidden: ' + path); }; StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) { res.writeHead(301, { 'Content-Type': 'text/html', 'Location': redirectUrl }); res.write('\n'); res.write('The document has moved here.
' ); res.end(); sys.puts('401 Moved Permanently: ' + redirectUrl); }; StaticServlet.prototype.sendFile_ = function(req, res, path) { var self = this; var file = fs.createReadStream(path); res.writeHead(200, { // CSP headers, uncomment to enable CSP //"X-WebKit-CSP": "default-src 'self';", //"X-Content-Security-Policy": "default-src 'self'", 'Content-Type': StaticServlet. MimeMap[path.split('.').pop()] || 'text/plain' }); if (req.method === 'HEAD') { res.end(); } else { file.on('data', res.write.bind(res)); file.on('close', function() { res.end(); }); file.on('error', function(error) { self.sendError_(req, res, error); }); } }; StaticServlet.prototype.sendDirectory_ = function(req, res, path) { var self = this; if (path.match(/[^\/]$/)) { req.url.pathname += '/'; var redirectUrl = url.format(url.parse(url.format(req.url))); return self.sendRedirect_(req, res, redirectUrl); } fs.readdir(path, function(err, files) { if (err) return self.sendError_(req, res, error); if (!files.length) return self.writeDirectoryIndex_(req, res, path, []); var remaining = files.length; files.forEach(function(fileName, index) { fs.stat(path + '/' + fileName, function(err, stat) { if (err) return self.sendError_(req, res, err); if (stat.isDirectory()) { files[index] = fileName + '/'; } if (!(--remaining)) return self.writeDirectoryIndex_(req, res, path, files); }); }); }); }; StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) { path = path.substring(1); res.writeHead(200, { 'Content-Type': 'text/html' }); if (req.method === 'HEAD') { res.end(); return; } res.write('\n'); res.write('# OpenURL by Stefan'tommie' Tomanek
#
# 05.06.2002
# * complete rewrite
use strict;
use vars qw($VERSION %IRSSI);
$VERSION = "20030208";
%IRSSI = (
authors => "Stefan 'tommie' Tomanek",
contact => "stefan\@pico.ruhr.de",
name => "OpenURL",
description => "Stores URLs in a list and launches mail, web or ftp software",
license => "GPLv2",
url => "http://scripts.irssi.org",
changed => "$VERSION",
commands => "openurl"
);
use Irssi 20020324;
use Irssi::TextUI;
use Irssi::UI;
use vars qw(@urls %urltypes $recent);
$recent = 1;
# RegExp & defaultcommands
%urltypes = ( http => { regexp => qr#((?:https?://[^\s<>"]+|www\.[-a-z0-9.]+)[^\s.,;<">\):])#, cmd => 'w3m "$1"' },
ftp => { regexp => qr#((?:ftp://[^\s<>"]+|ftp\.[-a-z0-9.]+)[^\s.,;<">\):])#, cmd => 'ncftp "$1"' },
mail => { regexp => qr#([-_a-z0-9.]+\@[-a-z0-9.]+\.[-a-z0-9.]+)#, cmd => 'mutt "$1" -s "$2"' },
);
sub draw_box ($$$$) {
my ($title, $text, $footer, $colour) = @_;
my $box = '';
$box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
foreach (split(/\n/, $text)) {
$box .= '%R|%n '.$_."\n";
} $box .= '%R`--<%n'.$footer.'%R>->%n';
$box =~ s/%.//g unless $colour;
return $box;
}
sub show_help() {
my $help=$IRSSI{name}." ".$VERSION."
/openurl
List the saved URLs
/openurl <number> <number>...
Load the corresponding URLs in your browser/mailer
/openurl paste <number> <number>...
Paste the selected URLs to the current channel/query
/openurl topics
Look for URLs in channel topics
/openurl clear
Clear all URLs
/openurl help
Display this help
";
my $text = '';
foreach (split(/\n/, $help)) {
$_ =~ s/^\/(.*)$/%9\/$1%9/;
$text .= $_."\n";
}
print CLIENTCRAP draw_box($IRSSI{name}." help", $text, "help", 1) ;
}
sub list_urls {
my $string = '';
my $i = 1;
foreach (@urls) {
my $text = $_->{url};
my $url = $_->{url};
$text = $_->{text} if Irssi::settings_get_bool('openurl_display_context');
$url =~ s/%/%%/g;
$text =~ s/%/%%/g;
$text =~ s/\Q$url/%U$url%U/;
if ($recent-1 == $i) {
$string .= '%B�%n';
} else {
$string .= ' ';
}
$string .= '%r['.$i.']%n ';
$string .= '<'.$_->{channel};
$string .= '/'.$_->{nick} unless $_->{nick} eq "";
$string .= '> ';
$string .= $text." %n\n";
$i++;
}
print CLIENTCRAP draw_box("OpenURL", $string, "URLs", 1);
}
sub event_private_message {
my ($server, $text, $nick, $address) = @_;
process_line($server, $nick, $nick, $text);
}
sub event_public_message {
my ($server, $text, $nick, $address, $target) = @_;
process_line($server, $target, $nick, $text);
}
sub event_topic_changed {
my ($channel) = @_;
process_line($channel->{server}, $channel->{name}, "", $channel->{topic});
}
sub process_line ($$$$) {
my ($server, $target, $nick, $line) = @_;
my $url = get_url($line);
if ($url) {
my $type = url_type($url);
return unless Irssi::settings_get_bool('openurl_watch_'.$type);
new_url($server, $target, $nick, $line, $url);
}
}
sub get_url ($) {
my ($text) = @_;
foreach (keys %urltypes) {
return $1 if ($text =~ /$urltypes{$_}->{regexp}/);
}
}
sub url_type ($) {
my ($url) = @_;
foreach (keys %urltypes) {
return $_ if ($url =~ /$urltypes{$_}->{regexp}/);
}
}
sub launch_url ($) {
my ($url) = @_;
my $type = url_type($url);
my $address = $url;
my $suffix= "";
if ($type eq "mail") {
$address = $1 if $url =~ /(.*?@.*?)($|\?)/;
$suffix = $2 if $url =~ /(.*?@.*?)\?subject=(.*)/;
}
my $command = Irssi::settings_get_str("openurl_app_".$type);
$command =~ s/\$1/$address/;
$command =~ s/\$2/$suffix/;
system($command);
}
sub new_url ($$$$$) {
my ($server, $channel, $nick, $text, $url) = @_;
$recent = 1 if ($recent > Irssi::settings_get_int('openurl_max_urls'));
# Check for existance of URL
my $i = 1;
foreach (@urls) {
if ($text eq $_->{text} && $channel eq $_->{channel}) {
my $note_id = add_note($server, $channel, $i);
push @{$_->{notes}}, $note_id;
return();
}
$i++;
}
if (defined $urls[$recent-1]) {
del_notes($recent);
}
$urls[$recent-1] = {channel => $channel,
text => $text,
nick => $nick,
url => $url,
notes => [],
};
my $note_id = add_note($server, $channel, $recent);
push @{$urls[$recent-1]{notes}}, $note_id;
$recent++;
}
sub del_notes ($) {
my ($num) = @_;
my $view;
my $witem = Irssi::window_item_find($urls[$num-1]->{channel});
if (defined $witem) {
$view = $witem->window()->view();
}
if (defined $view) {
foreach (@{$urls[$num-1]->{notes}}) {
my $line = $view->get_bookmark($_);
$view->remove_line($line) if defined $line;
$view->set_bookmark($_, undef);
}
@{$urls[$num-1]->{notes}} = ();
$view->redraw();
}
}
sub add_note ($$$) {
my ($server, $target, $num) = @_;
my $witem;
if (defined $server) {
$witem = $server->window_item_find($target);
} else {
$witem = Irssi::window_item_find($target);
}
if (defined $witem) {
$witem->print("%R>>%n OpenURL ".$num, MSGLEVEL_CLIENTCRAP);
# create a unique ID for the mark
my $foo = time().'-'.int(rand()*1000);
$witem->window()->view()->set_bookmark_bottom("openurl_".$num.'-'.$foo);
return("openurl_".$num.'-'.$foo);
}
return(undef);
}
sub clear_urls {
del_notes($_) foreach (0..scalar(@urls)-1);
pop(@urls) foreach (1..scalar(@urls));
$recent = 1;
print CLIENTCRAP '%R>>%n URLs cleared';
}
sub cmd_openurl ($$$) {
my ($arg, $server, $witem) = @_;
my @args = split(/ /, $arg);
if (scalar(@args) == 0) {
list_urls;
} elsif ($args[0] eq 'clear') {
clear_urls;
} elsif ($args[0] eq 'topics') {
event_topic_changed($_) foreach (Irssi::channels());
} elsif ($args[0] eq 'help') {
show_help();
} elsif ($args[0] eq 'open') {
launch_url($args[1]);
} else {
my $paste = 0;
if ($args[0] eq 'paste') {
$paste = 1;
shift(@args);
}
foreach (@args) {
next unless /\d+/;
next unless defined $urls[$_-1];
my $url = $urls[$_-1]->{url};
if ($paste == 1) {
if (ref $witem && ($witem->{type} eq "CHANNEL" || $witem->{type} eq "QUERY")) {
$witem->command("MSG ".$witem->{name}." ".$url);
}
} else {
launch_url($url);
}
}
}
}
foreach (keys %urltypes) {
Irssi::settings_add_str($IRSSI{'name'}, 'openurl_app_'.$_, "screen ".$urltypes{$_}->{cmd});
Irssi::settings_add_bool($IRSSI{'name'}, 'openurl_watch_'.$_, 1);
}
Irssi::settings_add_int($IRSSI{'name'}, 'openurl_max_urls', 20);
Irssi::settings_add_bool($IRSSI{'name'}, 'openurl_display_context', 1);
Irssi::signal_add_last("message private", "event_private_message");
Irssi::signal_add_last("message public", "event_public_message");
Irssi::signal_add_last("channel topic changed", "event_topic_changed");
#Irssi::signal_add('open url', \&launch_url);
foreach my $cmd ('topics', 'clear', 'paste', 'help') {
Irssi::command_bind('openurl '.$cmd => sub {
cmd_openurl("$cmd ".$_[0], $_[1], $_[2]); });
}
Irssi::command_bind('openurl', 'cmd_openurl');
print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /openurl help for help';