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