+++ /dev/null
-// TortoiseSVN - a Windows shell extension for easy version control\r
-\r
-// Copyright (C) 2007-2008 - TortoiseSVN\r
-\r
-// This program is free software; you can redistribute it and/or\r
-// modify it under the terms of the GNU General Public License\r
-// as published by the Free Software Foundation; either version 2\r
-// of the License, or (at your option) any later version.\r
-\r
-// This program is distributed in the hope that it will be useful,\r
-// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-// GNU General Public License for more details.\r
-\r
-// You should have received a copy of the GNU General Public License\r
-// along with this program; if not, write to the Free Software Foundation,\r
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-//\r
-#include "stdafx.h"\r
-#include "Hooks.h"\r
-#include "registry.h"\r
-#include "StringUtils.h"\r
-#include "TempFile.h"\r
-\r
-CHooks* CHooks::m_pInstance;\r
-\r
-CHooks::CHooks()\r
-{\r
-}\r
-\r
-CHooks::~CHooks()\r
-{\r
-};\r
-\r
-bool CHooks::Create()\r
-{\r
- if (m_pInstance == NULL)\r
- m_pInstance = new CHooks();\r
- CRegString reghooks = CRegString(_T("Software\\TortoiseSVN\\hooks"));\r
- CString strhooks = reghooks;\r
- // now fill the map with all the hooks defined in the string\r
- // the string consists of multiple lines, where one hook script is defined\r
- // as four lines:\r
- // line 1: the hook type\r
- // line 2: path to working copy where to apply the hook script\r
- // line 3: command line to execute\r
- // line 4: 'true' or 'false' for waiting for the script to finish\r
- // line 5: 'show' or 'hide' on how to start the hook script\r
- hookkey key;\r
- int pos = 0;\r
- hookcmd cmd;\r
- while ((pos = strhooks.Find('\n')) >= 0)\r
- {\r
- // line 1\r
- key.htype = GetHookType(strhooks.Mid(0, pos));\r
- if (pos+1 < strhooks.GetLength())\r
- strhooks = strhooks.Mid(pos+1);\r
- else\r
- strhooks.Empty();\r
- bool bComplete = false;\r
- if ((pos = strhooks.Find('\n')) >= 0)\r
- {\r
- // line 2\r
- key.path = CTSVNPath(strhooks.Mid(0, pos));\r
- if (pos+1 < strhooks.GetLength())\r
- strhooks = strhooks.Mid(pos+1);\r
- else\r
- strhooks.Empty();\r
- if ((pos = strhooks.Find('\n')) >= 0)\r
- {\r
- // line 3\r
- cmd.commandline = strhooks.Mid(0, pos);\r
- if (pos+1 < strhooks.GetLength())\r
- strhooks = strhooks.Mid(pos+1);\r
- else\r
- strhooks.Empty();\r
- if ((pos = strhooks.Find('\n')) >= 0)\r
- {\r
- // line 4\r
- cmd.bWait = (strhooks.Mid(0, pos).CompareNoCase(_T("true"))==0);\r
- if (pos+1 < strhooks.GetLength())\r
- strhooks = strhooks.Mid(pos+1);\r
- else\r
- strhooks.Empty();\r
- if ((pos = strhooks.Find('\n')) >= 0)\r
- {\r
- // line 5\r
- cmd.bShow = (strhooks.Mid(0, pos).CompareNoCase(_T("show"))==0);\r
- if (pos+1 < strhooks.GetLength())\r
- strhooks = strhooks.Mid(pos+1);\r
- else\r
- strhooks.Empty();\r
- bComplete = true;\r
- }\r
- }\r
- }\r
- }\r
- if (bComplete)\r
- {\r
- m_pInstance->insert(std::pair<hookkey, hookcmd>(key, cmd));\r
- }\r
- } \r
- return true;\r
-}\r
-\r
-CHooks& CHooks::Instance()\r
-{\r
- return *m_pInstance;\r
-}\r
-\r
-void CHooks::Destroy()\r
-{\r
- delete m_pInstance;\r
-}\r
-\r
-bool CHooks::Save()\r
-{\r
- CString strhooks;\r
- for (hookiterator it = begin(); it != end(); ++it)\r
- {\r
- strhooks += GetHookTypeString(it->first.htype);\r
- strhooks += '\n';\r
- strhooks += it->first.path.GetWinPathString();\r
- strhooks += '\n';\r
- strhooks += it->second.commandline;\r
- strhooks += '\n';\r
- strhooks += (it->second.bWait ? _T("true") : _T("false"));\r
- strhooks += '\n';\r
- strhooks += (it->second.bShow ? _T("show") : _T("hide"));\r
- strhooks += '\n';\r
- }\r
- CRegString reghooks = CRegString(_T("Software\\TortoiseSVN\\hooks"));\r
- reghooks = strhooks;\r
- if (reghooks.LastError)\r
- return false;\r
- return true;\r
-}\r
-\r
-bool CHooks::Remove(hookkey key)\r
-{\r
- return (erase(key) > 0);\r
-}\r
-\r
-void CHooks::Add(hooktype ht, const CTSVNPath& Path, LPCTSTR szCmd, bool bWait, bool bShow)\r
-{\r
- hookkey key;\r
- key.htype = ht;\r
- key.path = Path;\r
- hookiterator it = find(key);\r
- if (it!=end())\r
- erase(it);\r
-\r
- hookcmd cmd;\r
- cmd.commandline = szCmd;\r
- cmd.bWait = bWait;\r
- cmd.bShow = bShow;\r
- insert(std::pair<hookkey, hookcmd>(key, cmd));\r
-}\r
-\r
-CString CHooks::GetHookTypeString(hooktype t)\r
-{\r
- switch (t)\r
- {\r
- case start_commit_hook:\r
- return _T("start_commit_hook");\r
- case pre_commit_hook:\r
- return _T("pre_commit_hook");\r
- case post_commit_hook:\r
- return _T("post_commit_hook");\r
- case start_update_hook:\r
- return _T("start_update_hook");\r
- case pre_update_hook:\r
- return _T("pre_update_hook");\r
- case post_update_hook:\r
- return _T("post_update_hook");\r
- }\r
- return _T("");\r
-}\r
-\r
-hooktype CHooks::GetHookType(const CString& s)\r
-{\r
- if (s.Compare(_T("start_commit_hook"))==0)\r
- return start_commit_hook;\r
- if (s.Compare(_T("pre_commit_hook"))==0)\r
- return pre_commit_hook;\r
- if (s.Compare(_T("post_commit_hook"))==0)\r
- return post_commit_hook;\r
- if (s.Compare(_T("start_update_hook"))==0)\r
- return start_update_hook;\r
- if (s.Compare(_T("pre_update_hook"))==0)\r
- return pre_update_hook;\r
- if (s.Compare(_T("post_update_hook"))==0)\r
- return post_update_hook;\r
- return unknown_hook;\r
-}\r
-\r
-void CHooks::AddParam(CString& sCmd, const CString& param)\r
-{\r
- sCmd += _T(" \"");\r
- sCmd += param;\r
- sCmd += _T("\"");\r
-}\r
-\r
-void CHooks::AddPathParam(CString& sCmd, const CTSVNPathList& pathList)\r
-{\r
- CTSVNPath temppath = CTempFiles::Instance().GetTempFilePath(true);\r
- pathList.WriteToFile(temppath.GetWinPathString(), true);\r
- AddParam(sCmd, temppath.GetWinPathString());\r
-}\r
-\r
-void CHooks::AddCWDParam(CString& sCmd, const CTSVNPathList& pathList)\r
-{\r
- AddParam(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPathString());\r
-}\r
-\r
-void CHooks::AddDepthParam(CString& sCmd, svn_depth_t depth)\r
-{\r
- CString sTemp;\r
- sTemp.Format(_T("%d"), depth);\r
- AddParam(sCmd, sTemp);\r
-}\r
-\r
-void CHooks::AddErrorParam(CString& sCmd, const CString& error)\r
-{\r
- CTSVNPath tempPath;\r
- tempPath = CTempFiles::Instance().GetTempFilePath(true);\r
- CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)error);\r
- AddParam(sCmd, tempPath.GetWinPathString());\r
-}\r
-\r
-CTSVNPath CHooks::AddMessageFileParam(CString& sCmd, const CString& message)\r
-{\r
- CTSVNPath tempPath;\r
- tempPath = CTempFiles::Instance().GetTempFilePath(true);\r
- CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)message);\r
- AddParam(sCmd, tempPath.GetWinPathString());\r
- return tempPath;\r
-}\r
-\r
-bool CHooks::StartCommit(const CTSVNPathList& pathList, CString& message, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(start_commit_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- CTSVNPath temppath = AddMessageFileParam(sCmd, message);\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- if (!exitcode && !temppath.IsEmpty())\r
- {\r
- CStringUtils::ReadStringFromTextFile(temppath.GetWinPathString(), message);\r
- }\r
- return true;\r
-}\r
-\r
-bool CHooks::PreCommit(const CTSVNPathList& pathList, svn_depth_t depth, const CString& message, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(pre_commit_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- AddDepthParam(sCmd, depth);\r
- AddMessageFileParam(sCmd, message);\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- return true;\r
-}\r
-\r
-bool CHooks::PostCommit(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, const CString& message, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(post_commit_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- AddDepthParam(sCmd, depth);\r
- AddMessageFileParam(sCmd, message);\r
- AddParam(sCmd, rev.ToString());\r
- AddErrorParam(sCmd, error);\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- return true;\r
-}\r
-\r
-bool CHooks::StartUpdate(const CTSVNPathList& pathList, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(start_update_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- return true;\r
-}\r
-\r
-bool CHooks::PreUpdate(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(pre_update_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- AddDepthParam(sCmd, depth);\r
- AddParam(sCmd, rev.ToString());\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- return true;\r
-}\r
-\r
-bool CHooks::PostUpdate(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, DWORD& exitcode, CString& error)\r
-{\r
- hookiterator it = FindItem(post_update_hook, pathList);\r
- if (it == end())\r
- return false;\r
- CString sCmd = it->second.commandline;\r
- AddPathParam(sCmd, pathList);\r
- AddDepthParam(sCmd, depth);\r
- AddParam(sCmd, rev.ToString());\r
- AddErrorParam(sCmd, error);\r
- AddCWDParam(sCmd, pathList);\r
- exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
- return true;\r
-}\r
-\r
-hookiterator CHooks::FindItem(hooktype t, const CTSVNPathList& pathList)\r
-{\r
- hookkey key;\r
- for (int i=0; i<pathList.GetCount(); ++i)\r
- {\r
- CTSVNPath path = pathList[i];\r
- do \r
- {\r
- key.htype = t;\r
- key.path = path;\r
- hookiterator it = find(key);\r
- if (it != end())\r
- {\r
- return it;\r
- }\r
- path = path.GetContainingDirectory();\r
- } while(!path.IsEmpty());\r
- }\r
- // look for a script with a path as '*'\r
- key.htype = t;\r
- key.path = CTSVNPath(_T("*"));\r
- hookiterator it = find(key);\r
- if (it != end())\r
- {\r
- return it;\r
- }\r
-\r
- return end();\r
-}\r
-\r
-DWORD CHooks::RunScript(CString cmd, LPCTSTR currentDir, CString& error, bool bWait, bool bShow)\r
-{\r
- DWORD exitcode = 0;\r
- SECURITY_ATTRIBUTES sa;\r
- SecureZeroMemory(&sa, sizeof(sa));\r
- sa.nLength = sizeof(sa);\r
- sa.bInheritHandle = TRUE;\r
-\r
- HANDLE hOut = INVALID_HANDLE_VALUE;\r
- HANDLE hRedir = INVALID_HANDLE_VALUE;\r
- HANDLE hErr = INVALID_HANDLE_VALUE;\r
-\r
- // clear the error string\r
- error.Empty();\r
-\r
- // Create Temp File for redirection\r
- TCHAR szTempPath[MAX_PATH];\r
- TCHAR szOutput[MAX_PATH];\r
- TCHAR szErr[MAX_PATH];\r
- GetTempPath(sizeof(szTempPath)/sizeof(TCHAR),szTempPath);\r
- GetTempFileName(szTempPath, _T("svn"), 0, szErr);\r
-\r
- // setup redirection handles\r
- // output handle must be WRITE mode, share READ\r
- // redirect handle must be READ mode, share WRITE\r
- hErr = CreateFile(szErr, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0);\r
-\r
- if (hErr == INVALID_HANDLE_VALUE) \r
- {\r
- return (DWORD)-1;\r
- }\r
-\r
- hRedir = CreateFile(szErr, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);\r
-\r
- if (hRedir == INVALID_HANDLE_VALUE) \r
- {\r
- CloseHandle(hErr);\r
- return (DWORD)-1;\r
- }\r
-\r
- GetTempFileName(szTempPath, _T("svn"), 0, szOutput);\r
- hOut = CreateFile(szOutput, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0);\r
-\r
- if (hOut == INVALID_HANDLE_VALUE) \r
- {\r
- CloseHandle(hErr);\r
- CloseHandle(hRedir);\r
- return (DWORD)-1;\r
- }\r
-\r
- // setup startup info, set std out/err handles\r
- // hide window\r
- STARTUPINFO si;\r
- SecureZeroMemory(&si, sizeof(si));\r
- si.cb = sizeof(si);\r
- if (hOut != INVALID_HANDLE_VALUE) \r
- {\r
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;\r
- si.hStdOutput = hOut;\r
- si.hStdError = hErr;\r
- si.wShowWindow = bShow ? SW_SHOW : SW_HIDE;\r
- }\r
-\r
- PROCESS_INFORMATION pi;\r
- SecureZeroMemory(&pi, sizeof(pi));\r
-\r
- DWORD dwFlags = 0;\r
-\r
- if (!CreateProcess(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, dwFlags, NULL, currentDir, &si, &pi)) \r
- {\r
- int err = GetLastError(); // preserve the CreateProcess error\r
- if (hErr != INVALID_HANDLE_VALUE) \r
- {\r
- CloseHandle(hErr);\r
- CloseHandle(hRedir);\r
- }\r
- SetLastError(err);\r
- cmd.ReleaseBuffer();\r
- return (DWORD)-1;\r
- }\r
- cmd.ReleaseBuffer();\r
-\r
- CloseHandle(pi.hThread);\r
-\r
- // wait for process to finish, capture redirection and\r
- // send it to the parent window/console\r
- if (bWait)\r
- {\r
- DWORD dw;\r
- char buf[256];\r
- do \r
- {\r
- SecureZeroMemory(&buf,sizeof(buf));\r
- while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) \r
- {\r
- if (dw == 0) \r
- break;\r
- error += CString(CStringA(buf,dw));\r
- SecureZeroMemory(&buf,sizeof(buf));\r
- }\r
- } while (WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0);\r
-\r
- // perform any final flushing\r
- while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) \r
- {\r
- if (dw == 0) \r
- break;\r
-\r
- error += CString(CStringA(buf, dw));\r
- SecureZeroMemory(&buf,sizeof(buf));\r
- }\r
- WaitForSingleObject(pi.hProcess, INFINITE);\r
- GetExitCodeProcess(pi.hProcess, &exitcode);\r
- }\r
- CloseHandle(pi.hProcess);\r
- CloseHandle(hErr);\r
- CloseHandle(hOut);\r
- CloseHandle(hRedir);\r
- DeleteFile(szOutput);\r
- DeleteFile(szErr);\r
-\r
- return exitcode;\r
-}\r
-\r