diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cgi.c | 289 | ||||
| -rw-r--r-- | src/cookie.c | 2 | ||||
| -rw-r--r-- | src/cookies.c | 117 | ||||
| -rw-r--r-- | src/cookies.h | 2 | ||||
| -rw-r--r-- | src/error.c | 9 | ||||
| -rw-r--r-- | src/error.h | 3 | ||||
| -rw-r--r-- | src/fastcgi.h | 4 | ||||
| -rw-r--r-- | src/file.c | 2 | ||||
| -rw-r--r-- | src/file.h | 2 | ||||
| -rw-r--r-- | src/multipart.c | 478 | ||||
| -rw-r--r-- | src/param.c | 4 | ||||
| -rw-r--r-- | src/request.c | 40 | ||||
| -rw-r--r-- | src/request.h | 4 | ||||
| -rw-r--r-- | src/response.c | 107 | ||||
| -rw-r--r-- | src/response.h | 29 | ||||
| -rw-r--r-- | src/urlenc.c | 240 | ||||
| -rw-r--r-- | src/urlenc.h | 6 | ||||
| -rw-r--r-- | src/utils.c | 15 | ||||
| -rw-r--r-- | src/utils.h | 8 | 
19 files changed, 770 insertions, 591 deletions
| @@ -1,12 +1,14 @@  #include "cgi.h"  #include "cookie.h" +#include "cookies.h"  #include "error.h"  #include "file.h"  #include "multipart.h"  #include "param.h"  #include "request.h"  #include "urlenc.h" +#include "utils.h"  #include <ctype.h>  #include <stdio.h>  #include <stdlib.h> @@ -20,31 +22,30 @@ extern char ** environ;   */  static int plain_env(char ** dest, char * env_name)  { -    int          ok  = 1;      const char * env = getenv(env_name);      if (env) { -        *dest = str_alloc(strlen(env)); -        if (*dest) { -            strcpy(*dest, env); -        } else { -            ok = 0; +        *dest = magi_str_create(strlen(env)); +        if (!*dest) { +            return 0;          } +        strcpy(*dest, env);      } else {          *dest = 0;      } -    return ok; +    return 1;  }  static int lower_env(char ** dest, char * env_name)  { -    int ok = plain_env(dest, env_name); -    lowercase(*dest); -    return ok; +    if (plain_env(dest, env_name)) { +        magi_str_lowercase(*dest); +        return 1; +    } +    return 0;  }  static int cgi_http_env(struct magi_request * r)  { -    int     ok     = 1;      char ** env    = environ;      r->http_params = 0;      while (*env) { @@ -52,87 +53,110 @@ static int cgi_http_env(struct magi_request * r)              struct magi_param param;              /* At least one '=' must be in *env, according to format. */              char * name_end = strchr(*env, '='); -            param.name      = str_alloc(name_end - *env - 5); +            param.name      = magi_str_create(name_end - *env - 5);              if (param.name) {                  memcpy(param.name, *env + 5, name_end - *env - 5); -                param.data = str_alloc(strlen(name_end + 1)); +                param.data = magi_str_create(strlen(name_end + 1));                  if (param.data) {                      strcpy(param.data, name_end + 1);                  } else {                      free(param.name);                  }              } -            if (param.name && param.data) { -                ok = magi_param_list_add(&r->http_params, ¶m); -            } else { -                ok = 0; +            if (!param.name || !param.data || +                !magi_param_list_add(&r->http_params, ¶m)) { +                r->error = magi_error_memory; +                return 0;              }          }          ++env;      } -    return ok; +    return 1;  } -static void cgi_env(enum magi_error * e, struct magi_request * r) +static void cgi_env(struct magi_request * r)  { -    cgi_http_env(e, r); -    lower_env(e, &r->method, "REQUEST_METHOD"); -    plain_env(e, &r->uri, "REQUEST_URI"); -    plain_env(e, &r->document_root, "DOCUMENT_ROOT"); -    plain_env(e, &r->document_uri, "DOCUMENT_URI"); -    plain_env(e, &r->script_name, "SCRIPT_NAME"); -    plain_env(e, &r->script_filename, "SCRIPT_FILENAME"); -    plain_env(e, &r->remote_addr, "REMOTE_ADDR"); -    plain_env(e, &r->remote_port, "REMOTE_PORT"); -    plain_env(e, &r->server_addr, "SERVER_ADDR"); -    lower_env(e, &r->server_name, "SERVER_NAME"); -    plain_env(e, &r->server_port, "SERVER_PORT"); -    lower_env(e, &r->server_protocol, "SERVER_PROTOCOL"); -    plain_env(e, &r->server_software, "SERVER_SOFTWARE"); -    plain_env(e, &r->path_info, "PATH_INFO"); +    if (!cgi_http_env(r) || !lower_env(&r->method, "REQUEST_METHOD") || +        !plain_env(&r->uri, "REQUEST_URI") || +        !plain_env(&r->document_root, "DOCUMENT_ROOT") || +        !plain_env(&r->document_uri, "DOCUMENT_URI") || +        !plain_env(&r->script_name, "SCRIPT_NAME") || +        !plain_env(&r->script_filename, "SCRIPT_FILENAME") || +        !plain_env(&r->remote_addr, "REMOTE_ADDR") || +        !plain_env(&r->remote_port, "REMOTE_PORT") || +        !plain_env(&r->server_addr, "SERVER_ADDR") || +        !lower_env(&r->server_name, "SERVER_NAME") || +        !plain_env(&r->server_port, "SERVER_PORT") || +        !lower_env(&r->server_protocol, "SERVER_PROTOCOL") || +        !plain_env(&r->server_software, "SERVER_SOFTWARE") || +        !plain_env(&r->path_info, "PATH_INFO")) { +        r->error = magi_error_memory; +    }  } -static void cgi_cookies(enum magi_error * e, struct magi_cookie_list ** list) +static int cgi_cookies(struct magi_request * r)  { -    if (!*e) { -        const char * env = getenv("HTTP_COOKIE"); -        *list            = 0; -        if (env && *env) { -            magi_parse_cookie(e, list, env); -        } else { -            *list = 0; -        } +    const char * env = getenv("HTTP_COOKIE"); +    if (!env || !*env) { +        r->cookies = 0; +        return 1; +    } +    if (strlen(env) < r->cookies_max && r->cookies_max) { +        magi_cookies(r, env); +    } else { +        r->error = magi_error_limit;      } +    return r->error;  } -static int cgi_input_get(char ** input) +static void cgi_input_get(enum magi_error * e, char ** input, int max)  { -    int          ok        = 1;      const char * env_input = getenv("QUERY_STRING");      if (env_input) { -        *input = str_alloc(strlen(env_input)); -        if (*input) { -            strcpy(*input, env_input); -        } else { -            ok = 0; +        int len = strlen(env_input); +        if (len >= max && max) { +            *e = magi_error_limit; +            return; +        } +        *input = magi_str_create(len); +        if (!*input) { +            *e = magi_error_memory; +            return;          } +        strcpy(*input, env_input);      } -    return ok; +} + +static void cgi_url(struct magi_request * request) +{ +    char * in = 0; +    cgi_input_get(&request->error, &in, request->url_params_max); +    if (!request->error) { +        magi_urlenc(&request->url_params, request, in); +    } +    free(in);  }  static void cgi_input_post(enum magi_error * e, char ** input, int max)  {      if (!*e) {          int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10); -        if (input_len && (input_len < max || !max)) { -            *input = str_alloc(input_len); -            if (*input) { -                if (fread(*input, 1, input_len, stdin) != input_len) { -                    *e = magi_error_length; -                } -            } else { -                *e = magi_error_input; -            } +        if (!input_len) { +            *e = magi_error_length; +            return; +        } +        if (input_len >= max && max) { +            *e = magi_error_limit; +            return; +        } +        *input = magi_str_create(input_len); +        if (!*input) { +            *e = magi_error_memory; +            return; +        } +        if (fread(*input, 1, input_len, stdin) != input_len) { +            *e = magi_error_length; +            return;          }      }  } @@ -145,67 +169,132 @@ static char * bound(const char * type)          type += strspn(type, " \t") + 1;          if (*type == '"') {              ++type; -            res = create_str(type, strchr(type, '"')); +            res = magi_str_create_copy(type, strchr(type, '"'));          } else { -            res = create_str(type, type + strcspn(type, " \t")); +            res = magi_str_create_copy(type, type + strcspn(type, " \t"));          }      }      return res;  } -static int intput_getter(void * any) +static int next(void * any)  {      return getchar();  }  /* Interfacial CGI Request Handling */ -int magi_request_cgi(struct magi_request * request, -                     void (*callback)(struct magi_field * field, -                                      char *              buffer, -                                      int                 len), -                     int max_post) +int magi_request_cgi(struct magi_request * request)  { -    request->fields     = 0; -    request->error      = magi_error_none; -    enum magi_error * e = &request->error; -    cgi_env(e, request); -    cgi_cookies(e, &request->cookies); -    if (request->method) { -        if (!strcmp(request->method, "post")) { -            const char * t = getenv("CONTENT_TYPE"); -            if (t) { -                if (!strncmp(t, "multipart/form-data", 19)) { -                    char * boundary = bound(t); -                    if (boundary && *boundary) { -                        magi_parse_multipart(e, &request->fields, -                                             intput_getter, 0, boundary, -                                             callback); -                    } else { -                        *e = magi_error_nobound; -                    } -                    free(boundary); -                } else if (!strcmp(t, "application/x-www-form-urlencoded")) { -                    char * in = 0; -                    cgi_input_post(e, &in, max_post); -                    magi_parse_urlencoded(e, &request->fields, in); -                    free(in); -                } else { -                    *e = magi_error_unknown; -                } +    enum magi_error * e  = &request->error; +    request->files       = 0; +    request->params      = 0; +    request->url_params  = 0; +    request->http_params = 0; +    request->error       = magi_error_none; +    cgi_env(request); +    cgi_cookies(request); +    cgi_url(request); +    if (request->method && !strcmp(request->method, "post")) { +        const char * t = getenv("CONTENT_TYPE"); +        if (!t) { +            *e = magi_error_notype; +            return 0; +        } +        if (!strncmp(t, "multipart/form-data", 19)) { +            char * boundary = bound(t); +            if (boundary && *boundary) { +                magi_multipart(request, boundary, next, 0);              } else { -                *e = magi_error_notype; +                *e = magi_error_nobound;              } -        } else if (!strcmp(request->method, "get")) { +            free(boundary); +        } else if (!strcmp(t, "application/x-www-form-urlencoded")) {              char * in = 0; -            cgi_input_get(e, &in); -            magi_parse_urlencoded(e, &request->fields, in); +            cgi_input_post(e, &in, request->params_max); +            if (!*e) { +                magi_urlenc(&request->params, request, in); +            }              free(in); +        } else { +            *e = magi_error_unknown; +            return 0;          }      } -    return request->error == magi_error_none; +    return !request->error;  }  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * CGI Response   */ +void output_http_params(struct magi_param_list * list) +{ +    while (list) { +        fputs(list->item.name, stdout); +        fputs(": ", stdout); +        fputs(list->item.data, stdout); +        fputs("\r\n", stdout); +        list = list->next; +    } +} + +void output_cookies(struct magi_cookie_list * list) +{ +    while (list) { +        fputs("Set-Cookie: ", stdout); +        fputs(list->item.name, stdout); +        fputs("=", stdout); +        fputs(list->item.data, stdout); +        if (list->item.path) { +            fputs("; Path=", stdout); +            fputs(list->item.path, stdout); +        } +        if (list->item.domain) { +            fputs("; Domain=", stdout); +            fputs(list->item.domain, stdout); +        } +        if (list->item.port) { +            fputs("; Port=", stdout); +            fputs(list->item.port, stdout); +        } +        fputs("\r\n", stdout); +        list = list->next; +    } +} + +int magi_response_cgi(struct magi_response * response) +{ +    output_http_params(response->http_params); +    output_cookies(response->cookies); +    fputs(response->content_type, stdout); +    fputs("\r\n\r\n", stdout); +    fputs(response->content, stdout); +    return 1; +} + +int magi_error_cgi(enum magi_error error) +{ +    struct magi_response res; +    magi_response_setup(&res); +    magi_response_http(&res, "Status", "400 Bad Request"); +    magi_response_content_type(&res, magi_xhtml); +    magi_response_add_format( +        &res, +        "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' " +        "'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>" +        "<html xmlns='http://www.w3.org/1999/xhtml'>" +        "<head><title>400 Bad Request</title></head>" +        "<body>" +        "<h1>400 <i>Bad Request</i></h1>" +        "<h2>%s</h2>" +        "</body>" +        "</html>", +        magi_error_message(error)); +    if (magi_response_cgi(&res)) { +        magi_response_destroy(&res); +        return 1; +    } else { +        magi_response_destroy(&res); +        return 0; +    } +} diff --git a/src/cookie.c b/src/cookie.c index 506e4ef..eaa221f 100644 --- a/src/cookie.c +++ b/src/cookie.c @@ -13,7 +13,7 @@ int magi_cookie_list_add(struct magi_cookie_list ** list,          node->item = *item;          *list      = node;      } -    return node; +    return !!node;  }  struct magi_cookie * magi_cookie_list_get(struct magi_cookie_list * list, diff --git a/src/cookies.c b/src/cookies.c index f690ccc..4cbe659 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -1,5 +1,8 @@ +/* * *   TODO   * * */  #include "cookies.h" +#include "utils.h" +#include <stdlib.h>  #include <string.h> @@ -27,6 +30,7 @@ struct automata {      enum data_type             data_t;  }; +  static void nulify_cookie(struct automata * a)  {      a->cookie.name   = 0; @@ -43,23 +47,6 @@ static void buf_new(struct automata * a)      a->buf_size = 1;  } -static int buf_add(struct automata * a, char c) -{ -    int ok = 1; -    if (a->buf_len == a->buf_size - 1) { -        a->buf_size *= 2; -        a->buf = realloc(a->buf, a->buf_size); -    } -    if (a->buf) { -        a->buf_len++; -        a->buf[a->buf_len - 1] = c; -        a->buf[a->buf_len]     = 0; -    } else { -        ok = 0; -        magi_error_set("[cookie] Cannot allocate automata buffer."); -    } -    return ok; -}  static enum data_type what_is_name(const struct automata * a)  { @@ -78,6 +65,7 @@ static enum data_type what_is_name(const struct automata * a)      return data_t;  } +  static int end_name(struct automata * a)  {      int ok    = 1; @@ -114,14 +102,13 @@ static int end_data(struct automata * a)      case dt_version:          if (strcmp(a->buf, "1")) {              ok = 0; -            magi_error_set("[cookie] Version must be '1', readed: %s.", -                           a->buf);          }      }      buf_new(a);      return ok;  } +  static enum st parse_pre_name(struct automata * a, char c)  {      enum st state; @@ -129,12 +116,11 @@ static enum st parse_pre_name(struct automata * a, char c)          state = st_name;      } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) {          state = st_name; -        if (!buf_add(a, c)) { +        if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) {              state = st_error;          }      } else {          state = st_error; -        magi_error_set("[cookie] Pre-name, readed: \\%o (render: %c).", c, c);      }      return state;  } @@ -154,13 +140,11 @@ static enum st parse_name(struct automata * a, char c)          }      } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) {          state = st_name; -        if (!buf_add(a, c)) { +        if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) {              state = st_error;          }      } else {          state = st_error; -        magi_error_set("[cookie] Reading name, readed: \\%o (render: %c).", c, -                       c);      }      return state;  } @@ -174,9 +158,6 @@ static enum st parse_post_name(struct automata * a, char c)          state = st_post_name;      } else {          state = st_error; -        magi_error_set("[cookie] Waiting for name-value separator, " -                       "readed: \\%o (render: %c).", -                       c, c);      }      return state;  } @@ -191,12 +172,11 @@ static enum st parse_pre_data(struct automata * a, char c)          a->is_quoted = 1;      } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) {          state = st_data; -        if (!buf_add(a, c)) { +        if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) {              state = st_error;          }      } else {          state = st_error; -        magi_error_set("[cookie] Pre-value, readed: \\%o (render: %c).", c, c);      }      return state;  } @@ -217,14 +197,11 @@ static enum st parse_not_quoted_data(struct automata * a, char c)          }      } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) {          state = st_data; -        if (!buf_add(a, c)) { +        if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) {              state = st_error;          }      } else {          state = st_error; -        magi_error_set("[cookie] Reading not-quoted value, " -                       "readed: \\%o (render: %c).", -                       c, c);      }      return state;  } @@ -257,73 +234,63 @@ static enum st parse_post_data(struct automata * a, char c)          state = st_post_data;      } else {          state = st_error; -        magi_error_set( -            "[cookie] Waiting for separator between name-value pairs, " -            "readed: \\%o (render: %c).", -            c, c);      }      return state;  } -static int parse_end(struct automata * a, enum st s) + +static void parse_end(enum magi_error * e, struct automata * a, enum st s)  { -    int ok = 0; -    if (s == st_post_data) { -        ok = 1; -    } else if (s == st_data) { -        if (!a->is_quoted) { -            if (a->cookie.name) { -                if (end_data(a) && magi_cookie_list_add(a->list, &a->cookie)) { -                    ok = 1; -                    nulify_cookie(a); -                    buf_new(a); -                } -            } else { -                magi_error_set("[cookie] No cookies set."); -            } +    if (s == st_data) { +        if (a->is_quoted || !a->cookie.name) { +            *e = magi_error_cookies; +            return; +        } +        if (end_data(a) && magi_cookie_list_add(a->list, &a->cookie)) { +            nulify_cookie(a);          } else { -            magi_error_set("[cookie] In quotation when reached input end."); +            *e = magi_error_cookies;          } -    } else if (s != st_error) { -        magi_error_set("[cookie] Input ended in not correct state."); +    } else if (s != st_post_data) { +        *e = magi_error_cookies;      } -    free(a->cookie.name); -    free(a->cookie.data); -    free(a->cookie.path); -    free(a->cookie.domain); -    free(a->cookie.port); -    free(a->buf); -    return ok;  } -int magi_parse_cookie(struct magi_cookie_list ** list, const char * input) + +void magi_cookies(struct magi_request * request, const char * data)  { -    struct automata a     = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 1, 1, 0, 0, 0 }; -    enum st         state = st_pre_name; -    a.list                = list; -    while (*input && state) { -        char c = *input++; +    enum st         state; +    struct automata a = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 1, 1, 0, 0, 0 }; +    a.list            = &request->cookies; +    request->cookies  = 0; +    for (state = st_pre_name; state && *data; ++data) {          switch (state) {          case st_pre_name: -            state = parse_pre_name(&a, c); +            state = parse_pre_name(&a, *data);              break;          case st_name: -            state = parse_name(&a, c); +            state = parse_name(&a, *data);              break;          case st_post_name: -            state = parse_post_name(&a, c); +            state = parse_post_name(&a, *data);              break;          case st_pre_data: -            state = parse_pre_data(&a, c); +            state = parse_pre_data(&a, *data);              break;          case st_data: -            state = parse_data(&a, c); +            state = parse_data(&a, *data);              break;          case st_post_data: -            state = parse_post_data(&a, c); +            state = parse_post_data(&a, *data);          default:              break;          }      } -    return parse_end(&a, state); +    parse_end(&request->error, &a, state); +    free(a.cookie.name); +    free(a.cookie.data); +    free(a.cookie.path); +    free(a.cookie.domain); +    free(a.cookie.port); +    free(a.buf);  } diff --git a/src/cookies.h b/src/cookies.h index 3da16e7..98b2ada 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -1,7 +1,7 @@  #ifndef MAGI_INCLUDED_COOKIES  #define MAGI_INCLUDED_COOKIES -#include "cookie.h" +#include "request.h"  void magi_cookies(struct magi_request * request, const char * data); diff --git a/src/error.c b/src/error.c index 48c3c36..efe6dbf 100644 --- a/src/error.c +++ b/src/error.c @@ -3,16 +3,17 @@  const char * magi_error_message(enum magi_error error)  { -    static const char * const messages[] = { +    const char * const messages[] = {          0,                             /* magi_error_none */          "No boundary for multipart.",  /* magi_error_nobound */ -        "Content-type is unknown.",    /* magi_error_unknown */ -        "Content-type is not given.",  /* magi_error_notype */ +        "Content-Type is unknown.",    /* magi_error_unknown */ +        "Content-Type is not given.",  /* magi_error_notype */          "Mismatch of content length.", /* magi_error_length */          "Cannot allocate memory.",     /* magi_error_memory */          "Cannot read cookies.",        /* magi_error_cookies */          "Cannot decode URL.",          /* magi_error_urlenc */ -        "Cannot read multipart body."  /* magi_error_multipart */ +        "Cannot read multipart body.", /* magi_error_multipart */ +        "Part of request was too big." /* magi_error_limit */      };      return messages[error];  } diff --git a/src/error.h b/src/error.h index 46a07cd..f8f0aed 100644 --- a/src/error.h +++ b/src/error.h @@ -11,7 +11,8 @@ enum magi_error {      magi_error_memory,      magi_error_cookies,      magi_error_urlenc, -    magi_error_multipart +    magi_error_multipart, +    magi_error_limit  };  const char * magi_error_message(enum magi_error error); diff --git a/src/fastcgi.h b/src/fastcgi.h index d701fe0..0abd152 100644 --- a/src/fastcgi.h +++ b/src/fastcgi.h @@ -2,7 +2,9 @@  #define MAGI_INCLUDED_FASTCGI -/* TODO */ +/* * *   TODO   * * */ + +enum { magi_to_avoid_warning };  #endif @@ -12,7 +12,7 @@ int magi_file_list_add(struct magi_file_list ** list, struct magi_file * item)          node->item = *item;          *list      = node;      } -    return node; +    return !!node;  }  struct magi_file * magi_file_list_get(struct magi_file_list * list, @@ -1,6 +1,8 @@  #ifndef MAGI_INCLUDED_FILE  #define MAGI_INCLUDED_FILE +#include "param.h" +  struct magi_file {      /* All pointers must be valid as 'free' arguments. */ diff --git a/src/multipart.c b/src/multipart.c index c3e4a2d..5d7f261 100644 --- a/src/multipart.c +++ b/src/multipart.c @@ -3,6 +3,7 @@  #include "error.h"  #include "param.h" +#include "utils.h"  #include <ctype.h>  #include <stdio.h>  #include <stdlib.h> @@ -43,114 +44,128 @@ enum st {  };  struct automata { -    struct magi_field_list ** list; -    struct magi_field         field; -    struct magi_param         param; -    char *                    buf; -    int                       buf_size; -    int                       size; -    int                       len; -    char *                    boundary; -    int                       boundary_pos; -    int                       boundary_len; -    int                       is_end_suspected; -    int                       is_CR_readed; -    int                       is_quoted; -    void (*callback)(struct magi_field * field, char * buffer, int size); +    struct magi_request * request; +    struct magi_file      file; +    struct magi_param     param; +    struct magi_param     subparam; +    char *                buf; +    int                   buf_size; +    int                   size; +    int                   len; +    char *                boundary; +    int                   boundary_pos; +    int                   boundary_len; +    int                   is_end_suspected; +    int                   is_CR_readed; +    int                   is_quoted;  };  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * Automata Shortcuts   */ -static int content_disposition(struct automata * a) +static char * extract_filename(char * n)  { -    int    ok   = 1; -    char * name = strchr(a->param.data, '='); -    if (name) { -        name += strspn(name, " \t") + 1; -        if (*name == '"') { -            ++name; -            a->field.name = create_str(name, strchr(name, '"')); -            if (a->field.name == 0) { -                ok = 0; -            } else if (a->field.name[0] == 0) { -                ok = 0; -                magi_error_set( -                    "[multipart] Wrong content-disposition quotation."); -            } -        } else { -            a->field.name = create_str(name, name + strcspn(name, " \t")); -            if (a->field.name == 0) { -                ok = 0; -            } else if (!is_str_token(a->field.name)) { -                ok = 0; -                magi_error_set( -                    "[multipart] Content-disposition value is not valid, " -                    "readed: %s.", -                    a->field.name); -            } -        } -        if (ok) { -            free(a->param.name); -            free(a->param.data); -            a->param.name = 0; -            a->param.data = 0; -        } +    n = strchr(n, '='); +    if (!n) { +        return 0; +    } +    n += strspn(n, " \t") + 1; +    if (*n == '"') { +        ++n; +        return magi_str_create_copy(n, strchr(n, '"'));      } else { -        ok = 0; -        magi_error_set("[multipart] Content-disposition has no '=' symbol."); +        return magi_str_create_copy(n, n + strcspn(n, " \t"));      } -    return ok;  } -static int param_end(struct automata * a) +static int content_disposition(struct automata * a)  { -    int ok = 1; -    lowercase(a->param.name); -    if (!strcmp(a->param.name, "content-disposition")) { -        ok = content_disposition(a); +    char * n = strchr(a->subparam.data, '='); +    if (!n) { +        return 0; +    } +    n += strspn(n, " \t") + 1; +    if (*n == '"') { +        ++n; +        a->param.name = magi_str_create_copy(n, strchr(n, '"')); +        if (!a->param.name || !*a->param.name) { +            return 0; +        }      } else { -        ok            = magi_param_list_add(&a->field.params, &a->param); -        a->param.name = 0; -        a->param.data = 0; +        a->param.name = magi_str_create_copy(n, n + strcspn(n, " \t")); +        if (!a->param.name || !is_str_token(a->param.name)) { +            return 0; +        } +    } +    a->file.file_name = extract_filename(n); +    if (a->file.file_name) { +        a->file.param_name = a->param.name; +        a->file.params     = 0;      } +    free(a->subparam.name); +    free(a->subparam.data); +    a->subparam.name = 0; +    a->subparam.data = 0; +    return 1; +} + +static int subparam_end(struct automata * a) +{      a->size = 1;      a->len  = 0; -    return ok; +    magi_str_lowercase(a->subparam.name); +    if (!strcmp(a->subparam.name, "content-disposition")) { +        return content_disposition(a); +    } else if (magi_param_list_add(&a->file.params, &a->subparam)) { +        a->subparam.name = 0; +        a->subparam.data = 0; +        return 1; +    } +    return 0;  } -static int field_end(struct automata * a) +static int param_end(struct automata * a)  { -    int ok; -    if (a->field.name == 0) { -        ok = 0; -        magi_error_set("[multipart] Field name is empty or not specified."); -    } else { -        if (a->callback) { -            a->callback(&a->field, a->buf, a->buf_size); -            a->buf_size = 0; -        } -        a->field.len = a->len; -        ok           = magi_field_list_add(a->list, &a->field); -        if (!ok) { -            free(a->field.name); -            free(a->field.data); -            magi_param_list_destroy(a->field.params); -            free(a->field.params); +    if (!a->param.name) { +        a->request->error = magi_error_multipart; +        return 0; +    } +    if (a->file.file_name) { +        a->request->file_callback(&a->file, a->buf, a->buf_size, 1, +                                  a->request->file_callback_userdata); +        a->buf_size = 0; +        if (!magi_file_list_add(&a->request->files, &a->file)) { +            free(a->file.file_name); +            free(a->file.param_name); +            magi_param_list_destroy(a->file.params); +            free(a->file.params); +            a->request->error = magi_error_multipart; +            return 0;          } -        a->field.name   = 0; -        a->field.data   = 0; -        a->field.params = 0; -        a->size         = 1; -        a->len          = 0; +        a->file.file_name  = 0; +        a->file.param_name = 0; +        a->file.params     = 0; +        a->size            = 1; +        a->len             = 0; +        return 1; +    } +    if (!magi_param_list_add(&a->request->params, &a->param)) { +        free(a->param.name); +        free(a->param.data); +        a->request->error = magi_error_multipart; +        return 0;      } -    return ok; +    a->param.name = 0; +    a->param.data = 0; +    a->size       = 1; +    a->len        = 0; +    return 1;  }  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Boundary interfaces + * Boundary Interfaces   */  static char sepget(const struct automata * a)  { @@ -215,213 +230,168 @@ static int is_semiend(const struct automata * a)  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Data Reading + */ +static void apply_callback(struct automata * a) +{ +    int full = a->buf_size == a->request->file_callback_addon_max; +    if (a->file.file_name && full) { +        a->request->file_callback(&a->file, a->buf, a->buf_size, 0, +                                  a->request->file_callback_userdata); +        a->buf_size = 0; +    } +} + +static enum st data_add_act( +    struct automata * a, char c, char ** dest, int * len, int * size) +{ +    int pos = a->boundary_pos; +    for (a->boundary_pos = 0; a->boundary_pos < pos; ++a->boundary_pos) { +        if (a->is_end_suspected) { +            magi_str_add(dest, len, size, endget(a)); +        } else { +            magi_str_add(dest, len, size, sepget(a)); +        } +        apply_callback(a); +    } +    a->boundary_pos     = 0; +    a->is_end_suspected = 0; + +    if (sepget(a) != c) { +        magi_str_add(dest, len, size, c); +        apply_callback(a); +        return st_data; +    } else { +        a->boundary_pos++; +        if (a->boundary_pos == seplen(a)) { +            param_end(a); +            return st_pname_pre; +        } +        return st_data; +    } +} + +static enum st data_add(struct automata * a, char c) +{ +    if (a->file.file_name) { +        int max = a->request->file_callback_addon_max + 1; +        return data_add_act(a, c, &a->buf, &a->buf_size, &max); +    } else { +        return data_add_act(a, c, &a->param.data, &a->len, &a->size); +    } +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * State analysers   */  static enum st parse_begin(struct automata * a, char c)  { -    enum st state; -    if (sepget(a) == c) { +    if (sepget(a) != c) {    /* 'c' is not wanted character from separator; */ +        a->boundary_pos = 0; /* so nullify progress in reading separator. */ +    } else {          a->boundary_pos++;          if (a->boundary_pos == seplen(a)) { -            state = st_pname_pre; -        } else { -            state = st_begin; +            return st_pname_pre; /* Separator is completed, so move on. */          } -    } else { -        state           = st_begin; -        a->boundary_pos = 0;      } -    return state; +    return st_begin;  }  static enum st parse_pname_pre(struct automata * a, char c)  { -    enum st state;      if (a->is_CR_readed) { -        a->is_CR_readed = 0; -        if (c == '\n') { -            state           = st_data; -            a->boundary_pos = 0; -        } else { -            state = st_error; -            magi_error_set( -                "[multipart] Waiting for name, CR is readed alone."); +        if (c != '\n') { +            return st_error;          } +        a->is_CR_readed = 0; +        a->boundary_pos = 0; +        return st_data;      } else if (c == '\r') { -        state           = st_pname_pre;          a->is_CR_readed = 1; -    } else if (is_token(c)) { -        if (add(&a->param.name, &a->len, &a->size, c)) { -            state = st_pname; -        } else { -            state = st_error; -        } -    } else { -        state = st_error; -        magi_error_set( -            "[multipart] Waiting for name, readed: \\%o (render: %c).", c, c); +        return st_pname_pre; +    } else if (is_token(c) && +               magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { +        return st_pname;      } -    return state; +    return st_error;  }  static enum st parse_pname(struct automata * a, char c)  { -    enum st state;      if (c == ':') { -        state   = st_pdata;          a->len  = 0;          a->size = 1; +        return st_pdata;      } else if (c == ' ' || c == '\t') { -        state = st_pname_end; -    } else if (is_token(c)) { -        if (add(&a->param.name, &a->len, &a->size, c)) { -            state = st_pname; -        } else { -            state = st_error; -        } -    } else { -        state = st_error; -        magi_error_set("[multipart] Reading name, readed: \\%o (render: %c).", -                       c, c); +        return st_pname_end; +    } else if (is_token(c) && +               magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { +        return st_pname;      } -    return state; +    return st_error;  }  static enum st parse_pname_end(struct automata * a, char c)  { -    enum st state;      if (c == ':') { -        state   = st_pdata;          a->len  = 0;          a->size = 1; +        return st_pdata;      } else if (c == ' ' || c == '\t') { -        state = st_pname_end; -    } else { -        state = st_error; -        magi_error_set("[multipart] Waiting for name-value separator, " -                       "readed: \\%o (render: %c).", -                       c, c); +        return st_pname_end;      } -    return state; +    return st_error;  }  static enum st parse_pdata(struct automata * a, char c)  { -    enum st state;      if (a->is_CR_readed) {          a->is_CR_readed = 0;          if (c == '\n') { -            if (param_end(a)) { -                state = st_pname_pre; -            } else { -                state = st_error; +            if (subparam_end(a)) { +                return st_pname_pre;              } -        } else if (add(&a->param.data, &a->len, &a->size, '\r')) { -            if (add(&a->param.data, &a->len, &a->size, c)) { -                state = st_pdata; -            } else { -                state = st_error; -            } -        } else { -            state = st_error; +            return st_error; +        } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, '\r') && +                   magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { +            return st_pdata;          } +        return st_error;      } else if (c == '\r') { -        state           = st_pdata;          a->is_CR_readed = 1; -    } else { -        if (add(&a->param.data, &a->len, &a->size, c)) { -            state = st_pdata; -        } else { -            state = st_error; -        } +        return st_pdata; +    } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { +        return st_pdata;      } -    return state; -} - -static void apply_callback(struct automata * a) -{ -    if (a->callback && a->buf_size == magi_parse_multipart_callback_size) { -        a->callback(&a->field, a->buf, a->buf_size); -        a->buf_size = 0; -    } -} - -static enum st data_add(struct automata * a, char c) -{ -    static int max_buf_size = magi_parse_multipart_callback_size + 1; -    enum st    state; -    char **    dest; -    int *      len; -    int *      size; -    int        pos  = a->boundary_pos; -    state           = st_data; -    a->boundary_pos = 0; -    if (a->callback) { -        dest = &a->buf; -        len  = &a->buf_size; -        size = &max_buf_size; -    } else { -        dest = &a->field.data; -        len  = &a->len; -        size = &a->size; -    } -    while (a->boundary_pos < pos) { -        if (a->is_end_suspected) { -            add(dest, len, size, endget(a)); -        } else { -            add(dest, len, size, sepget(a)); -        } -        apply_callback(a); -        a->boundary_pos++; -    } -    a->boundary_pos     = 0; -    a->is_end_suspected = 0; -    if (sepget(a) == c) { -        a->boundary_pos++; -        if (a->boundary_pos == seplen(a)) { -            state = st_pname_pre; -            field_end(a); -        } else { -            state = st_data; -        } -    } else { -        add(dest, len, size, c); -        apply_callback(a); -    } -    return state; +    return st_error;  }  static enum st parse_data(struct automata * a, char c)  { -    enum st state;      if (a->is_end_suspected) { -        if (endget(a) == c) { -            a->boundary_pos++; -            if (a->boundary_pos == endlen(a)) { -                state = st_end; -                field_end(a); -            } else { -                state = st_data; -            } -        } else { -            state = data_add(a, c); +        if (endget(a) != c) { +            return data_add(a, c); +        } +        a->boundary_pos++; +        if (a->boundary_pos == endlen(a)) { +            param_end(a); +            return st_end;          } +        return st_data;      } else if (sepget(a) == c) {          a->boundary_pos++;          if (a->boundary_pos == seplen(a)) { -            state = st_pname_pre; -            field_end(a); -        } else { -            state = st_data; +            param_end(a); +            return st_pname_pre;          } +        return st_data;      } else if ((a->boundary_pos == seplen(a) - 2) && endget(a) == c) { -        state               = st_data;          a->is_end_suspected = 1;          a->boundary_pos++; -    } else { -        state = data_add(a, c); +        return st_data;      } -    return state; +    return data_add(a, c);  }  static enum st parse_end(struct automata * a, char c) @@ -433,14 +403,13 @@ static enum st parse_end(struct automata * a, char c)  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * Automata Runner   */ -static int run_automata(struct automata * a, -                        int (*next)(void * thing), -                        void * thing) +static void run_automata(struct automata * a, +                         int (*next)(void * next_userdata), +                         void * next_userdata)  { -    int     ok    = 1;      enum st state = st_begin; -    int     c; -    for (c = next(thing); state && c != EOF; c = next(thing)) { +    int     c     = next(next_userdata); +    while (state && state != st_end && c != EOF) {          switch (state) {          case st_begin:              state = parse_begin(a, c); @@ -465,43 +434,36 @@ static int run_automata(struct automata * a,          default:              break;          } +        c = next(next_userdata);      }      if (state == st_data && is_semiend(a)) {          state = st_end; -        field_end(a); +        param_end(a);      }      if (state != st_end) { -        ok = 0; -        if (state != st_error) { -            magi_error_set("[multipart] Input ended unexpectedly."); -        } -        free(a->field.name); -        free(a->field.data); +        a->request->error = magi_error_multipart; +        free(a->subparam.name); +        free(a->subparam.data);      } -    return ok;  }  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * Automata Interfaces   */ -int magi_parse_multipart(struct magi_request * request, -                         char *                boundary, -                         int (*get_next)(void * userdata), -                         void * get_next_userdata) +void magi_multipart(struct magi_request * request, +                    char *                boundary, +                    int (*next)(void * userdata), +                    void * next_userdata)  { -    struct automata a = { -        0, { 0, 0, 0 }, { 0, 0 }, 0, 0, 1, 0, 0, 2, 0, 0, 0 -    }; -    int ok         = 0; -    a.list         = list; -    a.boundary     = boundary; -    a.boundary_len = strlen(boundary); -    a.callback     = callback; -    a.buf          = malloc(magi_parse_multipart_callback_size + 1); +    struct automata a = { 0, { 0, 0, 0 }, { 0, 0 }, { 0, 0 }, 0, 0, 1, +                          0, 0,           2,        0,        0, 0 }; +    a.request         = request; +    a.boundary        = boundary; +    a.boundary_len    = strlen(boundary); +    a.buf             = malloc(request->file_callback_addon_max + 1);      if (a.buf) { -        ok = run_automata(&a, get_next, get_next_arg); +        run_automata(&a, next, next_userdata);          free(a.buf);      } -    return ok;  } diff --git a/src/param.c b/src/param.c index 73005af..e9f43f8 100644 --- a/src/param.c +++ b/src/param.c @@ -13,7 +13,7 @@ int magi_param_list_add(struct magi_param_list ** list,          node->item = *item;          *list      = node;      } -    return node; +    return !!node;  }  char * magi_param_list_get(struct magi_param_list * list, const char * name) @@ -21,7 +21,7 @@ char * magi_param_list_get(struct magi_param_list * list, const char * name)      if (!list || !name) {          return 0;      } else if (!strcmp(list->item.name, name)) { -        return &list->item.data; +        return list->item.data;      } else {          return magi_param_list_get(list->next, name);      } diff --git a/src/request.c b/src/request.c index 4a1464a..99f3a2a 100644 --- a/src/request.c +++ b/src/request.c @@ -21,13 +21,39 @@ void magi_request_setup(struct magi_request * request)      }  } -static void tempfiles(struct magi_file *      file, -                      char *                  addon, -                      int                     addon_len, -                      int                     is_addon_last, -                      struct magi_tempfiles * table) +void magi_tempfiles_add(struct magi_tempfiles * tmps, +                        const char *            name, +                        const char *            path, +                        int                     max)  { -    int pos; +    if (!tmps) { +        return; +    } +    if (tmps->count++) { +        tmps->param_names = realloc(tmps->param_names, +                                    sizeof(*tmps->param_names) * tmps->count); +        tmps->locations = +            realloc(tmps->locations, sizeof(*tmps->locations) * tmps->count); +        tmps->maximums = +            realloc(tmps->maximums, sizeof(*tmps->maximums) * tmps->count); +    } else { +        tmps->param_names = malloc(sizeof(*tmps->param_names)); +        tmps->locations   = malloc(sizeof(*tmps->locations)); +        tmps->maximums    = malloc(sizeof(*tmps->maximums)); +    } +    tmps->param_names[tmps->count] = name; +    tmps->locations[tmps->count]   = path; +    tmps->maximums[tmps->count]    = max; +} + +static void tempfiles(struct magi_file * file, +                      char *             addon, +                      int                addon_len, +                      int                is_addon_last, +                      void *             userdata) +{ +    struct magi_tempfiles * table = userdata; +    int                     pos;      for (pos = 0; pos != table->count; ++pos) {          if (!strcmp(table->param_names[pos], file->param_name)) {              static FILE * f = 0; @@ -81,7 +107,7 @@ static void request_free(struct magi_request * request)      free(request->path_info);  } -void magi_request_annul(struct magi_request * request) +static void request_annul(struct magi_request * request)  {      request->cookies         = 0;      request->http_params     = 0; diff --git a/src/request.h b/src/request.h index 5bb2070..be92370 100644 --- a/src/request.h +++ b/src/request.h @@ -88,6 +88,10 @@ struct magi_tempfiles {      const char ** locations;      int *         maximums; /* Null maximums[i] <=> unlimited tempfiles[i]. */  }; +void magi_tempfiles_add(struct magi_tempfiles * tmps, +                        const char *            name, +                        const char *            path, +                        int                     max);  /* Setup request callback with files loaded into corresponding to their   * parameter names locations; paths are in magi_tempfiles struct. */  void magi_request_setup_tempfiles(struct magi_request *   request, diff --git a/src/response.c b/src/response.c index e69de29..7e2ec0b 100644 --- a/src/response.c +++ b/src/response.c @@ -0,0 +1,107 @@ +#include "response.h" + +#include "utils.h" +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int vsnprintf(char * s, size_t n, const char * format, va_list arg); + + +void magi_response_setup(struct magi_response * response) +{ +    response->cookies      = 0; +    response->http_params  = 0; +    response->content_type = 0; +    response->content      = magi_str_create(0); +    response->len          = 0; +    response->size         = 1; +} + + +void magi_response_content_type(struct magi_response * response, +                                enum magi_content_type type) +{ +    const char * const messages[] = { +        "Content-Type: application/xhtml+xml", /* magi_xhtml */ +    }; +    if (!response->content_type) { +        const char * end       = messages[type] + strlen(messages[type]); +        response->content_type = magi_str_create_copy(messages[type], end); +    } +} + +void magi_response_add(struct magi_response * r, const char * addon) +{ +    int addon_len; +    if (!addon) { +        return; +    } +    addon_len = strlen(addon); +    if (r->len + addon_len + 1 >= r->size) { +        r->content = realloc(r->content, r->len + addon_len + 1); +    } +    memcpy(r->content + r->len, addon, addon_len + 1); +    r->len += addon_len; +} + +void magi_response_add_format(struct magi_response * response, +                              const char *           addon, +                              ...) +{ +    char *  act_addon; +    int     n; +    va_list args; +    va_start(args, addon); +    n = vsnprintf(0, 0, addon, args); +    va_end(args); +    if (n >= 0) { +        act_addon = malloc(n + 1); +        va_start(args, addon); +        vsnprintf(act_addon, n + 1, addon, args); +        va_end(args); +        magi_response_add(response, act_addon); +        free(act_addon); +    } +} + +void magi_response_cookie(struct magi_response * response, +                          struct magi_cookie *   cookie) +{ +    magi_cookie_list_add(&response->cookies, cookie); +} + +void magi_response_cookie_easy(struct magi_response * response, +                               const char *           name, +                               const char *           value) +{ +    struct magi_cookie cookie = { 0, 0, 0, 0, 0 }; +    cookie.name = magi_str_create_copy(name, name + strlen(name)); +    cookie.data = magi_str_create_copy(value, value + strlen(value)); +    magi_cookie_list_add(&response->cookies, &cookie); +} + +void magi_response_http(struct magi_response * response, +                        const char *           name, +                        const char *           data) +{ +    struct magi_param param = { 0, 0 }; +    param.name              = magi_str_create_copy(name, name + strlen(name)); +    param.data              = magi_str_create_copy(data, data + strlen(data)); +    magi_param_list_add(&response->http_params, ¶m); +} + + +void magi_response_destroy(struct magi_response * response) +{ +    if (!response) { +        return; +    } +    magi_cookie_list_destroy(response->cookies); +    magi_param_list_destroy(response->http_params); +    free(response->cookies); +    free(response->http_params); +    free(response->content_type); +    free(response->content); +} diff --git a/src/response.h b/src/response.h index 346ce3d..95f6537 100644 --- a/src/response.h +++ b/src/response.h @@ -1,36 +1,43 @@  #ifndef MAGI_INCLUDED_RESPONSE  #define MAGI_INCLUDED_RESPONSE -#include "error.h" +#include "cookie.h" +#include "param.h" -enum magi_content_type { magi_xhtml }; +enum magi_content_type { magi_xhtml = 0 };  struct magi_response {      struct magi_cookie_list * cookies;      struct magi_param_list *  http_params; -    struct magi_param_list *  url_params;      char *                    content_type;      char *                    content; +    int                       len; +    int                       size;  }; + +void magi_response_setup(struct magi_response * response); + +  void magi_response_content_type(struct magi_response * response,                                  enum magi_content_type type); -void magi_reponse_add(struct magi_response * response, const char * addon); -void magi_reponse_add_format(struct magi_response * response, -                             const char *           addon, -                             ...); +void magi_response_add(struct magi_response * response, const char * addon); +void magi_response_add_format(struct magi_response * response, +                              const char *           addon, +                              ...); -void magi_response_cookie(struct magi_response *     response, -                          const struct magi_cookie * cookie); +void magi_response_cookie(struct magi_response * response, +                          struct magi_cookie *   cookie);  void magi_response_cookie_easy(struct magi_response * response,                                 const char *           name,                                 const char *           value); -void magi_response_cookie_delete(struct magi_response * response, -                                 const char *           name); +void magi_response_http(struct magi_response * response, +                        const char *           name, +                        const char *           data);  void magi_response_destroy(struct magi_response * response); diff --git a/src/urlenc.c b/src/urlenc.c index 336dde0..8c38a0a 100644 --- a/src/urlenc.c +++ b/src/urlenc.c @@ -1,7 +1,6 @@ -#include "urlencoded.h" +#include "urlenc.h" -#include "error.h" -#include "field.h" +#include "utils.h"  #include <ctype.h>  #include <stdlib.h>  #include <string.h> @@ -10,16 +9,21 @@  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * Local Shortcuts   */ -/* Shouldn't be called with 'c' as not hex digit. */ -static char from_hex(char c) +/* Call only if is_hex(c). */ +static int from_hex(char c)  { -    char num;      if (isdigit(c)) { -        num = c - '0'; +        return c - '0';      } else { -        num = toupper(c) - 'A' + 10; +        return toupper(c) - 'A' + 10;      } -    return num; +} + +/* Call only if 0 <= num && num <= 15. */ +static char to_hex(int num) +{ +    static const char * const hex = "0123456789ABCDEF"; +    return hex[num];  }  static int is_hex(char c) @@ -27,43 +31,43 @@ static int is_hex(char c)      return isdigit(c) || strchr("ABCDEF", toupper(c));  } +static int is_url(char c) +{ +    return isalnum(c) || strchr("$-_.+ !*'(),", c); +} +  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   * URL Decoding   */  static int deurl(char ** data)  { -    int ok = 1; -    if (*data) { -        char * val = *data; -        int    ti  = 0; -        int    ci; -        for (ci = 0; ok && val[ci]; ++ti, ++ci) { -            if (val[ci] == '%') { -                if (is_hex(val[ci + 1]) && is_hex(val[ci + 2])) { -                    /* Since chars can be signed, arithmetics are not safe. */ -                    val[ti] = from_hex(val[ci + 2]);       /* 00xx */ -                    val[ti] |= from_hex(val[ci + 1]) << 4; /* XXxx */ -                    ci += 2; -                } else { -                    ok = 0; -                    magi_error_set( -                        "[urlencoded] Waiting for two hex digits after '%%', " -                        "readed: \\%o\\%o (render: %c%c)", -                        val[ci + 1], val[ci + 2], val[ci + 1], val[ci + 2]); -                } -            } else if (val[ci] == '+') { -                val[ti] = ' '; +    char * val = *data; +    int    ti; +    int    ci; +    if (!val) { +        *data  = malloc(1); +        **data = 0; +        return 1; +    } +    for (ti = 0, ci = 0; val[ci]; ++ti, ++ci) { +        if (val[ci] == '%') { +            if (is_hex(val[ci + 1]) && is_hex(val[ci + 2])) { +                /* Since chars can be signed, arithmetics are not safe. */ +                val[ti] = from_hex(val[ci + 2]);       /* 0000xxxx */ +                val[ti] |= from_hex(val[ci + 1]) << 4; /* XXXXxxxx */ +                ci += 2; /* Two extra characters are readed from code. */              } else { -                val[ti] = val[ci]; +                return 0;              } +        } else if (val[ci] == '+') { +            val[ti] = ' '; +        } else { +            val[ti] = val[ci];          } -        val[ti++] = 0; -    } else { -        *data  = malloc(1); -        **data = 0;      } -    return ok; +    val[ti] = 0; +    return 1;  } @@ -73,8 +77,8 @@ static int deurl(char ** data)  enum st { st_error = 0, st_name, st_data };  struct automata { -    struct magi_field_list ** list; -    struct magi_field         field; +    struct magi_param_list ** list; +    struct magi_param         param;      int                       size;      int                       len;  }; @@ -82,106 +86,106 @@ struct automata {  static enum st parse_name(struct automata * a, char c)  { -    enum st state; -    if (c != '&' && c != ';') { -        if (c == '=') { -            state   = st_data; -            a->size = 1; -            a->len  = 0; -        } else { -            if (a->len == a->size - 1) { -                a->size *= 2; -                a->field.name = realloc(a->field.name, a->size); -            } -            if (!a->field.name) { -                state = st_error; -                magi_error_set("[urlencoded] Cannot allocate field name."); -            } else { -                state = st_name; -                a->len++; -                a->field.name[a->len - 1] = c; -                a->field.name[a->len]     = 0; -            } -        } -    } else { -        state = st_error; -        magi_error_set("[urlencoded] Reading name, readed: \\%o (render: %c).", -                       c, c); +    if (c == '&' || c == ';') { /* Impossible character means error. */ +        return st_error; +    } +    if (c == '=') { /* Separator ends name. */ +        a->size = 1; +        a->len  = 0; +        return st_data;      } -    return state; + +    if (!magi_str_add(&a->param.name, &a->len, &a->size, c)) { +        return st_error; +    } +    return st_name;  }  static enum st end_data(struct automata * a)  { -    enum st state = st_error; -    if (deurl(&a->field.name) && deurl(&a->field.data)) { -        a->field.len = strlen(a->field.data); -        if (magi_field_list_add(a->list, &a->field)) { -            state         = st_name; +    if (deurl(&a->param.name) && deurl(&a->param.data)) { +        if (magi_param_list_add(a->list, &a->param)) {              a->size       = 1;              a->len        = 0; -            a->field.name = 0; -            a->field.data = 0; +            a->param.name = 0; +            a->param.data = 0; +            return st_name;          }      } -    return state; +    return st_error;  }  static enum st parse_data(struct automata * a, char c)  { -    enum st state; -    if (c != '=') { -        if (c == '&' || c == ';') { -            state = end_data(a); +    if (c == '=') { /* Impossible character means error. */ +        return st_error; +    } +    if (c == '&' || c == ';') { /* Separator ends data. */ +        return end_data(a); +    } + +    if (!magi_str_add(&a->param.data, &a->len, &a->size, c)) { +        return st_error; +    } +    return st_data; +} + +void magi_urlenc(struct magi_param_list ** list, +                 struct magi_request *     request, +                 const char *              encoded) +{ +    enum st         state; +    struct automata a = { 0, { 0, 0 }, 1, 0 }; +    a.list            = list; +    *list             = 0; +    if (!encoded || !*encoded) { +        return; +    } +    for (state = st_name; state && *encoded; ++encoded) { +        if (state == st_name) { +            state = parse_name(&a, *encoded);          } else { -            if (a->len == a->size - 1) { -                a->size *= 2; -                a->field.data = realloc(a->field.data, a->size); -            } -            if (!a->field.data) { -                state = st_error; -                magi_error_set("[urlencoded] Cannot allocate field data."); -            } else { -                state = st_data; -                a->len++; -                a->field.data[a->len - 1] = c; -                a->field.data[a->len]     = 0; -            } +            state = parse_data(&a, *encoded);          } -    } else { -        state = st_error; -        magi_error_set("[urlencoded] Reading data, readed: \\%o (render: %c).", -                       c, c);      } -    return state; +    if (state == st_name || !state || !end_data(&a)) { +        free(a.param.name); +        free(a.param.data); +        request->error = magi_error_urlenc; +    }  } -int magi_parse_urlencoded(struct magi_field_list ** list, const char * input) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * URL Encoding + */ +int magi_urlenc_size(const char * plain)  { -    enum st         state = st_name; -    struct automata a     = { 0, { 0, 0, 0 }, 1, 0 }; -    if (input && *input) { -        a.list = list; -        while (state && *input) { -            switch (state) { -            case st_name: -                state = parse_name(&a, *input); -                break; -            case st_data: -                state = parse_data(&a, *input); -            default: -                break; -            } -            ++input; +    int size; +    if (!plain) { +        return 0; +    } +    for (size = 0; *plain; ++plain, ++size) { +        if (!is_url(*plain)) { +            size += 2;          } -        if (state == st_data) { -            state = end_data(&a); -        } else if (state == st_name) { -            state = st_error; -            magi_error_set("[urlencoded] Input ended while reading name."); +    } +    return size; +} + +void magi_urlencode(const char * plain, char * code) +{ +    if (!plain || !code) { +        return; +    } +    while (*plain) { +        if (is_url(*plain)) { +            *++code = *plain; +        } else { +            *++code = '%'; +            *++code = to_hex(*plain & 0x0F); +            *++code = to_hex(*plain >> 4);          } -        free(a.field.name); -        free(a.field.data); +        ++plain;      } -    return state != st_error;  } diff --git a/src/urlenc.h b/src/urlenc.h index 9c14ccb..5240109 100644 --- a/src/urlenc.h +++ b/src/urlenc.h @@ -4,8 +4,12 @@  #include "request.h" -void magi_urlenc(struct magi_request * request, const char * encoded); +/* Fills request->url_params via parsing encoded data. */ +void magi_urlenc(struct magi_param_list ** list, +                 struct magi_request *     request, +                 const char *              encoded); +/* 'code' from urlencode must have size of at least magi_urlenc_size(plain). */  int  magi_urlenc_size(const char * plain);  void magi_urlencode(const char * plain, char * code); diff --git a/src/utils.c b/src/utils.c index 29e8219..931d1e4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,8 +1,8 @@  #include "utils.h" -#include "error.h"  #include <ctype.h>  #include <stdlib.h> +#include <string.h>  void magi_str_lowercase(char * str) @@ -35,17 +35,16 @@ char * magi_str_create(int len)      return str;  } -char * magi_str_add(char * dest, int * len, int * size, char c) +int magi_str_add(char ** dest, int * len, int * size, char c)  { -    int ok = 1;      if (*len + 1 == *size) {          *size *= 2; -        dest = realloc(dest, *size); +        *dest = realloc(*dest, *size);      } -    if (dest) { -        dest[*len] = c; +    if (*dest) { +        (*dest)[*len] = c;          ++*len; -        dest[*len] = 0; +        (*dest)[*len] = 0;      } -    return dest; +    return !!*dest;  } diff --git a/src/utils.h b/src/utils.h index 15904d0..f67c66c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,10 +2,14 @@  #define MAGI_INCLUDED_UTILS -void   magi_str_lowercase(char * str); +void magi_str_lowercase(char * str); + +/* Results of both create functions are malloced, so need to be freed. */  char * magi_str_create_copy(const char * begin, const char * end);  char * magi_str_create(int len); -char * magi_str_add(char * dest, int * len, int * size, char c); + +/* Null only in case of error. */ +int magi_str_add(char ** dest, int * len, int * size, char c);  #endif | 
