Example #1
0
int
server_file_index(struct httpd *env, struct client *clt, struct stat *st)
{
	char			  path[PATH_MAX];
	char			  tmstr[21];
	struct http_descriptor	 *desc = clt->clt_descreq;
	struct server_config	 *srv_conf = clt->clt_srv_conf;
	struct dirent		**namelist, *dp;
	int			  namesize, i, ret, fd = -1, namewidth, skip;
	int			  code = 500;
	struct evbuffer		 *evb = NULL;
	struct media_type	 *media;
	const char		 *stripped, *style;
	char			 *escapeduri, *escapedhtml, *escapedpath;
	struct tm		  tm;
	time_t			  t, dir_mtime;

	if ((ret = server_file_method(clt)) != 0) {
		code = ret;
		goto abort;
	}

	/* Request path is already canonicalized */
	stripped = server_root_strip(desc->http_path, srv_conf->strip);
	if ((size_t)snprintf(path, sizeof(path), "%s%s",
	    srv_conf->root, stripped) >= sizeof(path))
		goto abort;

	/* Now open the file, should be readable or we have another problem */
	if ((fd = open(path, O_RDONLY)) == -1)
		goto abort;

	/* Save last modification time */
	dir_mtime = MINIMUM(time(NULL), st->st_mtim.tv_sec);

	if ((evb = evbuffer_new()) == NULL)
		goto abort;

	if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1)
		goto abort;

	/* Indicate failure but continue going through the list */
	skip = 0;

	if ((escapedpath = escape_html(desc->http_path)) == NULL)
		goto fail;

	/* A CSS stylesheet allows minimal customization by the user */
	style = "body { background-color: white; color: black; font-family: "
	    "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n";
	/* Generate simple HTML index document */
	if (evbuffer_add_printf(evb,
	    "<!DOCTYPE html>\n"
	    "<html>\n"
	    "<head>\n"
	    "<title>Index of %s</title>\n"
	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
	    "</head>\n"
	    "<body>\n"
	    "<h1>Index of %s</h1>\n"
	    "<hr>\n<pre>\n",
	    escapedpath, style, escapedpath) == -1)
		skip = 1;

	free(escapedpath);

	for (i = 0; i < namesize; i++) {
		dp = namelist[i];

		if (skip ||
		    fstatat(fd, dp->d_name, st, 0) == -1) {
			free(dp);
			continue;
		}

		t = st->st_mtime;
		localtime_r(&t, &tm);
		strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
		namewidth = 51 - strlen(dp->d_name);

		if ((escapeduri = url_encode(dp->d_name)) == NULL)
			goto fail;
		if ((escapedhtml = escape_html(dp->d_name)) == NULL)
			goto fail;

		if (dp->d_name[0] == '.' &&
		    !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
			/* ignore hidden files starting with a dot */
		} else if (S_ISDIR(st->st_mode)) {
			namewidth -= 1; /* trailing slash */
			if (evbuffer_add_printf(evb,
			    "<a href=\"%s%s/\">%s/</a>%*s%s%20s\n",
			    strchr(escapeduri, ':') != NULL ? "./" : "",
			    escapeduri, escapedhtml,
			    MAXIMUM(namewidth, 0), " ", tmstr, "-") == -1)
				skip = 1;
		} else if (S_ISREG(st->st_mode)) {
			if (evbuffer_add_printf(evb,
			    "<a href=\"%s%s\">%s</a>%*s%s%20llu\n",
			    strchr(escapeduri, ':') != NULL ? "./" : "",
			    escapeduri, escapedhtml,
			    MAXIMUM(namewidth, 0), " ",
			    tmstr, st->st_size) == -1)
				skip = 1;
		}
		free(escapeduri);
		free(escapedhtml);
		free(dp);
	}
	free(namelist);

	if (skip ||
	    evbuffer_add_printf(evb,
	    "</pre>\n<hr>\n</body>\n</html>\n") == -1)
		goto abort;

	close(fd);
	fd = -1;

	media = media_find_config(env, srv_conf, "index.html");
	ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb),
	    dir_mtime);
	switch (ret) {
	case -1:
		goto fail;
	case 0:
		/* Connection is already finished */
		evbuffer_free(evb);
		goto done;
	default:
		break;
	}

	if (server_bufferevent_write_buffer(clt, evb) == -1)
		goto fail;
	evbuffer_free(evb);
	evb = NULL;

	bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
	if (clt->clt_persist)
		clt->clt_toread = TOREAD_HTTP_HEADER;
	else
		clt->clt_toread = TOREAD_HTTP_NONE;
	clt->clt_done = 0;

 done:
	server_reset_http(clt);
	return (0);
 fail:
	bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
	bufferevent_free(clt->clt_bev);
	clt->clt_bev = NULL;
 abort:
	if (fd != -1)
		close(fd);
	if (evb != NULL)
		evbuffer_free(evb);
	server_abort_http(clt, code, desc->http_path);
	return (-1);
}
Example #2
0
int
server_file_request(struct httpd *env, struct client *clt, char *path,
    struct stat *st)
{
	struct server_config	*srv_conf = clt->clt_srv_conf;
	struct media_type	*media;
	const char		*errstr = NULL;
	int			 fd = -1, ret, code = 500;

