/* * Here's the routine that will replace the standard error_exit method: */ void onion_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a onion_error_mgr struct, so coerce pointer */ onion_error_ptr myerr = (onion_error_ptr) cinfo->err; /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ //(*cinfo->err->output_message) (cinfo); /* Create the message */ char* msg = (char *) malloc( JMSG_LENGTH_MAX); ( *(cinfo->err->format_message) ) (cinfo, msg); onion_jpeg_data *d=(onion_jpeg_data*)cinfo->client_data; if (!d->sent){ onion_response_set_code(d->res, HTTP_INTERNAL_ERROR); onion_response_printf(d->res, "Libpng error: %s", msg); d->sent=1; } ONION_ERROR("%s", msg); free(msg); msg=NULL; /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); }
// If not sent yet, writes the error. also always write it to the log. static void error(png_struct *p, const char *msg){ onion_png_data *d=(onion_png_data*)png_get_io_ptr(p); if (!d->sent){ onion_response_set_code(d->res, HTTP_INTERNAL_ERROR); onion_response_printf(d->res, "Libpng error: %s", msg); d->sent=1; } ONION_ERROR("%s", msg); }
/** * @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_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 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 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; }
/** * @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; }
/// Handles the write of static data static int onion_url_static(struct onion_url_static_data *data, onion_request *req, onion_response *res){ onion_response_set_code(res, data->code); onion_response_set_length(res, strlen(data->text)); onion_response_write0(res, data->text); 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 setCode(int code){ onion_response_set_code(ptr,code); }