*/
 /* Multibyte support   Eiji Tokuya 2001-03-15 */
 
-#include "loadlib.h"
 #include "connection.h"
 #ifndef    NOT_USE_LIBPQ
 #include <libpq-fe.h>
 #else
 #include <errno.h>
 #endif /* WIN32 */
+#ifdef USE_KRB5
+#include "krb5svcs.h"
+#endif /* USE_KRB5 */
+#ifdef USE_GSS
+#include "gsssvcs.h"
+#endif /* USE_GSS */
 
 #include "environ.h"
 #include "socket.h"
        conninfo->cvt_null_date_string = -1;
        conninfo->autocommit_public = SQL_AUTOCOMMIT_ON;
        conninfo->accessible_only = -1;
+       conninfo->gssauth_use_gssapi = -1;
 #ifdef _HANDLE_ENLIST_IN_DTC_
        conninfo->xa_opt = -1;
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
    CONNLOCK_RELEASE(self);
 }
 
+static void
+CC_endup_authentication(ConnectionClass *self)
+{
+   SocketClass *sock = CC_get_socket(self);
+#ifdef USE_SSPI
+   if (0 != self->auth_svcs)
+   {
+       ReleaseSvcSpecData(sock, self->auth_svcs);
+       self->auth_svcs = 0;
+   }
+#endif /* USE_SSPI */
+#ifdef USE_GSS
+   pg_GSS_cleanup(sock);
+#endif /* USE_GSS */
+}
 
 CSTR   bgncmd = "BEGIN";
 CSTR   cmtcmd = "COMMIT";
            opts[cnt][0] = "database";  opts[cnt++][1] = ci->database;
        }
    }
-   if (ci->username[0])
+   if (ci->username[0] || !libpqopt)
    {
-       opts[cnt][0] = "user";      opts[cnt++][1] = ci->username;
+       char    *usrname = ci->username;
+#ifdef WIN32
+       DWORD namesize = sizeof(ci->username) - 2;
+#endif /* WIN32 */
+
+       opts[cnt][0] = "user";
+       if (!usrname[0])
+       {
+#ifdef WIN32
+           if (GetUserName(ci->username + 1, &namesize))
+               usrname = ci->username + 1;
+#endif /* WIN32 */
+       }
+mylog("!!! usrname=%s server=%s\n", usrname, ci->server);
+       opts[cnt++][1] = usrname;
    }
    if (libpqopt)
    {
        {
            opts[cnt][0] = "password";  opts[cnt++][1] = ci->password;
        }
+       if (ci->gssauth_use_gssapi)
+       {
+           opts[cnt][0] = "gsslib";    opts[cnt++][1] = "gssapi";
+       }
    }
    else
    {
 }
 #endif /* NOT_USE_LIBPQ */
 
+#if    defined(USE_SSPI) || defined(USE_GSS)
+/*
+ * Callee should free hte returned pointer.
+ */
+static char *MakePrincHint(ConnInfo *ci, BOOL sspi)
+{
+   size_t len;
+   char    *svcprinc;
+   char    *svcname;
+   BOOL    attrFound = FALSE;
+
+   svcname = extract_extra_attribute_setting(ci->conn_settings, "krbsrvname");
+
+mylog("!!! %s settings=%s svcname=%p\n", __FUNCTION__, ci->conn_settings, svcname);
+   if (svcname)
+       attrFound = TRUE;
+   else
+       svcname = "postgres";
+   len = strlen(svcname) + 1 + strlen(ci->server) + 1;
+   if (NULL != (svcprinc = malloc(len)))
+   {
+       if (sspi)
+           snprintf(svcprinc, len, "%s/%s", svcname, ci->server);
+       else
+           snprintf(svcprinc, len, "%s@%s", svcname, ci->server);
+   }
+   if (attrFound)
+       free(svcname);
+   return svcprinc;
+}
+#endif /* USE_SSPI */
+
 static char
 original_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
 {
    QResultClass *res;
    SocketClass *sock;
    ConnInfo   *ci = &(self->connInfo);
-   int         areq = -1;
-   int         beresp, sockerr;
+   int     areq = -1;
+   int     ret = 0, beresp, sockerr;
    char        msgbuffer[ERROR_MSG_LENGTH];
    char        salt[5], notice[512];
    CSTR        func = "original_CC_connect";
 
    mylog("%s: entering...\n", func);
 
+   /* lock the connetion during authentication */
+   ENTER_CONN_CS(self);
+#define    return  DONT_CALL_RETURN_FROM_HERE????
+
    if (password_req != AUTH_REQ_OK)
    {
        sock = self->sock;      /* already connected, just authenticate */
    else
    {
        if (0 == CC_initial_log(self, func))
-           return 0;
+           goto error_proc;
 
 #ifdef USE_SSPI
        ssl_try_count = 0;
                if (PROTOCOL_62(ci))
                {
                    CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not construct a socket to the server", func);
-                   return 0;
+                   goto error_proc;
                }
                if (PROTOCOL_63(ci))
                    strncpy_null(ci->protocol, PG62, sizeof(ci->protocol));
            if (!self->sock)
            {
                CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not construct a socket to the server", func);
-               return 0;
+               goto error_proc;
            }
        }
 
        if (SOCK_get_errcode(sock) != 0)
        {
            CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not connect to the server", func);
-           return 0;
+           goto error_proc;
        }
        mylog("connection to the server socket succeeded.\n");
 
                case 'S':
                    if (!StartupSspiService(sock, SchannelService, NULL))
                    {
-                       CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func);
-                       return 0;
+                       CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func);
+                       goto error_proc;
                    }
                    break;
                case 'N':
                        goto another_version_retry;
                    }
                default:
-                   CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func);
-                   return 0;
+                   CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func);
+                   goto error_proc;
            }
        }
 #endif /* USE_SSPI */
        else if (PROTOCOL_74(ci))
        {
            if (!protocol3_packet_build(self))
-               return 0;
+               goto error_proc;
        }
        else
        {
        if (SOCK_get_errcode(sock) != 0)
        {
            CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Failed to send the authentication packet", func);
-           return 0;
+           goto error_proc;
        }
        mylog("sent the authentication block successfully.\n");
    }
    {
        BOOL        beforeV2 = PG_VERSION_LT(self, 6.4),
                    ReadyForQuery = FALSE, retry = FALSE;
-       uint32  leng;
+       uint32  leng = 0;
+       int authRet;
 
        do
        {
                    if (retry)
                        break;
 
-                   return 0;
+                   goto error_proc;
                case 'R':
 
                    if (password_req != AUTH_REQ_OK)
 
                        case AUTH_REQ_KRB4:
                            CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 4 authentication not supported", func);
-                           return 0;
+                           goto error_proc;
 
                        case AUTH_REQ_KRB5:
+#ifdef USE_KRB5
+                           // pglock_thread();
+                           authRet = pg_krb5_sendauth(self);
+                           // pgunlock_thread();
+                           if (authRet != 0)
+                           {
+                               /* Error message already filled in */
+                               goto error_proc;
+                           }
+                           break;
+#endif /* USE_KRB5 */
                            CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 5 authentication not supported", func);
-                           return 0;
+                           goto error_proc;
 
                        case AUTH_REQ_PASSWORD:
                            mylog("in AUTH_REQ_PASSWORD\n");
                            if (ci->password[0] == '\0')
                            {
                                CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection.", func);
-                               return -areq;       /* need password */
+                               ret = -areq; /* need password */
+                               goto error_proc;
                            }
 
                            mylog("past need password\n");
 
                        case AUTH_REQ_CRYPT:
                            CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Password crypt authentication not supported", func);
-                           return 0;
+                           goto error_proc;
                        case AUTH_REQ_MD5:
                            mylog("in AUTH_REQ_MD5\n");
                            if (ci->password[0] == '\0')
                                CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection.", func);
                                if (salt_para)
                                    memcpy(salt_para, salt, sizeof(salt));
-                               return -areq; /* need password */
+                               ret = -areq; /* need password */
+                               goto error_proc;
                            }
                            if (md5_auth_send(self, salt))
                            {
                                CC_set_error(self, CONN_INVALID_AUTHENTICATION, "md5 hashing failed", func);
-                               return 0;
+                               goto error_proc;
                            }
                            break;
 
                        case AUTH_REQ_SCM_CREDS:
                            CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unix socket credential authentication not supported", func);
-                           return 0;
+                           goto error_proc;
+
+                       case AUTH_REQ_GSS:
+                           mylog("in AUTH_REQ_GSS\n");
+#if    defined(USE_SSPI)
+                           if (!ci->gssauth_use_gssapi)
+                           {
+                               self->auth_svcs = KerberosService;
+                               authRet = StartupSspiService(sock, self->auth_svcs, MakePrincHint(ci, TRUE));
+                               if (!authRet)
+                               {
+                                   CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func);
+                                   goto error_proc;
+                               }
+                               break;
+                           }
+#endif /* USE_SSPI */
+#ifdef USE_GSS
+#ifdef USE_SSPI
+                           if (ci->gssauth_use_gssapi)
+#endif /* USE_SSPI */
+                           {
+                                               // pglock_thread();
+                                               authRet = pg_GSS_startup(self, MakePrincHint(ci, FALSE));
+                                               // pgunlock_thread();
+                                               if (authRet != 0)
+                               {
+                                   CC_set_errornumber(self, CONN_INVALID_AUTHENTICATION);
+                                   goto error_proc;
+                               }
+                               break;
+                           }
+#endif /* USE_GSS */
+                           CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "GSS authentication not supported", func);
+                           goto error_proc;
+
+                       case AUTH_REQ_GSS_CONT:
+                           mylog("in AUTH_REQ_GSS_CONT\n");
+#if    defined(USE_SSPI)
+                           if (0 != self->auth_svcs)
+                           {
+                               authRet = ContinueSspiService(sock, self->auth_svcs, (void *) (leng - 4));
+                               if (!authRet)
+                               {
+                                   CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service continuation failed", func);
+                                   goto error_proc;
+                               }
+                               break;
+                           }
+#endif /* USE_SSPI */
+#ifdef USE_GSS
+                                           // pglock_thread();
+                                           authRet = pg_GSS_continue(self, leng - 4);
+                                           // pgunlock_thread();
+                                           if (authRet != 0)
+                           {
+                               CC_set_errornumber(self, CONN_INVALID_AUTHENTICATION);
+                               goto error_proc;
+                           }
+                           break;
+#else
+                           CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "GSS authentication not supported", func);
+                           goto error_proc;
+#endif
+
+                       case AUTH_REQ_SSPI:
+                           mylog("in AUTH_REQ_SSPI\n");
+#if    defined(USE_SSPI)
+                           self->auth_svcs = ci->gssauth_use_gssapi ? KerberosService : NegotiateService;
+                           if (!StartupSspiService(sock, self->auth_svcs, MakePrincHint(ci, TRUE)))
+                           {
+                               CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func);
+                               goto error_proc;
+                           }
+                           break;
+#else
+                           CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "SSPI authentication not supported", func);
+                           goto error_proc;
+#endif
 
                        default:
                            CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unknown authentication type", func);
