/* ntsrvcs.c */

/* Synchronet BBS as a set of Windows NT Services */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright 2003 Rob Swindell - http://www.synchro.net/copyright.html		*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

/* Synchronet-specific headers */
#include "sbbs.h"		/* various */
#include "sbbs_ini.h"	/* sbbs_read_ini() */
#include "ftpsrvr.h"	/* ftp_startup_t, ftp_server */
#include "websrvr.h"	/* web_startup_t, web_server */
#include "mailsrvr.h"	/* mail_startup_t, mail_server */
#include "services.h"	/* services_startup_t, services_thread */

/* Windows-specific headers */
#include <winsvc.h>

/* Temporary: Do not include web server in 3.1x-Win32 release build */
#if defined(_MSC_VER)
	#define NO_WEB_SERVER
#endif

/* Services doesn't work without JavaScript support */
#if !defined(JAVASCRIPT)
	#define	NO_SERVICES
#endif

#define SBBS_NTSVC_BBS_NAME		"SynchronetBBS"
#define SBBS_NTSVC_BBS_DISP		"Synchronet Telnet/RLogin Server"
#define SBBS_NTSVC_BBS_DESC		"Provides support for Telnet and RLogin clients and executes timed events. " \
								"This service provides the critical functions of your Synchronet BBS."

#define SBBS_NTSVC_FTP_NAME		"SynchronetFTP"
#define SBBS_NTSVC_FTP_DISP		"Synchronet FTP Server"
#define SBBS_NTSVC_FTP_DESC		"Provides support for FTP clients (including web browsers) for file transfers."

#define SBBS_NTSVC_WEB_NAME		"SynchronetWeb"
#define SBBS_NTSVC_WEB_DISP		"Synchronet Web Server"
#define SBBS_NTSVC_WEB_DESC		"Provides support for Web (HTML/HTTP) clients (browsers)."

#define SBBS_NTSVC_MAIL_NAME	"SynchronetMail"
#define SBBS_NTSVC_MAIL_DISP	"Synchronet SMTP/POP3 Mail Server"
#define SBBS_NTSVC_MAIL_DESC	"Sends and receives Internet e-mail (using SMTP) and allows users to remotely " \
								"access their e-mail using an Internet mail client (using POP3)."

#define SBBS_NTSVC_SERV_NAME	"SynchronetServices"
#define SBBS_NTSVC_SERV_DISP	"Synchronet Services"
#define SBBS_NTSVC_SERV_DESC	"Plugin servers (usually in JavaScript) for any TCP/UDP protocol. " \
								"Stock services include Finger, Gopher, NNTP, and IRC. Edit your ctrl/services.ini " \
								"file for configuration of individual Synchronet Services."
							
/* Global variables */

SERVICE_STATUS			bbs_status;
SERVICE_STATUS_HANDLE	bbs_status_handle;
SERVICE_STATUS			ftp_status;
SERVICE_STATUS_HANDLE	ftp_status_handle;
SERVICE_STATUS			web_status;
SERVICE_STATUS_HANDLE	web_status_handle;
SERVICE_STATUS			mail_status;
SERVICE_STATUS_HANDLE	mail_status_handle;
SERVICE_STATUS			services_status;
SERVICE_STATUS_HANDLE	services_status_handle;

bbs_startup_t		bbs_startup;
ftp_startup_t		ftp_startup;
mail_startup_t		mail_startup;
services_startup_t	services_startup;
web_startup_t		web_startup;

#ifdef _WINSOCKAPI_

static WSADATA WSAData;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0)
		return(TRUE);

    fprintf(stderr,"!WinSock startup ERROR %d\n", status);
	return(FALSE);
}

