3 * @purpose Runs the FCGI request loop to handle web interface requests.
5 * fcgi_stdio.h must be included before all else so the stdio function
6 * redirection works ok.
9 #include <fcgi_stdio.h>
10 #include <openssl/sha.h>
15 static void LoginHandler(void *data, char *params) {
16 static char loginkey[41] = {0}, ip[256];
17 static time_t timestamp = 0;
18 const char *key, *value;
19 int force = 0, end = 0;
21 while ((params = FCGI_KeyPair(params, &key, &value))) {
22 if (!strcmp(key, "force"))
24 else if (!strcmp(key, "end"))
30 FCGI_BeginJSON(200, "login");
35 time_t now = time(NULL);
36 if (force || !*loginkey || (now - timestamp > 180)) {
38 unsigned char sha1[20];
42 SHA1_Update(&sha1ctx, &now, sizeof(now));
43 SHA1_Update(&sha1ctx, &i, sizeof(i));
44 SHA1_Final(sha1, &sha1ctx);
47 for (i = 0; i < 20; i++)
48 sprintf(loginkey+i*2, "%02x", sha1[i]);
49 sprintf(ip, "%s", getenv("REMOTE_ADDR"));
50 FCGI_BeginJSON(200, "login");
51 FCGI_BuildJSON("key", loginkey);
55 strftime(buf, 128, "%H:%M:%S %d-%m-%Y",localtime(×tamp));
56 FCGI_BeginJSON(401, "login");
57 FCGI_BuildJSON("description", "Already logged in");
58 FCGI_BuildJSON("user", ip);
59 FCGI_BuildJSON("time", buf);
65 * Extracts a key/value pair from a request string.
66 * Note that the input is modified by this function.
67 * @param in The string from which to extract the pair
68 * @param key A pointer to a variable to hold the key string
69 * @param value A pointer to a variable to hold the value string
70 * @return A pointer to the start of the next search location, or NULL if
73 char *FCGI_KeyPair(char *in, const char **key, const char **value)
76 if (!in || !*in) { //Invalid input or string is EOL
81 //Find either = or &, whichever comes first
82 if ((ptr = strpbrk(in, "=&"))) {
83 if (*ptr == '&') { //No value specified
87 //Stopped at an '=' sign
90 if ((ptr = strchr(ptr,'&'))) {
96 } else { //No value specified and no other pair
104 * Begins a response to the client in JSON format.
105 * @param status_code The HTTP status code to be returned.
106 * @param module The name of the module that initiated the response.
108 void FCGI_BeginJSON(StatusCodes status_code, const char *module)
110 switch (status_code) {
113 case STATUS_UNAUTHORIZED:
114 printf("Status: 401 Unauthorized\r\n");
117 printf("Status: 400 Bad Request\r\n");
119 printf("Content-type: application/json; charset=utf-8\r\n\r\n");
121 printf("\t\"module\" : \"%s\"", module);
125 * Adds a key/value pair to a JSON response. The response must have already
126 * been initiated by FCGI_BeginJSON. Note that characters are not escaped.
127 * @param key The key of the JSON entry
128 * ¶m value The value associated with the key.
130 void FCGI_BuildJSON(const char *key, const char *value)
132 printf(",\r\n\t\"%s\" : \"%s\"", key, value);
136 * Ends a JSON response that was initiated by FCGI_BeginJSON.
144 * Main FCGI request loop that receives/responds to client requests.
145 * @param data A data field to be passed to the selected module handler.
147 void FCGI_RequestLoop (void *data)
150 while (FCGI_Accept() >= 0) {
151 ModuleHandler module_handler = NULL;
152 char module[BUFSIZ], params[BUFSIZ];
154 //strncpy doesn't zero-truncate properly
155 snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
156 snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
158 //Remove trailing slashes (if present) from module query
159 size_t lastchar = strlen(module) - 1;
160 if (lastchar > 0 && module[lastchar] == '/')
161 module[lastchar] = 0;
164 if (!strcmp("sensors", module)) {
165 module_handler = Handler_Sensors;
166 } else if (!strcmp("login", module)) {
167 module_handler = LoginHandler;
168 } else if (!strcmp("actuators", module)) {
172 if (module_handler) {
173 module_handler(data, params);
177 FCGI_BeginJSON(400, module);
178 FCGI_BuildJSON("description", "400 Invalid response");
179 snprintf(buf, BUFSIZ, "%d", count);
180 FCGI_BuildJSON("request-number", buf);
181 FCGI_BuildJSON("params", params);
182 FCGI_BuildJSON("host", getenv("SERVER_HOSTNAME"));
183 FCGI_BuildJSON("user", getenv("REMOTE_USER"));
184 FCGI_BuildJSON("userip", getenv("REMOTE_ADDR"));