	if ((ret = server_file_method(clt)) != 0) {
		code = ret;
		goto abort;
	}

	if ((ret = server_file_modified_since(clt->clt_descreq, st)) != -1)
		return (ret);

	/* Now open the file, should be readable or we have another problem */
	if ((fd = open(path, O_RDONLY)) == -1)
		goto abort;

	media = media_find_config(env, srv_conf, path);
	ret = server_response_http(clt, 200, media, st->st_size,
	    MINIMUM(time(NULL), st->st_mtim.tv_sec));
	switch (ret) {
	case -1:
		goto fail;
	case 0:
		/* Connection is already finished */
		close(fd);
		goto done;
	default:
		break;
	}

	clt->clt_fd = fd;
	if (clt->clt_srvbev != NULL)
		bufferevent_free(clt->clt_srvbev);

	clt->clt_srvbev_throttled = 0;
	clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read,
	    server_write, server_file_error, clt);
	if (clt->clt_srvbev == NULL) {
		errstr = "failed to allocate file buffer event";
		goto fail;
	}

	/* Adjust read watermark to the socket output buffer size */
	bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0,
	    clt->clt_sndbufsiz);

	bufferevent_settimeout(clt->clt_srvbev,
	    srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
	bufferevent_enable(clt->clt_srvbev, EV_READ);
	bufferevent_disable(clt->clt_bev, EV_READ);

 done:
	server_reset_http(clt);
	return (0);
 fail:
	bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
	bufferevent_free(clt->clt_bev);
	clt->clt_bev = NULL;
 abort:
	if (errstr == NULL)
		errstr = strerror(errno);
	server_abort_http(clt, code, errstr);
	return (-1);
}
Example #3
0
int
server_partial_file_request(struct httpd *env, struct client *clt, char *path,
    struct stat *st, char *range_str)
{
	struct server_config	*srv_conf = clt->clt_srv_conf;
	struct http_descriptor	*resp = clt->clt_descresp;
	struct http_descriptor	*desc = clt->clt_descreq;
	struct media_type	*media, multipart_media;
	struct range		*range;
	struct evbuffer		*evb = NULL;
	size_t			 content_length;
	int			 code = 500, fd = -1, i, nranges, ret;
	uint32_t		 boundary;
	char			 content_range[64];
	const char		*errstr = NULL;

	/* Ignore range request for methods other than GET */
	if (desc->http_method != HTTP_METHOD_GET)
		return server_file_request(env, clt, path, st);

	if ((range = parse_range(range_str, st->st_size, &nranges)) == NULL) {
		code = 416;
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes */%lld", st->st_size);
		errstr = content_range;
		goto abort;
	}

	/* Now open the file, should be readable or we have another problem */
	if ((fd = open(path, O_RDONLY)) == -1)
		goto abort;

	media = media_find_config(env, srv_conf, path);
	if ((evb = evbuffer_new()) == NULL) {
		errstr = "failed to allocate file buffer";
		goto abort;
	}

	if (nranges == 1) {
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes %lld-%lld/%lld", range->start, range->end,
		    st->st_size);
		if (kv_add(&resp->http_headers, "Content-Range",
		    content_range) == NULL)
			goto abort;