-                           return 0;
+                           goto error_proc;
                    }
                    break;
                case 'S': /* parameter status */
                default:
                    snprintf(notice, sizeof(notice), "Unexpected protocol character='%c' during authentication", beresp);
                    CC_set_error(self, CONN_INVALID_AUTHENTICATION, notice, func);
-                   return 0;
+                   goto error_proc;
            }
            if (retry)
            {
    }
 
 sockerr_proc:
+   ret = 1;
+error_proc:
+#undef return
+   CC_endup_authentication(self);
+   LEAVE_CONN_CS(self);
+   if (0 >= ret)
+       return ret;
    if (0 != (sockerr = SOCK_get_errcode(sock)))
    {
        if (0 == CC_get_errornumber(self))
 
    mylog("sslmode=%s\n", self->connInfo.sslmode);
 #ifndef    NOT_USE_LIBPQ
+#ifdef USE_SSPI
+   if (0 != self->svcs_allowed)
+       ;
+   else
+#endif /* USE_SSPI */
    if (self->connInfo.username[0] == '\0')
        call_libpq = TRUE;
    else if (self->connInfo.sslmode[0] != SSLLBYTE_DISABLE) 
-#ifdef USE_SSPI
-   {
-       if (0 == (SchannelService & self->svcs_allowed))
-           call_libpq = TRUE;
-   }
-#else
        call_libpq = TRUE;
-#endif /* USE_SSPI */
    if (call_libpq)
    {
        ret = LIBPQ_CC_connect(self, password_req, salt_para);
         */
        if (0 == ret && CONN_UNABLE_TO_LOAD_DLL == CC_get_errornumber(self))
        {
-           self->svcs_allowed |= SchannelService;
+           self->svcs_allowed |= (SchannelService | KerberosService | NegotiateService);
            ret = original_CC_connect(self, password_req, salt_para);
        }
 #endif /* USE_SSPI */
 
 #define AUTH_REQ_CRYPT     4
 #define AUTH_REQ_MD5       5
 #define AUTH_REQ_SCM_CREDS 6
+#define AUTH_REQ_GSS       7
+#define AUTH_REQ_GSS_CONT  8
+#define AUTH_REQ_SSPI      9
 
 /* Startup Packet sizes */
 #define SM_DATABASE        64
    signed char cvt_null_date_string;
    signed char autocommit_public;
    signed char accessible_only;
+   signed char gssauth_use_gssapi;
    UInt4       extra_opts;
 #ifdef _HANDLE_ENLIST_IN_DTC_
    signed char xa_opt;
    pgNAME      tableIns;
 #ifdef USE_SSPI
    UInt4       svcs_allowed;
+   UInt4       auth_svcs;
 #endif /* USE_SSPI */
 #if defined(WIN_MULTITHREAD_SUPPORT)
    CRITICAL_SECTION    cs;
 
                            new_string = malloc(cbValueMax);
                            lc = localeconv();
                            for (i = 0, j = 0; ptr[i]; i++)
+                           {
                                if (ptr[i] == '.')
                                {
                                    strncpy_null(&new_string[j], lc->decimal_point, cbValueMax - j);
                                }
                                else
                                    new_string[j++] = ptr[i];
+                           }
                            new_string[j] = '\0';
                            strncpy_null(rgbValueBindRow, new_string, copy_len + 1);
                            free(new_string);
 
 
 extern GLOBAL_VALUES globals;
 
-static void encode(const UCHAR *in, UCHAR *out);
-static void decode(const UCHAR *in, UCHAR *out);
+static void encode(const UCHAR *in, UCHAR *out, int outlen);
+static void decode(const UCHAR *in, UCHAR *out, int outlen);
 
 #define    OVR_EXTRA_BITS (BIT_FORCEABBREVCONNSTR | BIT_FAKE_MSS | BIT_BDE_ENVIRONMENT | BIT_CVT_NULL_DATE | BIT_ACCESSIBLE_ONLY)
 UInt4  getExtraOptions(const ConnInfo *ci)
 makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len)
 {
    char        got_dsn = (ci->dsn[0] != '\0');
-   char        encoded_conn_settings[LARGE_REGISTRY_LEN];
+   char        encoded_item[LARGE_REGISTRY_LEN];
    ssize_t     hlen, nlen, olen;
    /*BOOL      abbrev = (len <= 400);*/
    BOOL        abbrev = (len < 1024) || 0 < ci->force_abbrev_connstr;
    UInt4       flag;
 
 inolog("force_abbrev=%d abbrev=%d\n", ci->force_abbrev_connstr, abbrev);
+   encode(ci->password, encoded_item, sizeof(encoded_item));
    /* fundamental info */
    nlen = MAX_CONNECT_STRING;
    olen = snprintf(connect_string, nlen, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;PWD=%s",
            ci->server,
            ci->port,
            ci->username,
-           ci->password);
+           encoded_item);
    if (olen < 0 || olen >= nlen)
    {
        connect_string[0] = '\0';
        return;
    }
 
-   encode(ci->conn_settings, encoded_conn_settings);
+   encode(ci->conn_settings, encoded_item, sizeof(encoded_item));
 
    /* extra info */
    hlen = strlen(connect_string);
            INI_BYTEAASLONGVARBINARY "=%d;"
            INI_USESERVERSIDEPREPARE "=%d;"
            INI_LOWERCASEIDENTIFIER "=%d;"
+#ifdef WIN32
+           INI_GSSAUTHUSEGSSAPI "=%d"
+#endif /* WIN32 */
 #ifdef _HANDLE_ENLIST_IN_DTC_
            INI_XAOPT "=%d" /* XAOPT */
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
            ,ci->show_oid_column
            ,ci->row_versioning
            ,ci->show_system_tables
-           ,encoded_conn_settings
+           ,encoded_item
            ,ci->drivers.fetch_max
            ,ci->drivers.socket_buffersize
            ,ci->drivers.unknown_sizes
            ,ci->bytea_as_longvarbinary
            ,ci->use_server_side_prepare
            ,ci->lower_case_identifier
+#ifdef WIN32
+           ,ci->gssauth_use_gssapi
+#endif /* WIN32 */
 #ifdef _HANDLE_ENLIST_IN_DTC_
            ,ci->xa_opt
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
            flag |= BIT_USESERVERSIDEPREPARE;
        if (ci->lower_case_identifier)
            flag |= BIT_LOWERCASEIDENTIFIER;
+       if (ci->gssauth_use_gssapi)
+           flag |= BIT_GSSAUTHUSEGSSAPI;
 
        if (ci->sslmode[0])
        {
                INI_INT8AS "=%d;"
                ABBR_EXTRASYSTABLEPREFIXES "=%s;"
                INI_ABBREVIATE "=%02x%x",
-               encoded_conn_settings,
+               encoded_item,
                ci->drivers.fetch_max,
                ci->drivers.socket_buffersize,
                ci->drivers.max_varchar_size,
    ci->bytea_as_longvarbinary = (char)((flag & BIT_BYTEAASLONGVARBINARY) != 0);
    ci->use_server_side_prepare = (char)((flag & BIT_USESERVERSIDEPREPARE) != 0);
    ci->lower_case_identifier = (char)((flag & BIT_LOWERCASEIDENTIFIER) != 0);
+   ci->gssauth_use_gssapi = (char)((flag & BIT_GSSAUTHUSEGSSAPI) != 0);
 }
 BOOL
 copyAttributes(ConnInfo *ci, const char *attribute, const char *value)
    else if (stricmp(attribute, INI_SERVER) == 0 || stricmp(attribute, SPEC_SERVER) == 0)
        strcpy(ci->server, value);
 
-   else if (stricmp(attribute, INI_USER) == 0 || stricmp(attribute, INI_UID) == 0)
+   else if (stricmp(attribute, INI_USERNAME) == 0 || stricmp(attribute, INI_UID) == 0)
        strcpy(ci->username, value);
 
    else if (stricmp(attribute, INI_PASSWORD) == 0 || stricmp(attribute, "pwd") == 0)
-       strcpy(ci->password, value);
+       decode(value, ci->password, sizeof(ci->password));
 
    else if (stricmp(attribute, INI_PORT) == 0)
        strcpy(ci->port, value);
                ci->conn_settings[len - 1] = '\0';
        }
        else
