/*-------
  Module:			drvconn.c
 *
 * Description:		This module contains only routines related to
 *					implementing SQLDriverConnect.
 *
 * Classes:			n/a
 *
 * API functions:	SQLDriverConnect
 *
 * Comments:		See "notice.txt" for copyright and license information.
 *-------
 */

#include "psqlodbc.h"

#include <stdio.h>
#include <stdlib.h>

#include "connection.h"

#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#define NEAR
#else
#include <winsock.h>
#endif

#include <string.h>

#ifdef WIN32
#include <windowsx.h>
#include "resource.h"
#endif
#include "pgapifunc.h"

#ifndef TRUE
#define TRUE	(BOOL)1
#endif
#ifndef FALSE
#define FALSE	(BOOL)0
#endif

#include "dlg_specific.h"

/* prototypes */
void		dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);
static void dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);

#ifdef WIN32
BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
RETCODE		dconn_DoDialog(HWND hwnd, ConnInfo *ci);

extern HINSTANCE NEAR s_hModule;	/* Saved module handle. */
#endif


RETCODE		SQL_API
PGAPI_DriverConnect(
					HDBC hdbc,
					HWND hwnd,
					UCHAR FAR * szConnStrIn,
					SWORD cbConnStrIn,
					UCHAR FAR * szConnStrOut,
					SWORD cbConnStrOutMax,
					SWORD FAR * pcbConnStrOut,
					UWORD fDriverCompletion)
{
	static char *func = "PGAPI_DriverConnect";
	ConnectionClass *conn = (ConnectionClass *) hdbc;
	ConnInfo   *ci;

#ifdef WIN32
	RETCODE		dialog_result;
#endif
	RETCODE		result;
	char		connStrIn[MAX_CONNECT_STRING];
	char		connStrOut[MAX_CONNECT_STRING];
	int			retval;
	char		password_required = FALSE;
	int			len = 0;
	SWORD		lenStrout;


	mylog("%s: entering...\n", func);

	if (!conn)
	{
		CC_log_error(func, "", NULL);
		return SQL_INVALID_HANDLE;
	}

	make_string(szConnStrIn, cbConnStrIn, connStrIn);

	mylog("**** PGAPI_DriverConnect: fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, connStrIn);
	qlog("conn=%u, PGAPI_DriverConnect( in)='%s', fDriverCompletion=%d\n", conn, connStrIn, fDriverCompletion);

	ci = &(conn->connInfo);

	/* Parse the connect string and fill in conninfo for this hdbc. */
	dconn_get_connect_attributes(connStrIn, ci);

	/*
	 * If the ConnInfo in the hdbc is missing anything, this function will
	 * fill them in from the registry (assuming of course there is a DSN
	 * given -- if not, it does nothing!)
	 */
	getDSNinfo(ci, CONN_DONT_OVERWRITE);
	dconn_get_common_attributes(connStrIn, ci);
	logs_on_off(1, ci->drivers.debug, ci->drivers.commlog);

	/* Fill in any default parameters if they are not there. */
	getDSNdefaults(ci);
	/* initialize pg_version */
	CC_initialize_pg_version(conn);

#ifdef WIN32
dialog:
#endif
	ci->focus_password = password_required;

	switch (fDriverCompletion)
	{
#ifdef WIN32
		case SQL_DRIVER_PROMPT:
			dialog_result = dconn_DoDialog(hwnd, ci);
			if (dialog_result != SQL_SUCCESS)
				return dialog_result;
			break;

		case SQL_DRIVER_COMPLETE_REQUIRED:

			/* Fall through */

		case SQL_DRIVER_COMPLETE:

			/* Password is not a required parameter. */
			if (ci->username[0] == '\0' ||
				ci->server[0] == '\0' ||
				ci->database[0] == '\0' ||
				ci->port[0] == '\0' ||
				password_required)
			{
				dialog_result = dconn_DoDialog(hwnd, ci);
				if (dialog_result != SQL_SUCCESS)
					return dialog_result;
			}
			break;
#else
		case SQL_DRIVER_PROMPT:
		case SQL_DRIVER_COMPLETE:
		case SQL_DRIVER_COMPLETE_REQUIRED:
#endif
		case SQL_DRIVER_NOPROMPT:
			break;
	}

	/*
	 * Password is not a required parameter unless authentication asks for
	 * it. For now, I think it's better to just let the application ask
	 * over and over until a password is entered (the user can always hit
	 * Cancel to get out)
	 */
	if (ci->username[0] == '\0' ||
		ci->server[0] == '\0' ||
		ci->database[0] == '\0' ||
		ci->port[0] == '\0')
	{
		/* (password_required && ci->password[0] == '\0')) */

		return SQL_NO_DATA_FOUND;
	}

	/* do the actual connect */
	retval = CC_connect(conn, password_required);
	if (retval < 0)
	{							/* need a password */
		if (fDriverCompletion == SQL_DRIVER_NOPROMPT)
		{
			CC_log_error(func, "Need password but Driver_NoPrompt", conn);
			return SQL_ERROR;	/* need a password but not allowed to
								 * prompt so error */
		}
		else
		{
#ifdef WIN32
			password_required = TRUE;
			goto dialog;
#else
			return SQL_ERROR;	/* until a better solution is found. */
#endif
		}
	}
	else if (retval == 0)
	{
		/* error msg filled in above */
		CC_log_error(func, "Error from CC_Connect", conn);
		return SQL_ERROR;
	}

	/*
	 * Create the Output Connection String
	 */
	result = SQL_SUCCESS;

	lenStrout = cbConnStrOutMax;
	if (conn->ms_jet && lenStrout > 255)
		lenStrout = 255;
	makeConnectString(connStrOut, ci, lenStrout);
	len = strlen(connStrOut);

	if (szConnStrOut)
	{
		/*
		 * Return the completed string to the caller. The correct method
		 * is to only construct the connect string if a dialog was put up,
		 * otherwise, it should just copy the connection input string to
		 * the output. However, it seems ok to just always construct an
		 * output string.  There are possible bad side effects on working
		 * applications (Access) by implementing the correct behavior,
		 * anyway.
		 */
		strncpy_null(szConnStrOut, connStrOut, cbConnStrOutMax);

		if (len >= cbConnStrOutMax)
		{
			int			clen;

			for (clen = strlen(szConnStrOut) - 1; clen >= 0 && szConnStrOut[clen] != ';'; clen--)
				szConnStrOut[clen] = '\0';
			result = SQL_SUCCESS_WITH_INFO;
			conn->errornumber = CONN_TRUNCATED;
			conn->errormsg = "The buffer was too small for the ConnStrOut.";
		}
	}

	if (pcbConnStrOut)
		*pcbConnStrOut = len;

	mylog("szConnStrOut = '%s' len=%d,%d\n", szConnStrOut, len, cbConnStrOutMax);
	qlog("conn=%u, PGAPI_DriverConnect(out)='%s'\n", conn, szConnStrOut);


	mylog("PGAPI_DRiverConnect: returning %d\n", result);
	return result;
}


#ifdef WIN32
RETCODE
dconn_DoDialog(HWND hwnd, ConnInfo *ci)
{
	int			dialog_result;

	mylog("dconn_DoDialog: ci = %u\n", ci);

	if (hwnd)
	{
		dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG),
							hwnd, dconn_FDriverConnectProc, (LPARAM) ci);
		if (!dialog_result || (dialog_result == -1))
			return SQL_NO_DATA_FOUND;
		else
			return SQL_SUCCESS;
	}

	return SQL_ERROR;
}


