/** * @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; }
//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; }
/** * @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; }
/** * @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; }
/** * @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; }
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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
void setHeader(const std::string &k, const std::string &v){ onion_response_set_header(ptr, k.c_str(), v.c_str()); }
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; }
/** * @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; }