1 /** 2 * makefsdata: Converts a directory structure for use with the lwIP httpd. 3 * 4 * This file is part of the lwIP TCP/IP stack. 5 * 6 * Author: Jim Pettinato 7 * Simon Goldschmidt 8 * 9 * @todo: 10 * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and 11 * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments 12 */ 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #ifdef WIN32 17 #define WIN32_LEAN_AND_MEAN 18 #include "windows.h" 19 #else 20 #include <dir.h> 21 #endif 22 #include <dos.h> 23 #include <string.h> 24 #include <time.h> 25 #include <sys/stat.h> 26 27 /** Makefsdata can generate *all* files deflate-compressed (where file size shrinks). 28 * Since nearly all browsers support this, this is a good way to reduce ROM size. 29 * To compress the files, "miniz.c" must be downloaded seperately. 30 */ 31 #ifndef MAKEFS_SUPPORT_DEFLATE 32 #define MAKEFS_SUPPORT_DEFLATE 0 33 #endif 34 35 #define COPY_BUFSIZE (1024*1024) /* 1 MByte */ 36 37 #if MAKEFS_SUPPORT_DEFLATE 38 #include "../miniz.c" 39 40 typedef unsigned char uint8; 41 typedef unsigned short uint16; 42 typedef unsigned int uint; 43 44 #define my_max(a,b) (((a) > (b)) ? (a) : (b)) 45 #define my_min(a,b) (((a) < (b)) ? (a) : (b)) 46 47 /* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. 48 COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */ 49 #define COMP_OUT_BUF_SIZE COPY_BUFSIZE 50 51 /* OUT_BUF_SIZE is the size of the output buffer used during decompression. 52 OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */ 53 #define OUT_BUF_SIZE COPY_BUFSIZE 54 static uint8 s_outbuf[OUT_BUF_SIZE]; 55 static uint8 s_checkbuf[OUT_BUF_SIZE]; 56 57 /* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). 58 This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */ 59 tdefl_compressor g_deflator; 60 tinfl_decompressor g_inflator; 61 62 int deflate_level = 10; /* default compression level, can be changed via command line */ 63 #define USAGE_ARG_DEFLATE " [-defl<:compr_level>]" 64 #else /* MAKEFS_SUPPORT_DEFLATE */ 65 #define USAGE_ARG_DEFLATE "" 66 #endif /* MAKEFS_SUPPORT_DEFLATE */ 67 68 /* Compatibility defines Win32 vs. DOS */ 69 #ifdef WIN32 70 71 #define FIND_T WIN32_FIND_DATAA 72 #define FIND_T_FILENAME(fInfo) (fInfo.cFileName) 73 #define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) 74 #define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) 75 #define FIND_RET_T HANDLE 76 #define FINDFIRST_FILE(path, result) FindFirstFileA(path, result) 77 #define FINDFIRST_DIR(path, result) FindFirstFileA(path, result) 78 #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) 79 #define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE) 80 #define FINDNEXT_SUCCEEDED(ret) (ret == TRUE) 81 82 #define GETCWD(path, len) GetCurrentDirectoryA(len, path) 83 #define CHDIR(path) SetCurrentDirectoryA(path) 84 #define CHDIR_SUCCEEDED(ret) (ret == TRUE) 85 86 #else 87 88 #define FIND_T struct ffblk 89 #define FIND_T_FILENAME(fInfo) (fInfo.ff_name) 90 #define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC) 91 #define FIND_T_IS_FILE(fInfo) (1) 92 #define FIND_RET_T int 93 #define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH) 94 #define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC) 95 #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) 96 #define FINDFIRST_SUCCEEDED(ret) (ret == 0) 97 #define FINDNEXT_SUCCEEDED(ret) (ret == 0) 98 99 #define GETCWD(path, len) getcwd(path, len) 100 #define CHDIR(path) chdir(path) 101 #define CHDIR_SUCCEEDED(ret) (ret == 0) 102 103 #endif 104 105 #define NEWLINE "\r\n" 106 #define NEWLINE_LEN 2 107 108 /* define this to get the header variables we use to build HTTP headers */ 109 #define LWIP_HTTPD_DYNAMIC_HEADERS 1 110 #define LWIP_HTTPD_SSI 1 111 #include "lwip/init.h" 112 #include "../httpd_structs.h" 113 #include "lwip/apps/fs.h" 114 115 #include "../core/inet_chksum.c" 116 #include "../core/def.c" 117 118 /** (Your server name here) */ 119 const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n"; 120 char serverIDBuffer[1024]; 121 122 /* change this to suit your MEM_ALIGNMENT */ 123 #define PAYLOAD_ALIGNMENT 4 124 /* set this to 0 to prevent aligning payload */ 125 #define ALIGN_PAYLOAD 1 126 /* define this to a type that has the required alignment */ 127 #define PAYLOAD_ALIGN_TYPE "unsigned int" 128 static int payload_alingment_dummy_counter = 0; 129 130 #define HEX_BYTES_PER_LINE 16 131 132 #define MAX_PATH_LEN 256 133 134 struct file_entry 135 { 136 struct file_entry* next; 137 const char* filename_c; 138 }; 139 140 int process_sub(FILE *data_file, FILE *struct_file); 141 int process_file(FILE *data_file, FILE *struct_file, const char *filename); 142 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, 143 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed); 144 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i); 145 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i); 146 void concat_files(const char *file1, const char *file2, const char *targetfile); 147 int check_path(char* path, size_t size); 148 149 /* 5 bytes per char + 3 bytes per line */ 150 static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)]; 151 152 char curSubdir[MAX_PATH_LEN]; 153 char lastFileVar[MAX_PATH_LEN]; 154 char hdr_buf[4096]; 155 156 unsigned char processSubs = 1; 157 unsigned char includeHttpHeader = 1; 158 unsigned char useHttp11 = 0; 159 unsigned char supportSsi = 1; 160 unsigned char precalcChksum = 0; 161 unsigned char includeLastModified = 0; 162 #if MAKEFS_SUPPORT_DEFLATE 163 unsigned char deflateNonSsiFiles = 0; 164 size_t deflatedBytesReduced = 0; 165 size_t overallDataBytes = 0; 166 #endif 167 168 struct file_entry* first_file = NULL; 169 struct file_entry* last_file = NULL; 170 171 static void print_usage(void) 172 { 173 printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE); 174 printf(" targetdir: relative or absolute path to files to convert" NEWLINE); 175 printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE); 176 printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE); 177 printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE); 178 printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE); 179 printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE); 180 printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE); 181 printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE); 182 printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE); 183 #if MAKEFS_SUPPORT_DEFLATE 184 printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE); 185 printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE); 186 #endif 187 printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE); 188 printf(" process files in subdirectory 'fs'" NEWLINE); 189 } 190 191 int main(int argc, char *argv[]) 192 { 193 char path[MAX_PATH_LEN]; 194 char appPath[MAX_PATH_LEN]; 195 FILE *data_file; 196 FILE *struct_file; 197 int filesProcessed; 198 int i; 199 char targetfile[MAX_PATH_LEN]; 200 strcpy(targetfile, "fsdata.c"); 201 202 memset(path, 0, sizeof(path)); 203 memset(appPath, 0, sizeof(appPath)); 204 205 printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE); 206 printf(" by Jim Pettinato - circa 2003 " NEWLINE); 207 printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE); 208 209 strcpy(path, "fs"); 210 for (i = 1; i < argc; i++) { 211 if (argv[i] == NULL) { 212 continue; 213 } 214 if (argv[i][0] == '-') { 215 if (strstr(argv[i], "-svr:") == argv[i]) { 216 snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]); 217 serverID = serverIDBuffer; 218 printf("Using Server-ID: \"%s\"\n", serverID); 219 } else if (strstr(argv[i], "-s") == argv[i]) { 220 processSubs = 0; 221 } else if (strstr(argv[i], "-e") == argv[i]) { 222 includeHttpHeader = 0; 223 } else if (strstr(argv[i], "-11") == argv[i]) { 224 useHttp11 = 1; 225 } else if (strstr(argv[i], "-nossi") == argv[i]) { 226 supportSsi = 0; 227 } else if (strstr(argv[i], "-c") == argv[i]) { 228 precalcChksum = 1; 229 } else if (strstr(argv[i], "-f:") == argv[i]) { 230 strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1); 231 targetfile[sizeof(targetfile) - 1] = 0; 232 printf("Writing to file \"%s\"\n", targetfile); 233 } else if (strstr(argv[i], "-m") == argv[i]) { 234 includeLastModified = 1; 235 } else if (strstr(argv[i], "-defl") == argv[i]) { 236 #if MAKEFS_SUPPORT_DEFLATE 237 char* colon = strstr(argv[i], ":"); 238 if (colon) { 239 if (colon[1] != 0) { 240 int defl_level = atoi(&colon[1]); 241 if ((defl_level >= 0) && (defl_level <= 10)) { 242 deflate_level = defl_level; 243 } else { 244 printf("ERROR: deflate level must be [0..10]" NEWLINE); 245 exit(0); 246 } 247 } 248 } 249 deflateNonSsiFiles = 1; 250 printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level); 251 #else 252 printf("WARNING: Deflate support is disabled\n"); 253 #endif 254 } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) { 255 print_usage(); 256 exit(0); 257 } 258 } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) { 259 print_usage(); 260 exit(0); 261 } else { 262 strncpy(path, argv[i], sizeof(path)-1); 263 path[sizeof(path)-1] = 0; 264 } 265 } 266 267 if (!check_path(path, sizeof(path))) { 268 printf("Invalid path: \"%s\"." NEWLINE, path); 269 exit(-1); 270 } 271 272 GETCWD(appPath, MAX_PATH_LEN); 273 /* if command line param or subdir named 'fs' not found spout usage verbiage */ 274 if (!CHDIR_SUCCEEDED(CHDIR(path))) { 275 /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */ 276 printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path); 277 print_usage(); 278 exit(-1); 279 } 280 CHDIR(appPath); 281 282 printf("HTTP %sheader will %s statically included." NEWLINE, 283 (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""), 284 (includeHttpHeader ? "be" : "not be")); 285 286 sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */ 287 printf(" Processing all files in directory %s", path); 288 if (processSubs) { 289 printf(" and subdirectories..." NEWLINE NEWLINE); 290 } else { 291 printf("..." NEWLINE NEWLINE); 292 } 293 294 data_file = fopen("fsdata.tmp", "wb"); 295 if (data_file == NULL) { 296 printf("Failed to create file \"fsdata.tmp\"\n"); 297 exit(-1); 298 } 299 struct_file = fopen("fshdr.tmp", "wb"); 300 if (struct_file == NULL) { 301 printf("Failed to create file \"fshdr.tmp\"\n"); 302 fclose(data_file); 303 exit(-1); 304 } 305 306 CHDIR(path); 307 308 fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE); 309 fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE); 310 fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE); 311 312 fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE); 313 /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */ 314 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE); 315 /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */ 316 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE); 317 318 /* define alignment defines */ 319 #if ALIGN_PAYLOAD 320 fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE); 321 #endif 322 fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE); 323 fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE); 324 #if ALIGN_PAYLOAD 325 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE); 326 #endif 327 328 sprintf(lastFileVar, "NULL"); 329 330 filesProcessed = process_sub(data_file, struct_file); 331 332 /* data_file now contains all of the raw data.. now append linked list of 333 * file header structs to allow embedded app to search for a file name */ 334 fprintf(data_file, NEWLINE NEWLINE); 335 fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar); 336 fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed); 337 338 fclose(data_file); 339 fclose(struct_file); 340 341 CHDIR(appPath); 342 /* append struct_file to data_file */ 343 printf(NEWLINE "Creating target file..." NEWLINE NEWLINE); 344 concat_files("fsdata.tmp", "fshdr.tmp", targetfile); 345 346 /* if succeeded, delete the temporary files */ 347 if (remove("fsdata.tmp") != 0) { 348 printf("Warning: failed to delete fsdata.tmp\n"); 349 } 350 if (remove("fshdr.tmp") != 0) { 351 printf("Warning: failed to delete fshdr.tmp\n"); 352 } 353 354 printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed); 355 #if MAKEFS_SUPPORT_DEFLATE 356 if (deflateNonSsiFiles) { 357 printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE, 358 (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes)); 359 } 360 #endif 361 printf(NEWLINE); 362 363 while (first_file != NULL) { 364 struct file_entry* fe = first_file; 365 first_file = fe->next; 366 free(fe); 367 } 368 369 return 0; 370 } 371 372 int check_path(char* path, size_t size) 373 { 374 size_t slen; 375 if (path[0] == 0) { 376 /* empty */ 377 return 0; 378 } 379 slen = strlen(path); 380 if (slen >= size) { 381 /* not NULL-terminated */ 382 return 0; 383 } 384 while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) { 385 /* path should not end with trailing backslash */ 386 path[slen] = 0; 387 slen--; 388 } 389 if (slen == 0) { 390 return 0; 391 } 392 return 1; 393 } 394 395 static void copy_file(const char *filename_in, FILE *fout) 396 { 397 FILE *fin; 398 size_t len; 399 void* buf; 400 fin = fopen(filename_in, "rb"); 401 if (fin == NULL) { 402 printf("Failed to open file \"%s\"\n", filename_in); 403 exit(-1); 404 } 405 buf = malloc(COPY_BUFSIZE); 406 while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) { 407 fwrite(buf, 1, len, fout); 408 } 409 free(buf); 410 fclose(fin); 411 } 412 413 void concat_files(const char *file1, const char *file2, const char *targetfile) 414 { 415 FILE *fout; 416 fout = fopen(targetfile, "wb"); 417 if (fout == NULL) { 418 printf("Failed to open file \"%s\"\n", targetfile); 419 exit(-1); 420 } 421 copy_file(file1, fout); 422 copy_file(file2, fout); 423 fclose(fout); 424 } 425 426 int process_sub(FILE *data_file, FILE *struct_file) 427 { 428 FIND_T fInfo; 429 FIND_RET_T fret; 430 int filesProcessed = 0; 431 432 if (processSubs) { 433 /* process subs recursively */ 434 size_t sublen = strlen(curSubdir); 435 size_t freelen = sizeof(curSubdir) - sublen - 1; 436 LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir)); 437 fret = FINDFIRST_DIR("*", &fInfo); 438 if (FINDFIRST_SUCCEEDED(fret)) { 439 do { 440 const char *curName = FIND_T_FILENAME(fInfo); 441 if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) { 442 continue; 443 } 444 if (!FIND_T_IS_DIR(fInfo)) { 445 continue; 446 } 447 if (freelen > 0) { 448 CHDIR(curName); 449 strncat(curSubdir, "/", freelen); 450 strncat(curSubdir, curName, freelen - 1); 451 curSubdir[sizeof(curSubdir) - 1] = 0; 452 printf("processing subdirectory %s/..." NEWLINE, curSubdir); 453 filesProcessed += process_sub(data_file, struct_file); 454 CHDIR(".."); 455 curSubdir[sublen] = 0; 456 } else { 457 printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName); 458 } 459 } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); 460 } 461 } 462 463 fret = FINDFIRST_FILE("*.*", &fInfo); 464 if (FINDFIRST_SUCCEEDED(fret)) { 465 /* at least one file in directory */ 466 do { 467 if (FIND_T_IS_FILE(fInfo)) { 468 const char *curName = FIND_T_FILENAME(fInfo); 469 printf("processing %s/%s..." NEWLINE, curSubdir, curName); 470 if (process_file(data_file, struct_file, curName) < 0) { 471 printf(NEWLINE "Error... aborting" NEWLINE); 472 return -1; 473 } 474 filesProcessed++; 475 } 476 } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); 477 } 478 return filesProcessed; 479 } 480 481 u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed) 482 { 483 FILE *inFile; 484 size_t fsize = 0; 485 u8_t* buf; 486 size_t r; 487 int rs; 488 inFile = fopen(filename, "rb"); 489 if (inFile == NULL) { 490 printf("Failed to open file \"%s\"\n", filename); 491 exit(-1); 492 } 493 fseek(inFile, 0, SEEK_END); 494 rs = ftell(inFile); 495 if (rs < 0) { 496 printf("ftell failed with %d\n", errno); 497 exit(-1); 498 } 499 fsize = (size_t)rs; 500 fseek(inFile, 0, SEEK_SET); 501 buf = (u8_t*)malloc(fsize); 502 LWIP_ASSERT("buf != NULL", buf != NULL); 503 r = fread(buf, 1, fsize, inFile); 504 *file_size = fsize; 505 *is_compressed = 0; 506 #if MAKEFS_SUPPORT_DEFLATE 507 overallDataBytes += fsize; 508 if (deflateNonSsiFiles) { 509 if (can_be_compressed) { 510 if (fsize < OUT_BUF_SIZE) { 511 u8_t* ret_buf; 512 tdefl_status status; 513 size_t in_bytes = fsize; 514 size_t out_bytes = OUT_BUF_SIZE; 515 const void *next_in = buf; 516 void *next_out = s_outbuf; 517 /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */ 518 mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); 519 if (!deflate_level) { 520 comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; 521 } 522 status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); 523 if (status != TDEFL_STATUS_OKAY) { 524 printf("tdefl_init() failed!\n"); 525 exit(-1); 526 } 527 memset(s_outbuf, 0, sizeof(s_outbuf)); 528 status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH); 529 if (status != TDEFL_STATUS_DONE) { 530 printf("deflate failed: %d\n", status); 531 exit(-1); 532 } 533 LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE); 534 if (out_bytes < fsize) { 535 ret_buf = (u8_t*)malloc(out_bytes); 536 LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL); 537 memcpy(ret_buf, s_outbuf, out_bytes); 538 { 539 /* sanity-check compression be inflating and comparing to the original */ 540 tinfl_status dec_status; 541 tinfl_decompressor inflator; 542 size_t dec_in_bytes = out_bytes; 543 size_t dec_out_bytes = OUT_BUF_SIZE; 544 next_out = s_checkbuf; 545 546 tinfl_init(&inflator); 547 memset(s_checkbuf, 0, sizeof(s_checkbuf)); 548 dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0); 549 LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE); 550 LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes); 551 LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize)); 552 } 553 /* free original buffer, use compressed data + size */ 554 free(buf); 555 buf = ret_buf; 556 *file_size = out_bytes; 557 printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize)); 558 deflatedBytesReduced += (size_t)(fsize - out_bytes); 559 *is_compressed = 1; 560 } else { 561 printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize)); 562 } 563 } else { 564 printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE); 565 } 566 } else { 567 printf(" - SSI file, cannot be compressed" NEWLINE); 568 } 569 } 570 #else 571 LWIP_UNUSED_ARG(can_be_compressed); 572 #endif 573 fclose(inFile); 574 return buf; 575 } 576 577 void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size) 578 { 579 size_t written, i, src_off=0; 580 581 size_t off = 0; 582 for (i = 0; i < file_size; i++) { 583 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5); 584 sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]); 585 off += 5; 586 if ((++src_off % HEX_BYTES_PER_LINE) == 0) { 587 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN); 588 memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN); 589 off += NEWLINE_LEN; 590 } 591 if (off + 20 >= sizeof(file_buffer_c)) { 592 written = fwrite(file_buffer_c, 1, off, data_file); 593 LWIP_ASSERT("written == off", written == off); 594 off = 0; 595 } 596 } 597 written = fwrite(file_buffer_c, 1, off, data_file); 598 LWIP_ASSERT("written == off", written == off); 599 } 600 601 int write_checksums(FILE *struct_file, const char *varname, 602 u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size) 603 { 604 int chunk_size = TCP_MSS; 605 int offset, src_offset; 606 size_t len; 607 int i = 0; 608 #if LWIP_TCP_TIMESTAMPS 609 /* when timestamps are used, usable space is 12 bytes less per segment */ 610 chunk_size -= 12; 611 #endif 612 613 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); 614 fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname); 615 616 if (hdr_len > 0) { 617 /* add checksum for HTTP header */ 618 fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len); 619 i++; 620 } 621 src_offset = 0; 622 for (offset = hdr_len; ; offset += len) { 623 unsigned short chksum; 624 void* data = (void*)&file_data[src_offset]; 625 len = LWIP_MIN(chunk_size, (int)file_size - src_offset); 626 if (len == 0) { 627 break; 628 } 629 chksum = ~inet_chksum(data, (u16_t)len); 630 /* add checksum for data */ 631 fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len); 632 i++; 633 } 634 fprintf(struct_file, "};" NEWLINE); 635 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); 636 return i; 637 } 638 639 static int is_valid_char_for_c_var(char x) 640 { 641 if (((x >= 'A') && (x <= 'Z')) || 642 ((x >= 'a') && (x <= 'z')) || 643 ((x >= '0') && (x <= '9')) || 644 (x == '_')) { 645 return 1; 646 } 647 return 0; 648 } 649 650 static void fix_filename_for_c(char* qualifiedName, size_t max_len) 651 { 652 struct file_entry* f; 653 size_t len = strlen(qualifiedName); 654 char *new_name = (char*)malloc(len + 2); 655 int filename_ok; 656 int cnt = 0; 657 size_t i; 658 if (len + 3 == max_len) { 659 printf("File name too long: \"%s\"\n", qualifiedName); 660 exit(-1); 661 } 662 strcpy(new_name, qualifiedName); 663 for (i = 0; i < len; i++) { 664 if (!is_valid_char_for_c_var(new_name[i])) { 665 new_name[i] = '_'; 666 } 667 } 668 do { 669 filename_ok = 1; 670 for (f = first_file; f != NULL; f = f->next) { 671 if (!strcmp(f->filename_c, new_name)) { 672 filename_ok = 0; 673 cnt++; 674 /* try next unique file name */ 675 sprintf(&new_name[len], "%d", cnt); 676 break; 677 } 678 } 679 } while (!filename_ok && (cnt < 999)); 680 if (!filename_ok) { 681 printf("Failed to get unique file name: \"%s\"\n", qualifiedName); 682 exit(-1); 683 } 684 strcpy(qualifiedName, new_name); 685 free(new_name); 686 } 687 688 static void register_filename(const char* qualifiedName) 689 { 690 struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry)); 691 fe->filename_c = strdup(qualifiedName); 692 fe->next = NULL; 693 if (first_file == NULL) { 694 first_file = last_file = fe; 695 } else { 696 last_file->next = fe; 697 last_file = fe; 698 } 699 } 700 701 int is_ssi_file(const char* filename) 702 { 703 size_t loop; 704 for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { 705 if (strstr(filename, g_pcSSIExtensions[loop])) { 706 return 1; 707 } 708 } 709 return 0; 710 } 711 712 int process_file(FILE *data_file, FILE *struct_file, const char *filename) 713 { 714 char varname[MAX_PATH_LEN]; 715 int i = 0; 716 char qualifiedName[MAX_PATH_LEN]; 717 int file_size; 718 u16_t http_hdr_chksum = 0; 719 u16_t http_hdr_len = 0; 720 int chksum_count = 0; 721 u8_t flags = 0; 722 const char* flags_str; 723 u8_t has_content_len; 724 u8_t* file_data; 725 int is_compressed = 0; 726 727 /* create qualified name (@todo: prepend slash or not?) */ 728 sprintf(qualifiedName,"%s/%s", curSubdir, filename); 729 /* create C variable name */ 730 strcpy(varname, qualifiedName); 731 /* convert slashes & dots to underscores */ 732 fix_filename_for_c(varname, MAX_PATH_LEN); 733 register_filename(varname); 734 #if ALIGN_PAYLOAD 735 /* to force even alignment of array, type 1 */ 736 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE); 737 fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++); 738 fprintf(data_file, "#endif" NEWLINE); 739 #endif /* ALIGN_PAYLOAD */ 740 fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname); 741 /* encode source file name (used by file system, not returned to browser) */ 742 fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1); 743 file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i); 744 #if ALIGN_PAYLOAD 745 /* pad to even number of bytes to assure payload is on aligned boundary */ 746 while(i % PAYLOAD_ALIGNMENT != 0) { 747 fprintf(data_file, "0x%02.2x,", 0); 748 i++; 749 } 750 #endif /* ALIGN_PAYLOAD */ 751 fprintf(data_file, NEWLINE); 752 753 has_content_len = !is_ssi_file(filename); 754 file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed); 755 if (includeHttpHeader) { 756 file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed); 757 flags = FS_FILE_FLAGS_HEADER_INCLUDED; 758 if (has_content_len) { 759 flags |= FS_FILE_FLAGS_HEADER_PERSISTENT; 760 } 761 } 762 if (precalcChksum) { 763 chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size); 764 } 765 766 /* build declaration of struct fsdata_file in temp file */ 767 fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname); 768 fprintf(struct_file, "file_%s," NEWLINE, lastFileVar); 769 fprintf(struct_file, "data_%s," NEWLINE, varname); 770 fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i); 771 fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i); 772 switch(flags) 773 { 774 case(FS_FILE_FLAGS_HEADER_INCLUDED): 775 flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED"; 776 break; 777 case(FS_FILE_FLAGS_HEADER_PERSISTENT): 778 flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT"; 779 break; 780 case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT): 781 flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT"; 782 break; 783 default: 784 flags_str = "0"; 785 break; 786 } 787 fprintf(struct_file, "%s," NEWLINE, flags_str); 788 if (precalcChksum) { 789 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); 790 fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname); 791 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); 792 } 793 fprintf(struct_file, "}};" NEWLINE NEWLINE); 794 strcpy(lastFileVar, varname); 795 796 /* write actual file contents */ 797 i = 0; 798 fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size); 799 process_file_data(data_file, file_data, file_size); 800 fprintf(data_file, "};" NEWLINE NEWLINE); 801 free(file_data); 802 return 0; 803 } 804 805 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, 806 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed) 807 { 808 int i = 0; 809 int response_type = HTTP_HDR_OK; 810 const char* file_type; 811 const char *cur_string; 812 size_t cur_len; 813 int written = 0; 814 size_t hdr_len = 0; 815 u16_t acc; 816 const char *file_ext; 817 int j; 818 u8_t provide_last_modified = includeLastModified; 819 820 memset(hdr_buf, 0, sizeof(hdr_buf)); 821 822 if (useHttp11) { 823 response_type = HTTP_HDR_OK_11; 824 } 825 826 fprintf(data_file, NEWLINE "/* HTTP header */"); 827 if (strstr(filename, "404") == filename) { 828 response_type = HTTP_HDR_NOT_FOUND; 829 if (useHttp11) { 830 response_type = HTTP_HDR_NOT_FOUND_11; 831 } 832 } else if (strstr(filename, "400") == filename) { 833 response_type = HTTP_HDR_BAD_REQUEST; 834 if (useHttp11) { 835 response_type = HTTP_HDR_BAD_REQUEST_11; 836 } 837 } else if (strstr(filename, "501") == filename) { 838 response_type = HTTP_HDR_NOT_IMPL; 839 if (useHttp11) { 840 response_type = HTTP_HDR_NOT_IMPL_11; 841 } 842 } 843 cur_string = g_psHTTPHeaderStrings[response_type]; 844 cur_len = strlen(cur_string); 845 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); 846 written += file_put_ascii(data_file, cur_string, cur_len, &i); 847 i = 0; 848 if (precalcChksum) { 849 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 850 hdr_len += cur_len; 851 } 852 853 cur_string = serverID; 854 cur_len = strlen(cur_string); 855 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); 856 written += file_put_ascii(data_file, cur_string, cur_len, &i); 857 i = 0; 858 if (precalcChksum) { 859 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 860 hdr_len += cur_len; 861 } 862 863 file_ext = filename; 864 if (file_ext != NULL) { 865 while(strstr(file_ext, ".") != NULL) { 866 file_ext = strstr(file_ext, "."); 867 file_ext++; 868 } 869 } 870 if ((file_ext == NULL) || (*file_ext == 0)) { 871 printf("failed to get extension for file \"%s\", using default.\n", filename); 872 file_type = HTTP_HDR_DEFAULT_TYPE; 873 } else { 874 file_type = NULL; 875 for (j = 0; j < NUM_HTTP_HEADERS; j++) { 876 if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) { 877 file_type = g_psHTTPHeaders[j].content_type; 878 break; 879 } 880 } 881 if (file_type == NULL) { 882 printf("failed to get file type for extension \"%s\", using default.\n", file_ext); 883 file_type = HTTP_HDR_DEFAULT_TYPE; 884 } 885 } 886 887 /* Content-Length is used for persistent connections in HTTP/1.1 but also for 888 download progress in older versions 889 @todo: just use a big-enough buffer and let the HTTPD send spaces? */ 890 if (provide_content_len) { 891 char intbuf[MAX_PATH_LEN]; 892 int content_len = file_size; 893 memset(intbuf, 0, sizeof(intbuf)); 894 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; 895 cur_len = strlen(cur_string); 896 fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2); 897 written += file_put_ascii(data_file, cur_string, cur_len, &i); 898 if (precalcChksum) { 899 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 900 hdr_len += cur_len; 901 } 902 903 _itoa(content_len, intbuf, 10); 904 strcat(intbuf, "\r\n"); 905 cur_len = strlen(intbuf); 906 written += file_put_ascii(data_file, intbuf, cur_len, &i); 907 i = 0; 908 if (precalcChksum) { 909 memcpy(&hdr_buf[hdr_len], intbuf, cur_len); 910 hdr_len += cur_len; 911 } 912 } 913 if (provide_last_modified) { 914 char modbuf[256]; 915 struct stat stat_data; 916 struct tm* t; 917 memset(modbuf, 0, sizeof(modbuf)); 918 memset(&stat_data, 0, sizeof(stat_data)); 919 cur_string = modbuf; 920 strcpy(modbuf, "Last-Modified: "); 921 if (stat(filename, &stat_data) != 0) { 922 printf("stat(%s) failed with error %d\n", filename, errno); 923 exit(-1); 924 } 925 t = gmtime(&stat_data.st_mtime); 926 if (t == NULL) { 927 printf("gmtime() failed with error %d\n", errno); 928 exit(-1); 929 } 930 strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t); 931 cur_len = strlen(cur_string); 932 fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2); 933 written += file_put_ascii(data_file, cur_string, cur_len, &i); 934 if (precalcChksum) { 935 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 936 hdr_len += cur_len; 937 } 938 939 modbuf[0] = 0; 940 strcat(modbuf, "\r\n"); 941 cur_len = strlen(modbuf); 942 written += file_put_ascii(data_file, modbuf, cur_len, &i); 943 i = 0; 944 if (precalcChksum) { 945 memcpy(&hdr_buf[hdr_len], modbuf, cur_len); 946 hdr_len += cur_len; 947 } 948 } 949 950 /* HTTP/1.1 implements persistent connections */ 951 if (useHttp11) { 952 if (provide_content_len) { 953 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE]; 954 } else { 955 /* no Content-Length available, so a persistent connection is no possible 956 because the client does not know the data length */ 957 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; 958 } 959 cur_len = strlen(cur_string); 960 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); 961 written += file_put_ascii(data_file, cur_string, cur_len, &i); 962 i = 0; 963 if (precalcChksum) { 964 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 965 hdr_len += cur_len; 966 } 967 } 968 969 #if MAKEFS_SUPPORT_DEFLATE 970 if (is_compressed) { 971 /* tell the client about the deflate encoding */ 972 LWIP_ASSERT("error", deflateNonSsiFiles); 973 cur_string = "Content-Encoding: deflate\r\n"; 974 cur_len = strlen(cur_string); 975 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); 976 written += file_put_ascii(data_file, cur_string, cur_len, &i); 977 i = 0; 978 } 979 #else 980 LWIP_UNUSED_ARG(is_compressed); 981 #endif 982 983 /* write content-type, ATTENTION: this includes the double-CRLF! */ 984 cur_string = file_type; 985 cur_len = strlen(cur_string); 986 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); 987 written += file_put_ascii(data_file, cur_string, cur_len, &i); 988 i = 0; 989 990 /* ATTENTION: headers are done now (double-CRLF has been written!) */ 991 992 if (precalcChksum) { 993 memcpy(&hdr_buf[hdr_len], cur_string, cur_len); 994 hdr_len += cur_len; 995 996 LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff); 997 LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len); 998 acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len); 999 *http_hdr_len = (u16_t)hdr_len; 1000 *http_hdr_chksum = acc; 1001 } 1002 1003 return written; 1004 } 1005 1006 int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i) 1007 { 1008 int x; 1009 for (x = 0; x < len; x++) { 1010 unsigned char cur = ascii_string[x]; 1011 fprintf(file, "0x%02.2x,", cur); 1012 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { 1013 fprintf(file, NEWLINE); 1014 } 1015 } 1016 return len; 1017 } 1018 1019 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i) 1020 { 1021 int x; 1022 int idx = 0; 1023 for (x = 0; x < len; x++) { 1024 unsigned char cur = ascii_string[x]; 1025 sprintf(&buf[idx], "0x%02.2x,", cur); 1026 idx += 5; 1027 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { 1028 sprintf(&buf[idx], NEWLINE); 1029 idx += NEWLINE_LEN; 1030 } 1031 } 1032 return len; 1033 } 1034