Example #1
0
/**
 * @short Allows to change some properties of the file
 */
onion_connection_status onion_webdav_proppatch(const char *filename, onion_webdav *wd, onion_request *req, onion_response *res){
	xmlDocPtr doc;
	const onion_block *block=onion_request_get_data(req);
// 	ONION_DEBUG("%s",onion_block_data(block));
	if (!block)
		return OCS_INTERNAL_ERROR;
	doc = xmlParseMemory((char*)onion_block_data(block), onion_block_size(block));
	
	xmlNode *root = NULL;
	root = xmlDocGetRootElement(doc);
	int ok=0;
	
	while (root){
		ONION_DEBUG("%s", root->name);
		if (strcmp((const char*)root->name,"propertyupdate")==0){
			xmlNode *propertyupdate = root->children;
			while (propertyupdate){
				ONION_DEBUG("%s", propertyupdate->name);
				if (strcmp((const char*)propertyupdate->name,"set")==0){
					xmlNode *set = propertyupdate->children;
					while (set){
						ONION_DEBUG("%s", set->name);
						if (strcmp((const char*)set->name,"prop")==0){
							ONION_DEBUG("in prop");
							xmlNode *prop = set->children;
							while (prop){
								ONION_DEBUG("prop %s", prop->name);
								if (strcmp((const char*)prop->name,"executable")==0){
									ONION_DEBUG("Setting executable %s", prop->children->content);
									struct stat st;
									stat(filename, &st);
									if (toupper(prop->children->content[0])=='T'){
										chmod(filename, st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH);
									}
									else{
										chmod(filename, st.st_mode & ~( S_IXUSR | S_IXGRP | S_IXOTH) );
									}
									ok=1;
								}
								prop=prop->next;
							}
						}
						set=set->next;
					}
				}
				propertyupdate=propertyupdate->next;
			}
		}
		root=root->next;
	}
	
	xmlFreeDoc(doc); 
	if (ok){
		onion_response_write_headers(res);
		return OCS_PROCESSED;
	}
	else{
		return OCS_INTERNAL_ERROR;
	}
}
Example #2
0
void t02_full_cycle_http10(){
	INIT_LOCAL();
	
	onion *server=onion_new(0);
	onion_add_listen_point(server,NULL,NULL,onion_buffer_listen_point_new());
	onion_request *request;
	char buffer[4096];
	memset(buffer,0,sizeof(buffer));
	
	request=onion_request_new(server->listen_points[0]);
	
	onion_response *response=onion_response_new(request);
	
	onion_response_set_length(response, 30);
	FAIL_IF_NOT_EQUAL(response->length,30);
	onion_response_write_headers(response);
	
	onion_response_write0(response,"123456789012345678901234567890");
	onion_response_flush(response);
	FAIL_IF_NOT_EQUAL(response->sent_bytes,30);
	
	onion_response_free(response);
	strncpy(buffer,onion_buffer_listen_point_get_buffer_data(request),sizeof(buffer));
	onion_request_free(request);
	onion_free(server);
	
	FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.0 200 OK\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Connection: Keep-Alive\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Content-Length: 30\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Server: libonion");
	FAIL_IF_NOT_STRSTR(buffer, "coralbits");
	FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\n123456789012345678901234567890");
	
	END_LOCAL();
}
Example #3
0
/**
 * @short Write some response data.
 * @memberof onion_response_t
 *
 * This is the main write data function. If the headers have not been sent yet, they are now.
 *
 * It's internally used also by the write0 and printf versions.
 *
 * Also it does some buffering, so data is not sent as written by code, but only in chunks.
 * These chunks are when the response is finished, or when the internal buffer is full. This
 * helps performance, and eases the programming on the user side.
 *
 * If length is 0, forces the write of pending data.
 *
 * @returns The bytes written, normally just length. On error returns OCS_CLOSE_CONNECTION.
 */