static BOOL winsock_cleanup(void)	
{
	if(WSACleanup()==0)
		return(TRUE);

	fprintf(stderr,"!WinSock cleanup ERROR %d\n",ERROR_VALUE);
	return(FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)	
#define winsock_cleanup()	(TRUE)

#endif

/****************************************************************************/
/* BBS local/log print routine												*/
/****************************************************************************/
static int bbs_lputs(char *str)
{
    return(0);
}

/****************************************************************************/
/* Event thread local/log print routine										*/
/****************************************************************************/
static int event_lputs(char *str)
{
    return 0;
}

static void bbs_started(void)
{
	bbs_status.dwCurrentState=SERVICE_RUNNING;
	bbs_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	SetServiceStatus(bbs_status_handle, &bbs_status);
}

static void bbs_terminated(int code)
{
	if(code) {
		bbs_status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		bbs_status.dwServiceSpecificExitCode=code;
		SetServiceStatus(bbs_status_handle, &bbs_status);
	}
}

static void WINAPI bbs_ctrl_handler(DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			bbs_terminate();
			bbs_status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(bbs_status_handle, &bbs_status);
}

static void WINAPI bbs_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
    if((bbs_status_handle = RegisterServiceCtrlHandler(SBBS_NTSVC_BBS_NAME, bbs_ctrl_handler))==0) {
		fprintf(stderr,"!ERROR %d registering service control handler\n",GetLastError());
		return;
	}

	memset(&bbs_status,0,sizeof(bbs_status));
	bbs_status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	bbs_status.dwWaitHint=30000;	/* milliseconds */

	bbs_status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(bbs_status_handle, &bbs_status);

	bbs_thread(&bbs_startup);

	bbs_status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(bbs_status_handle, &bbs_status);
}


/****************************************************************************/
/* FTP local/log print routine												*/
/****************************************************************************/
static int ftp_lputs(char *str)
{
	return 0;
}

static void ftp_started(void)
{
	ftp_status.dwCurrentState=SERVICE_RUNNING;
	ftp_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	SetServiceStatus(ftp_status_handle, &ftp_status);
}

static void ftp_terminated(int code)
{
	if(code) {
		ftp_status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		ftp_status.dwServiceSpecificExitCode=code;
		SetServiceStatus(ftp_status_handle, &ftp_status);
	}
}

static void WINAPI ftp_ctrl_handler(DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			ftp_terminate();
			ftp_status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(ftp_status_handle, &ftp_status);
}

static void WINAPI
ftp_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
    if((ftp_status_handle = RegisterServiceCtrlHandler(SBBS_NTSVC_FTP_NAME, ftp_ctrl_handler))==0) {
		fprintf(stderr,"!ERROR %d registering service control handler\n",GetLastError());
		return;
	}

	memset(&ftp_status,0,sizeof(ftp_status));
	ftp_status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	ftp_status.dwWaitHint=30000;	/* milliseconds */

	ftp_status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(ftp_status_handle, &ftp_status);

	ftp_server(&ftp_startup);

	ftp_status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(ftp_status_handle, &ftp_status);
}

/****************************************************************************/
/* Web local/log print routine												*/
/****************************************************************************/
static int web_lputs(char *str)
{
    return 0;
}

static void web_started(void)
{
	web_status.dwCurrentState=SERVICE_RUNNING;
	web_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	SetServiceStatus(web_status_handle, &web_status);
}

static void web_terminated(int code)
{
	if(code) {
		web_status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		web_status.dwServiceSpecificExitCode=code;
		SetServiceStatus(web_status_handle, &web_status);
	}
}

static void WINAPI web_ctrl_handler(DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			web_terminate();
			web_status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(web_status_handle, &web_status);
}

static void WINAPI
web_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
    if((web_status_handle = RegisterServiceCtrlHandler(SBBS_NTSVC_WEB_NAME, web_ctrl_handler))==0) {
		fprintf(stderr,"!ERROR %d registering service control handler\n",GetLastError());
		return;
	}

	memset(&web_status,0,sizeof(web_status));
	web_status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	web_status.dwWaitHint=30000;	/* milliseconds */

	web_status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(web_status_handle, &web_status);

#if !defined(NO_WEB_SERVER)
	web_server(&web_startup);
#endif

	web_status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(web_status_handle, &web_status);
}

/****************************************************************************/
/* Mail Server local/log print routine										*/
/****************************************************************************/
static int mail_lputs(char *str)
{
	return 0;
}

static void mail_started(void)
{
	mail_status.dwCurrentState=SERVICE_RUNNING;
	mail_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	SetServiceStatus(mail_status_handle, &mail_status);
}

static void mail_terminated(int code)
{
	if(code) {
		mail_status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		mail_status.dwServiceSpecificExitCode=code;
		SetServiceStatus(mail_status_handle, &mail_status);
	}
}

