Merge pull request #15 from jtanx/master
[matches/MCTX3420.git] / testing / fastcgi-approach / fastcgi.c
1 /**
2  * @file fastcgi.c
3  * @purpose Runs the FCGI request loop to handle web interface requests.
4  *
5  * fcgi_stdio.h must be included before all else so the stdio function
6  * redirection works ok.
7  */
8
9 #include <fcgi_stdio.h>
10 #include <openssl/sha.h>
11 #include "fastcgi.h"
12 #include "common.h"
13 #include <time.h>
14
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;
20
21         while ((params = FCGI_KeyPair(params, &key, &value))) {
22                 if (!strcmp(key, "force"))
23                         force = !force;
24                 else if (!strcmp(key, "end"))
25                         end = !end;
26         }
27
28         if (end) {
29                 *loginkey = 0;
30                 FCGI_BeginJSON(200, "login");
31                 FCGI_EndJSON();
32                 return;
33         }
34
35         time_t now = time(NULL);
36         if (force || !*loginkey || (now - timestamp > 180)) {
37                 SHA_CTX sha1ctx;
38                 unsigned char sha1[20];
39                 int i = rand();
40
41                 SHA1_Init(&sha1ctx);
42                 SHA1_Update(&sha1ctx, &now, sizeof(now));
43                 SHA1_Update(&sha1ctx, &i, sizeof(i));
44                 SHA1_Final(sha1, &sha1ctx);
45
46                 timestamp = now;
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);
52                 FCGI_EndJSON();
53         } else {
54                 char buf[128];
55                 strftime(buf, 128, "%H:%M:%S %d-%m-%Y",localtime(&timestamp)); 
56                 FCGI_BeginJSON(401, "login");
57                 FCGI_BuildJSON("description", "Already logged in");
58                 FCGI_BuildJSON("user", ip); 
59                 FCGI_BuildJSON("time", buf);
60                 FCGI_EndJSON();
61         }
62 }
63
64 /**
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
71  *         the EOL is reached.
72  */
73 char *FCGI_KeyPair(char *in, const char **key, const char **value)
74 {
75         char *ptr;
76         if (!in || !*in) { //Invalid input or string is EOL
77                 return NULL;
78         }
79
80         *key = in;
81         //Find either = or &, whichever comes first
82         if ((ptr = strpbrk(in, "=&"))) {
83                 if (*ptr == '&') { //No value specified
84                         *value = ptr;
85                         *ptr++ = 0;
86                 } else {
87                         //Stopped at an '=' sign
88                         *ptr++ = 0;
89                         *value = ptr;
90                         if ((ptr = strchr(ptr,'&'))) {
91                                 *ptr++ = 0;
92                         } else {
93                                 ptr = "";
94                         }
95                 }
96         } else { //No value specified and no other pair
97                 ptr = "";
98                 *value = ptr;
99         }
100         return ptr;
101 }
102
103 /**
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.
107  */
108 void FCGI_BeginJSON(StatusCodes status_code, const char *module)
109 {
110         switch (status_code) {
111                 case STATUS_OK:
112                         break;
113                 case STATUS_UNAUTHORIZED:
114                         printf("Status: 401 Unauthorized\r\n");
115                         break;
116                 default:
117                         printf("Status: 400 Bad Request\r\n");
118         }
119         printf("Content-type: application/json; charset=utf-8\r\n\r\n");
120         printf("{\r\n");
121         printf("\t\"module\" : \"%s\"", module);
122 }
123
124 /**
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  * &param value The value associated with the key.
129  */
130 void FCGI_BuildJSON(const char *key, const char *value)
131 {
132         printf(",\r\n\t\"%s\" : \"%s\"", key, value);
133 }
134
135 /**
136  * Ends a JSON response that was initiated by FCGI_BeginJSON.
137  */
138 void FCGI_EndJSON() 
139 {
140         printf("\r\n}\r\n");
141 }
142
143 /**
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.
146  */ 
147 void FCGI_RequestLoop (void *data)
148 {
149         int count = 0;
150         while (FCGI_Accept() >= 0)   {
151                 ModuleHandler module_handler = NULL;
152                 char module[BUFSIZ], params[BUFSIZ];
153
154                 //strncpy doesn't zero-truncate properly
155                 snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
156                 snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
157                 
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;
162                 
163
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)) {
169                         
170                 }
171
172                 if (module_handler) {
173                         module_handler(data, params);
174                 } else {
175                         char buf[BUFSIZ];
176                         
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"));
185                         FCGI_EndJSON();
186                 }
187
188                 count++;
189         }
190 }

UCC git Repository :: git.ucc.asn.au