/*
	CVSNT Checkout trigger handler
    Copyright (C) 2005 Tony Hoyle and March-Hare Software Ltd

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef _WIN32
#pragma warning(disable:4503) // Decorated name length warning
#endif

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <process.h>
#include <winsock2.h>
#include <mapi.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#define MODULE checkout

#include <ctype.h>
#include <cvstools.h>
#include <map>
#include "../version.h"

#define CVSROOT_SHADOW		"CVSROOT/shadow"

static bool g_verbose;
#ifndef _WIN32
static bool g_cifslogin;
#endif
cvs::filename g_repos;
cvs::string g_command;
std::map<cvs::filename, int> module_list;
std::map<cvs::string, int> tag_list;

#ifdef _WIN32
HMODULE g_hInst;

BOOL CALLBACK DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	g_hInst = hModule;
	return TRUE;
}

#include "checkout_resource.h"

int win32config(const struct plugin_interface *ui, void *wnd);
#endif

int initcheckout(const struct trigger_interface_t* cb, const char *command, const char *date, const char *hostname, const char *username, const char *virtual_repository, const char *physical_repository, const char *sessionid, const char *editor, int count_uservar, const char **uservar, const char **userval, const char *client_version, const char *character_set)
{
	char value[256];
	int val = 0;

	if(!CGlobalSettings::GetGlobalValue("cvsnt","Plugins","CheckoutTrigger",value,sizeof(value)))
		val = atoi(value);
	if(!val)
	{
		CServerIo::trace(3,"Checkout trigger not enabled.");
		return -1;
	}
 	else
 	{
 		CServerIo::trace(3,"Checkout trigger is enabled.");
 	}
 
#ifndef _WIN32
 	g_cifslogin = false;
 	if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutLogin",value,sizeof(value)))
 		g_cifslogin = atoi(value)?true:false;
#endif

	g_verbose = false;
	if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutVerbose",value,sizeof(value)))
		g_verbose = atoi(value)?true:false;

	g_repos = physical_repository;
	g_command = command;

	return 0;
}

int closecheckout(const struct trigger_interface_t* cb)
{
	return 0;
}

int pretagcheckout(const struct trigger_interface_t* cb, const char *message, const char *directory, int name_list_count, const char **name_list, const char **version_list, char tag_type, const char *action, const char *tag)
{
	module_list[directory]++;
	if(tag && tag[0])
		tag_list[tag]++;
	else
		tag_list["HEAD"]++;

	return 0;
}

int verifymsgcheckout(const struct trigger_interface_t* cb, const char *directory, const char *filename)
{
	return 0;
}

int loginfocheckout(const struct trigger_interface_t* cb, const char *message, const char *status, const char *directory, int change_list_count, change_info_t *change_list)
{
	module_list[directory]++;

	for(int n=0; n<change_list_count; n++)
	{
		if(change_list[n].tag)
			tag_list[change_list[n].tag]++;
		else
			tag_list["HEAD"]++;
	}
	return 0;
}

int historycheckout(const struct trigger_interface_t* cb, char type, const char *workdir, const char *revs, const char *name, const char *bugid, const char *message)
{
	return 0;
}

int notifycheckout(const struct trigger_interface_t* cb, const char *message, const char *bugid, const char *directory, const char *notify_user, const char *tag, const char *type, const char *file)
{
	return 0;
}

int precommitcheckout(const struct trigger_interface_t* cb, int name_list_count, const char **name_list, const char *message, const char *directory)
{
	return 0;
}

int postcommitcheckout(const struct trigger_interface_t* cb, const char *directory)
{
	return 0;
}

int precommandcheckout(const struct trigger_interface_t* cb, int argc, const char **argv)
{
	return 0;
}

static int outputProc(const char *str, size_t len, void * /*param*/)
{
	if(g_verbose)
		return CServerIo::output(len,str);
	return 0;
}

static int errorProc(const char *str, size_t len, void * /*param*/)
{
	if(g_verbose)
		return CServerIo::error(len,str);
	return 0;
}