ssize_t onion_response_write(onion_response *res, const char *data, size_t length){
	if (res->flags&OR_SKIP_CONTENT){
		if (!(res->flags&OR_HEADER_SENT)){ // Automatic header write
			onion_response_write_headers(res);
		}
		ONION_DEBUG("Skipping content as we are in HEAD mode");
		return OCS_CLOSE_CONNECTION;
	}
	if (length==0){
		onion_response_flush(res);
		return 0;
	}
	//ONION_DEBUG0("Write %d bytes [%d total] (%p)", length, res->sent_bytes, res);

	int l=length;
	int w=0;
	while (res->buffer_pos+l>sizeof(res->buffer)){
		int wb=sizeof(res->buffer)-res->buffer_pos;
		memcpy(&res->buffer[res->buffer_pos], data, wb);

		res->buffer_pos=sizeof(res->buffer);
		if (onion_response_flush(res)<0)
			return w;

		l-=wb;
		data+=wb;
		w+=wb;
	}

	memcpy(&res->buffer[res->buffer_pos], data, l);
	res->buffer_pos+=l;
	w+=l;

	return w;
}
Example #4
0
/**
 * @short Writes all buffered output waiting for sending.
 * @ingroup response
 *
 * If header has not been sent yet (delayed), it uses a temporary buffer to send it now. This
 * way header can use the buffer_size information to send the proper content-length, even when it
 * wasnt properly set by programmer. Whith this information its possib to keep alive the connection
 * on more cases.
 */
int onion_response_flush(onion_response * res) {
  res->sent_bytes += res->buffer_pos;
  res->sent_bytes_total += res->buffer_pos;
  if (res->buffer_pos == 0)     // Not used.
    return 0;
  if (!(res->flags & OR_HEADER_SENT)) { // Automatic header write
    ONION_DEBUG0
        ("Doing fast header hack: store current buffer, send current headers. Resend buffer.");
    char tmpb[sizeof(res->buffer)];
    int tmpp = res->buffer_pos;
    memcpy(tmpb, res->buffer, res->buffer_pos);
    res->buffer_pos = 0;

    onion_response_write_headers(res);
    onion_response_write(res, tmpb, tmpp);
    return 0;
  }
  if (res->flags & OR_SKIP_CONTENT)     // HEAD request
    return 0;
  ONION_DEBUG0("Flush %d bytes", res->buffer_pos);

  onion_request *req = res->request;
  ssize_t(*write) (onion_request *, const char *data, size_t len);
  write = req->connection.listen_point->write;

  ssize_t w;
  off_t pos = 0;
  //ONION_DEBUG0("Write %d bytes",res->buffer_pos);
  if (res->flags & OR_CHUNKED) {
    char tmp[16];
    snprintf(tmp, sizeof(tmp), "%X\r\n", (unsigned int)res->buffer_pos);
    if ((w = write(req, tmp, strlen(tmp))) <= 0) {
      ONION_WARNING("Error writing chunk encoding length. Aborting write.");
      return OCS_CLOSE_CONNECTION;
    }
    ONION_DEBUG0("Write %d-%d bytes", res->buffer_pos, w);
  }
  while ((w =
          write(req, &res->buffer[pos], res->buffer_pos)) != res->buffer_pos) {
    if (w <= 0 || res->buffer_pos < 0) {
      ONION_ERROR("Error writing %d bytes. Maybe closed connection. Code %d. ",
                  res->buffer_pos, w);
      perror("");
      res->buffer_pos = 0;
      return OCS_CLOSE_CONNECTION;
    }
    pos += w;
    ONION_DEBUG0("Write %d-%d bytes", res->buffer_pos, w);
    res->buffer_pos -= w;
  }
  if (res->flags & OR_CHUNKED) {
    write(req, "\r\n", 2);
  }
  res->buffer_pos = 0;
  return 0;
}
Example #5
0
/**
 * @short Returns known options.
 * 
 * Just known options, no more. I think many clients ignore this. (without PUT, gnome's file manager was trying).
 */
