| |
| |
| |
| /* This function cleans a path to its most basic form, performing the following transformations : |
| * |
| * - ./ -> [empty] |
| * - // -> / |
| * - /base/parent/../ -> /base/ |
| * |
| * If possible, it leaves the original string in place and does not copy characters, otherwise |
| * characters are copied. |
| */ |
| |
| void |
| ndk_clean_path (ngx_str_t *path, ngx_uint_t complex, size_t off) |
| { |
| u_char *s, *p, *m, *e, c, *l; |
| ngx_uint_t root; |
| |
| if (path->len == 1) { |
| |
| if (path->data[0] == '.') { |
| path->len = 0; |
| } |
| |
| return; |
| } |
| |
| // strip initial './' |
| |
| s = path->data; |
| e = s + path->len; |
| |
| if (off) { |
| p = s + off; |
| goto check_basic; |
| } |
| |
| if (*s == '/') |
| root = 1; |
| else |
| root = 0; |
| |
| while (s < e) { |
| |
| switch (*s) { |
| |
| case '/' : |
| |
| // '//' => '/' |
| |
| s++; |
| continue; |
| |
| case '.' : |
| |
| if (s == e-1) { |
| |
| if (root) { |
| path->data[0] = '/'; |
| path->len = 1; |
| } else { |
| path->len = 0; |
| } |
| |
| return; |
| } |
| |
| // './' => '' |
| |
| if (s[1] == '/') { |
| |
| s += 2; |
| |
| if (s == e) { |
| |
| path->len = 0; |
| return; |
| } |
| |
| continue; |
| } |
| } |
| |
| break; |
| } |
| |
| if (root && *s != '/') { |
| s--; |
| } |
| |
| p = s; |
| |
| check_basic : |
| |
| for ( ; p<e; p++) { |
| |
| if (*p == '/') { |
| |
| new_dir_first : |
| |
| if (e - p == 1) |
| break; |
| |
| switch (p[1]) { |
| |
| case '/' : |
| |
| // '//' => '/' |
| |
| m = p + 2; |
| goto copy; |
| |
| case '.' : |
| |
| if (e - p == 2) |
| break; |
| |
| switch (p[2]) { |
| |
| case '/' : |
| |
| // './' => '' |
| |
| m = p + 2; |
| goto copy; |
| |
| case '.' : |
| |
| if (e - p == 3 || p[3] == '/') { |
| |
| if (p == s) { |
| |
| s += 3; |
| continue; |
| } |
| |
| if (p - s >= 2) { |
| |
| if (p[-1] == '.' && p[-2] == '.') { |
| |
| if (p - s == 2 || p[-3] == '/') { // = '../../' |
| |
| p += 2; // 3? |
| continue; |
| } |
| } |
| } |
| |
| m = p + 4; |
| |
| if (complex) { |
| |
| for (p--; p >= s; p--) { |
| |
| switch (*p) { |
| |
| case '/' : |
| goto copy; |
| |
| case '$' : |
| |
| p = m - 1; |
| |
| if (m == e) |
| goto end_basic; |
| |
| goto new_dir_first; |
| } |
| } |
| |
| } else { |
| |
| for (p--; p > s; p--) { |
| |
| // '/path/folder/../' => '/path/' |
| |
| if (*p == '/') |
| break; |
| } |
| } |
| |
| goto copy; |
| } |
| } |
| } |
| } |
| } |
| |
| end_basic : |
| |
| path->data = s; |
| path->len = p - s; |
| |
| return; |
| |
| copy : |
| |
| p++; |
| |
| if (m < e) |
| goto new_dir; |
| |
| while (m < e) { |
| |
| c = *m; |
| *p = c; |
| p++; |
| |
| if (c == '/') { |
| |
| m++; |
| |
| new_dir : |
| |
| for ( ; m<e; m++) { |
| |
| c = *m; |
| if (c != '/') |
| break; |
| } |
| |
| if (m == e) |
| break; |
| |
| if (c == '.') { |
| |
| if (e - m == 1) |
| break; |
| |
| switch (m[1]) { |
| |
| case '/' : |
| |
| // './' => '' |
| |
| m += 2; |
| if (m == e) |
| break; |
| |
| goto new_dir; |
| |
| case '.' : |
| |
| if (e - m == 2 || m[2] == '/') { |
| |
| if (m - s >= 3) { // NOTE : this is one higher than above because m has moved on 1 |
| |
| if (p[-2] == '.' && p[-3] == '.') { |
| |
| if (m - s == 3 || p[-4] == '/') { // = '../../' |
| |
| p[0] = '.'; |
| p[1] = '.'; |
| p[2] = '/'; |
| p += 3; |
| m += 3; |
| goto new_dir; |
| } |
| } |
| } |
| |
| if (complex) { |
| |
| l = p; |
| |
| for (p -= 2; p >= s; p--) { |
| |
| switch (*p) { |
| |
| case '/' : |
| break; |
| |
| case '$' : |
| |
| l[0] = '.'; |
| l[1] = '.'; |
| l[2] = '/'; |
| p = l + 4; |
| break; |
| |
| default : |
| continue; |
| } |
| |
| break; |
| } |
| |
| m += 3; |
| if (m == e) |
| goto end; |
| |
| goto new_dir; |
| |
| } else { |
| |
| for (p -= 2; p > s; p--) { |
| |
| // '/path/folder/../' => '/path/' |
| |
| if (*p == '/') |
| break; |
| } |
| |
| m += 3; |
| if (m == e) |
| goto end; |
| |
| goto new_dir; |
| } |
| } |
| } |
| } |
| |
| } else { |
| m++; |
| } |
| } |
| |
| end : |
| |
| path->data = s; |
| path->len = p - s; |
| } |
| |
| |
| /* This function converts a path to its directory version, and assumes that there is always space |
| * to allocatate an extra character on the end (which is only true if the provided strings always |
| * have NULL's at the end (hence the 'safe'). |
| */ |
| |
| void |
| ndk_path_to_dir_safe (ngx_str_t *path, ngx_uint_t complex, size_t off) |
| { |
| size_t len; |
| u_char *p, *m; |
| |
| ndk_clean_path (path, complex, off); |
| |
| len = path->len; |
| if (!len) |
| return; |
| |
| p = path->data; |
| m = p + len - 1; |
| |
| if (*m != '/') { |
| |
| p [len] = '/'; |
| path->len++; |
| } |
| } |
| |
| |
| /* Divides a path given by path/to/path1:path/to/path2 into separate strings and returns an |
| * array of these strings. |
| */ |
| |
| ngx_array_t * |
| ndk_split_path_create (ngx_conf_t *cf, ngx_str_t *path) |
| { |
| ngx_str_t *str; |
| int n; |
| u_char *m, *s, *e; |
| ngx_array_t *a; |
| |
| if (path == NULL) |
| return NULL; |
| |
| n = ndk_strcntc (path, ':'); |
| |
| a = ngx_array_create (cf->pool, n + 1, sizeof (ngx_str_t)); |
| if (a == NULL) { |
| return NULL; |
| } |
| |
| s = path->data; |
| e = s + path->len; |
| |
| while (s < e) { |
| |
| m = s; |
| |
| while (m < e && *m != ':') m++; |
| |
| if (m == s) { |
| s = m+1; |
| continue; |
| } |
| |
| str = ngx_array_push (a); |
| if (str == NULL) { |
| return NULL; |
| } |
| |
| str->data = s; |
| str->len = m - s; |
| |
| if (ngx_conf_full_name (cf->cycle, str, 0) == NGX_ERROR) |
| return NULL; |
| |
| s = m+1; |
| } |
| |
| return a; |
| } |
| |
| |
| |
| ngx_array_t * |
| ndk_split_path_create_raw (ngx_conf_t *cf, char *path) |
| { |
| ngx_str_t *str; |
| int n; |
| char *m, *s; |
| ngx_array_t *a; |
| |
| if (path == NULL) |
| return NULL; |
| |
| n = ndk_strccnt (path, ':'); |
| |
| a = ngx_array_create (cf->pool, n + 1, sizeof (ngx_str_t)); |
| if (a == NULL) { |
| return NULL; |
| } |
| |
| s = path; |
| |
| |
| while (*s != '\0') { |
| |
| m = s; |
| |
| while (*m != '\0' && *m != ':') m++; |
| |
| if (m == s) { |
| s = m+1; |
| continue; |
| } |
| |
| str = ngx_array_push (a); |
| if (str == NULL) { |
| return NULL; |
| } |
| |
| str->data = (u_char *) s; |
| str->len = m - s; |
| |
| if (ngx_conf_full_name (cf->cycle, str, 0) == NGX_ERROR) |
| return NULL; |
| |
| if (*m == '\0') |
| break; |
| |
| s = m+1; |
| } |
| |
| return a; |
| } |
| |
| |
| |
| char * |
| ndk_conf_set_full_path_slot (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
| { |
| char *p = conf; |
| |
| ngx_str_t *path, *value; |
| ngx_conf_post_t *post; |
| |
| path = (ngx_str_t *) (p + cmd->offset); |
| |
| if (path->data) { |
| return "is duplicate"; |
| } |
| |
| value = cf->args->elts; |
| |
| *path = value[1]; |
| |
| if (ngx_conf_full_name (cf->cycle, path, 0) == NGX_ERROR) |
| return NGX_CONF_ERROR; |
| |
| if (cmd->post) { |
| post = cmd->post; |
| return post->post_handler(cf, post, path); |
| } |
| |
| return NGX_CONF_OK; |
| } |
| |
| |
| |
| char * |
| ndk_conf_set_split_path_slot (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
| { |
| // TODO : change to use the path func above |
| |
| char *p = conf; |
| |
| ngx_str_t *value, *str; |
| ngx_array_t **a; |
| ngx_conf_post_t *post; |
| int n; |
| u_char *m, *s, *e; |
| |
| a = (ngx_array_t **) (p + cmd->offset); |
| |
| if (*a != NGX_CONF_UNSET_PTR) { |
| return "is duplicate"; |
| } |
| |
| value = cf->args->elts; |
| value++; |
| |
| n = ndk_strcntc (value, ':') + 1; |
| |
| *a = ngx_array_create (cf->pool, n, sizeof (ngx_str_t)); |
| if (*a == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| s = value->data; |
| e = s + value->len; |
| |
| while (s < e) { |
| |
| m = s; |
| |
| while (m < e && *m != ':') m++; |
| |
| if (m == s) { |
| s = m+1; |
| continue; |
| } |
| |
| str = ngx_array_push (*a); |
| if (str == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| str->data = s; |
| str->len = m - s; |
| |
| if (ngx_conf_full_name (cf->cycle, str, 0) == NGX_ERROR) |
| return NGX_CONF_ERROR; |
| |
| s = m+1; |
| } |
| |
| |
| if (cmd->post) { |
| post = cmd->post; |
| return post->post_handler (cf, post, a); |
| } |
| |
| return NGX_CONF_OK; |
| } |
| |
| |
| |
| char * |
| ndk_conf_set_full_path (ngx_conf_t *cf, void *data, ngx_str_t *path) |
| { |
| if (ngx_conf_full_name (cf->cycle, path, 0) == NGX_ERROR) |
| return NGX_CONF_ERROR; |
| |
| return NGX_CONF_OK; |
| } |
| |
| |
| |
| char * |
| ndk_conf_set_full_conf_path (ngx_conf_t *cf, void *data, ngx_str_t *path) |
| { |
| if (ngx_conf_full_name (cf->cycle, path, 1) == NGX_ERROR) |
| return NGX_CONF_ERROR; |
| |
| return NGX_CONF_OK; |
| } |
| |
| |