Esempio n. 1
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;
}
Esempio n. 2
0
//HTTP handler to forward http requests
int forward(void *p, onion_request *req, onion_response *res){

	if (onion_request_get_flags(req)&OR_GET){
		printf("We got a GET. WOHOOOOOO!!!\n")
	}

	//Set header and type
	onion_response_set_header(res, "HTTP-Translator", "Forward that data");
	onion_response_set_header(res, "Content-Type", "text/plain");

	//Set example message
	onion_response_write0(res, "First try");

	//Tell if everything worked properly
	return OCS_PROCESSED;
}
Esempio n. 3
0
/**
 * @short Main webdav handler, just redirects to the proper handler depending on headers and method
 */
onion_connection_status onion_webdav_handler(onion_webdav *wd, onion_request *req, onion_response *res){
	onion_response_set_header(res, "Dav", "1,2");
	onion_response_set_header(res, "MS-Author-Via", "DAV");
	
#ifdef __DEBUG__
	const onion_block *data=onion_request_get_data(req);
	if (data){
		ONION_DEBUG0("Have data!\n %s", onion_block_data(data));
	}
#endif

	
	char filename[512];
	snprintf(filename, sizeof(filename), "%s/%s", wd->path, onion_request_get_path(req));
	
	ONION_DEBUG("Check %s and %s", wd->path, filename);
	if (wd->check_permissions(wd->path, filename, req)!=0){
		return onion_shortcut_response("Forbidden", HTTP_FORBIDDEN, req, res);
	}

	
	switch (onion_request_get_flags(req)&OR_METHODS){
		case OR_GET:
		case OR_HEAD:
			return onion_webdav_get(filename, wd, req, res);
		case OR_OPTIONS:
			return onion_webdav_options(filename, wd, req, res);
		case OR_PROPFIND:
			return onion_webdav_propfind(filename, wd, req, res);
		case OR_PUT:
			return onion_webdav_put(filename, wd, req, res);
		case OR_DELETE:
			return onion_webdav_delete(filename, wd, req, res);
		case OR_MOVE:
			return onion_webdav_move(filename, wd, req, res);
		case OR_MKCOL:
			return onion_webdav_mkcol(filename, wd, req, res);
		case OR_PROPPATCH:
			return onion_webdav_proppatch(filename, wd, req, res);
	}
	
	onion_response_set_code(res, HTTP_NOT_IMPLEMENTED);
	onion_response_write0(res, "<h1>Work in progress...</h1>\n");
	
	return OCS_PROCESSED;
}
Esempio n. 4
0
/**
 * @short Sets the header length. Normally it should be through set_header, but as its very common and needs some procesing here is a shortcut
 * @memberof onion_response_t
 */
void onion_response_set_length(onion_response *res, size_t len){
	if (len!=res->sent_bytes && res->flags&OR_HEADER_SENT){
		ONION_WARNING("Trying to set length after headers sent. Undefined onion behaviour.");
		return;
	}
	char tmp[16];
	sprintf(tmp,"%lu",(unsigned long)len);
	onion_response_set_header(res, "Content-Length", tmp);
	res->length=len;
	res->flags|=OR_LENGTH_SET;
}
Esempio n. 5
0
/**
 * @short Shortcut to answer some json data
 * 
 * It converts to json the passed dict and returns it. The dict is freed before returning.
 */
