static int uwsgi_routing_func_basicauth(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { // skip if already authenticated if (wsgi_req->remote_user_len > 0) { return UWSGI_ROUTE_NEXT; } if (wsgi_req->authorization_len > 7 && ur->data2_len > 0) { if (strncmp(wsgi_req->authorization, "Basic ", 6)) goto forbidden; size_t auth_len = 0; char *auth = uwsgi_base64_decode(wsgi_req->authorization+6, wsgi_req->authorization_len-6, &auth_len); if (auth) { if (!ur->custom) { // check htpasswd-like file uint16_t ulen = htpasswd_check(ur->data2, auth); if (ulen > 0) { wsgi_req->remote_user = uwsgi_req_append(wsgi_req, "REMOTE_USER", 11, auth, ulen); if (!wsgi_req->remote_user) { free(auth); goto forbidden; } wsgi_req->remote_user_len = ulen; } else if (ur->data3_len == 0) { free(auth); goto forbidden; } } else { if (!uwsgi_strncmp(auth, auth_len, ur->data2, ur->data2_len)) { wsgi_req->remote_user = uwsgi_req_append(wsgi_req, "REMOTE_USER", 11, auth, ur->custom); if (!wsgi_req->remote_user) { free(auth); goto forbidden; } wsgi_req->remote_user_len = ur->custom; } else if (ur->data3_len == 0) { free(auth); goto forbidden; } } free(auth); return UWSGI_ROUTE_NEXT; } } forbidden: if (uwsgi_response_prepare_headers(wsgi_req, "401 Authorization Required", 26)) goto end; char *realm = uwsgi_concat3n("Basic realm=\"", 13, ur->data, ur->data_len, "\"", 1); // no need to check for errors uwsgi_response_add_header(wsgi_req, "WWW-Authenticate", 16, realm, 13 + ur->data_len + 1); free(realm); uwsgi_response_write_body_do(wsgi_req, "Unauthorized", 12); end: return UWSGI_ROUTE_BREAK; }
static int uwsgi_ssi_request(struct wsgi_request *wsgi_req) { struct uwsgi_buffer *ub = NULL; if (uwsgi_parse_vars(wsgi_req)) { return -1; } if (!wsgi_req->document_root_len || !wsgi_req->path_info_len) { uwsgi_log("[uwsgi-ssi] DOCUMENT_ROOT and PATH_INFO must be defined !!!\n"); uwsgi_500(wsgi_req); return UWSGI_OK; } char *filename = uwsgi_concat3n(wsgi_req->document_root, wsgi_req->document_root_len, "/", 1, wsgi_req->path_info, wsgi_req->path_info_len); size_t filename_len = wsgi_req->document_root_len + 1 + wsgi_req->path_info_len; // we expand the path for future security implementations char *real_filename = uwsgi_expand_path(filename, filename_len, NULL); free(filename); if (!real_filename) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_buffer *ub_ssi = uwsgi_buffer_from_file(real_filename); free(real_filename); if (!ub_ssi) { uwsgi_500(wsgi_req); return UWSGI_OK; } ub = uwsgi_ssi_parse(wsgi_req, ub_ssi->buf, ub_ssi->pos); uwsgi_buffer_destroy(ub_ssi); if (!ub) { uwsgi_500(wsgi_req); return UWSGI_OK; } // prepare headers if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) { uwsgi_500(wsgi_req); goto end; } // content_length if (uwsgi_response_add_content_length(wsgi_req, ub->pos)) { uwsgi_500(wsgi_req); goto end; } // content_type if (uwsgi_response_add_content_type(wsgi_req, "text/html", 9)) { uwsgi_500(wsgi_req); goto end; } uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); end: if (ub) { uwsgi_buffer_destroy(ub); } return UWSGI_OK; }
// status could be NNN or NNN message int uwsgi_response_prepare_headers(struct wsgi_request *wsgi_req, char *status, uint16_t status_len) { if (wsgi_req->headers_sent || wsgi_req->headers_size || wsgi_req->response_size || status_len < 3 || wsgi_req->write_errors) return -1; if (!wsgi_req->headers) { wsgi_req->headers = uwsgi_buffer_new(uwsgi.page_size); wsgi_req->headers->limit = UMAX16; } // reset the buffer (could be useful for rollbacks...) wsgi_req->headers->pos = 0; // reset headers count wsgi_req->header_cnt = 0; struct uwsgi_buffer *hh = NULL; wsgi_req->status = uwsgi_str3_num(status); #ifdef UWSGI_ROUTING // apply error routes if (uwsgi_apply_error_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { // from now on ignore write body requests... wsgi_req->ignore_body = 1; return -1; } wsgi_req->is_error_routing = 0; #endif if (status_len <= 4) { char *new_sc = NULL; size_t new_sc_len = 0; uint16_t sc_len = 0; const char *sc = uwsgi_http_status_msg(status, &sc_len); if (sc) { new_sc = uwsgi_concat3n(status, 3, " ", 1, (char *)sc, sc_len); new_sc_len = 4+sc_len; } else { new_sc = uwsgi_concat2n(status, 3, " Unknown", 8); new_sc_len = 11; } hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, new_sc, new_sc_len); free(new_sc); } else { hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, status, status_len); } if (!hh) {wsgi_req->write_errors++; return -1;} if (uwsgi_buffer_append(wsgi_req->headers, hh->buf, hh->pos)) goto error; uwsgi_buffer_destroy(hh); return 0; error: uwsgi_buffer_destroy(hh); wsgi_req->write_errors++; return -1; }
static char *htpasswd_check_sha1(char *pwd) { #ifdef UWSGI_SSL char sha1[20]; uwsgi_sha1(pwd, strlen(pwd), sha1); size_t len = 0; char *b64 = uwsgi_base64_encode(sha1, 20, &len); if (!b64) return NULL; // we add a new line for being fgets-friendly char *crypted = uwsgi_concat3n("{SHA}", 5, b64, len, "\n", 1); free(b64); return crypted; #else uwsgi_log("*** WARNING, rebuild uWSGI with SSL support for htpasswd sha1 feature ***\n"); return NULL; #endif }
// status could be NNN or NNN message int uwsgi_response_prepare_headers(struct wsgi_request *wsgi_req, char *status, uint16_t status_len) { if (wsgi_req->headers_sent || wsgi_req->headers_size || wsgi_req->response_size || status_len < 3 || wsgi_req->write_errors) return -1; if (!wsgi_req->headers) { wsgi_req->headers = uwsgi_buffer_new(uwsgi.page_size); wsgi_req->headers->limit = UMAX16; } // reset the buffer (could be useful for rollbacks...) wsgi_req->headers->pos = 0; struct uwsgi_buffer *hh = NULL; if (status_len <= 4) { char *new_sc = NULL; size_t new_sc_len = 0; uint16_t sc_len = 0; const char *sc = uwsgi_http_status_msg(status, &sc_len); if (sc) { new_sc = uwsgi_concat3n(status, 3, " ", 1, (char *)sc, sc_len); new_sc_len = 4+sc_len; } else { new_sc = uwsgi_concat2n(status, 3, " Unknown", 8); new_sc_len = 11; } hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, new_sc, new_sc_len); free(new_sc); } else { hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, status, status_len); } if (!hh) {wsgi_req->write_errors++; return -1;} if (uwsgi_buffer_append(wsgi_req->headers, hh->buf, hh->pos)) goto error; uwsgi_buffer_destroy(hh); wsgi_req->status = uwsgi_str3_num(status); return 0; error: uwsgi_buffer_destroy(hh); wsgi_req->write_errors++; return -1; }
/* this is the dynamic loader, it loads app reading information from a wsgi_request */ PyObject *uwsgi_dyn_loader(void *arg1) { PyObject *callable = NULL; char *tmpstr; struct wsgi_request *wsgi_req = (struct wsgi_request *) arg1; // MANAGE UWSGI_SCRIPT if (wsgi_req->script_len > 0) { tmpstr = uwsgi_strncopy(wsgi_req->script, wsgi_req->script_len); callable = uwsgi_uwsgi_loader((void *)tmpstr); free(tmpstr); } // MANAGE UWSGI_MODULE else if (wsgi_req->module_len > 0) { if (wsgi_req->callable_len > 0) { tmpstr = uwsgi_concat3n(wsgi_req->module, wsgi_req->module_len, ":", 1, wsgi_req->callable, wsgi_req->callable_len); } else { tmpstr = uwsgi_strncopy(wsgi_req->module, wsgi_req->module_len); } callable = uwsgi_uwsgi_loader((void *)tmpstr); free(tmpstr); } // MANAGE UWSGI_FILE else if (wsgi_req->file_len > 0) { tmpstr = uwsgi_strncopy(wsgi_req->file, wsgi_req->file_len); callable = uwsgi_file_loader((void *)tmpstr); free(tmpstr); } // TODO MANAGE UWSGI_PASTE /* else if (wsgi_req->wsgi_paste_len > 0) { tmpstr = uwsgi_strncopy(wsgi_req->paste, wsgi_req->paste_len); callable = uwsgi_paste_loader((void *)tmpstr); free(tmpstr); } */ return callable; }
static int uwsgi_mongrel2_json_parse(json_t * root, struct wsgi_request *wsgi_req) { char *json_val; char *query_string = NULL; size_t query_string_len = 0; size_t script_name_len = 0; void *json_iter; char *json_key; json_t *json_value; if ((json_val = uwsgi_mongrel2_json_get_string(root, "METHOD"))) { if (!strcmp(json_val, "JSON")) { return -1; } wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_METHOD", 14, json_val, strlen(json_val)); } // pretty easy, we get the file and we map it to wsgi_req->post_file, uWSGI read api will automatically use this if ((json_val = uwsgi_mongrel2_json_get_string(root, "x-mongrel2-upload-done"))) { wsgi_req->post_file = fopen(json_val, "r"); if (!wsgi_req->post_file) { uwsgi_error_open(json_val); return -1; } } else if (uwsgi_mongrel2_json_get_string(root, "x-mongrel2-upload-start")) { return -1; } wsgi_req->uh->pktsize += uwsgi_mongrel2_json_add(wsgi_req, root, "VERSION", "SERVER_PROTOCOL", 15, NULL, NULL); wsgi_req->uh->pktsize += uwsgi_mongrel2_json_add(wsgi_req, root, "QUERY", "QUERY_STRING", 12, &query_string, &query_string_len); if (query_string == NULL) { // always set QUERY_STRING wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "QUERY_STRING", 12, "", 0); } // set SCRIPT_NAME to an empty value wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SCRIPT_NAME", 11, "", 0); if ((json_val = uwsgi_mongrel2_json_get_string(root, "PATH"))) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "PATH_INFO", 9, json_val + script_name_len, strlen(json_val + script_name_len)); if (query_string_len) { char *request_uri = uwsgi_concat3n(json_val, strlen(json_val), "?", 1, query_string, query_string_len); wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_URI", 11, request_uri, strlen(json_val) + 1 + query_string_len); free(request_uri); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_URI", 11, json_val, strlen(json_val)); } } if ((json_val = uwsgi_mongrel2_json_get_string(root, "URL_SCHEME"))) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "UWSGI_SCHEME", 12, json_val, strlen(json_val)); } if ((json_val = uwsgi_mongrel2_json_get_string(root, "host"))) { char *colon = strchr(json_val, ':'); if (colon) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PORT", 11, colon + 1, strlen(colon + 1)); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PORT", 11, "80", 2); } } if ((json_val = uwsgi_mongrel2_json_get_string(root, "x-forwarded-for"))) { char *colon = strchr(json_val, ','); if (colon) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REMOTE_ADDR", 11, colon + 1, (colon + 1) - json_val); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REMOTE_ADDR", 11, json_val, strlen(json_val)); } } if ((json_val = uwsgi_mongrel2_json_get_string(root, "content-length"))) { wsgi_req->post_cl = atoi(json_val); } wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_NAME", 11, uwsgi.hostname, uwsgi.hostname_len); json_iter = json_object_iter(root); while (json_iter) { json_key = (char *) json_object_iter_key(json_iter); // is it a header ? if (json_key[0] >= 97) { json_value = json_object_iter_value(json_iter); if (json_is_string(json_value)) { json_val = (char *) json_string_value(json_value); wsgi_req->uh->pktsize += proto_base_add_uwsgi_header(wsgi_req, json_key, strlen(json_key), json_val, strlen(json_val)); } } json_iter = json_object_iter_next(root, json_iter); } return 0; }
// dumb/fake tnetstring implementation...all is a string static int uwsgi_mongrel2_tnetstring_parse(struct wsgi_request *wsgi_req, char *buf, int len) { char *ptr = buf; char *watermark = buf + len; char *key = NULL; size_t keylen = 0; char *val = NULL; size_t vallen = 0; uint16_t script_name_len = 0; char *query_string = NULL; uint16_t query_string_len = 0; int async_upload = 0; // set an empty SCRIPT_NAME wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SCRIPT_NAME", 11, "", 0); while (ptr < watermark) { ptr = uwsgi_netstring(ptr, len - (ptr - buf), &key, &keylen); if (ptr == NULL) break; // empty keys are not allowed if (keylen == 0) break; if (ptr >= watermark) break; ptr = uwsgi_netstring(ptr, len - (ptr - buf), &val, &vallen); if (ptr == NULL) break; if (key[0] < 97) { if (!uwsgi_strncmp("METHOD", 6, key, keylen)) { if (!uwsgi_strncmp("JSON", 4, val, vallen)) { return -1; } wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_METHOD", 14, val, vallen); } else if (!uwsgi_strncmp("VERSION", 7, key, keylen)) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PROTOCOL", 15, val, vallen); } else if (!uwsgi_strncmp("QUERY", 5, key, keylen)) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "QUERY_STRING", 12, val, vallen); query_string = val; query_string_len = vallen; } else if (!uwsgi_strncmp("PATH", 4, key, keylen)) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "PATH_INFO", 9, val + script_name_len, vallen - script_name_len); if (query_string_len) { char *request_uri = uwsgi_concat3n(val, vallen, "?", 1, query_string, query_string_len); wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_URI", 11, request_uri, vallen + 1 + query_string_len); free(request_uri); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_URI", 11, val, vallen); } } } else { // add header if (!uwsgi_strncmp("host", 4, key, keylen)) { char *colon = memchr(val, ':', vallen); if (colon) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PORT", 11, colon + 1, vallen - ((colon + 1) - val)); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PORT", 11, "80", 2); } } else if (!uwsgi_strncmp("content-length", 14, key, keylen)) { wsgi_req->post_cl = uwsgi_str_num(val, vallen); } else if (!uwsgi_strncmp("x-mongrel2-upload-done", 22, key, keylen)) { char *post_filename = uwsgi_concat2n(val, vallen, "", 0); wsgi_req->post_file = fopen(post_filename, "r"); if (!wsgi_req->post_file) { uwsgi_error_open(post_filename); wsgi_req->do_not_log = 1; } async_upload += 2; free(post_filename); } else if (!uwsgi_strncmp("x-forwarded-for", 15, key, keylen)) { char *colon = memchr(val, ',', vallen); if (colon) { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REMOTE_ADDR", 11, colon + 1, (colon + 1) - val); } else { wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REMOTE_ADDR", 11, val, vallen); } } else if (!uwsgi_strncmp("x-mongrel2-upload-start", 23, key, keylen)) { async_upload += 1; } wsgi_req->uh->pktsize += proto_base_add_uwsgi_header(wsgi_req, key, keylen, val, vallen); } } wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "SERVER_NAME", 11, uwsgi.hostname, uwsgi.hostname_len); if (query_string == NULL) { // always set QUERY_STRING wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "QUERY_STRING", 12, "", 0); } // reject uncomplete upload if (async_upload == 1) { return -1; } return 0; }
static int uwsgi_webdav_add_props(struct wsgi_request *wsgi_req, xmlNode *req_prop, xmlNode * multistatus, xmlNsPtr dav_ns, char *uri, char *filename, int with_values) { struct stat st; if (stat(filename, &st)) { uwsgi_error("uwsgi_webdav_add_props()/stat()"); return -1; } int is_collection = 0; xmlNode *response = xmlNewChild(multistatus, dav_ns, BAD_CAST "response", NULL); uint16_t uri_len = strlen(uri) ; char *encoded_uri = uwsgi_malloc( (uri_len * 3) + 1); http_url_encode(uri, &uri_len, encoded_uri); encoded_uri[uri_len] = 0; xmlNewChild(response, dav_ns, BAD_CAST "href", BAD_CAST encoded_uri); free(encoded_uri); xmlNode *r_propstat = xmlNewChild(response, dav_ns, BAD_CAST "propstat", NULL); char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7); xmlNewChild(r_propstat, dav_ns, BAD_CAST "status", BAD_CAST r_status); free(r_status); xmlNode *r_prop = xmlNewChild(r_propstat, dav_ns, BAD_CAST "prop", NULL); if (with_values) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "displayname")) { char *base_uri = uwsgi_get_last_char(uri, '/'); if (base_uri) { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST base_uri+1); } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST uri); } } if (S_ISDIR(st.st_mode)) is_collection = 1; xmlNode *r_type = NULL; if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { r_type = xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (is_collection) { xmlNewChild(r_type, dav_ns, BAD_CAST "collection", NULL); is_collection = 1; } } if (!is_collection) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontentlength")) { char *r_contentlength = uwsgi_num2str(st.st_size); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", BAD_CAST r_contentlength); free(r_contentlength); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontenttype")) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filename, strlen(filename), &mime_type_len); if (mime_type) { char *r_ctype = uwsgi_concat2n(mime_type, mime_type_len, "", 0); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getcontenttype", BAD_CAST r_ctype); free(r_ctype); } } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "creationdate")) { // there is no creation date on UNIX/POSIX, ctime is the nearest thing... char *cdate = uwsgi_webdav_new_date(st.st_ctime); if (cdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "creationdate", BAD_CAST cdate); free(cdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getlastmodified")) { char *mdate = uwsgi_webdav_new_date(st.st_mtime); if (mdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getlastmodified", BAD_CAST mdate); free(mdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getetag")) { char *etag = uwsgi_num2str(st.st_mtime); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getetag", BAD_CAST etag); free(etag); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "executable")) { xmlNewChild(r_prop, dav_ns, BAD_CAST "executable", NULL); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "owner")) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "owner", NULL); } if (wsgi_req->remote_user_len > 0) { if (udav.principal_base) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-principal")) { char *current_user_principal = uwsgi_concat2n(udav.principal_base, strlen(udav.principal_base), wsgi_req->remote_user, wsgi_req->remote_user_len); xmlNode *cup = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); xmlNewTextChild(cup, dav_ns, BAD_CAST "href", BAD_CAST current_user_principal); if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { if (!strcmp(current_user_principal, uri)) { xmlNewChild(r_type, dav_ns, BAD_CAST "principal", NULL); } } free(current_user_principal); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-privilege-set")) { xmlNode *cups = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); xmlNode *privilege = xmlNewChild(cups, dav_ns, BAD_CAST "privilege", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "all", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "read", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-content", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-properties", NULL); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "supported-report-set")) { xmlNode *report_set = xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); xmlNode *supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-property-search"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "sync-collection"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "expand-property"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-search-property-set"); } uwsgi_webdav_foreach_prop(udav.add_prop, req_prop, r_prop, 0, NULL ); uwsgi_webdav_foreach_prop(udav.add_prop_href, req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_prop_comp,req_prop, r_prop, 2 , NULL); uwsgi_webdav_foreach_prop(udav.add_rtype_prop,req_prop, r_type, 0, "resourcetype"); if (is_collection) { uwsgi_webdav_foreach_prop(udav.add_rtype_collection_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_collection_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_comp,req_prop, r_prop, 2, NULL); } else { uwsgi_webdav_foreach_prop(udav.add_rtype_object_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_object_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_comp,req_prop, r_prop, 2, NULL); } } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (!S_ISDIR(st.st_mode)) { xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontenttype", NULL); } xmlNewChild(r_prop, dav_ns, BAD_CAST "creationdate", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getlastmodified", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); if (wsgi_req->remote_user_len > 0) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); if (udav.principal_base) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); } } } #if defined(__linux__) || defined(__APPLE__) // get xattr for user.uwsgi.webdav. #if defined(__linux__) ssize_t rlen = listxattr(filename, NULL, 0); #elif defined(__APPLE__) ssize_t rlen = listxattr(filename, NULL, 0, 0); #endif // do not return -1 as the previous xml is valid !!! if (rlen <= 0) return 0; // use calloc to avoid races char *xattrs = uwsgi_calloc(rlen); #if defined(__linux__) if (listxattr(filename, xattrs, rlen) <= 0) { #elif defined(__APPLE__) if (listxattr(filename, xattrs, rlen, 0) <= 0) { #endif free(xattrs); return 0; } // parse the name list ssize_t i; char *key = NULL; for(i=0;i<rlen;i++) { // check for wrong condition if (xattrs[i] == 0 && key == NULL) break; if (key && xattrs[i] == 0) { if (!uwsgi_starts_with(key, strlen(key), "user.uwsgi.webdav.", 18)) { if (uwsgi_string_list_has_item(udav.skip_prop, key + 18, strlen(key + 18))) continue; xmlNsPtr xattr_ns = NULL; // does it has a namespace ? char *separator = strchr(key + 18, '|'); char *xattr_key = key + 18; if (separator) { xattr_key = separator + 1; *separator = 0; if (!uwsgi_webdav_prop_requested(req_prop, key + 18, xattr_key)) continue; } else { if (!uwsgi_webdav_prop_requested(req_prop, NULL, xattr_key)) continue; } xmlNode *xattr_item = NULL; if (with_values) { #if defined(__linux__) ssize_t rlen2 = getxattr(filename, key, NULL, 0); #elif defined(__APPLE__) ssize_t rlen2 = getxattr(filename, key, NULL, 0, 0, 0); #endif if (rlen > 0) { // leave space for final 0 char *xvalue = uwsgi_calloc(rlen2 + 1); #if defined(__linux__) if (getxattr(filename, key, xvalue, rlen2) > 0) { #elif defined(__APPLE__) if (getxattr(filename, key, xvalue, rlen2, 0 ,0) > 0) { #endif xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, BAD_CAST xvalue); } free(xvalue); } else if (rlen == 0) { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } } else { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } if (separator && xattr_item) { xattr_ns = xmlNewNs(xattr_item, BAD_CAST (key + 18), NULL); *separator = '|'; xmlSetNs(xattr_item, xattr_ns); } } key = NULL; } else if (key == NULL) { key = &xattrs[i]; } } free(xattrs); #endif return 0; } static size_t uwsgi_webdav_expand_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; char *docroot = ua->interpreter; size_t docroot_len = strlen(docroot); // merge docroot with path_info char *tmp_filename = uwsgi_concat3n(docroot, docroot_len, "/", 1, item, item_len); // try expanding the path if (!realpath(tmp_filename, filename)) { free(tmp_filename); return 0; } free(tmp_filename); return strlen(filename); } static size_t uwsgi_webdav_expand_fake_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { char *last_slash = uwsgi_get_last_charn(item, item_len, '/'); if (!last_slash) return 0; size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, item, last_slash - item, filename); if (!filename_len) return 0; // check for overflow if (filename_len + (item_len - (last_slash - item)) >= PATH_MAX) return 0; memcpy(filename + filename_len, last_slash, (item_len - (last_slash - item))); filename_len += (item_len - (last_slash - item)); filename[(int)filename_len] = 0; return filename_len; } static xmlDoc *uwsgi_webdav_manage_prop(struct wsgi_request *wsgi_req, xmlNode *req_prop, char *filename, size_t filename_len, int with_values) { // default 1 depth int depth = 1; uint16_t http_depth_len = 0; char *http_depth = uwsgi_get_var(wsgi_req, "HTTP_DEPTH", 10, &http_depth_len); if (http_depth) { depth = uwsgi_str_num(http_depth, http_depth_len); } xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0"); xmlNode *multistatus = xmlNewNode(NULL, BAD_CAST "multistatus"); xmlDocSetRootElement(rdoc, multistatus); xmlNsPtr dav_ns = xmlNewNs(multistatus, BAD_CAST "DAV:", BAD_CAST "D"); xmlSetNs(multistatus, dav_ns); if (depth == 0) { char *uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, filename, with_values); free(uri); } else { DIR *collection = opendir(filename); struct dirent de; for (;;) { struct dirent *de_r = NULL; if (readdir_r(collection, &de, &de_r)) { uwsgi_error("uwsgi_wevdav_manage_propfind()/readdir_r()"); break; } if (de_r == NULL) { break; } char *uri = NULL; char *direntry = NULL; if (!strcmp(de.d_name, "..")) { // skip .. continue; } else if (!strcmp(de.d_name, ".")) { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); direntry = uwsgi_concat2n(filename, filename_len, "", 0); } else if (wsgi_req->path_info[wsgi_req->path_info_len - 1] == '/') { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } else { uri = uwsgi_concat3n(wsgi_req->path_info, wsgi_req->path_info_len, "/", 1, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, direntry, with_values); free(uri); free(direntry); } closedir(collection); } return rdoc; }
int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_t document_root_len, char *path_info, uint16_t path_info_len, int is_a_file) { struct stat st; char real_filename[PATH_MAX + 1]; size_t real_filename_len = 0; char *filename = NULL; size_t filename_len = 0; struct uwsgi_string_list *index = NULL; if (!is_a_file) { filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len); filename_len = document_root_len + 1 + path_info_len; } else { filename = uwsgi_concat2n(document_root, document_root_len, "", 0); filename_len = document_root_len; } #ifdef UWSGI_DEBUG uwsgi_log("[uwsgi-fileserve] checking for %s\n", filename); #endif if (uwsgi.static_cache_paths) { uwsgi_rlock(uwsgi.static_cache_paths->lock); uint64_t item_len; char *item = uwsgi_cache_get2(uwsgi.static_cache_paths, filename, filename_len, &item_len); if (item && item_len > 0 && item_len <= PATH_MAX) { memcpy(real_filename, item, item_len); real_filename_len = item_len; uwsgi_rwunlock(uwsgi.static_cache_paths->lock); goto found; } uwsgi_rwunlock(uwsgi.static_cache_paths->lock); } if (!realpath(filename, real_filename)) { #ifdef UWSGI_DEBUG uwsgi_log("[uwsgi-fileserve] unable to get realpath() of the static file\n"); #endif free(filename); return -1; } real_filename_len = strlen(real_filename); if (uwsgi.static_cache_paths) { uwsgi_wlock(uwsgi.static_cache_paths->lock); uwsgi_cache_set2(uwsgi.static_cache_paths, filename, filename_len, real_filename, real_filename_len, uwsgi.use_static_cache_paths, UWSGI_CACHE_FLAG_UPDATE); uwsgi_rwunlock(uwsgi.static_cache_paths->lock); } found: free(filename); if (uwsgi_starts_with(real_filename, real_filename_len, document_root, document_root_len)) { struct uwsgi_string_list *safe = uwsgi.static_safe; while(safe) { if (!uwsgi_starts_with(real_filename, real_filename_len, safe->value, safe->len)) { goto safe; } safe = safe->next; } uwsgi_log("[uwsgi-fileserve] security error: %s is not under %.*s or a safe path\n", real_filename, document_root_len, document_root); return -1; } safe: if (!uwsgi_static_stat(wsgi_req, real_filename, &real_filename_len, &st, &index)) { if (index) { // if we are here the PATH_INFO need to be changed if (uwsgi_req_append_path_info_with_index(wsgi_req, index->value, index->len)) { return -1; } } // skip methods other than GET and HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3) && uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { return -1; } // check for skippable ext struct uwsgi_string_list *sse = uwsgi.static_skip_ext; while (sse) { if (real_filename_len >= sse->len) { if (!uwsgi_strncmp(real_filename + (real_filename_len - sse->len), sse->len, sse->value, sse->len)) { return -1; } } sse = sse->next; } #ifdef UWSGI_ROUTING // before sending the file, we need to check if some rule applies if (!wsgi_req->is_routing && uwsgi_apply_routes_do(wsgi_req, NULL, 0) == UWSGI_ROUTE_BREAK) { return 0; } wsgi_req->routes_applied = 1; #endif return uwsgi_real_file_serve(wsgi_req, real_filename, real_filename_len, &st); } return -1; }
struct uwsgi_socket *uwsgi_new_socket(char *name) { struct uwsgi_socket *uwsgi_sock = uwsgi.sockets, *old_uwsgi_sock; struct sockaddr_in sin; socklen_t socket_type_len; if (!uwsgi_sock) { uwsgi.sockets = uwsgi_malloc(sizeof(struct uwsgi_socket)); uwsgi_sock = uwsgi.sockets; } else { while (uwsgi_sock) { old_uwsgi_sock = uwsgi_sock; uwsgi_sock = uwsgi_sock->next; } uwsgi_sock = uwsgi_malloc(sizeof(struct uwsgi_socket)); old_uwsgi_sock->next = uwsgi_sock; } memset(uwsgi_sock, 0, sizeof(struct uwsgi_socket)); uwsgi_sock->name = name; uwsgi_sock->fd = -1; if (!name) return uwsgi_sock; if (name[0] == '=') { int shared_socket = atoi(uwsgi_sock->name + 1); if (shared_socket >= 0) { struct uwsgi_socket *uss = uwsgi_get_shared_socket_by_num(shared_socket); if (!uss) { uwsgi_log("unable to use shared socket %d\n", shared_socket); exit(1); } uwsgi_sock->bound = 1; uwsgi_sock->shared = 1; uwsgi_sock->from_shared = shared_socket; return uwsgi_sock; } } if (!uwsgi_startswith(name, "fd://", 5)) { uwsgi_add_socket_from_fd(uwsgi_sock, atoi(name + 5)); return uwsgi_sock; } char *tcp_port = strrchr(name, ':'); if (tcp_port) { // INET socket, check for 0 port if (tcp_port[1] == 0 || tcp_port[1] == '0') { uwsgi_sock->fd = bind_to_tcp(name, uwsgi.listen_queue, tcp_port); uwsgi_sock->family = AF_INET; uwsgi_sock->bound = 1; uwsgi_sock->auto_port = 1; socket_type_len = sizeof(struct sockaddr_in); if (getsockname(uwsgi_sock->fd, (struct sockaddr *) &sin, &socket_type_len)) { uwsgi_error("getsockname()"); exit(1); } char *auto_port = uwsgi_num2str(ntohs(sin.sin_port)); uwsgi_sock->name = uwsgi_concat3n(name, tcp_port - name, ":", 1, auto_port, strlen(auto_port)); } // is it fd 0 ? else if (tcp_port[1] == ':') { uwsgi_sock->fd = 0; uwsgi_sock->family = AF_INET; uwsgi_sock->bound = 1; socket_type_len = sizeof(struct sockaddr_in); if (getsockname(0, (struct sockaddr *) &sin, &socket_type_len)) { uwsgi_error("getsockname()"); exit(1); } char *auto_port = uwsgi_num2str(ntohs(sin.sin_port)); char *auto_ip = inet_ntoa(sin.sin_addr); uwsgi_sock->name = uwsgi_concat3n(auto_ip, strlen(auto_ip), ":", 1, auto_port, strlen(auto_port)); } } return uwsgi_sock; }
static int uwsgi_request_xslt(struct wsgi_request *wsgi_req) { char *xmlfile = NULL; char *output = NULL; int output_rlen = 0; char filename[PATH_MAX+1]; size_t filename_len = 0; char stylesheet[PATH_MAX+1]; size_t stylesheet_len = 0; char *params = NULL; if (uwsgi_parse_vars(wsgi_req)) { return -1; } // set default values if (!uxslt.content_type_len) { if (!uxslt.content_type) { uxslt.content_type = "text/html"; } uxslt.content_type_len = strlen(uxslt.content_type); } struct uwsgi_string_list *usl = uxslt.docroot; // first check for static docroots if (usl) { while(usl) { xmlfile = uwsgi_concat3n(usl->value, usl->len, "/", 1, wsgi_req->path_info, wsgi_req->path_info_len); if (uwsgi_is_file(xmlfile)) { break; } free(xmlfile); xmlfile = NULL; usl = usl->next; } } // fallback to DOCUMENT_ROOT else { if (wsgi_req->document_root_len == 0) { uwsgi_403(wsgi_req); return UWSGI_OK; } xmlfile = uwsgi_concat3n(wsgi_req->document_root, wsgi_req->document_root_len, "/", 1, wsgi_req->path_info, wsgi_req->path_info_len); } if (!xmlfile) { uwsgi_404(wsgi_req); return UWSGI_OK; } // we have the full path, check if it is valid if (!uwsgi_expand_path(xmlfile, strlen(xmlfile), filename)) { free(xmlfile); uwsgi_404(wsgi_req); return UWSGI_OK; } free(xmlfile); if (!uwsgi_is_file(filename)) { uwsgi_403(wsgi_req); return UWSGI_OK; } filename_len = strlen(filename); // now search for the xslt file int found = 0; // first check for specific vars usl = uxslt.var; while(usl) { uint16_t rlen; char *value = uwsgi_get_var(wsgi_req, usl->value, usl->len, &rlen); if (value) { memcpy(stylesheet, value, rlen); stylesheet[rlen] = 0; stylesheet_len = rlen; found = 1; break; } usl = usl->next; } if (found) goto apply; // then check for custom extensions if (uxslt.ext) { usl = uxslt.ext; while(usl) { char *tmp_path = uwsgi_concat2n(filename, filename_len, usl->value, usl->len); if (uwsgi_is_file(tmp_path)) { stylesheet_len = filename_len + usl->len; memcpy(stylesheet, tmp_path, stylesheet_len); stylesheet[stylesheet_len] = 0; free(tmp_path); found = 1; break; } free(tmp_path); usl = usl->next; } } // use default extensions .xsl/.xslt else { char *tmp_path = uwsgi_concat2n(filename, filename_len, ".xsl", 4); if (uwsgi_is_file(tmp_path)) { stylesheet_len = filename_len + 4; memcpy(stylesheet, tmp_path, stylesheet_len); stylesheet[stylesheet_len] = 0; free(tmp_path); goto apply; } free(tmp_path); tmp_path = uwsgi_concat2n(filename, filename_len, ".xslt", 5); if (uwsgi_is_file(tmp_path)) { stylesheet_len = filename_len + 5; memcpy(stylesheet, tmp_path, stylesheet_len); stylesheet[stylesheet_len] = 0; found = 1; } free(tmp_path); } if (found) goto apply; // finally check for static stylesheets usl = uxslt.stylesheet; while(usl) { if (uwsgi_is_file(usl->value)) { memcpy(stylesheet, usl->value, usl->len); stylesheet_len = usl->len; stylesheet[stylesheet_len] = 0; found = 1; break; } usl = usl->next; } if (found) goto apply; uwsgi_404(wsgi_req); return UWSGI_OK; apply: if (wsgi_req->query_string_len > 0) { params = uwsgi_concat2n(wsgi_req->query_string, wsgi_req->query_string_len, "", 0); } // we have both the file and the stylesheet, let's run the engine output = uwsgi_xslt_apply(filename, stylesheet, params, &output_rlen); if (params) free(params); if (!output) { uwsgi_500(wsgi_req); return UWSGI_OK; } // prepare headers if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) { uwsgi_500(wsgi_req); goto end; } // content_length if (uwsgi_response_add_content_length(wsgi_req, output_rlen)) { uwsgi_500(wsgi_req); goto end; } // content_type if (uwsgi_response_add_content_type(wsgi_req, uxslt.content_type, uxslt.content_type_len)) { uwsgi_500(wsgi_req); goto end; } uwsgi_response_write_body_do(wsgi_req, output, output_rlen); end: xmlFree(output); return UWSGI_OK; }
static int uwsgi_routing_func_memcached(struct wsgi_request *wsgi_req, struct uwsgi_route *ur){ // this is the buffer for the memcached response char buf[MEMCACHED_BUFSIZE]; size_t i; char last_char = 0; struct uwsgi_router_memcached_conf *urmc = (struct uwsgi_router_memcached_conf *) ur->data2; char **subject = (char **) (((char *)(wsgi_req))+ur->subject); uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len); struct uwsgi_buffer *ub_key = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urmc->key, urmc->key_len); if (!ub_key) return UWSGI_ROUTE_BREAK; int fd = uwsgi_connect(urmc->addr, 0, 1); if (fd < 0) { uwsgi_buffer_destroy(ub_key) ; goto end; } // wait for connection; int ret = uwsgi.wait_write_hook(fd, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]); if (ret <= 0) { uwsgi_buffer_destroy(ub_key) ; close(fd); goto end; } // build the request and send it char *cmd = uwsgi_concat3n("get ", 4, ub_key->buf, ub_key->pos, "\r\n", 2); if (uwsgi_write_true_nb(fd, cmd, 6+ub_key->pos, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT])) { uwsgi_buffer_destroy(ub_key); free(cmd); close(fd); goto end; } uwsgi_buffer_destroy(ub_key); free(cmd); // ok, start reading the response... // first we need to get a full line; size_t found = 0; size_t pos = 0; for(;;) { ssize_t len = read(fd, buf + pos, MEMCACHED_BUFSIZE - pos); if (len > 0) { pos += len; goto read; } if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait; } close(fd); goto end; wait: ret = uwsgi.wait_read_hook(fd, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]); // when we have a chunk try to read the first line if (ret > 0) { len = read(fd, buf + pos, MEMCACHED_BUFSIZE - pos); if (len > 0) { pos += len; goto read; } } close(fd); goto end; read: for(i=0;i<pos;i++) { if (last_char == '\r' && buf[i] == '\n') { found = i-1; break; } last_char = buf[i]; } if (found) break; } // ok parse the first line size_t response_size = memcached_firstline_parse(buf, found); if (response_size == 0) { close(fd); goto end; } if (urmc->type_num == 1) { if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) { close(fd); goto end; } if (uwsgi_response_add_content_type(wsgi_req, urmc->content_type, urmc->content_type_len)) { close(fd); goto end; } if (uwsgi_response_add_content_length(wsgi_req, response_size)) { close(fd); goto end; } } size_t remains = pos-(found+2); if (remains >= response_size) { uwsgi_response_write_body_do(wsgi_req, buf+found+2, response_size); close(fd); goto end; } // send what we have if (uwsgi_response_write_body_do(wsgi_req, buf+found+2, remains)) { close(fd); goto end; } // and now start reading til the output is consumed response_size -= remains; while(response_size > 0) { ssize_t len = read(fd, buf, UMIN(MEMCACHED_BUFSIZE, response_size)); if (len > 0) goto write; if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait2; } goto error; wait2: ret = uwsgi.wait_read_hook(fd, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]); if (ret > 0) { len = read(fd, buf, UMIN(MEMCACHED_BUFSIZE, response_size)); if (len > 0) goto write; } goto error; write: if (uwsgi_response_write_body_do(wsgi_req, buf, len)) { goto error; } response_size -= len; } close(fd); return UWSGI_ROUTE_BREAK; error: close(fd); end: if (ur->custom) return UWSGI_ROUTE_NEXT; return UWSGI_ROUTE_BREAK; }