1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-27 03:21:53 +03:00
samba-mirror/source4/web_server/web_server.c
Andrew Tridgell fc45b63e47 r6998: - added support for application[] data, which is global to all clients using the web server.
This allows for things like
     application['state'] = "shuttting down"
  and then every web client can see that the server is going down

- added support for session[] data. This allows web pages to store
  long term data specific to this client. It relies on
  cookies. Sessions auto timeout (default timeout 5 minutes). The
  timeout can be set in the scripts.

- changed from processing all .html files as esp, to only processing
  .esp files as esp. This makes it easier to compare the samba web
  server to appWeb as a reference implementation.

- expanded the number of standard variables setup by esp. See the
  showvars.esp example page for all variables.
(This used to be commit c418b23c2e)
2007-10-10 13:17:03 -05:00

260 lines
6.7 KiB
C

/*
Unix SMB/CIFS implementation.
web server startup
Copyright (C) Andrew Tridgell 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "smbd/service_task.h"
#include "smbd/service_stream.h"
#include "web_server/web_server.h"
#include "lib/events/events.h"
#include "system/filesys.h"
/* don't allow connections to hang around forever */
#define HTTP_TIMEOUT 30
/*
destroy a web connection
*/
static int websrv_destructor(void *ptr)
{
struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
if (web->output.fd != -1) {
close(web->output.fd);
}
return 0;
}
/*
called when a connection times out. This prevents a stuck connection
from hanging around forever
*/
static void websrv_timeout(struct event_context *event_context,
struct timed_event *te,
struct timeval t, void *private)
{
struct websrv_context *web = talloc_get_type(private, struct websrv_context);
stream_terminate_connection(web->conn, "websrv_context: timeout");
}
/*
called when a web connection becomes readable
*/
static void websrv_recv(struct stream_connection *conn, uint16_t flags)
{
struct websrv_context *web = talloc_get_type(conn->private,
struct websrv_context);
NTSTATUS status;
uint8_t buf[1024];
size_t nread;
uint8_t *p;
DATA_BLOB b;
/* not the most efficient http parser ever, but good enough for us */
status = socket_recv(conn->socket, buf, sizeof(buf), &nread, 0);
if (NT_STATUS_IS_ERR(status)) goto failed;
if (!NT_STATUS_IS_OK(status)) return;
status = data_blob_append(web, &web->input.partial, buf, nread);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* parse any lines that are available */
b = web->input.partial;
while (!web->input.end_of_headers &&
(p=memchr(b.data, '\n', b.length))) {
const char *line = b.data;
*p = 0;
if (p != b.data && p[-1] == '\r') {
p[-1] = 0;
}
status = http_parse_header(web, line);
if (!NT_STATUS_IS_OK(status)) return;
b.length -= (p - b.data) + 1;
b.data = p+1;
}
/* keep any remaining bytes in web->input.partial */
if (b.length == 0) {
b.data = NULL;
}
b = data_blob_talloc(web, b.data, b.length);
data_blob_free(&web->input.partial);
web->input.partial = b;
/* we finish when we have both the full headers (terminated by
a blank line) and any post data, as indicated by the
content_length */
if (web->input.end_of_headers &&
web->input.partial.length == web->input.content_length) {
EVENT_FD_NOT_READABLE(web->conn->event.fde);
http_process_input(web);
}
return;
failed:
stream_terminate_connection(conn, "websrv_recv: failed\n");
}
/*
called when a web connection becomes writable
*/
static void websrv_send(struct stream_connection *conn, uint16_t flags)
{
struct websrv_context *web = talloc_get_type(conn->private,
struct websrv_context);
NTSTATUS status;
size_t nsent;
DATA_BLOB b;
b = web->output.content;
b.data += web->output.nsent;
b.length -= web->output.nsent;
status = socket_send(conn->socket, &b, &nsent, 0);
if (NT_STATUS_IS_ERR(status)) {
stream_terminate_connection(web->conn, "socket_send: failed");
return;
}
if (!NT_STATUS_IS_OK(status)) {
return;
}
web->output.nsent += nsent;
/* possibly read some more raw data from a file */
if (web->output.content.length == web->output.nsent &&
web->output.fd != -1) {
uint8_t buf[2048];
ssize_t nread;
data_blob_free(&web->output.content);
web->output.nsent = 0;
nread = read(web->output.fd, buf, sizeof(buf));
if (nread == 0) {
close(web->output.fd);
web->output.fd = -1;
}
if (nread == -1 && errno == EINTR) {
return;
}
web->output.content = data_blob_talloc(web, buf, nread);
}
if (web->output.content.length == web->output.nsent) {
stream_terminate_connection(web->conn, NULL);
}
}
/*
establish a new connection to the web server
*/
static void websrv_accept(struct stream_connection *conn)
{
struct task_server *task = talloc_get_type(conn->private, struct task_server);
struct websrv_context *web;
web = talloc_zero(conn, struct websrv_context);
if (web == NULL) goto failed;
web->task = task;
web->conn = conn;
conn->private = web;
web->output.fd = -1;
talloc_set_destructor(web, websrv_destructor);
event_add_timed(conn->event.ctx, web,
timeval_current_ofs(HTTP_TIMEOUT, 0),
websrv_timeout, web);
return;
failed:
talloc_free(conn);
}
static const struct stream_server_ops web_stream_ops = {
.name = "web",
.accept_connection = websrv_accept,
.recv_handler = websrv_recv,
.send_handler = websrv_send,
};
/*
startup the web server task
*/
static void websrv_task_init(struct task_server *task)
{
NTSTATUS status;
uint16_t port = lp_web_port();
const struct model_ops *model_ops;
/* run the web server as a single process */
model_ops = process_model_byname("single");
if (!model_ops) goto failed;
if (lp_interfaces() && lp_bind_interfaces_only()) {
int num_interfaces = iface_count();
int i;
for(i = 0; i < num_interfaces; i++) {
const char *address = iface_n_ip(i);
status = stream_setup_socket(task->event_ctx, model_ops,
&web_stream_ops,
"ipv4", address,
&port, task);
if (!NT_STATUS_IS_OK(status)) goto failed;
}
} else {
status = stream_setup_socket(task->event_ctx, model_ops,
&web_stream_ops,
"ipv4", lp_socket_address(),
&port, task);
if (!NT_STATUS_IS_OK(status)) goto failed;
}
/* startup the esp processor - unfortunately we can't do this
per connection as that wouldn't allow for session variables */
status = http_setup_esp(task);
if (!NT_STATUS_IS_OK(status)) goto failed;
return;
failed:
task_terminate(task, "Failed to startup web server task");
}
/*
called on startup of the web server service It's job is to start
listening on all configured sockets
*/
static NTSTATUS websrv_init(struct event_context *event_context,
const struct model_ops *model_ops)
{
return task_server_startup(event_context, model_ops, websrv_task_init);
}
/* called at smbd startup - register ourselves as a server service */
NTSTATUS server_service_web_init(void)
{
return register_server_service("web", websrv_init);
}