onion_connection_status onion_shortcut_response_json(onion_dict *d, onion_request *req, onion_response *res){
	onion_response_set_header(res, "Content-Type", "application/json");
	
	onion_block *bl=onion_dict_to_json(d);
	onion_dict_free(d);
	char tmp[16];
	snprintf(tmp,sizeof(tmp),"%ld",(long)onion_block_size(bl));
	int ret=onion_shortcut_response_extra_headers(onion_block_data(bl), HTTP_OK, req, res, "Content-Length", tmp, NULL);
	onion_block_free(bl);
	return ret;
}
Esempio n. 6
0
int onion_handler_auth_pam_handler(onion_handler_auth_pam_data *d, onion_request *request, onion_response *res){
	/// Use session to know if already logged in, so do not mess with PAM so often.
	if (onion_request_get_session(request, "pam_logged_in"))
		return onion_handler_handle(d->inside, request, res);
	
	const char *o=onion_request_get_header(request, "Authorization");
	char *auth=NULL;
	char *username=NULL;
	char *passwd=NULL;
	if (o && strncmp(o,"Basic",5)==0){
		//fprintf(stderr,"auth: '%s'\n",&o[6]);
		auth=onion_base64_decode(&o[6], NULL);
		username=auth;
		int i=0;
		while (auth[i]!='\0' && auth[i]!=':') i++;
		if (auth[i]==':'){
			auth[i]='\0'; // so i have user ready
			passwd=&auth[i+1];
		}
		else
			passwd=NULL;
	}
	
	// I have my data, try to authorize
	if (username && passwd){
		int ok=authorize(d->pamname, username, passwd);
		
		if (ok){ // I save the username at the session, so it can be accessed later.
			onion_dict *session=onion_request_get_session_dict(request);
			onion_dict_lock_write(session);
			onion_dict_add(session, "username", username, OD_REPLACE|OD_DUP_VALUE);
			onion_dict_add(session, "pam_logged_in", username, OD_REPLACE|OD_DUP_VALUE);
			onion_dict_unlock(session);
			
			free(auth);
			return onion_handler_handle(d->inside, request, res);
		}
	}
	if (auth)
		free(auth);

	
	// Not authorized. Ask for it.
	char temp[256];
	sprintf(temp, "Basic realm=\"%s\"",d->realm);
	onion_response_set_header(res, "WWW-Authenticate",temp);
	onion_response_set_code(res, HTTP_UNAUTHORIZED);
	onion_response_set_length(res,sizeof(RESPONSE_UNAUTHORIZED));
	
	onion_response_write(res,RESPONSE_UNAUTHORIZED,sizeof(RESPONSE_UNAUTHORIZED));
	return OCS_PROCESSED;
}
Esempio n. 7
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;
}
Esempio n. 8
0
/**
 * @short Sets a new cookie into the response.
 * @ingroup response
 *
 * @param res Response object
 * @param cookiename Name for the cookie
 * @param cookievalue Value for the cookis
 * @param validity_t Seconds this cookie is valid (added to current datetime). -1 to do not expire, 0 to expire inmediatly.
 * @param path Cookie valid only for this path
 * @param Domain Cookie valid only for this domain (www.example.com, or *.example.com).
 * @param flags Flags from onion_cookie_flags_t, for example OC_SECURE or OC_HTTP_ONLY
 *
 * @returns boolean indicating succesfully added the cookie or not.
 *
 * If validity is 0, cookie is set to expire right now.
 *
 * If the cookie is too long (all data > 4096), it is not added. A warning is
 * emmited and returns false.
 */