BOOL FAR	PASCAL
dconn_FDriverConnectProc(
						 HWND hdlg,
						 UINT wMsg,
						 WPARAM wParam,
						 LPARAM lParam)
{
	ConnInfo   *ci;

	switch (wMsg)
	{
		case WM_INITDIALOG:
			ci = (ConnInfo *) lParam;

			/* Change the caption for the setup dialog */
			SetWindowText(hdlg, "PostgreSQL Connection");

			SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");

			/* Hide the DSN and description fields */
			ShowWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), SW_HIDE);
			ShowWindow(GetDlgItem(hdlg, IDC_DSNAME), SW_HIDE);
			ShowWindow(GetDlgItem(hdlg, IDC_DESCTEXT), SW_HIDE);
			ShowWindow(GetDlgItem(hdlg, IDC_DESC), SW_HIDE);

			SetWindowLong(hdlg, DWL_USER, lParam);		/* Save the ConnInfo for
														 * the "OK" */
			SetDlgStuff(hdlg, ci);

			if (ci->database[0] == '\0')
				;				/* default focus */
			else if (ci->server[0] == '\0')
				SetFocus(GetDlgItem(hdlg, IDC_SERVER));
			else if (ci->port[0] == '\0')
				SetFocus(GetDlgItem(hdlg, IDC_PORT));
			else if (ci->username[0] == '\0')
				SetFocus(GetDlgItem(hdlg, IDC_USER));
			else if (ci->focus_password)
				SetFocus(GetDlgItem(hdlg, IDC_PASSWORD));
			break;

		case WM_COMMAND:
			switch (GET_WM_COMMAND_ID(wParam, lParam))
			{
				case IDOK:
					ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);

					GetDlgStuff(hdlg, ci);

				case IDCANCEL:
					EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
					return TRUE;

				case IDC_DRIVER:
					ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
					DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
								   hdlg, driver_optionsProc, (LPARAM) ci);
					break;

				case IDC_DATASOURCE:
					ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
					DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
								   hdlg, ds_optionsProc, (LPARAM) ci);
					break;
			}
	}

	return FALSE;
}
#endif   /* WIN32 */