onion_connection_status onion_webdav_options(const char *filename, onion_webdav *wd, onion_request *req, onion_response *res){
	onion_response_set_header(res, "Allow", "OPTIONS,GET,HEAD,PUT,PROPFIND");
	onion_response_set_header(res, "Content-Type", "httpd/unix-directory");
	
	onion_response_set_length(res, 0);
	
	onion_response_write_headers(res);
	
	return OCS_PROCESSED;
}
Example #6
0
onion_connection_status method(void *ignore, onion_request *req, onion_response *res){
	if (onion_response_write_headers(res)==OR_SKIP_CONTENT) // Head
		return OCS_PROCESSED;
	
	onion_response_write0(res, "<html><body>\n<h1>Petition resume</h1>\n");
	int flags=onion_request_get_flags(req);
	if (flags&OR_GET)
		onion_response_write0(res,"<h2>GET");
	else if (flags&OR_POST)
		onion_response_write0(res,"<h2>POST");
	else 
		onion_response_write0(res,"<h2>UNKNOWN");
	
	onion_response_printf(res," %s</h2>\n<ul>",onion_request_get_path(req));

	const onion_dict *get=onion_request_get_query_dict(req);
	const onion_dict *post=onion_request_get_post_dict(req);
	const onion_dict *headers=onion_request_get_header_dict(req);
	
	onion_response_printf(res,"<li>Header %d elements<ul>",onion_dict_count(headers));
	if (headers) onion_dict_preorder(headers, print_dict_element, res);
	onion_response_printf(res,"</ul></li>\n");

	onion_response_printf(res,"<li>GET %d elements<ul>",onion_dict_count(get));
	if (get) onion_dict_preorder(get, print_dict_element, res);
	onion_response_printf(res,"</ul></li>\n");

	onion_response_printf(res,"<li>POST %d elements<ul>",onion_dict_count(post));
	if (post) onion_dict_preorder(post, print_dict_element, res);
	onion_response_printf(res,"</ul></li>\n");

	onion_response_write0(res,"<p>\n");
	onion_response_write0(res,"<form method=\"GET\">"
														"<input type=\"text\" name=\"test\">"
														"<input type=\"submit\" name=\"submit\" value=\"GET\">"
														"</form><p>\n");
	onion_response_write0(res,"<form method=\"POST\" enctype=\"application/x-www-form-urlencoded\">"
														"<textarea name=\"text\"></textarea>"
														"<input type=\"text\" name=\"test\">"
														"<input type=\"submit\" name=\"submit\"  value=\"POST urlencoded\">"
														"</form>"
														"<p>\n");
	onion_response_write0(res,"<form method=\"POST\" enctype=\"multipart/form-data\">"
														"<input type=\"file\" name=\"file\">"
														"<textarea name=\"text\"></textarea>"
														"<input type=\"text\" name=\"test\">"
														"<input type=\"submit\" name=\"submit\" value=\"POST multipart\">"
														"</form>"
														"<p>\n");
	
	onion_response_write0(res,"</body></html>");
	
	return OCS_PROCESSED;
}
Example #7
0
/**
 * @short Performs the real request: set the code and write data
 */
int onion_handler_static_handler(onion_handler_static_data *d, onion_request *request, onion_response *res){
	int length=strlen(d->data);
	onion_response_set_length(res, length);
	onion_response_set_code(res, d->code);
	
	onion_response_write_headers(res);
	//fprintf(stderr,"Write %d bytes\n",length);
	onion_response_write(res, d->data, length);

	return OCS_PROCESSED;
}
Example #8
0
int onion_handler_opack_handler(onion_handler_opack_data *d, onion_request *request, onion_response *res){
	if (strcmp(d->path, onion_request_get_path(request))!=0)
		return 0;
		
	if (d->length)
		onion_response_set_length(res, d->length);
	onion_response_write_headers(res);

	d->render(res);
	
	return OCS_PROCESSED;
}
Example #9
0
/* Like 'search_file' */
onion_connection_status OnionServer::getSettingKinectWrapped(
		Onion::Request &req, Onion::Response &res)
{
	const char* path = onion_request_get_fullpath( req.c_handler() );
#ifdef VERBOSE
	printf("Request of %s.\n",path);
#endif
	std::string filename("./html/");
	filename.append(path);

	std::ifstream file(filename.c_str());
	std::string line;

	if( file.is_open()){

		/* Create header with mime type and charset information for several file extensions.
		 * This is just a workaround. There should be an automatic mechanicm
		 * in libonion. */
		int periodPos = filename.find_last_of('.');
		std::string extension = filename.substr(periodPos+1);
		std::string key("Content-Type");
		std::string defaultType("text/html; charset: utf-8");

		std::string mime = m_mimedict.get( extension , defaultType ) ;
		res.setHeader(key,mime);
		onion_response_write_headers(res.c_handler());// missing in cpp bindings?
		//res.writeHeaders();//this was added by me...

		try{
			/*
			res.write("json_kinect = ", 14);
			const char* kinect = m_settingKinect.getConfig(true);
			size_t len = strlen( kinect );
			res.write(kinect, (int) len );
			res.write(";\n", 2 );
			*/

			while (std::getline(file, line)) {
				res.write( line.c_str(), line.size() );
				res.write("\n", 1 );
			}
		}//catch ( const boost::iobase::failure &ex )
		catch ( const std::exception & ex ){
			std::cerr << "Can not read " << filename << std::endl;
			res.write( ErrReadFailed.c_str(), ErrReadFailed.size());
		}
	}else{
		res.write( ErrNotFound.c_str(), ErrNotFound.size());
	}

	return OCS_PROCESSED;
}
Example #10
0
/**
 * @short Handles a propfind
 * 
 * @param path the shared path.
 */