-           decode(value, ci->conn_settings);
+           decode(value, ci->conn_settings, sizeof(ci->conn_settings));
    }
    else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, ABBR_DISALLOWPREMATURE) == 0)
        ci->disallow_premature = atoi(value);
        ci->use_server_side_prepare = atoi(value);
    else if (stricmp(attribute, INI_LOWERCASEIDENTIFIER) == 0 || stricmp(attribute, ABBR_LOWERCASEIDENTIFIER) == 0)
        ci->lower_case_identifier = atoi(value);
+   else if (stricmp(attribute, INI_GSSAUTHUSEGSSAPI) == 0 || stricmp(attribute, ABBR_GSSAUTHUSEGSSAPI) == 0)
+       ci->gssauth_use_gssapi = atoi(value);
    else if (stricmp(attribute, INI_SSLMODE) == 0 || stricmp(attribute, ABBR_SSLMODE) == 0)
    {
        switch (value[0])
        ci->use_server_side_prepare = DEFAULT_USESERVERSIDEPREPARE;
    if (ci->lower_case_identifier < 0)
        ci->lower_case_identifier = DEFAULT_LOWERCASEIDENTIFIER;
+   if (ci->gssauth_use_gssapi < 0)
+       ci->gssauth_use_gssapi = DEFAULT_GSSAUTHUSEGSSAPI;
    if (ci->sslmode[0] == '\0')
        strcpy(ci->sslmode, DEFAULT_SSLMODE);
    if (ci->force_abbrev_connstr < 0)
 {
    CSTR    func = "getDSNinfo";
    char       *DSN = ci->dsn;
-   char        encoded_conn_settings[LARGE_REGISTRY_LEN],
+   char        encoded_item[LARGE_REGISTRY_LEN],
                temp[SMALL_REGISTRY_LEN];
 
 /*
        SQLGetPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI);
 
    if (ci->username[0] == '\0' || overwrite)
-       SQLGetPrivateProfileString(DSN, INI_USER, "", ci->username, sizeof(ci->username), ODBC_INI);
+       SQLGetPrivateProfileString(DSN, INI_USERNAME, "", ci->username, sizeof(ci->username), ODBC_INI);
 
    if (ci->password[0] == '\0' || overwrite)
-       SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", ci->password, sizeof(ci->password), ODBC_INI);
+   {
+       SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", encoded_item, sizeof(encoded_item), ODBC_INI);
+       decode(encoded_item, ci->password, sizeof(ci->password));
+   }
 
    if (ci->port[0] == '\0' || overwrite)
        SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI);
 
    if (ci->conn_settings[0] == '\0' || overwrite)
    {
-       SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_conn_settings, sizeof(encoded_conn_settings), ODBC_INI);
-       decode(encoded_conn_settings, ci->conn_settings);
+       SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_item, sizeof(encoded_item), ODBC_INI);
+       decode(encoded_item, ci->conn_settings, sizeof(ci->conn_settings));
    }
 
    if (ci->translation_dll[0] == '\0' || overwrite)
            ci->lower_case_identifier = atoi(temp);
    }
 
+   if (ci->gssauth_use_gssapi < 0 || overwrite)
+   {
+       SQLGetPrivateProfileString(DSN, INI_GSSAUTHUSEGSSAPI, "", temp, sizeof(temp), ODBC_INI);
+       if (temp[0])
+           ci->gssauth_use_gssapi = atoi(temp);
+   }
+
    if (ci->sslmode[0] == '\0' || overwrite)
        SQLGetPrivateProfileString(DSN, INI_SSLMODE, "", ci->sslmode, sizeof(ci->sslmode), ODBC_INI);
 
 writeDSNinfo(const ConnInfo *ci)
 {
    const char *DSN = ci->dsn;
-   char        encoded_conn_settings[LARGE_REGISTRY_LEN],
+   char        encoded_item[LARGE_REGISTRY_LEN],
                temp[SMALL_REGISTRY_LEN];
 
-   encode(ci->conn_settings, encoded_conn_settings);
 
    SQLWritePrivateProfileString(DSN,
                                 INI_KDESC,
                                 ODBC_INI);
 
    SQLWritePrivateProfileString(DSN,
-                                INI_USER,
+                                INI_USERNAME,
                                 ci->username,
                                 ODBC_INI);
    SQLWritePrivateProfileString(DSN, INI_UID, ci->username, ODBC_INI);
 
+   encode(ci->password, encoded_item, sizeof(encoded_item));
    SQLWritePrivateProfileString(DSN,
                                 INI_PASSWORD,
-                                ci->password,
+                                encoded_item,
                                 ODBC_INI);
 
    SQLWritePrivateProfileString(DSN,
                                 temp,
                                 ODBC_INI);
 
+   encode(ci->conn_settings, encoded_item, sizeof(encoded_item));
    SQLWritePrivateProfileString(DSN,
                                 INI_CONNSETTINGS,
-                                encoded_conn_settings,
+                                encoded_item,
                                 ODBC_INI);
 
    sprintf(temp, "%d", ci->disallow_premature);
                                 INI_LOWERCASEIDENTIFIER,
                                 temp,
                                 ODBC_INI);
+   sprintf(temp, "%d", ci->gssauth_use_gssapi);
+   SQLWritePrivateProfileString(DSN,
+                                INI_GSSAUTHUSEGSSAPI,
+                                temp,
+                                ODBC_INI);
    SQLWritePrivateProfileString(DSN,
                                 INI_SSLMODE,
                                 ci->sslmode,
 }
 
 static void
-encode(const UCHAR *in, UCHAR *out)
+encode(const UCHAR *in, UCHAR *out, int outlen)
 {
    size_t i, ilen = strlen(in),
                o = 0;
    UCHAR   inc;
 
-   for (i = 0; i < ilen; i++)
+   for (i = 0; i < ilen && o < outlen - 1; i++)
    {
        inc = in[i];
        if (inc == '+')
        {
+           if (o + 2 >= outlen)
+               break;
            sprintf(&out[o], "%%2B");
            o += 3;
        }
            out[o++] = '+';
        else if (!isalnum(inc))
        {
+           if (o + 2 >= outlen)
+               break;
            sprintf(&out[o], "%%%02x", inc);
            o += 3;
        }
 }
 
 static void
-decode(const UCHAR *in, UCHAR *out)
+decode(const UCHAR *in, UCHAR *out, int outlen)
 {
    size_t i, ilen = strlen(in),
                o = 0;
    UCHAR   inc;
 
-   for (i = 0; i < ilen; i++)
+   for (i = 0; i < ilen && o < outlen - 1; i++)
    {
        inc = in[i];
        if (inc == '+')
    }
    out[o++] = '\0';
 }
+
+char *extract_attribute_setting(const char *str, const char *attr, BOOL ref_comment)
+{
+   const UCHAR *cptr, *sptr = NULL;
+   UCHAR   *rptr;
+   BOOL    allowed_cmd = TRUE, in_quote = FALSE, in_comment = FALSE;
+   int step = 0, skiplen;
+   size_t  len = 0, attrlen = strlen(attr);
+
+        for (cptr = (UCHAR *) str; *cptr; cptr++)
+        {
+       if (in_quote)
+       {
+           if (LITERAL_QUOTE == *cptr)
+           {
+               if (4 == step)
+               {
+                   len = cptr - sptr;
+                   step++;
+               }
+               in_quote = FALSE;
+           }
+           continue;
+       }
+       else if (in_comment)
+       {
+           if ('*' == *cptr &&
+               '/' == cptr[1])
+           {
+               if (4 == step)
+               {
+                   len = cptr - sptr;
+                   step++;
+               }
+               in_comment = FALSE;
+               cptr++;
+               continue;
+           }
+           if (!ref_comment)
+               continue;
+       }
+       else if ('/' == *cptr &&
+            '*' == cptr[1])
+       {
+           in_comment = TRUE;
+           cptr++;
+           continue;
+       }
+       if (';' == *cptr)
+       {
+           if (4 == step)
+           {
+               len = cptr - sptr;
+           }
+           allowed_cmd = TRUE;
+           step = 0;
+           continue;
+       }
+       if (!allowed_cmd)
+           continue;
+       if (isspace(*cptr))
+       {
+           if (4 == step)
+           {
+               len =  cptr - sptr;
+               step++;
+           }
+           continue;
+       }
+       switch (step)
+       {
+           case 0:
+               if (0 != strnicmp(cptr, "set", 3))
+               {
+                   allowed_cmd = FALSE;
+                   continue;
+               }
+               step++;
+               cptr += 3;
+               break;
+           case 1:
+               if (0 != strnicmp(cptr, attr, attrlen))
+               {
+                   allowed_cmd = FALSE;
+                   continue;
+               }
+               step++;
+               cptr += (attrlen - 1);
+               break;
+           case 2:
+               skiplen = 0;
+               if (0 != strnicmp(cptr, "=", 1))
+               {
+                   skiplen = strlen("to");
+                   if (0 != strnicmp(cptr, "to", 2))
+                   {
+                       allowed_cmd = FALSE;
+                       continue;
+                   }
+               }
+               step++;
+               cptr += skiplen;
+               break;
+           case 3:
+               if (LITERAL_QUOTE == *cptr)
+               {
+                   cptr++;
+                   sptr = cptr;
+               }
+               else
+                   sptr = cptr;
+               step++;
+               break;
+       }
+   }
+   if (!sptr)
+       return NULL;
+   rptr = malloc(len + 1);
+   memcpy(rptr, sptr, len);
+   rptr[len] = '\0';
+   mylog("extracted a %s '%s' from %s\n", attr, rptr, str);
+   return rptr;
+}
+
+/*
+ * extract the specified attribute from the comment part.
+ *     attribute=[']value[']
+ */
+char *extract_extra_attribute_setting(const char *str, const char *attr)
+{
+   const UCHAR *cptr, *sptr = NULL;
+   UCHAR   *rptr;
+   BOOL    allowed_cmd = FALSE, in_quote = FALSE, in_comment = FALSE;
+   int step = 0, step_last = 2;
+   size_t  len = 0, attrlen = strlen(attr);
+
+        for (cptr = (UCHAR *) str; *cptr; cptr++)
+        {
+       if (in_quote)
+       {
+           if (LITERAL_QUOTE == *cptr)
+           {
+               if (step_last == step)
+               {
+                   len = cptr - sptr;
+                   step = 0;
+               }
+               in_quote = FALSE;
+           }
+           continue;
+       }
+       else if (in_comment)
+       {
+           if ('*' == *cptr &&
+               '/' == cptr[1])
+           {
+               if (step_last == step)
+               {
+                   len = cptr - sptr;
+                   step = 0;
+               }
+               in_comment = FALSE;
+               allowed_cmd = FALSE;
+               cptr++;
+               continue;
+           }
+       }
+       else if ('/' == *cptr &&
+            '*' == cptr[1])
+       {
+           in_comment = TRUE;
+           allowed_cmd = TRUE;
+           cptr++;
+           continue;
+       }
+       else
+       {
+           if (LITERAL_QUOTE == *cptr)
+               in_quote = TRUE;
+           continue;
+       }
+       /* now in comment */
+       if (';' == *cptr ||
+           isspace(*cptr))
+       {
+           if (step_last == step)
+               len = cptr - sptr;
+           allowed_cmd = TRUE;
+           step = 0;
+           continue;
+       }
+       if (!allowed_cmd)
+           continue;
+       switch (step)
+       {
+           case 0:
+               if (0 != strnicmp(cptr, attr, attrlen))
+               {
+                   allowed_cmd = FALSE;
+                   continue;
+               }
+               if (cptr[attrlen] != '=')
+               {
+                   allowed_cmd = FALSE;
+                   continue;
+               }
+               step++;
+               cptr += attrlen;
+               break;
+           case 1:
+               if (LITERAL_QUOTE == *cptr)
+               {
+                   in_quote = TRUE;
+                   cptr++;
+                   sptr = cptr;
+               }
+               else
+                   sptr = cptr;
+               step++;
+               break;
+       }
+   }
+   if (!sptr)
+       return NULL;
+   rptr = malloc(len + 1);
+   memcpy(rptr, sptr, len);
+   rptr[len] = '\0';
+   mylog("extracted a %s '%s' from %s\n", attr, rptr, str);
+   return rptr;
+}
 
                         * Postmaster is listening */
 #define INI_DATABASE           "Database"  /* Database Name */
 #define INI_UID                "UID"       /* Default User Name */
-#define INI_USER           "Username"  /* Default User Name */
+#define INI_USERNAME           "Username"  /* Default User Name */
 #define INI_PASSWORD           "Password"  /* Default Password */
 
 #define    INI_ABBREVIATE          "CX"
 #define ABBR_SSLMODE           "CA"
 #define INI_EXTRAOPTIONS       "AB"
 #define INI_LOGDIR         "Logdir"
+#define INI_GSSAUTHUSEGSSAPI       "GssAuthUseGSS"
+#define ABBR_GSSAUTHUSEGSSAPI      "D0"
 
 #define    SSLMODE_DISABLE     "disable"
 #define    SSLMODE_ALLOW       "allow"
 #define BIT_BYTEAASLONGVARBINARY       (1L<<24)
 #define BIT_USESERVERSIDEPREPARE       (1L<<25)
 #define BIT_LOWERCASEIDENTIFIER            (1L<<26)
+#define BIT_GSSAUTHUSEGSSAPI           (1L<<27)
 
-#define EFFECTIVE_BIT_COUNT            27
+#define EFFECTIVE_BIT_COUNT            28
 
 /* Mask for extra options  */
 #define    BIT_FORCEABBREVCONNSTR          1L
 #define DEFAULT_USESERVERSIDEPREPARE   0
 #define DEFAULT_LOWERCASEIDENTIFIER    0
 #define DEFAULT_SSLMODE            SSLMODE_DISABLE
+#define DEFAULT_GSSAUTHUSEGSSAPI   0
 
 #ifdef _HANDLE_ENLIST_IN_DTC_
 #define DEFAULT_XAOPT          1
 int     changeDriverNameOfaDSN(const char *dsn, const char *driver_name, DWORD *errcode);
 UInt4  getExtraOptions(const ConnInfo *);
 BOOL   setExtraOptions(ConnInfo *, const char *str, const char *format);
+char   *extract_attribute_setting(const char *str, const char *attr, BOOL ref_comment);
+char   *extract_extra_attribute_setting(const char *str, const char *attr);
 
 #ifdef __cplusplus
 }
 
            CheckDlgButton(hdlg, DS_SERVERSIDEPREPARE, ci->use_server_side_prepare);
            CheckDlgButton(hdlg, DS_BYTEAASLONGVARBINARY, ci->bytea_as_longvarbinary);
            /*CheckDlgButton(hdlg, DS_LOWERCASEIDENTIFIER, ci->lower_case_identifier);*/
+           CheckDlgButton(hdlg, DS_GSSAUTHUSEGSSAPI, ci->gssauth_use_gssapi);
 
+#if    defined(NOT_USE_LIBPQ) && !defined(USE_SSPI) && !defined(USE_GSS)
+           EnableWindow(GetDlgItem(hdlg, DS_GSSAUTHUSEGSSAPI), FALSE);
+#endif
            EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column));
 
            /* Datasource Connection Settings */
                    ci->use_server_side_prepare = IsDlgButtonChecked(hdlg, DS_SERVERSIDEPREPARE);
                    ci->bytea_as_longvarbinary = IsDlgButtonChecked(hdlg, DS_BYTEAASLONGVARBINARY);
                    /*ci->lower_case_identifier = IsDlgButtonChecked(hdlg, DS_LOWERCASEIDENTIFIER);*/
