Index: Docs/src/compiler.but
===================================================================
--- Docs/src/compiler.but	(revision 6823)
+++ Docs/src/compiler.but	(working copy)
@@ -124,6 +124,14 @@
 
 \c !finalize 'sign.bat "%1" "Product Installer" http://example.com'
 
+\S1{unfinalize} !unfinalize
+
+\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 !unfinalize 'sign.bat "%1" "Product Installer" http://example.com'
+
 \S1{system} !system
 
 \c command [compare comparevalue | symbol]
Index: Source/tokens.cpp
===================================================================
--- Source/tokens.cpp	(revision 6823)
+++ Source/tokens.cpp	(working copy)
@@ -248,6 +248,7 @@
 {TOK_MANIFEST_SUPPORTEDOS,_T("ManifestSupportedOS"),1,-1,_T("none|all|WinVista|Win7|Win8|Win8.1|Win10|{GUID} [...]"),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 [<OP retval>]"),TP_ALL},
+{TOK_P_UNFINALIZE,_T("!unfinalize"),1,2,_T("command_with_%1 [<OP retval>]"),TP_ALL},
 {TOK_P_SYSTEMEXEC,_T("!system"),1,2,_T("command [<OP retval> | <retvalsymbol>]\n    OP=(< > <> =)"),TP_ALL},
 {TOK_P_EXECUTE,  _T("!execute"),1,2,_T("command [<OP retval> | <retvalsymbol>]\n    OP=(< > <> =)"),TP_ALL},
 {TOK_P_MAKENSIS,_T("!makensis"),1,2,_T("parameters [<OP retval> | <retvalsymbol>]"),TP_ALL},
Index: Source/util.cpp
===================================================================
--- Source/util.cpp	(revision 6823)
+++ Source/util.cpp	(working copy)
@@ -604,6 +604,38 @@
   return f;
 }
 
+int get_temp_file_path(TCHAR *path, int len)
+{
+  if (!path) return 0;
+#ifdef _WIN32
+  TCHAR buf[MAX_PATH], buf2[MAX_PATH];
+  GetTempPath(MAX_PATH, buf);
+  if (!GetTempFileName(buf, _T("nst"), 0, buf2)) return 0;
+  my_strncpy(path, buf2, len);
+#else // !_WIN32
+  char t[] = ("/tmp/makensisXXXXXX");
+  const mode_t old_umask = umask(0077);
+  int fd = mkstemp(t);
+  umask(old_umask);
+  if (fd == -1) {
+#ifdef _UNICODE
+    L_tok_p_tempfile_oom:
+#endif
+    return 0;
+  }
+  close(fd);
+#ifdef _UNICODE
+  TCHAR *retstr = NSISRT_mbtowc(t);
+  if (!retstr) goto L_tok_p_tempfile_oom;
+  my_strncpy(path, retstr, len);
+  NSISRT_free(retstr);
+#else
+  my_strncpy(path, t, len);
+#endif
+#endif // ~_WIN32
+  return 1;
+}
+
 size_t my_strftime(TCHAR *s, size_t max, const TCHAR  *fmt, const struct tm *tm) {
   return _tcsftime(s, max, fmt, tm);
 }
Index: Source/exehead/exec.c
===================================================================
--- Source/exehead/exec.c	(revision 6823)
+++ Source/exehead/exec.c	(working copy)
@@ -1557,36 +1557,7 @@
         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)
-          {
-            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);
-              }
-            }
-            myWriteFile(hFile,(char*)filebuf,filehdrsize);
-            GlobalFree(filebuf);
-            ret=GetCompressedDataFromDataBlock(-1,hFile);
-          }
+          ret=GetCompressedDataFromDataBlock(parm2,hFile);
           CloseHandle(hFile);
         }
         log_printf3(_T("created uninstaller: %d, \"%s\""),ret,buf1);
Index: Source/build.h
===================================================================
--- Source/build.h	(revision 6823)
+++ Source/build.h	(working copy)
@@ -452,7 +452,8 @@
       struct postbuild_cmd*next;
       int cmpop, cmpval;
       TCHAR cmd[1];
