void t02_several_add_methods(){ INIT_TEST(); onion_block *block=onion_block_new(); FAIL_IF_EQUAL(block, NULL); int i; for (i=0;i<1024;i++){ onion_block_add_char(block, (char)i); } onion_block_clear(block); onion_block_add_str(block, "first "); for (i=0;i<1024;i++) onion_block_add_str(block, "test "); FAIL_IF_NOT_STRSTR(onion_block_data(block), "test"); for (i=0;i<1024;i++) onion_block_add_data(block, "world", 4); FAIL_IF_STRSTR(onion_block_data(block), "world"); FAIL_IF_NOT_STRSTR(onion_block_data(block), "worl"); int s=onion_block_size(block); onion_block_add_block(block, block); FAIL_IF_NOT_EQUAL(onion_block_size(block), s+s); onion_block_free(block); END_TEST(); }
/// Writes the desired function to st->out static void function_write(parser_status *st, function_data *d){ if (d->code){ if (use_orig_line_numbers) fprintf(st->out, "#line 1\n"); if (d->is_static) fprintf(st->out, "static "); fprintf(st->out, "void %s(%s){\n", d->id, d->signature ? d->signature : "onion_dict *context, onion_response *res" ); if (use_orig_line_numbers){ fprintf(st->out, "#line 1\n"); // Write code, but change \n\n to \n const char *data=onion_block_data(d->code); int ldata=onion_block_size(d->code); int i=0, li=0; char lc=0; for (i=0;i<ldata;i++){ if (data[i]=='\n' && lc=='\n'){ // Two in a row fwrite(&data[li], 1, i-li-1, st->out); li=i; } lc=data[i]; } fwrite(&data[li], 1, i-li, st->out); fprintf(st->out, "#line 1\n"); } else{ fwrite(onion_block_data(d->code), 1, onion_block_size(d->code), st->out); } fprintf(st->out,"}\n"); } }
onion_connection_status return_length(void *_, onion_request * req, onion_response * res) { ONION_DEBUG("Data size: %d", onion_block_size(onion_request_get_data(req))); onion_response_printf(res, "%ld\n", onion_block_size(onion_request_get_data(req))); 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 Helps to prepare each pair. */ static void onion_dict_json_preorder(onion_block *block, const char *key, const void *value, int flags){ if (!onion_block_size(block)) // Error somewhere. return; char *s; s=onion_c_quote_new(key); if (s==NULL){ onion_block_clear(block); return; } onion_block_add_str(block, s); free(s); onion_block_add_char(block, ':'); if (flags&OD_DICT){ onion_block *tmp; tmp=onion_dict_to_json((onion_dict*)value); if (!tmp){ onion_block_clear(block); return; } onion_block_add_block(block, tmp); onion_block_free(tmp); } else{ s=onion_c_quote_new(value); if (s==NULL){ onion_block_clear(block); return; } onion_block_add_str(block, s); free(s); } onion_block_add_data(block, ", ",2); }
/** * @short One block read from in, prepare the output. * * Depending on the mode of the block it calls the appropiate handler: variable, tag or just write text. */ void write_block(parser_status *st, onion_block *b){ int mode=st->last_wmode; //ONION_DEBUG("Write mode %d, code %s", mode, b->data); switch(mode){ case TEXT: { int oldl; if ( (oldl=onion_block_size(b)) ){ char *safe=onion_c_quote_new(onion_block_data(b)); function_add_code(st, " onion_response_write(res, %s, %d);\n", safe, oldl); free(safe); } } break; case VARIABLE: variable_write(st, b); break; case TAG: tag_write(st, b); break; default: ONION_ERROR("Unknown final mode %d", mode); } onion_block_clear(st->rawblock); }
/** * @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 t04_server_overflow(){ INIT_LOCAL(); onion *server=onion_new(0); onion_listen_point *lp=onion_buffer_listen_point_new(); onion_add_listen_point(server,NULL,NULL,lp); onion_set_root_handler(server, onion_handler_static("Succedded", 200)); onion_block *long_req=onion_block_new(); onion_block_add_str(long_req,"GET / HTTP/1.1\n"); int i; for(i=0;i<1000;i++){ onion_block_add_str(long_req,"Header-1: This is header1 Header-2: This is header 2 "); } onion_request *req=onion_request_new(lp); onion_request_write(req, onion_block_data(long_req),onion_block_size(long_req)-1); // send it all, but the final 0. const char *buffer=onion_buffer_listen_point_get_buffer_data(req); FAIL_IF_NOT_EQUAL_STR(buffer,""); onion_request_write(req, "\n\n",2); // finish this request. no \n\n before to check possible bugs. buffer=onion_buffer_listen_point_get_buffer_data(req); FAIL_IF_EQUAL_STR(buffer,""); FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.1 200 OK\r\n"); FAIL_IF_NOT_STRSTR(buffer, "\r\nContent-Length: 9\r\n"); FAIL_IF_NOT_STRSTR(buffer, "libonion"); FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\nSuccedded"); onion_block_free(long_req); onion_request_free(req); onion_free(server); END_LOCAL(); }
/** * @short Returns the set of props for this query * * The block contains the propfind xml, and it returns the mask of props to show. * */ static int onion_webdav_parse_propfind(const onion_block *block){ // For parsing the data xmlDocPtr doc; doc = xmlParseMemory((char*)onion_block_data(block), onion_block_size(block)); if (doc == NULL) { ONION_ERROR("Error: unable to parse OPTIONS"); return OCS_INTERNAL_ERROR; } int props=0; xmlNode *root = NULL; root = xmlDocGetRootElement(doc); while (root){ if (strcmp((const char*)root->name,"propfind")==0){ xmlNode *propfind = root->children; while (propfind){ if (strcmp((const char*)propfind->name,"prop")==0){ xmlNode *prop = propfind->children; while (prop){ if (strcmp((const char*)prop->name, "text")==0) // ignore ; else if (strcmp((const char*)prop->name, "resourcetype")==0) props|=WD_RESOURCE_TYPE; else if (strcmp((const char*)prop->name, "getcontentlength")==0) props|=WD_CONTENT_LENGTH; else if (strcmp((const char*)prop->name, "getlastmodified")==0) props|=WD_LAST_MODIFIED; else if (strcmp((const char*)prop->name, "creationdate")==0) props|=WD_CREATION_DATE; else if (strcmp((const char*)prop->name, "getetag")==0) props|=WD_ETAG; else if (strcmp((const char*)prop->name, "getcontenttype")==0) props|=WD_CONTENT_TYPE; else if (strcmp((const char*)prop->name, "displayname")==0) props|=WD_DISPLAY_NAME; else if (strcmp((const char*)prop->name, "executable")==0) props|=WD_EXECUTABLE; else{ char tmp[256]; snprintf(tmp,sizeof(tmp),"g0:%s", prop->name); ONION_DEBUG("Unknown requested property with tag %s", prop->name); } prop=prop->next; } } propfind=propfind->next; } } root=root->next; } xmlFreeDoc(doc); return props; }
/** * @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; }
/// Writes the desired function to st->out static void function_write(parser_status * st, function_data * d) { ONION_DEBUG("Write function %s", d->id); if (d->code) { if (use_orig_line_numbers) fprintf(st->out, "#line 1\n"); if (d->is_static) fprintf(st->out, "static "); fprintf(st->out, "void %s(%s){\n", d->id, d->signature ? d->signature : "onion_dict *context, onion_response *res"); const char *data = onion_block_data(d->code); int ldata = onion_block_size(d->code); if (use_orig_line_numbers) { fprintf(st->out, "#line 1\n"); // Write code, but change \n\n to \n int i = 0, li = 0, diff; char lc = 0; ssize_t r; for (i = 0; i < ldata; i++) { if (data[i] == '\n' && lc == '\n') { // Two in a row diff = i - li - 1; r = fwrite(&data[li], 1, diff, st->out); if (r != diff) { ONION_ERROR("Could not write all data"); abort(); } li = i; } lc = data[i]; } diff = i - li; r = fwrite(&data[li], 1, diff, st->out); if (r != diff) { ONION_ERROR("Could not write all data"); abort(); } fprintf(st->out, "#line 1\n"); } else { ssize_t r = fwrite(data, 1, ldata, st->out); if (r != ldata) { ONION_ERROR("Could not write all data"); abort(); } } fprintf(st->out, "}\n"); } }
/** * @short One block read from in, prepare the output. * * Depending on the mode of the block it calls the appropiate handler: variable, tag or just write text. */ void write_block(parser_status *st, onion_block *b){ int mode=st->last_wmode; //ONION_DEBUG("Write mode %d, code %s", mode, b->data); switch(mode){ case TEXT: { int oldl; if ( (oldl=onion_block_size(b)) ){ if (oldl>0){ // Not Just \0 int use_orig_line_numbers_bak=use_orig_line_numbers; use_orig_line_numbers=0; char *safe=onion_c_quote_new(onion_block_data(b)); function_add_code(st, " onion_response_write(res, "); int pos=0; int size=strlen(safe); char *s=safe; //ONION_DEBUG("------- pos %d, size %d",pos,size); while (pos<size){ //ONION_DEBUG("pos %d, size %d",pos,size); char *e=strstr(s, "\n"); if (!e) break; *e='\0'; if (pos==0) function_add_code(st, "%s\n", s); else function_add_code(st, " %s\n", s); pos+=(e-s)+1; s=e+1; } if (pos==0) function_add_code(st, "%s, %d);\n", s, oldl); else function_add_code(st, " %s, %d);\n", s, oldl); use_orig_line_numbers=use_orig_line_numbers_bak; free(safe); } } } break; case VARIABLE: variable_write(st, b); break; case TAG: tag_write(st, b); break; default: ONION_ERROR("Unknown final mode %d", mode); } onion_block_clear(st->rawblock); }
/** * @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; }
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 Converts a dict to a json string * @memberof onion_dict_t * * Given a dictionary and a buffer (with size), it writes a json dictionary to it. * * @returns an onion_block with the json data, or NULL on error */ onion_block *onion_dict_to_json(onion_dict *dict){ onion_block *block=onion_block_new(); onion_block_add_char(block, '{'); if (dict && dict->root) onion_dict_node_preorder(dict->root, (void*)onion_dict_json_preorder, block); int s=onion_block_size(block); if (s==0){ // Error. onion_block_free(block); return NULL; } if (s!=1) // To remove a final ", " onion_block_rewind(block, 2); onion_block_add_char(block, '}'); return block; }
/** * @short Adds some code to the top function */ void function_add_code(parser_status *st, const char *fmt, ...){ function_data *p=(function_data*)st->function_stack->tail->data; if (p->flags&F_NO_MORE_WRITE) return; char tmp[4096]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (use_orig_line_numbers){ char line[32]; int p=onion_block_size(st->current_code); if (p && onion_block_data(st->current_code)[p-1]!='\n') onion_block_add_char(st->current_code, '\n'); snprintf(line,sizeof(line),"#line %d\n", st->line); // I have to do it for every \n too. This is going to be slow. const char *orig=tmp; int lorig=strlen(orig); int i=0, li=0; for (i=0;i<lorig;i++){ if (orig[i]=='\n'){ onion_block_add_str(st->current_code, line); onion_block_add_data(st->current_code, &orig[li], i-li+1); li=i; } } if (i-1!=li){ onion_block_add_str(st->current_code, line); onion_block_add_str(st->current_code, &orig[li]); } } else{ //ONION_DEBUG("Add to level %d text %s",list_count(st->function_stack), tmp); onion_block_add_str(st->current_code, tmp); } }
/** * @short Helps to prepare each pair. */ static void onion_dict_json_preorder(onion_block *block, const char *key, const void *value, int flags){ if (!onion_block_size(block)) // Error somewhere. return; onion_block_add_char(block,'\"'); onion_json_quote_add(block, key); onion_block_add_data(block,"\":",2); if (flags&OD_DICT){ onion_block *tmp; tmp=onion_dict_to_json((onion_dict*)value); if (!tmp){ onion_block_clear(block); return; } onion_block_add_block(block, tmp); onion_block_free(tmp); } else{ onion_block_add_char(block,'\"'); onion_json_quote_add(block, value); onion_block_add_char(block,'\"'); } onion_block_add_data(block, ", ",2); }
/** * @short Reads from the data to fulfill content-length data. */ static onion_connection_status parse_CONTENT_LENGTH(onion_request *req, onion_buffer *data){ onion_token *token=req->parser_data; size_t skip=data->pos; // First packet will be headers + some data, later ony data int length=data->size-skip; bool exit=false; size_t current_size=onion_block_size(req->data); ONION_DEBUG0("Adding data to request->data (non form POST) %d + %d / %d [%d/%d] %p", current_size, length, token->extra_size, skip, data->size, data->data+current_size); if (length + current_size >= token->extra_size){ ONION_DEBUG0("Done"); exit=true; length=token->extra_size - current_size; } onion_block_add_data(req->data, &data->data[skip], length); data->pos+=length; // done if (exit) return OCS_REQUEST_READY; return OCS_NEED_MORE_DATA; }
void t10_tojson(){ INIT_LOCAL(); onion_dict *d=onion_dict_new(); const char *tmp; int s; onion_block *b; b=onion_dict_to_json(d); tmp=onion_block_data(b); ONION_DEBUG("Json returned is '%s'", tmp); FAIL_IF_NOT_EQUAL_STR(tmp,"{}"); onion_block_free(b); onion_dict_add(d, "test", "json", 0); b=onion_dict_to_json(d); tmp=onion_block_data(b); s=onion_block_size(b); ONION_DEBUG("Json returned is '%s'", tmp); FAIL_IF(s<=0); FAIL_IF_EQUAL(strstr(tmp,"{"), NULL); FAIL_IF_EQUAL(strstr(tmp,"}"), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"test\""), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"json\""), NULL); FAIL_IF_NOT_EQUAL(strstr(tmp,","), NULL); onion_block_free(b); onion_dict_add(d, "other", "data", 0); b=onion_dict_to_json(d); tmp=onion_block_data(b); s=onion_block_size(b); ONION_DEBUG("Json returned is '%s'", tmp); FAIL_IF(s<=0); FAIL_IF_EQUAL(strstr(tmp,"{"), NULL); FAIL_IF_EQUAL(strstr(tmp,"}"), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"test\""), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"json\""), NULL); FAIL_IF_EQUAL(strstr(tmp,","), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"other\""), NULL); FAIL_IF_EQUAL(strstr(tmp,"\"data\""), NULL); onion_block_free(b); onion_dict_add(d, "with\"", "data\n", 0); b=onion_dict_to_json(d); tmp=onion_block_data(b); s=onion_block_size(b); ONION_DEBUG("Json returned is '%s'", tmp); FAIL_IF(s<=0); FAIL_IF_EQUAL(strstr(tmp,"\\n"), NULL); FAIL_IF_EQUAL(strstr(tmp,"\\\""), NULL); onion_block_free(b); onion_dict_free(d); END_LOCAL(); }
onion_block *connect_and_send(const char *ip, const char *port, const onion_block * msg, size_t maxsize) { int fd; { struct addrinfo hints; struct addrinfo *server; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; if (getaddrinfo(ip, port, &hints, &server) < 0) { ONION_ERROR("Error getting server info"); return NULL; } fd = socket(server->ai_family, server->ai_socktype | SOCK_CLOEXEC, server->ai_protocol); if (connect(fd, server->ai_addr, server->ai_addrlen) == -1) { close(fd); fd = -1; ONION_ERROR("Error connecting to server %s:%s", ip, port); return NULL; } freeaddrinfo(server); } size_t left = onion_block_size(msg); const char *data = onion_block_data(msg); while (left > 0) { ONION_DEBUG("."); int towrite = (left > maxsize) ? maxsize : left; int ret = write(fd, data, towrite); FAIL_IF(ret <= 0); if (ret <= 0) { ONION_ERROR("Error sending data."); return NULL; } left -= ret; data += ret; } onion_block *bl = onion_block_new(); char tmp[256]; int r = 0; int total = 0; do { r = read(fd, tmp, sizeof(tmp)); ONION_DEBUG("+ %d", r); if (r > 0) { total += r; onion_block_add_data(bl, tmp, r); } } while (r > 0); ONION_DEBUG("Total %d", total); FAIL_IF(total == 0); close(fd); return bl; }
/** * Current block is a tag, slice it and call the proper handler. */ void tag_write(parser_status *st, onion_block *b){ //ONION_DEBUG("Write tag %s",b->data); list *command=list_new((void*)tag_token_free); char mode=0; // 0 skip spaces, 1 in single var, 2 in quotes // Split into elements for the list int i, li=0; const char *data=onion_block_data(b); int size=onion_block_size(b); for (i=0;i<size;i++){ char c=data[i]; switch(mode){ case 0: if (!isspace(c)){ if (c=='"'){ mode=2; li=i+1; } else{ mode=1; li=i; } } break; case 1: if (isspace(c)){ mode=0; list_add(command, tag_token_new(&data[li], i-li, T_VAR)); } break; case 2: if (c=='"'){ mode=0; list_add(command, tag_token_new(&data[li], i-li, T_STRING)); } break; } } if (mode==1) list_add(command, tag_token_new(&data[li], i-li, T_VAR)); if (!command->head){ ONION_ERROR("%s:%d Incomplete command", st->infilename, st->line); st->status=1; return; } // Call the function. tag_token *commandtoken=command->head->data; const char *commandname=commandtoken->data; void (*f)(parser_status *st, list *args); f=(void*)onion_dict_get(tags, commandname); if (f) f(st, command); else{ ONION_ERROR("%s:%d Unknown command '%s'. ", st->infilename, st->line, commandname); st->status=1; } list_free(command); }