+                   ci->gssauth_use_gssapi = IsDlgButtonChecked(hdlg, DS_GSSAUTHUSEGSSAPI);
 
                    /* OID Options */
                    sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX));
 
 #if defined(DYNAMIC_LOAD)
 #define    WIN_DYN_LOAD
 CSTR   libpqdll = "LIBPQ.dll";
+#ifdef _WIN64
+CSTR   gssapidll = "GSSAPI64.dll";
+#else
+CSTR   gssapidll = "GSSAPI32.dll";
+#endif /* _WIN64 */
 #ifdef UNICODE_SUPPORT
 CSTR   pgenlist = "pgenlist";
 CSTR   pgenlistdll = "PGENLIST.dll";
 #endif /* DYNAMIC_LOAD */
 #endif /* WIN32 */
 
-CSTR   libpq = "libpq";
+CSTR   libpqlib = "libpq";
+#ifdef _WIN64
+CSTR   gssapilib = "gssapi64";
+#else
+CSTR   gssapilib = "gssapi32";
+#endif /* _WIN64 */
 #ifndef    NOT_USE_LIBPQ
 CSTR   checkproc = "PQconninfoParse";
 static int sslverify_available = -1;
 
 #if defined(_MSC_DELAY_LOAD_IMPORT)
 static BOOL    loaded_libpq = FALSE, loaded_ssllib = FALSE;