-    } *postbuild_cmds;
+    } *postbuild_cmds, *postubuild_cmds;
+    int run_postbuild_cmds(postbuild_cmd *in_cmds, const TCHAR* pc1_filename, const TCHAR *token_str);
     int check_external_exitcode(int exitcode, int op, int val);
 
     TCHAR build_packname[1024], build_packcmd[1024];
Index: Source/build.cpp
===================================================================
--- Source/build.cpp	(revision 6823)
+++ Source/build.cpp	(working copy)
@@ -191,6 +191,7 @@
   // init public data.
   build_packname[0]=build_packcmd[0]=build_output_filename[0]=0;
   postbuild_cmds=NULL;
+  postubuild_cmds=NULL;
 
   // Added by ramon 23 May 2003
   build_allowskipfiles=1;
@@ -3001,40 +3002,7 @@
       fileend,total_usize,pc/10,pc%10);
   }
   fclose(fp);
-  if (postbuild_cmds)
-  {
-    for (struct postbuild_cmd *cmd=postbuild_cmds; cmd; cmd = cmd->next)
-    {
-      TCHAR *cmdstr = cmd->cmd, *cmdstrbuf = NULL;
-      TCHAR *arg = _tcsstr(cmdstr, _T("%1"));
-      if (arg)    // if found, replace %1 by build_output_filename
-      {
-        const size_t cchbldoutfile = _tcslen(build_output_filename);
-        cmdstrbuf = (TCHAR*) malloc( (_tcslen(cmdstr) + cchbldoutfile + 1)*sizeof(TCHAR) );
-        if (!cmdstrbuf)
-        {
-          ERROR_MSG(_T("Error: can't allocate memory for finalize command\n"));
-          return PS_ERROR;
-        }
-        arg -= ((UINT_PTR)cmdstr)/sizeof(TCHAR), arg += ((UINT_PTR)cmdstrbuf)/sizeof(TCHAR);
-        _tcscpy(cmdstrbuf,cmdstr);
-        cmdstr = cmdstrbuf;
-        memmove(arg+cchbldoutfile, arg+2, (_tcslen(arg+2)+1)*sizeof(TCHAR));
-        memmove(arg, build_output_filename, cchbldoutfile*sizeof(TCHAR));
-        //BUGBUG: Should we call PathConvertWinToPosix on build_output_filename?
-      }
-
-      SCRIPT_MSG(_T("\nFinalize command: %") NPRIs _T("\n"),cmdstr);
-      int ret = sane_system(cmdstr);
-      if (!check_external_exitcode(ret, cmd->cmpop, cmd->cmpval))
-      {
-        ERROR_MSG(_T("%") NPRIs _T(" %d, aborting\n"), _T("Finalize command returned"), ret);
-        return PS_ERROR;
-      }
-      if (ret != 0) INFO_MSG(_T("%") NPRIs _T(" %d\n"), _T("Finalize command returned"), ret);
-      free(cmdstrbuf);
-    }
-  }
+  if (postbuild_cmds) run_postbuild_cmds(postbuild_cmds, build_output_filename, _T("Finalize"));
   print_warnings();
   return PS_OK;
 }
@@ -3085,18 +3053,22 @@
   {
     SCRIPT_MSG(_T("Generating uninstaller... "));
 
+    MMapBuf udata;
     firstheader fh={0,};
 
+    entry *ent = (entry *) build_entries.get();
+    if (!ent) return PS_ERROR; // better to check this early
+
     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;
     }
@@ -3112,44 +3084,17 @@
       return PS_ERROR;
     }
 
-    entry *ent = (entry *) build_entries.get();
-    if (!ent)
+    // create the uninstaller
+    LPBYTE uninst_header = (LPBYTE) malloc(m_exehead_size);
+    if (!uninst_header)
     {
       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--)
-    {
-      if (ent->which == EW_WRITEUNINSTALLER)
-      {
-        ent->offsets[1] = uninstdata_offset;
-        ent->offsets[2] = (int) m_unicon_size;
-        uns--;
-        if (!uns)
-          break;
-      }
-      ent++;
-    }
+    memcpy(uninst_header, m_exehead, m_exehead_size);
 