int postcommandcheckout(const struct trigger_interface_t* cb, const char *directory)
{
	cvs::filename fn;
	CFileAccess acc;
	cvs::string line;

	if(g_command!="tag" && g_command!="rtag" && g_command!="commit")
		return 0;

	cvs::sprintf(fn,80,"%s/%s",g_repos.c_str(),CVSROOT_SHADOW);
	if(!acc.open(fn.c_str(),"r"))
	{
		CServerIo::trace(3,"Could not open "CVSROOT_SHADOW);
		return 0;
	}
 	else
 	{
 		CServerIo::trace(3,"Opened "CVSROOT_SHADOW);
 	}

	int linenum;
	for(linenum=1; acc.getline(line); linenum++)
	{
		const char *p=line.c_str();
		while(isspace((unsigned char)*p))
			p++;
		if(*p=='#')
			continue;

		CTokenLine tok(p);

		if(tok.size()!=3)
		{
			CServerIo::error("Malformed line %d in "CVSROOT_SHADOW" - Need Module Tag Directory",linenum);
			continue;
		}

		bool found = false;
		cvs::string match;
		for(std::map<cvs::filename,int>::const_iterator i = module_list.begin(); i!=module_list.end(); ++i)
		{
			CServerIo::trace(3,"Regexp match: %s - %s",tok[0],i->first.c_str());
			cvs::wildcard_filename mod(i->first.c_str());
			if(mod.matches_regexp(tok[0])) // Wildcard match
			{
				CServerIo::trace(3,"Match found!");
				found=true;
				match = i->first.c_str();
				break;
			}
		}
		if(!found)
			continue;

		found=false;
		for(std::map<cvs::string,int>::const_iterator i = tag_list.begin(); i!=tag_list.end(); ++i)
		{
			if(!strcmp(i->first.c_str(),tok[1]))
			{
				found=true;
				break;
			}
		}
		if(!found)
			continue;

#ifndef _WIN32
 		if(g_cifslogin)
 		{
 			CRunFile rf1;
 			char host[256],name[256]="cifslogin",user[256]="",password[256]="";
 			if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutLoginUser",user,sizeof(user)))
 			{
 				CServerIo::trace(3,"Checkout plugin: Login user name not set");
				user[0]='\0';
 			}
 			if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutLoginPassword",password,sizeof(password)))
 			{
 				CServerIo::trace(3,"Checkout plugin: Login password not set");
				password[0]='\0';
 			}
			
 			if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutLoginCommand",name,sizeof(name)))
 			{
 				CServerIo::trace(3,"Checkout plugin: Login command not set - assume \"cifslogin\"");
 				strcpy(name,"cifslogin");
 			}
 			if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutLoginServer",host,sizeof(host)))
 			{
 				CServerIo::trace(3,"Checkout plugin: Login server not set");
				host[0]='\0';
 			}

			if (*host)
			{
	 			rf1.setOutput(outputProc,NULL); 
				rf1.setError(errorProc,NULL); 
 			
	 			rf1.addArg(name); 
				if (*password)
				{
		 			rf1.addArg("-P");
					rf1.addArg(password);
				}
	 			rf1.addArg(host);
				if (*user)
		 			rf1.addArg(user);
	 			if(!rf1.run(NULL))
	 			{
	 				CServerIo::error("Unable to run checkout login command");
	 				return 0;
	 			}
	 			int ret;
	 			rf1.wait(ret);
	 		}
		}
#endif

		CRunFile rf;
		char cvscmd[2048]="\0";
		char cvsopt[2048]="\0";
		char cvscmdopt[2048]="\0";

		rf.setOutput(outputProc,NULL);
		rf.setError(errorProc,NULL);

 		if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommand",cvscmd,sizeof(cvscmd)))
 		{
 			CServerIo::trace(3,"Checkout plugin: Command not set  - usng default");
 			rf.addArg(CGlobalSettings::GetCvsCommand());
 		}
 		else
 		{
 			CServerIo::trace(3,"Checkout plugin: Using specified command");
 			rf.addArg(cvscmd);
 		}
 		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutOption",cvsopt,sizeof(cvsopt)))
 		{
 			CServerIo::trace(3,"Checkout plugin: Options set  - %s",cvsopt);
 			rf.addArg(cvsopt);
 		}
		rf.addArg("-d");
		rf.addArg(g_repos.c_str());
		rf.addArg("co");
 		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommandOption",cvscmdopt,sizeof(cvscmdopt)))
 		{
 			CServerIo::trace(3,"Checkout plugin: Command options set  - %s",cvscmdopt);
 			rf.addArg(cvscmdopt);
 		}
		rf.addArg("-d");
		rf.addArg(tok[2]);
		rf.addArg("-r");
		rf.addArg(tok[1]);
		rf.addArg(match.c_str());
		if(!rf.run(NULL))
		{
			CServerIo::error("Unable to run cvs checkout");
			return 0;
		}
		int ret;
		rf.wait(ret);
	}

	return 0;
}