-static BOOL    loaded_pgenlist = FALSE;
+static BOOL    loaded_pgenlist = FALSE, loaded_gssapi = FALSE;
 /*
  * Load psqlodbc path based libpq dll.
  */
 #else
            __pfnDliNotifyHook2 = NULL;
 #endif /* _MSC_VER */
-           if (_strnicmp(pdli->szDll, libpq, 5) == 0)
+           if (_strnicmp(pdli->szDll, libpqlib, strlen(libpqlib)) == 0)
            {
-               if (hmodule = MODULE_load_from_psqlodbc_path(libpq), NULL == hmodule)
-                   hmodule = LoadLibrary(libpq);
+               if (hmodule = MODULE_load_from_psqlodbc_path(libpqlib), NULL == hmodule)
+                   hmodule = LoadLibrary(libpqlib);
 #ifndef    NOT_USE_LIBPQ
                if (sslverify_available < 0)
                {
                if (hmodule = MODULE_load_from_psqlodbc_path(pgenlist), NULL == hmodule)
                    hmodule = LoadLibrary(pgenlist);
            }
+#ifdef USE_GSS
+           else if (_strnicmp(pdli->szDll, gssapilib, strlen(gssapilib)) == 0)
+           {
+#ifndef    NOT_USE_LIBPQ
+                       if (hmodule = GetModuleHandle(gssapilib), NULL == hmodule)
+#endif
+               {
+                   if (hmodule = MODULE_load_from_psqlodbc_path(gssapilib), NULL == hmodule)
+                   {
+                       if (hmodule = LoadLibrary(gssapilib), NULL != hmodule)
+                           loaded_gssapi = TRUE;
+                   }
+                   else
+                       loaded_gssapi = TRUE;
+               }
+           }
+#endif /* USE_GSS */
            else if (0 == _stricmp(pdli->szDll, libarray[0]) ||
                 0 == _stricmp(pdli->szDll, libarray[1]))
            {
        success = (*func)(pgenlistdll);
        mylog("%s unload success=%d\n", pgenlistdll, success);
    }
+   if (loaded_gssapi)
+   {
+       success = (*func)(gssapidll);
+       mylog("%s unload success=%d\n", gssapidll, success);
+   }
    return;
 }
 #else
            sslverify_available = 0;
 #else
 #ifdef HAVE_LIBLTDL
-       lt_dlhandle dlhandle = lt_dlopenext(libpq);
+       lt_dlhandle dlhandle = lt_dlopenext(libpqlib);
 
        sslverify_available = 1;
        if (NULL != dlhandle)
    mylog("checking libpq library\n");
    /* First search the driver's folder */
 #ifndef    NOT_USE_LIBPQ
-   if (NULL == (hmodule = MODULE_load_from_psqlodbc_path(libpq)))
+   if (NULL == (hmodule = MODULE_load_from_psqlodbc_path(libpqlib)))
        /* Second try the PATH ordinarily */
-       hmodule = LoadLibrary(libpq);
+       hmodule = LoadLibrary(libpqlib);
    mylog("libpq hmodule=%p\n", hmodule);
 #endif /* NOT_USE_LIBPQ */
 #ifdef USE_SSPI
 
  *
  * Comments:       See "notice.txt" for copyright and license information.
  *
- * $Id: psqlodbc.h,v 1.132 2009/10/25 13:36:31 hinoue Exp $
+ * $Id: psqlodbc.h,v 1.133 2010/01/17 13:10:41 hinoue Exp $
  *
  */
 
 #define    FORMAT_UINT4    "%u"    /* UInt4 */
 
 #ifdef WIN32
+#ifndef    SSIZE_T_DEFINED
 #define    ssize_t SSIZE_T
+#define    SSIZE_T_DEFINED
+#endif
 #define    FORMAT_SIZE_T   "%Iu"   /* size_t */
 #define    FORMAT_SSIZE_T  "%Id"   /* ssize_t */
 #define    FORMAT_INTEGER  "%ld"   /* SQLINTEGER */
 
     PUSHBUTTON      "\93K\97p",IDAPPLY,128,224,50,14
     CONTROL         "bytea\82ðLO\82Æ\82µ\82Ä\88µ\82¤",DS_BYTEAASLONGVARBINARY,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,15,85,87,10
+    CONTROL         "Kerberos\89\9e\93\9a\82ÉGSSAPI\97\98\97p",DS_GSSAUTHUSEGSSAPI,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,139,85,110,10
 END
 
 DLG_OPTIONS_GLOBAL DIALOG DISCARDABLE  0, 0, 306, 115
                     BS_AUTOCHECKBOX | WS_TABSTOP,163,71,90,10
     CONTROL         "bytea as LO",DS_BYTEAASLONGVARBINARY,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,16,84,87,10
+    CONTROL         "use gssapi for GSS request",DS_GSSAUTHUSEGSSAPI,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,163,84,110,10
     GROUPBOX        "Int8 As",IDC_STATIC,5,97,256,25
     CONTROL         "default",DS_INT8_AS_DEFAULT,"Button",BS_AUTORADIOBUTTON | 
                     WS_GROUP,12,107,40,10
 
 #define DS_EXTRA_OPTIONS       1084
 #define IDC_TEST           1085
 #define DS_LOGDIR          1086
+#define DS_GSSAUTHUSEGSSAPI        1087
 
 // Next default values for new objects
 // 
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        105
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1087
+#define _APS_NEXT_CONTROL_VALUE         1088
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
 
 #ifdef USE_SSPI
 #include "sspisvcs.h"
 #endif /* USE_SSPI */
+#ifdef USE_GSS
+#include "gsssvcs.h"
+#endif /* USE_GSS */
 #ifndef    NOT_USE_LIBPQ
 #include <libpq-fe.h>
 #ifdef USE_SSL
        rv->ssd = NULL;
        rv->sspisvcs = 0;
 #endif /* USE_SSPI */
+#ifdef USE_GSS
+       rv->gctx = NULL;
+       rv->gtarg_nam = NULL;
+#endif /* USE_GSS */
 #ifndef    NOT_USE_LIBPQ
        rv->via_libpq = FALSE;
 #ifdef USE_SSL
 #ifdef USE_SSPI
        if (self->ssd)
        {
-           ReleaseSvcSpecData(self);
+           ReleaseSvcSpecData(self, (UInt4) -1);
            free(self->ssd);
            self->ssd = NULL;
        }
        self->sspisvcs = 0;
 #endif /* USE_SSPI */
+#ifdef USE_GSS
+       pg_GSS_cleanup(self);
+#endif /* USE_GSS */
    }
 
    if (self->buffer_in)
 
 #define __SOCKET_H__
 
 #include "psqlodbc.h"
