onion_connection_status upload_file(upload_file_data *data, onion_request *req, onion_response *res){ if (onion_request_get_flags(req)&OR_POST){ const char *name=onion_request_get_post(req,"file"); const char *filename=onion_request_get_file(req,"file"); if (name && filename){ char finalname[1024]; snprintf(finalname,sizeof(finalname),"%s/%s",data->abspath,name); ONION_DEBUG("Copying from %s to %s",filename,finalname); unlink(finalname); // Just try to unlink it, if fail, sure its because it does not exist. int src=open(filename,O_RDONLY); int dst=open(finalname, O_WRONLY|O_CREAT, 0666); if (!src || !dst){ ONION_ERROR("Could not open src or dst file (%d %d)",src,dst); return OCS_INTERNAL_ERROR; } ssize_t r,w; char buffer[1024*4]; while ( (r=read(src,buffer,sizeof(buffer))) > 0){ w=write(dst,buffer,r); if (w!=r){ ONION_ERROR("Error writing file"); break; } } close(src); close(dst); } } return 0; // I just ignore the request, but process over the FILE data }
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 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);; }
/** * @short Serves a directory listing. * * It checks if the given request is a directory listing and processes it, or fallbacks to the * next handler. */ int fileserver_page(const char *basepath, onion_request *req, onion_response *res){ if ((onion_request_get_flags(req)&OR_METHODS) == OR_GET){ // only get. const char *path=onion_request_get_path(req); // Try to get the real path, and check if its a dir char dirname[256]; snprintf(dirname, sizeof(dirname), "%s/%s", basepath, onion_request_get_path(req)); char *realp=realpath(dirname, NULL); if (!realp) return OCS_INTERNAL_ERROR; DIR *dir=opendir(realp); if (dir){ // its a dir, fill the dictionary. onion_dict *d=onion_dict_new(); onion_dict_add(d, "dirname", path, 0); if (path[0]!='\0' && path[1]!='\0') onion_dict_add(d, "go_up", "true", 0); onion_dict *files=onion_dict_new(); onion_dict_add(d, "files", files, OD_DICT|OD_FREE_VALUE); struct dirent *de; while ( (de=readdir(dir)) ){ // Fill one files.[filename] per file. onion_dict *file=onion_dict_new(); onion_dict_add(files, de->d_name, file, OD_DUP_KEY|OD_DICT|OD_FREE_VALUE); onion_dict_add(file, "name", de->d_name, OD_DUP_VALUE); char tmp[256]; snprintf(tmp, sizeof(tmp), "%s/%s", realp, de->d_name); struct stat st; stat(tmp, &st); snprintf(tmp, sizeof(tmp), "%d", (int)st.st_size); onion_dict_add(file, "size", tmp, OD_DUP_VALUE); snprintf(tmp, sizeof(tmp), "%d", st.st_uid); onion_dict_add(file, "owner", tmp, OD_DUP_VALUE); if (S_ISDIR(st.st_mode)) onion_dict_add(file, "type", "dir", 0); else onion_dict_add(file, "type", "file", 0); } closedir(dir); free(realp); return fileserver_html_template(d, req, res); } free(realp); } return OCS_NOT_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; }
onion_connection_status post_json_check(json_response *post, onion_request *req, onion_response *res){ post->processed=1; FAIL_IF_NOT_EQUAL_INT(onion_request_get_flags(req)&OR_METHODS, OR_POST); FAIL_IF_NOT_EQUAL_STR(onion_request_get_header(req, "Content-Type"),"application/json"); const onion_block *data=onion_request_get_data(req); FAIL_IF_EQUAL(data, NULL); if (!data) return OCS_INTERNAL_ERROR; FAIL_IF_NOT_EQUAL_INT(onion_block_size(data), sizeof(JSON_EXAMPLE)); FAIL_IF_NOT_EQUAL_INT(memcmp(onion_block_data(data), JSON_EXAMPLE, sizeof(JSON_EXAMPLE)), 0); post->processed=2; 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; }
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 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; }