bool onion_response_add_cookie(onion_response * res, const char *cookiename,
                               const char *cookievalue, time_t validity_t,
                               const char *path, const char *domain,
                               int flags) {
  char data[4096];
  int pos;
  pos = snprintf(data, sizeof(data), "%s=%s", cookiename, cookievalue);
  if (validity_t == 0)
    pos +=
        snprintf(data + pos, sizeof(data) - pos,
                 "; expires=Thu, 01 Jan 1970 00:00:00 GMT");
  else if (validity_t > 0) {
    struct tm *tmp;
    time_t t = time(NULL) + validity_t;
    tmp = localtime(&t);
    pos +=
        strftime(data + pos, sizeof(data) - pos,
                 "; expires=%a, %d %b %Y %H:%M:%S %Z", tmp);
  }
  if (path)
    pos += snprintf(data + pos, sizeof(data) - pos, "; path=%s", path);
  if (domain)
    pos += snprintf(data + pos, sizeof(data) - pos, "; domain=%s", domain);
  if (flags & OC_HTTP_ONLY)
    pos += snprintf(data + pos, sizeof(data) - pos, "; HttpOnly");
  if (flags & OC_SECURE)
    pos += snprintf(data + pos, sizeof(data) - pos, "; Secure");

  if (pos >= sizeof(data)) {
    ONION_WARNING("Cookie too long to be constructed. Not added to response.");
    return false;
  }

  onion_response_set_header(res, "Set-Cookie", data);
  ONION_DEBUG("Set cookie %s", data);

  return true;
}
Esempio n. 9
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;
}
Esempio n. 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){
	ONION_DEBUG0("PROPFIND");
	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(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(res, onion_block_data(block), onion_block_size(block));
	
	onion_block_free(block);
	
	return OCS_PROCESSED;
}
Esempio n. 11
0
/**
 * @short Returns the directory listing
 */
int onion_handler_export_local_directory(onion_handler_export_local_data *data, const char *realp, const char *showpath, onion_request *req, onion_response *res){
	DIR *dir=opendir(realp);
	if (!dir) // Continue on next. Quite probably a custom error.
		return 0;
	onion_response_set_header(res, "Content-Type", "text/html; charset=utf-8");
	
	onion_response_write0(res,"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
														"<html>\n"
														" <head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"content-type\"/>\n");
	onion_response_printf(res,"<title>Directory listing of %s</title>\n",showpath);
	onion_response_write0(res,"</head>\n" 
" <body>\n"
"<script>\n"
"showListing = function(){\n"
"	var q = function(t){\n"
"		return t.replace('\"','%22')\n"
"	}	\n"
"	var t=document.getElementById('filebody')\n"
"  while ( t.childNodes.length >= 1 )\n"
"		t.removeChild( t.firstChild );       \n"
"	for (var i=0;i<files.length;i++){\n"
"		var f=files[i]\n"
"		var h='<tr class=\"'+f[3]+'\"><td><a href=\"'+q(f[0])+'\">'+f[0]+'</a></td><td>'+f[1]+'</td><td>'+f[2]+'</td></tr>'\n"
"		t.innerHTML+=h\n"
"	}\n"
"}\n"
"\n"
"update = function(n){\n"
"	var _cmp = function(a,b){\n"
"		if (a[n]<b[n])\n"
"			return -1\n"
"		if (a[n]>b[n])\n"
"			return 1\n"
"		return 0\n"
"	}\n"
"	files=files.sort(_cmp)\n"
"	showListing()\n"
"}\n"
"window.onload=function(){\n"
"	files=files.splice(0,files.length-1)\n"
"	update(0)\n"
"}\n"
"\n"
"files=[\n");
	
	struct dirent *fi;
	struct stat    st;
	char temp[1024];
	struct passwd  *pwd;
	while ( (fi=readdir(dir)) != NULL ){
		if (fi->d_name[0]=='.')
			continue;
		snprintf(temp,sizeof(temp),"%s/%s",realp,fi->d_name);
		stat(temp, &st);
		pwd=getpwuid(st.st_uid);
		
		if (S_ISDIR(st.st_mode))
			onion_response_printf(res, "  ['%s/',%d,'%s','dir'],\n",fi->d_name, st.st_size, pwd ? pwd->pw_name : "???");
		else
			onion_response_printf(res, "  ['%s',%d,'%s','file'],\n",fi->d_name, st.st_size, pwd ? pwd->pw_name : "???");
	}

	onion_response_write0(res,"  [] ]\n</script>\n");

	
	if (data->renderer_header)
		data->renderer_header(res, showpath);
	
	
	onion_response_write0(res,"<table id=\"filelist\">\n"
				"<thead><tr>\n"
				" <th onclick=\"update(0)\" id=\"filename\">Filename</th>\n"
				" <th onclick=\"update(1)\" id=\"size\">Size</th>"
				" <th onclick=\"update(2)\" id=\"owner\">Owner</th></tr>\n"
				"</thead>\n"
				"<tbody id=\"filebody\">\n</tbody>\n</table>\n</body>\n");

	if (data->renderer_footer)
		data->renderer_footer(res, showpath);

	onion_response_write0(res,"</body></html>");

	closedir(dir);
	
	return OCS_PROCESSED;
}
Esempio n. 12
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;
}
Esempio n. 13
0
 void setHeader(const std::string &k, const std::string &v){
   onion_response_set_header(ptr, k.c_str(), v.c_str());
 }
Esempio n. 14
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;
}
Esempio n. 15
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;
}