+#if defined (USE_GSS)
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#else
+#include <gssapi/gssapi.h>
+#endif
+#endif
 #include <errno.h>
 
 #ifndef WIN32
    void        *pqconn;    /* libpq PGConn */
    BOOL        via_libpq;  /* using libpq library ? */
 #endif /* NOT_USE_LIBPQ */
+#ifdef USE_GSS
+   gss_ctx_id_t    gctx;       /* GSS context */
+   gss_name_t  gtarg_nam;  /* GSS target name */
+#endif /* USE_GSS */
 
-   char        reverse;        /* used to handle Postgres 6.2 protocol
-                                * (reverse byte order) */
-
+   char        reverse;    /* used to handle Postgres 6.2 protocol
+                       * (reverse byte order) */
 };
 
 #define SOCK_get_char(self)    (SOCK_get_next_byte(self, FALSE))
 
    return ttllen;
 }
  
-CSTR   SCHANNEL = "sChannel";
-static int DoSchannelNegotiation(SocketClass *, const char *opt);
-int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt)
+/* 
+ * service specific data
+ */
+
+/* Schannel specific data */
+typedef    struct {
+   CredHandle  hCred;
+   CtxtHandle  hCtxt;
+   PBYTE       ioovrbuf;
+   size_t      ioovrlen;
+   PBYTE       iobuf;
+   size_t      iobuflen;
+   size_t      ioread;
+} SchannelSpec;
+
+/* Kerberos/Negotiate common specific data */
+typedef    struct {
+   LPTSTR      svcprinc;
+   CredHandle  hKerbEtcCred;
+   BOOL        ValidCtxt;
+   CtxtHandle  hKerbEtcCtxt;
+} KerberosEtcSpec;
+
+typedef struct {
+   SchannelSpec    sdata;
+   KerberosEtcSpec kdata;
+} SspiData;
+
+static int DoSchannelNegotiation(SocketClass *, SspiData *, const void *opt);
+static int DoKerberosNegotiation(SocketClass *, SspiData *, const void *opt);
+static int DoNegotiateNegotiation(SocketClass *, SspiData *, const void *opt);
+static int DoKerberosEtcProcessAuthentication(SocketClass *, const void *opt);
+
+static SspiData *SspiDataAlloc(SocketClass *self)
+{
+   SspiData    *sspidata;
+
+   if (sspidata = self->ssd, !sspidata)
+       sspidata = calloc(sizeof(SspiData), 1);
+   return sspidata;
+}
+
+int StartupSspiService(SocketClass *self, SSPI_Service svc, const void *opt)
 {
    CSTR func = "DoServicelNegotiation";
+   SspiData    *sspidata;
 
+   if (NULL == (sspidata = SspiDataAlloc(self)))
+       return -1;
    switch (svc)
    {
        case SchannelService:
-           return DoSchannelNegotiation(self, opt);
+           return DoSchannelNegotiation(self, sspidata, opt);
+       case KerberosService:
+           return DoKerberosNegotiation(self, sspidata, opt);
+       case NegotiateService:
+           return DoNegotiateNegotiation(self, sspidata, opt);
    }
 
+   free(sspidata);
    return -1;
 }
 
+int ContinueSspiService(SocketClass *self, SSPI_Service svc, const void *opt)
+{
+   CSTR func = "ContinueSspiService";
+
+   switch (svc)
+   {
+       case KerberosService:
+       case NegotiateService:
+           return DoKerberosEtcProcessAuthentication(self, opt);
+   }
+
+   return -1;
+}
+
+static BOOL format_sspierr(char *errmsg, size_t buflen, SECURITY_STATUS r, const char *cmd, const char *cmd2)
+{
+   BOOL ret = FALSE;
+
+   if (!cmd2)
+       cmd2 = ""; 
+   if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
+       r, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+       errmsg, (DWORD)buflen, NULL))
+       ret = TRUE;
+   if (ret)
+   {
+       size_t  tlen = strlen(errmsg);
+       errmsg += tlen;
+       buflen -= tlen;
+       snprintf(errmsg, buflen, " in %s:%s", cmd, cmd2);
+   }
+   else
+       snprintf(errmsg, buflen, "%s:%s failed ", cmd, cmd2);
+   return ret;
+}
+
+static void SSPI_set_error(SocketClass *s, SECURITY_STATUS r, const char *cmd, const char *cmd2)
+{
+   int gerrno = SOCK_ERRNO;
+   char    emsg[256];
+
+   format_sspierr(emsg, sizeof(emsg), r, cmd, cmd2);
+   s->errornumber = r;
+   if (NULL != s->_errormsg_)
+       free(s->_errormsg_);
+   if (NULL != emsg)
+       s->_errormsg_ = strdup(emsg);
+   else
+       s->_errormsg_ = NULL;
+   mylog("(%d)%s ERRNO=%d\n", r, emsg, gerrno);
+}
+
 /*
  * Stuff for Schannel service
  */
 #define    UNI_SCHANNEL TEXT("sChannel")
 #define    IO_BUFFER_SIZE  0x10000
 
-typedef    struct {
-   CredHandle  hCred;
-   CtxtHandle  hCtxt;
-   PBYTE       ioovrbuf;
-   size_t      ioovrlen;
-   PBYTE       iobuf;
-   size_t      iobuflen;
-   size_t      ioread;
-} SchannelSpec;
-
 static SECURITY_STATUS CreateSchannelCredentials(LPCTSTR, LPSTR, PCredHandle);
 static SECURITY_STATUS PerformSchannelClientHandshake(SOCKET, PCredHandle, LPSTR, CtxtHandle *, SecBuffer *);
 static SECURITY_STATUS SchannelClientHandshakeLoop(SOCKET, PCredHandle, CtxtHandle *, BOOL, SecBuffer *);
 
 static HCERTSTORE  hMyCertStore = NULL;
 
-static int DoSchannelNegotiation(SocketClass *self, const char *opt)
+static int DoSchannelNegotiation(SocketClass *self, SspiData *sspidata, const void *opt)
 {
    CSTR func = "DoSchannelNegotiation";
+   SECURITY_STATUS r = SEC_E_OK;
+   const char  *cmd = NULL;
    SecBuffer   ExtraData;
    BOOL        ret = 0, cCreds = FALSE, cCtxt = FALSE;
-   SchannelSpec    *ssd = NULL;
+   SchannelSpec    *ssd = &(sspidata->sdata);
 
-   if (NULL == (ssd = calloc(sizeof(SchannelSpec), 1)))
-   {
-       return 0;
-   }
-   if (SEC_E_OK != CreateSchannelCredentials(NULL, NULL, &ssd->hCred))
+   if (SEC_E_OK != (r = CreateSchannelCredentials(NULL, NULL, &ssd->hCred)))
    {
-       mylog("%s:CreateSchannel failed\n", func);
+       cmd = "CreateSchannelCredentials";
+       mylog("%s:%s failed\n", func, cmd);
        goto cleanup;
    }
    cCreds = TRUE;
-   if (SEC_E_OK != PerformSchannelClientHandshake(self->socket, &ssd->hCred, NULL, &ssd->hCtxt, &ExtraData))
+   if (SEC_E_OK != (r = PerformSchannelClientHandshake(self->socket, &ssd->hCred, NULL, &ssd->hCtxt, &ExtraData)))
    {
-       mylog("%s:PerformSchannelClientHandshake failed\n", func);
+       cmd = "PerformSchannelClientHandshake";
+       mylog("%s:%s failed\n", func, cmd);
        goto cleanup;
    }
    cCtxt = TRUE;
    if (ret)
    {
        self->sspisvcs |= SchannelService;
-       self->ssd = ssd;
+       self->ssd = sspidata;
    }
    else
    {
+       SSPI_set_error(self, r, __FUNCTION__, cmd); 
        if (cCreds)
            FreeCredentialHandle(&ssd->hCred);
        if (cCtxt)
            DeleteSecurityContext(&ssd->hCtxt);
        if (ssd->iobuf)
            free(ssd->iobuf);
-       free(ssd);
+       if (!self->ssd)
+           free(sspidata);
    }
    return ret;
 }
     */
 
    Status = AcquireCredentialsHandle(
-           NULL,                   /* Name of principal */    
-           UNI_SCHANNEL,           /* Name of package */
-           SECPKG_CRED_OUTBOUND,   /* Flags indicating use */
-           NULL,                   /* Pointer to logon ID */
-           &SchannelCred,          /* Package specific data */
-           NULL,                   /* Pointer to GetKey() func */
-           NULL,                   /* Value to pass to GetKey() */
-           phCreds,                /* (out) Cred Handle */
-           &tsExpiry);             /* (out) Lifetime (optional) */
+           NULL,           /* Name of principal */    
+           UNI_SCHANNEL,       /* Name of package */
+           SECPKG_CRED_OUTBOUND,   /* Flags indicating use */
+           NULL,           /* Pointer to logon ID */
+           &SchannelCred,      /* Package specific data */
+           NULL,           /* Pointer to GetKey() func */
+           NULL,           /* Value to pass to GetKey() */
+           phCreds,        /* (out) Cred Handle */
+           &tsExpiry);     /* (out) Lifetime (optional) */
    if (Status != SEC_E_OK)
    {
        mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status);
 
    if (scRet != SEC_I_CONTINUE_NEEDED)
    {
-       mylog("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
+       mylog("**** Error %x returned by InitializeSecurityContext (1)\n", scRet);
        return scRet;
    }
 
                OutBuffers[0].cbBuffer);
        if (cbData <= 0)
        {
-           mylog("**** Error %d sending data to server (1)\n", SOCK_ERRNO);
+           mylog("**** Error %x sending data to server\n", SOCK_ERRNO);
            FreeContextBuffer(OutBuffers[0].pvBuffer);
            DeleteSecurityContext(phContext);
            return SEC_E_INTERNAL_ERROR;
                                continue;
                        default:
                            scRet = SEC_E_INTERNAL_ERROR;
+                           SOCK_ERRNO_SET(gerrno);
                            break;
                    }
                    break;
    }
 }
 