onion_connection_status onion_webdav_propfind(const char *filename, onion_webdav *wd, onion_request* req, onion_response* res){
	// Prepare the basepath, necesary for props.
	char *basepath=NULL;
	int pathlen=0;
	const char *current_path=onion_request_get_path(req);
	const char *fullpath=onion_request_get_fullpath(req);
	pathlen=(current_path-fullpath);
	basepath=alloca(pathlen+1);
	memcpy(basepath, fullpath, pathlen+1);
	ONION_DEBUG0("Pathbase initial <%s> %d", basepath, pathlen); 
	while(basepath[pathlen]=='/' && pathlen>0)
		pathlen--;
	basepath[pathlen+1]=0;
				 
	ONION_DEBUG0("PROPFIND; pathbase %s", basepath);
	int depth;
	{
		const char *depths=onion_request_get_header(req, "Depth");
		if (!depths){
			ONION_ERROR("Missing Depth header on webdav request");
			return OCS_INTERNAL_ERROR;
		}
		if (strcmp(depths,"infinity")==0){
			ONION_ERROR("Infinity depth not supported yet.");
			return OCS_INTERNAL_ERROR;
		}
		depth=atoi(depths);
	}

	int props=onion_webdav_parse_propfind(onion_request_get_data(req));
	ONION_DEBUG("Asking for props %08X, depth %d", props, depth);
	
	onion_block *block=onion_webdav_write_propfind(basepath, filename, onion_request_get_path(req), depth, props);
	
	if (!block) // No block, resource does not exist
		return onion_shortcut_response("Not found", HTTP_NOT_FOUND, req, res);
		
	ONION_DEBUG0("Printing block %s", onion_block_data(block));
	
	onion_response_set_header(res, "Content-Type", "text/xml; charset=\"utf-8\"");
	onion_response_set_length(res, onion_block_size(block));
	onion_response_set_code(res, HTTP_MULTI_STATUS);
	onion_response_write_headers(res);
	onion_response_flush(res);
	
	onion_response_write(res, onion_block_data(block), onion_block_size(block));
	
	onion_block_free(block);
	
	return OCS_PROCESSED;
}
Example #11
0
onion_connection_status check_answer(void *privdata, onion_request *req, onion_response *res)
{
	trivia_question const* const q = privdata;
	if (onion_request_get_flags(req)&OR_HEAD){
		onion_response_write_headers(res);
		return OCS_PROCESSED;
	}
	const char *answer = onion_request_get_post(req,"answer");
	const char *uri;
	if(strcmp_ignoring_case_and_whitespace(q->correct_answer, answer) == 0) {
		lamp->green = 65535;
		uri = q->correct_uri;
	} else {
		lamp->red = 65535;
		uri = q->again_uri;
	}
	return onion_shortcut_response_extra_headers("<h1>302 - Moved</h1>", HTTP_REDIRECT, req, res, "Location", uri, NULL );
}
Example #12
0
/**
 * @short Frees the memory consumed by this object
 * @memberof onion_response_t
 * @ingroup response
 *
 * This function returns the close status: OR_KEEP_ALIVE or OR_CLOSE_CONNECTION as needed.
 *
 * @returns Whether the connection should be closed or not, or an error status to be handled by server.
 * @see onion_connection_status
 */
