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-2008 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 HAVE_LIBOPENSSL
00098 " -e pattern encrypt all files whose URI match the given pattern (*)\n"
00099 " -k key_file encryption key filename\n"
00100 #endif
00101 #ifdef HAVE_LIBZ
00102 " -z compress all compressable content (based on MIME types)\n"
00103 " -Z pattern compress all files whose URI match the given pattern (*)\n"
00104 #endif
00105 " dir directory tree path\n"
00106 "\n"
00107 " (*) may be used more then once\n"
00108 "\n"
00109 " translate OPTIONS\n"
00110 #ifdef HAVE_LIBOPENSSL
00111 " -E encrypt file content\n"
00112 #endif
00113 " -i file input file\n"
00114 #ifdef HAVE_LIBOPENSSL
00115 " -k key_file encryption key filename\n"
00116 #endif
00117 " -o file output file\n"
00118 " -u URI URI of translated page\n"
00119 " (KLONE_CIPHER_KEY environ var is used if not provided)\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 HAVE_LIBOPENSSL
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 HAVE_LIBOPENSSL
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_SIZE) <= 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 con("translating %s to %s (uri: %s)", ctx->file_in, ctx->file_out,
00293 ctx->uri);
00294
00295
00296 strncpy(ti.file_in, ctx->file_in, U_FILENAME_MAX);
00297
00298
00299 strncpy(ti.file_out, ctx->file_out, U_FILENAME_MAX);
00300
00301
00302 if(ctx->depend_out)
00303 strncpy(ti.depend_out, ctx->depend_out, U_FILENAME_MAX);
00304
00305
00306 strncpy(ti.uri, ctx->uri, URI_BUFSZ);
00307
00308
00309 memset(ti.key, 0, CODEC_CIPHER_KEY_SIZE);
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 key_found = 1;
00319 strncpy(ti.key, key_env, CODEC_CIPHER_KEY_SIZE);
00320 }
00321
00322
00323 if(ctx->key_file)
00324 {
00325 key_found = 1;
00326 con_err_ifm(set_key_from_file(&ti, ctx->key_file),
00327 "error reading key file [%s]", ctx->key_file);
00328 }
00329
00330 if(ctx->encrypt)
00331 {
00332 if(!key_found)
00333 con_err("encryption key required (use -k or KLONE_CIPHER_KEY "
00334 "environ variable)");
00335 ti.encrypt = 1;
00336 }
00337
00338
00339 if((mm = u_get_mime_map(ctx->file_in)) != NULL)
00340 strncpy(ti.mime_type, mm->mime_type, MIME_BUFSZ);
00341 else
00342 strncpy(ti.mime_type, "application/octet-stream", MIME_BUFSZ);
00343
00344
00345 if(ctx->compress)
00346 ti.comp = 1;
00347
00348
00349 klone_die_if(stat(ctx->file_in, &st), "input file not found");
00350
00351 ti.file_size = st.st_size;
00352 ti.mtime = st.st_mtime;
00353
00354
00355 dbg_err_if(translate(&ti));
00356
00357 return 0;
00358 err:
00359
00360 u_remove(ti.file_out);
00361 con(" ");
00362 return ~0;
00363 }
00364
00365 static int is_cpp(const char *file_in)
00366 {
00367 size_t l;
00368
00369 dbg_err_if (file_in == NULL);
00370
00371 l = strlen(file_in);
00372 if(l < 4)
00373 return 0;
00374
00375
00376 if(tolower(file_in[--l]) == 'c' && tolower(file_in[--l]) == 'c')
00377 return 1;
00378
00379 err:
00380 return 0;
00381 }
00382
00383
00384 static int cb_file(struct dirent *de, const char *path , void *arg)
00385 {
00386 static const char *prefix = "$(srcdir)";
00387 const mime_map_t *mm;
00388 char uri_md5[MD5_DIGEST_BUFSZ];
00389 char file_in[U_FILENAME_MAX], uri[URI_BUFSZ], *base_uri = (char*)arg;
00390 char fullpath[U_FILENAME_MAX];
00391 const char *ext;
00392 int is_a_script, enc = 0, zip = 0;
00393
00394 dbg_err_if (de == NULL);
00395 dbg_err_if (path == NULL);
00396 dbg_err_if (arg == NULL);
00397
00398 dbg_err_if(u_snprintf(fullpath, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00399
00400
00401 dbg_err_if(translate_makefile_filepath(fullpath, prefix, file_in,
00402 sizeof(file_in)));
00403
00404
00405 dbg_err_if(u_snprintf(uri, URI_BUFSZ, "%s/%s", base_uri, de->d_name));
00406 dbg_err_if(u_md5(uri, strlen(uri), uri_md5));
00407
00408
00409 if(!pm_is_empty(ctx->excl_patt) && pm_match(ctx->excl_patt, uri))
00410 {
00411 if(ctx->verbose)
00412 con("%s skipped", uri);
00413
00414 ctx->nexcl++;
00415
00416 return 0;
00417 }
00418
00419 is_a_script = translate_is_a_script(de->d_name);
00420
00421 ctx->nfile++;
00422
00423
00424 if(!pm_is_empty(ctx->enc_patt) && pm_match(ctx->enc_patt, uri))
00425 enc = 1;
00426
00427 if(ctx->compress)
00428 {
00429
00430 if(!pm_is_empty(ctx->comp_patt))
00431 {
00432 if(pm_match(ctx->comp_patt, uri))
00433 zip = 1;
00434 } else {
00435
00436 if((mm = u_get_mime_map(uri)) != NULL)
00437 zip = mm->comp;
00438 }
00439 }
00440
00441
00442 if(ctx->verbose == 1)
00443 con("%s (encrypted: %s, compressed: %s)",
00444 uri, enc ? "yes" : "no", zip ? "yes" : "no");
00445 else if(ctx->verbose > 1)
00446 con("%s -> %s (encrypted: %s, compressed: %s)",
00447 file_in + strlen(prefix), uri,
00448 enc ? "yes" : "no", zip ? "yes" : "no");
00449
00450 ext = u_match_ext(file_in, "klx") ? "cc" : "c";
00451
00452 dbg_err_if(io_printf(ctx->iom, " \\\n" KL1_FILE_FMT, uri_md5, ext) < 0);
00453
00454 dbg_err_if(io_printf(ctx->ior, "KLONE_REGISTER(action,%s);\n", uri_md5) <0);
00455
00456
00457
00458 dbg_err_if(io_printf(ctx->iod,
00459 "\n" KL1_FILE_FMT
00460 ": %s\n\t$(KLONE) -c translate -i $< -o $@ %s -u /%s %s %s %s %s\n"
00461 "%s",
00462 uri_md5, ext, file_in,
00463 is_a_script ? "-d $@.kld" : "",
00464 uri,
00465 zip ? "-z" : "",
00466 enc ? "-E" : "",
00467 enc && ctx->key_file ? "-k" : "",
00468 enc && ctx->key_file ? ctx->key_file : "",
00469 is_a_script ? "\t$(MKDEP) -f $@.d $(CFLAGS) -a $@\n" : ""
00470 ) < 0);
00471
00472
00473 if(is_a_script)
00474 {
00475 dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".kld\n",
00476 uri_md5, ext) < 0);
00477 dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".d\n",
00478 uri_md5, ext) < 0);
00479 }
00480
00481 return 0;
00482 err:
00483 return ~0;
00484 }
00485
00486 static int cb_dir(struct dirent *de, const char *path , void *arg)
00487 {
00488 char dir[U_FILENAME_MAX], base_uri[URI_BUFSZ], *cur_uri = (char*)arg;
00489
00490 dbg_err_if (de == NULL);
00491 dbg_err_if (path == NULL);
00492 dbg_err_if (arg == NULL);
00493
00494 ctx->ndir++;
00495
00496 dbg_err_if(u_snprintf(dir, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00497
00498 dbg_err_if(u_snprintf(base_uri, URI_BUFSZ, "%s/%s", cur_uri, de->d_name));
00499
00500 u_foreach_dir_item(dir, S_IFREG, cb_file, (void*)base_uri);
00501
00502 u_foreach_dir_item(dir, S_IFDIR, cb_dir, (void*)base_uri);
00503
00504 return 0;
00505 err:
00506 return ~0;
00507 }
00508
00509 static int print_register_header(io_t *out)
00510 {
00511 dbg_err_if (out == NULL);
00512
00513 dbg_err_if(io_printf(out, "#include <klone_conf.h>\n") < 0);
00514 dbg_err_if(io_printf(out, "#include <klone/hook.h>\n") < 0);
00515 dbg_err_if(io_printf(out, "static void do_register(int);\n") < 0);
00516 dbg_err_if(io_printf(out, "void unregister_pages(void);\n") < 0);
00517 dbg_err_if(io_printf(out, "void register_pages(void);\n") < 0);
00518
00519 dbg_err_if(io_printf(out,
00520 "void unregister_pages(void) { \n"
00521 "do_register(0); }\n"
00522 ) < 0);
00523 dbg_err_if(io_printf(out,
00524 "void register_pages(void) { \n"
00525 "do_register(1);\n"
00526 "#ifdef ENABLE_HOOKS\n"
00527 " hooks_setup(); \n"
00528 "#endif \n"
00529 "}\n") < 0);
00530 dbg_err_if(io_printf(out,
00531 "static void do_register(int action) {\n") < 0);
00532 dbg_err_if(io_printf(out,
00533 "#define KLONE_REGISTER(a, md5) \\\n"
00534 " do { \\\n"
00535 " void module_init_##md5(void); \\\n"
00536 " void module_term_##md5(void); \\\n"
00537 " if(a) module_init_##md5(); \\\n"
00538 " else module_term_##md5(); \\\n"
00539 " } while(0) \n") < 0);
00540
00541 return 0;
00542 err:
00543 return ~0;
00544 }
00545
00546 static int print_register_footer(io_t *out)
00547 {
00548 dbg_err_if (out == NULL);
00549
00550 dbg_err_if(io_printf(out, "#undef KLONE_REGISTER\n") < 0);
00551 dbg_err_if(io_printf(out, "}\n") < 0);
00552
00553 return 0;
00554 err:
00555 return ~0;
00556 }
00557
00558 static int trans_site(char *root_dir, char *base_uri)
00559 {
00560 dbg_err_if (root_dir == NULL);
00561 dbg_err_if (base_uri == NULL);
00562
00563
00564 dbg_err_if(u_file_open("autogen.mk", O_CREAT | O_TRUNC | O_WRONLY,
00565 &ctx->iom));
00566 dbg_err_if(u_file_open("autogen.dps", O_CREAT | O_TRUNC | O_WRONLY,
00567 &ctx->iod));
00568
00569 dbg_err_if(u_file_open("register.c", O_CREAT | O_TRUNC | O_WRONLY,
00570 &ctx->ior));
00571
00572 dbg_err_if(print_register_header(ctx->ior));
00573
00574 dbg_err_if(io_printf(ctx->iom, "embfs_rootdir=%s\n", root_dir) < 0);
00575 dbg_err_if(io_printf(ctx->iom, "autogen_src= ") < 0);
00576
00577
00578 u_foreach_dir_item(root_dir, S_IFREG, cb_file, base_uri);
00579
00580 u_foreach_dir_item(root_dir, S_IFDIR, cb_dir, base_uri);
00581
00582 dbg_err_if(print_register_footer(ctx->ior));
00583
00584 io_free(ctx->ior);
00585 io_free(ctx->iod);
00586 io_free(ctx->iom);
00587
00588 return 0;
00589 err:
00590 if(ctx->ior)
00591 io_free(ctx->ior);
00592 if(ctx->iod)
00593 io_free(ctx->iod);
00594 if(ctx->iom)
00595 io_free(ctx->iom);
00596 return ~0;
00597 }
00598
00599 static int command_import(void)
00600 {
00601 char *root_dir, *base_uri;
00602
00603 if(ctx->narg != 1)
00604 usage();
00605
00606 root_dir = ctx->arg[0];
00607 dbg_err_if(root_dir == NULL);
00608
00609 if((base_uri = ctx->base_uri) == NULL)
00610 {
00611 base_uri = u_strdup("");
00612 dbg_err_if (base_uri == NULL);
00613 }
00614
00615 dbg_err_if(trans_site(root_dir, base_uri));
00616
00617 con("%lu dirs and %lu files imported, %lu files skipped",
00618 ctx->ndir, ctx->nfile, ctx->nexcl);
00619
00620 return 0;
00621 err:
00622 con("import error");
00623 return ~0;
00624 }
00625
00626 static int dispatch_command(void)
00627 {
00628 switch(ctx->cmd)
00629 {
00630 case CMD_TRANS:
00631 dbg_err_if(command_trans());
00632 break;
00633 case CMD_IMPORT:
00634 dbg_err_if(command_import());
00635 break;
00636 default:
00637 con_err("unknown command");
00638 }
00639
00640 return 0;
00641 err:
00642 return ~0;
00643 }
00644
00645 int main(int argc, char **argv)
00646 {
00647 context_t context;
00648
00649 ctx = &context;
00650
00651
00652 memset(ctx, 0, sizeof(context_t));
00653
00654
00655 dbg_err_if(pm_create(&ctx->comp_patt));
00656 dbg_err_if(pm_create(&ctx->enc_patt));
00657 dbg_err_if(pm_create(&ctx->excl_patt));
00658
00659
00660 dbg_err_if(parse_opt(argc, argv));
00661
00662
00663 dbg_err_if(dispatch_command());
00664
00665 return EXIT_SUCCESS;
00666 err:
00667 return EXIT_FAILURE;
00668 }