+/*
+ * Stuff for Kerberos etc service
+ */
+#define    UNI_KERBEROS TEXT("Kerberos")
+#define    UNI_NEGOTIATE TEXT("Negotiate")
+#define    IO_BUFFER_SIZE  0x10000
+
+
+static SECURITY_STATUS CreateKerberosEtcCredentials(LPCTSTR, SEC_CHAR *, LPCTSTR, PCredHandle);
+static SECURITY_STATUS PerformKerberosEtcClientHandshake(SocketClass *, KerberosEtcSpec *ssd, size_t);
+
+static int DoKerberosNegotiation(SocketClass *self, SspiData *sspidata, const void *opt)
+{
+   CSTR func = "DoKerberosNegotiation";
+   SECURITY_STATUS r = SEC_E_OK;
+   const char *    cmd = NULL;
+   BOOL        ret = 0;
+   KerberosEtcSpec *ssd = &(sspidata->kdata);
+
+mylog("!!! %s in\n", __FUNCTION__);
+   if (SEC_E_OK != (r = CreateKerberosEtcCredentials(NULL, UNI_KERBEROS, (LPCTSTR) opt, &ssd->hKerbEtcCred)))
+   {
+       cmd = "CreateKerberosCredentials";
+       mylog("%s:%s failed\n", func, cmd);
+       SSPI_set_error(self, r, __FUNCTION__, cmd); 
+       return 0;
+   }
+mylog("!!! CreateKerberosCredentials passed\n");
+
+   ssd->svcprinc = (LPTSTR) opt;
+   self->sspisvcs |= KerberosService;
+   self->ssd = sspidata;
+   return DoKerberosEtcProcessAuthentication(self, NULL);
+}
+
+static int DoNegotiateNegotiation(SocketClass *self, SspiData *sspidata, const void *opt)
+{
+   CSTR func = "DoNegotiateNegotiation";
+   SECURITY_STATUS r = SEC_E_OK;
+   const char *    cmd = NULL;
+   BOOL        ret = 0;
+   KerberosEtcSpec *ssd = &(sspidata->kdata);
+
+mylog("!!! %s in\n", __FUNCTION__);
+   if (SEC_E_OK != (r = CreateKerberosEtcCredentials(NULL, UNI_NEGOTIATE, (LPCTSTR) opt, &ssd->hKerbEtcCred)))
+   {
+       cmd = "CreateNegotiateCredentials";
+       mylog("%s:%s failed\n", func, cmd);
+       SSPI_set_error(self, r, __FUNCTION__, cmd); 
+       return 0;
+   }
+mylog("!!! CreateNegotiateCredentials passed\n");
+
+   ssd->svcprinc = (LPTSTR) opt;
+   self->sspisvcs |= NegotiateService;
+   self->ssd = sspidata;
+   return DoKerberosEtcProcessAuthentication(self, NULL);
+}
+
+static int DoKerberosEtcProcessAuthentication(SocketClass *self, const void *opt)
+{
+   CSTR func = "DoKerberosEtcProcessAuthentication";
+   SECURITY_STATUS r = SEC_E_OK;
+   const char *    cmd = NULL;
+   BOOL        ret = 0, cCtxt = FALSE;
+   KerberosEtcSpec *ssd;
+
+mylog("!!! %s in\n", __FUNCTION__);
+   ssd = &(((SspiData *)(self->ssd))->kdata);
+   if (SEC_E_OK != (r = PerformKerberosEtcClientHandshake(self, ssd, (size_t) opt)))
+   {
+       cmd = "PerformKerberosEtcClientHandshake";
+       mylog("%s:%s failed\n", func, cmd);
+       goto cleanup;
+   }
+mylog("!!! PerformKerberosEtcClientHandshake passed\n");
+   cCtxt = TRUE;
+   ret = TRUE;
+cleanup:
+   if (!ret)
+   {
+       SSPI_set_error(self, r, __FUNCTION__, cmd); 
+       FreeCredentialHandle(&ssd->hKerbEtcCred);
+       if (cCtxt)
+       {
+           DeleteSecurityContext(&ssd->hKerbEtcCtxt);
+       }
+       self->sspisvcs &= (~(KerberosService | NegotiateService));
+   }
+   return ret;
+}
+
+static
+SECURITY_STATUS
+CreateKerberosEtcCredentials(
+   LPCTSTR opt,        /* in */
+   SEC_CHAR *packname, /* in */
+   LPCTSTR pszUserName,    /* in */
+   PCredHandle phCreds)    /* out */
+{
+   TimeStamp   tsExpiry;
+   SECURITY_STATUS Status;
+
+   /*
+    * Create an SSPI credential.
+    */
+
+   Status = AcquireCredentialsHandle(
+           NULL,           /* Name of principal */    
+           packname,       /* Name of package */
+           SECPKG_CRED_OUTBOUND,   /* Flags indicating use */
+           NULL,           /* Pointer to logon ID */
+           NULL,           /* Package specific data */
+           NULL,           /* Pointer to GetKey() func */
+           NULL,           /* Value to pass to GetKey() */
+           phCreds,        /* (out) Cred Handle */
+           &tsExpiry);     /* (out) Lifetime (optional) */
+   if (Status != SEC_E_OK)
+   {
+       mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status);
+       goto cleanup;
+   }
+
+cleanup:
+
+    return Status;
+}
+
+static
+SECURITY_STATUS
+PerformKerberosEtcClientHandshake(
+   SocketClass *sock,      /* in */
+   KerberosEtcSpec *ssd,       /* i-o */
+   size_t      inlen)
+{
+   SecBufferDesc   InBuffer;
+   SecBuffer   InBuffers[1];
+   SecBufferDesc   OutBuffer;
+   SecBuffer   OutBuffers[1];
+   DWORD       dwSSPIFlags;
+   DWORD       dwSSPIOutFlags;
+   TimeStamp   tsExpiry;
+   SECURITY_STATUS scRet;
+   CtxtHandle  hContext;
+   PBYTE       inbuf = NULL;
+
+mylog("!!! inlen=%u svcprinc=%s\n", inlen, ssd->svcprinc); 
+   if (ssd->ValidCtxt && inlen > 0)
+   {
+       if (NULL == (inbuf = malloc(inlen + 1)))
+       {
+           return SEC_E_INTERNAL_ERROR;
+       }
+       SOCK_get_n_char(sock, inbuf, inlen);
+       if (SOCK_get_errcode(sock) != 0)
+       {
+           mylog("**** Error %d receiving data from server (1)\n", SOCK_ERRNO);
+           free(inbuf);
+           return SEC_E_INTERNAL_ERROR;
+       }
+
+       InBuffer.ulVersion = SECBUFFER_VERSION;
+       InBuffer.cBuffers = 1;
+       InBuffer.pBuffers = InBuffers;
+       InBuffers[0].pvBuffer = inbuf; 
+       InBuffers[0].cbBuffer = inlen; 
+       InBuffers[0].BufferType = SECBUFFER_TOKEN; 
+   }
+
+   dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
+           ISC_REQ_REPLAY_DETECT   |
+           ISC_REQ_CONFIDENTIALITY |
+           ISC_RET_EXTENDED_ERROR  |
+           ISC_REQ_ALLOCATE_MEMORY |
+           ISC_REQ_STREAM;
+
+   /*
+    *  Initiate a ClientHello message and generate a token.
+    */
+
+   OutBuffers[0].pvBuffer  = NULL;
+   OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+   OutBuffers[0].cbBuffer  = 0;
+
+   OutBuffer.cBuffers = 1;
+   OutBuffer.pBuffers = OutBuffers;
+   OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+mylog("!!! before InitializeSecurityContext\n"); 
+   scRet = InitializeSecurityContext(
+                   &ssd->hKerbEtcCred,
+                   ssd->ValidCtxt ? &ssd->hKerbEtcCtxt : NULL,
+                   ssd->svcprinc,
+                   dwSSPIFlags,
+                   0,
+                   SECURITY_NATIVE_DREP,
+                   ssd->ValidCtxt ? &InBuffer : NULL,
+                   0,
+                   &hContext,
+                   &OutBuffer,
+                   &dwSSPIOutFlags,
+                   &tsExpiry);
+mylog("!!! %s:InitializeSecurityContext ret=%x\n", __FUNCTION__, scRet); 
+
+   if (inbuf)
+       free(inbuf);
+   if (SEC_E_OK != scRet && SEC_I_CONTINUE_NEEDED != scRet)
+   {
+       mylog("**** Error %x returned by InitializeSecurityContext\n", scRet);
+       return scRet;
+   }
+   if (!ssd->ValidCtxt)
+   {
+       memcpy(&ssd->hKerbEtcCtxt, &hContext, sizeof(CtxtHandle));
+       ssd->ValidCtxt = TRUE;
+   }
+
+mylog("!!! cbBuffer=%d pvBuffer=%p\n", OutBuffers[0].cbBuffer, OutBuffers[0].pvBuffer); 
+   /* Send response to server if there is one. */
+   if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+   {
+       int reslen = OutBuffers[0].cbBuffer;
+mylog("!!! responding 'p' + int(%d) + %dbytes of data\n", reslen + 4, reslen); 
+       SOCK_put_char(sock, 'p');
+       SOCK_put_int(sock, reslen + 4, 4);
+       SOCK_put_n_char(sock, OutBuffers[0].pvBuffer, reslen);
+       SOCK_flush_output(sock);
+       if (SOCK_get_errcode(sock) != 0)
+       {
+           mylog("**** Error %d sending data to server (1)\n", SOCK_ERRNO);
+           FreeContextBuffer(OutBuffers[0].pvBuffer);
+           return SEC_E_INTERNAL_ERROR;
+       }
+
+       mylog("%d bytes of handshake data sent\n", OutBuffers[0].cbBuffer);
+
+       /* Free output buffer. */
+       FreeContextBuffer(OutBuffers[0].pvBuffer);
+       OutBuffers[0].pvBuffer = NULL;
+   }
+
+   return SEC_E_OK;
+   // return KerberosEtcClientHandshakeLoop(Socket, ssd, TRUE, pExtraData);
+}
+
+
 int SSPI_recv(SocketClass *self, void *buffer, int len)
 {
    CSTR func = "SSPI_recv";
        DWORD   cbIoBuffer, cbIoBufferLength;
 
        DWORD   cbData;
-       SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+       SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata);
 
 mylog("buflen=%d,%d ovrlen=%d\n", ssd->iobuflen, ssd->ioread, ssd->ioovrlen);
        if (ssd->ioovrlen > 0)
                    {
                        case EINTR:
                            continue;
-                       case ECONNRESET:
-                           break;
                        case EWOULDBLOCK:
                            retry_count++;
                            if (Socket_wait_for_ready(self->socket, FALSE, retry_count) >= 0)
                                continue;
                        default:
+                           SOCK_ERRNO_SET(gerrno);
                            scRet = SEC_E_INTERNAL_ERROR;
                            break;
                    }
        LPVOID  lpTrail;
        SecBuffer   sb[4];
        SecBufferDesc   sbd;