onion_connection_status onion_response_free(onion_response * res) {
  // write pending data.
  if (!(res->flags & OR_HEADER_SENT) && res->buffer_pos < sizeof(res->buffer))
    onion_response_set_length(res, res->buffer_pos);

  if (!(res->flags & OR_HEADER_SENT))
    onion_response_write_headers(res);

  onion_response_flush(res);
  onion_request *req = res->request;

  if (res->flags & OR_CHUNKED) {        // Set the chunked data end.
    req->connection.listen_point->write(req, "0\r\n\r\n", 5);
  }

  int r = OCS_CLOSE_CONNECTION;

  // it is a rare ocasion that there is no request, but although unlikely, it may happen
  if (req) {
    // keep alive only on HTTP/1.1.
    ONION_DEBUG0
        ("keep alive [req wants] %d && ([skip] %d || [lenght ok] %d==%d || [chunked] %d)",
         onion_request_keep_alive(req), res->flags & OR_SKIP_CONTENT,
         res->length, res->sent_bytes, res->flags & OR_CHUNKED);
    if (onion_request_keep_alive(req)
        && (res->flags & OR_SKIP_CONTENT || res->length == res->sent_bytes
            || res->flags & OR_CHUNKED)
        )
      r = OCS_KEEP_ALIVE;

    if ((onion_log_flags & OF_NOINFO) != OF_NOINFO)
      // FIXME! This is no proper logging at all. Maybe use a handler.
      ONION_INFO("[%s] \"%s %s\" %d %d (%s)",
                 onion_request_get_client_description(res->request),
                 onion_request_methods[res->request->flags & OR_METHODS],
                 res->request->fullpath, res->code, res->sent_bytes,
                 (r == OCS_KEEP_ALIVE) ? "Keep-Alive" : "Close connection");
  }

  onion_dict_free(res->headers);
  onion_low_free(res);

  return r;
}
Example #13
0
/**
 * @short Handler that just echoes all data, writing what was a header, what the method...
 */
onion_connection_status allinfo_handler(void *data, onion_request *req){
	onion_response *res=onion_response_new(req);
	onion_response_write_headers(res);
	
	int f=onion_request_get_flags(req);
	onion_response_printf(res, "Method: %s\n",(f&OR_GET) ? "GET" : (f&OR_HEAD) ? "HEAD" : "POST");
	onion_response_printf(res, "Path: %s\n",onion_request_get_path(req));
	onion_response_printf(res, "Version: %s\n",onion_request_get_flags(req)&OR_HTTP11 ? "HTTP/1.1" : "HTTP/1.0");

	allinfo_dict_print_t aid;
	aid.res=res;
	aid.part="Query";
	onion_dict_preorder(onion_request_get_query_dict(req),allinfo_query, &aid);
	aid.part="Header";
	onion_dict_preorder(onion_request_get_header_dict(req),allinfo_query, &aid);
	aid.part="POST";
	onion_dict_preorder(onion_request_get_post_dict(req),allinfo_query, &aid);
	aid.part="FILE";
	onion_dict_preorder(onion_request_get_file_dict(req),allinfo_query, &aid);
	
	return onion_response_free(res);;
}
Example #14
0
onion_connection_status sessions(void *ignore, onion_request *req){
	onion_response *res=onion_response_new(req);
	onion_dict *session=onion_request_get_session_dict(req);

	if (onion_request_get_query(req, "reset")){
		onion_request_session_free(req);
		onion_response_write0(res, "ok");
		return onion_response_free(res);
	}
	
	const char *n=onion_dict_get(session, "count");
	int count;
	if (n){
		count=atoi(n)+1;
	}
	else
		count=0;
	char tmp[16];
	snprintf(tmp,sizeof(tmp),"%d",count);
	onion_dict_add(session, "count", tmp, OD_DUP_ALL|OD_REPLACE);
	
	if (onion_response_write_headers(res)==OR_SKIP_CONTENT) // Head
		return onion_response_free(res);
	
	onion_response_write0(res, "<html><body>\n<h1>Session data</h1>\n");

	if (session){
		onion_response_printf(res,"<ul>\n");
		onion_dict_preorder(session, print_dict_element, res);
		onion_response_printf(res,"</ul>\n");
	}
	else{
		onion_response_printf(res,"No session data");
	}
	onion_response_write0(res,"</body></html>");
	
	return onion_response_free(res);
}
Example #15
0
/**
 * @short Shortcut for fast responses, like errors, with extra headers
 * 
 * Prepares a fast response. You pass only the request, the text and the code, and it do the full response
 * object and sends the data.
 * 
 * On this version you also pass a NULL terminated list of headers, in key, value pairs.
 */
onion_connection_status onion_shortcut_response_extra_headers(const char* response, int code, onion_request* req, onion_response *res, ... ){
	unsigned int l=strlen(response);
	const char *key, *value;
	
	onion_response_set_length(res,l);
	onion_response_set_code(res,code);
	
	va_list ap;
	va_start(ap, res);
	while ( (key=va_arg(ap, const char *)) ){
		value=va_arg(ap, const char *);
		if (key && value)
			onion_response_set_header(res, key, value);
		else
			break;
	}
	va_end(ap);

	onion_response_write_headers(res);
	
	onion_response_write(res,response,l);
	return OCS_PROCESSED;
}
Example #16
0
/**
 * @short Default error printer. 
 * @memberof onion_server_t
 * 
 * Ugly errors, that can be reimplemented setting a handler with onion_server_set_internal_error_handler.
 */