		content_length = range->end - range->start + 1;
		if (buffer_add_range(fd, evb, range) == 0)
			goto abort;

	} else {
		content_length = 0;
		boundary = arc4random();
		/* Generate a multipart payload of byteranges */
		while (nranges--) {
			if ((i = evbuffer_add_printf(evb, "\r\n--%ud\r\n",
			    boundary)) == -1)
				goto abort;

			content_length += i;
			if ((i = evbuffer_add_printf(evb,
			    "Content-Type: %s/%s\r\n",
			    media->media_type, media->media_subtype)) == -1)
				goto abort;

			content_length += i;
			if ((i = evbuffer_add_printf(evb,
			    "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
			    range->start, range->end, st->st_size)) == -1)
				goto abort;

			content_length += i;
			if (buffer_add_range(fd, evb, range) == 0)
				goto abort;

			content_length += range->end - range->start + 1;
			range++;
		}

		if ((i = evbuffer_add_printf(evb, "\r\n--%ud--\r\n",
		    boundary)) == -1)
			goto abort;

		content_length += i;

		/* prepare multipart/byteranges media type */
		(void)strlcpy(multipart_media.media_type, "multipart",
		    sizeof(multipart_media.media_type));
		(void)snprintf(multipart_media.media_subtype,
		    sizeof(multipart_media.media_subtype),
		    "byteranges; boundary=%ud", boundary);
		media = &multipart_media;
	}

	ret = server_response_http(clt, 206, media, content_length,
	    MINIMUM(time(NULL), st->st_mtim.tv_sec));
	switch (ret) {
	case -1:
		goto fail;
	case 0:
		/* Connection is already finished */
		close(fd);
		evbuffer_free(evb);
		evb = NULL;
		goto done;
	default:
		break;
	}

	if (server_bufferevent_write_buffer(clt, evb) == -1)
		goto fail;

	evbuffer_free(evb);
	evb = NULL;

	bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
	if (clt->clt_persist)
		clt->clt_toread = TOREAD_HTTP_HEADER;
	else
		clt->clt_toread = TOREAD_HTTP_NONE;
	clt->clt_done = 0;

 done:
	server_reset_http(clt);
	return (0);
 fail:
	bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
	bufferevent_free(clt->clt_bev);
	clt->clt_bev = NULL;
 abort:
	if (errstr == NULL)
		errstr = strerror(errno);
	server_abort_http(clt, code, errstr);
	return (-1);
}
Example #4
0
int
server_partial_file_request(struct httpd *env, struct client *clt, char *path,
    struct stat *st, char *range_str)
{
	struct server_config	*srv_conf = clt->clt_srv_conf;
	struct http_descriptor	*resp = clt->clt_descresp;
	struct http_descriptor	*desc = clt->clt_descreq;
	struct media_type	*media, multipart_media;
	struct range_data	*r = &clt->clt_ranges;
	struct range		*range;
	size_t			 content_length = 0;
	int			 code = 500, fd = -1, i, nranges, ret;
	char			 content_range[64];
	const char		*errstr = NULL;

	/* Ignore range request for methods other than GET */
	if (desc->http_method != HTTP_METHOD_GET)
		return server_file_request(env, clt, path, st);

	if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) {
		code = 416;
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes */%lld", st->st_size);
		errstr = content_range;
		goto abort;
	}

	/* Now open the file, should be readable or we have another problem */
	if ((fd = open(path, O_RDONLY)) == -1)
		goto abort;

	media = media_find_config(env, srv_conf, path);
	r->range_media = media;

	if (nranges == 1) {
		range = &r->range[0];
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes %lld-%lld/%lld", range->start, range->end,
		    st->st_size);
		if (kv_add(&resp->http_headers, "Content-Range",
		    content_range) == NULL)
			goto abort;

		range = &r->range[0];
		content_length += range->end - range->start + 1;
	} else {
		/* Add boundary, all parts will be handled by the callback */
		arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary));

		/* Calculate Content-Length of the complete multipart body */
		for (i = 0; i < nranges; i++) {
			range = &r->range[i];

			/* calculate Content-Length of the complete body */
			if ((ret = snprintf(NULL, 0,
			    "\r\n--%llu\r\n"
			    "Content-Type: %s/%s\r\n"
			    "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
			    clt->clt_boundary,
			    media->media_type, media->media_subtype,
			    range->start, range->end, st->st_size)) < 0)
				goto abort;

			/* Add data length */
			content_length += ret + range->end - range->start + 1;

		}
		if ((ret = snprintf(NULL, 0, "\r\n--%llu--\r\n",
		    clt->clt_boundary)) < 0)
			goto abort;
		content_length += ret;

		/* prepare multipart/byteranges media type */
		(void)strlcpy(multipart_media.media_type, "multipart",
		    sizeof(multipart_media.media_type));
		(void)snprintf(multipart_media.media_subtype,
		    sizeof(multipart_media.media_subtype),
		    "byteranges; boundary=%llu", clt->clt_boundary);
		media = &multipart_media;
	}

	/* Start with first range */
	r->range_toread = TOREAD_HTTP_RANGE;

	ret = server_response_http(clt, 206, media, content_length,
	    MINIMUM(time(NULL), st->st_mtim.tv_sec));
	switch (ret) {
	case -1:
		goto fail;
	case 0:
		/* Connection is already finished */
		close(fd);
		goto done;
	default:
		break;
	}

	clt->clt_fd = fd;
	if (clt->clt_srvbev != NULL)
		bufferevent_free(clt->clt_srvbev);

	clt->clt_srvbev_throttled = 0;
	clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange,
	    server_write, server_file_error, clt);
	if (clt->clt_srvbev == NULL) {
		errstr = "failed to allocate file buffer event";
		goto fail;
	}

	/* Adjust read watermark to the socket output buffer size */
	bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0,
	    clt->clt_sndbufsiz);

	bufferevent_settimeout(clt->clt_srvbev,
	    srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
	bufferevent_enable(clt->clt_srvbev, EV_READ);
	bufferevent_disable(clt->clt_bev, EV_READ);

 done:
	server_reset_http(clt);
	return (0);
 fail:
	bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
	bufferevent_free(clt->clt_bev);
	clt->clt_bev = NULL;
 abort:
	if (fd != -1)
		close(fd);
	if (errstr == NULL)
		errstr = strerror(errno);
	server_abort_http(clt, code, errstr);
	return (-1);
}