diff --git a/Docs/src/compiler.but b/Docs/src/compiler.but index c4be5a45..fef66994 100644 --- a/Docs/src/compiler.but +++ b/Docs/src/compiler.but @@ -120,9 +120,17 @@ This option makes the compiler use an external EXE packer (such as \W{http://www \c command [compare comparevalue] -This option will execute 'command' using a call to system() after the output EXE has been generated. You can typically use it to sign (Authenticode) your installer. If 'command' contains a '%1' it will be replaced by the executables filename. +This option will execute 'command' using a call to system() after the installer EXE has been generated. You can typically use it to sign (Authenticode) your installer. If 'command' contains a '%1' it will be replaced by the executables filename. -\c !finalize 'sign.bat "%1" "Product Installer" http://example.com' +\c !finalize 'sign.bat "%1" "MyProduct Installer" http://example.com' + +\S1{uninstfinalize} !uninstfinalize + +\c command [compare comparevalue] + +This option will execute 'command' using a call to system() after the uninstaller EXE has been generated. You can typically use it to sign (Authenticode) your uninstaller. If 'command' contains a '%1' it will be replaced by the executables filename. + +\c !uninstfinalize 'sign.bat "%1" "MyProduct Installer" http://example.com' \S1{system} !system diff --git a/Docs/src/history.but b/Docs/src/history.but index c1a7cf50..3c827735 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -10,6 +10,10 @@ Released on ???? ??th, 20?? \S1{v3.08-cl} Changelog +\S2{} Major Changes + +\b Added \R{uninstfinalize}{!uninstfinalize} (\W{http://sf.net/p/nsis/patches/280}{patch #280}, \W{http://sf.net/p/nsis/bugs/1241}{bug #1241}) + \S2{} Minor Changes \b Disallow start maximized mode diff --git a/Source/build.cpp b/Source/build.cpp index c2e5f7cd..b0cd9b14 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -100,8 +100,8 @@ CEXEBuild::~CEXEBuild() for (int i = 0; i < nlt; i++) DeleteLangTable(nla+i); - if (postbuild_cmds) - postbuild_cmds->delete_all(); + if (postbuild_cmds) postbuild_cmds->delete_all(); + if (postubuild_cmds) postubuild_cmds->delete_all(); } CEXEBuild::CEXEBuild(signed char pponly, bool warnaserror) : @@ -191,7 +191,7 @@ CEXEBuild::CEXEBuild(signed char pponly, bool warnaserror) : build_cursection=NULL; // init public data. build_packname[0]=build_packcmd[0]=build_output_filename[0]=0; - postbuild_cmds=NULL; + postbuild_cmds=postubuild_cmds=NULL; // Added by ramon 23 May 2003 build_allowskipfiles=1; @@ -1731,21 +1731,21 @@ int CEXEBuild::add_page(int type) #ifndef NSIS_CONFIG_LICENSEPAGE if (type == PAGE_LICENSE) { - ERROR_MSG(_T("Error: can't add license page, NSIS_CONFIG_LICENSEPAGE not defined.\n")); + ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "license", "NSIS_CONFIG_LICENSEPAGE"); return PS_ERROR; } #endif #ifndef NSIS_CONFIG_COMPONENTPAGE if (type == PAGE_COMPONENTS) { - ERROR_MSG(_T("Error: can't add components page, NSIS_CONFIG_COMPONENTPAGE not defined.\n")); + ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "components", "NSIS_CONFIG_COMPONENTPAGE"); return PS_ERROR; } #endif #ifndef NSIS_CONFIG_UNINSTALL_SUPPORT if (type == PAGE_COMPONENTS) { - ERROR_MSG(_T("Error: can't add uninstConfirm page, NSIS_CONFIG_UNINSTALL_SUPPORT not defined.\n")); + ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "uninstConfirm", "NSIS_CONFIG_UNINSTALL_SUPPORT"); return PS_ERROR; } #endif @@ -2539,14 +2539,11 @@ int CEXEBuild::pack_exe_header() // write out exe header, pack, read back in, and // update the header info - FILE *tmpfile=FOPEN(build_packname,("wb")); - if (!tmpfile) + if (m_exehead_size != write_octets_to_file(build_packname, m_exehead, m_exehead_size)) { ERROR_MSG(_T("Error: writing temporary file \"%") NPRIs _T("\" for pack\n"),build_packname); return PS_ERROR; } - fwrite(m_exehead,1,m_exehead_size,tmpfile); - fclose(tmpfile); int ec = sane_system(build_packcmd); if (ec == -1) { @@ -3064,23 +3061,33 @@ int CEXEBuild::uninstall_generate() { SCRIPT_MSG(_T("Generating uninstaller... ")); + const int start_offset = postubuild_cmds ? truncate_cast(int, m_exehead_size) : 0; + entry *ent = (entry *) build_entries.get(); + if (!ent) return PS_ERROR; // Check this early + + MMapBuf udata; firstheader fh={0,}; GrowBuf uhd; { - GrowBuf udata; + GrowBuf udata_exehead; set_uninstall_mode(1); - PrepareHeaders(&udata); + PrepareHeaders(&udata_exehead); - fh.length_of_header=udata.getlen(); - int err=add_data((char*)udata.get(),udata.getlen(),&uhd); + fh.length_of_header=udata_exehead.getlen(); + int err=add_data((char*)udata_exehead.get(),udata_exehead.getlen(),&uhd); set_uninstall_mode(0); if (err < 0) return PS_ERROR; } crc32_t crc=0; +#ifdef NSIS_CONFIG_CRC_SUPPORT + bool calc_crc = true; +#else + bool calc_crc = false; +#endif // Get offsets of icons to replace for uninstall // Also makes sure that the icons are there and in the right size. @@ -3091,41 +3098,34 @@ int CEXEBuild::uninstall_generate() return PS_ERROR; } - entry *ent = (entry *) build_entries.get(); - if (!ent) - { - delete [] unicon_data; - return PS_ERROR; - } - - int ents = build_header.blocks[NB_ENTRIES].num; int uns = uninstaller_writes_used; int uninstdata_offset = build_datablock.getlen(); - while (ents--) + for (int ents = build_header.blocks[NB_ENTRIES].num; ents--; ent++) { if (ent->which == EW_WRITEUNINSTALLER) { ent->offsets[1] = uninstdata_offset; - ent->offsets[2] = (int) m_unicon_size; - uns--; - if (!uns) + ent->offsets[2] = start_offset ? 0 : (int) m_unicon_size; + if (!--uns) break; } - ent++; } - if (add_db_data((char *)unicon_data,m_unicon_size) < 0) + if (!start_offset && add_db_data((char *)unicon_data,m_unicon_size) < 0) { delete [] unicon_data; return PS_ERROR; } -#ifdef NSIS_CONFIG_CRC_SUPPORT + if (start_offset || calc_crc) { - // "create" the uninstaller + // create the uninstaller LPBYTE uninst_header = (LPBYTE) malloc(m_exehead_size); if (!uninst_header) + { + delete [] unicon_data; return PS_ERROR; + } memcpy(uninst_header, m_exehead, m_exehead_size); @@ -3140,17 +3140,25 @@ int CEXEBuild::uninstall_generate() seeker += dwSize; } - delete [] unicon_data; - + if (calc_crc) + { +#ifdef NSIS_CONFIG_CRC_SUPPORT #ifdef NSIS_CONFIG_CRC_ANAL - crc=CRC32(crc, uninst_header, (DWORD)m_exehead_size); + crc=CRC32(crc, uninst_header, (DWORD)m_exehead_size); #else - crc=CRC32(crc, uninst_header + 512, (DWORD)m_exehead_size - 512); + crc=CRC32(crc, uninst_header + 512, (DWORD)m_exehead_size - 512); #endif +#endif + } + + // write the exehead + if (start_offset) + udata.add((char *)uninst_header, truncate_cast(int, m_exehead_size)); free(uninst_header); } -#endif + delete [] unicon_data; + fh.nsinst[0]=FH_INT1; fh.nsinst[1]=FH_INT2; fh.nsinst[2]=FH_INT3; @@ -3165,8 +3173,6 @@ int CEXEBuild::uninstall_generate() fh.length_of_all_following_data= uhd.getlen()+ubuild_datablock.getlen()+(int)sizeof(firstheader)+(build_crcchk?sizeof(crc32_t):0); - MMapBuf udata; - { growbuf_writer_sink sink(&udata, mk_writer_target_info()); firstheader_writer w(&sink); @@ -3231,8 +3237,8 @@ int CEXEBuild::uninstall_generate() compressor->End(); } - firstheader *_fh=(firstheader *)udata.get(0, sizeof(firstheader)); - _fh->length_of_all_following_data=FIX_ENDIAN_INT32(udata.getlen()+(build_crcchk?sizeof(crc32_t):0)); + firstheader *ufh=(firstheader *)udata.get(start_offset, sizeof(firstheader)); + ufh->length_of_all_following_data=FIX_ENDIAN_INT32((udata.getlen()-start_offset)+(build_crcchk?sizeof(crc32_t):0)); udata.release(); } else @@ -3264,11 +3270,11 @@ int CEXEBuild::uninstall_generate() if (build_crcchk) { int pos = 0; - int left = udata.getlen(); + int left = udata.getlen() - start_offset; while (left > 0) { int l = min(build_filebuflen, left); - crc = CRC32(crc, (unsigned char *) udata.get(pos, l), l); + crc = CRC32(crc, (unsigned char *) udata.get(pos + start_offset, l), l); udata.release(); pos += l; left -= l; @@ -3280,14 +3286,57 @@ int CEXEBuild::uninstall_generate() } #endif - if (add_db_data(&udata) < 0) - return PS_ERROR; + if (start_offset) + { + TCHAR* fpath; + unsigned long in_len; + if (!(fpath = create_tempfile_path())) + { + ERROR_MSG(_T("Error: can't get temporary path\n")); + return PS_ERROR; + } + size_t ret_size = write_octets_to_file(fpath, (unsigned char*)udata.get(0, udata.getlen()), udata.getlen()); + udata.release(); + if ((size_t)udata.getlen() != ret_size) + { + ERROR_MSG(_T("Error: can't write %d bytes to output\n"), udata.getlen()); + free(fpath); + return PS_ERROR; + } + if (PS_OK != run_postbuild_cmds(postubuild_cmds, fpath, _T("UninstFinalize"))) + { + free(fpath); + return PS_ERROR; + } + BYTE* in_buf = alloc_and_read_file(fpath, in_len); + _tremove(fpath); + free(fpath); + if (!in_buf) + { + ERROR_MSG(_T("Error: can't read %d bytes from input\n"), in_len); + return PS_ERROR; + } + MMapBuf udata_in; + udata_in.add(in_buf, in_len); + free(in_buf); + + if (add_db_data(&udata_in) < 0) + return PS_ERROR; + + uninstall_size_full = udata_in.getlen(); + udata_in.clear(); + } + else + { + if (add_db_data(&udata) < 0) + return PS_ERROR; + + //uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int); + uninstall_size_full = fh.length_of_all_following_data+(int)m_unicon_size; + } udata.clear(); - //uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int); - uninstall_size_full=fh.length_of_all_following_data+(int)m_unicon_size; - // compressed size uninstall_size=build_datablock.getlen()-uninstdata_offset; diff --git a/Source/build.h b/Source/build.h index 583ca903..7429bb0c 100644 --- a/Source/build.h +++ b/Source/build.h @@ -390,7 +390,7 @@ class CEXEBuild { int pp_define(LineParser&line); int pp_undef(LineParser&line); int pp_packhdr(LineParser&line); - int pp_finalize(LineParser&line); + int pp_finalize(int which_token, LineParser&line); int pp_execute(int which_token, LineParser&line); int pp_addincludedir(LineParser&line); int pp_include(LineParser&line); @@ -606,7 +606,7 @@ class CEXEBuild { TCHAR cmd[1]; void delete_all(); static postbuild_cmd* make(const TCHAR *cmdstr, int cmpop, int cmpval); - } *postbuild_cmds; + } *postbuild_cmds, *postubuild_cmds; int run_postbuild_cmds(const postbuild_cmd *cmds, const TCHAR *templatearg_pc1, const TCHAR* commandname); int check_external_exitcode(int exitcode, int op, int val); diff --git a/Source/exehead/exec.c b/Source/exehead/exec.c index 73994d3d..7fc8e739 100644 --- a/Source/exehead/exec.c +++ b/Source/exehead/exec.c @@ -1563,36 +1563,41 @@ static int NSISCALL ExecuteEntry(entry *entry_) hFile=myOpenFile(buf1,GENERIC_WRITE,CREATE_ALWAYS); if (hFile != INVALID_HANDLE_VALUE) { - unsigned char *filebuf; - int filehdrsize = g_filehdrsize; - filebuf=(unsigned char *)GlobalAlloc(GPTR,filehdrsize); - if (filebuf) + int dboffset = parm1; + if (parm2) { - SetSelfFilePointer(0); - ReadSelfFile((char*)filebuf,filehdrsize); + unsigned char *filebuf; + int filehdrsize = g_filehdrsize; + filebuf=(unsigned char *)GlobalAlloc(GPTR,filehdrsize); + if (filebuf) { - // parm1 = uninstdata_offset - // parm2 = m_unicon_size - unsigned char* seeker; - unsigned char* unicon_data = seeker = (unsigned char*)GlobalAlloc(GPTR,parm2); - if (unicon_data) { - GetCompressedDataFromDataBlockToMemory(parm1,unicon_data,parm2); - while (*seeker) { - struct icondata { - DWORD dwSize; - DWORD dwOffset; - } id = *(struct icondata *) seeker; - seeker += sizeof(struct icondata); - mini_memcpy(filebuf+id.dwOffset, seeker, id.dwSize); - seeker += id.dwSize; + SetSelfFilePointer(0); + ReadSelfFile((char*)filebuf,filehdrsize); + { + // parm1 = uninstdata_offset + // parm2 = m_unicon_size + unsigned char* seeker; + unsigned char* unicon_data = seeker = (unsigned char*)GlobalAlloc(GPTR,parm2); + if (unicon_data) { + GetCompressedDataFromDataBlockToMemory(parm1,unicon_data,parm2); + while (*seeker) { + struct icondata { + DWORD dwSize; + DWORD dwOffset; + } id = *(struct icondata *) seeker; + seeker += sizeof(struct icondata); + mini_memcpy(filebuf+id.dwOffset, seeker, id.dwSize); + seeker += id.dwSize; + } + GlobalFree(unicon_data); } - GlobalFree(unicon_data); } + myWriteFile(hFile,(char*)filebuf,filehdrsize); + GlobalFree(filebuf); + dboffset = -1; } - myWriteFile(hFile,(char*)filebuf,filehdrsize); - GlobalFree(filebuf); - ret=GetCompressedDataFromDataBlock(-1,hFile); } + ret=GetCompressedDataFromDataBlock(dboffset, hFile); CloseHandle(hFile); } log_printf3(_T("created uninstaller: %d, \"%s\""),ret,buf1); diff --git a/Source/script.cpp b/Source/script.cpp index 8c45a7f0..7e63da11 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -530,7 +530,7 @@ parse_again: extern FILE *g_output; bool pptok = is_pp_token(tkid), docmd = pptok; bool both = TOK_P_VERBOSE == tkid || TOK_P_WARNING == tkid || TOK_P_ECHO == tkid; - if (TOK_P_FINALIZE == tkid || TOK_P_PACKEXEHEADER == tkid) docmd = false; + if (TOK_P_FINALIZE == tkid || TOK_P_UNINSTFINALIZE == tkid || TOK_P_PACKEXEHEADER == tkid) docmd = false; if (docmd && is_unsafe_pp_token(tkid) && preprocessonly > 0) docmd = false; if (!docmd || both) _ftprintf(g_output,(_T("%") NPRIs _T("\n")),ppoline.get()); if (!docmd && !both) return PS_OK; @@ -2553,7 +2553,8 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) case TOK_P_PACKEXEHEADER: return pp_packhdr(line); case TOK_P_FINALIZE: - return pp_finalize(line); + case TOK_P_UNINSTFINALIZE: + return pp_finalize(which_token, line); case TOK_P_SYSTEMEXEC: case TOK_P_EXECUTE: case TOK_P_MAKENSIS: diff --git a/Source/scriptpp.cpp b/Source/scriptpp.cpp index 730f3260..48eea839 100644 --- a/Source/scriptpp.cpp +++ b/Source/scriptpp.cpp @@ -1058,8 +1058,15 @@ template void slist_append(T&list, T&item) (prev ? prev->next : list) = item; } -int CEXEBuild::pp_finalize(LineParser&line) +int CEXEBuild::pp_finalize(int which_token, LineParser&line) { +#ifndef NSIS_CONFIG_UNINSTALL_SUPPORT + if (which_token == TOK_P_UNINSTFINALIZE) + { + ERROR_MSG(_T("Error: %") NPRIs _T(" specified, %") NPRIns _T(" not defined.\n"), line.gettoken_str(0), "NSIS_CONFIG_UNINSTALL_SUPPORT"); + return PS_ERROR; + } +#endif TCHAR* cmdstr = line.gettoken_str(1); int validparams = false; postbuild_cmd *newcmd = postbuild_cmd::make(cmdstr, line.gettoken_enum(2, _T("<\0>\0<>\0=\0ignore\0")), line.gettoken_int(3, &validparams)); @@ -1067,8 +1074,8 @@ int CEXEBuild::pp_finalize(LineParser&line) newcmd->cmpop = 4, validparams = true; // Just a command, ignore the exit code if (newcmd->cmpop == -1 || !validparams) PRINTHELP(); - slist_append(postbuild_cmds, newcmd); - SCRIPT_MSG(_T("!finalize: \"%") NPRIs _T("\"\n"), cmdstr); + slist_append(which_token == TOK_P_UNINSTFINALIZE ? postubuild_cmds : postbuild_cmds, newcmd); + SCRIPT_MSG(_T("!%") NPRIns _T("finalize: \"%") NPRIs _T("\"\n"), which_token == TOK_P_UNINSTFINALIZE ? "uninst" : "", cmdstr); return PS_OK; } diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 80e7f4c1..7764aa63 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -273,6 +273,7 @@ static tokenType tokenlist[TOK__LAST] = {TOK_MANIFEST_GDISCALING,_T("ManifestGdiScaling"),1,0,_T("notset|true"),TP_GLOBAL}, {TOK_P_PACKEXEHEADER,_T("!packhdr"),2,0,_T("temp_file_name command_line_to_compress_that_temp_file"),TP_ALL}, {TOK_P_FINALIZE,_T("!finalize"),1,2,_T("command_with_%1 []"),TP_ALL}, +{TOK_P_UNINSTFINALIZE,_T("!uninstfinalize"),1,2,_T("command_with_%1 []"),TP_ALL}, {TOK_P_SYSTEMEXEC,_T("!system"),1,2,_T("command [ | ]\n OP=(< > <> =)"),TP_ALL}, {TOK_P_EXECUTE,_T("!execute"),1,2,_T("command [ | ]\n OP=(< > <> =)"),TP_ALL}, {TOK_P_MAKENSIS,_T("!makensis"),1,2,_T("parameters [ | ]"),TP_ALL}, diff --git a/Source/tokens.h b/Source/tokens.h index 49dd6fef..c0978370 100644 --- a/Source/tokens.h +++ b/Source/tokens.h @@ -117,6 +117,7 @@ enum TOK_P_UNDEF, TOK_P_PACKEXEHEADER, TOK_P_FINALIZE, + TOK_P_UNINSTFINALIZE, TOK_P_SYSTEMEXEC, TOK_P_EXECUTE, TOK_P_MAKENSIS, diff --git a/Source/util.cpp b/Source/util.cpp index d9f7555a..dfedb3ed 100644 --- a/Source/util.cpp +++ b/Source/util.cpp @@ -714,6 +714,15 @@ char* create_file_view_readonly(const TCHAR *filepath, FILEVIEW&mmfv) #endif } +size_t write_octets_to_file(const TCHAR *filename, const void *data, size_t cb) +{ + FILE *hfile = FOPEN(filename, ("wb")); + if (!hfile) return 0; + size_t ret = fwrite(data, 1, cb, hfile); + fclose(hfile); + return ret; +} + TCHAR* create_tempfile_path() { TCHAR *tfpath = NULL; diff --git a/Source/util.h b/Source/util.h index 828f7dbf..51ea3c0c 100644 --- a/Source/util.h +++ b/Source/util.h @@ -287,6 +287,8 @@ typedef struct { char*base; size_t internal; } FILEVIEW; void close_file_view(FILEVIEW&mmfv); char* create_file_view_readonly(const TCHAR *filepath, FILEVIEW&mmfv); +size_t write_octets_to_file(const TCHAR *filename, const void *data, size_t cb); + // round a value up to be a multiple of 512 // assumption: T is an int type template inline T align_to_512(const T x) { return (x+511) & ~511; }