Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

tools/klone/main.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: main.c,v 1.33 2006/03/21 19:15:38 tat Exp $
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 /* command list enums */
00036 enum command_e { CMD_UNKNOWN, CMD_TRANS, CMD_IMPORT };
00037 
00038 /* runtime flags */
00039 enum flags_e { FLAG_NONE, FLAG_VERBOSE };
00040 
00041 typedef struct 
00042 {
00043     char *file_in, *file_out;   /* [trans] input, output file       */
00044     char *uri;                  /* [trans] translated file uri      */
00045     int verbose;                /* >0 when verbose mode is on       */
00046     char **arg;                 /* argv                             */
00047     size_t narg;                /* argc                             */
00048     int cmd;                    /* command to run                   */
00049     char *base_uri;             /* site base uri                    */
00050     int encrypt;                /* >0 when encryption is enabled    */
00051     int compress;               /* >0 when compress is enabled      */
00052     char *enc_patt;             /* encrypt file pattern             */
00053     char *comp_patt;            /* compress file pattern            */
00054     char *key_file;             /* encryption key file name         */
00055     io_t *iom, *iod, *ior;      /* io makefile, io deps             */
00056     size_t ndir, nfile;         /* dir and file count               */
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     /* common switches */
00136     strcpy(opts, "hvVb:i:o:u:c:");
00137 
00138     /* encryption switches */
00139 #ifdef HAVE_LIBOPENSSL
00140     strcat(opts, "k:e:E");
00141 #endif
00142 
00143     /* compression switches */
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': /* verbose on */
00153             ctx->verbose = 1;
00154             break;
00155         case 'V': /* print name/version info and exit */
00156             u_print_version_and_exit();
00157             break;
00158 
00159 #ifdef HAVE_LIBOPENSSL
00160         case 'E': /* encryption on */
00161             ctx->encrypt = 1;
00162             break;
00163         case 'e': /* encrypt file pattern */
00164             ctx->enc_patt = u_strdup(optarg);
00165             warn_err_if(ctx->enc_patt == NULL);
00166             break;
00167         case 'k': /* encryption key filename */
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': /* compress file pattern */
00175             ctx->compress = 1;
00176             ctx->comp_patt = u_strdup(optarg);
00177             warn_err_if(ctx->comp_patt == NULL);
00178             break;
00179         case 'z': /* compress */
00180             ctx->compress = 1;
00181             break;
00182 #endif
00183         case 'c': /* command */
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': /* input file */
00192             ctx->file_in = u_strdup(optarg);
00193             warn_err_if(ctx->file_in == NULL);
00194             break;
00195         case 'b': /* base_uri */
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': /* output file */
00207             ctx->file_out = u_strdup(optarg);
00208             warn_err_if(ctx->file_out == NULL);
00209             break;
00210         case 'u': /* translated page uri */
00211             /* skip the first char to avoid MSYS path translation bug
00212              * (see klone-site.c) */
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;  /* # of args left */
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();    /* no argument allowed */
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     /* input file */
00277     strncpy(ti.file_in, ctx->file_in, U_FILENAME_MAX);
00278 
00279     /* output file */
00280     strncpy(ti.file_out, ctx->file_out, U_FILENAME_MAX);
00281 
00282     /* uri */
00283     strncpy(ti.uri, ctx->uri, URI_BUFSZ);
00284 
00285     /* zero out the key (some byte could not be overwritten with small keys) */
00286     memset(ti.key, 0, CODEC_CIPHER_KEY_SIZE);
00287 
00288     /* sanity checks */
00289     con_err_ifm(ctx->key_file && !ctx->encrypt, "-k used but -E is missing");
00290 
00291     /* encryption key */
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     /* if -k has been used the overwrite KLONE_CIPHER_KEY env var (if present)*/
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     /* set MIME type */
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     /* compress if requested and the file is compressable (by MIME type) */
00322     if(ctx->compress)
00323         ti.comp = 1;
00324 
00325     /* be sure that the input file exists */
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     /* translate it */
00332     dbg_err_if(translate(&ti));
00333 
00334     return 0;
00335 err:
00336     /* delete output file on error */
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     /* if the file name ends with "[Cc][Cc]" consider it a c++ file */
00353     if(tolower(file_in[--l]) == 'c' && tolower(file_in[--l]) == 'c')
00354         return 1; /* c++ */
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     /* input file */
00376     if(path[0] == U_PATH_SEPARATOR)
00377     {   /* absolute path */
00378         dbg_err_if(u_snprintf(file_in, U_FILENAME_MAX, "%s/%s", path, 
00379             de->d_name));
00380     } else {
00381         /* relative path, use $(srcdir) */
00382         dbg_err_if(u_snprintf(file_in, U_FILENAME_MAX, "%s/%s/%s", prefix, 
00383             path, de->d_name));
00384     }
00385 
00386     /* base uri */
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     /* if the URI match the given encrypt pattern then encrypt it */
00391     if(ctx->enc_patt && !fnmatch(ctx->enc_patt, uri, 0))
00392         enc = 1;
00393 
00394     if(ctx->compress) /* -z or -Z have been used */
00395     {
00396         /* if the URI match the given compress pattern then compress it */
00397         if(ctx->comp_patt)
00398         {   /* compression enabled basing on file URI pattern */
00399             if(!fnmatch(ctx->comp_patt, uri, 0))
00400                 zip = 1;
00401         } else {
00402             /* compression enabled basing on URI MIME types */
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     /* we're adding a '/' before the uri (that will be removed by klone.exe) 
00421      * to avoid MSYS (win32) automatic path translation oddity */
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     /* makefile */
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     /* for each file call cb_file */
00520     u_foreach_dir_item(root_dir, S_IFREG, cb_file, base_uri);
00521     /* for each directory call cb_dir */
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();    /* just on directory expected */
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     /* zero-out the context */
00593     memset(ctx, 0, sizeof(context_t));
00594 
00595     /* parse command line switches and set ctx->cmd and params */
00596     dbg_err_if(parse_opt(argc, argv));
00597 
00598     /* run the command */
00599     dbg_err_if(dispatch_command());
00600 
00601     return EXIT_SUCCESS;
00602 err:
00603     return EXIT_FAILURE;
00604 }

←Products
© 2005-2006 - KoanLogic S.r.l. - All rights reserved