void
dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
{
	char	   *our_connect_string;
	char	   *pair,
			   *attribute,
			   *value,
			   *equals;
	char	   *strtok_arg;

	memset(ci, 0, sizeof(ConnInfo));
#ifdef	DRIVER_CURSOR_IMPLEMENT
	ci->updatable_cursors = 1;
#endif   /* DRIVER_CURSOR_IMPLEMENT */

	our_connect_string = strdup(connect_string);
	strtok_arg = our_connect_string;

	mylog("our_connect_string = '%s'\n", our_connect_string);

	while (1)
	{
		pair = strtok(strtok_arg, ";");
		if (strtok_arg)
			strtok_arg = 0;
		if (!pair)
			break;

		equals = strchr(pair, '=');
		if (!equals)
			continue;

		*equals = '\0';
		attribute = pair;		/* ex. DSN */
		value = equals + 1;		/* ex. 'CEO co1' */

		mylog("attribute = '%s', value = '%s'\n", attribute, value);

		if (!attribute || !value)
			continue;

		/* Copy the appropriate value to the conninfo  */
		copyAttributes(ci, attribute, value);

	}

	free(our_connect_string);
}

static void
dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
{
	char	   *our_connect_string;
	char	   *pair,
			   *attribute,
			   *value,
			   *equals;
	char	   *strtok_arg;

	our_connect_string = strdup(connect_string);
	strtok_arg = our_connect_string;

	mylog("our_connect_string = '%s'\n", our_connect_string);

	while (1)
	{
		pair = strtok(strtok_arg, ";");
		if (strtok_arg)
			strtok_arg = 0;
		if (!pair)
			break;

		equals = strchr(pair, '=');
		if (!equals)
			continue;

		*equals = '\0';
		attribute = pair;		/* ex. DSN */
		value = equals + 1;		/* ex. 'CEO co1' */

		mylog("attribute = '%s', value = '%s'\n", attribute, value);

		if (!attribute || !value)
			continue;

		/* Copy the appropriate value to the conninfo  */
		copyCommonAttributes(ci, attribute, value);

	}

	free(our_connect_string);
}