static void WINAPI mail_ctrl_handler(DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			mail_terminate();
			mail_status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(mail_status_handle, &mail_status);
}

static void WINAPI
mail_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
    if((mail_status_handle = RegisterServiceCtrlHandler(SBBS_NTSVC_MAIL_NAME, mail_ctrl_handler))==0) {
		fprintf(stderr,"!ERROR %d registering service control handler\n",GetLastError());
		return;
	}

	memset(&mail_status,0,sizeof(mail_status));
	mail_status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	mail_status.dwWaitHint=30000;	/* milliseconds */

	mail_status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(mail_status_handle, &mail_status);

	mail_server(&mail_startup);

	mail_status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(mail_status_handle, &mail_status);
}


/****************************************************************************/
/* Services local/log print routine											*/
/****************************************************************************/
static int services_lputs(char *str)
{
	return 0;
}

static void services_started(void)
{
	services_status.dwCurrentState=SERVICE_RUNNING;
	services_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	SetServiceStatus(services_status_handle, &services_status);
}

static void services_terminated(int code)
{
	if(code) {
		services_status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		services_status.dwServiceSpecificExitCode=code;
		SetServiceStatus(services_status_handle, &services_status);
	}
}

static void WINAPI services_ctrl_handler(DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			services_terminate();
			services_status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(services_status_handle, &services_status);
}

static void WINAPI 
services_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
    if((services_status_handle = RegisterServiceCtrlHandler(SBBS_NTSVC_SERV_NAME, services_ctrl_handler))==0) {
		fprintf(stderr,"!ERROR %d registering service control handler\n",GetLastError());
		return;
	}

	memset(&services_status,0,sizeof(services_status));
	services_status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	services_status.dwWaitHint=30000;	/* milliseconds */

	services_status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(services_status_handle, &services_status);

	services_thread(&services_startup);

	services_status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(services_status_handle, &services_status);
}

/******************************************/
/* NT Serivce Install/Uninstall Functions */
/******************************************/

/* ChangeServiceConfig2 is a Win2K+ API function, must call dynamically */
typedef WINADVAPI BOOL (WINAPI *ChangeServiceConfig2_t)(SC_HANDLE, DWORD, LPCVOID);

static void describe_service(HANDLE hSCMlib, SC_HANDLE hService, char* description)
{
	ChangeServiceConfig2_t changeServiceConfig2;
	static SERVICE_DESCRIPTION service_desc;
  
	if(hSCMlib==NULL)
		return;

	service_desc.lpDescription=description;

	if((changeServiceConfig2 = (ChangeServiceConfig2_t)GetProcAddress(hSCMlib, "ChangeServiceConfig2A"))!=NULL)
		changeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &service_desc);
}

static SC_HANDLE create_service(HANDLE hSCMlib, SC_HANDLE hSCManager
								,char* name, char* DISP_name, char* description, char* path)
{
    SC_HANDLE   hService;

	printf("Installing service: %-40s ... ", DISP_name);

    hService = CreateService(
        hSCManager,					// SCManager database
        name,						// name of service
        DISP_name,				// name to display
        SERVICE_ALL_ACCESS,         // desired access
        SERVICE_WIN32_OWN_PROCESS,  // service type
		SERVICE_AUTO_START,			// start type (auto or manual)
        SERVICE_ERROR_NORMAL,       // error control type
        path,						// service's binary
        NULL,                       // no load ordering group
        NULL,                       // no tag identifier
        "",							// dependencies
        NULL,                       // LocalSystem account
        NULL);                      // no password

	if(hService==NULL)
		printf("!ERROR %d\n",GetLastError());
	else {
		describe_service(hSCMlib, hService,description);
		CloseServiceHandle(hService);
		printf("Successful\n");
	}

	return(hService);
}


