| 1 | /* ------------------------------------------------------------------------ */ |
| 2 | /* */ |
| 3 | /* Main file of public UNACE. */ |
| 4 | /* */ |
| 5 | /* ------------------------------------------------------------------------ */ |
| 6 | |
| 7 | |
| 8 | #define INCL_WIN /* Window Manager Functions */ |
| 9 | #define INCL_DOS |
| 10 | #define INCL_BASE |
| 11 | |
| 12 | |
| 13 | //--------------- include general files ------------------------------------// |
| 14 | #include <ctype.h> // tolower() |
| 15 | #include <fcntl.h> // open() |
| 16 | #include <stdio.h> // printf() sprintf() remove() |
| 17 | #include <stdlib.h> // malloc() |
| 18 | #include <string.h> // str*() |
| 19 | #include <sys/types.h> |
| 20 | #include <sys/stat.h> // S_I* AMIGA: fstat() |
| 21 | #if (!defined(__EMX__) && !defined(__OS2__) && !defined(WINNT) && !defined(WIN32)) || defined(__CYGWIN__) |
| 22 | #include <sys/errno.h> |
| 23 | #endif |
| 24 | |
| 25 | #if !defined(__EMX__) && !defined(__OS2__) && !defined(WIN32) && !defined(WINNT) |
| 26 | #define stricmp strcasecmp |
| 27 | #endif |
| 28 | |
| 29 | #include "install.h" |
| 30 | #include "instsup.h" |
| 31 | extern int installstate; |
| 32 | |
| 33 | off_t acelseek(off_t offset, int whence); |
| 34 | int aceread(void *buf, size_t count); |
| 35 | int aceopen(const char *path, int flags); |
| 36 | int aceclose(int fd); |
| 37 | int acesize(void); |
| 38 | int acetell(int fd); |
| 39 | char *replacestr(char *str1, char *str2, char *str3); |
| 40 | |
| 41 | //--------------- include unace specific header files ----------------------// |
| 42 | #include "os.h" |
| 43 | |
| 44 | #include "globals.h" |
| 45 | #include "portable.h" |
| 46 | #include "uac_comm.h" |
| 47 | #include "uac_crc.h" |
| 48 | #include "uac_crt.h" |
| 49 | #include "uac_dcpr.h" |
| 50 | #include "uac_sys.h" |
| 51 | |
| 52 | #ifdef CRYPT |
| 53 | #include "unace_ps.h" |
| 54 | #endif /* CRYPT */ |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | //--------------- BEGIN OF UNACE ROUTINES ----------------------------------// |
| 60 | |
| 61 | int pipeit(char *format, ...) |
| 62 | { |
| 63 | /* Do nothing ... perhaps pipe this somewhere in the future */ |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | void init_unace(void) // initializes unace |
| 68 | { |
| 69 | buf_rd =malloc(size_rdb * sizeof(ULONG)); // Allocate buffers: increase |
| 70 | buf =malloc(size_buf); // sizes when possible to speed |
| 71 | buf_wr =malloc(size_wrb); // up the program |
| 72 | readbuf=malloc(size_headrdb); |
| 73 | |
| 74 | if (buf_rd ==NULL || |
| 75 | buf ==NULL || |
| 76 | buf_wr ==NULL || |
| 77 | readbuf==NULL ) |
| 78 | f_err = ERR_MEM; |
| 79 | |
| 80 | make_crctable(); // initialize CRC table |
| 81 | dcpr_init(); // initialize decompression |
| 82 | |
| 83 | set_handler(); // ctrl+break etc. |
| 84 | } |
| 85 | |
| 86 | void done_unace(void) |
| 87 | { |
| 88 | if (buf_rd ) free(buf_rd ); |
| 89 | if (buf ) free(buf ); |
| 90 | if (buf_wr ) free(buf_wr ); |
| 91 | if (readbuf ) free(readbuf ); |
| 92 | if (dcpr_text) free(dcpr_text); |
| 93 | } |
| 94 | |
| 95 | INT read_header(INT print_err) // reads any header from archive |
| 96 | { |
| 97 | USHORT rd, |
| 98 | head_size, |
| 99 | crc_ok; |
| 100 | LONG crc; |
| 101 | UCHAR *tp=readbuf; |
| 102 | |
| 103 | acelseek(skipsize, SEEK_CUR); // skip ADDSIZE block |
| 104 | |
| 105 | if (aceread(&head, 4)<4) |
| 106 | return (0); // read CRC and header size |
| 107 | |
| 108 | #ifdef HI_LO_BYTE_ORDER |
| 109 | WORDswap(&head.HEAD_CRC); |
| 110 | WORDswap(&head.HEAD_SIZE); |
| 111 | #endif |
| 112 | // read size_headrdb bytes into |
| 113 | head_size = head.HEAD_SIZE; // header structure |
| 114 | rd = (head_size > size_headrdb) ? size_headrdb : head_size; |
| 115 | if (aceread(readbuf, rd) < rd) |
| 116 | return (0); |
| 117 | head_size -= rd; |
| 118 | crc = getcrc(CRC_MASK, readbuf, rd); |
| 119 | |
| 120 | while (head_size) // skip rest of header |
| 121 | { |
| 122 | rd = (head_size > size_buf) ? size_buf : head_size; |
| 123 | if (aceread(buf, rd) < rd) |
| 124 | return (0); |
| 125 | head_size -= rd; |
| 126 | crc = getcrc(crc, (UCHAR*)buf, rd); |
| 127 | } |
| 128 | |
| 129 | head.HEAD_TYPE =*tp++; // generic buffer to head conversion |
| 130 | head.HEAD_FLAGS=BUFP2WORD(tp); |
| 131 | |
| 132 | if (head.HEAD_FLAGS & ACE_ADDSIZE) |
| 133 | skipsize = head.ADDSIZE = BUF2LONG(tp); // get ADDSIZE |
| 134 | else |
| 135 | skipsize = 0; |
| 136 | |
| 137 | // check header CRC |
| 138 | if (!(crc_ok = head.HEAD_CRC == (crc & 0xffff)) && print_err) |
| 139 | pipeit("\nError: archive is broken\n"); |
| 140 | else |
| 141 | switch (head.HEAD_TYPE) // specific buffer to head conversion |
| 142 | { |
| 143 | case MAIN_BLK: |
| 144 | memcpy(mhead.ACESIGN, tp, acesign_len); tp+=acesign_len; |
| 145 | mhead.VER_MOD=*tp++; |
| 146 | mhead.VER_CR =*tp++; |
| 147 | mhead.HOST_CR=*tp++; |
| 148 | mhead.VOL_NUM=*tp++; |
| 149 | mhead.TIME_CR=BUFP2LONG(tp); |
| 150 | mhead.RES1 =BUFP2WORD(tp); |
| 151 | mhead.RES2 =BUFP2WORD(tp); |
| 152 | mhead.RES =BUFP2LONG(tp); |
| 153 | mhead.AV_SIZE=*tp++; |
| 154 | memcpy(mhead.AV, tp, rd-(USHORT)(tp-readbuf)); |
| 155 | break; |
| 156 | case FILE_BLK: |
| 157 | fhead.PSIZE =BUFP2LONG(tp); |
| 158 | fhead.SIZE =BUFP2LONG(tp); |
| 159 | fhead.FTIME =BUFP2LONG(tp); |
| 160 | fhead.ATTR =BUFP2LONG(tp); |
| 161 | fhead.CRC32 =BUFP2LONG(tp); |
| 162 | fhead.TECH.TYPE =*tp++; |
| 163 | fhead.TECH.QUAL =*tp++; |
| 164 | fhead.TECH.PARM =BUFP2WORD(tp); |
| 165 | fhead.RESERVED =BUFP2WORD(tp); |
| 166 | fhead.FNAME_SIZE=BUFP2WORD(tp); |
| 167 | memcpy(fhead.FNAME, tp, rd-(USHORT)(tp-readbuf)); |
| 168 | break; |
| 169 | // default: (REC_BLK and future things): |
| 170 | // do nothing 'cause isn't needed for extraction |
| 171 | } |
| 172 | |
| 173 | return (crc_ok); |
| 174 | } |
| 175 | // maximum SFX module size |
| 176 | #define max_sfx_size 65536 // (needed by read_arc_head) |
| 177 | |
| 178 | INT read_arc_head(void) // searches for the archive header and reads it |
| 179 | { |
| 180 | INT i, |
| 181 | flags, |
| 182 | buf_pos = 0; |
| 183 | LONG arc_head_pos, |
| 184 | old_fpos, |
| 185 | fpos = 0; |
| 186 | struct stat st; |
| 187 | |
| 188 | fstat(archan, &st); |
| 189 | |
| 190 | memset(buf, 0, size_buf); |
| 191 | |
| 192 | while (acetell(archan)<acesize() && fpos < max_sfx_size) |
| 193 | { |
| 194 | old_fpos = fpos; |
| 195 | fpos += aceread(&buf[buf_pos], size_buf - buf_pos); |
| 196 | |
| 197 | for (i = 0; i < size_buf; i++) // look for the acesign |
| 198 | { |
| 199 | if (!memcmp(acesign, &buf[i], acesign_len)) |
| 200 | { |
| 201 | // seek to the probable begin |
| 202 | // of the archive |
| 203 | arc_head_pos = old_fpos + i - buf_pos - bytes_before_acesign; |
| 204 | acelseek(arc_head_pos, SEEK_SET); |
| 205 | if (read_header(0)) // try to read archive header |
| 206 | { |
| 207 | flags = mhead.HEAD_FLAGS; |
| 208 | adat.sol = (flags & ACE_SOLID) > 0; |
| 209 | adat.vol = (flags & ACE_MULT_VOL) > 0; |
| 210 | adat.vol_num = mhead.VOL_NUM; |
| 211 | adat.time_cr = mhead.TIME_CR; |
| 212 | return (1); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | // was no archive header, |
| 217 | // continue search |
| 218 | acelseek(fpos, SEEK_SET); |
| 219 | memcpy(buf, &buf[size_buf - 512], 512); |
| 220 | buf_pos = 512; // keep 512 old bytes |
| 221 | } |
| 222 | return (0); |
| 223 | } |
| 224 | |
| 225 | INT open_archive(INT print_err) // opens archive (or volume) |
| 226 | { |
| 227 | CHAR av_str[80]; |
| 228 | |
| 229 | #if defined(__OS2_) || defined(__EMX__) || defined(WIN32) |
| 230 | archan = aceopen(aname, O_RDONLY | O_BINARY); // open file |
| 231 | #else |
| 232 | archan = aceopen(aname, O_RDONLY); // open file |
| 233 | #endif |
| 234 | #if !defined(__EMX__) && !defined(__OS2__) |
| 235 | farchan = fdopen(archan, "rb"); |
| 236 | #endif |
| 237 | |
| 238 | if (archan == -1) |
| 239 | { |
| 240 | error("\nError opening file %s", aname); |
| 241 | return (0); |
| 242 | } |
| 243 | if (!read_arc_head()) // read archive header |
| 244 | { |
| 245 | error("Invalid embedded archive file."); |
| 246 | #if !defined(__EMX__) && !defined(__OS2__) |
| 247 | fclose(farchan); |
| 248 | #endif |
| 249 | aceclose(archan); |
| 250 | return (0); |
| 251 | } |
| 252 | |
| 253 | pipeit("\nProcessing archive: %s\n\n", aname); |
| 254 | if (head.HEAD_FLAGS & ACE_AV) |
| 255 | { |
| 256 | pipeit("Authenticity Verification:"); // print the AV |
| 257 | sprintf(av_str, "\ncreated on %d.%d.%d by ", |
| 258 | ts_day(adat.time_cr), ts_month(adat.time_cr), ts_year(adat.time_cr)); |
| 259 | pipeit(av_str); |
| 260 | strncpy(av_str, (CHAR*)mhead.AV, mhead.AV_SIZE); |
| 261 | av_str[mhead.AV_SIZE] = 0; |
| 262 | pipeit("%s\n\n", av_str); |
| 263 | } |
| 264 | comment_out("Main comment:"); // print main comment |
| 265 | return (1); |
| 266 | } |
| 267 | |
| 268 | void get_next_volname(void) // get file name of next volume |
| 269 | { |
| 270 | CHAR *cp; |
| 271 | INT num; |
| 272 | |
| 273 | if ((cp = (CHAR *) strrchr(aname, '.')) == NULL || !*(cp + 1)) |
| 274 | num = -1; |
| 275 | else |
| 276 | { |
| 277 | cp++; |
| 278 | num = (*(cp + 1) - '0') * 10 + *(cp + 2) - '0'; |
| 279 | if (!in(num, 0, 99)) |
| 280 | num = -1; |
| 281 | if (in(*cp, '0', '9')) |
| 282 | num += (*cp - '0') * 100; |
| 283 | } |
| 284 | num++; |
| 285 | |
| 286 | if (num < 100) |
| 287 | *cp = 'C'; |
| 288 | else |
| 289 | *cp = num / 100 + '0'; |
| 290 | *(cp + 1) = (num / 10) % 10 + '0'; |
| 291 | *(cp + 2) = num % 10 + '0'; |
| 292 | } |
| 293 | |
| 294 | INT proc_vol(void) // opens volume |
| 295 | { |
| 296 | INT i; |
| 297 | CHAR s[80]; |
| 298 | |
| 299 | if (!fileexists(aname) || !f_allvol_pr) |
| 300 | { |
| 301 | do |
| 302 | { |
| 303 | sprintf(s, "Ready to process %s?", aname); |
| 304 | #if defined(__MINGW32__) |
| 305 | beep(500,500); |
| 306 | #else |
| 307 | beep(); |
| 308 | #endif |
| 309 | i = confirm(s); // ask whether ready or not |
| 310 | f_allvol_pr = (i == 1); // "Always" --> process all volumes |
| 311 | if (i >= 2) |
| 312 | { |
| 313 | f_err = ERR_FOUND; |
| 314 | return (0); |
| 315 | } |
| 316 | } |
| 317 | while (!fileexists(aname)); |
| 318 | } |
| 319 | |
| 320 | if (!open_archive(1)) // open volume |
| 321 | { |
| 322 | pipeit("\nError while opening archive. File not found or archive broken.\n"); |
| 323 | f_err = ERR_OPEN; |
| 324 | return (0); |
| 325 | } |
| 326 | |
| 327 | return (1); |
| 328 | } |
| 329 | |
| 330 | INT proc_next_vol(void) // opens next volume to process |
| 331 | { |
| 332 | #if !defined(__EMX__) && !defined(__OS2__) |
| 333 | fclose(farchan); |
| 334 | #endif |
| 335 | aceclose(archan); // close handle |
| 336 | get_next_volname(); // get file name of next volume |
| 337 | |
| 338 | if (!proc_vol()) // try to open volume, read archive header |
| 339 | return 0; |
| 340 | if (!read_header(1)) // read 2nd header |
| 341 | { |
| 342 | f_err=ERR_READ; |
| 343 | return 0; |
| 344 | } |
| 345 | return 1; |
| 346 | } |
| 347 | |
| 348 | INT read_adds_blk(CHAR * buffer, INT len) // reads part of ADD_SIZE block |
| 349 | { |
| 350 | INT rd = 0, |
| 351 | l = len; |
| 352 | LONG i; |
| 353 | |
| 354 | #ifdef CRYPT |
| 355 | char *cbuffer=buffer; |
| 356 | |
| 357 | if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW)) |
| 358 | len = crypt_len(len); |
| 359 | #endif /* CRYPT */ |
| 360 | while (!f_err && len && skipsize) |
| 361 | { |
| 362 | i = (skipsize > len) ? len : skipsize; |
| 363 | skipsize -= i; |
| 364 | |
| 365 | /* How do I test failure when compiling -mno-cygwin? */ |
| 366 | #if !defined(__MINGW32__) |
| 367 | errno = 0; |
| 368 | #endif |
| 369 | rd += aceread(buffer, i); |
| 370 | #if !defined(__MINGW32__) |
| 371 | if (errno) |
| 372 | { |
| 373 | error("\nRead error\n"); |
| 374 | f_err = ERR_READ; |
| 375 | } |
| 376 | #endif |
| 377 | |
| 378 | buffer += i; |
| 379 | len -= i; |
| 380 | |
| 381 | if (!skipsize) // if block is continued on next volume |
| 382 | if (head.HEAD_FLAGS & ACE_SP_AFTER && !proc_next_vol()) |
| 383 | break; |
| 384 | } |
| 385 | #ifdef CRYPT |
| 386 | if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW)) |
| 387 | decrypt(cbuffer, rd); |
| 388 | #endif /* CRYPT */ |
| 389 | |
| 390 | return (rd > l ? l : rd); |
| 391 | } |
| 392 | |
| 393 | void crc_print(void) // checks CRC, prints message |
| 394 | { |
| 395 | INT crc_not_ok = rd_crc != fhead.CRC32; /* check CRC of file */ |
| 396 | |
| 397 | if(crc_not_ok && installstate != ABORTED) |
| 398 | error("CRC error reading archive!"); |
| 399 | } |
| 400 | |
| 401 | void analyze_file(void) // analyzes one file (for solid archives) |
| 402 | { |
| 403 | pipeit("\n Analyzing"); |
| 404 | flush; |
| 405 | while (!cancel() && (dcpr_adds_blk(buf_wr, size_wrb))) // decompress only |
| 406 | ; |
| 407 | crc_print(); |
| 408 | } |
| 409 | |
| 410 | void extract_file(void) // extracts one file |
| 411 | { |
| 412 | INT rd; |
| 413 | |
| 414 | pipeit("\n Extracting"); |
| 415 | flush; // decompress block |
| 416 | while (!cancel() && (rd = dcpr_adds_blk(buf_wr, size_wrb))) |
| 417 | { |
| 418 | if (write(wrhan, buf_wr, rd) != rd) // write block |
| 419 | { |
| 420 | error("\nWrite error\n"); |
| 421 | f_err = ERR_WRITE; |
| 422 | } |
| 423 | } |
| 424 | crc_print(); |
| 425 | } |
| 426 | |
| 427 | |
| 428 | /* extracts or tests all files of the archive |
| 429 | */ |
| 430 | void extract_files(int nopath, int test, char *getfilename) |
| 431 | { |
| 432 | CHAR file[PATH_MAX]; |
| 433 | char *tmpfile; |
| 434 | extern unsigned current_file; |
| 435 | extern char installdir2[]; |
| 436 | |
| 437 | while (!cancel() && read_header(1)) |
| 438 | { |
| 439 | if (head.HEAD_TYPE == FILE_BLK) |
| 440 | { |
| 441 | if(installstate == ABORTED) |
| 442 | return; |
| 443 | current_file++; |
| 444 | if(installstate == INSTALLING) |
| 445 | DoGUI(); |
| 446 | |
| 447 | comment_out("File comment:"); // show file comment |
| 448 | ace_fname(file, &head, nopath); // get file name |
| 449 | |
| 450 | /* We allow expansion of "userdir" to the selected directory by the user */ |
| 451 | tmpfile = replacestr(file, "USERDIR", installdir2); |
| 452 | strcpy(file, tmpfile); |
| 453 | free(tmpfile); |
| 454 | |
| 455 | if(!getfilename || (getfilename && stricmp(getfilename, file) == 0)) |
| 456 | { |
| 457 | pipeit("\n%s", file); |
| 458 | flush; |
| 459 | dcpr_init_file(); // initialize decompression of file |
| 460 | if (!f_err) |
| 461 | { |
| 462 | if (test || |
| 463 | (wrhan = create_dest_file(file, (INT) fhead.ATTR))<0) |
| 464 | { |
| 465 | if (test || adat.sol) |
| 466 | analyze_file(); // analyze file |
| 467 | } |
| 468 | else |
| 469 | { |
| 470 | extract_file(); // extract it |
| 471 | #ifdef DOS // set file time |
| 472 | _dos_setftime(wrhan, (USHORT) (fhead.FTIME >> 16), (USHORT) fhead.FTIME); |
| 473 | #endif |
| 474 | close(wrhan); |
| 475 | #if defined(__OS2__) || defined(__EMX__) |
| 476 | { |
| 477 | FILESTATUS3 fileinfo; |
| 478 | |
| 479 | DosQueryPathInfo(file, FIL_STANDARD, &fileinfo, sizeof(FILESTATUS3)); |
| 480 | *((USHORT*)&fileinfo.fdateCreation) = (USHORT)(fhead.FTIME >> 16); |
| 481 | *((USHORT*)&fileinfo.ftimeCreation) = (USHORT)fhead.FTIME; |
| 482 | *((USHORT*)&fileinfo.fdateLastAccess) = (USHORT)(fhead.FTIME >> 16); |
| 483 | *((USHORT*)&fileinfo.ftimeLastAccess) = (USHORT)fhead.FTIME; |
| 484 | *((USHORT*)&fileinfo.fdateLastWrite) = (USHORT)(fhead.FTIME >> 16); |
| 485 | *((USHORT*)&fileinfo.ftimeLastWrite) = (USHORT)fhead.FTIME; |
| 486 | DosSetPathInfo(file, FIL_STANDARD, (PVOID)&fileinfo, sizeof(FILESTATUS3), 0); |
| 487 | } |
| 488 | #endif |
| 489 | #ifdef DOS // set file attributes |
| 490 | _dos_setfileattr(file, (UINT) fhead.ATTR); |
| 491 | #endif |
| 492 | #ifdef AMIGA |
| 493 | { // set file date and time |
| 494 | struct DateTime dt; |
| 495 | char Date[9], Time[9]; |
| 496 | ULONG tstamp=fhead.FTIME; |
| 497 | |
| 498 | sprintf(Date, "%02d-%02d-%02d", ts_year(tstamp)-1900, ts_month(tstamp), ts_day(tstamp)); |
| 499 | sprintf(Time, "%02d:%02d:%02d", ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp)); |
| 500 | |
| 501 | dt.dat_Format = FORMAT_INT; |
| 502 | dt.dat_Flags = 0; |
| 503 | dt.dat_StrDate= Date; |
| 504 | dt.dat_StrTime= Time; |
| 505 | |
| 506 | if (StrToDate(&dt)) |
| 507 | SetFileDate(file, &dt.dat_Stamp); |
| 508 | } |
| 509 | #endif |
| 510 | if (f_err) |
| 511 | remove(file); |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | } |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | unsigned percentage(ULONG p, ULONG d) |
| 520 | { |
| 521 | return (unsigned)( d ? (d/2+p*100)/d : 100 ); |
| 522 | } |
| 523 | |
| 524 | int installer_unpack(CHAR * filename, int operation) // processes the archive |
| 525 | { |
| 526 | init_unace(); // initialize unace |
| 527 | strcpy(aname, "installer"); // get archive name |
| 528 | |
| 529 | if(filename) |
| 530 | f_ovrall=1; |
| 531 | |
| 532 | if (open_archive(1)) // open archive to process |
| 533 | { |
| 534 | if (adat.vol_num) |
| 535 | pipeit("\nFirst volume of archive required!\n"); |
| 536 | else |
| 537 | { |
| 538 | if(operation == 2) |
| 539 | extract_files(0, 0, filename); |
| 540 | } |
| 541 | #if !defined(__EMX__) && !defined(__OS2__) |
| 542 | fclose(farchan); |
| 543 | #endif |
| 544 | aceclose(archan); |
| 545 | if (f_err) |
| 546 | { |
| 547 | pipeit("\nError occurred\n"); |
| 548 | if (f_criterr) |
| 549 | pipeit("Critical error on drive %c\n", f_criterr); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | done_unace(); |
| 554 | return 0; |
| 555 | } |