int premodulecheckout(const struct trigger_interface_t* cb, const char *module)
{
	module_list[module]++;
	return 0;
}

int postmodulecheckout(const struct trigger_interface_t* cb, const char *module)
{
	module_list[module]++;
	return 0;
}

int get_templatecheckout(const struct trigger_interface_t *cb, const char *directory, const char **template_ptr)
{
	return 0;
}

int parse_keywordcheckout(const struct trigger_interface_t *cb, const char *keyword,const char *directory,const char *file,const char *branch,const char *author,const char *printable_date,const char *rcs_date,const char *locker,const char *state,const char *version,const char *name,const char *bugid, const char *commitid, const property_info *props, size_t numprops, const char **value)
{
	return 0;
}

int prercsdiffcheckout(const struct trigger_interface_t *cb, const char *file, const char *directory, const char *oldfile, const char *newfile, const char *type, const char *options, const char *oldversion, const char *newversion, unsigned long added, unsigned long removed)
{
	return 0;
}

int rcsdiffcheckout(const struct trigger_interface_t *cb, const char *file, const char *directory, const char *oldfile, const char *newfile, const char *diff, size_t difflen, const char *type, const char *options, const char *oldversion, const char *newversion, unsigned long added, unsigned long removed)
{
	return 0;
}

static int init(const struct plugin_interface *plugin);
static int destroy(const struct plugin_interface *plugin);
static void *get_interface(const struct plugin_interface *plugin, unsigned interface_type, void *param);

static trigger_interface callbacks =
{
	{
		PLUGIN_INTERFACE_VERSION,
		"Automatic checkout extension",CVSNT_PRODUCTVERSION_STRING,"CheckoutTrigger",
		init,
		destroy,
		get_interface,
	#ifdef _WIN32
		win32config
	#else
		NULL
	#endif
	},
	initcheckout,
	closecheckout,
	pretagcheckout,
	verifymsgcheckout,
	loginfocheckout,
	historycheckout,
	notifycheckout,
	precommitcheckout,
	postcommitcheckout,
	precommandcheckout,
	postcommandcheckout,
	premodulecheckout,
	postmodulecheckout,
	get_templatecheckout,
	parse_keywordcheckout,
	prercsdiffcheckout,
	rcsdiffcheckout
};

static int init(const struct plugin_interface *plugin)
{
	return 0;
}

static int destroy(const struct plugin_interface *plugin)
{
	return 0;
}

static void *get_interface(const struct plugin_interface *plugin, unsigned interface_type, void *param)
{
	if(interface_type!=pitTrigger)
		return NULL;

	return (void*)&callbacks;
}

plugin_interface *get_plugin_interface()
{
	return &callbacks.plugin;
}

