Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 : * Copyright (C) 2005-2011 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <stdarg.h>
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <ctype.h>
28 : #include <sys/stat.h>
29 :
30 : #include <freetds/odbc.h>
31 : #include <freetds/utils/string.h>
32 : #include <freetds/utils.h>
33 : #include <freetds/replacements.h>
34 :
35 : #define ODBC_PARAM(p) static const char odbc_param_##p[] = #p;
36 : ODBC_PARAM_LIST
37 : #undef ODBC_PARAM
38 :
39 : #ifdef _WIN32
40 : #define ODBC_PARAM(p) odbc_param_##p,
41 : static const char *odbc_param_names[] = {
42 : ODBC_PARAM_LIST
43 : };
44 : #undef ODBC_PARAM
45 : #endif
46 :
47 : #if !HAVE_SQLGETPRIVATEPROFILESTRING
48 :
49 : /*
50 : * Last resort place to check for INI file. This is usually set at compile time
51 : * by build scripts.
52 : */
53 : #ifndef SYS_ODBC_INI
54 : #define SYS_ODBC_INI "/etc/odbc.ini"
55 : #endif
56 :
57 : /**
58 : * Call this to get the INI file containing Data Source Names.
59 : * @note rules for determining the location of ODBC config may be different
60 : * then what you expect - at this time they differ from unixODBC
61 : *
62 : * @return file opened or NULL if error
63 : * @retval 1 worked
64 : */
65 : static FILE *tdoGetIniFileName(void);
66 :
67 : /* avoid name collision with system headers */
68 : #define SQLGetPrivateProfileString tds_SQLGetPrivateProfileString
69 :
70 : /**
71 : * SQLGetPrivateProfileString
72 : *
73 : * PURPOSE
74 : *
75 : * This is an implementation of a common MS API call. This implementation
76 : * should only be used if the ODBC sub-system/SDK does not have it.
77 : * For example; unixODBC has its own so those using unixODBC should NOT be
78 : * using this implementation because unixODBC;
79 : * - provides caching of ODBC config data
80 : * - provides consistent interpretation of ODBC config data (i.e, location)
81 : *
82 : * ARGS
83 : *
84 : * see ODBC documentation
85 : *
86 : * RETURNS
87 : *
88 : * see ODBC documentation
89 : *
90 : * NOTES:
91 : *
92 : * - the spec is not entirely implemented... consider this a lite version
93 : * - rules for determining the location of ODBC config may be different then what you
94 : * expect see tdoGetIniFileName().
95 : *
96 : */
97 : static int SQLGetPrivateProfileString(LPCSTR pszSection, LPCSTR pszEntry, LPCSTR pszDefault, LPSTR pRetBuffer, int nRetBuffer,
98 : LPCSTR pszFileName);
99 : #endif
100 :
101 : #if defined(FILENAME_MAX) && FILENAME_MAX < 512
102 : #undef FILENAME_MAX
103 : #define FILENAME_MAX 512
104 : #endif
105 :
106 : static int
107 64 : parse_server(TDS_ERRS *errs, char *server, TDSLOGIN * login)
108 : {
109 64 : char *p = (char *) strchr(server, '\\');
110 :
111 64 : if (p) {
112 0 : if (!tds_dstr_copy(&login->instance_name, p+1)) {
113 0 : odbc_errs_add(errs, "HY001", NULL);
114 0 : return 0;
115 : }
116 0 : *p = 0;
117 : } else {
118 64 : p = (char *) strchr(server, ',');
119 64 : if (p && atoi(p+1) > 0) {
120 0 : login->port = atoi(p+1);
121 0 : *p = 0;
122 : }
123 : }
124 :
125 64 : if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs)))
126 64 : if (!tds_dstr_copy(&login->server_host_name, server)) {
127 0 : odbc_errs_add(errs, "HY001", NULL);
128 0 : return 0;
129 : }
130 :
131 : return 1;
132 : }
133 :
134 : static int
135 : myGetPrivateProfileString(const char *DSN, const char *key, char *buf)
136 : {
137 18044 : buf[0] = '\0';
138 18044 : return SQLGetPrivateProfileString(DSN, key, "", buf, FILENAME_MAX, "odbc.ini");
139 : }
140 :
141 : /**
142 : * Read connection information from given DSN
143 : * @param DSN DSN name
144 : * @param login where to store connection info
145 : * @return 1 if success 0 otherwhise
146 : */
147 : int
148 860 : odbc_get_dsn_info(TDS_ERRS *errs, const char *DSN, TDSLOGIN * login)
149 : {
150 : char tmp[FILENAME_MAX];
151 860 : int freetds_conf_less = 1;
152 :
153 : /* use old servername */
154 860 : if (myGetPrivateProfileString(DSN, odbc_param_Servername, tmp) > 0) {
155 860 : freetds_conf_less = 0;
156 860 : if (!tds_dstr_copy(&login->server_name, tmp)) {
157 0 : odbc_errs_add(errs, "HY001", NULL);
158 0 : return 0;
159 : }
160 860 : tds_read_conf_file(login, tmp);
161 860 : if (myGetPrivateProfileString(DSN, odbc_param_Server, tmp) > 0) {
162 0 : odbc_errs_add(errs, "HY000", "You cannot specify both SERVERNAME and SERVER");
163 0 : return 0;
164 : }
165 860 : if (myGetPrivateProfileString(DSN, odbc_param_Address, tmp) > 0) {
166 0 : odbc_errs_add(errs, "HY000", "You cannot specify both SERVERNAME and ADDRESS");
167 0 : return 0;
168 : }
169 : }
170 :
171 : /* search for server (compatible with ms one) */
172 : if (freetds_conf_less) {
173 0 : int address_specified = 0;
174 :
175 0 : if (myGetPrivateProfileString(DSN, odbc_param_Address, tmp) > 0) {
176 0 : address_specified = 1;
177 : /* TODO parse like MS */
178 :
179 0 : if (TDS_FAILED(tds_lookup_host_set(tmp, &login->ip_addrs))) {
180 0 : odbc_errs_add(errs, "HY000", "Error parsing ADDRESS attribute");
181 0 : return 0;
182 : }
183 : }
184 0 : if (myGetPrivateProfileString(DSN, odbc_param_Server, tmp) > 0) {
185 0 : if (!tds_dstr_copy(&login->server_name, tmp)) {
186 0 : odbc_errs_add(errs, "HY001", NULL);
187 0 : return 0;
188 : }
189 0 : if (!address_specified) {
190 0 : if (!parse_server(errs, tmp, login))
191 : return 0;
192 : }
193 : }
194 : }
195 :
196 860 : if (myGetPrivateProfileString(DSN, odbc_param_Port, tmp) > 0)
197 0 : tds_parse_conf_section(TDS_STR_PORT, tmp, login);
198 :
199 860 : if (myGetPrivateProfileString(DSN, odbc_param_TDS_Version, tmp) > 0)
200 0 : tds_parse_conf_section(TDS_STR_VERSION, tmp, login);
201 :
202 860 : if (myGetPrivateProfileString(DSN, odbc_param_Language, tmp) > 0)
203 0 : tds_parse_conf_section(TDS_STR_LANGUAGE, tmp, login);
204 :
205 1720 : if (tds_dstr_isempty(&login->database)
206 844 : && myGetPrivateProfileString(DSN, odbc_param_Database, tmp) > 0)
207 844 : if (!tds_dstr_copy(&login->database, tmp)) {
208 0 : odbc_errs_add(errs, "HY001", NULL);
209 0 : return 0;
210 : }
211 :
212 860 : if (myGetPrivateProfileString(DSN, odbc_param_TextSize, tmp) > 0)
213 0 : tds_parse_conf_section(TDS_STR_TEXTSZ, tmp, login);
214 :
215 860 : if (myGetPrivateProfileString(DSN, odbc_param_PacketSize, tmp) > 0)
216 0 : tds_parse_conf_section(TDS_STR_BLKSZ, tmp, login);
217 :
218 860 : if (myGetPrivateProfileString(DSN, odbc_param_ClientCharset, tmp) > 0)
219 0 : tds_parse_conf_section(TDS_STR_CLCHARSET, tmp, login);
220 :
221 860 : if (myGetPrivateProfileString(DSN, odbc_param_DumpFile, tmp) > 0)
222 0 : tds_parse_conf_section(TDS_STR_DUMPFILE, tmp, login);
223 :
224 860 : if (myGetPrivateProfileString(DSN, odbc_param_DumpFileAppend, tmp) > 0)
225 0 : tds_parse_conf_section(TDS_STR_APPENDMODE, tmp, login);
226 :
227 860 : if (myGetPrivateProfileString(DSN, odbc_param_DebugFlags, tmp) > 0)
228 0 : tds_parse_conf_section(TDS_STR_DEBUGFLAGS, tmp, login);
229 :
230 860 : if (myGetPrivateProfileString(DSN, odbc_param_Encryption, tmp) > 0)
231 0 : tds_parse_conf_section(TDS_STR_ENCRYPTION, tmp, login);
232 :
233 860 : if (myGetPrivateProfileString(DSN, odbc_param_UseNTLMv2, tmp) > 0)
234 0 : tds_parse_conf_section(TDS_STR_USENTLMV2, tmp, login);
235 :
236 860 : if (myGetPrivateProfileString(DSN, odbc_param_REALM, tmp) > 0)
237 0 : tds_parse_conf_section(TDS_STR_REALM, tmp, login);
238 :
239 860 : if (myGetPrivateProfileString(DSN, odbc_param_ServerSPN, tmp) > 0)
240 0 : tds_parse_conf_section(TDS_STR_SPN, tmp, login);
241 :
242 860 : if (myGetPrivateProfileString(DSN, odbc_param_Trusted_Connection, tmp) > 0 && tds_config_boolean(odbc_param_Trusted_Connection, tmp, login)) {
243 0 : tds_dstr_empty(&login->user_name);
244 0 : tds_dstr_empty(&login->password);
245 : }
246 :
247 860 : if (myGetPrivateProfileString(DSN, odbc_param_MARS_Connection, tmp) > 0 && tds_config_boolean(odbc_param_MARS_Connection, tmp, login)) {
248 0 : login->mars = 1;
249 : }
250 :
251 860 : if (myGetPrivateProfileString(DSN, odbc_param_AttachDbFilename, tmp) > 0)
252 0 : tds_parse_conf_section(TDS_STR_DBFILENAME, tmp, login);
253 :
254 860 : if (myGetPrivateProfileString(DSN, odbc_param_Timeout, tmp) > 0)
255 0 : tds_parse_conf_section(TDS_STR_TIMEOUT, tmp, login);
256 :
257 : return 1;
258 : }
259 :
260 : /**
261 : * Swap two DSTR
262 : */
263 : static void
264 : odbc_dstr_swap(DSTR *a, DSTR *b)
265 : {
266 504 : DSTR tmp = *a;
267 504 : *a = *b;
268 504 : *b = tmp;
269 : }
270 :
271 : static const char *
272 916 : parse_value(TDS_ERRS *errs, const char *p, const char *connect_string_end, DSTR *value)
273 : {
274 : const char *end;
275 : char *dst;
276 :
277 : /* easy case, just ';' terminated */
278 916 : if (p == connect_string_end || *p != '{') {
279 676 : end = (const char *) memchr(p, ';', connect_string_end - p);
280 676 : if (!end)
281 6 : end = connect_string_end;
282 676 : if (!tds_dstr_copyn(value, p, end - p)) {
283 0 : odbc_errs_add(errs, "HY001", NULL);
284 0 : return NULL;
285 : }
286 : return end;
287 : }
288 :
289 240 : ++p;
290 : /* search "};" */
291 240 : end = p;
292 : for (;;) {
293 : /* search next '}' */
294 288 : end = (const char *) memchr(end, '}', connect_string_end - end);
295 264 : if (end == NULL) {
296 8 : odbc_errs_add(errs, "HY000", "Syntax error in connection string");
297 8 : return NULL;
298 : }
299 256 : end++;
300 :
301 : /* termination ? */
302 256 : if (end == connect_string_end || end[0] == ';') {
303 232 : end--;
304 : break;
305 : }
306 :
307 : /* wrong syntax ? */
308 24 : if (end[0] != '}') {
309 0 : odbc_errs_add(errs, "HY000", "Syntax error in connection string");
310 0 : return NULL;
311 : }
312 24 : end++;
313 : }
314 232 : if (!tds_dstr_alloc(value, end - p)) {
315 0 : odbc_errs_add(errs, "HY001", NULL);
316 0 : return NULL;
317 : }
318 464 : dst = tds_dstr_buf(value);
319 2384 : for (; p < end; ++p) {
320 2152 : char ch = *p;
321 2152 : *dst++ = ch;
322 2152 : if (ch == '}')
323 24 : ++p;
324 : }
325 464 : tds_dstr_setlen(value, dst - tds_dstr_buf(value));
326 :
327 232 : return end + 1;
328 : }
329 :
330 : /**
331 : * Parse connection string and fill login according
332 : * @param connect_string connect string
333 : * @param connect_string_end connect string end (pointer to char past last)
334 : * @param login where to store connection info
335 : * @return true if success false otherwise
336 : */
337 : bool
338 150 : odbc_parse_connect_string(TDS_ERRS *errs, const char *connect_string, const char *connect_string_end, TDSLOGIN * login,
339 : TDS_PARSED_PARAM *parsed_params)
340 : {
341 : const char *p, *end;
342 150 : DSTR *dest_s, value = DSTR_INITIALIZER;
343 : enum { CFG_DSN = 1, CFG_SERVER = 2, CFG_SERVERNAME = 4 };
344 150 : unsigned int cfgs = 0; /* flags for indicate second parse of string */
345 : char option[24];
346 150 : int trusted = 0;
347 :
348 150 : if (parsed_params)
349 150 : memset(parsed_params, 0, sizeof(*parsed_params)*ODBC_PARAM_SIZE);
350 :
351 1044 : for (p = connect_string; p < connect_string_end && *p;) {
352 : int num_param = -1;
353 :
354 : dest_s = NULL;
355 :
356 : /* handle empty options */
357 916 : while (p < connect_string_end && *p == ';')
358 0 : ++p;
359 :
360 : /* parse option */
361 916 : end = (const char *) memchr(p, '=', connect_string_end - p);
362 916 : if (!end)
363 : break;
364 :
365 : /* account for spaces between ;'s. */
366 916 : while (p < end && *p == ' ')
367 0 : ++p;
368 :
369 916 : if ((end - p) >= (int) sizeof(option))
370 0 : option[0] = 0;
371 : else {
372 916 : memcpy(option, p, end - p);
373 916 : option[end - p] = 0;
374 : }
375 :
376 : /* parse value */
377 916 : p = end + 1;
378 916 : end = parse_value(errs, p, connect_string_end, &value);
379 916 : if (!end)
380 : goto Cleanup;
381 :
382 : #define CHK_PARAM(p) (strcasecmp(option, odbc_param_##p) == 0 && (num_param=ODBC_PARAM_##p) >= 0)
383 908 : if (CHK_PARAM(Server)) {
384 : /* error if servername or DSN specified */
385 64 : if ((cfgs & (CFG_DSN|CFG_SERVERNAME)) != 0) {
386 0 : odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified");
387 0 : goto Cleanup;
388 : }
389 64 : if (!cfgs) {
390 64 : dest_s = &login->server_name;
391 128 : if (!parse_server(errs, tds_dstr_buf(&value), login))
392 : goto Cleanup;
393 : cfgs = CFG_SERVER;
394 : }
395 844 : } else if (CHK_PARAM(Servername)) {
396 32 : if ((cfgs & (CFG_DSN|CFG_SERVER)) != 0) {
397 0 : odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified");
398 0 : goto Cleanup;
399 : }
400 32 : if (!cfgs) {
401 32 : odbc_dstr_swap(&login->server_name, &value);
402 32 : tds_read_conf_file(login, tds_dstr_cstr(&login->server_name));
403 16 : cfgs = CFG_SERVERNAME;
404 16 : p = connect_string;
405 16 : continue;
406 : }
407 812 : } else if (CHK_PARAM(DSN)) {
408 140 : if ((cfgs & (CFG_SERVER|CFG_SERVERNAME)) != 0) {
409 0 : odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified");
410 0 : goto Cleanup;
411 : }
412 140 : if (!cfgs) {
413 140 : if (!odbc_get_dsn_info(errs, tds_dstr_cstr(&value), login))
414 : goto Cleanup;
415 70 : cfgs = CFG_DSN;
416 70 : p = connect_string;
417 70 : continue;
418 : }
419 672 : } else if (CHK_PARAM(Database)) {
420 134 : dest_s = &login->database;
421 538 : } else if (CHK_PARAM(UID)) {
422 142 : dest_s = &login->user_name;
423 396 : } else if (CHK_PARAM(PWD)) {
424 142 : dest_s = &login->password;
425 254 : } else if (CHK_PARAM(APP)) {
426 6 : dest_s = &login->app_name;
427 248 : } else if (CHK_PARAM(WSID)) {
428 0 : dest_s = &login->client_host_name;
429 248 : } else if (CHK_PARAM(Language)) {
430 0 : tds_parse_conf_section(TDS_STR_LANGUAGE, tds_dstr_cstr(&value), login);
431 248 : } else if (CHK_PARAM(Port)) {
432 128 : tds_parse_conf_section(TDS_STR_PORT, tds_dstr_cstr(&value), login);
433 184 : } else if (CHK_PARAM(TDS_Version)) {
434 16 : tds_parse_conf_section(TDS_STR_VERSION, tds_dstr_cstr(&value), login);
435 176 : } else if (CHK_PARAM(TextSize)) {
436 0 : tds_parse_conf_section(TDS_STR_TEXTSZ, tds_dstr_cstr(&value), login);
437 176 : } else if (CHK_PARAM(PacketSize)) {
438 0 : tds_parse_conf_section(TDS_STR_BLKSZ, tds_dstr_cstr(&value), login);
439 176 : } else if (CHK_PARAM(ClientCharset)
440 96 : || strcasecmp(option, "client_charset") == 0) {
441 80 : num_param = ODBC_PARAM_ClientCharset;
442 160 : tds_parse_conf_section(TDS_STR_CLCHARSET, tds_dstr_cstr(&value), login);
443 96 : } else if (CHK_PARAM(DumpFile)) {
444 0 : tds_parse_conf_section(TDS_STR_DUMPFILE, tds_dstr_cstr(&value), login);
445 96 : } else if (CHK_PARAM(DumpFileAppend)) {
446 0 : tds_parse_conf_section(TDS_STR_APPENDMODE, tds_dstr_cstr(&value), login);
447 96 : } else if (CHK_PARAM(DebugFlags)) {
448 0 : tds_parse_conf_section(TDS_STR_DEBUGFLAGS, tds_dstr_cstr(&value), login);
449 96 : } else if (CHK_PARAM(Encryption)) {
450 0 : tds_parse_conf_section(TDS_STR_ENCRYPTION, tds_dstr_cstr(&value), login);
451 96 : } else if (CHK_PARAM(UseNTLMv2)) {
452 0 : tds_parse_conf_section(TDS_STR_USENTLMV2, tds_dstr_cstr(&value), login);
453 96 : } else if (CHK_PARAM(REALM)) {
454 0 : tds_parse_conf_section(TDS_STR_REALM, tds_dstr_cstr(&value), login);
455 96 : } else if (CHK_PARAM(ServerSPN)) {
456 0 : tds_parse_conf_section(TDS_STR_SPN, tds_dstr_cstr(&value), login);
457 96 : } else if (CHK_PARAM(Trusted_Connection)) {
458 0 : trusted = tds_config_boolean(option, tds_dstr_cstr(&value), login);
459 0 : tdsdump_log(TDS_DBG_INFO1, "trusted %s -> %d\n", tds_dstr_cstr(&value), trusted);
460 : num_param = -1;
461 : /* TODO odbc_param_Address field */
462 96 : } else if (CHK_PARAM(MARS_Connection)) {
463 0 : if (tds_config_boolean(option, tds_dstr_cstr(&value), login))
464 0 : login->mars = 1;
465 96 : } else if (CHK_PARAM(AttachDbFilename)) {
466 0 : dest_s = &login->db_filename;
467 96 : } else if (CHK_PARAM(ApplicationIntent)) {
468 : const char *readonly_intent;
469 :
470 0 : if (strcasecmp(tds_dstr_cstr(&value), "ReadOnly") == 0) {
471 : readonly_intent = "yes";
472 0 : } else if (strcasecmp(tds_dstr_cstr(&value), "ReadWrite") == 0) {
473 : readonly_intent = "no";
474 : } else {
475 0 : tdsdump_log(TDS_DBG_ERROR, "Invalid ApplicationIntent %s\n", tds_dstr_cstr(&value));
476 : goto Cleanup;
477 : }
478 :
479 0 : tds_parse_conf_section(TDS_STR_READONLY_INTENT, readonly_intent, login);
480 0 : tdsdump_log(TDS_DBG_INFO1, "Application Intent %s\n", readonly_intent);
481 96 : } else if (CHK_PARAM(Timeout)) {
482 0 : tds_parse_conf_section(TDS_STR_TIMEOUT, tds_dstr_cstr(&value), login);
483 : }
484 :
485 822 : if (num_param >= 0 && parsed_params) {
486 726 : parsed_params[num_param].p = p;
487 726 : parsed_params[num_param].len = end - p;
488 : }
489 :
490 : /* copy to destination */
491 822 : if (dest_s)
492 : odbc_dstr_swap(dest_s, &value);
493 :
494 822 : p = end;
495 : /* handle "" ";.." cases */
496 822 : if (p >= connect_string_end)
497 : break;
498 808 : ++p;
499 : }
500 :
501 142 : if (trusted) {
502 0 : if (parsed_params) {
503 0 : parsed_params[ODBC_PARAM_Trusted_Connection].p = "Yes";
504 0 : parsed_params[ODBC_PARAM_Trusted_Connection].len = 3;
505 0 : parsed_params[ODBC_PARAM_UID].p = NULL;
506 0 : parsed_params[ODBC_PARAM_PWD].p = NULL;
507 : }
508 0 : tds_dstr_empty(&login->user_name);
509 0 : tds_dstr_empty(&login->password);
510 : }
511 :
512 142 : tds_dstr_free(&value);
513 142 : return true;
514 :
515 8 : Cleanup:
516 8 : tds_dstr_free(&value);
517 8 : return false;
518 : }
519 :
520 : #ifdef _WIN32
521 : int
522 : odbc_build_connect_string(TDS_ERRS *errs, TDS_PARSED_PARAM *params, char **out)
523 : {
524 : unsigned n;
525 : size_t len = 1;
526 : char *p;
527 :
528 : /* compute string size */
529 : for (n = 0; n < ODBC_PARAM_SIZE; ++n) {
530 : if (params[n].p)
531 : len += strlen(odbc_param_names[n]) + params[n].len + 2;
532 : }
533 :
534 : /* allocate */
535 : p = tds_new(char, len);
536 : if (!p) {
537 : odbc_errs_add(errs, "HY001", NULL);
538 : return 0;
539 : }
540 : *out = p;
541 :
542 : /* build it */
543 : for (n = 0; n < ODBC_PARAM_SIZE; ++n) {
544 : if (params[n].p)
545 : p += sprintf(p, "%s=%.*s;", odbc_param_names[n], (int) params[n].len, params[n].p);
546 : }
547 : *p = 0;
548 : return 1;
549 : }
550 : #endif
551 :
552 : #if !HAVE_SQLGETPRIVATEPROFILESTRING
553 :
554 : #ifdef _WIN32
555 : # error There is something wrong in configuration...
556 : #endif
557 :
558 : typedef struct
559 : {
560 : LPCSTR entry;
561 : LPSTR buffer;
562 : int buffer_len;
563 : int ret_val;
564 : int found;
565 : }
566 : ProfileParam;
567 :
568 : static void
569 54132 : tdoParseProfile(const char *option, const char *value, void *param)
570 : {
571 54132 : ProfileParam *p = (ProfileParam *) param;
572 :
573 54132 : if (strcasecmp(p->entry, option) == 0) {
574 1704 : strlcpy(p->buffer, value, p->buffer_len);
575 :
576 1704 : p->ret_val = strlen(p->buffer);
577 1704 : p->found = 1;
578 : }
579 54132 : }
580 :
581 : static int
582 18044 : SQLGetPrivateProfileString(LPCSTR pszSection, LPCSTR pszEntry, LPCSTR pszDefault, LPSTR pRetBuffer, int nRetBuffer,
583 : LPCSTR pszFileName)
584 : {
585 : FILE *hFile;
586 : ProfileParam param;
587 :
588 18044 : tdsdump_log(TDS_DBG_FUNC, "SQLGetPrivateProfileString(%p, %p, %p, %p, %d, %p)\n",
589 : pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer, pszFileName);
590 :
591 18044 : if (!pszSection) {
592 : /* spec says return list of all section names - but we will just return nothing */
593 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: Functionality for NULL pszSection not implemented.\n");
594 : return 0;
595 : }
596 :
597 18044 : if (!pszEntry) {
598 : /* spec says return list of all key names in section - but we will just return nothing */
599 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: Functionality for NULL pszEntry not implemented.\n");
600 : return 0;
601 : }
602 :
603 18044 : if (nRetBuffer < 1)
604 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: No space to return a value because nRetBuffer < 1.\n");
605 :
606 18044 : if (pszFileName && *pszFileName == '/')
607 0 : hFile = fopen(pszFileName, "r");
608 : else
609 18044 : hFile = tdoGetIniFileName();
610 :
611 18044 : if (hFile == NULL) {
612 0 : tdsdump_log(TDS_DBG_ERROR, "ERROR: Could not open configuration file\n");
613 : return 0;
614 : }
615 :
616 18044 : param.entry = pszEntry;
617 18044 : param.buffer = pRetBuffer;
618 18044 : param.buffer_len = nRetBuffer;
619 18044 : param.ret_val = 0;
620 18044 : param.found = 0;
621 :
622 18044 : pRetBuffer[0] = 0;
623 18044 : tds_read_conf_section(hFile, pszSection, tdoParseProfile, ¶m);
624 :
625 18044 : if (pszDefault && !param.found) {
626 16340 : strlcpy(pRetBuffer, pszDefault, nRetBuffer);
627 :
628 16340 : param.ret_val = strlen(pRetBuffer);
629 : }
630 :
631 18044 : fclose(hFile);
632 18044 : return param.ret_val;
633 : }
634 :
635 : static FILE *
636 18044 : tdoGetIniFileName()
637 : {
638 18044 : FILE *ret = NULL;
639 : char *p;
640 : char *fn;
641 :
642 : /*
643 : * First, try the ODBCINI environment variable
644 : */
645 18044 : if ((p = getenv("ODBCINI")) != NULL)
646 18044 : ret = fopen(p, "r");
647 :
648 : /*
649 : * Second, try the HOME environment variable
650 : */
651 18044 : if (!ret && (p = tds_get_homedir()) != NULL) {
652 0 : fn = NULL;
653 0 : if (asprintf(&fn, "%s/.odbc.ini", p) > 0) {
654 0 : ret = fopen(fn, "r");
655 0 : free(fn);
656 : }
657 0 : free(p);
658 : }
659 :
660 : /*
661 : * As a last resort, try SYS_ODBC_INI
662 : */
663 18044 : if (!ret)
664 0 : ret = fopen(SYS_ODBC_INI, "r");
665 :
666 18044 : return ret;
667 : }
668 :
669 : #endif /* !HAVE_SQLGETPRIVATEPROFILESTRING */
670 :
671 : #ifdef UNIXODBC
672 :
673 : /*
674 : * Begin BIG Hack.
675 : *
676 : * We need these from odbcinstext.h but it wants to
677 : * include <log.h> and <ini.h>, which are not in the
678 : * standard include path. XXX smurph
679 : * confirmed by unixODBC stuff, odbcinstext.h shouldn't be installed. freddy77
680 : */
681 : #define INI_MAX_LINE 1000
682 : #define INI_MAX_OBJECT_NAME INI_MAX_LINE
683 : #define INI_MAX_PROPERTY_NAME INI_MAX_LINE
684 : #define INI_MAX_PROPERTY_VALUE INI_MAX_LINE
685 :
686 : #define ODBCINST_PROMPTTYPE_LABEL 0 /* readonly */
687 : #define ODBCINST_PROMPTTYPE_TEXTEDIT 1
688 : #define ODBCINST_PROMPTTYPE_LISTBOX 2
689 : #define ODBCINST_PROMPTTYPE_COMBOBOX 3
690 : #define ODBCINST_PROMPTTYPE_FILENAME 4
691 : #define ODBCINST_PROMPTTYPE_HIDDEN 5
692 :
693 : typedef struct tODBCINSTPROPERTY
694 : {
695 : /** pointer to next property, NULL if last property */
696 : struct tODBCINSTPROPERTY *pNext;
697 :
698 : /** property name */
699 : char szName[INI_MAX_PROPERTY_NAME + 1];
700 :
701 : /** property value */
702 : char szValue[INI_MAX_PROPERTY_VALUE + 1];
703 :
704 : /** PROMPTTYPE_TEXTEDIT, PROMPTTYPE_LISTBOX, PROMPTTYPE_COMBOBOX, PROMPTTYPE_FILENAME */
705 : int nPromptType;
706 :
707 : /** array of pointers terminated with a NULL value in array */
708 : char **aPromptData;
709 :
710 : /** help on this property (driver setups should keep it short) */
711 : char *pszHelp;
712 :
713 : /** CALLER CAN STORE A POINTER TO ? HERE */
714 : void *pWidget;
715 :
716 : /** app should refresh widget ie Driver Setup has changed aPromptData or szValue */
717 : int bRefresh;
718 :
719 : /** for odbcinst internal use... only first property has valid one */
720 : void *hDLL;
721 : }
722 : ODBCINSTPROPERTY, *HODBCINSTPROPERTY;
723 :
724 : /*
725 : * End BIG Hack.
726 : */
727 :
728 : int ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty);
729 :
730 : static const char *const aTDSver[] = {
731 : "",
732 : "4.2",
733 : "5.0",
734 : "7.0",
735 : "7.1",
736 : "7.2",
737 : "7.3",
738 : "7.4",
739 : NULL
740 : };
741 :
742 : static const char *const aLanguage[] = {
743 : "us_english",
744 : NULL
745 : };
746 :
747 : static const char *const aEncryption[] = {
748 : TDS_STR_ENCRYPTION_OFF,
749 : TDS_STR_ENCRYPTION_REQUEST,
750 : TDS_STR_ENCRYPTION_REQUIRE,
751 : NULL
752 : };
753 :
754 : static const char *const aBoolean[] = {
755 : "Yes",
756 : "No",
757 : NULL
758 : };
759 :
760 : /*
761 : static const char *aAuth[] = {
762 : "Server",
763 : "Domain",
764 : "Both",
765 : NULL
766 : };
767 : */
768 :
769 : static HODBCINSTPROPERTY
770 : addProperty(HODBCINSTPROPERTY hLastProperty)
771 : {
772 : hLastProperty->pNext = (HODBCINSTPROPERTY) calloc(1, sizeof(ODBCINSTPROPERTY));
773 : hLastProperty = hLastProperty->pNext;
774 : return hLastProperty;
775 : }
776 :
777 : static HODBCINSTPROPERTY
778 : definePropertyString(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
779 : {
780 : hLastProperty = addProperty(hLastProperty);
781 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_TEXTEDIT;
782 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
783 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
784 : hLastProperty->pszHelp = (char *) strdup(comment);
785 : return hLastProperty;
786 : }
787 :
788 : static HODBCINSTPROPERTY
789 : definePropertyBoolean(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
790 : {
791 : hLastProperty = addProperty(hLastProperty);
792 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_LISTBOX;
793 : hLastProperty->aPromptData = malloc(sizeof(aBoolean));
794 : memcpy(hLastProperty->aPromptData, aBoolean, sizeof(aBoolean));
795 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
796 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
797 : hLastProperty->pszHelp = (char *) strdup(comment);
798 : return hLastProperty;
799 : }
800 :
801 : static HODBCINSTPROPERTY
802 : definePropertyHidden(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
803 : {
804 : hLastProperty = addProperty(hLastProperty);
805 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_HIDDEN;
806 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
807 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
808 : hLastProperty->pszHelp = (char *) strdup(comment);
809 : return hLastProperty;
810 : }
811 :
812 : static HODBCINSTPROPERTY
813 : definePropertyList(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const void *list, int size, const char *comment)
814 : {
815 : hLastProperty = addProperty(hLastProperty);
816 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_LISTBOX;
817 : hLastProperty->aPromptData = malloc(size);
818 : memcpy(hLastProperty->aPromptData, list, size);
819 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
820 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
821 : hLastProperty->pszHelp = (char *) strdup(comment);
822 : return hLastProperty;
823 : }
824 :
825 : int
826 : ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty)
827 : {
828 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Servername, "",
829 : "Name of FreeTDS connection to connect to.\n"
830 : "This server name refer to entry in freetds.conf file, not real server name.\n"
831 : "This property cannot be used with Server property.");
832 :
833 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Server, "",
834 : "Name of server to connect to.\n"
835 : "This should be the name of real server.\n"
836 : "This property cannot be used with Servername property.");
837 :
838 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Address, "",
839 : "The hostname or ip address of the server.");
840 :
841 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Port, "1433",
842 : "TCP/IP Port to connect to.");
843 :
844 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Database, "",
845 : "Default database.");
846 :
847 : hLastProperty = definePropertyList(hLastProperty, odbc_param_TDS_Version, "4.2", (void*) aTDSver, sizeof(aTDSver),
848 : "The TDS protocol version.\n"
849 : " 4.2 MSSQL 6.5 or Sybase < 10.x\n"
850 : " 5.0 Sybase >= 10.x\n"
851 : " 7.0 MSSQL 7\n"
852 : " 7.1 MSSQL 2000\n"
853 : " 7.2 MSSQL 2005\n"
854 : " 7.3 MSSQL 2008\n"
855 : " 7.4 MSSQL 2012 or 2014"
856 : );
857 :
858 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Language, "us_english", (void*) aLanguage, sizeof(aLanguage),
859 : "The default language setting.");
860 :
861 : hLastProperty = definePropertyHidden(hLastProperty, odbc_param_TextSize, "",
862 : "Text datatype limit.");
863 :
864 : /* ??? in odbc.ini ??? */
865 : /*
866 : hLastProperty = definePropertyString(hLastProperty, odbc_param_UID, "",
867 : "User ID (Beware of security issues).");
868 :
869 : hLastProperty = definePropertyString(hLastProperty, odbc_param_PWD, "",
870 : "Password (Beware of security issues).");
871 : */
872 :
873 : /*
874 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Authentication, "Server", aAuth, sizeof(aAuth),
875 : "The server authentication mechanism.");
876 :
877 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Domain, "",
878 : "The default domain to use when using Domain Authentication.");
879 : */
880 :
881 : hLastProperty = definePropertyString(hLastProperty, odbc_param_PacketSize, "",
882 : "Size of network packets.");
883 :
884 : hLastProperty = definePropertyString(hLastProperty, odbc_param_ClientCharset, "",
885 : "The client character set name to convert application characters to UCS-2 in TDS 7.0 and higher.");
886 :
887 : hLastProperty = definePropertyString(hLastProperty, odbc_param_DumpFile, "",
888 : "Specifies the location of a tds dump file and turns on logging.");
889 :
890 : hLastProperty = definePropertyBoolean(hLastProperty, odbc_param_DumpFileAppend, "",
891 : "Appends dump file instead of overwriting it. Useful for debugging when many processes are active.");
892 :
893 : hLastProperty = definePropertyString(hLastProperty, odbc_param_DebugFlags, "",
894 : "Sets granularity of logging. A set of bit that specify levels and informations. See table below for bit specification.");
895 :
896 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Encryption, TDS_STR_ENCRYPTION_OFF, aEncryption, sizeof(aEncryption),
897 : "The encryption method.");
898 :
899 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Timeout, "10",
900 : "Connection timeout.");
901 :
902 : return 1;
903 : }
904 :
905 : #endif
|