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