#ifdef _WIN32
BOOL CALLBACK ConfigDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	char value[MAX_PATH];
	int nSel;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		nSel = 0;
		if(!CGlobalSettings::GetGlobalValue("cvsnt","Plugins","CheckoutTrigger",value,sizeof(value)))
			nSel = atoi(value);
		if(!nSel)
		{
			EnableWindow(GetDlgItem(hWnd,IDC_CHECK2),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_CHECKOUTDEF),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_CHECKOUTMOD),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),FALSE);
		}
		else
			SendDlgItemMessage(hWnd,IDC_CHECK1,BM_SETCHECK,1,NULL);
		nSel = 0;
		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutVerbose",value,sizeof(value)))
			nSel = atoi(value);
		SendDlgItemMessage(hWnd,IDC_CHECK2,BM_SETCHECK,nSel?1:0,NULL);
		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutOption",value,sizeof(value)))
			SetDlgItemText(hWnd,IDC_COMMANDOPT,value);
		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommandOption",value,sizeof(value)))
			SetDlgItemText(hWnd,IDC_VERBOPT,value);
		if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommand",value,sizeof(value)))
			SetDlgItemText(hWnd,IDC_COMMANDNAME,value);
		else
			value[0]='\0';
		if(*value)
		{
			CheckRadioButton(hWnd,IDC_CHECKOUTDEF,IDC_CHECKOUTMOD,IDC_CHECKOUTMOD);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),TRUE);
		}
		else
		{
			CheckRadioButton(hWnd,IDC_CHECKOUTDEF,IDC_CHECKOUTMOD,IDC_CHECKOUTDEF);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),FALSE);
		}
		return FALSE;
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
		case IDC_CHECK1:
			nSel=SendDlgItemMessage(hWnd,IDC_CHECK1,BM_GETCHECK,NULL,NULL);
			EnableWindow(GetDlgItem(hWnd,IDC_CHECK2),nSel?TRUE:FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_CHECKOUTDEF),nSel?TRUE:FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_CHECKOUTMOD),nSel?TRUE:FALSE);
			if (!nSel)
			{
				EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),FALSE);
				EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),FALSE);
				EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),FALSE);
			}
			return TRUE;
		case IDC_CHECKOUTDEF:
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),FALSE);
			value[0]='\0';
			SetDlgItemText(hWnd,IDC_COMMANDNAME,value);
			SetDlgItemText(hWnd,IDC_COMMANDOPT,value);
			SetDlgItemText(hWnd,IDC_VERBOPT,value);
			return TRUE;
		case IDC_CHECKOUTMOD:
			if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommand",value,sizeof(value)))
				SetDlgItemText(hWnd,IDC_COMMANDNAME,value);
			if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutOption",value,sizeof(value)))
				SetDlgItemText(hWnd,IDC_COMMANDOPT,value);
			if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","CheckoutCommandOption",value,sizeof(value)))
				SetDlgItemText(hWnd,IDC_VERBOPT,value);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDNAME),TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_COMMANDOPT),TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_VERBOPT),TRUE);
			return TRUE;
		case IDOK:
			nSel=SendDlgItemMessage(hWnd,IDC_CHECK1,BM_GETCHECK,NULL,NULL);
            CGlobalSettings::SetGlobalValue("cvsnt","Plugins","CheckoutTrigger",nSel);
			nSel=SendDlgItemMessage(hWnd,IDC_CHECK2,BM_GETCHECK,NULL,NULL);
            CGlobalSettings::SetGlobalValue("cvsnt","PServer","CheckoutVerbose",nSel);
			GetDlgItemText(hWnd,IDC_COMMANDNAME,value,sizeof(value));
			CGlobalSettings::SetGlobalValue("cvsnt","PServer","CheckoutCommand",value);
			GetDlgItemText(hWnd,IDC_COMMANDOPT,value,sizeof(value));
			CGlobalSettings::SetGlobalValue("cvsnt","PServer","CheckoutOption",value);
			GetDlgItemText(hWnd,IDC_VERBOPT,value,sizeof(value));
			CGlobalSettings::SetGlobalValue("cvsnt","PServer","CheckoutCommandOption",value);
		case IDCANCEL:
			EndDialog(hWnd,LOWORD(wParam));
			return TRUE;
		}
		break;
	}
	return FALSE;
}

int win32config(const struct plugin_interface *ui, void *wnd)
{
	HWND hWnd = (HWND)wnd;
	int ret = DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, ConfigDlgProc);
	return ret==IDOK?0:-1;
}
#endif



syntax highlighted by Code2HTML, v. 0.9.1