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}