static int install(void)
{
	HANDLE		hSCMlib;
    SC_HANDLE   hSCManager;
    char		path[MAX_PATH+1];

	printf("Installing Synchronet NT Services...\n");

	hSCMlib = LoadLibrary("ADVAPI32.DLL");

    if(GetModuleFileName(NULL,path,sizeof(path))==0)
    {
        fprintf(stderr,"!ERROR %d getting module file name\n",GetLastError());
        return(-1);
    }

    hSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if(hSCManager==NULL) {
		fprintf(stderr,"!ERROR %d opening SC manager\n",GetLastError());
		return(-1);
	}

	create_service(hSCMlib, hSCManager, SBBS_NTSVC_BBS_NAME,	SBBS_NTSVC_BBS_DISP,	SBBS_NTSVC_BBS_DESC, path);
	create_service(hSCMlib, hSCManager, SBBS_NTSVC_FTP_NAME,	SBBS_NTSVC_FTP_DISP,	SBBS_NTSVC_FTP_DESC, path);
#if !defined(NO_WEB_SERVER)
	create_service(hSCMlib, hSCManager, SBBS_NTSVC_WEB_NAME,	SBBS_NTSVC_WEB_DISP,	SBBS_NTSVC_WEB_DESC, path);
#endif
	create_service(hSCMlib, hSCManager, SBBS_NTSVC_MAIL_NAME,	SBBS_NTSVC_MAIL_DISP,	SBBS_NTSVC_MAIL_DESC, path);
	create_service(hSCMlib, hSCManager, SBBS_NTSVC_SERV_NAME,	SBBS_NTSVC_SERV_DISP,	SBBS_NTSVC_SERV_DESC, path);

	if(hSCMlib!=NULL)
		FreeLibrary(hSCMlib);

	CloseServiceHandle(hSCManager);

	return(0);
}

static void remove_service(SC_HANDLE hSCManager, char* name, char* DISP_name)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

	printf("Removing service: %-40s ... ", DISP_name);

    hService = OpenService(hSCManager, name, SERVICE_ALL_ACCESS);

	if(hService==NULL) {
		printf("\n!ERROR %d opening service: %s\n",GetLastError(),name);
		return;
	}

    // try to stop the service
    if(ControlService( hService, SERVICE_CONTROL_STOP, &status))
    {
        printf("\nStopping: %s ... ",name);

        while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_STOP_PENDING)
			Sleep(1000);

        if(status.dwCurrentState == SERVICE_STOPPED)
            printf("Stopped.\n");
        else
            printf("FAILED!\n");
    }

    // now remove the service
    if(DeleteService(hService))
		printf("Successful\n");
	else
		printf("!ERROR %d\n",GetLastError());
    CloseServiceHandle(hService);
}


static int uninstall(void)
{
    SC_HANDLE   hSCManager;

    hSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if(hSCManager==NULL) {
		fprintf(stderr,"!ERROR %d opening SC manager\n",GetLastError());
		return(-1);
	}

	remove_service(hSCManager, SBBS_NTSVC_BBS_NAME,		SBBS_NTSVC_BBS_DISP);
	remove_service(hSCManager, SBBS_NTSVC_FTP_NAME,		SBBS_NTSVC_FTP_DISP);
#if !defined(NO_WEB_SERVER)
	remove_service(hSCManager, SBBS_NTSVC_WEB_NAME,		SBBS_NTSVC_WEB_DISP);
#endif
	remove_service(hSCManager, SBBS_NTSVC_MAIL_NAME,	SBBS_NTSVC_MAIL_DISP);
	remove_service(hSCManager, SBBS_NTSVC_SERV_NAME,	SBBS_NTSVC_SERV_DISP);

	CloseServiceHandle(hSCManager);

	return(0);
}


