1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007,2013 Christian Grothoff 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. 19 */ 20 21 /** 22 * @file test_postprocessor.c 23 * @brief Testcase for postprocessor 24 * @author Christian Grothoff 25 */ 26 27 #include "platform.h" 28 #include "microhttpd.h" 29 #include "internal.h" 30 #include <stdlib.h> 31 #include <string.h> 32 #include <stdio.h> 33 34 #ifndef WINDOWS 35 #include <unistd.h> 36 #endif 37 38 /** 39 * Array of values that the value checker "wants". 40 * Each series of checks should be terminated by 41 * five NULL-entries. 42 */ 43 const char *want[] = { 44 #define URL_DATA "abc=def&x=5" 45 #define URL_START 0 46 "abc", NULL, NULL, NULL, "def", 47 "x", NULL, NULL, NULL, "5", 48 #define URL_END (URL_START + 10) 49 NULL, NULL, NULL, NULL, NULL, 50 #define FORM_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata\r\n--AaB03x--\r\n" 51 #define FORM_START (URL_END + 5) 52 "field1", NULL, NULL, NULL, "Joe Blow", 53 "pics", "file1.txt", "text/plain", "binary", "filedata", 54 #define FORM_END (FORM_START + 10) 55 NULL, NULL, NULL, NULL, NULL, 56 #define FORM_NESTED_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"\r\nContent-type: multipart/mixed, boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--" 57 #define FORM_NESTED_START (FORM_END + 5) 58 "field1", NULL, NULL, NULL, "Jane Blow", 59 "pics", "file1.txt", "text/plain", NULL, "filedata1", 60 "pics", "file2.gif", "image/gif", "binary", "filedata2", 61 #define FORM_NESTED_END (FORM_NESTED_START + 15) 62 NULL, NULL, NULL, NULL, NULL, 63 #define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3=" 64 #define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5) 65 "key1", NULL, NULL, NULL, "value1", 66 "key2", NULL, NULL, NULL, "", 67 "key3", NULL, NULL, NULL, "", 68 #define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15) 69 NULL, NULL, NULL, NULL, NULL 70 }; 71 72 static int 73 mismatch (const char *a, const char *b) 74 { 75 if (a == b) 76 return 0; 77 if ((a == NULL) || (b == NULL)) 78 return 1; 79 return 0 != strcmp (a, b); 80 } 81 82 static int 83 value_checker (void *cls, 84 enum MHD_ValueKind kind, 85 const char *key, 86 const char *filename, 87 const char *content_type, 88 const char *transfer_encoding, 89 const char *data, uint64_t off, size_t size) 90 { 91 int *want_off = cls; 92 int idx = *want_off; 93 94 #if 0 95 fprintf (stderr, 96 "VC: `%s' `%s' `%s' `%s' `%.*s'\n", 97 key, filename, content_type, transfer_encoding, 98 (int) size, 99 data); 100 #endif 101 if ( (0 != off) && (0 == size) ) 102 return MHD_YES; 103 if ((idx < 0) || 104 (want[idx] == NULL) || 105 (0 != strcmp (key, want[idx])) || 106 (mismatch (filename, want[idx + 1])) || 107 (mismatch (content_type, want[idx + 2])) || 108 (mismatch (transfer_encoding, want[idx + 3])) || 109 (0 != memcmp (data, &want[idx + 4][off], size))) 110 { 111 *want_off = -1; 112 return MHD_NO; 113 } 114 if (off + size == strlen (want[idx + 4])) 115 *want_off = idx + 5; 116 return MHD_YES; 117 118 } 119 120 121 static int 122 test_urlencoding () 123 { 124 struct MHD_Connection connection; 125 struct MHD_HTTP_Header header; 126 struct MHD_PostProcessor *pp; 127 unsigned int want_off = URL_START; 128 int i; 129 int delta; 130 size_t size; 131 132 memset (&connection, 0, sizeof (struct MHD_Connection)); 133 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 134 connection.headers_received = &header; 135 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 136 header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; 137 header.kind = MHD_HEADER_KIND; 138 pp = MHD_create_post_processor (&connection, 139 1024, &value_checker, &want_off); 140 i = 0; 141 size = strlen (URL_DATA); 142 while (i < size) 143 { 144 delta = 1 + MHD_random_ () % (size - i); 145 MHD_post_process (pp, &URL_DATA[i], delta); 146 i += delta; 147 } 148 MHD_destroy_post_processor (pp); 149 if (want_off != URL_END) 150 return 1; 151 return 0; 152 } 153 154 155 static int 156 test_multipart_garbage () 157 { 158 struct MHD_Connection connection; 159 struct MHD_HTTP_Header header; 160 struct MHD_PostProcessor *pp; 161 unsigned int want_off; 162 size_t size = strlen (FORM_DATA); 163 size_t splitpoint; 164 char xdata[size + 3]; 165 166 /* fill in evil garbage at the beginning */ 167 xdata[0] = '-'; 168 xdata[1] = 'x'; 169 xdata[2] = '\r'; 170 memcpy (&xdata[3], FORM_DATA, size); 171 size += 3; 172 173 size = strlen (FORM_DATA); 174 for (splitpoint = 1; splitpoint < size; splitpoint++) 175 { 176 want_off = FORM_START; 177 memset (&connection, 0, sizeof (struct MHD_Connection)); 178 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 179 connection.headers_received = &header; 180 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 181 header.value = 182 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x"; 183 header.kind = MHD_HEADER_KIND; 184 pp = MHD_create_post_processor (&connection, 185 1024, &value_checker, &want_off); 186 MHD_post_process (pp, xdata, splitpoint); 187 MHD_post_process (pp, &xdata[splitpoint], size - splitpoint); 188 MHD_destroy_post_processor (pp); 189 if (want_off != FORM_END) 190 return (int) splitpoint; 191 } 192 return 0; 193 } 194 195 196 static int 197 test_multipart_splits () 198 { 199 struct MHD_Connection connection; 200 struct MHD_HTTP_Header header; 201 struct MHD_PostProcessor *pp; 202 unsigned int want_off; 203 size_t size; 204 size_t splitpoint; 205 206 size = strlen (FORM_DATA); 207 for (splitpoint = 1; splitpoint < size; splitpoint++) 208 { 209 want_off = FORM_START; 210 memset (&connection, 0, sizeof (struct MHD_Connection)); 211 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 212 connection.headers_received = &header; 213 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 214 header.value = 215 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x"; 216 header.kind = MHD_HEADER_KIND; 217 pp = MHD_create_post_processor (&connection, 218 1024, &value_checker, &want_off); 219 MHD_post_process (pp, FORM_DATA, splitpoint); 220 MHD_post_process (pp, &FORM_DATA[splitpoint], size - splitpoint); 221 MHD_destroy_post_processor (pp); 222 if (want_off != FORM_END) 223 return (int) splitpoint; 224 } 225 return 0; 226 } 227 228 229 static int 230 test_multipart () 231 { 232 struct MHD_Connection connection; 233 struct MHD_HTTP_Header header; 234 struct MHD_PostProcessor *pp; 235 unsigned int want_off = FORM_START; 236 int i; 237 int delta; 238 size_t size; 239 240 memset (&connection, 0, sizeof (struct MHD_Connection)); 241 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 242 connection.headers_received = &header; 243 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 244 header.value = 245 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x"; 246 header.kind = MHD_HEADER_KIND; 247 pp = MHD_create_post_processor (&connection, 248 1024, &value_checker, &want_off); 249 i = 0; 250 size = strlen (FORM_DATA); 251 while (i < size) 252 { 253 delta = 1 + MHD_random_ () % (size - i); 254 MHD_post_process (pp, &FORM_DATA[i], delta); 255 i += delta; 256 } 257 MHD_destroy_post_processor (pp); 258 if (want_off != FORM_END) 259 return 2; 260 return 0; 261 } 262 263 264 static int 265 test_nested_multipart () 266 { 267 struct MHD_Connection connection; 268 struct MHD_HTTP_Header header; 269 struct MHD_PostProcessor *pp; 270 unsigned int want_off = FORM_NESTED_START; 271 int i; 272 int delta; 273 size_t size; 274 275 memset (&connection, 0, sizeof (struct MHD_Connection)); 276 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 277 connection.headers_received = &header; 278 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 279 header.value = 280 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x"; 281 header.kind = MHD_HEADER_KIND; 282 pp = MHD_create_post_processor (&connection, 283 1024, &value_checker, &want_off); 284 i = 0; 285 size = strlen (FORM_NESTED_DATA); 286 while (i < size) 287 { 288 delta = 1 + MHD_random_ () % (size - i); 289 MHD_post_process (pp, &FORM_NESTED_DATA[i], delta); 290 i += delta; 291 } 292 MHD_destroy_post_processor (pp); 293 if (want_off != FORM_NESTED_END) 294 return 4; 295 return 0; 296 } 297 298 299 static int 300 test_empty_value () 301 { 302 struct MHD_Connection connection; 303 struct MHD_HTTP_Header header; 304 struct MHD_PostProcessor *pp; 305 unsigned int want_off = URL_EMPTY_VALUE_START; 306 int i; 307 int delta; 308 size_t size; 309 310 memset (&connection, 0, sizeof (struct MHD_Connection)); 311 memset (&header, 0, sizeof (struct MHD_HTTP_Header)); 312 connection.headers_received = &header; 313 header.header = MHD_HTTP_HEADER_CONTENT_TYPE; 314 header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; 315 header.kind = MHD_HEADER_KIND; 316 pp = MHD_create_post_processor (&connection, 317 1024, &value_checker, &want_off); 318 i = 0; 319 size = strlen (URL_EMPTY_VALUE_DATA); 320 while (i < size) 321 { 322 delta = 1 + MHD_random_ () % (size - i); 323 MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta); 324 i += delta; 325 } 326 MHD_destroy_post_processor (pp); 327 if (want_off != URL_EMPTY_VALUE_END) 328 return 8; 329 return 0; 330 } 331 332 333 334 335 int 336 main (int argc, char *const *argv) 337 { 338 unsigned int errorCount = 0; 339 340 errorCount += test_multipart_splits (); 341 errorCount += test_multipart_garbage (); 342 errorCount += test_urlencoding (); 343 errorCount += test_multipart (); 344 errorCount += test_nested_multipart (); 345 errorCount += test_empty_value (); 346 if (errorCount != 0) 347 fprintf (stderr, "Error (code: %u)\n", errorCount); 348 return errorCount != 0; /* 0 == pass */ 349 } 350