static int onion_default_error(void *handler, onion_request *req, onion_response *res){
	const char *msg;
	int l;
	int code;
	switch(req->flags&0x0F000){
		case OR_INTERNAL_ERROR:
			msg=ERROR_500;
			l=sizeof(ERROR_500)-1;
			code=HTTP_INTERNAL_ERROR;
			break;
		case OR_NOT_IMPLEMENTED:
			msg=ERROR_505;
			l=sizeof(ERROR_505)-1;
			code=HTTP_NOT_IMPLEMENTED;
			break;
    case OR_FORBIDDEN:
      msg=ERROR_403;
      l=sizeof(ERROR_403)-1;
      code=HTTP_FORBIDDEN;
      break;
		default:
			msg=ERROR_404;
			l=sizeof(ERROR_404)-1;
			code=HTTP_NOT_FOUND;
			break;
	}
	
	ONION_DEBUG0("Internally managed error: %s, code %d.", msg, code);
	
	onion_response_set_code(res,code);
	onion_response_set_length(res, l);
	onion_response_write_headers(res);
	
	onion_response_write(res,msg,l);
	return OCS_PROCESSED;
}
Example #17
0
    void writeHeaders(){
			onion_response_write_headers(ptr);
		}
Example #18
0
/**
 * @short Writes an image as png to the response object
 * 
 * @param image flat buffer with all pixels
 * @param Bpp Bytes per pixel: 1 grayscale, 2 grayscale with alpha, 3 RGB, 4 RGB with alpha. Negative if in BGR format (cairo)
 * @param width The width of the image
 * @param height The height of the image
 * @param res where to write the image, it sets the necessary structs
 */
int onion_png_response(unsigned char *image, int Bpp, int width, int height, onion_response *res){
	// Many copied from example.c from libpng source code.
	png_structp png_ptr;
	png_infop info_ptr;

	/* Create and initialize the png_struct with the desired error handler
	* functions.  If you want to use the default stderr and longjump method,
	* you can supply NULL for the last three parameters.  We also check that
	* the library version is compatible with the one used at compile time,
	* in case we are using dynamically linked libraries.  REQUIRED.
	*/
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, error, warning);

	if (png_ptr == NULL)
	{
		return OCS_INTERNAL_ERROR;
	}

	/* Allocate/initialize the image information data.  REQUIRED */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL)
	{
		png_destroy_write_struct(&png_ptr,  NULL);
		return OCS_INTERNAL_ERROR;
	}

	onion_png_data opd;
	opd.res=res;
	opd.sent=0;
	png_set_write_fn(png_ptr, (void *)&opd, onion_png_write, onion_png_flush);
	/* where user_io_ptr is a structure you want available to the callbacks */

	onion_response_set_header(res, "Content-Type", "image/png");
	if (onion_response_write_headers(res)==OR_SKIP_CONTENT) // Maybe it was HEAD.
		return OCS_PROCESSED;
	 
	/* Set the image information here.  Width and height are up to 2^31,
	* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
	* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
	* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
	* or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
	* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
	* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
	*/
	if (Bpp<0){
		png_set_bgr(png_ptr);
		Bpp=-Bpp;
	}

	switch(Bpp){
		case 1:
			png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
					PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		break;
		case 2:
			png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY_ALPHA,
					PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		break;
		case 3:
			png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
					PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		break;
		case 4:
			png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
					PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		break;
		default:
			png_error(png_ptr, "Wrong bytes per pixel");
			break;
	}
	
	png_uint_32 k;
	png_bytep row_pointers[height];

	if (height > PNG_UINT_32_MAX/png_sizeof(png_bytep))
		png_error (png_ptr, "Image is too tall to process in memory");

	for (k = 0; k < height; k++)
		row_pointers[k] = (png_byte *) (image + k*width*Bpp);

	// Sets the rows to save at the image
	png_set_rows(png_ptr, info_ptr, row_pointers);
	 
	// If header already sent, then there was an error.
	if (opd.sent){
		return OCS_PROCESSED;
	}
	opd.sent=1;
	// Finally WRITE the image. Uses the onion_response_write via onion_png_write helper.
	png_write_png(png_ptr, info_ptr, 0, NULL);
	
	/* Clean up after the write, and free any memory allocated */
	png_destroy_write_struct(&png_ptr, &info_ptr);

	/* That's it */
	return OCS_PROCESSED;
}
Example #19
0
/**
 * @short This shortcut returns the given file contents. 
 * 
 * It sets all the compilant headers (TODO), cache and so on.
 * 
 * This is the recomended way to send static files; it even can use sendfile Linux call 
 * if suitable (TODO).
 * 
 * It does no security checks, so caller must be security aware.
 */
