00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "common_bundle.h"
00016
00017 #include <basis/array.cpp>
00018 #include <basis/byte_array.h>
00019 #include <basis/guards.h>
00020 #include <basis/portable.h>
00021 #include <data_struct/string_table.h>
00022 #include <opsystem/application_shell.h>
00023 #include <opsystem/byte_filer.h>
00024 #include <opsystem/command_line.h>
00025 #include <loggers/console_logger.h>
00026 #include <opsystem/directory.h>
00027 #include <opsystem/filename.h>
00028 #include <loggers/file_logger.h>
00029 #include <opsystem/filetime.h>
00030 #include <opsystem/heavy_file_ops.h>
00031 #include <data_struct/static_memory_gremlin.h>
00032 #include <textual/parser_bits.h>
00033 #include <textual/tokenizer.h>
00034 #include <win_ext/window_classist.h>
00035
00036 #include <stdio.h>
00037 #include <sys/stat.h>
00038 #include <zlib.h>
00039 #ifdef __UNIX__
00040 #include <utime.h>
00041 #endif
00042 #ifdef __WIN32__
00043 #include <direct.h>
00044 #include <io.h>
00045 #include <shlobj.h>
00046 #include <sys/utime.h>
00047 #endif
00048
00049 const int CHUNKING_SIZE = 64 * KILOBYTE;
00050
00051
00052 #define BASE_LOG(to_print) program_wide_logger().log(to_print)
00053 #define LOG(to_print) STAMPED_EMERGENCY_LOG(program_wide_logger(), to_print)
00054
00055
00056
00057
00058 const char *ERROR_TITLE = "An Error Caused Incomplete Installation";
00059
00060
00062
00063 class unpacker_stub : public application_shell
00064 {
00065 public:
00066 unpacker_stub()
00067 : application_shell("unpacker_stub"),
00068 _app_name(filename(__argv[0]).basename()) {}
00069 IMPLEMENT_CLASS_NAME("unpacker_stub");
00070
00071 int print_instructions();
00072
00073 virtual int execute();
00074
00075 private:
00076 istring _app_name;
00077 array<manifest_chunk> _manifest;
00078 string_table _variables;
00079 };
00080
00082
00083 void show_message(const istring &msg, const istring &title)
00084 {
00085 #ifndef __WIN32__
00086 BASE_LOG(title);
00087 BASE_LOG(istring('-', title.length()));
00088 BASE_LOG(msg);
00089 #else
00090 MessageBox(0, to_unicode_temp(msg), to_unicode_temp(title),
00091 MB_OK | MB_ICONINFORMATION);
00092 #endif
00093 }
00094
00095 int unpacker_stub::print_instructions()
00096 {
00097 isprintf msg("\
00098 %s: This program unpacks its contents into the locations\n\
00099 specified at packing time. The --target flag can be used to specify a\n\
00100 different TARGET directory for the installation (for components that use\n\
00101 the default TARGET variable to specify their install folder).\n\
00102 One can also pass a --keyword flag to specify a keyword; the files in the\n\
00103 bundle that are marked with that keyword will be installed, but files that\n\
00104 are missing the keyword will not be.\n\
00105 Further, variables can be overridden on the command line in the\n\
00106 form: X=Y.\n\n\
00107 The line below uses all these parameters as an example:\n\n\
00108 %s --target c:\\Program Files\\gubernator --keyword dlls_only SILENT=true\n\
00109 \n\
00110 Additional Notes:\n\
00111 \n\
00112 One helpful variable is \"LOGDIR\". This is where the unpacking log file\n\
00113 will be written to. The default is \"~/logs\" (or \"$TMP/logs\" on win32)\n\
00114 until overridden.\n\
00115 \n", _app_name.s(), _app_name.s());
00116 show_message(msg, "Unpacking Instructions");
00117 return 12;
00118 }
00119
00120
00121
00122
00123
00124
00125
00126 byte MANIFEST_OFFSET_ARRAY[]
00127 = { 'm', 'u', 'f', 't', 'i', 'l', 'o', 'c', 0, 0, 0, 0, 0, 0, 0, 0 };
00128
00129 int unpacker_stub::execute()
00130 {
00131 #ifdef ADMIN_CHECK
00132 #ifdef __WIN32__
00133 if (IsUserAnAdmin()) {
00134 ::MessageBox(0, to_unicode_temp("IS admin in bundler"), to_unicode_temp("bundler"), MB_OK);
00135 } else {
00136 ::MessageBox(0, to_unicode_temp("NOT admin in bundler"), to_unicode_temp("bundler"), MB_OK);
00137 }
00138 #endif
00139 #endif
00140
00141 command_line cmds(__argc, __argv);
00142
00143 int indy = 0;
00144 if (cmds.find('?', indy)) return print_instructions();
00145 if (cmds.find("?", indy)) return print_instructions();
00146
00147
00148
00149 #ifndef __WIN32__
00150 portable::set_environ("EXE_END", "");
00151 portable::set_environ("DLL_START", "lib");
00152 portable::set_environ("DLL_END", ".so");
00153 #else
00154 portable::set_environ("EXE_END", ".exe");
00155 portable::set_environ("DLL_START", "");
00156 portable::set_environ("DLL_END", ".dll");
00157 #endif
00158
00159
00160 bool provided_target = true;
00161 istring target;
00162 cmds.get_value("target", target);
00163
00164 if (!target) {
00165 target = portable::env_string("TMP") + "/unbundled";
00166 provided_target = false;
00167 }
00168
00169 portable::set_environ("TARGET", target);
00170
00171 {
00172 istring logdir = portable::env_string("LOGDIR");
00173 #ifdef __WIN32__
00174 if (!logdir) {
00175 logdir = portable::env_string("TMP") + "/logs";
00176 portable::set_environ("LOGDIR", logdir);
00177 }
00178 #else
00179 if (!logdir) {
00180 istring homedir = portable::env_string("HOME");
00181 logdir = homedir + "/logs";
00182 portable::set_environ("LOGDIR", logdir);
00183 }
00184 #endif
00185 }
00186
00187 istring keyword;
00188 cmds.get_value("keyword", keyword);
00189
00190 istring vars_set;
00191
00192 for (int x = 0; x < cmds.entries(); x++) {
00193 command_parameter curr = cmds.get(x);
00194 if (curr.type() != command_parameter::VALUE) continue;
00195 if (curr.text().find('=', 0) < 0) continue;
00196 tokenizer t;
00197 t.parse(curr.text());
00198 if (!t.symbols()) continue;
00199 istring var = t.table().name(0);
00200 istring value = t.table()[0];
00201 vars_set += istring("variable set: ") + var + "=" + value
00202 + log_base::platform_ending();
00203 _variables.add(var, value);
00204 portable::set_environ(var, value);
00205 }
00206
00207
00208 istring logdir = portable::env_string("LOGDIR");
00209
00210 istring appname = filename(portable::application_name()).rootname();
00211 istring logname = logdir + "/" + appname + ".log";
00212 log_base *old_log = set_PW_logger_for_combo(logname);
00213 WHACK(old_log);
00214
00215 BASE_LOG(istring('#', 76));
00216 BASE_LOG(appname + " command-line parameters:");
00217 BASE_LOG(cmds.text_form());
00218 BASE_LOG(istring('#', 76));
00219
00220 BASE_LOG(vars_set);
00221
00222 #ifdef __WIN32__
00223
00224
00225 window_handle f_window = create_simplistic_window("temp_stubby_class",
00226 "stubby window title");
00227 #endif
00228
00229
00230 byte_array temp_packed(2 * sizeof(int), MANIFEST_OFFSET_ARRAY + 8);
00231 int manifest_offset;
00232 if (!basis::obscure_detach(temp_packed, manifest_offset)) {
00233 show_message(istring("could not read manifest offset in: ") + __argv[0],
00234 ERROR_TITLE);
00235 return 24;
00236 }
00237
00238 filename this_exe(__argv[0]);
00239 if (!this_exe.exists()) {
00240 show_message(istring("could not access this exe image: ") + this_exe.raw(),
00241 ERROR_TITLE);
00242 return 23;
00243 }
00244
00245
00246 byte_filer our_exe(this_exe, "rb");
00247 our_exe.seek(manifest_offset);
00248
00249
00250 if (our_exe.read(temp_packed, 2 * sizeof(int)) <= 0) {
00251 show_message(istring("could not read the manifest length in: ")
00252 + this_exe.raw(), ERROR_TITLE);
00253 return 26;
00254 }
00255 int item_count;
00256 basis::obscure_detach(temp_packed, item_count);
00257
00258 _manifest.insert(0, item_count);
00259
00260
00261 for (int i = 0; i < item_count; i++) {
00262 manifest_chunk &curr = _manifest[i];
00263 bool worked = manifest_chunk::read_manifest(our_exe, curr);
00264 if (!worked) {
00265 show_message(isprintf("could not read chunk for item #%d [%s]: ", i,
00266 curr._target.s())
00267 + this_exe.raw(), ERROR_TITLE);
00268 return 86;
00269 }
00270 }
00271
00272 #ifdef DEBUG_STUB
00273 LOG("read the following info from manifest:");
00274 istring temp = "size\ttarget\r\n";
00275 for (int i = 0; i < _manifest.length(); i++) {
00276 manifest_chunk &curr = _manifest[i];
00277 temp += isprintf("%d\t%s\r\n", curr._size, curr._target.s());
00278 }
00279 guards::alert_message(temp, "manifest contents");
00280 #endif
00281
00282
00283
00284
00285 for (int festdex = 0; festdex < _manifest.length(); festdex++) {
00286 manifest_chunk &curr = _manifest[festdex];
00287 int size_left = curr._size;
00288
00289
00290 curr._target = parser_bits::substitute_env_vars(curr._target, false);
00291 curr._parms = parser_bits::substitute_env_vars(curr._parms, false);
00292 #ifdef DEBUG_STUB
00293 BASE_LOG(istring("processing ") + curr._target
00294 + isprintf(", size=%d, flags=%d", curr._size, curr._flags));
00295 if (!!curr._parms)
00296 BASE_LOG(istring(" parms: ") + curr._parms);
00297 #endif
00298
00299
00300 bool keyword_good = true;
00301 if (!!keyword && !curr._keywords.member(keyword)) {
00302
00303 keyword_good = false;
00304
00305 }
00306
00307 if (curr._flags & SET_VARIABLE) {
00308 if (keyword_good) {
00309
00310
00311 if (provided_target && (curr._target == "TARGET") ) {
00312 BASE_LOG(istring("skipping ") + curr._target + "=" + curr._parms
00313 + ": was provided explicitly as " + target);
00314 } else if (_variables.find(curr._target)) {
00315 BASE_LOG(istring("skipping ") + curr._target + "=" + curr._parms
00316 + ": was provided on command line.");
00317 } else {
00318 BASE_LOG(istring("setting ") + curr._target + "=" + curr._parms);
00319 portable::set_environ(curr._target, curr._parms);
00320
00321
00322 if (curr._target == "LOGDIR") {
00323 istring logdir = curr._parms;
00324 istring appname = filename(portable::application_name()).rootname();
00325 istring logname = logdir + "/" + appname + ".log";
00326 log_base *old_log = set_PW_logger_for_combo(logname);
00327 WHACK(old_log);
00328 }
00329 }
00330 }
00331 continue;
00332 }
00333
00334 if (! (curr._flags & OMIT_PACKING) ) {
00335
00336
00337
00338 filename target_dir = filename(curr._target).dirname();
00339 if (keyword_good && !target_dir.exists()
00340 && !directory::recursive_create(target_dir)) {
00341 LOG(isprintf("failed to create directory %s for item #%d: ",
00342 target_dir.raw().s(), festdex) + curr._target);
00343 }
00344
00345 if (curr._flags & NO_OVERWRITE) {
00346 filename target_file(curr._target);
00347 if (target_file.exists()) {
00348 BASE_LOG(istring("not overwriting existing ") + curr._target);
00349 keyword_good = false;
00350 }
00351 }
00352
00353 byte_filer *targo = NIL;
00354 if (keyword_good) targo = new byte_filer(curr._target, "wb");
00355 byte_array uncompressed(256 * KILOBYTE);
00356 byte_array temp(256 * KILOBYTE);
00357
00358 bool first_read = true;
00359
00360 bool too_tiny_complaint_already = false;
00361
00362
00363
00364
00365
00366 while (first_read || !our_exe.eof()) {
00367 first_read = false;
00368 int real_size = 0, packed_size = 0;
00369
00370 bool worked = manifest_chunk::read_an_obscured_int(our_exe, real_size);
00371 if (!worked) {
00372 show_message(isprintf("failed while reading real size "
00373 "for item #%d: ", festdex) + curr._target, ERROR_TITLE);
00374 return 99;
00375 }
00376
00377 worked = manifest_chunk::read_an_obscured_int(our_exe, packed_size);
00378 if (!worked) {
00379 show_message(isprintf("failed while reading packed size "
00380 "for item #%d: ", festdex) + curr._target, ERROR_TITLE);
00381 return 99;
00382 }
00383
00384
00385 if ( (real_size == -1) && (packed_size == -1) ) {
00386
00387 break;
00388 }
00389
00390 #ifdef DEBUG_STUB
00391 BASE_LOG(isprintf("chunk packed_size=%d, real_size=%d", packed_size,
00392 real_size));
00393 #endif
00394
00395
00396 if (packed_size) {
00397 int ret = our_exe.read(temp, packed_size);
00398 if (ret <= 0) {
00399 show_message(isprintf("failed while reading item #%d: ", festdex)
00400 + curr._target, ERROR_TITLE);
00401 return 99;
00402 } else if (ret != packed_size) {
00403 show_message(isprintf("bad trouble ahead, item #%d had different "
00404 " size on read (expected %d, got %d): ", festdex, packed_size,
00405 ret) + curr._target, ERROR_TITLE);
00406 }
00407
00408 uncompressed.reset(real_size + KILOBYTE);
00409 uLongf destlen = uncompressed.length();
00410 int uncomp_ret = uncompress(uncompressed.access(), &destlen,
00411 temp.observe(), packed_size);
00412 if (uncomp_ret != Z_OK) {
00413 show_message(isprintf("failed while uncompressing item #%d: ",
00414 festdex) + curr._target, ERROR_TITLE);
00415 return 99;
00416 }
00417
00418 if (int(destlen) != real_size) {
00419 LOG(isprintf("got a different unpacked size for item #%d: ",
00420 festdex) + curr._target);
00421 }
00422
00423
00424 size_left -= real_size;
00425 if (size_left < 0) {
00426 if (!too_tiny_complaint_already) {
00427 LOG(isprintf("item #%d was larger than expected (non-fatal): ",
00428 festdex) + curr._target);
00429 too_tiny_complaint_already = true;
00430 }
00431 }
00432
00433 uncompressed.zap(real_size, uncompressed.length() - 1);
00434
00435 if (targo) {
00436
00437 ret = targo->write(uncompressed);
00438 if (ret != uncompressed.length()) {
00439 show_message(isprintf("failed while writing item #%d: ", festdex)
00440 + curr._target, ERROR_TITLE);
00441 return 93;
00442 }
00443 }
00444 }
00445 }
00446 if (targo) targo->close();
00447 WHACK(targo);
00448
00449 file_time t;
00450 if (!t.unpack(curr._timestamp)) {
00451 show_message(istring("failed to interpret timestamp for ")
00452 + curr._target, ERROR_TITLE);
00453 return 97;
00454 }
00455 utimbuf held_time;
00456 held_time.actime = t.raw();
00457 held_time.modtime = t.raw();
00458
00459 utime(curr._target.s(), &held_time);
00460 }
00461
00462
00463 if ( (curr._flags & TARGET_EXECUTE) && keyword_good) {
00464
00465 chmod(curr._target.s(), 0766);
00466 istring prev_dir = portable::current_directory();
00467
00468 BASE_LOG(istring("launching ") + curr._target);
00469 if (!!curr._parms)
00470 BASE_LOG(istring(" with parameters: ") + curr._parms);
00471 BASE_LOG(istring('-', 76));
00472
00473 u_int kid;
00474 u_int retval = portable::launch_process(curr._target, curr._parms,
00475 portable::AWAIT_APP_EXIT | portable::HIDE_APP_WINDOW, kid);
00476 if (retval != 0) {
00477 if (! (curr._flags & IGNORE_ERRORS) ) {
00478 if (curr._flags & QUIET_FAILURE) {
00479
00480 LOG(istring("failed to launch process, targ=")
00481 + curr._target + " with parms " + curr._parms
00482 + isprintf(" error=%d", retval));
00483 } else {
00484 show_message(istring("failed to launch process, targ=")
00485 + curr._target + " with parms " + curr._parms
00486 + isprintf(" error=%d", retval), ERROR_TITLE);
00487 }
00488 return retval;
00489 } else {
00490 LOG(istring("ignoring failure to launch process, targ=")
00491 + curr._target + " with parms " + curr._parms
00492 + isprintf(" error=%d", retval));
00493 }
00494 }
00495
00496 chdir(prev_dir.s());
00497
00498 BASE_LOG(istring('-', 76));
00499 }
00500
00501 }
00502
00503 #ifdef __WIN32__
00504 whack_simplistic_window(f_window);
00505 #endif
00506
00507 return 0;
00508 }
00509
00511
00512 HOOPLE_MAIN(unpacker_stub, )
00513
00514 #ifdef __BUILD_STATIC_APPLICATION__
00515
00516 #include <basis/array.cpp>
00517 #include <basis/byte_array.cpp>
00518 #include <basis/callstack_tracker.cpp>
00519 #include <basis/chaos.cpp>
00520 #include <basis/convert_utf.cpp>
00521 #include <basis/definitions.cpp>
00522 #include <basis/earth_time.cpp>
00523 #include <basis/guards.cpp>
00524 #include <basis/istring.cpp>
00525 #include <basis/log_base.cpp>
00526 #include <basis/memory_checker.cpp>
00527 #include <basis/mutex.cpp>
00528 #include <basis/object_base.cpp>
00529 #include <basis/outcome.cpp>
00530 #include <basis/packable.cpp>
00531 #include <basis/portable.cpp>
00532 #include <basis/sequence.cpp>
00533 #include <basis/set.cpp>
00534 #include <basis/utility.cpp>
00535 #include <basis/version_record.cpp>
00536 #include <data_struct/amorph.cpp>
00537 #include <data_struct/bit_vector.cpp>
00538 #include <data_struct/byte_hasher.cpp>
00539 #include <data_struct/configurator.cpp>
00540 #include <data_struct/hash_table.cpp>
00541 #include <data_struct/pointer_hash.cpp>
00542 #include <data_struct/stack.cpp>
00543 #include <data_struct/static_memory_gremlin.cpp>
00544 #include <data_struct/string_hash.cpp>
00545 #include <data_struct/string_hasher.cpp>
00546 #include <data_struct/string_table.cpp>
00547 #include <data_struct/symbol_table.cpp>
00548 #include <data_struct/table_configurator.cpp>
00549 #include <loggers/console_logger.cpp>
00550 #include <loggers/file_logger.cpp>
00551 #include <loggers/locked_logger.cpp>
00552 #include <loggers/null_logger.cpp>
00553 #include <loggers/program_wide_logger.cpp>
00554 #include <opsystem/application_base.cpp>
00555 #include <opsystem/application_shell.cpp>
00556 #include <opsystem/byte_filer.cpp>
00557 #include <opsystem/command_line.cpp>
00558 #include <opsystem/critical_events.cpp>
00559 #include <opsystem/directory.cpp>
00560 #include <opsystem/file_info.cpp>
00561 #include <opsystem/filename.cpp>
00562 #include <opsystem/filename_list.cpp>
00563 #include <opsystem/filetime.cpp>
00564 #include <opsystem/heavy_file_ops.cpp>
00565 #include <opsystem/huge_file.cpp>
00566 #include <opsystem/ini_config.cpp>
00567 #include <opsystem/ini_parser.cpp>
00568 #include <opsystem/path_configuration.cpp>
00569 #include <opsystem/rendezvous.cpp>
00570 #include <textual/byte_format.cpp>
00571 #include <textual/parser_bits.cpp>
00572 #include <textual/string_manipulation.cpp>
00573 #include <textual/tokenizer.cpp>
00574 #endif // __BUILD_STATIC_APPLICATION__
00575