-    if (add_db_data((char *)unicon_data,m_unicon_size) < 0)
     {
-      delete [] unicon_data;
-      return PS_ERROR;
-    }
-
-#ifdef NSIS_CONFIG_CRC_SUPPORT
-    {
-      // "create" the uninstaller
-      LPBYTE uninst_header = (LPBYTE) malloc(m_exehead_size);
-      if (!uninst_header)
-        return PS_ERROR;
-
-      memcpy(uninst_header, m_exehead, m_exehead_size);
-
       // patch the icons
       LPBYTE seeker = unicon_data;
       while (*seeker) {
@@ -3160,18 +3105,22 @@
         memcpy(uninst_header + dwOffset, seeker, dwSize);
         seeker += dwSize;
       }
+    }
 
-      delete [] unicon_data;
+    delete [] unicon_data;
 
+#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
 
-      free(uninst_header);
-    }
-#endif
+    // write the exehead
+    udata.add((char *)uninst_header, m_exehead_size);
+    free(uninst_header);
+
     fh.nsinst[0]=FH_INT1;
     fh.nsinst[1]=FH_INT2;
     fh.nsinst[2]=FH_INT3;
@@ -3186,8 +3135,6 @@
     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);
@@ -3252,8 +3199,8 @@
         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 *_fh=(firstheader *)udata.get(m_exehead_size, sizeof(firstheader));
+      _fh->length_of_all_following_data=FIX_ENDIAN_INT32((udata.getlen()-m_exehead_size)+(build_crcchk?sizeof(crc32_t):0));
       udata.release();
     }
     else
@@ -3285,11 +3232,11 @@
     if (build_crcchk)
     {
       int pos = 0;
-      int left = udata.getlen();
+      int left = udata.getlen() - m_exehead_size;
       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 + m_exehead_size, l), l);
         udata.release();
         pos += l;
         left -= l;
@@ -3301,16 +3248,84 @@
     }
 #endif
 
-    if (add_db_data(&udata) < 0)
-      return PS_ERROR;
+    if (postubuild_cmds)
+    {
+      TCHAR fpath[260]; // should be long enough
+      if (!get_temp_file_path(fpath, 259))
+      {
+        ERROR_MSG(_T("Error: can't get temporary path\n"));
+        return PS_ERROR;
+      }
+      FILE *fp = FOPEN(fpath,("w+b"));
+      if (!fp)
+      {
+        ERROR_MSG(_T("Error: can't open output file\n"));
+        return PS_ERROR;
+      }
+      int datalen = udata.getlen();
+      int ret_size = (int)fwrite(udata.get(0,datalen),1,datalen,fp);
+      fclose(fp);
+      if (ret_size != datalen)
+      {
+        ERROR_MSG(_T("Error: can't write %d bytes to output\n"),datalen);
+        return PS_ERROR;
+      }
+      if (!run_postbuild_cmds(postubuild_cmds, fpath, _T("UnFinalize")))
+      {
+        return PS_ERROR;
+      }
+      fp = FOPEN(fpath,("r+b"));
+      if (!fp)
+      {
+        ERROR_MSG(_T("Error: can't open input file\n"));
+        return PS_ERROR;
+      }
+      fseek(fp, 0, SEEK_END);
+      int file_len = (int)ftell(fp);
+      fseek(fp, 0, SEEK_SET);
+      char* in_buf = (char*)malloc(file_len);
+      if (!in_buf)
+      {
+        ERROR_MSG(_T("Error: can't allocate memory for read\n"));
+        return PS_ERROR;
+      }
+      ret_size = fread(in_buf, 1, file_len, fp);
+      fclose(fp);
+      _tunlink(fpath);
+      if (ret_size != file_len)
+      {
+        ERROR_MSG(_T("Error: can't read %d bytes from input\n"),file_len);
+        free(in_buf);
+        return PS_ERROR;
+      }
+      udata.clear();
+      udata.add(in_buf, file_len);
 
+      free(in_buf);
+    }
+    //uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int);
+    uninstall_size_full = udata.getlen();
+    int uninst_offset = add_db_data(&udata);
+    if (uninst_offset < 0) return PS_ERROR;
+
     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;
