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