1 /*
2  * Copyright (c) 2018 the CivetWeb developers
3  * MIT License
4  */
5 
6 /* Simple demo of a REST callback. */
7 #ifdef _WIN32
8 #include <windows.h>
9 #else
10 #include <unistd.h>
11 #endif
12 
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 
17 #include "cJSON.h"
18 #include "civetweb.h"
19 
20 
21 #ifdef NO_SSL
22 #define PORT "8089"
23 #define HOST_INFO "http://localhost:8089"
24 #else
25 #define PORT "8089r,8843s"
26 #define HOST_INFO "https://localhost:8843"
27 #endif
28 
29 #define EXAMPLE_URI "/example"
30 #define EXIT_URI "/exit"
31 
32 int exitNow = 0;
33 
34 
35 static int
SendJSON(struct mg_connection * conn,cJSON * json_obj)36 SendJSON(struct mg_connection *conn, cJSON *json_obj)
37 {
38 	char *json_str = cJSON_PrintUnformatted(json_obj);
39 	size_t json_str_len = strlen(json_str);
40 
41 	/* Send HTTP message header */
42 	mg_send_http_ok(conn, "application/json; charset=utf-8", json_str_len);
43 
44 	/* Send HTTP message content */
45 	mg_write(conn, json_str, json_str_len);
46 
47 	/* Free string allocated by cJSON_Print* */
48 	cJSON_free(json_str);
49 
50 	return (int)json_str_len;
51 }
52 
53 
54 static unsigned request = 0; /* demo data: request counter */
55 
56 
57 static int
ExampleGET(struct mg_connection * conn)58 ExampleGET(struct mg_connection *conn)
59 {
60 	cJSON *obj = cJSON_CreateObject();
61 
62 	if (!obj) {
63 		/* insufficient memory? */
64 		mg_send_http_error(conn, 500, "Server error");
65 		return 500;
66 	}
67 
68 
69 	cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION);
70 	cJSON_AddNumberToObject(obj, "request", ++request);
71 	SendJSON(conn, obj);
72 	cJSON_Delete(obj);
73 
74 	return 200;
75 }
76 
77 
78 static int
ExampleDELETE(struct mg_connection * conn)79 ExampleDELETE(struct mg_connection *conn)
80 {
81 	request = 0;
82 	mg_send_http_error(conn,
83 	                   204,
84 	                   "%s",
85 	                   ""); /* Return "deleted" = "204 No Content" */
86 
87 	return 204;
88 }
89 
90 
91 static int
ExamplePUT(struct mg_connection * conn)92 ExamplePUT(struct mg_connection *conn)
93 {
94 	char buffer[1024];
95 	int dlen = mg_read(conn, buffer, sizeof(buffer) - 1);
96 	cJSON *obj, *elem;
97 	unsigned newvalue;
98 
99 	if ((dlen < 1) || (dlen >= sizeof(buffer))) {
100 		mg_send_http_error(conn, 400, "%s", "No request body data");
101 		return 400;
102 	}
103 	buffer[dlen] = 0;
104 
105 	obj = cJSON_Parse(buffer);
106 	if (obj == NULL) {
107 		mg_send_http_error(conn, 400, "%s", "Invalid request body data");
108 		return 400;
109 	}
110 
111 	elem = cJSON_GetObjectItemCaseSensitive(obj, "request");
112 
113 	if (!cJSON_IsNumber(elem)) {
114 		cJSON_Delete(obj);
115 		mg_send_http_error(conn,
116 		                   400,
117 		                   "%s",
118 		                   "No \"request\" number in body data");
119 		return 400;
120 	}
121 
122 	newvalue = (unsigned)elem->valuedouble;
123 
124 	if ((double)newvalue != elem->valuedouble) {
125 		cJSON_Delete(obj);
126 		mg_send_http_error(conn,
127 		                   400,
128 		                   "%s",
129 		                   "Invalid \"request\" number in body data");
130 		return 400;
131 	}
132 
133 	request = newvalue;
134 	cJSON_Delete(obj);
135 
136 	mg_send_http_error(conn, 201, "%s", ""); /* Return "201 Created" */
137 
138 	return 201;
139 }
140 
141 
142 static int
ExamplePOST(struct mg_connection * conn)143 ExamplePOST(struct mg_connection *conn)
144 {
145 	/* In this example, do the same for PUT and POST */
146 	return ExamplePUT(conn);
147 }
148 
149 
150 static int
ExamplePATCH(struct mg_connection * conn)151 ExamplePATCH(struct mg_connection *conn)
152 {
153 	/* In this example, do the same for PUT and PATCH */
154 	return ExamplePUT(conn);
155 }
156 
157 
158 static int
ExampleHandler(struct mg_connection * conn,void * cbdata)159 ExampleHandler(struct mg_connection *conn, void *cbdata)
160 {
161 
162 	const struct mg_request_info *ri = mg_get_request_info(conn);
163 	(void)cbdata; /* currently unused */
164 
165 	if (0 == strcmp(ri->request_method, "GET")) {
166 		return ExampleGET(conn);
167 	}
168 	if (0 == strcmp(ri->request_method, "PUT")) {
169 		return ExamplePUT(conn);
170 	}
171 	if (0 == strcmp(ri->request_method, "POST")) {
172 		return ExamplePOST(conn);
173 	}
174 	if (0 == strcmp(ri->request_method, "DELETE")) {
175 		return ExampleDELETE(conn);
176 	}
177 	if (0 == strcmp(ri->request_method, "PATCH")) {
178 		return ExamplePATCH(conn);
179 	}
180 
181 	/* this is not a GET request */
182 	mg_send_http_error(
183 	    conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported");
184 	return 405;
185 }
186 
187 
188 int
ExitHandler(struct mg_connection * conn,void * cbdata)189 ExitHandler(struct mg_connection *conn, void *cbdata)
190 {
191 	mg_printf(conn,
192 	          "HTTP/1.1 200 OK\r\nContent-Type: "
193 	          "text/plain\r\nConnection: close\r\n\r\n");
194 	mg_printf(conn, "Server will shut down.\n");
195 	mg_printf(conn, "Bye!\n");
196 	exitNow = 1;
197 	return 1;
198 }
199 
200 
201 int
log_message(const struct mg_connection * conn,const char * message)202 log_message(const struct mg_connection *conn, const char *message)
203 {
204 	puts(message);
205 	return 1;
206 }
207 
208 
209 int
main(int argc,char * argv[])210 main(int argc, char *argv[])
211 {
212 	const char *options[] = {"listening_ports",
213 	                         PORT,
214 	                         "request_timeout_ms",
215 	                         "10000",
216 	                         "error_log_file",
217 	                         "error.log",
218 #ifndef NO_SSL
219 	                         "ssl_certificate",
220 	                         "../../resources/cert/server.pem",
221 	                         "ssl_protocol_version",
222 	                         "3",
223 	                         "ssl_cipher_list",
224 	                         "DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
225 #endif
226 	                         "enable_auth_domain_check",
227 	                         "no",
228 	                         0};
229 
230 	struct mg_callbacks callbacks;
231 	struct mg_context *ctx;
232 	int err = 0;
233 
234 /* Check if libcivetweb has been built with all required features. */
235 #ifndef NO_SSL
236 	if (!mg_check_feature(2)) {
237 		fprintf(stderr,
238 		        "Error: Embedded example built with SSL support, "
239 		        "but civetweb library build without.\n");
240 		err = 1;
241 	}
242 
243 
244 	mg_init_library(MG_FEATURES_SSL);
245 
246 #else
247 	mg_init_library(0);
248 
249 #endif
250 	if (err) {
251 		fprintf(stderr, "Cannot start CivetWeb - inconsistent build.\n");
252 		return EXIT_FAILURE;
253 	}
254 
255 
256 	/* Callback will print error messages to console */
257 	memset(&callbacks, 0, sizeof(callbacks));
258 	callbacks.log_message = log_message;
259 
260 	/* Start CivetWeb web server */
261 	ctx = mg_start(&callbacks, 0, options);
262 
263 	/* Check return value: */
264 	if (ctx == NULL) {
265 		fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");
266 		return EXIT_FAILURE;
267 	}
268 
269 	/* Add handler EXAMPLE_URI, to explain the example */
270 	mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
271 	mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
272 
273 	/* Show sone info */
274 	printf("Start example: %s%s\n", HOST_INFO, EXAMPLE_URI);
275 	printf("Exit example:  %s%s\n", HOST_INFO, EXIT_URI);
276 
277 
278 	/* Wait until the server should be closed */
279 	while (!exitNow) {
280 #ifdef _WIN32
281 		Sleep(1000);
282 #else
283 		sleep(1);
284 #endif
285 	}
286 
287 	/* Stop the server */
288 	mg_stop(ctx);
289 
290 	printf("Server stopped.\n");
291 	printf("Bye!\n");
292 
293 	return EXIT_SUCCESS;
294 }
295