onion_connection_status onion_shortcut_response_file(const char *filename, onion_request *request, onion_response *res){
	int fd=open(filename,O_RDONLY|O_CLOEXEC);
	
	if (fd<0)
		return OCS_NOT_PROCESSED;

	if(O_CLOEXEC == 0) { // Good compiler know how to cut this out
		int flags=fcntl(fd, F_GETFD);
		if (flags==-1){
			ONION_ERROR("Retrieving flags from file descriptor");
		}
		flags|=FD_CLOEXEC;
		if (fcntl(fd, F_SETFD, flags)==-1){
			ONION_ERROR("Setting O_CLOEXEC to file descriptor");
		}
	}
	
	struct stat st;
	if (stat(filename, &st)!=0){
		ONION_WARNING("File does not exist: %s",filename);
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	if (S_ISDIR(st.st_mode)){
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	size_t length=st.st_size;
	
	char etag[64];
	onion_shortcut_etag(&st, etag);
		
	const char *range=onion_request_get_header(request, "Range");
	if (range){
		strncat(etag,range,sizeof(etag)-1);
	}
	onion_response_set_header(res, "Etag", etag);
	
	if (range && strncmp(range,"bytes=",6)==0){
		onion_response_set_code(res, HTTP_PARTIAL_CONTENT);
		//ONION_DEBUG("Need just a range: %s",range);
		char tmp[1024];
		strncpy(tmp, range+6, 1024);
		char *start=tmp;
		char *end=tmp;
		while (*end!='-' && *end) end++;
		if (*end=='-'){
			*end='\0';
			end++;
			
			//ONION_DEBUG("Start %s, end %s",start,end);
			size_t ends, starts;
			if (*end)
				ends=atol(end);
			else
				ends=length;
			starts=atol(start);
			length=ends-starts+1;
			lseek(fd, starts, SEEK_SET);
			snprintf(tmp,sizeof(tmp),"bytes %d-%d/%d",(unsigned int)starts, (unsigned int)ends, (unsigned int)st.st_size);
			//onion_response_set_header(res, "Accept-Ranges","bytes");
			onion_response_set_header(res, "Content-Range",tmp);
		}
	}
	
	onion_response_set_length(res, length);
	onion_response_set_header(res, "Content-Type", onion_mime_get(filename) );
	ONION_DEBUG("Mime type is %s",onion_mime_get(filename));

  ONION_DEBUG0("Etag %s", etag);
  const char *prev_etag=onion_request_get_header(request, "If-None-Match");
  if (prev_etag && (strcmp(prev_etag, etag)==0)){
    ONION_DEBUG0("Not modified");
    onion_response_set_length(res, 0);
    onion_response_set_code(res, HTTP_NOT_MODIFIED);
    onion_response_write_headers(res);
    close(fd);
    return OCS_PROCESSED;
  }
	onion_response_write_headers(res);
	if ((onion_request_get_flags(request)&OR_HEAD) == OR_HEAD){ // Just head.
		length=0;
	}
	
	if (length){
#ifdef USE_SENDFILE
		if (request->connection.listen_point->write==(void*)onion_http_write){ // Lets have a house party! I can use sendfile!
			onion_response_write(res,NULL,0);
			ONION_DEBUG("Using sendfile");
			int r=sendfile(request->connection.fd, fd, NULL, length);
			ONION_DEBUG("Wrote %d, should be %d (%s)", r, length, r==length ? "ok" : "nok");
			if (r!=length || r<0){
				ONION_ERROR("Could not send all file (%s)", strerror(errno));
				close(fd);
				return OCS_INTERNAL_ERROR;
			}
			res->sent_bytes+=length;
			res->sent_bytes_total+=length;
		}
		else
#endif
		{ // Ok, no I cant, do it as always.
			int r=0,w;
			size_t tr=0;
			char tmp[4046];
			if (length>sizeof(tmp)){
				size_t max=length-sizeof(tmp);
				while( tr<max ){
					r=read(fd,tmp,sizeof(tmp));
					tr+=r;
					if (r<0)
						break;
					w=onion_response_write(res, tmp, r);
					if (w!=r){
						ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
						break;
					}
				}
			}
			if (sizeof(tmp) >= (length-tr)){
				r=read(fd, tmp, length-tr);
				w=onion_response_write(res, tmp, r);
				if (w!=r){
					ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
				}
			}
		}
	}
	close(fd);
	return OCS_PROCESSED;
}
Example #20
0
int onion_jpeg_response ( unsigned char * image,
		int image_num_color_channels,
		J_COLOR_SPACE image_color_space, /* See jpeglib.h for list of available color spaces. */
		int image_width,
		int image_height,
		int output_quality,
		onion_response *res
		)
{
	struct jpeg_compress_struct cinfo;
  struct onion_error_mgr jerr;
	/* More stuff */
	JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
	int row_stride;               /* physical row width in image buffer */

	onion_response_set_header(res, "Content-Type", "image/jpeg");
	if (onion_response_write_headers(res)==OR_SKIP_CONTENT) // Maybe it was HEAD.
		return OCS_PROCESSED;

	/* Step 1: allocate and initialize JPEG compression object */
	// 1.1 Setup used error routines
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = onion_error_exit;
	/* Establish the setjmp return context for onion_error_exit to use. */
	if (setjmp(jerr.setjmp_buffer)) {
		/* If we get here, the JPEG code has signaled an error.
		 * We need to clean up the JPEG object, and return.
		 */
		jpeg_destroy_compress(&cinfo);
		return 0;
	}

	jpeg_create_compress(&cinfo);

	/* Step 2: specify data destination */
	onion_jpeg_data ojd;
	ojd.res=res;
	ojd.sent=0;
	cinfo.client_data = (void *)&ojd;

	// Set function handler to operate directly on response buffers
	struct jpeg_destination_mgr dest;
	cinfo.dest = &dest;
	cinfo.dest->init_destination = &onion_init_destination;
	cinfo.dest->empty_output_buffer = &onion_empty_output_buffer;
	cinfo.dest->term_destination = &onion_term_destination;

	/* Step 3: set parameters for compression */

	/* First we supply a description of the input image.
	 * Four fields of the cinfo struct must be filled in:
	 */
	cinfo.image_width = image_width;      /* image width and height, in pixels */
	cinfo.image_height = image_height;
	cinfo.input_components = image_num_color_channels;           /* # of color components per pixel */
	cinfo.in_color_space = image_color_space;       /* colorspace of input image */
	/* Now use the library's routine to set default compression parameters.
	 * (You must set at least cinfo.in_color_space before calling this,
	 * since the defaults depend on the source color space.)
	 */

	jpeg_set_defaults(&cinfo);
	/* Now you can set any non-default parameters you wish to.
	 * Here we just illustrate the use of quality (quantization table) scaling:
	 */
	jpeg_set_quality(&cinfo, output_quality, TRUE /* limit to baseline-JPEG values */);

	// If header already sent, then there was an error.
	if (ojd.sent){
		return OCS_PROCESSED;
	}

	/* Step 4: Start compressor */

	/* TRUE ensures that we will write a complete interchange-JPEG file.
	 * Pass TRUE unless you are very sure of what you're doing.
	 */
	jpeg_start_compress(&cinfo, TRUE);

	/* Step 5: while (scan lines remain to be written) */
	/*           jpeg_write_scanlines(...); */

	/* Here we use the library's state variable cinfo.next_scanline as the
	 * loop counter, so that we don't have to keep track ourselves.
	 * To keep things simple, we pass one scanline per call; you can pass
	 * more if you wish, though.
	 */
	row_stride = image_width * image_num_color_channels; /* JSAMPLEs per row in image_buffer */

	while (cinfo.next_scanline < cinfo.image_height) {
		/* jpeg_write_scanlines expects an array of pointers to scanlines.
		 * Here the array is only one element long, but you could pass
		 * more than one scanline at a time if that's more convenient.
		 */
		row_pointer[0] = & image[cinfo.next_scanline * row_stride];
		(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);

		// Check if response creation was aborted.
		if (ojd.sent){
			break;
		}
	}

	ojd.sent=1;

	/* Step 6: Finish compression */
	jpeg_finish_compress(&cinfo);

	/* Step 7: release JPEG compression object */
	jpeg_destroy_compress(&cinfo);

	/* That's it */
	return OCS_PROCESSED;
}