Imported Upstream version 3.2
[platform/upstream/libwebsockets.git] / plugins / protocol_post_demo.c
1 /*
2  * ws protocol handler plugin for "POST demo"
3  *
4  * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * The person who associated a work with this deed has dedicated
10  * the work to the public domain by waiving all of his or her rights
11  * to the work worldwide under copyright law, including all related
12  * and neighboring rights, to the extent allowed by law. You can copy,
13  * modify, distribute and perform the work, even for commercial purposes,
14  * all without asking permission.
15  *
16  * These test plugins are intended to be adapted for use in your code, which
17  * may be proprietary.  So unlike the library itself, they are licensed
18  * Public Domain.
19  */
20
21 #if !defined (LWS_PLUGIN_STATIC)
22 #define LWS_DLL
23 #define LWS_INTERNAL
24 #include <libwebsockets.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef WIN32
33 #include <io.h>
34 #endif
35 #include <stdio.h>
36
37 struct per_session_data__post_demo {
38         struct lws_spa *spa;
39         char result[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE];
40         char filename[64];
41         long file_length;
42 #if !defined(LWS_WITH_ESP32)
43         lws_filefd_type fd;
44 #endif
45         uint8_t completed:1;
46         uint8_t sent_headers:1;
47         uint8_t sent_body:1;
48 };
49
50 static const char * const param_names[] = {
51         "text",
52         "send",
53         "file",
54         "upload",
55 };
56
57 enum enum_param_names {
58         EPN_TEXT,
59         EPN_SEND,
60         EPN_FILE,
61         EPN_UPLOAD,
62 };
63
64 static int
65 file_upload_cb(void *data, const char *name, const char *filename,
66                char *buf, int len, enum lws_spa_fileupload_states state)
67 {
68         struct per_session_data__post_demo *pss =
69                         (struct per_session_data__post_demo *)data;
70 #if !defined(LWS_WITH_ESP32)
71         int n;
72
73         (void)n;
74 #endif
75
76         switch (state) {
77         case LWS_UFS_OPEN:
78                 lws_strncpy(pss->filename, filename, sizeof(pss->filename));
79                 /* we get the original filename in @filename arg, but for
80                  * simple demo use a fixed name so we don't have to deal with
81                  * attacks  */
82 #if !defined(LWS_WITH_ESP32)
83                 pss->fd = (lws_filefd_type)(long long)lws_open("/tmp/post-file",
84                                O_CREAT | O_TRUNC | O_RDWR, 0600);
85 #endif
86                 break;
87         case LWS_UFS_FINAL_CONTENT:
88         case LWS_UFS_CONTENT:
89                 if (len) {
90                         pss->file_length += len;
91
92                         /* if the file length is too big, drop it */
93                         if (pss->file_length > 100000)
94                                 return 1;
95
96 #if !defined(LWS_WITH_ESP32)
97                         n = write((int)(long long)pss->fd, buf, len);
98                         lwsl_info("%s: write %d says %d\n", __func__, len, n);
99 #else
100                         lwsl_notice("%s: Received chunk size %d\n", __func__, len);
101 #endif
102                 }
103                 if (state == LWS_UFS_CONTENT)
104                         break;
105 #if !defined(LWS_WITH_ESP32)
106                 close((int)(long long)pss->fd);
107                 pss->fd = LWS_INVALID_FILE;
108 #endif
109                 break;
110         case LWS_UFS_CLOSE:
111                 break;
112         }
113
114         return 0;
115 }
116
117 /*
118  * returns length in bytes
119  */
120
121 static int
122 format_result(struct per_session_data__post_demo *pss)
123 {
124         unsigned char *p, *start, *end;
125         int n;
126
127         p = (unsigned char *)pss->result + LWS_PRE;
128         start = p;
129         end = p + sizeof(pss->result) - LWS_PRE - 1;
130
131         p += lws_snprintf((char *)p, end -p,
132                         "<!DOCTYPE html><html lang=\"en\"><head>"
133                         "<meta charset=utf-8 http-equiv=\"Content-Language\" "
134                         "content=\"en\"/>"
135           "<title>LWS Server Status</title>"
136           "</head><body><h1>Form results (after urldecoding)</h1>"
137           "<table><tr><td>Name</td><td>Length</td><td>Value</td></tr>");
138
139         for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) {
140                 if (!lws_spa_get_string(pss->spa, n))
141                         p += lws_snprintf((char *)p, end - p,
142                             "<tr><td><b>%s</b></td><td>0"
143                             "</td><td>NULL</td></tr>",
144                             param_names[n]);
145                 else
146                         p += lws_snprintf((char *)p, end - p,
147                             "<tr><td><b>%s</b></td><td>%d"
148                             "</td><td>%s</td></tr>",
149                             param_names[n],
150                             lws_spa_get_length(pss->spa, n),
151                             lws_spa_get_string(pss->spa, n));
152         }
153
154         p += lws_snprintf((char *)p, end - p,
155                         "</table><br><b>filename:</b> %s, "
156                         "<b>length</b> %ld",
157                         pss->filename, pss->file_length);
158
159         p += lws_snprintf((char *)p, end - p, "</body></html>");
160
161         return (int)lws_ptr_diff(p, start);
162 }
163
164 static int
165 callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
166                    void *user, void *in, size_t len)
167 {
168         struct per_session_data__post_demo *pss =
169                         (struct per_session_data__post_demo *)user;
170         unsigned char *p, *start, *end;
171         int n;
172
173         switch (reason) {
174         case LWS_CALLBACK_HTTP_BODY:
175                 /* create the POST argument parser if not already existing */
176                 if (!pss->spa) {
177                         pss->spa = lws_spa_create(wsi, param_names,
178                                         LWS_ARRAY_SIZE(param_names), 1024,
179                                         file_upload_cb, pss);
180                         if (!pss->spa)
181                                 return -1;
182
183                         pss->filename[0] = '\0';
184                         pss->file_length = 0;
185                 }
186
187                 /* let it parse the POST data */
188                 if (lws_spa_process(pss->spa, in, (int)len))
189                         return -1;
190                 break;
191
192         case LWS_CALLBACK_HTTP_BODY_COMPLETION:
193                 lwsl_debug("LWS_CALLBACK_HTTP_BODY_COMPLETION: %p\n", wsi);
194                 /* call to inform no more payload data coming */
195                 lws_spa_finalize(pss->spa);
196
197                 pss->completed = 1;
198                 lws_callback_on_writable(wsi);
199                 break;
200
201         case LWS_CALLBACK_HTTP_WRITEABLE:
202                 if (!pss->completed)
203                         break;
204
205                 p = (unsigned char *)pss->result + LWS_PRE;
206                 start = p;
207                 end = p + sizeof(pss->result) - LWS_PRE - 1;
208
209                 if (!pss->sent_headers) {
210                         n = format_result(pss);
211
212                         if (lws_add_http_header_status(wsi, HTTP_STATUS_OK,
213                                                        &p, end))
214                                 goto bail;
215
216                         if (lws_add_http_header_by_token(wsi,
217                                         WSI_TOKEN_HTTP_CONTENT_TYPE,
218                                         (unsigned char *)"text/html", 9,
219                                         &p, end))
220                                 goto bail;
221                         if (lws_add_http_header_content_length(wsi, n, &p, end))
222                                 goto bail;
223                         if (lws_finalize_http_header(wsi, &p, end))
224                                 goto bail;
225
226                         /* first send the headers ... */
227                         n = lws_write(wsi, start, lws_ptr_diff(p, start),
228                                       LWS_WRITE_HTTP_HEADERS);
229                         if (n < 0)
230                                 goto bail;
231
232                         pss->sent_headers = 1;
233                         lws_callback_on_writable(wsi);
234                         break;
235                 }
236
237                 if (!pss->sent_body) {
238                         n = format_result(pss);
239
240                         n = lws_write(wsi, (unsigned char *)start, n,
241                                       LWS_WRITE_HTTP_FINAL);
242
243                         pss->sent_body = 1;
244                         if (n < 0)
245                                 return 1;
246                         goto try_to_reuse;
247                 }
248                 break;
249
250         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
251                 /* called when our wsi user_space is going to be destroyed */
252                 if (pss->spa) {
253                         lws_spa_destroy(pss->spa);
254                         pss->spa = NULL;
255                 }
256                 break;
257
258         default:
259                 break;
260         }
261
262         return 0;
263
264 bail:
265
266         return 1;
267
268 try_to_reuse:
269         if (lws_http_transaction_completed(wsi))
270                 return -1;
271
272         return 0;
273 }
274
275 #define LWS_PLUGIN_PROTOCOL_POST_DEMO \
276         { \
277                 "protocol-post-demo", \
278                 callback_post_demo, \
279                 sizeof(struct per_session_data__post_demo), \
280                 1024, \
281                 0, NULL, 0 \
282         }
283
284 #if !defined (LWS_PLUGIN_STATIC)
285
286 static const struct lws_protocols protocols[] = {
287         LWS_PLUGIN_PROTOCOL_POST_DEMO
288 };
289
290 LWS_EXTERN LWS_VISIBLE int
291 init_protocol_post_demo(struct lws_context *context,
292                         struct lws_plugin_capability *c)
293 {
294         if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
295                 lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
296                          c->api_magic);
297                 return 1;
298         }
299
300         c->protocols = protocols;
301         c->count_protocols = LWS_ARRAY_SIZE(protocols);
302         c->extensions = NULL;
303         c->count_extensions = 0;
304
305         return 0;
306 }
307
308 LWS_EXTERN LWS_VISIBLE int
309 destroy_protocol_post_demo(struct lws_context *context)
310 {
311         return 0;
312 }
313
314 #endif