+    int ents = build_header.blocks[NB_ENTRIES].num;
+    int uns = uninstaller_writes_used;
+    while (ents--)
+    {
+      if (ent->which == EW_WRITEUNINSTALLER)
+      {
+        ent->offsets[1] = uninst_offset;
+        uns--;
+        if (!uns)
+          break;
+      }
+      ent++;
+    }
 
     // compressed size
-    uninstall_size=build_datablock.getlen()-uninstdata_offset;
+    uninstall_size=build_datablock.getlen()-uninst_offset;
 
     SCRIPT_MSG(_T("Done!\n"));
   }
@@ -3950,6 +3965,43 @@
   }
 }
 
+int CEXEBuild::run_postbuild_cmds(postbuild_cmd *in_cmds, const TCHAR* pc1_filename, const TCHAR *token_str)
+{
+  if (!in_cmds) return 0;
+  for (struct postbuild_cmd *cmd=in_cmds; cmd; cmd = cmd->next)
+  {
+    TCHAR *cmdstr = cmd->cmd, *cmdstrbuf = NULL;
+    TCHAR *arg = _tcsstr(cmdstr, _T("%1"));
+    if (arg)    // if found, replace %1 by pc1_filename
+    {
+      const size_t cchbldoutfile = _tcslen(pc1_filename);
+      cmdstrbuf = (TCHAR*) malloc( (_tcslen(cmdstr) + cchbldoutfile + 1)*sizeof(TCHAR) );
+      if (!cmdstrbuf)
+      {
+        ERROR_MSG(_T("Error: can't allocate memory for %") NPRIs _T(" command\n"), token_str);
+        return 0;
+      }
+      arg -= ((UINT_PTR)cmdstr)/sizeof(TCHAR), arg += ((UINT_PTR)cmdstrbuf)/sizeof(TCHAR);
+      _tcscpy(cmdstrbuf,cmdstr);
+      cmdstr = cmdstrbuf;
+      memmove(arg+cchbldoutfile, arg+2, (_tcslen(arg+2)+1)*sizeof(TCHAR));
+      memmove(arg, pc1_filename, cchbldoutfile*sizeof(TCHAR));
+      //BUGBUG: Should we call PathConvertWinToPosix on pc1_filename?
+    }
+
+    SCRIPT_MSG(_T("\n%") NPRIs _T(" command: %") NPRIs _T("\n"),token_str,cmdstr);
+    int ret = sane_system(cmdstr);
+    free(cmdstrbuf);
+    if (!check_external_exitcode(ret, cmd->cmpop, cmd->cmpval))
+    {
+      ERROR_MSG(_T("%") NPRIs _T(" %d, aborting\n"), _T("%") NPRIs _T(" command returned"), ret, token_str);
+      return 0;
+    }
+    if (ret != 0) INFO_MSG(_T("%") NPRIs _T(" %d\n"), _T("%") NPRIs _T(" command returned"), ret, token_str);
+  }
+  return 1;
+}
+
 int CEXEBuild::check_external_exitcode(int exitcode, int op, int val)
 {
   switch(op)
Index: Source/script.cpp
===================================================================
--- Source/script.cpp	(revision 6823)
+++ Source/script.cpp	(working copy)
@@ -1354,43 +1354,18 @@
 
     case TOK_P_TEMPFILE:
       {
-        TCHAR *symbol = line.gettoken_str(1);
-        const TCHAR *fpath;
-#ifdef _WIN32
-        TCHAR buf[MAX_PATH], buf2[MAX_PATH];
-        GetTempPath(MAX_PATH, buf);
-        if (!GetTempFileName(buf, _T("nst"), 0, buf2))
+        TCHAR *symbol = line.gettoken_str(1), fpath[260]; // should be long enough
+        if (!get_temp_file_path(fpath, 259))
         {
           ERROR_MSG(_T("!tempfile: unable to create temporary file.\n"));
           return PS_ERROR;
         }
-        fpath = buf2;
-#else // !_WIN32
-        char t[] = ("/tmp/makensisXXXXXX");
-        const mode_t old_umask = umask(0077);
-        int fd = mkstemp(t);
-        umask(old_umask);
-        if (fd == -1) { L_tok_p_tempfile_oom:
-          ERROR_MSG(_T("!tempfile: unable to create temporary file.\n"));
-          return PS_ERROR;
-        }
-        close(fd);
-#ifdef _UNICODE
-        if (!(fpath = NSISRT_mbtowc(t))) goto L_tok_p_tempfile_oom;
-#else
-        fpath = t;
-#endif
-#endif // ~_WIN32
-
         if (definedlist.add(symbol, fpath))
         {
           ERROR_MSG(_T("!tempfile: \"%") NPRIs _T("\" already defined!\n"), symbol);
           return PS_ERROR;
         }
         SCRIPT_MSG(_T("!tempfile: \"%") NPRIs _T("\"=\"%") NPRIs _T("\"\n"), symbol, fpath);
-#if !defined(_WIN32) && defined(_UNICODE)
-        NSISRT_free(fpath);
-#endif
       }
     return PS_OK;
 
@@ -3221,6 +3196,26 @@
         SCRIPT_MSG(_T("!finalize: \"%") NPRIs _T("\"\n"),cmdstr);
       }
     return PS_OK;
