mirror of
https://github.com/mdbtools/mdbtools.git
synced 2025-04-05 12:55:27 +08:00
581 lines
14 KiB
C
581 lines
14 KiB
C
/* MDB Tools - A library for reading MS Access database file
|
|
* Copyright (C) 2000-2004 Brian Bruns
|
|
*
|
|
* portions based on FreeTDS, Copyright (C) 1998-1999 Brian Bruns
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
|
|
#include "connectparams.h"
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
/*
|
|
* * Last resort place to check for INI file. This is usually set at compile time
|
|
* * by build scripts.
|
|
* */
|
|
#ifndef SYS_ODBC_INI
|
|
#define SYS_ODBC_INI "/etc/odbc.ini"
|
|
#endif
|
|
|
|
#if defined(FILENAME_MAX) && FILENAME_MAX < 512
|
|
#undef FILENAME_MAX
|
|
#define FILENAME_MAX 512
|
|
#endif
|
|
|
|
#define max_line 256
|
|
static char line[max_line];
|
|
|
|
static guint HashFunction (gconstpointer key);
|
|
#if !HAVE_SQLGETPRIVATEPROFILESTRING
|
|
static GString* GetIniFileName ();
|
|
static int FileExists (const gchar* name);
|
|
static int FindSection (FILE* stream, const char* section);
|
|
static int GetNextItem (FILE* stream, char** name, char** value);
|
|
#endif //!HAVE_SQLGETPRIVATEPROFILESTRING
|
|
|
|
static void visit (gpointer key, gpointer value, gpointer user_data);
|
|
static gboolean cleanup (gpointer key, gpointer value, gpointer user_data);
|
|
|
|
/*
|
|
* Allocate create a ConnectParams object
|
|
*/
|
|
|
|
ConnectParams* NewConnectParams ()
|
|
{
|
|
ConnectParams* params = (ConnectParams *) g_malloc(sizeof (ConnectParams));
|
|
if (!params)
|
|
return params;
|
|
|
|
params->dsnName = g_string_new ("");
|
|
params->iniFileName = NULL;
|
|
params->table = g_hash_table_new (HashFunction, g_str_equal);
|
|
|
|
return params;
|
|
}
|
|
|
|
/*
|
|
* Destroy a ConnectParams object
|
|
*/
|
|
|
|
void FreeConnectParams (ConnectParams* params)
|
|
{
|
|
if (params)
|
|
{
|
|
if (params->dsnName)
|
|
g_string_free (params->dsnName, TRUE);
|
|
if (params->iniFileName)
|
|
g_string_free (params->iniFileName, TRUE);
|
|
if (params->table)
|
|
{
|
|
g_hash_table_foreach_remove (params->table, cleanup, NULL);
|
|
g_hash_table_destroy (params->table);
|
|
}
|
|
g_free(params);
|
|
}
|
|
}
|
|
|
|
#if !HAVE_SQLGETPRIVATEPROFILESTRING
|
|
static int LoadDSN (
|
|
const gchar* iniFileName, const gchar* dsnName, GHashTable* table)
|
|
{
|
|
FILE* stream;
|
|
gchar* name;
|
|
gchar* value;
|
|
|
|
|
|
if ((stream = fopen (iniFileName, "r" )) != NULL )
|
|
{
|
|
if (!FindSection (stream, dsnName))
|
|
{
|
|
g_printerr ("Couldn't find DSN %s in %s\n", dsnName, iniFileName);
|
|
fclose (stream);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
while (GetNextItem (stream, &name, &value))
|
|
{
|
|
g_hash_table_insert (table, strdup (name), strdup (value));
|
|
}
|
|
}
|
|
|
|
fclose( stream );
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Find the settings for the specified ODBC DSN
|
|
*/
|
|
gboolean LookupDSN (ConnectParams* params, const gchar* dsnName)
|
|
{
|
|
if (!params) {
|
|
fprintf(stderr,"LookupDSN: no parameters, returning FALSE\n");
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* Set the DSN name property
|
|
*/
|
|
/* params->dsnName = g_string_assign (params->dsnName, dsnName); */
|
|
/*
|
|
* Search for the ODBC ini file
|
|
*/
|
|
if (!(params->iniFileName = GetIniFileName ())) {
|
|
fprintf(stderr,"LookupDSN: GetIniFileName returned FALSE\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!LoadDSN (params->iniFileName->str, dsnName, params->table)) {
|
|
fprintf(stderr,"LookupDSN: LoadDSN returned FALSE\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/*
|
|
* Get the value of a given ODBC Connection Parameter
|
|
*/
|
|
|
|
gchar* GetConnectParam (ConnectParams* params, const gchar* paramName)
|
|
{
|
|
if (!params || !params->table)
|
|
return NULL;
|
|
|
|
return g_hash_table_lookup (params->table, paramName);
|
|
}
|
|
|
|
#else
|
|
gboolean LookupDSN (ConnectParams* params, const gchar* dsnName)
|
|
{
|
|
return TRUE;
|
|
}
|
|
gchar* GetConnectParam (ConnectParams* params, const gchar* paramName)
|
|
{
|
|
static char tmp[FILENAME_MAX];
|
|
|
|
/* use old servername */
|
|
tmp[0] = '\0';
|
|
if (SQLGetPrivateProfileString(params->dsnName->str, paramName, "", tmp, FILENAME_MAX, "odbc.ini") > 0) {
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
#endif /* !HAVE_SQLGETPRIVATEPROFILESTRING */
|
|
|
|
/*
|
|
* Apply a connection string to the ODBC Parameter Settings
|
|
*/
|
|
|
|
void SetConnectString (ConnectParams* params, const gchar* connectString)
|
|
{
|
|
int end;
|
|
char *cs, *s, *p, *name, *value;
|
|
gpointer key;
|
|
gpointer oldvalue;
|
|
|
|
if (!params)
|
|
return;
|
|
/*
|
|
* Make a copy of the connection string so we can modify it
|
|
*/
|
|
cs = (char *) g_strdup(connectString);
|
|
s = cs;
|
|
/*
|
|
* Loop over ';' seperated name=value pairs
|
|
*/
|
|
p = strchr (s, '=');
|
|
while (p)
|
|
{
|
|
if (p) *p = '\0';
|
|
/*
|
|
* Extract name
|
|
*/
|
|
name = s;
|
|
if (p) s = p + 1;
|
|
/*
|
|
* Extract value
|
|
*/
|
|
p = strchr (s, ';');
|
|
if (p) *p = '\0';
|
|
value = s;
|
|
if (p) s = p + 1;
|
|
/*
|
|
* remove trailing spaces from name
|
|
*/
|
|
end = strlen (name) - 1;
|
|
while (end > 0 && isspace(name[end]))
|
|
name[end--] = '\0';
|
|
/*
|
|
* remove leading spaces from value
|
|
*/
|
|
while (isspace(*value))
|
|
value++;
|
|
|
|
if (g_hash_table_lookup_extended (params->table, name, &key, &oldvalue))
|
|
{
|
|
/*
|
|
* remove previous value
|
|
*/
|
|
g_hash_table_remove (params->table, key);
|
|
/*
|
|
* cleanup strings
|
|
*/
|
|
g_free (key);
|
|
g_free (oldvalue);
|
|
}
|
|
/*
|
|
* Insert the name/value pair into the hash table.
|
|
*
|
|
* Note that these g_strdup allocations are freed in cleanup,
|
|
* which is called by FreeConnectParams.
|
|
*/
|
|
g_hash_table_insert (params->table, g_strdup (name), g_strdup (value));
|
|
|
|
p = strchr (s, '=');
|
|
}
|
|
|
|
g_free (cs);
|
|
}
|
|
|
|
/*
|
|
* Dump all the ODBC Connection Paramters to a file (e.g. stdout)
|
|
*/
|
|
|
|
void DumpParams (ConnectParams* params, FILE* output)
|
|
{
|
|
if (!params)
|
|
{
|
|
g_printerr ("NULL ConnectionParams pointer\n");
|
|
return;
|
|
}
|
|
|
|
if (params->dsnName)
|
|
g_printerr ("Parameter values for DSN: %s\n", params->dsnName->str);
|
|
|
|
if (params->iniFileName)
|
|
g_printerr ("Ini File is %s\n", params->iniFileName->str);
|
|
|
|
g_hash_table_foreach (params->table, visit, output);
|
|
}
|
|
|
|
/*
|
|
* Return the value of the DSN from the conneciton string
|
|
*/
|
|
|
|
gchar* ExtractDSN (ConnectParams* params, const gchar* connectString)
|
|
{
|
|
char *p, *q, *s;
|
|
|
|
if (!params)
|
|
return NULL;
|
|
/*
|
|
* Position ourselves to the beginning of "DSN"
|
|
*/
|
|
p = strstr (connectString, "DSN");
|
|
if (!p) return NULL;
|
|
/*
|
|
* Position ourselves to the "="
|
|
*/
|
|
q = strchr (p, '=');
|
|
if (!q) return NULL;
|
|
/*
|
|
* Skip over any leading spaces
|
|
*/
|
|
q++;
|
|
while (isspace(*q))
|
|
q++;
|
|
/*
|
|
* Copy the DSN value to a buffer
|
|
*/
|
|
s = line;
|
|
while (*q && *q != ';')
|
|
*s++ = *q++;
|
|
*s = '\0';
|
|
/*
|
|
* Save it as a string in the params object
|
|
*/
|
|
params->dsnName = g_string_assign (params->dsnName, line);
|
|
|
|
return params->dsnName->str;
|
|
}
|
|
|
|
gchar* ExtractDBQ (ConnectParams* params, const gchar* connectString)
|
|
{
|
|
char *p, *q, *s;
|
|
|
|
if (!params)
|
|
return NULL;
|
|
/*
|
|
* Position ourselves to the beginning of "DSN"
|
|
*/
|
|
p = strstr (connectString, "DBQ");
|
|
if (!p) return NULL;
|
|
/*
|
|
* Position ourselves to the "="
|
|
*/
|
|
q = strchr (p, '=');
|
|
if (!q) return NULL;
|
|
/*
|
|
* Skip over any leading spaces
|
|
*/
|
|
q++;
|
|
while (isspace(*q))
|
|
q++;
|
|
/*
|
|
* Copy the DSN value to a buffer
|
|
*/
|
|
s = line;
|
|
while (*q && *q != ';')
|
|
*s++ = *q++;
|
|
*s = '\0';
|
|
/*
|
|
* Save it as a string in the params object
|
|
*/
|
|
params->dsnName = g_string_assign (params->dsnName, line);
|
|
|
|
return params->dsnName->str;
|
|
}
|
|
|
|
/*
|
|
* Begin local function definitions
|
|
*/
|
|
|
|
/*
|
|
* Make a hash from all the characters
|
|
*/
|
|
static guint HashFunction (gconstpointer key)
|
|
{
|
|
guint value = 0;
|
|
const char* s = key;
|
|
|
|
while (*s) value += *s++;
|
|
|
|
return value;
|
|
}
|
|
|
|
#if !HAVE_SQLGETPRIVATEPROFILESTRING
|
|
static GString* GetIniFileName ()
|
|
{
|
|
char* setting;
|
|
GString* iniFileName = g_string_new ("");
|
|
/*
|
|
* First, try the ODBCINI environment variable
|
|
*/
|
|
if ((setting = getenv ("ODBCINI")) != NULL)
|
|
{
|
|
g_string_assign (iniFileName, getenv ("ODBCINI"));
|
|
|
|
if (FileExists (iniFileName->str))
|
|
return iniFileName;
|
|
|
|
g_string_assign (iniFileName, "");
|
|
}
|
|
/*
|
|
* Second, try the HOME environment variable
|
|
*/
|
|
if ((setting = getenv ("HOME")) != NULL)
|
|
{
|
|
g_string_assign (iniFileName, setting);
|
|
iniFileName = g_string_append (iniFileName, "/.odbc.ini");
|
|
|
|
if (FileExists (iniFileName->str))
|
|
return iniFileName;
|
|
|
|
g_string_assign (iniFileName, "");
|
|
}
|
|
/*
|
|
* As a last resort, try SYS_ODBC_INI
|
|
*/
|
|
g_string_assign (iniFileName, SYS_ODBC_INI);
|
|
if (FileExists (iniFileName->str))
|
|
return iniFileName;
|
|
|
|
g_string_assign (iniFileName, "");
|
|
|
|
return iniFileName;
|
|
}
|
|
|
|
static int FileExists (const gchar* name)
|
|
{
|
|
struct stat fileStat;
|
|
|
|
return (stat (name, &fileStat) == 0);
|
|
}
|
|
|
|
static int FindSection (FILE* stream, const char* section)
|
|
{
|
|
char* s;
|
|
char sectionPattern[max_line];
|
|
int len;
|
|
|
|
strcpy (sectionPattern, "[");
|
|
strcat (sectionPattern, section);
|
|
strcat (sectionPattern, "]");
|
|
|
|
s = fgets (line, max_line, stream);
|
|
while (s != NULL)
|
|
{
|
|
/*
|
|
* Get rid of the newline character
|
|
*/
|
|
len = strlen (line);
|
|
if (len > 0) line[strlen (line) - 1] = '\0';
|
|
/*
|
|
* look for the section header
|
|
*/
|
|
if (strcmp (line, sectionPattern) == 0)
|
|
return 1;
|
|
|
|
s = fgets (line, max_line, stream);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int GetNextItem (FILE* stream, char** name, char** value)
|
|
{
|
|
char* s;
|
|
int len;
|
|
char equals[] = "="; /* used for seperator for strtok */
|
|
char* token;
|
|
|
|
if (name == NULL || value == NULL)
|
|
{
|
|
g_printerr ("GetNextItem, invalid parameters");
|
|
return 0;
|
|
}
|
|
|
|
s = fgets (line, max_line, stream);
|
|
if (s == NULL)
|
|
{
|
|
//perror ("fgets");
|
|
return 0;
|
|
}
|
|
/*
|
|
* Get rid of the newline character
|
|
*/
|
|
len = strlen (line);
|
|
if (len > 0) line[strlen (line) - 1] = '\0';
|
|
/*
|
|
* Extract name from name = value
|
|
*/
|
|
if ((token = strtok (line, equals)) == NULL) return 0;
|
|
|
|
len = strlen (token);
|
|
while (len > 0 && isspace(token[len-1]))
|
|
{
|
|
len--;
|
|
token[len] = '\0';
|
|
}
|
|
*name = token;
|
|
/*
|
|
* extract value from name = value
|
|
*/
|
|
token = strtok (NULL, equals);
|
|
if (token == NULL) return 0;
|
|
while (*token && isspace(token[0]))
|
|
token++;
|
|
|
|
*value = token;
|
|
|
|
return 1;
|
|
}
|
|
#endif //!HAVE_SQLGETPRIVATEPROFILESTRING
|
|
|
|
static void visit (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
FILE* output = (FILE*) user_data;
|
|
fprintf(output, "Parameter: %s, Value: %s\n", (char*)key, (char*)value);
|
|
}
|
|
|
|
static gboolean cleanup (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
g_free (key);
|
|
g_free (value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef UNIXODBC
|
|
|
|
/*
|
|
* Begin BIG Hack.
|
|
*
|
|
* We need these from odbcinstext.h but it wants to
|
|
* include <log.h> and <ini.h>, which are not in the
|
|
* standard include path. XXX smurph
|
|
* confirmed by unixODBC stuff, odbcinstext.h shouldn't be installed. freddy77
|
|
*/
|
|
#define INI_MAX_LINE 1000
|
|
#define INI_MAX_OBJECT_NAME INI_MAX_LINE
|
|
#define INI_MAX_PROPERTY_NAME INI_MAX_LINE
|
|
#define INI_MAX_PROPERTY_VALUE INI_MAX_LINE
|
|
|
|
#define ODBCINST_PROMPTTYPE_LABEL 0 /* readonly */
|
|
#define ODBCINST_PROMPTTYPE_TEXTEDIT 1
|
|
#define ODBCINST_PROMPTTYPE_LISTBOX 2
|
|
#define ODBCINST_PROMPTTYPE_COMBOBOX 3
|
|
#define ODBCINST_PROMPTTYPE_FILENAME 4
|
|
#define ODBCINST_PROMPTTYPE_HIDDEN 5
|
|
#define ODBCINST_PROMPTTYPE_TEXTEDIT_PASSWORD 6
|
|
|
|
typedef struct tODBCINSTPROPERTY
|
|
{
|
|
struct tODBCINSTPROPERTY *pNext; /* pointer to next property, NULL if last property */
|
|
|
|
char szName[INI_MAX_PROPERTY_NAME + 1]; /* property name */
|
|
char szValue[INI_MAX_PROPERTY_VALUE + 1]; /* property value */
|
|
int nPromptType; /* PROMPTTYPE_TEXTEDIT, PROMPTTYPE_LISTBOX, PROMPTTYPE_COMBOBOX, PROMPTTYPE_FILENAME */
|
|
char **aPromptData; /* array of pointers terminated with a NULL value in array. */
|
|
char *pszHelp; /* help on this property (driver setups should keep it short) */
|
|
void *pWidget; /* CALLER CAN STORE A POINTER TO ? HERE */
|
|
int bRefresh; /* app should refresh widget ie Driver Setup has changed aPromptData or szValue */
|
|
void *hDLL; /* for odbcinst internal use... only first property has valid one */
|
|
}
|
|
ODBCINSTPROPERTY, *HODBCINSTPROPERTY;
|
|
|
|
/*
|
|
* End BIG Hack.
|
|
*/
|
|
|
|
int
|
|
ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty)
|
|
{
|
|
hLastProperty->pNext = (HODBCINSTPROPERTY) malloc(sizeof(ODBCINSTPROPERTY));
|
|
hLastProperty = hLastProperty->pNext;
|
|
memset(hLastProperty, 0, sizeof(ODBCINSTPROPERTY));
|
|
hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_FILENAME;
|
|
strncpy(hLastProperty->szName, "Database", INI_MAX_PROPERTY_NAME);
|
|
strncpy(hLastProperty->szValue, "", INI_MAX_PROPERTY_VALUE);
|
|
hLastProperty->pszHelp = (char *) strdup("Filename and Path of MDB file to connect to.\n"
|
|
"Use the full path to the database file.");
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif
|