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 #include <string.h>
17 #include <time.h>
18 #include <sys/stat.h>
19 
20 #include "tinydir.h"
21 
22 /** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
23  * Since nearly all browsers support this, this is a good way to reduce ROM size.
24  * To compress the files, "miniz.c" must be downloaded separately OR
25  * MAKEFS_SUPPORT_DEFLATE_ZLIB must be set and the zlib library and headers
26  * must be present on the system compiling this program.
27  */
28 #ifndef MAKEFS_SUPPORT_DEFLATE
29 #define MAKEFS_SUPPORT_DEFLATE 0
30 #ifndef MAKEFS_SUPPORT_DEFLATE_ZLIB
31 #define MAKEFS_SUPPORT_DEFLATE_ZLIB 0
32 #endif /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
33 #endif /* MAKEFS_SUPPORT_DEFLATE */
34 
35 #define COPY_BUFSIZE (1024*1024) /* 1 MByte */
36 
37 #if MAKEFS_SUPPORT_DEFLATE
38 #if MAKEFS_SUPPORT_DEFLATE_ZLIB
39 #include <zlib.h>
40 #else
41 #include "../miniz.c"
42 #endif /* MAKEFS_SUPPORT_DEFLATE */
43 
44 typedef unsigned char uint8;
45 typedef unsigned short uint16;
46 typedef unsigned int uint;
47 
48 #define my_max(a,b) (((a) > (b)) ? (a) : (b))
49 #define my_min(a,b) (((a) < (b)) ? (a) : (b))
50 
51 /* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
52    COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
53 #define COMP_OUT_BUF_SIZE COPY_BUFSIZE
54 
55 /* OUT_BUF_SIZE is the size of the output buffer used during decompression.
56    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) */
57 #define OUT_BUF_SIZE COPY_BUFSIZE
58 static uint8 s_outbuf[OUT_BUF_SIZE];
59 static uint8 s_checkbuf[OUT_BUF_SIZE];
60 
61 #ifndef MAKEFS_SUPPORT_DEFLATE_ZLIB
62 /* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
63    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. */
64 tdefl_compressor g_deflator;
65 #endif /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
66 
67 static int deflate_level; /* default compression level, can be changed via command line */
68 #define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
69 #else /* MAKEFS_SUPPORT_DEFLATE */
70 #define USAGE_ARG_DEFLATE ""
71 #endif /* MAKEFS_SUPPORT_DEFLATE */
72 
73 #ifdef WIN32
74 
75 #define GETCWD(path, len)             GetCurrentDirectoryA(len, path)
76 #define GETCWD_SUCCEEDED(ret)         (ret != 0)
77 #define CHDIR(path)                   SetCurrentDirectoryA(path)
78 #define CHDIR_SUCCEEDED(ret)          (ret == TRUE)
79 
80 #elif __linux__
81 
82 #define GETCWD(path, len)             getcwd(path, len)
83 #define GETCWD_SUCCEEDED(ret)         (ret != NULL)
84 #define CHDIR(path)                   chdir(path)
85 #define CHDIR_SUCCEEDED(ret)          (ret == 0)
86 
87 #else
88 
89 #error makefsdata not supported on this platform
90 
91 #endif
92 
93 #define NEWLINE     "\r\n"
94 #define NEWLINE_LEN 2
95 
96 /* Define this here since we don't include any external C files and ports might override it */
97 #define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
98                                      x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
99 
100 /* define this to get the header variables we use to build HTTP headers */
101 #define LWIP_HTTPD_DYNAMIC_HEADERS 1
102 #define LWIP_HTTPD_SSI             1
103 #include "lwip/init.h"
104 #include "../httpd_structs.h"
105 #include "lwip/apps/fs.h"
106 
107 #include "../core/inet_chksum.c"
108 #include "../core/def.c"
109 
110 /** (Your server name here) */
111 static const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
112 static char serverIDBuffer[1024];
113 
114 /* change this to suit your MEM_ALIGNMENT */
115 #define PAYLOAD_ALIGNMENT 4
116 /* set this to 0 to prevent aligning payload */
117 #define ALIGN_PAYLOAD 1
118 /* define this to a type that has the required alignment */
119 #define PAYLOAD_ALIGN_TYPE "unsigned int"
120 static int payload_alingment_dummy_counter = 0;
121 
122 #define HEX_BYTES_PER_LINE 16
123 
124 #define MAX_PATH_LEN 256
125 
126 struct file_entry {
127   struct file_entry *next;
128   const char *filename_c;
129 };
130 
131 int process_sub(FILE *data_file, FILE *struct_file);
132 int process_file(FILE *data_file, FILE *struct_file, const char *filename);
133 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
134                            u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
135 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
136 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
137 void concat_files(const char *file1, const char *file2, const char *targetfile);
138 int check_path(char *path, size_t size);
139 static int checkSsiByFilelist(const char* filename_listfile);
140 static int ext_in_list(const char* filename, const char *ext_list);
141 static int file_to_exclude(const char* filename);
142 static int file_can_be_compressed(const char* filename);
143 
144 /* 5 bytes per char + 3 bytes per line */
145 static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
146 
147 static char curSubdir[MAX_PATH_LEN-3];
148 static char lastFileVar[MAX_PATH_LEN];
149 static char hdr_buf[4096];
150 
151 static unsigned char processSubs = 1;
152 static unsigned char includeHttpHeader = 1;
153 static unsigned char useHttp11 = 0;
154 static unsigned char supportSsi = 1;
155 static unsigned char precalcChksum = 0;
156 static unsigned char includeLastModified = 0;
157 #if MAKEFS_SUPPORT_DEFLATE
158 static unsigned char deflateNonSsiFiles = 0;
159 static size_t deflatedBytesReduced = 0;
160 static size_t overallDataBytes = 0;
161 #endif
162 static const char *exclude_list = NULL;
163 static const char *ncompress_list = NULL;
164 
165 static struct file_entry *first_file = NULL;
166 static struct file_entry *last_file = NULL;
167 
168 static char *ssi_file_buffer;
169 static char **ssi_file_lines;
170 static size_t ssi_file_num_lines;
171 
print_usage(void)172 static void print_usage(void)
173 {
174   printf(" Usage: htmlgen [targetdir] [-s] [-e] [-11] [-nossi] [-ssi:<filename>] [-c] [-f:<filename>] [-m] [-svr:<name>] [-x:<ext_list>] [-xc:<ext_list>" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
175   printf("   targetdir: relative or absolute path to files to convert" NEWLINE);
176   printf("   switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
177   printf("   switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
178   printf("   switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
179   printf("   switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
180   printf("   switch -ssi: ssi filename (ssi support controlled by file list, not by extension)" NEWLINE);
181   printf("   switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
182   printf("   switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
183   printf("   switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
184   printf("   switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
185   printf("   switch -x: comma separated list of extensions of files to exclude (e.g., -x:json,txt)" NEWLINE);
186   printf("   switch -xc: comma separated list of extensions of files to not compress (e.g., -xc:mp3,jpg)" NEWLINE);
187 #if MAKEFS_SUPPORT_DEFLATE
188   printf("   switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
189   printf("                 ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
190 #endif
191   printf("   if targetdir not specified, htmlgen will attempt to" NEWLINE);
192   printf("   process files in subdirectory 'fs'" NEWLINE);
193 }
194 
main(int argc,char * argv[])195 int main(int argc, char *argv[])
196 {
197   char path[MAX_PATH_LEN];
198   char appPath[MAX_PATH_LEN];
199   FILE *data_file;
200   FILE *struct_file;
201   int filesProcessed;
202   int i;
203   char targetfile[MAX_PATH_LEN];
204   strcpy(targetfile, "fsdata.c");
205 
206   memset(path, 0, sizeof(path));
207   memset(appPath, 0, sizeof(appPath));
208 
209   printf(NEWLINE " makefsdata v" LWIP_VERSION_STRING " - HTML to C source converter" NEWLINE);
210   printf("     by Jim Pettinato               - circa 2003 " NEWLINE);
211   printf("     extended by Simon Goldschmidt  - 2009 " NEWLINE NEWLINE);
212 
213   LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
214 
215   strcpy(path, "fs");
216   for (i = 1; i < argc; i++) {
217     if (argv[i] == NULL) {
218       continue;
219     }
220     if (argv[i][0] == '-') {
221       if (strstr(argv[i], "-svr:") == argv[i]) {
222         snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
223         serverID = serverIDBuffer;
224         printf("Using Server-ID: \"%s\"\n", serverID);
225       } else if (!strcmp(argv[i], "-s")) {
226         processSubs = 0;
227       } else if (!strcmp(argv[i], "-e")) {
228         includeHttpHeader = 0;
229       } else if (!strcmp(argv[i], "-11")) {
230         useHttp11 = 1;
231       } else if (!strcmp(argv[i], "-nossi")) {
232         supportSsi = 0;
233       } else if (strstr(argv[i], "-ssi:") == argv[i]) {
234         const char* ssi_list_filename = &argv[i][5];
235         if (checkSsiByFilelist(ssi_list_filename)) {
236           printf("Reading list of SSI files from \"%s\"\n", ssi_list_filename);
237         } else {
238           printf("Failed to load list of SSI files from \"%s\"\n", ssi_list_filename);
239         }
240       } else if (!strcmp(argv[i], "-c")) {
241         precalcChksum = 1;
242       } else if (strstr(argv[i], "-f:") == argv[i]) {
243         strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
244         targetfile[sizeof(targetfile) - 1] = 0;
245         printf("Writing to file \"%s\"\n", targetfile);
246       } else if (!strcmp(argv[i], "-m")) {
247         includeLastModified = 1;
248       } else if (strstr(argv[i], "-defl") == argv[i]) {
249 #if MAKEFS_SUPPORT_DEFLATE
250         const char *colon = &argv[i][5];
251         if (*colon == ':') {
252           int defl_level = atoi(&colon[1]);
253           if ((colon[1] != 0) && (defl_level >= 0) && (defl_level <= 10)) {
254             deflate_level = defl_level;
255           } else {
256             printf("ERROR: deflate level must be [0..10]" NEWLINE);
257             exit(0);
258           }
259         } else {
260           /* default to highest compression */
261           deflate_level = 10;
262         }
263         deflateNonSsiFiles = 1;
264         printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
265 #else
266         printf("WARNING: Deflate support is disabled\n");
267 #endif
268       } else if (strstr(argv[i], "-x:") == argv[i]) {
269         exclude_list = &argv[i][3];
270         printf("Excluding files with extensions %s" NEWLINE, exclude_list);
271       } else if (strstr(argv[i], "-xc:") == argv[i]) {
272         ncompress_list = &argv[i][4];
273         printf("Skipping compression for files with extensions %s" NEWLINE, ncompress_list);
274       } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
275         print_usage();
276         exit(0);
277       }
278     } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
279       print_usage();
280       exit(0);
281     } else {
282       strncpy(path, argv[i], sizeof(path) - 1);
283       path[sizeof(path) - 1] = 0;
284     }
285   }
286 
287   if (!check_path(path, sizeof(path))) {
288     printf("Invalid path: \"%s\"." NEWLINE, path);
289     exit(-1);
290   }
291 
292   if(!GETCWD_SUCCEEDED(GETCWD(appPath, MAX_PATH_LEN))) {
293     printf("Unable to get current dir." NEWLINE);
294     exit(-1);
295   }
296   /* if command line param or subdir named 'fs' not found spout usage verbiage */
297   if (!CHDIR_SUCCEEDED(CHDIR(path))) {
298     /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
299     printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
300     print_usage();
301     exit(-1);
302   }
303   if(!CHDIR_SUCCEEDED(CHDIR(appPath))) {
304     printf("Invalid path: \"%s\"." NEWLINE, appPath);
305     exit(-1);
306   }
307 
308   printf("HTTP %sheader will %s statically included." NEWLINE,
309          (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
310          (includeHttpHeader ? "be" : "not be"));
311 
312   curSubdir[0] = '\0'; /* start off in web page's root directory - relative paths */
313   printf("  Processing all files in directory %s", path);
314   if (processSubs) {
315     printf(" and subdirectories..." NEWLINE NEWLINE);
316   } else {
317     printf("..." NEWLINE NEWLINE);
318   }
319 
320   data_file = fopen("fsdata.tmp", "wb");
321   if (data_file == NULL) {
322     printf("Failed to create file \"fsdata.tmp\"\n");
323     exit(-1);
324   }
325   struct_file = fopen("fshdr.tmp", "wb");
326   if (struct_file == NULL) {
327     printf("Failed to create file \"fshdr.tmp\"\n");
328     fclose(data_file);
329     exit(-1);
330   }
331 
332   if(!CHDIR_SUCCEEDED(CHDIR(path))) {
333     printf("Invalid path: \"%s\"." NEWLINE, path);
334     exit(-1);
335   }
336 
337   fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
338   fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
339 
340   fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
341   /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
342   fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
343   /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
344   fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
345 
346   /* define alignment defines */
347 #if ALIGN_PAYLOAD
348   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);
349 #endif
350   fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE"  NEWLINE "#define FSDATA_ALIGN_PRE"  NEWLINE "#endif" NEWLINE);
351   fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
352 #if ALIGN_PAYLOAD
353   fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
354 #endif
355 
356   sprintf(lastFileVar, "NULL");
357 
358   filesProcessed = process_sub(data_file, struct_file);
359 
360   /* data_file now contains all of the raw data.. now append linked list of
361    * file header structs to allow embedded app to search for a file name */
362   fprintf(data_file, NEWLINE NEWLINE);
363   fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
364   fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
365 
366   fclose(data_file);
367   fclose(struct_file);
368 
369   if(!CHDIR_SUCCEEDED(CHDIR(appPath))) {
370     printf("Invalid path: \"%s\"." NEWLINE, appPath);
371     exit(-1);
372   }
373 
374   /* append struct_file to data_file */
375   printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
376   concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
377 
378   /* if succeeded, delete the temporary files */
379   if (remove("fsdata.tmp") != 0) {
380     printf("Warning: failed to delete fsdata.tmp\n");
381   }
382   if (remove("fshdr.tmp") != 0) {
383     printf("Warning: failed to delete fshdr.tmp\n");
384   }
385 
386   printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
387 #if MAKEFS_SUPPORT_DEFLATE
388   if (deflateNonSsiFiles) {
389     printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
390            (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced * 100.0) / overallDataBytes));
391   }
392 #endif
393   printf(NEWLINE);
394 
395   while (first_file != NULL) {
396     struct file_entry *fe = first_file;
397     first_file = fe->next;
398     free(fe);
399   }
400 
401   if (ssi_file_buffer) {
402     free(ssi_file_buffer);
403   }
404   if (ssi_file_lines) {
405     free(ssi_file_lines);
406   }
407 
408   return 0;
409 }
410 
check_path(char * path,size_t size)411 int check_path(char *path, size_t size)
412 {
413   size_t slen;
414   if (path[0] == 0) {
415     /* empty */
416     return 0;
417   }
418   slen = strlen(path);
419   if (slen >= size) {
420     /* not NULL-terminated */
421     return 0;
422   }
423   while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
424     /* path should not end with trailing backslash */
425     path[slen] = 0;
426     slen--;
427   }
428   if (slen == 0) {
429     return 0;
430   }
431   return 1;
432 }
433 
copy_file(const char * filename_in,FILE * fout)434 static void copy_file(const char *filename_in, FILE *fout)
435 {
436   FILE *fin;
437   size_t len;
438   void *buf;
439   fin = fopen(filename_in, "rb");
440   if (fin == NULL) {
441     printf("Failed to open file \"%s\"\n", filename_in);
442     exit(-1);
443   }
444   buf = malloc(COPY_BUFSIZE);
445   while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
446     fwrite(buf, 1, len, fout);
447   }
448   free(buf);
449   fclose(fin);
450 }
451 
concat_files(const char * file1,const char * file2,const char * targetfile)452 void concat_files(const char *file1, const char *file2, const char *targetfile)
453 {
454   FILE *fout;
455   fout = fopen(targetfile, "wb");
456   if (fout == NULL) {
457     printf("Failed to open file \"%s\"\n", targetfile);
458     exit(-1);
459   }
460   copy_file(file1, fout);
461   copy_file(file2, fout);
462   fclose(fout);
463 }
464 
process_sub(FILE * data_file,FILE * struct_file)465 int process_sub(FILE *data_file, FILE *struct_file)
466 {
467   tinydir_dir dir;
468   int filesProcessed = 0;
469 
470   if (processSubs) {
471     /* process subs recursively */
472     size_t sublen = strlen(curSubdir);
473     size_t freelen = sizeof(curSubdir) - sublen - 1;
474     int ret;
475     LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
476 
477     ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
478 
479     if (ret == 0) {
480       unsigned int i;
481       for (i = 0; i < dir.n_files; i++) {
482         tinydir_file file;
483 
484         ret = tinydir_readfile_n(&dir, &file, i);
485 
486         if (ret == 0) {
487 #if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
488           size_t num_char_converted;
489           char currName[256];
490           wcstombs_s(&num_char_converted, currName, sizeof(currName), file.name, sizeof(currName));
491 #else
492           const char *currName = file.name;
493 #endif
494 
495           if (currName[0] == '.') {
496             continue;
497           }
498           if (!file.is_dir) {
499             continue;
500           }
501           if (freelen > 0) {
502             if(!CHDIR_SUCCEEDED(CHDIR(currName))) {
503               printf("Invalid path: \"%s\"." NEWLINE, currName);
504               exit(-1);
505             }
506             strncat(curSubdir, "/", freelen);
507             strncat(curSubdir, currName, freelen - 1);
508             curSubdir[sizeof(curSubdir) - 1] = 0;
509             printf("processing subdirectory %s/..." NEWLINE, curSubdir);
510             filesProcessed += process_sub(data_file, struct_file);
511             if(!CHDIR_SUCCEEDED(CHDIR(".."))) {
512               printf("Unable to get back to parent dir of: \"%s\"." NEWLINE, currName);
513               exit(-1);
514             }
515             curSubdir[sublen] = 0;
516           } else {
517             printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, currName);
518           }
519         }
520       }
521     }
522 
523     ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
524     if (ret == 0) {
525       unsigned int i;
526       for (i = 0; i < dir.n_files; i++) {
527         tinydir_file file;
528 
529         ret = tinydir_readfile_n(&dir, &file, i);
530 
531         if (ret == 0) {
532           if (!file.is_dir) {
533 #if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
534             size_t num_char_converted;
535             char curName[256];
536             wcstombs_s(&num_char_converted, curName, sizeof(curName), file.name, sizeof(curName));
537 #else
538             const char *curName = file.name;
539 #endif
540 
541             if (strcmp(curName, "fsdata.tmp") == 0) {
542               continue;
543             }
544             if (strcmp(curName, "fshdr.tmp") == 0) {
545               continue;
546             }
547             if (file_to_exclude(curName)) {
548               printf("skipping %s/%s by exclude list (-x option)..." NEWLINE, curSubdir, curName);
549               continue;
550             }
551 
552             printf("processing %s/%s..." NEWLINE, curSubdir, curName);
553 
554             if (process_file(data_file, struct_file, curName) < 0) {
555               printf(NEWLINE "Error... aborting" NEWLINE);
556               return -1;
557             }
558             filesProcessed++;
559           }
560         }
561       }
562     }
563   }
564 
565   return filesProcessed;
566 }
567 
get_file_data(const char * filename,int * file_size,int can_be_compressed,int * is_compressed)568 static u8_t *get_file_data(const char *filename, int *file_size, int can_be_compressed, int *is_compressed)
569 {
570   FILE *inFile;
571   size_t fsize = 0;
572   u8_t *buf;
573   size_t r;
574   int rs;
575   LWIP_UNUSED_ARG(r); /* for LWIP_NOASSERT */
576   inFile = fopen(filename, "rb");
577   if (inFile == NULL) {
578     printf("Failed to open file \"%s\"\n", filename);
579     exit(-1);
580   }
581   fseek(inFile, 0, SEEK_END);
582   rs = ftell(inFile);
583   if (rs < 0) {
584     printf("ftell failed with %d\n", errno);
585     exit(-1);
586   }
587   fsize = (size_t)rs;
588   fseek(inFile, 0, SEEK_SET);
589   buf = (u8_t *)malloc(fsize);
590   LWIP_ASSERT("buf != NULL", buf != NULL);
591   r = fread(buf, 1, fsize, inFile);
592   LWIP_ASSERT("r == fsize", r == fsize);
593   *file_size = fsize;
594   *is_compressed = 0;
595 #if MAKEFS_SUPPORT_DEFLATE
596   overallDataBytes += fsize;
597   if (deflateNonSsiFiles) {
598     if (can_be_compressed) {
599       if (fsize < OUT_BUF_SIZE) {
600         u8_t *ret_buf;
601 #ifndef MAKEFS_SUPPORT_DEFLATE_ZLIB
602         tdefl_status status;
603 #else /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
604         int status;
605 #endif /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
606         size_t in_bytes = fsize;
607         size_t out_bytes = OUT_BUF_SIZE;
608         const void *next_in = buf;
609         void *next_out = s_outbuf;
610         memset(s_outbuf, 0, sizeof(s_outbuf));
611 #ifndef MAKEFS_SUPPORT_DEFLATE_ZLIB
612         /* 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). */
613         mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
614         if (!deflate_level) {
615           comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
616         }
617         status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
618         if (status != TDEFL_STATUS_OKAY) {
619           printf("tdefl_init() failed!\n");
620           exit(-1);
621         }
622         status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
623         if (status != TDEFL_STATUS_DONE) {
624           printf("deflate failed: %d\n", status);
625           exit(-1);
626         }
627 #else /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
628         status = compress2(next_out, &out_bytes, next_in, in_bytes, deflate_level);
629         if (status != Z_OK) {
630           printf("deflate failed: %d\n", status);
631           exit(-1);
632         }
633 #endif /*  MAKEFS_SUPPORT_DEFLATE_ZLIB */
634         LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
635         if (out_bytes < fsize) {
636           ret_buf = (u8_t *)malloc(out_bytes);
637           LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
638           memcpy(ret_buf, s_outbuf, out_bytes);
639           {
640             /* sanity-check compression be inflating and comparing to the original */
641             size_t dec_in_bytes = out_bytes;
642             size_t dec_out_bytes = OUT_BUF_SIZE;
643             next_out = s_checkbuf;
644             memset(s_checkbuf, 0, sizeof(s_checkbuf));
645 #ifndef MAKEFS_SUPPORT_DEFLATE_ZLIB
646             tinfl_status dec_status;
647             tinfl_decompressor inflator;
648 
649             tinfl_init(&inflator);
650             dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
651             LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
652 #else /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
653             int dec_status;
654             dec_status = uncompress2 (s_checkbuf, &dec_out_bytes, ret_buf, &dec_in_bytes);
655             LWIP_ASSERT("tinfl_decompress failed", dec_status == Z_OK);
656 #endif /* MAKEFS_SUPPORT_DEFLATE_ZLIB */
657             LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
658             LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
659           }
660           /* free original buffer, use compressed data + size */
661           free(buf);
662           buf = ret_buf;
663           *file_size = out_bytes;
664           printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes * 100.0) / fsize));
665           deflatedBytesReduced += (size_t)(fsize - out_bytes);
666           *is_compressed = 1;
667         } else {
668           printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
669         }
670       } else {
671         printf(" - uncompressed: (file is larger than deflate buffer)" NEWLINE);
672       }
673     } else {
674       printf(" - cannot be compressed" NEWLINE);
675     }
676   }
677 #else
678   LWIP_UNUSED_ARG(can_be_compressed);
679 #endif
680   fclose(inFile);
681   return buf;
682 }
683 
process_file_data(FILE * data_file,u8_t * file_data,size_t file_size)684 static void process_file_data(FILE *data_file, u8_t *file_data, size_t file_size)
685 {
686   size_t written, i, src_off = 0;
687   size_t off = 0;
688   LWIP_UNUSED_ARG(written); /* for LWIP_NOASSERT */
689   for (i = 0; i < file_size; i++) {
690     LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
691     sprintf(&file_buffer_c[off], "0x%02x,", file_data[i]);
692     off += 5;
693     if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
694       LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
695       memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
696       off += NEWLINE_LEN;
697     }
698     if (off + 20 >= sizeof(file_buffer_c)) {
699       written = fwrite(file_buffer_c, 1, off, data_file);
700       LWIP_ASSERT("written == off", written == off);
701       off = 0;
702     }
703   }
704   written = fwrite(file_buffer_c, 1, off, data_file);
705   LWIP_ASSERT("written == off", written == off);
706 }
707 
write_checksums(FILE * struct_file,const char * varname,u16_t hdr_len,u16_t hdr_chksum,const u8_t * file_data,size_t file_size)708 static int write_checksums(FILE *struct_file, const char *varname,
709                            u16_t hdr_len, u16_t hdr_chksum, const u8_t *file_data, size_t file_size)
710 {
711   int chunk_size = TCP_MSS;
712   int offset, src_offset;
713   size_t len;
714   int i = 0;
715 #if LWIP_TCP_TIMESTAMPS
716   /* when timestamps are used, usable space is 12 bytes less per segment */
717   chunk_size -= 12;
718 #endif
719 
720   fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
721   fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
722 
723   if (hdr_len > 0) {
724     /* add checksum for HTTP header */
725     fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
726     i++;
727   }
728   src_offset = 0;
729   for (offset = hdr_len; ; offset += len) {
730     unsigned short chksum;
731     const void *data = (const void *)&file_data[src_offset];
732     len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
733     if (len == 0) {
734       break;
735     }
736     chksum = ~inet_chksum(data, (u16_t)len);
737     /* add checksum for data */
738     fprintf(struct_file, "{%d, 0x%04x, %"SZT_F"}," NEWLINE, offset, chksum, len);
739     i++;
740   }
741   fprintf(struct_file, "};" NEWLINE);
742   fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
743   return i;
744 }
745 
is_valid_char_for_c_var(char x)746 static int is_valid_char_for_c_var(char x)
747 {
748   if (((x >= 'A') && (x <= 'Z')) ||
749       ((x >= 'a') && (x <= 'z')) ||
750       ((x >= '0') && (x <= '9')) ||
751       (x == '_')) {
752     return 1;
753   }
754   return 0;
755 }
756 
fix_filename_for_c(char * qualifiedName,size_t max_len)757 static void fix_filename_for_c(char *qualifiedName, size_t max_len)
758 {
759   struct file_entry *f;
760   size_t len = strlen(qualifiedName);
761   char *new_name = (char *)malloc(len + 2);
762   int filename_ok;
763   int cnt = 0;
764   size_t i;
765   if (len + 3 == max_len) {
766     printf("File name too long: \"%s\"\n", qualifiedName);
767     exit(-1);
768   }
769   strcpy(new_name, qualifiedName);
770   for (i = 0; i < len; i++) {
771     if (!is_valid_char_for_c_var(new_name[i])) {
772       new_name[i] = '_';
773     }
774   }
775   do {
776     filename_ok = 1;
777     for (f = first_file; f != NULL; f = f->next) {
778       if (!strcmp(f->filename_c, new_name)) {
779         filename_ok = 0;
780         cnt++;
781         /* try next unique file name */
782         sprintf(&new_name[len], "%d", cnt);
783         break;
784       }
785     }
786   } while (!filename_ok && (cnt < 999));
787   if (!filename_ok) {
788     printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
789     exit(-1);
790   }
791   strcpy(qualifiedName, new_name);
792   free(new_name);
793 }
794 
register_filename(const char * qualifiedName)795 static void register_filename(const char *qualifiedName)
796 {
797   struct file_entry *fe = (struct file_entry *)malloc(sizeof(struct file_entry));
798   fe->filename_c = strdup(qualifiedName);
799   fe->next = NULL;
800   if (first_file == NULL) {
801     first_file = last_file = fe;
802   } else {
803     last_file->next = fe;
804     last_file = fe;
805   }
806 }
807 
checkSsiByFilelist(const char * filename_listfile)808 static int checkSsiByFilelist(const char* filename_listfile)
809 {
810   FILE *f = fopen(filename_listfile, "r");
811   if (f != NULL) {
812     char *buf;
813     long rs;
814     size_t fsize, readcount;
815     size_t i, l, num_lines;
816     char **lines;
817     int state;
818 
819     fseek(f, 0, SEEK_END);
820     rs = ftell(f);
821     if (rs < 0) {
822       printf("ftell failed with %d\n", errno);
823       fclose(f);
824       return 0;
825     }
826     fsize = (size_t)rs;
827     fseek(f, 0, SEEK_SET);
828     buf = (char*)malloc(fsize);
829     if (!buf) {
830       printf("failed to allocate ssi file buffer\n");
831       fclose(f);
832       return 0;
833     }
834     memset(buf, 0, fsize);
835     readcount = fread(buf, 1, fsize, f);
836     fclose(f);
837     if ((readcount > fsize) || !readcount) {
838       printf("failed to read data from ssi file\n");
839       free(buf);
840       return 0;
841     }
842 
843     /* first pass: get the number of lines (and convert newlines to '0') */
844     num_lines = 1;
845     for (i = 0; i < readcount; i++) {
846       if (buf[i] == '\n') {
847         num_lines++;
848         buf[i] = 0;
849       } else if (buf[i] == '\r') {
850         buf[i] = 0;
851       }
852     }
853     /* allocate the line pointer array */
854     lines = (char**)malloc(sizeof(char*) * num_lines);
855     if (!lines) {
856       printf("failed to allocate ssi line buffer\n");
857       free(buf);
858       return 0;
859     }
860     memset(lines, 0, sizeof(char*) * num_lines);
861     l = 0;
862     state = 0;
863     for (i = 0; i < readcount; i++) {
864       if (state) {
865         /* waiting for null */
866         if (buf[i] == 0) {
867           state = 0;
868         }
869       } else {
870         /* waiting for beginning of new string */
871         if (buf[i] != 0) {
872           LWIP_ASSERT("lines array overflow", l < num_lines);
873           lines[l] = &buf[i];
874           state = 1;
875           l++;
876         }
877       }
878     }
879     LWIP_ASSERT("lines array overflow", l < num_lines);
880 
881     ssi_file_buffer = buf;
882     ssi_file_lines = lines;
883     ssi_file_num_lines = l;
884   }
885   return 0;
886 }
887 
is_ssi_file(const char * filename)888 static int is_ssi_file(const char *filename)
889 {
890   if (supportSsi) {
891     if (ssi_file_buffer) {
892       /* compare by list */
893       size_t i;
894       int ret = 0;
895       /* build up the relative path to this file */
896       size_t sublen = strlen(curSubdir);
897       size_t freelen = sizeof(curSubdir) - sublen - 1;
898       strncat(curSubdir, "/", freelen);
899       strncat(curSubdir, filename, freelen - 1);
900       curSubdir[sizeof(curSubdir) - 1] = 0;
901       for (i = 0; i < ssi_file_num_lines; i++) {
902         const char *listed_file = ssi_file_lines[i];
903         /* compare without the leading '/' */
904         if (!strcmp(&curSubdir[1], listed_file)) {
905           ret = 1;
906         }
907       }
908       curSubdir[sublen] = 0;
909       return ret;
910 #if LWIP_HTTPD_SSI_BY_FILE_EXTENSION
911     } else {
912       /* check file extension */
913       size_t loop;
914       for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
915         if (strstr(filename, g_pcSSIExtensions[loop])) {
916           return 1;
917         }
918       }
919 #endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */
920     }
921   }
922   return 0;
923 }
924 
ext_in_list(const char * filename,const char * ext_list)925 static int ext_in_list(const char* filename, const char *ext_list)
926 {
927   int found = 0;
928   const char *ext = ext_list;
929   if (ext_list == NULL) {
930     return 0;
931   }
932   while(*ext != '\0') {
933     const char *comma = strchr(ext, ',');
934     size_t ext_size;
935     size_t filename_size = strlen(filename);
936     if (comma == NULL) {
937       comma = strchr(ext, '\0');
938     }
939     ext_size = comma - ext;
940     if ((filename[filename_size - ext_size - 1] == '.') &&
941       !strncmp(&filename[filename_size - ext_size], ext, ext_size)) {
942         found = 1;
943         break;
944     }
945     ext = comma + 1;
946   }
947 
948   return found;
949 }
950 
file_to_exclude(const char * filename)951 static int file_to_exclude(const char *filename)
952 {
953     return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
954 }
955 
file_can_be_compressed(const char * filename)956 static int file_can_be_compressed(const char *filename)
957 {
958     return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
959 }
960 
process_file(FILE * data_file,FILE * struct_file,const char * filename)961 int process_file(FILE *data_file, FILE *struct_file, const char *filename)
962 {
963   char varname[MAX_PATH_LEN];
964   int i = 0;
965   char qualifiedName[MAX_PATH_LEN];
966   int file_size;
967   u16_t http_hdr_chksum = 0;
968   u16_t http_hdr_len = 0;
969   int chksum_count = 0;
970   u8_t flags = 0;
971   u8_t has_content_len;
972   u8_t *file_data;
973   int is_ssi;
974   int can_be_compressed;
975   int is_compressed = 0;
976   int flags_printed;
977 
978   /* create qualified name (@todo: prepend slash or not?) */
979   snprintf(qualifiedName, sizeof(qualifiedName), "%s/%s", curSubdir, filename);
980   /* create C variable name */
981   strncpy(varname, qualifiedName, sizeof(varname));
982   /* convert slashes & dots to underscores */
983   fix_filename_for_c(varname, MAX_PATH_LEN);
984   register_filename(varname);
985 #if ALIGN_PAYLOAD
986   /* to force even alignment of array, type 1 */
987   fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
988   fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
989   fprintf(data_file, "#endif" NEWLINE);
990 #endif /* ALIGN_PAYLOAD */
991   fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
992   /* encode source file name (used by file system, not returned to browser) */
993   fprintf(data_file, "/* %s (%"SZT_F" chars) */" NEWLINE, qualifiedName, strlen(qualifiedName) + 1);
994   file_put_ascii(data_file, qualifiedName, strlen(qualifiedName) + 1, &i);
995 #if ALIGN_PAYLOAD
996   /* pad to even number of bytes to assure payload is on aligned boundary */
997   while (i % PAYLOAD_ALIGNMENT != 0) {
998     fprintf(data_file, "0x%02x,", 0);
999     i++;
1000   }
1001 #endif /* ALIGN_PAYLOAD */
1002   fprintf(data_file, NEWLINE);
1003 
1004   is_ssi = is_ssi_file(filename);
1005   if (is_ssi) {
1006     flags |= FS_FILE_FLAGS_SSI;
1007   }
1008   has_content_len = !is_ssi;
1009   can_be_compressed = includeHttpHeader && !is_ssi && file_can_be_compressed(filename);
1010   file_data = get_file_data(filename, &file_size, can_be_compressed, &is_compressed);
1011   if (includeHttpHeader) {
1012     file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
1013     flags |= FS_FILE_FLAGS_HEADER_INCLUDED;
1014     if (has_content_len) {
1015       flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
1016       if (useHttp11) {
1017         flags |= FS_FILE_FLAGS_HEADER_HTTPVER_1_1;
1018       }
1019     }
1020   }
1021   if (precalcChksum) {
1022     chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
1023   }
1024 
1025   /* build declaration of struct fsdata_file in temp file */
1026   fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
1027   fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
1028   fprintf(struct_file, "data_%s," NEWLINE, varname);
1029   fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
1030   fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
1031 
1032   flags_printed = 0;
1033   if (flags & FS_FILE_FLAGS_HEADER_INCLUDED) {
1034     fputs("FS_FILE_FLAGS_HEADER_INCLUDED", struct_file);
1035     flags_printed = 1;
1036   }
1037   if (flags & FS_FILE_FLAGS_HEADER_PERSISTENT) {
1038     if (flags_printed) {
1039       fputs(" | ", struct_file);
1040     }
1041     fputs("FS_FILE_FLAGS_HEADER_PERSISTENT", struct_file);
1042     flags_printed = 1;
1043   }
1044   if (flags & FS_FILE_FLAGS_HEADER_HTTPVER_1_1) {
1045     if (flags_printed) {
1046       fputs(" | ", struct_file);
1047     }
1048     fputs("FS_FILE_FLAGS_HEADER_HTTPVER_1_1", struct_file);
1049     flags_printed = 1;
1050   }
1051   if (flags & FS_FILE_FLAGS_SSI) {
1052     if (flags_printed) {
1053       fputs(" | ", struct_file);
1054     }
1055     fputs("FS_FILE_FLAGS_SSI", struct_file);
1056     flags_printed = 1;
1057   }
1058   if (!flags_printed) {
1059     fputs("0", struct_file);
1060   }
1061   fputs("," NEWLINE, struct_file);
1062   if (precalcChksum) {
1063     fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
1064     fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
1065     fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
1066   }
1067   fprintf(struct_file, "}};" NEWLINE NEWLINE);
1068   strcpy(lastFileVar, varname);
1069 
1070   /* write actual file contents */
1071   i = 0;
1072   fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
1073   process_file_data(data_file, file_data, file_size);
1074   fprintf(data_file, "};" NEWLINE NEWLINE);
1075   free(file_data);
1076   return 0;
1077 }
1078 
file_write_http_header(FILE * data_file,const char * filename,int file_size,u16_t * http_hdr_len,u16_t * http_hdr_chksum,u8_t provide_content_len,int is_compressed)1079 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
1080                            u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
1081 {
1082   int i = 0;
1083   int response_type = HTTP_HDR_OK;
1084   const char *file_type;
1085   const char *cur_string;
1086   size_t cur_len;
1087   int written = 0;
1088   size_t hdr_len = 0;
1089   u16_t acc;
1090   const char *file_ext;
1091   size_t j;
1092   u8_t provide_last_modified = includeLastModified;
1093 
1094   memset(hdr_buf, 0, sizeof(hdr_buf));
1095 
1096   if (useHttp11) {
1097     response_type = HTTP_HDR_OK_11;
1098   }
1099 
1100   fprintf(data_file, NEWLINE "/* HTTP header */");
1101   if (strstr(filename, "404.") == filename) {
1102     response_type = HTTP_HDR_NOT_FOUND;
1103     if (useHttp11) {
1104       response_type = HTTP_HDR_NOT_FOUND_11;
1105     }
1106   } else if (strstr(filename, "400.") == filename) {
1107     response_type = HTTP_HDR_BAD_REQUEST;
1108     if (useHttp11) {
1109       response_type = HTTP_HDR_BAD_REQUEST_11;
1110     }
1111   } else if (strstr(filename, "501.") == filename) {
1112     response_type = HTTP_HDR_NOT_IMPL;
1113     if (useHttp11) {
1114       response_type = HTTP_HDR_NOT_IMPL_11;
1115     }
1116   }
1117   cur_string = g_psHTTPHeaderStrings[response_type];
1118   cur_len = strlen(cur_string);
1119   fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1120   written += file_put_ascii(data_file, cur_string, cur_len, &i);
1121   i = 0;
1122   if (precalcChksum) {
1123     memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1124     hdr_len += cur_len;
1125   }
1126 
1127   cur_string = serverID;
1128   cur_len = strlen(cur_string);
1129   fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1130   written += file_put_ascii(data_file, cur_string, cur_len, &i);
1131   i = 0;
1132   if (precalcChksum) {
1133     memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1134     hdr_len += cur_len;
1135   }
1136 
1137   file_ext = filename;
1138   if (file_ext != NULL) {
1139     while (strstr(file_ext, ".") != NULL) {
1140       file_ext = strstr(file_ext, ".");
1141       file_ext++;
1142     }
1143   }
1144   if ((file_ext == NULL) || (*file_ext == 0)) {
1145     printf("failed to get extension for file \"%s\", using default.\n", filename);
1146     file_type = HTTP_HDR_DEFAULT_TYPE;
1147   } else {
1148     file_type = NULL;
1149     for (j = 0; j < NUM_HTTP_HEADERS; j++) {
1150       if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
1151         file_type = g_psHTTPHeaders[j].content_type;
1152         break;
1153       }
1154     }
1155     if (file_type == NULL) {
1156       printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
1157       file_type = HTTP_HDR_DEFAULT_TYPE;
1158     }
1159   }
1160 
1161   /* Content-Length is used for persistent connections in HTTP/1.1 but also for
1162      download progress in older versions
1163      @todo: just use a big-enough buffer and let the HTTPD send spaces? */
1164   if (provide_content_len) {
1165     char intbuf[MAX_PATH_LEN];
1166     int content_len = file_size;
1167     memset(intbuf, 0, sizeof(intbuf));
1168     cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
1169     cur_len = strlen(cur_string);
1170     fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, content_len, cur_len + 2);
1171     written += file_put_ascii(data_file, cur_string, cur_len, &i);
1172     if (precalcChksum) {
1173       memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1174       hdr_len += cur_len;
1175     }
1176 
1177     lwip_itoa(intbuf, sizeof(intbuf), content_len);
1178     strcat(intbuf, "\r\n");
1179     cur_len = strlen(intbuf);
1180     written += file_put_ascii(data_file, intbuf, cur_len, &i);
1181     i = 0;
1182     if (precalcChksum) {
1183       memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
1184       hdr_len += cur_len;
1185     }
1186   }
1187   if (provide_last_modified) {
1188     char modbuf[256];
1189     struct stat stat_data;
1190     struct tm *t;
1191     memset(modbuf, 0, sizeof(modbuf));
1192     memset(&stat_data, 0, sizeof(stat_data));
1193     cur_string = modbuf;
1194     strcpy(modbuf, "Last-Modified: ");
1195     if (stat(filename, &stat_data) != 0) {
1196       printf("stat(%s) failed with error %d\n", filename, errno);
1197       exit(-1);
1198     }
1199     t = gmtime(&stat_data.st_mtime);
1200     if (t == NULL) {
1201       printf("gmtime() failed with error %d\n", errno);
1202       exit(-1);
1203     }
1204     strftime(&modbuf[15], sizeof(modbuf) - 15, "%a, %d %b %Y %H:%M:%S GMT", t);
1205     cur_len = strlen(cur_string);
1206     fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, cur_len + 2);
1207     written += file_put_ascii(data_file, cur_string, cur_len, &i);
1208     if (precalcChksum) {
1209       memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1210       hdr_len += cur_len;
1211     }
1212 
1213     modbuf[0] = 0;
1214     strcat(modbuf, "\r\n");
1215     cur_len = strlen(modbuf);
1216     written += file_put_ascii(data_file, modbuf, cur_len, &i);
1217     i = 0;
1218     if (precalcChksum) {
1219       memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
1220       hdr_len += cur_len;
1221     }
1222   }
1223 
1224   /* HTTP/1.1 implements persistent connections */
1225   if (useHttp11) {
1226     if (provide_content_len) {
1227       cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
1228     } else {
1229       /* no Content-Length available, so a persistent connection is no possible
1230          because the client does not know the data length */
1231       cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
1232     }
1233     cur_len = strlen(cur_string);
1234     fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1235     written += file_put_ascii(data_file, cur_string, cur_len, &i);
1236     i = 0;
1237     if (precalcChksum) {
1238       memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1239       hdr_len += cur_len;
1240     }
1241   }
1242 
1243 #if MAKEFS_SUPPORT_DEFLATE
1244   if (is_compressed) {
1245     /* tell the client about the deflate encoding */
1246     LWIP_ASSERT("error", deflateNonSsiFiles);
1247     cur_string = "Content-Encoding: deflate\r\n";
1248     cur_len = strlen(cur_string);
1249     fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
1250     written += file_put_ascii(data_file, cur_string, cur_len, &i);
1251     i = 0;
1252   }
1253 #else
1254   LWIP_UNUSED_ARG(is_compressed);
1255 #endif
1256 
1257   /* write content-type, ATTENTION: this includes the double-CRLF! */
1258   cur_string = file_type;
1259   cur_len = strlen(cur_string);
1260   fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1261   written += file_put_ascii(data_file, cur_string, cur_len, &i);
1262   i = 0;
1263 
1264   /* ATTENTION: headers are done now (double-CRLF has been written!) */
1265 
1266   if (precalcChksum) {
1267     LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
1268     memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1269     hdr_len += cur_len;
1270 
1271     LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
1272     acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
1273     *http_hdr_len = (u16_t)hdr_len;
1274     *http_hdr_chksum = acc;
1275   }
1276 
1277   return written;
1278 }
1279 
file_put_ascii(FILE * file,const char * ascii_string,int len,int * i)1280 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i)
1281 {
1282   int x;
1283   for (x = 0; x < len; x++) {
1284     unsigned char cur = ascii_string[x];
1285     fprintf(file, "0x%02x,", cur);
1286     if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1287       fprintf(file, NEWLINE);
1288     }
1289   }
1290   return len;
1291 }
1292 
s_put_ascii(char * buf,const char * ascii_string,int len,int * i)1293 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
1294 {
1295   int x;
1296   int idx = 0;
1297   for (x = 0; x < len; x++) {
1298     unsigned char cur = ascii_string[x];
1299     sprintf(&buf[idx], "0x%02x,", cur);
1300     idx += 5;
1301     if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1302       sprintf(&buf[idx], NEWLINE);
1303       idx += NEWLINE_LEN;
1304     }
1305   }
1306   return len;
1307 }
1308