| | 28 | |
|---|
| | 29 | my $tt = Template->new({ |
|---|
| | 30 | INCLUDE_PATH => 'templates', |
|---|
| | 31 | |
|---|
| | 32 | START_TAG => quotemeta("[["), |
|---|
| | 33 | END_TAG => quotemeta("]]"), |
|---|
| | 34 | PRE_CHOMP => 2, # CHOMP_COLLAPSE |
|---|
| | 35 | POST_CHOMP => 2, # CHOMP_COLLAPSE |
|---|
| | 36 | RECURSION => 1, |
|---|
| | 37 | }); |
|---|
| | 38 | |
|---|
| | 39 | sub set_config_listenaddr { |
|---|
| | 40 | my ($self, $addr) = @_; |
|---|
| | 41 | |
|---|
| | 42 | $self->{listenaddr} = DJabberd::Util::as_bind_addr($addr); |
|---|
| | 43 | |
|---|
| | 44 | # We default to localhost if no interface is specified |
|---|
| | 45 | # User can explicitly say 0.0.0.0: to bind to everything. |
|---|
| | 46 | $self->{listenaddr} = "127.0.0.1:".$self->{listenaddr} if $self->{listenaddr} =~ /^\d+$/; |
|---|
| | 47 | } |
|---|
| 26 | | |
|---|
| 27 | | # Configure a new service in Perlbal |
|---|
| 28 | | my $ctx = Perlbal::CommandContext->new; |
|---|
| 29 | | my $writer = sub { |
|---|
| 30 | | $logger->info($_[0]); |
|---|
| 31 | | }; |
|---|
| 32 | | |
|---|
| 33 | | my $c = sub { |
|---|
| 34 | | my ($line) = @_; |
|---|
| 35 | | my $success = Perlbal::run_manage_command($line, $writer, $ctx); |
|---|
| 36 | | |
|---|
| 37 | | unless ($success) { |
|---|
| 38 | | $logger->logdie("Error configuring Perlbal service when running ".$line); |
|---|
| 39 | | } |
|---|
| 40 | | }; |
|---|
| 41 | | |
|---|
| 42 | | $c->("LOAD cgilike"); |
|---|
| 43 | | $c->("CREATE SERVICE djabberdadmin"); |
|---|
| 44 | | $c->("SET listen = 127.0.0.1:8045"); |
|---|
| 45 | | $c->("SET role = web_server"); |
|---|
| 46 | | $c->("SET plugins = cgilike"); |
|---|
| 47 | | $c->("PERLHANDLER = DJabberd::WebAdmin::handle_web_request"); |
|---|
| 48 | | $c->("ENABLE djabberdadmin"); |
|---|
| 49 | | |
|---|
| 50 | | # Now for a bit of yuck. |
|---|
| 51 | | # Perlbal's not really designed to run in someone else's event loop, |
|---|
| 52 | | # so we have to fake it out a bit and do some of the stuff it would |
|---|
| 53 | | # otherwise have done in its main run() function. |
|---|
| 54 | | # TODO: Make a nicer API for embedding Perlbal |
|---|
| 55 | | |
|---|
| 56 | | $Perlbal::run_started = 1; |
|---|
| 57 | | Perlbal::run_global_hook("pre_event_loop"); |
|---|
| | 51 | |
|---|
| | 52 | $logger->logdie("No ListenAddr specified for WebAdmin") unless $self->{listenaddr}; |
|---|
| | 53 | |
|---|
| | 54 | # We depend on the "cgilike" plugin |
|---|
| | 55 | # FIXME: Should add a nice API to Perlbal for this |
|---|
| | 56 | Perlbal::run_manage_command("LOAD cgilike", sub { $logger->info('[perlbal] '.$_[0]); }); |
|---|
| | 57 | |
|---|
| | 58 | # Create an anonymous Perlbal service |
|---|
| | 59 | my $pbsvc = Perlbal->create_service(); |
|---|
| | 60 | |
|---|
| | 61 | $pbsvc->set('listen', $self->{listenaddr}); |
|---|
| | 62 | $pbsvc->set('role', 'web_server'); |
|---|
| | 63 | $pbsvc->set('plugins', 'cgilike'); |
|---|
| | 64 | |
|---|
| | 65 | # It'd be good if there was a nicer API to do this, but whatever |
|---|
| | 66 | $pbsvc->run_manage_command('PERLHANDLER = DJabberd::WebAdmin::handle_web_request'); |
|---|
| | 67 | |
|---|
| | 68 | $pbsvc->enable(); |
|---|
| | 69 | |
|---|
| | 70 | # Let Perlbal do any global initialization it needs to do. |
|---|
| | 71 | Perlbal::initialize(); |
|---|
| | 134 | sub handle_static_resource { |
|---|
| | 135 | my ($r, $name) = @_; |
|---|
| | 136 | |
|---|
| | 137 | my $fn = undef; |
|---|
| | 138 | my $type = undef; |
|---|
| | 139 | |
|---|
| | 140 | if ($name eq 'style') { |
|---|
| | 141 | $fn = 'stat/style.css'; |
|---|
| | 142 | $type = 'text/css'; |
|---|
| | 143 | } |
|---|
| | 144 | else { |
|---|
| | 145 | $fn = 'stat/'.$name.'.png'; |
|---|
| | 146 | $type = 'image/png'; |
|---|
| | 147 | } |
|---|
| | 148 | |
|---|
| | 149 | return 404 unless defined($fn) && -f $fn; |
|---|
| | 150 | |
|---|
| | 151 | $r->response_header('Content-type' => $type); |
|---|
| | 152 | $r->send_response_header(); |
|---|
| | 153 | |
|---|
| | 154 | # FIXME: Should really add an API to Cgilike's $r for this, which can then use sendfile |
|---|
| | 155 | # This is lame. |
|---|
| | 156 | |
|---|
| | 157 | return 404 unless open (STATFILE, '<', $fn); |
|---|
| | 158 | |
|---|
| | 159 | # FIXME: Really should to binmode() the fh underlying $r, but no nice API for this right now |
|---|
| | 160 | # and DJabberd doesn't work on Windows anyway. |
|---|
| | 161 | binmode STATFILE; |
|---|
| | 162 | |
|---|
| | 163 | my $buf = ""; |
|---|
| | 164 | while (read(STATFILE, $buf, 1024)) { |
|---|
| | 165 | print $buf; |
|---|
| | 166 | } |
|---|
| | 167 | |
|---|
| | 168 | close(STATFILE); |
|---|
| | 169 | |
|---|
| | 170 | return Perlbal::Plugin::Cgilike::HANDLED; |
|---|
| | 171 | } |
|---|
| | 172 | |
|---|
| 137 | | print q{<html><head><title>}.($title ? ehtml($title)." - " : '').q{DJabberd Web Admin</title><body>}; |
|---|
| 138 | | |
|---|
| 139 | | print "<h1>".ehtml($title)."</h1>"; |
|---|
| 140 | | |
|---|
| 141 | | print "<div id='body'>"; |
|---|
| 142 | | $page->print_body; |
|---|
| 143 | | print "</div>"; |
|---|
| 144 | | |
|---|
| 145 | | print "<div id='vhostselector'>"; |
|---|
| 146 | | print "<h1>Configured VHosts</h1>"; |
|---|
| 147 | | print "<ul>"; |
|---|
| 148 | | |
|---|
| 149 | | $server->foreach_vhost(sub { |
|---|
| 150 | | my $vhost = shift; |
|---|
| 151 | | my $name = $vhost->server_name; |
|---|
| 152 | | print "<li><a href='/".ehtml($name)."/'>".ehtml($name)."</a></li>"; |
|---|
| 153 | | }); |
|---|
| 154 | | |
|---|
| 155 | | print "</ul>"; |
|---|
| 156 | | print "</div>"; |
|---|
| 157 | | |
|---|
| 158 | | print q{</body></html>}; |
|---|
| | 215 | |
|---|
| | 216 | my @pathbits = $r->path_segments; |
|---|
| | 217 | |
|---|
| | 218 | my @tabs = ( |
|---|
| | 219 | { |
|---|
| | 220 | caption => 'Summary', |
|---|
| | 221 | urlname => 'summary', |
|---|
| | 222 | }, |
|---|
| | 223 | { |
|---|
| | 224 | caption => 'Client Sessions', |
|---|
| | 225 | urlname => 'clients', |
|---|
| | 226 | }, |
|---|
| | 227 | { |
|---|
| | 228 | caption => 'Server Sessions', |
|---|
| | 229 | urlname => 'servers', |
|---|
| | 230 | }, |
|---|
| | 231 | ); |
|---|
| | 232 | |
|---|
| | 233 | $tt->process('page.tt', { |
|---|
| | 234 | section_title => $title ? $title : "DJabberd Web Admin", |
|---|
| | 235 | page_title => 'Summary', |
|---|
| | 236 | head_title => sub { ($title ? $title.' - ' : '')."DJabberd Web Admin"; }, |
|---|
| | 237 | body => sub { return ${ capture_output(sub { $page->print_body; }) }; }, |
|---|
| | 238 | tabs => [ |
|---|
| | 239 | map { |
|---|
| | 240 | { |
|---|
| | 241 | caption => $_->{caption}, |
|---|
| | 242 | url => '../'.$_->{urlname}.'/', |
|---|
| | 243 | current => ($pathbits[1] eq $_->{urlname} ? 1 : 0), |
|---|
| | 244 | } |
|---|
| | 245 | } @tabs |
|---|
| | 246 | ], |
|---|
| | 247 | vhosts => sub { |
|---|
| | 248 | my @ret = (); |
|---|
| | 249 | $server->foreach_vhost(sub { |
|---|
| | 250 | my $vhost = shift; |
|---|
| | 251 | my $name = $vhost->server_name; |
|---|
| | 252 | push @ret, { |
|---|
| | 253 | hostname => $name, # The real hostname |
|---|
| | 254 | url => '/'.$name.'/summary/', # FIXME: should urlencode $name here |
|---|
| | 255 | name => $name, # Some display name (just the hostname for now) |
|---|
| | 256 | current => ($pathbits[0] eq $name ? 1 : 0), |
|---|
| | 257 | }; |
|---|
| | 258 | }); |
|---|
| | 259 | return [ sort { $a->{name} cmp $b->{name} } @ret ]; |
|---|
| | 260 | }, |
|---|
| | 261 | djabberd_version => $DJabberd::VERSION, |
|---|
| | 262 | perlbal_version => $Perlbal::VERSION, |
|---|
| | 263 | }, $r); |
|---|
| | 264 | |
|---|
| | 265 | } |
|---|
| | 266 | |
|---|
| | 267 | sub capture_output { |
|---|
| | 268 | my $sub = shift; |
|---|
| | 269 | |
|---|
| | 270 | my $fh = Symbol::gensym(); |
|---|
| | 271 | my $ret = ""; |
|---|
| | 272 | open($fh, '>', \$ret); |
|---|
| | 273 | |
|---|
| | 274 | my $oldfh = select($fh); |
|---|
| | 275 | |
|---|
| | 276 | $sub->(@_); |
|---|
| | 277 | |
|---|
| | 278 | select($oldfh); |
|---|
| | 279 | close($fh); |
|---|
| | 280 | |
|---|
| | 281 | return \$ret; |
|---|