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 48 : parse_server(TDS_ERRS *errs, char *server, TDSLOGIN * login)
108 : {
109 48 : char *p = (char *) strchr(server, '\\');
110 :
111 48 : 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 48 : p = (char *) strchr(server, ',');
119 48 : if (p && atoi(p+1) > 0) {
120 0 : login->port = atoi(p+1);
121 0 : *p = 0;
122 : }
123 : }
124 :
125 48 : if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs)))
126 48 : 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 464 : DSTR tmp = *a;
267 464 : *a = *b;
268 464 : *b = tmp;
269 : }
270 :
271 : static const char *
272 836 : 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 836 : if (p == connect_string_end || *p != '{') {
279 612 : end = (const char *) memchr(p, ';', connect_string_end - p);
280 612 : if (!end)
281 6 : end = connect_string_end;
282 612 : 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 224 : ++p;
290 : /* search "};" */
291 224 : end = p;
292 : for (;;) {
293 : /* search next '}' */
294 272 : end = (const char *) memchr(end, '}', connect_string_end - end);
295 248 : if (end == NULL) {
296 0 : odbc_errs_add(errs, "HY000", "Syntax error in connection string");
297 0 : return NULL;
298 : }
299 248 : end++;
300 :
301 : /* termination ? */
302 248 : if (end == connect_string_end || end[0] == ';') {
303 224 : 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 224 : if (!tds_dstr_alloc(value, end - p)) {
315 0 : odbc_errs_add(errs, "HY001", NULL);
316 0 : return NULL;
317 : }
318 448 : dst = tds_dstr_buf(value);
319 2312 : for (; p < end; ++p) {
320 2088 : char ch = *p;
321 2088 : *dst++ = ch;
322 2088 : if (ch == '}')
323 24 : ++p;
324 : }
325 448 : tds_dstr_setlen(value, dst - tds_dstr_buf(value));
326 :
327 224 : 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 134 : 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 134 : DSTR *dest_s, value = DSTR_INITIALIZER;
343 : enum { CFG_DSN = 1, CFG_SERVER = 2, CFG_SERVERNAME = 4 };
344 134 : unsigned int cfgs = 0; /* flags for indicate second parse of string */
345 : char option[24];
346 134 : int trusted = 0;
347 :
348 134 : if (parsed_params)
349 134 : memset(parsed_params, 0, sizeof(*parsed_params)*ODBC_PARAM_SIZE);
350 :
351 964 : 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 836 : while (p < connect_string_end && *p == ';')
358 0 : ++p;
359 :
360 : /* parse option */
361 836 : end = (const char *) memchr(p, '=', connect_string_end - p);
362 836 : if (!end)
363 : break;
364 :
365 : /* account for spaces between ;'s. */
366 836 : while (p < end && *p == ' ')
367 0 : ++p;
368 :
369 836 : if ((end - p) >= (int) sizeof(option))
370 0 : option[0] = 0;
371 : else {
372 836 : memcpy(option, p, end - p);
373 836 : option[end - p] = 0;
374 : }
375 :
376 : /* parse value */
377 836 : p = end + 1;
378 836 : end = parse_value(errs, p, connect_string_end, &value);
379 836 : if (!end)
380 : return false;
381 :
382 : #define CHK_PARAM(p) (strcasecmp(option, odbc_param_##p) == 0 && (num_param=ODBC_PARAM_##p) >= 0)
383 836 : if (CHK_PARAM(Server)) {
384 : /* error if servername or DSN specified */
385 48 : if ((cfgs & (CFG_DSN|CFG_SERVERNAME)) != 0) {
386 0 : tds_dstr_free(&value);
387 0 : odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified");
388 0 : return false;
389 : }
390 48 : if (!cfgs) {
391 48 : dest_s = &login->server_name;
392 : /* not that safe cast but works -- freddy77 */
393 96 : if (!parse_server(errs, (char *) tds_dstr_cstr(&value), login)) {
394 0 : tds_dstr_free(&value);
395 0 : return false;
396 : }
397 : cfgs = CFG_SERVER;
398 : }
399 788 : } 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 756 : } 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 616 : } else if (CHK_PARAM(Database)) {
428 126 : dest_s = &login->database;
429 490 : } else if (CHK_PARAM(UID)) {
430 134 : dest_s = &login->user_name;
431 356 : } else if (CHK_PARAM(PWD)) {
432 134 : dest_s = &login->password;
433 222 : } else if (CHK_PARAM(APP)) {
434 6 : dest_s = &login->app_name;
435 216 : } else if (CHK_PARAM(WSID)) {
436 0 : dest_s = &login->client_host_name;
437 216 : } else if (CHK_PARAM(Language)) {
438 0 : tds_parse_conf_section(TDS_STR_LANGUAGE, tds_dstr_cstr(&value), login);
439 216 : } else if (CHK_PARAM(Port)) {
440 96 : tds_parse_conf_section(TDS_STR_PORT, tds_dstr_cstr(&value), login);
441 168 : } else if (CHK_PARAM(TDS_Version)) {
442 16 : tds_parse_conf_section(TDS_STR_VERSION, tds_dstr_cstr(&value), login);
443 160 : } else if (CHK_PARAM(TextSize)) {
444 0 : tds_parse_conf_section(TDS_STR_TEXTSZ, tds_dstr_cstr(&value), login);
445 160 : } else if (CHK_PARAM(PacketSize)) {
446 0 : tds_parse_conf_section(TDS_STR_BLKSZ, tds_dstr_cstr(&value), login);
447 160 : } else if (CHK_PARAM(ClientCharset)
448 80 : || 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 80 : } else if (CHK_PARAM(DumpFile)) {
452 0 : tds_parse_conf_section(TDS_STR_DUMPFILE, tds_dstr_cstr(&value), login);
453 80 : } else if (CHK_PARAM(DumpFileAppend)) {
454 0 : tds_parse_conf_section(TDS_STR_APPENDMODE, tds_dstr_cstr(&value), login);
455 80 : } else if (CHK_PARAM(DebugFlags)) {
456 0 : tds_parse_conf_section(TDS_STR_DEBUGFLAGS, tds_dstr_cstr(&value), login);
457 80 : } else if (CHK_PARAM(Encryption)) {
458 0 : tds_parse_conf_section(TDS_STR_ENCRYPTION, tds_dstr_cstr(&value), login);
459 80 : } else if (CHK_PARAM(UseNTLMv2)) {
460 0 : tds_parse_conf_section(TDS_STR_USENTLMV2, tds_dstr_cstr(&value), login);
461 80 : } else if (CHK_PARAM(REALM)) {
462 0 : tds_parse_conf_section(TDS_STR_REALM, tds_dstr_cstr(&value), login);
463 80 : } else if (CHK_PARAM(ServerSPN)) {
464 0 : tds_parse_conf_section(TDS_STR_SPN, tds_dstr_cstr(&value), login);
465 80 : } 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 80 : } else if (CHK_PARAM(MARS_Connection)) {
471 0 : if (tds_config_boolean(option, tds_dstr_cstr(&value), login))
472 0 : login->mars = 1;
473 80 : } else if (CHK_PARAM(AttachDbFilename)) {
474 0 : dest_s = &login->db_filename;
475 80 : } 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 80 : } else if (CHK_PARAM(Timeout)) {
491 0 : tds_parse_conf_section(TDS_STR_TIMEOUT, tds_dstr_cstr(&value), login);
492 : }
493 :
494 750 : if (num_param >= 0 && parsed_params) {
495 670 : parsed_params[num_param].p = p;
496 670 : parsed_params[num_param].len = end - p;
497 : }
498 :
499 : /* copy to destination */
500 750 : if (dest_s)
501 : odbc_dstr_swap(dest_s, &value);
502 :
503 750 : p = end;
504 : /* handle "" ";.." cases */
505 750 : if (p >= connect_string_end)
506 : break;
507 744 : ++p;
508 : }
509 :
510 134 : 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 134 : tds_dstr_free(&value);
522 134 : 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 void
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 : }
585 :
586 : static int
587 18044 : SQLGetPrivateProfileString(LPCSTR pszSection, LPCSTR pszEntry, LPCSTR pszDefault, LPSTR pRetBuffer, int nRetBuffer,
588 : LPCSTR pszFileName)
589 : {
590 : FILE *hFile;
591 : ProfileParam param;
592 :
593 18044 : tdsdump_log(TDS_DBG_FUNC, "SQLGetPrivateProfileString(%p, %p, %p, %p, %d, %p)\n",
594 : pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer, pszFileName);
595 :
596 18044 : if (!pszSection) {
597 : /* spec says return list of all section names - but we will just return nothing */
598 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: Functionality for NULL pszSection not implemented.\n");
599 : return 0;
600 : }
601 :
602 18044 : if (!pszEntry) {
603 : /* spec says return list of all key names in section - but we will just return nothing */
604 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: Functionality for NULL pszEntry not implemented.\n");
605 : return 0;
606 : }
607 :
608 18044 : if (nRetBuffer < 1)
609 0 : tdsdump_log(TDS_DBG_WARN, "WARNING: No space to return a value because nRetBuffer < 1.\n");
610 :
611 18044 : if (pszFileName && *pszFileName == '/')
612 0 : hFile = fopen(pszFileName, "r");
613 : else
614 18044 : hFile = tdoGetIniFileName();
615 :
616 18044 : if (hFile == NULL) {
617 0 : tdsdump_log(TDS_DBG_ERROR, "ERROR: Could not open configuration file\n");
618 : return 0;
619 : }
620 :
621 18044 : param.entry = pszEntry;
622 18044 : param.buffer = pRetBuffer;
623 18044 : param.buffer_len = nRetBuffer;
624 18044 : param.ret_val = 0;
625 18044 : param.found = 0;
626 :
627 18044 : pRetBuffer[0] = 0;
628 18044 : tds_read_conf_section(hFile, pszSection, tdoParseProfile, ¶m);
629 :
630 18044 : if (pszDefault && !param.found) {
631 16340 : strlcpy(pRetBuffer, pszDefault, nRetBuffer);
632 :
633 16340 : param.ret_val = strlen(pRetBuffer);
634 : }
635 :
636 18044 : fclose(hFile);
637 18044 : return param.ret_val;
638 : }
639 :
640 : static FILE *
641 18044 : tdoGetIniFileName()
642 : {
643 18044 : FILE *ret = NULL;
644 : char *p;
645 : char *fn;
646 :
647 : /*
648 : * First, try the ODBCINI environment variable
649 : */
650 18044 : if ((p = getenv("ODBCINI")) != NULL)
651 18044 : ret = fopen(p, "r");
652 :
653 : /*
654 : * Second, try the HOME environment variable
655 : */
656 18044 : if (!ret && (p = tds_get_homedir()) != NULL) {
657 0 : fn = NULL;
658 0 : if (asprintf(&fn, "%s/.odbc.ini", p) > 0) {
659 0 : ret = fopen(fn, "r");
660 0 : free(fn);
661 : }
662 0 : free(p);
663 : }
664 :
665 : /*
666 : * As a last resort, try SYS_ODBC_INI
667 : */
668 18044 : if (!ret)
669 0 : ret = fopen(SYS_ODBC_INI, "r");
670 :
671 18044 : return ret;
672 : }
673 :
674 : #endif /* !HAVE_SQLGETPRIVATEPROFILESTRING */
675 :
676 : #ifdef UNIXODBC
677 :
678 : /*
679 : * Begin BIG Hack.
680 : *
681 : * We need these from odbcinstext.h but it wants to
682 : * include <log.h> and <ini.h>, which are not in the
683 : * standard include path. XXX smurph
684 : * confirmed by unixODBC stuff, odbcinstext.h shouldn't be installed. freddy77
685 : */
686 : #define INI_MAX_LINE 1000
687 : #define INI_MAX_OBJECT_NAME INI_MAX_LINE
688 : #define INI_MAX_PROPERTY_NAME INI_MAX_LINE
689 : #define INI_MAX_PROPERTY_VALUE INI_MAX_LINE
690 :
691 : #define ODBCINST_PROMPTTYPE_LABEL 0 /* readonly */
692 : #define ODBCINST_PROMPTTYPE_TEXTEDIT 1
693 : #define ODBCINST_PROMPTTYPE_LISTBOX 2
694 : #define ODBCINST_PROMPTTYPE_COMBOBOX 3
695 : #define ODBCINST_PROMPTTYPE_FILENAME 4
696 : #define ODBCINST_PROMPTTYPE_HIDDEN 5
697 :
698 : typedef struct tODBCINSTPROPERTY
699 : {
700 : /** pointer to next property, NULL if last property */
701 : struct tODBCINSTPROPERTY *pNext;
702 :
703 : /** property name */
704 : char szName[INI_MAX_PROPERTY_NAME + 1];
705 :
706 : /** property value */
707 : char szValue[INI_MAX_PROPERTY_VALUE + 1];
708 :
709 : /** PROMPTTYPE_TEXTEDIT, PROMPTTYPE_LISTBOX, PROMPTTYPE_COMBOBOX, PROMPTTYPE_FILENAME */
710 : int nPromptType;
711 :
712 : /** array of pointers terminated with a NULL value in array */
713 : char **aPromptData;
714 :
715 : /** help on this property (driver setups should keep it short) */
716 : char *pszHelp;
717 :
718 : /** CALLER CAN STORE A POINTER TO ? HERE */
719 : void *pWidget;
720 :
721 : /** app should refresh widget ie Driver Setup has changed aPromptData or szValue */
722 : int bRefresh;
723 :
724 : /** for odbcinst internal use... only first property has valid one */
725 : void *hDLL;
726 : }
727 : ODBCINSTPROPERTY, *HODBCINSTPROPERTY;
728 :
729 : /*
730 : * End BIG Hack.
731 : */
732 :
733 : int ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty);
734 :
735 : static const char *const aTDSver[] = {
736 : "",
737 : "4.2",
738 : "5.0",
739 : "7.0",
740 : "7.1",
741 : "7.2",
742 : "7.3",
743 : "7.4",
744 : NULL
745 : };
746 :
747 : static const char *const aLanguage[] = {
748 : "us_english",
749 : NULL
750 : };
751 :
752 : static const char *const aEncryption[] = {
753 : TDS_STR_ENCRYPTION_OFF,
754 : TDS_STR_ENCRYPTION_REQUEST,
755 : TDS_STR_ENCRYPTION_REQUIRE,
756 : NULL
757 : };
758 :
759 : static const char *const aBoolean[] = {
760 : "Yes",
761 : "No",
762 : NULL
763 : };
764 :
765 : /*
766 : static const char *aAuth[] = {
767 : "Server",
768 : "Domain",
769 : "Both",
770 : NULL
771 : };
772 : */
773 :
774 : static HODBCINSTPROPERTY
775 : addProperty(HODBCINSTPROPERTY hLastProperty)
776 : {
777 : hLastProperty->pNext = (HODBCINSTPROPERTY) calloc(1, sizeof(ODBCINSTPROPERTY));
778 : hLastProperty = hLastProperty->pNext;
779 : return hLastProperty;
780 : }
781 :
782 : static HODBCINSTPROPERTY
783 : definePropertyString(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
784 : {
785 : hLastProperty = addProperty(hLastProperty);
786 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_TEXTEDIT;
787 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
788 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
789 : hLastProperty->pszHelp = (char *) strdup(comment);
790 : return hLastProperty;
791 : }
792 :
793 : static HODBCINSTPROPERTY
794 : definePropertyBoolean(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
795 : {
796 : hLastProperty = addProperty(hLastProperty);
797 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_LISTBOX;
798 : hLastProperty->aPromptData = malloc(sizeof(aBoolean));
799 : memcpy(hLastProperty->aPromptData, aBoolean, sizeof(aBoolean));
800 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
801 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
802 : hLastProperty->pszHelp = (char *) strdup(comment);
803 : return hLastProperty;
804 : }
805 :
806 : static HODBCINSTPROPERTY
807 : definePropertyHidden(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const char *comment)
808 : {
809 : hLastProperty = addProperty(hLastProperty);
810 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_HIDDEN;
811 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
812 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
813 : hLastProperty->pszHelp = (char *) strdup(comment);
814 : return hLastProperty;
815 : }
816 :
817 : static HODBCINSTPROPERTY
818 : definePropertyList(HODBCINSTPROPERTY hLastProperty, const char *name, const char *value, const void *list, int size, const char *comment)
819 : {
820 : hLastProperty = addProperty(hLastProperty);
821 : hLastProperty->nPromptType = ODBCINST_PROMPTTYPE_LISTBOX;
822 : hLastProperty->aPromptData = malloc(size);
823 : memcpy(hLastProperty->aPromptData, list, size);
824 : strlcpy(hLastProperty->szName, name, INI_MAX_PROPERTY_NAME);
825 : strlcpy(hLastProperty->szValue, value, INI_MAX_PROPERTY_VALUE);
826 : hLastProperty->pszHelp = (char *) strdup(comment);
827 : return hLastProperty;
828 : }
829 :
830 : int
831 : ODBCINSTGetProperties(HODBCINSTPROPERTY hLastProperty)
832 : {
833 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Servername, "",
834 : "Name of FreeTDS connection to connect to.\n"
835 : "This server name refer to entry in freetds.conf file, not real server name.\n"
836 : "This property cannot be used with Server property.");
837 :
838 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Server, "",
839 : "Name of server to connect to.\n"
840 : "This should be the name of real server.\n"
841 : "This property cannot be used with Servername property.");
842 :
843 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Address, "",
844 : "The hostname or ip address of the server.");
845 :
846 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Port, "1433",
847 : "TCP/IP Port to connect to.");
848 :
849 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Database, "",
850 : "Default database.");
851 :
852 : hLastProperty = definePropertyList(hLastProperty, odbc_param_TDS_Version, "4.2", (void*) aTDSver, sizeof(aTDSver),
853 : "The TDS protocol version.\n"
854 : " 4.2 MSSQL 6.5 or Sybase < 10.x\n"
855 : " 5.0 Sybase >= 10.x\n"
856 : " 7.0 MSSQL 7\n"
857 : " 7.1 MSSQL 2000\n"
858 : " 7.2 MSSQL 2005\n"
859 : " 7.3 MSSQL 2008\n"
860 : " 7.4 MSSQL 2012 or 2014"
861 : );
862 :
863 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Language, "us_english", (void*) aLanguage, sizeof(aLanguage),
864 : "The default language setting.");
865 :
866 : hLastProperty = definePropertyHidden(hLastProperty, odbc_param_TextSize, "",
867 : "Text datatype limit.");
868 :
869 : /* ??? in odbc.ini ??? */
870 : /*
871 : hLastProperty = definePropertyString(hLastProperty, odbc_param_UID, "",
872 : "User ID (Beware of security issues).");
873 :
874 : hLastProperty = definePropertyString(hLastProperty, odbc_param_PWD, "",
875 : "Password (Beware of security issues).");
876 : */
877 :
878 : /*
879 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Authentication, "Server", aAuth, sizeof(aAuth),
880 : "The server authentication mechanism.");
881 :
882 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Domain, "",
883 : "The default domain to use when using Domain Authentication.");
884 : */
885 :
886 : hLastProperty = definePropertyString(hLastProperty, odbc_param_PacketSize, "",
887 : "Size of network packets.");
888 :
889 : hLastProperty = definePropertyString(hLastProperty, odbc_param_ClientCharset, "",
890 : "The client character set name to convert application characters to UCS-2 in TDS 7.0 and higher.");
891 :
892 : hLastProperty = definePropertyString(hLastProperty, odbc_param_DumpFile, "",
893 : "Specifies the location of a tds dump file and turns on logging.");
894 :
895 : hLastProperty = definePropertyBoolean(hLastProperty, odbc_param_DumpFileAppend, "",
896 : "Appends dump file instead of overwriting it. Useful for debugging when many processes are active.");
897 :
898 : hLastProperty = definePropertyString(hLastProperty, odbc_param_DebugFlags, "",
899 : "Sets granularity of logging. A set of bit that specify levels and informations. See table below for bit specification.");
900 :
901 : hLastProperty = definePropertyList(hLastProperty, odbc_param_Encryption, TDS_STR_ENCRYPTION_OFF, aEncryption, sizeof(aEncryption),
902 : "The encryption method.");
903 :
904 : hLastProperty = definePropertyString(hLastProperty, odbc_param_Timeout, "10",
905 : "Connection timeout.");
906 :
907 : return 1;
908 : }
909 :
910 : #endif
|