1#include "include/pw.h"
  2#include "include/pwlib/cgi.h"
  3#include "include/pwlib/env.h"
  4#include "include/pwlib/file.h"
  5#include "include/pwlib/uri.h"
  6
  7[[nodiscard]] bool cgi_get_request_headers(PwValuePtr request_headers)
  8{
  9    PwValue env = PW_NULL;
 10    if (!pw_read_environment(&env)) {
 11        return false;
 12    }
 13    if (!pw_create(PwTypeId_BasicMap, request_headers)) {
 14        return false;
 15    }
 16    PwValue cgi_param_names = PW_NULL;
 17    if (!pw_set_va2(PwTypeId_BasicSet, &cgi_param_names,
 18                    PwStaticString("AUTH_TYPE"),
 19                    PwStaticString("CONTENT_LENGTH"),
 20                    PwStaticString("CONTENT_TYPE"),
 21                    PwStaticString("GATEWAY_INTERFACE"),
 22                    PwStaticString("PATH_INFO"),  // not set by nginx
 23                    PwStaticString("PATH_TRANSLATED"),
 24                    PwStaticString("QUERY_STRING"),
 25                    PwStaticString("REMOTE_ADDR"),
 26                    PwStaticString("REMOTE_HOST"),
 27                    PwStaticString("REMOTE_IDENT"),
 28                    PwStaticString("REMOTE_PORT"),
 29                    PwStaticString("REMOTE_USER"),
 30                    PwStaticString("REQUEST_METHOD"),
 31                    PwStaticString("REQUEST_SCHEME"),
 32                    PwStaticString("SCRIPT_NAME"),
 33                    PwStaticString("SERVER_NAME"),
 34                    PwStaticString("SERVER_PORT"),
 35                    PwStaticString("SERVER_PROTOCOL"),
 36                    PwStaticString("SERVER_SOFTWARE"),
 37                    PwStaticString("HTTPS")  // apache compatibility
 38       )) {
 39        return false;
 40    }
 41    unsigned n = pw_map_length(&env);
 42    for (unsigned i = 0; i < n; i++) {
 43        PwValue key = PW_NULL;
 44        PwValue value = PW_NULL;
 45        if (!pw_map_item(&env, i, &key, &value)) {
 46            return false;
 47        }
 48        if(pw_set_has_item(&cgi_param_names, &key)) {
 49
 50            // CGI-specific variables
 51
 52            if (pw_equal(&key, "HTTPS")) {
 53                pw_destroy(&key);
 54                key = PwStaticString("REQUEST_SCHEME");
 55                if (pw_string_eqi(&value, "on") || pw_equal(&value, "1")
 56                    || pw_string_eqi(&value, "true") || pw_string_eqi(&value, "yes") || pw_string_eqi(&value, "y")) {
 57                    pw_destroy(&value);
 58                    value = PwString("https");
 59                } else {
 60                    pw_destroy(&value);
 61                    value = PwString("http");
 62                }
 63            } else if (pw_equal(&key, "CONTENT_LENGTH")) {
 64                pw_destroy(&key);
 65                key = PwStaticString("Content-Length");
 66
 67            } else if (pw_equal(&key, "CONTENT_TYPE")) {
 68                pw_destroy(&key);
 69                key = PwStaticString("Content-Type");
 70            }
 71            if (!pw_map_update(request_headers, &key, &value)) {
 72                return false;
 73            }
 74        } else if (pw_startswith(&key, "HTTP_")) {
 75
 76            // HTTP request headers
 77
 78            PwValue parts = PW_NULL;
 79            if (!pw_string_split_chr(&key, '_', 0, &parts)) {
 80                return false;
 81            }
 82            PwValue capitalized = PW_NULL;
 83            if (!pw_create(PwTypeId_BasicArray, &capitalized)) {
 84                return false;
 85            }
 86            unsigned num_parts = pw_array_length(&parts);
 87            for (unsigned i = 1; i < num_parts; i++) {
 88                PwValue item = PW_NULL;;
 89                if (!pw_array_item(&parts, i, &item)) {
 90                    return false;
 91                }
 92                if (!pw_string_capitalize(&item)) {
 93                    return false;
 94                }
 95                if (!pw_array_append(&capitalized, &item)) {
 96                    return false;
 97                }
 98            }
 99            if (!pw_array_join(&capitalized, "-", &key)) {
100                return false;
101            }
102            if (!pw_map_update(request_headers, &key, &value)) {
103                return false;
104            }
105        }
106    }
107    return true;
108}
109
110[[nodiscard]] bool cgi_load_request(PwValuePtr request_headers, PwValuePtr result)
111{
112    PwValue content_length_str = PW_NULL;
113    if (!pw_map_get(request_headers, "Content-Length", &content_length_str)) {
114        pw_exception(PwStatus(PweBasic), "Missing Content-Length");
115        return false;
116    }
117
118    unsigned content_length;
119    if (!pw_as_unsigned(&content_length_str, &content_length)) {
120        return false;
121    }
122
123    PwValue stdin_file = PW_NULL;
124    if (!pw_create(PwTypeId_File, &stdin_file)) {
125        return false;
126    }
127    if (!pw_set_fd(&stdin_file, 0, false)) {
128        return false;
129    }
130    PwValue data = PW_STRING();
131    if (!pw_read_file(&stdin_file, &data, content_length)) {
132        return false;
133    }
134
135    // parse request
136
137    PwValue content_type = PW_NULL;
138    if (!pw_map_get(request_headers, "Content-Type", &content_type)) {
139        return false;
140    }
141
142    if (pw_equal(&content_type, "application/x-www-form-urlencoded")) {
143        return pw_parse_uri_query(&data, result);
144    }
145
146    // TODO: application/json
147
148    // TODO: multipart/form-data; boundary=----geckoformboundary6f97956df84d18bdeb973e9ed75005
149
150    // TODO: file upload
151
152    pw_exception(PwStatus(PweBasic), "Bad content type of request");
153    return false;
154}