/** * @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; } }
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(); }
/** * @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; }
/** * @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; }
/** * @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; }
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; }
/** * @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; }
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; }
/* 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; }
/** * @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; }
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 ); }
/** * @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; }
/** * @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);; }
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); }
/** * @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 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; }
void writeHeaders(){ onion_response_write_headers(ptr); }
/** * @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; }
/** * @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; }
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; }