+    case TOK_P_UNFINALIZE:
+#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
+      {
+        TCHAR* cmdstr=line.gettoken_str(1);        
+        int validparams=false;
+        struct postbuild_cmd *newcmd, *prevcmd;
+        newcmd=(struct postbuild_cmd*) (new BYTE[FIELD_OFFSET(struct postbuild_cmd,cmd[_tcsclen(cmdstr)+1])]);
+        newcmd->next=NULL, _tcscpy(newcmd->cmd,cmdstr);
+        newcmd->cmpop=line.gettoken_enum(2,_T("<\0>\0<>\0=\0ignore\0")), newcmd->cmpval=line.gettoken_int(3,&validparams);
+        if (line.getnumtokens() == 1+1) newcmd->cmpop=4, validparams=true; // just a command, ignore the exit code
+        if (newcmd->cmpop == -1 || !validparams) PRINTHELP();
+        for (prevcmd=postubuild_cmds; prevcmd && prevcmd->next;) prevcmd=prevcmd->next;
+        if (prevcmd) prevcmd->next=newcmd; else postubuild_cmds=newcmd;
+        SCRIPT_MSG(_T("!unfinalize: \"%") NPRIs _T("\"\n"),cmdstr);
+      }
+    return PS_OK;
+#else//!NSIS_CONFIG_UNINSTALL_SUPPORT
+      ERROR_MSG(_T("Error: %") NPRIs _T(" specified, NSIS_CONFIG_UNINSTALL_SUPPORT not defined.\n"), line.gettoken_str(0));
+    return PS_ERROR;
+#endif
     case TOK_P_SYSTEMEXEC:
     case TOK_P_EXECUTE:
     case TOK_P_MAKENSIS:
Index: Source/tokens.h
===================================================================
--- Source/tokens.h	(revision 6823)
+++ Source/tokens.h	(working copy)
@@ -109,6 +109,7 @@
   TOK_P_UNDEF,
   TOK_P_PACKEXEHEADER,
   TOK_P_FINALIZE,
+  TOK_P_UNFINALIZE,
   TOK_P_SYSTEMEXEC,
   TOK_P_EXECUTE,
   TOK_P_MAKENSIS,
Index: Source/util.h
===================================================================
--- Source/util.h	(revision 6823)
+++ Source/util.h	(working copy)
@@ -272,6 +272,8 @@
 FILE* my_fopen(const TCHAR *path, const char *mode);
 #define FOPEN(a, b) my_fopen((a), (b))
 
+int get_temp_file_path(TCHAR *path, int len);
+
 // round a value up to be a multiple of 512
 // assumption: T is an int type
 template <class T> inline T align_to_512(const T x) { return (x+511) & ~511; }
