/*------- Module: drvconn.c * * Description: This module contains only routines related to * implementing SQLDriverConnect. * * Classes: n/a * * API functions: SQLDriverConnect * * Comments: See "readme.txt" for copyright and license information. *------- */ #include "psqlodbc.h" #include #include #include "connection.h" #include "misc.h" #ifndef WIN32 #include #include #else #include #endif #include #ifdef WIN32 #include #include "resource.h" #endif #include "pgapifunc.h" #include "dlg_specific.h" #define FORCE_PASSWORD_DISPLAY #define NULL_IF_NULL(a) (a ? a : "(NULL)") #ifndef FORCE_PASSWORD_DISPLAY static char * hide_password(const char *str) { char *outstr, *pwdp; if (!str) return NULL; outstr = strdup(str); if (!outstr) return NULL; if (pwdp = strstr(outstr, "PWD="), !pwdp) pwdp = strstr(outstr, "pwd="); if (pwdp) { char *p; for (p=pwdp + 4; *p && *p != ';'; p++) *p = 'x'; } return outstr; } #endif /* prototypes */ static BOOL dconn_get_DSN_or_Driver(const char *connect_string, ConnInfo *ci); static BOOL dconn_get_connect_attributes(const char *connect_string, ConnInfo *ci); #ifdef WIN32 LRESULT CALLBACK dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam); RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci); extern HINSTANCE s_hModule; /* Saved module handle. */ #endif #define PASSWORD_IS_REQUIRED 1 static int paramRequired(const ConnInfo *ci, int reqs) { int required = 0; const char *pw = SAFE_NAME(ci->password); /* Password is not necessarily a required parameter. */ if ((reqs & PASSWORD_IS_REQUIRED) != 0) if ('\0' == pw[0]) required |= PASSWORD_IS_REQUIRED; return required; } RETCODE SQL_API PGAPI_DriverConnect(HDBC hdbc, HWND hwnd, const SQLCHAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR * szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT * pcbConnStrOut, SQLUSMALLINT fDriverCompletion) { CSTR func = "PGAPI_DriverConnect"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci; #ifdef WIN32 RETCODE dialog_result; BOOL didUI = FALSE; #endif const char *lackMessage = NULL; RETCODE result; char *connStrIn = NULL; char connStrOut[MAX_CONNECT_STRING]; int retval; char salt[5]; ssize_t len = 0; SQLSMALLINT lenStrout; int reqs = 0; MYLOG(0, "entering...\n"); if (!conn) { CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } connStrIn = make_string(szConnStrIn, cbConnStrIn, NULL, 0); /* CodeDEX with CID=11878 */ if (NULL == connStrIn) { CC_set_error(conn, CONN_NO_MEMORY_ERROR, "Out of memory while making_string for connStrIn", func); return SQL_ERROR; } #ifdef FORCE_PASSWORD_DISPLAY MYLOG(0, "**** fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, connStrIn); #else if (get_mylog()) { char *hide_str = hide_password(connStrIn); MYLOG(0, "**** fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, NULL_IF_NULL(hide_str)); if (hide_str) free(hide_str); } #endif /* FORCE_PASSWORD_DISPLAY */ ci = &(conn->connInfo); /* First parse the connect string and get the name of DSN or Driver */ if (!dconn_get_DSN_or_Driver(connStrIn, ci)) { CC_set_error(conn, CONN_OPENDB_ERROR, "Connection string parse error", func); return SQL_ERROR; } /* * 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, NULL); /* Parse the connect string and fill in conninfo for this hdbc. */ if (!dconn_get_connect_attributes(connStrIn, ci)) { CC_set_error(conn, CONN_OPENDB_ERROR, "Connection string parse error", func); return SQL_ERROR; } logs_on_off(1, ci->drivers.debug, ci->drivers.commlog); if (connStrIn) { free(connStrIn); connStrIn = NULL; } /* initialize pg_version */ CC_initialize_pg_version(conn); memset(salt, 0, sizeof(salt)); #ifdef WIN32 dialog: #endif MYLOG(DETAIL_LOG_LEVEL, "DriverCompletion=%d\n", fDriverCompletion); switch (fDriverCompletion) { #ifdef WIN32 case SQL_DRIVER_PROMPT: if (NULL == hwnd) break; dialog_result = dconn_DoDialog(hwnd, ci); didUI = TRUE; if (dialog_result != SQL_SUCCESS) return dialog_result; break; case SQL_DRIVER_COMPLETE_REQUIRED: /* Fall through */ case SQL_DRIVER_COMPLETE: if (NULL == hwnd) break; if (paramRequired(ci, reqs)) { dialog_result = dconn_DoDialog(hwnd, ci); didUI = TRUE; 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 (paramRequired(ci, reqs)) { if (!lackMessage) lackMessage = "Please supply password"; CC_set_error(conn, CONN_OPENDB_ERROR, lackMessage, func); return SQL_ERROR; } reqs = 0; MYLOG(DETAIL_LOG_LEVEL, "before CC_connect\n"); /* do the actual connect */ retval = CC_connect(conn, salt); 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 if (ci->password_required) reqs |= PASSWORD_IS_REQUIRED; if (hwnd && paramRequired(ci, reqs)) goto dialog; #endif /* WIN32 */ /* Prompting for missing options is only supported on Windows. */ return SQL_ERROR; } } 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 = (1 == retval ? SQL_SUCCESS : SQL_SUCCESS_WITH_INFO); 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((char *) szConnStrOut, connStrOut, cbConnStrOutMax); if (len >= cbConnStrOutMax) { int clen; for (clen = cbConnStrOutMax - 1; clen >= 0 && szConnStrOut[clen] != ';'; clen--) szConnStrOut[clen] = '\0'; result = SQL_SUCCESS_WITH_INFO; CC_set_error(conn, CONN_TRUNCATED, "The buffer was too small for the ConnStrOut.", func); } } if (pcbConnStrOut) *pcbConnStrOut = (SQLSMALLINT) len; #ifdef FORCE_PASSWORD_DISPLAY if (cbConnStrOutMax > 0) { MYLOG(0, "szConnStrOut = '%s' len=" FORMAT_SSIZE_T ",%d\n", NULL_IF_NULL((char *) szConnStrOut), len, cbConnStrOutMax); } #else if (get_mylog()) { char *hide_str = NULL; if (cbConnStrOutMax > 0) hide_str = hide_password(szConnStrOut); MYLOG(0, "szConnStrOut = '%s' len=%d,%d\n", NULL_IF_NULL(hide_str), len, cbConnStrOutMax); if (hide_str) free(hide_str); } #endif /* FORCE_PASSWORD_DISPLAY */ MYLOG(0, "leaving %d\n", result); return result; } #ifdef WIN32 RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci) { LRESULT dialog_result; MYLOG(0, "entering ci = %p\n", ci); if (hwnd) { dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG), hwnd, dconn_FDriverConnectProc, (LPARAM) ci); if (-1 == dialog_result) { int errc = GetLastError(); MYLOG(0, " LastError=%d\n", errc); } if (!dialog_result || (dialog_result == -1)) return SQL_NO_DATA_FOUND; else return SQL_SUCCESS; } MYLOG(0, " No window specified\n"); return SQL_ERROR; } LRESULT CALLBACK dconn_FDriverConnectProc( HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { ConnInfo *ci; char strbuf[64]; switch (wMsg) { case WM_INITDIALOG: ci = (ConnInfo *) lParam; /* Change the caption for the setup dialog */ SetWindowText(hdlg, "PostgreSQL Connection"); LoadString(s_hModule, IDS_ADVANCE_CONNECTION, strbuf, sizeof(strbuf)); SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), strbuf); /* 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); ShowWindow(GetDlgItem(hdlg, IDC_DRIVER), SW_HIDE); ShowWindow(GetDlgItem(hdlg, IDC_TEST), SW_HIDE); ShowWindow(GetDlgItem(hdlg, IDC_MANAGEDSN), SW_HIDE); if ('\0' != ci->server[0]) EnableWindow(GetDlgItem(hdlg, IDC_SERVER), FALSE); if ('\0' != ci->port[0]) EnableWindow(GetDlgItem(hdlg, IDC_PORT), FALSE); SetWindowLongPtr(hdlg, DWLP_USER, lParam); /* Save the ConnInfo for the "OK" */ SetDlgStuff(hdlg, ci); if (ci->password_required) { HWND notu = GetDlgItem(hdlg, IDC_NOTICE_USER); SetFocus(GetDlgItem(hdlg, IDC_PASSWORD)); SetWindowText(notu, " Supply password "); ShowWindow(notu, SW_SHOW); SendMessage(notu, WM_CTLCOLOR, 0, 0); } else 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->autobalance == 0) SetFocus(GetDlgItem(hdlg, IDC_AB)); else if (ci->refreshcnlisttime == 0) SetFocus(GetDlgItem(hdlg, IDC_RT)); else if (ci->priority == 0) SetFocus(GetDlgItem(hdlg, IDC_PR)); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: ci = (ConnInfo *) GetWindowLongPtr(hdlg, DWLP_USER); GetDlgStuff(hdlg, ci); case IDCANCEL: EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK); return TRUE; case IDC_DATASOURCE: ci = (ConnInfo *) GetWindowLongPtr(hdlg, DWLP_USER); DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV), hdlg, ds_options1Proc, (LPARAM) ci); break; } break; case WM_CTLCOLORSTATIC: if (lParam == (LPARAM)GetDlgItem(hdlg, IDC_NOTICE_USER)) { HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); SetTextColor((HDC)wParam, RGB(255, 0, 0)); return (LRESULT)hBrush; } break; } return FALSE; } #endif /* WIN32 */ #define ATTRIBUTE_DELIMITER ';' #define OPENING_BRACKET '{' #define CLOSING_BRACKET '}' typedef BOOL (*copyfunc)(ConnInfo *, const char *attribute, char *value); static BOOL dconn_get_attributes(copyfunc func, const char *connect_string, ConnInfo *ci) { BOOL ret = TRUE; char *our_connect_string; char *pair, *attribute, *value, *termp; BOOL eoftok; char *equals, *delp; char *strtok_arg; #ifdef HAVE_STRTOK_R char *last = NULL; #endif /* HAVE_STRTOK_R */ if (our_connect_string = strdup(connect_string), NULL == our_connect_string) return FALSE; strtok_arg = our_connect_string; #ifdef FORCE_PASSWORD_DISPLAY MYLOG(0, "our_connect_string = '%s'\n", our_connect_string); #else if (get_mylog()) { char *hide_str = hide_password(our_connect_string); MYLOG(0, "our_connect_string = '%s'\n", hide_str); free(hide_str); } #endif /* FORCE_PASSWORD_DISPLAY */ termp = strchr(our_connect_string, '\0'); eoftok = FALSE; while (!eoftok) { if (strtok_arg != NULL && strtok_arg >= termp) /* for safety */ break; #ifdef HAVE_STRTOK_R pair = strtok_r(strtok_arg, ";", &last); #else pair = strtok(strtok_arg, ";"); #endif /* HAVE_STRTOK_R */ if (strtok_arg) strtok_arg = NULL; if (!pair) break; equals = strchr(pair, '='); if (!equals) continue; *equals = '\0'; attribute = pair; /* ex. DSN */ value = equals + 1; /* ex. 'CEO co1' */ /* * Values enclosed with braces({}) can contain ; etc * We don't remove the braces here because * decode_or_remove_braces() in dlg_specifi.c * would remove them later. * Just correct the misdetected delimter(;). */ switch (*value) { const char *valuen, *closep; case OPENING_BRACKET: delp = strchr(value, '\0'); if (delp >= termp) { eoftok = TRUE; break; } /* Where's a corresponding closing bracket? */ closep = strchr(value, CLOSING_BRACKET); if (NULL != closep && closep[1] == '\0') break; for (valuen = value; valuen < termp; closep = strchr(valuen, CLOSING_BRACKET)) { if (NULL == closep) { if (!delp) /* error */ { MYLOG(0, "closing bracket doesn't exist 1\n"); ret = FALSE; goto cleanup; } closep = strchr(delp + 1, CLOSING_BRACKET); if (!closep) /* error */ { MYLOG(0, "closing bracket doesn't exist 2\n"); ret = FALSE; goto cleanup; } *delp = ATTRIBUTE_DELIMITER; /* restore delimiter */ delp = NULL; } if (CLOSING_BRACKET == closep[1]) { valuen = closep + 2; if (valuen >= termp) break; else if (valuen == delp) { *delp = ATTRIBUTE_DELIMITER; delp = NULL; } continue; } else if (ATTRIBUTE_DELIMITER == closep[1] || '\0' == closep[1] || delp == closep + 1) { delp = (char *) (closep + 1); *delp = '\0'; strtok_arg = delp + 1; if (strtok_arg + 1 >= termp) eoftok = TRUE; break; } MYLOG(0, "subsequent char to the closing bracket is %c value=%s\n", closep[1], value); ret = FALSE; goto cleanup; } } /* Copy the appropriate value to the conninfo */ (*func)(ci, attribute, value); } cleanup: free(our_connect_string); return ret; } static BOOL dconn_get_DSN_or_Driver(const char *connect_string, ConnInfo *ci) { CC_conninfo_init(ci, INIT_GLOBALS); return dconn_get_attributes(get_DSN_or_Driver, connect_string, ci); } static BOOL dconn_get_connect_attributes(const char *connect_string, ConnInfo *ci) { return dconn_get_attributes(copyConnAttributes, connect_string, ci); }