-       SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+       SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata);
 
        QueryContextAttributes(&ssd->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sizes);
        slen = len;
        return send(self->socket, (char *) buffer, len, SEND_FLAG);
 }
 
-void ReleaseSvcSpecData(SocketClass *self)
+void ReleaseSvcSpecData(SocketClass *self, UInt4 svc)
 {
    if (!self->ssd)
        return;
-   if (0 != (self->sspisvcs & SchannelService))
+   if (0 != (self->sspisvcs & (svc & SchannelService)))
    {
-       SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+       SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata);
 
        if (ssd->iobuf)
        {
        DeleteSecurityContext(&ssd->hCtxt);
        self->sspisvcs &= (~SchannelService);
    }
+   if (0 != (self->sspisvcs & (svc & (KerberosService | NegotiateService))))
+   {
+       KerberosEtcSpec *ssd = &(((SspiData *)(self->ssd))->kdata);
+
+       if (ssd->svcprinc)
+       {
+           free(ssd->svcprinc);
+           ssd->svcprinc = NULL;
+       }
+       FreeCredentialHandle(&ssd->hKerbEtcCred);
+       if (ssd->ValidCtxt)
+       {
+           DeleteSecurityContext(&ssd->hKerbEtcCtxt);
+           ssd->ValidCtxt = FALSE;
+       }
+       self->sspisvcs &= (~(KerberosService | NegotiateService));
+   }
 }
 #endif /* USE_SSPI */
 
 typedef enum {
    SchannelService = 1L
    ,KerberosService = (1L << 1)
+   ,NegotiateService = (1L << 2)
 } SSPI_Service;
 
-void   ReleaseSvcSpecData(SocketClass *self);
-int    StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt);
+void   ReleaseSvcSpecData(SocketClass *self, UInt4);
+int    StartupSspiService(SocketClass *self, SSPI_Service svc, const void *opt);
+int    ContinueSspiService(SocketClass *self, SSPI_Service svc, const void *opt);
 int    SSPI_recv(SocketClass *self, void *buf, int len);
 int    SSPI_send(SocketClass *self, const void *buf, int len);
 
 
 }
 
 /*
- * Scan the query wholly or partially (if specifed next_cmd).
+ * Scan the query wholly or partially (if the next_cmd param specified).
  * Also count the number of parameters respectviely.
  */
 void
    char    tchar, bchar, escape_in_literal = '\0';
    char    in_literal = FALSE, in_identifier = FALSE,
        in_dollar_quote = FALSE, in_escape = FALSE,
+       in_line_comment = FALSE, in_comment = FALSE,
        del_found = FALSE;
    po_ind_t multi = FALSE;
    SQLSMALLINT num_p;
            if (tchar == identifier_quote)
                in_identifier = FALSE;
        }
+       else if (in_line_comment)
+       {
+           if (PG_LINEFEED == tchar)
+               in_line_comment = FALSE;
+       }
+       else if (in_comment)
+       {
+           if ('*' == tchar && '/' == sptr[1])
+           {
+               encoded_nextchar(&encstr);
+               sptr++;
+               in_comment = FALSE;
+           }
+       }
        else
        {
            if (tchar == '?')
            }
            else if (tchar == identifier_quote)
                in_identifier = TRUE;
+           else if ('-' == tchar)
+           {
+               if ('-' == sptr[1])
+               {
+                   encoded_nextchar(&encstr);
+                   sptr++;
+                   in_line_comment = TRUE;
+               }
+           }
+           else if ('/' == tchar)
+           {
+               if ('*' == sptr[1])
+               {
+                   encoded_nextchar(&encstr);
+                   sptr++;
+                   in_comment = TRUE;
+               }
+           }
            if (!isspace(tchar))
                bchar = tchar;
        }
                break;
            case 'B': /* Binary data */
            case 'D': /* ASCII data */
-               QR_get_tupledata(res, id == 'B');
+               if (!QR_get_tupledata(res, id == 'B'))
+               {
+                   rcvend = TRUE;
+                   res = NULL;
+               }
                break;
            case 'S': /* parameter status */
                getParameterValues(conn);
 
 #ifndef __VERSION_H__
 #define __VERSION_H__
 
-#define POSTGRESDRIVERVERSION      "08.04.0200"
-#define POSTGRES_RESOURCE_VERSION  "08.04.0200\0"
-#define PG_DRVFILE_VERSION     8,4,02,00
-#define PG_BUILD_VERSION       "200912260001"
+#define POSTGRESDRIVERVERSION      "08.04.0201"
+#define POSTGRES_RESOURCE_VERSION  "08.04.0201\0"
+#define PG_DRVFILE_VERSION     8,4,02,01
+#define PG_BUILD_VERSION       "201001130001"
 
 #endif