/****************************************************************************/
/* Main Entry Point															*/
/****************************************************************************/
int main(int argc, char** argv)
{
	char*	ctrl_dir;
	char	str[MAX_PATH+1];
	char	ini_file[MAX_PATH+1];
	char	host_name[128]="";
	FILE*	fp=NULL;

	SERVICE_TABLE_ENTRY  ServiceDispatchTable[] = 
    { 
        { SBBS_NTSVC_BBS_NAME,	bbs_start		}, 
		{ SBBS_NTSVC_FTP_NAME,	ftp_start		},
#if !defined(NO_WEB_SERVER)
		{ SBBS_NTSVC_WEB_NAME,	web_start		},
#endif
		{ SBBS_NTSVC_MAIL_NAME,	mail_start		},
		{ SBBS_NTSVC_SERV_NAME,	services_start	},
        { NULL,					NULL			}	/* Terminator */
    }; 

	printf("\nSynchronet NT Services  Version %s%c  %s\n\n"
		,VERSION,REVISION,COPYRIGHT_NOTICE);

	if(argc>1 && !stricmp(argv[1],"-install"))
		return install();

	if(argc>1 && !stricmp(argv[1],"-remove"))
		return uninstall();

    printf("%s -install          to install the services\n", getfname(argv[0]) );
    printf("%s -remove           to remove the services\n", getfname(argv[0]) );

	ctrl_dir=getenv("SBBSCTRL");	/* read from environment variable */
	if(ctrl_dir==NULL || ctrl_dir[0]==0) {
		ctrl_dir="/sbbs/ctrl";		/* Not set? Use default */
		printf("!SBBSCTRL environment variable not set, using default value: %s\n\n"
			,ctrl_dir);
	}

	if(!winsock_startup())
		return(-1);

	gethostname(host_name,sizeof(host_name)-1);

	if(!winsock_cleanup())
		return(-1);

	sprintf(ini_file,"%s%c%s.ini",ctrl_dir,PATH_DELIM,host_name);
	if(!fexistcase(ini_file))
		sprintf(ini_file,"%s%csbbs.ini",ctrl_dir,PATH_DELIM);

	/* Initialize BBS startup structure */
    memset(&bbs_startup,0,sizeof(bbs_startup));
    bbs_startup.size=sizeof(bbs_startup);

	bbs_startup.lputs=bbs_lputs;
	bbs_startup.event_log=event_lputs;
    bbs_startup.started=bbs_started;
    bbs_startup.terminated=bbs_terminated;
    strcpy(bbs_startup.ctrl_dir,ctrl_dir);

	/* Initialize FTP startup structure */
    memset(&ftp_startup,0,sizeof(ftp_startup));
    ftp_startup.size=sizeof(ftp_startup);
	ftp_startup.lputs=ftp_lputs;
    ftp_startup.started=ftp_started;
    ftp_startup.terminated=ftp_terminated;
    strcpy(ftp_startup.ctrl_dir,ctrl_dir);

	/* Initialize Web Server startup structure */
    memset(&web_startup,0,sizeof(web_startup));
    web_startup.size=sizeof(web_startup);
	web_startup.lputs=web_lputs;
    web_startup.started=web_started;
    web_startup.terminated=web_terminated;
    strcpy(web_startup.ctrl_dir,ctrl_dir);

	/* Initialize Mail Server startup structure */
    memset(&mail_startup,0,sizeof(mail_startup));
    mail_startup.size=sizeof(mail_startup);
	mail_startup.lputs=mail_lputs;
    mail_startup.started=mail_started;
    mail_startup.terminated=mail_terminated;
    strcpy(mail_startup.ctrl_dir,ctrl_dir);

	/* Initialize Services startup structure */
    memset(&services_startup,0,sizeof(services_startup));
    services_startup.size=sizeof(services_startup);
	services_startup.lputs=services_lputs;
    services_startup.started=services_started;
    services_startup.terminated=services_terminated;
    strcpy(services_startup.ctrl_dir,ctrl_dir);

	/* Read .ini file here */
	if(ini_file[0]!=0 && (fp=fopen(ini_file,"r"))!=NULL) {
		sprintf(str,"Reading %s",ini_file);
		bbs_lputs(str);
	}

	/* We call this function to set defaults, even if there's no .ini file */
	sbbs_read_ini(fp, 
		NULL,	&bbs_startup,
		NULL,	&ftp_startup, 
		NULL,	&web_startup,
		NULL,	&mail_startup, 
		NULL,	&services_startup);

	/* close .ini file here */
	if(fp!=NULL)
		fclose(fp);

    printf("\nStartServiceCtrlDispatcher being called.\n" );
    printf("This may take several seconds.  Please wait.\n" );

	if(!StartServiceCtrlDispatcher(ServiceDispatchTable)) 
    { 
		sprintf(str,"!Synchronet StartServiceCtrlDispatcher ERROR %d",GetLastError());
		printf("%s\n",str);
        OutputDebugString(str); 
    } 

	return(0);
}