00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "klone_conf.h"
00012 #include <sys/stat.h>
00013 #ifdef HAVE_SYS_DIR
00014 #include <sys/dir.h>
00015 #endif
00016 #include <sys/types.h>
00017 #include <dirent.h>
00018 #include <stdlib.h>
00019 #include <ctype.h>
00020 #include <stdio.h>
00021 #include <fcntl.h>
00022 #ifdef HAVE_UNISTD
00023 #include <unistd.h>
00024 #endif
00025 #ifdef HAVE_GETOPT
00026 #include <getopt.h>
00027 #endif
00028 #include <u/libu.h>
00029 #include <klone/os.h>
00030 #include <klone/request.h>
00031 #include <klone/response.h>
00032 #include <klone/translat.h>
00033 #include <klone/utils.h>
00034 #include <klone/run.h>
00035 #include <klone/mime_map.h>
00036 #include <klone/version.h>
00037 #include "pm.h"
00038
00039 int facility = LOG_LOCAL0;
00040
00041
00042 enum command_e { CMD_UNKNOWN, CMD_TRANS, CMD_IMPORT };
00043
00044
00045 enum flags_e { FLAG_NONE, FLAG_VERBOSE };
00046
00047 typedef struct
00048 {
00049 char *file_in, *file_out;
00050 char *depend_out;
00051 char *uri;
00052 int verbose;
00053 char **arg;
00054 size_t narg;
00055 int cmd;
00056 char *base_uri;
00057 int encrypt;
00058 int compress;
00059 pm_t *comp_patt;
00060 pm_t *enc_patt;
00061 pm_t *excl_patt;
00062 char *key_file;
00063 io_t *iom, *iod, *ior;
00064 size_t ndir, nfile;
00065 size_t nexcl;
00066 } context_t;
00067
00068 context_t *ctx;
00069
00070 #define KL1_FILE_FMT "pg_%s.%s"
00071
00072 #define usage_if(expr) if(expr) usage();
00073
00074 static void usage(void)
00075 {
00076 static const char * us =
00077 "Usage: klone [-hvV] -c COMMAND OPTIONS ARGUMENTS\n"
00078 "Version: %s - Copyright (c) 2005-2012 KoanLogic s.r.l.\n"
00079 "All rights reserved.\n"
00080 "\n"
00081 " -h display this help\n"
00082 " -v verbose mode\n"
00083 " -V print KLone version and exit\n"
00084 " -c command command to execute (see COMMAND LIST)\n"
00085 "\n"
00086 "\n"
00087 " COMMAND LIST:\n"
00088 " import import a directory tree in the embedded filesystem\n"
00089 " translate convert a file or a dynamic web page to a C file\n"
00090 "\n"
00091 "\n"
00092 " COMMANDS SYNTAX:\n"
00093 "\n"
00094 " import OPTIONS dir\n"
00095 " -b URI base URI\n"
00096 " -x pattern exclude all files whose URI match the given pattern (*)\n"
00097 #ifdef SSL_ON
00098 " -e pattern encrypt all files whose URI match the given pattern (*)\n"
00099 " -k key_file encryption key filename\n"
00100 " (KLONE_CIPHER_KEY environ var is used if not provided)\n"
00101 #endif
00102 #ifdef HAVE_LIBZ
00103 " -z compress all compressable content (based on MIME types)\n"
00104 " -Z pattern compress all files whose URI match the given pattern (*)\n"
00105 #endif
00106 " dir directory tree path\n"
00107 "\n"
00108 " (*) may be used more then once\n"
00109 "\n"
00110 " translate OPTIONS\n"
00111 #ifdef SSL_ON
00112 " -E encrypt file content\n"
00113 #endif
00114 " -i file input file\n"
00115 #ifdef SSL_ON
00116 " -k key_file encryption key filename\n"
00117 #endif
00118 " -o file output file\n"
00119 " -u URI URI of translated page\n"
00120 #ifdef HAVE_LIBZ
00121 " -z compress file content\n"
00122 #endif
00123 "\n";
00124
00125 fprintf(stderr, us, klone_version());
00126
00127 exit(EXIT_FAILURE);
00128 }
00129
00130 static void remove_trailing_slash(char *s)
00131 {
00132 size_t len;
00133
00134 dbg_ifb (s == NULL) return;
00135
00136 len = strlen(s);
00137 if(len && s[len - 1] == '/')
00138 s[len - 1] = 0;
00139 }
00140
00141 static int parse_opt(int argc, char **argv)
00142 {
00143 int ret;
00144 char opts[512];
00145
00146 if(argc == 1)
00147 usage();
00148
00149
00150 strcpy(opts, "hvVx:b:i:o:u:c:d:");
00151
00152
00153 #ifdef SSL_ON
00154 strcat(opts, "k:e:E");
00155 #endif
00156
00157
00158 #ifdef HAVE_LIBZ
00159 strcat(opts, "zZ:");
00160 #endif
00161
00162 while((ret = getopt(argc, argv, opts)) != -1)
00163 {
00164 switch(ret)
00165 {
00166 case 'v':
00167 ctx->verbose++;
00168 break;
00169 case 'V':
00170 u_print_version_and_exit();
00171 break;
00172
00173 #ifdef SSL_ON
00174 case 'E':
00175 ctx->encrypt = 1;
00176 break;
00177 case 'e':
00178 dbg_err_if(pm_add(ctx->enc_patt, u_strdup(optarg)));
00179 break;
00180 case 'k':
00181 ctx->key_file = u_strdup(optarg);
00182 warn_err_if(ctx->key_file == NULL);
00183 break;
00184 #endif
00185
00186 #ifdef HAVE_LIBZ
00187 case 'Z':
00188 ctx->compress = 1;
00189 dbg_err_if(pm_add(ctx->comp_patt, u_strdup(optarg)));
00190 break;
00191 case 'z':
00192 ctx->compress = 1;
00193 break;
00194 #endif
00195 case 'c':
00196 if(!strcasecmp(optarg, "import"))
00197 ctx->cmd = CMD_IMPORT;
00198 else if(!strcasecmp(optarg, "translate"))
00199 ctx->cmd = CMD_TRANS;
00200 else
00201 con_err("unknown command: %s", optarg);
00202 break;
00203 case 'i':
00204 ctx->file_in = u_strdup(optarg);
00205 warn_err_if(ctx->file_in == NULL);
00206 break;
00207 case 'x':
00208 dbg_err_if(pm_add(ctx->excl_patt, u_strdup(optarg)));
00209 break;
00210 case 'b':
00211 ctx->base_uri = u_strdup(optarg);
00212 warn_err_if(ctx->base_uri == NULL);
00213
00214 if(ctx->base_uri[0] != '/')
00215 klone_die("base URI must be absolute "
00216 "(i.e. must start with a '/')");
00217
00218 remove_trailing_slash(ctx->base_uri);
00219
00220 break;
00221 case 'o':
00222 ctx->file_out = u_strdup(optarg);
00223 warn_err_if(ctx->file_out == NULL);
00224 break;
00225 case 'd':
00226 ctx->depend_out = u_strdup(optarg);
00227 warn_err_if(ctx->depend_out == NULL);
00228 break;
00229 case 'u':
00230
00231
00232 ctx->uri = u_strdup(1+optarg);
00233 warn_err_if(ctx->uri == NULL);
00234
00235 if(ctx->uri[0] != '/')
00236 klone_die("URI must be absolute (i.e. must start with a '/')");
00237
00238 remove_trailing_slash(ctx->uri);
00239
00240 break;
00241 default:
00242 case 'h':
00243 usage();
00244 }
00245 }
00246
00247 klone_die_if(ctx->cmd == 0, "missing command argument (-c)");
00248 ctx->narg = argc - optind;
00249 ctx->arg = argv + optind;
00250
00251 return 0;
00252 err:
00253 return ~0;
00254 }
00255
00256 static int set_key_from_file(trans_info_t *pti, const char *key_file)
00257 {
00258 io_t *io = NULL;
00259
00260 dbg_err_if (pti == NULL);
00261 dbg_err_if (key_file == NULL);
00262
00263 dbg_err_if(u_file_open(key_file, O_RDONLY, &io));
00264
00265 dbg_err_if(io_read(io, pti->key, CODEC_CIPHER_KEY_LEN) <= 0);
00266
00267 io_free(io);
00268
00269 return 0;
00270 err:
00271 return ~0;
00272 }
00273
00274 static int command_trans(void)
00275 {
00276 trans_info_t ti;
00277 const mime_map_t *mm;
00278 struct stat st;
00279 char *key_env;
00280 int key_found = 0;
00281
00282 if(ctx->narg != 0)
00283 usage();
00284
00285 memset(&ti, 0, sizeof(trans_info_t));
00286
00287 klone_die_if(!ctx->file_in, "input file name required (-i file)");
00288 klone_die_if(!ctx->file_out, "output file name required (-o file)");
00289 klone_die_if(!ctx->uri, "translated page URI required (-u uri)");
00290
00291 if(ctx->verbose)
00292 u_con("translating %s to %s (uri: %s)", ctx->file_in, ctx->file_out,
00293 ctx->uri);
00294
00295
00296 u_strlcpy(ti.file_in, ctx->file_in, sizeof ti.file_in);
00297
00298
00299 u_strlcpy(ti.file_out, ctx->file_out, sizeof ti.file_out);
00300
00301
00302 if(ctx->depend_out)
00303 u_strlcpy(ti.depend_out, ctx->depend_out, sizeof ti.depend_out);
00304
00305
00306 u_strlcpy(ti.uri, ctx->uri, sizeof ti.uri);
00307
00308
00309 memset(ti.key, 0, CODEC_CIPHER_KEY_BUFSZ);
00310
00311
00312 con_err_ifm(ctx->key_file && !ctx->encrypt, "-k used but -E is missing");
00313
00314
00315 key_env = getenv("KLONE_CIPHER_KEY");
00316 if(key_env && strlen(key_env))
00317 {
00318 con_err_ifm(strlen(key_env) != CODEC_CIPHER_KEY_LEN,
00319 "wrong key length; the encryption key must be %d bytes long",
00320 CODEC_CIPHER_KEY_LEN);
00321
00322 key_found = 1;
00323 memcpy(ti.key, key_env, strlen(key_env));
00324 }
00325
00326
00327 if(ctx->key_file)
00328 {
00329 key_found = 1;
00330 con_err_ifm(set_key_from_file(&ti, ctx->key_file),
00331 "error reading key file [%s]", ctx->key_file);
00332 }
00333
00334 if(ctx->encrypt)
00335 {
00336 if(!key_found)
00337 con_err("encryption key required (use -k or KLONE_CIPHER_KEY "
00338 "environ variable)");
00339 ti.encrypt = 1;
00340 }
00341
00342
00343 if((mm = u_get_mime_map(ctx->file_in)) != NULL)
00344 u_strlcpy(ti.mime_type, mm->mime_type, sizeof ti.mime_type);
00345 else
00346 u_strlcpy(ti.mime_type, "application/octet-stream",
00347 sizeof ti.mime_type);
00348
00349
00350 if(ctx->compress)
00351 ti.comp = 1;
00352
00353
00354 klone_die_if(stat(ctx->file_in, &st), "input file not found");
00355
00356 ti.file_size = st.st_size;
00357 ti.mtime = st.st_mtime;
00358
00359
00360 dbg_err_if(translate(&ti));
00361
00362 return 0;
00363 err:
00364
00365 u_remove(ti.file_out);
00366 u_con(" ");
00367 return ~0;
00368 }
00369
00370 static int is_cpp(const char *file_in)
00371 {
00372 size_t l;
00373
00374 dbg_err_if (file_in == NULL);
00375
00376 l = strlen(file_in);
00377 if(l < 4)
00378 return 0;
00379
00380
00381 if(tolower(file_in[--l]) == 'c' && tolower(file_in[--l]) == 'c')
00382 return 1;
00383
00384 err:
00385 return 0;
00386 }
00387
00388
00389 static int cb_file(struct dirent *de, const char *path , void *arg)
00390 {
00391 static const char *prefix = "$(srcdir)";
00392 const mime_map_t *mm;
00393 char uri_md5[MD5_DIGEST_BUFSZ];
00394 char file_in[U_FILENAME_MAX], uri[URI_BUFSZ], *base_uri = (char*)arg;
00395 char fullpath[U_FILENAME_MAX];
00396 const char *ext;
00397 int is_a_script, enc = 0, zip = 0;
00398
00399 dbg_err_if (de == NULL);
00400 dbg_err_if (path == NULL);
00401 dbg_err_if (arg == NULL);
00402
00403 dbg_err_if(u_snprintf(fullpath, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00404
00405
00406 dbg_err_if(translate_makefile_filepath(fullpath, prefix, file_in,
00407 sizeof(file_in)));
00408
00409
00410 dbg_err_if(u_snprintf(uri, URI_BUFSZ, "%s/%s", base_uri, de->d_name));
00411 dbg_err_if(u_md5(uri, strlen(uri), uri_md5));
00412
00413
00414 if(!pm_is_empty(ctx->excl_patt) && pm_match(ctx->excl_patt, uri))
00415 {
00416 if(ctx->verbose)
00417 u_con("%s skipped", uri);
00418
00419 ctx->nexcl++;
00420
00421 return 0;
00422 }
00423
00424 is_a_script = translate_is_a_script(de->d_name);
00425
00426 ctx->nfile++;
00427
00428
00429 if(!pm_is_empty(ctx->enc_patt) && pm_match(ctx->enc_patt, uri))
00430 enc = 1;
00431
00432 if(ctx->compress)
00433 {
00434
00435 if(!pm_is_empty(ctx->comp_patt))
00436 {
00437 if(pm_match(ctx->comp_patt, uri))
00438 zip = 1;
00439 } else {
00440
00441 if((mm = u_get_mime_map(uri)) != NULL)
00442 zip = mm->comp;
00443 }
00444 }
00445
00446
00447 if(ctx->verbose == 1)
00448 u_con("%s (encrypted: %s, compressed: %s)",
00449 uri, enc ? "yes" : "no", zip ? "yes" : "no");
00450 else if(ctx->verbose > 1)
00451 u_con("%s -> %s (encrypted: %s, compressed: %s)",
00452 file_in + strlen(prefix), uri,
00453 enc ? "yes" : "no", zip ? "yes" : "no");
00454
00455 ext = u_match_ext(file_in, "klx") ? "cc" : "c";
00456
00457 dbg_err_if(io_printf(ctx->iom, " \\\n" KL1_FILE_FMT, uri_md5, ext) < 0);
00458
00459 dbg_err_if(io_printf(ctx->ior, "KLONE_REGISTER(action,%s);\n", uri_md5) <0);
00460
00461
00462
00463 dbg_err_if(io_printf(ctx->iod,
00464 "\n" KL1_FILE_FMT
00465 ": %s\n\t$(KLONE) -c translate -i $< -o $@ %s -u /%s %s %s %s %s\n"
00466 "%s",
00467 uri_md5, ext, file_in,
00468 is_a_script ? "-d $@.kld" : "",
00469 uri,
00470 zip ? "-z" : "",
00471 enc ? "-E" : "",
00472 enc && ctx->key_file ? "-k" : "",
00473 enc && ctx->key_file ? ctx->key_file : "",
00474 is_a_script ? "\t$(MKDEP) -f $@.d $(CFLAGS) -a $@\n" : ""
00475 ) < 0);
00476
00477
00478 if(is_a_script)
00479 {
00480 dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".kld\n",
00481 uri_md5, ext) < 0);
00482 dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".d\n",
00483 uri_md5, ext) < 0);
00484 }
00485
00486 return 0;
00487 err:
00488 return ~0;
00489 }
00490
00491 static int cb_dir(struct dirent *de, const char *path , void *arg)
00492 {
00493 char dir[U_FILENAME_MAX], base_uri[URI_BUFSZ], *cur_uri = (char*)arg;
00494
00495 dbg_err_if (de == NULL);
00496 dbg_err_if (path == NULL);
00497 dbg_err_if (arg == NULL);
00498
00499 ctx->ndir++;
00500
00501 dbg_err_if(u_snprintf(dir, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00502
00503 dbg_err_if(u_snprintf(base_uri, URI_BUFSZ, "%s/%s", cur_uri, de->d_name));
00504
00505 u_foreach_dir_item(dir, S_IFREG, cb_file, (void*)base_uri);
00506
00507 u_foreach_dir_item(dir, S_IFDIR, cb_dir, (void*)base_uri);
00508
00509 return 0;
00510 err:
00511 return ~0;
00512 }
00513
00514 static int print_register_header(io_t *out)
00515 {
00516 dbg_err_if (out == NULL);
00517
00518 dbg_err_if(io_printf(out, "#include <klone_conf.h>\n") < 0);
00519 dbg_err_if(io_printf(out, "#include <klone/hook.h>\n") < 0);
00520 dbg_err_if(io_printf(out, "static void do_register(int);\n") < 0);
00521 dbg_err_if(io_printf(out, "void unregister_pages(void);\n") < 0);
00522 dbg_err_if(io_printf(out, "void register_pages(void);\n") < 0);
00523
00524 dbg_err_if(io_printf(out,
00525 "void unregister_pages(void) { \n"
00526 "do_register(0); }\n"
00527 ) < 0);
00528 dbg_err_if(io_printf(out,
00529 "void register_pages(void) { \n"
00530 "do_register(1);\n"
00531 "#ifdef ENABLE_HOOKS\n"
00532 " hooks_setup(); \n"
00533 "#endif \n"
00534 "}\n") < 0);
00535 dbg_err_if(io_printf(out,
00536 "static void do_register(int action) {\n") < 0);
00537 dbg_err_if(io_printf(out,
00538 "#define KLONE_REGISTER(a, md5) \\\n"
00539 " do { \\\n"
00540 " void module_init_##md5(void); \\\n"
00541 " void module_term_##md5(void); \\\n"
00542 " if(a) module_init_##md5(); \\\n"
00543 " else module_term_##md5(); \\\n"
00544 " } while(0) \n") < 0);
00545
00546 return 0;
00547 err:
00548 return ~0;
00549 }
00550
00551 static int print_register_footer(io_t *out)
00552 {
00553 dbg_err_if (out == NULL);
00554
00555 dbg_err_if(io_printf(out, "#undef KLONE_REGISTER\n") < 0);
00556 dbg_err_if(io_printf(out, "}\n") < 0);
00557
00558 return 0;
00559 err:
00560 return ~0;
00561 }
00562
00563 static int trans_site(char *root_dir, char *base_uri)
00564 {
00565 dbg_err_if (root_dir == NULL);
00566 dbg_err_if (base_uri == NULL);
00567
00568
00569 dbg_err_if(u_file_open("autogen.mk", O_CREAT | O_TRUNC | O_WRONLY,
00570 &ctx->iom));
00571 dbg_err_if(u_file_open("autogen.dps", O_CREAT | O_TRUNC | O_WRONLY,
00572 &ctx->iod));
00573
00574 dbg_err_if(u_file_open("register.c", O_CREAT | O_TRUNC | O_WRONLY,
00575 &ctx->ior));
00576
00577 dbg_err_if(print_register_header(ctx->ior));
00578
00579 dbg_err_if(io_printf(ctx->iom, "embfs_rootdir=%s\n", root_dir) < 0);
00580 dbg_err_if(io_printf(ctx->iom, "autogen_src= ") < 0);
00581
00582
00583 u_foreach_dir_item(root_dir, S_IFREG, cb_file, base_uri);
00584
00585 u_foreach_dir_item(root_dir, S_IFDIR, cb_dir, base_uri);
00586
00587 dbg_err_if(print_register_footer(ctx->ior));
00588
00589 io_free(ctx->ior);
00590 io_free(ctx->iod);
00591 io_free(ctx->iom);
00592
00593 return 0;
00594 err:
00595 if(ctx->ior)
00596 io_free(ctx->ior);
00597 if(ctx->iod)
00598 io_free(ctx->iod);
00599 if(ctx->iom)
00600 io_free(ctx->iom);
00601 return ~0;
00602 }
00603
00604 static int command_import(void)
00605 {
00606 char *root_dir, *base_uri;
00607
00608 if(ctx->narg != 1)
00609 usage();
00610
00611 root_dir = ctx->arg[0];
00612 dbg_err_if(root_dir == NULL);
00613
00614 if((base_uri = ctx->base_uri) == NULL)
00615 {
00616 base_uri = u_strdup("");
00617 dbg_err_if (base_uri == NULL);
00618 }
00619
00620 dbg_err_if(trans_site(root_dir, base_uri));
00621
00622 u_con("%lu dirs and %lu files imported, %lu files skipped",
00623 (unsigned long) ctx->ndir, (unsigned long) ctx->nfile,
00624 (unsigned long) ctx->nexcl);
00625
00626 return 0;
00627 err:
00628 u_con("import error");
00629 return ~0;
00630 }
00631
00632 static int dispatch_command(void)
00633 {
00634 switch(ctx->cmd)
00635 {
00636 case CMD_TRANS:
00637 dbg_err_if(command_trans());
00638 break;
00639 case CMD_IMPORT:
00640 dbg_err_if(command_import());
00641 break;
00642 default:
00643 con_err("unknown command");
00644 }
00645
00646 return 0;
00647 err:
00648 return ~0;
00649 }
00650
00651 int main(int argc, char **argv)
00652 {
00653 context_t context;
00654
00655 ctx = &context;
00656
00657
00658 memset(ctx, 0, sizeof(context_t));
00659
00660
00661 dbg_err_if(pm_create(&ctx->comp_patt));
00662 dbg_err_if(pm_create(&ctx->enc_patt));
00663 dbg_err_if(pm_create(&ctx->excl_patt));
00664
00665
00666 dbg_err_if(parse_opt(argc, argv));
00667
00668
00669 dbg_err_if(dispatch_command());
00670
00671 return EXIT_SUCCESS;
00672 err:
00673 return EXIT_FAILURE;
00674 }