[ODBCCP32] Sync with Wine Staging 1.7.55. CORE-10536
[reactos.git] / reactos / dll / win32 / odbccp32 / odbccp32.c
1 /*
2 * Implementation of the ODBC driver installer
3 *
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Hans Leidekker
6 * Copyright 2007 Bill Medland
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #define WIN32_NO_STATUS
24
25 #include <assert.h>
26 #include <stdarg.h>
27
28 #define COBJMACROS
29
30 #include <windef.h>
31 #include <winbase.h>
32 #include <winreg.h>
33 #include <winnls.h>
34 #include <wine/debug.h>
35
36 #include <odbcinst.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(odbc);
39
40 /* Registry key names */
41 static const WCHAR drivers_key[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C','\\','O','D','B','C','I','N','S','T','.','I','N','I','\\','O','D','B','C',' ','D','r','i','v','e','r','s',0};
42 static const WCHAR odbcW[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C',0};
43
44 /* This config mode is known to be process-wide.
45 * MSDN documentation suggests that the value is hidden somewhere in the registry but I haven't found it yet.
46 * Although both the registry and the ODBC.ini files appear to be maintained together they are not maintained automatically through the registry's IniFileMapping.
47 */
48 static UWORD config_mode = ODBC_BOTH_DSN;
49
50 /* MSDN documentation suggests that the error subsystem handles errors 1 to 8
51 * only and experimentation (Windows 2000) shows that the errors are process-
52 * wide so go for the simple solution; static arrays.
53 */
54 static int num_errors;
55 static int error_code[8];
56 static const WCHAR *error_msg[8];
57 static const WCHAR odbc_error_general_err[] = {'G','e','n','e','r','a','l',' ','e','r','r','o','r',0};
58 static const WCHAR odbc_error_invalid_buff_len[] = {'I','n','v','a','l','i','d',' ','b','u','f','f','e','r',' ','l','e','n','g','t','h',0};
59 static const WCHAR odbc_error_component_not_found[] = {'C','o','m','p','o','n','e','n','t',' ','n','o','t',' ','f','o','u','n','d',0};
60 static const WCHAR odbc_error_out_of_mem[] = {'O','u','t',' ','o','f',' ','m','e','m','o','r','y',0};
61 static const WCHAR odbc_error_invalid_param_sequence[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','e','q','u','e','n','c','e',0};
62 static const WCHAR odbc_error_invalid_param_string[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','t','r','i','n','g',0};
63
64 /* Push an error onto the error stack, taking care of ranges etc. */
65 static void push_error(int code, LPCWSTR msg)
66 {
67 if (num_errors < sizeof error_code/sizeof error_code[0])
68 {
69 error_code[num_errors] = code;
70 error_msg[num_errors] = msg;
71 num_errors++;
72 }
73 }
74
75 /* Clear the error stack */
76 static void clear_errors(void)
77 {
78 num_errors = 0;
79 }
80
81 static inline void * heap_alloc(size_t len)
82 {
83 return HeapAlloc(GetProcessHeap(), 0, len);
84 }
85
86 static inline BOOL heap_free(void *mem)
87 {
88 return HeapFree(GetProcessHeap(), 0, mem);
89 }
90
91 static inline WCHAR *heap_strdupAtoW(const char *str)
92 {
93 LPWSTR ret = NULL;
94
95 if(str) {
96 DWORD len;
97
98 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
99 ret = heap_alloc(len*sizeof(WCHAR));
100 if(ret)
101 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
102 }
103
104 return ret;
105 }
106
107
108 BOOL WINAPI ODBCCPlApplet( LONG i, LONG j, LONG * p1, LONG * p2)
109 {
110 clear_errors();
111 FIXME( "( %d %d %p %p) : stub!\n", i, j, p1, p2);
112 return FALSE;
113 }
114
115 static LPWSTR SQLInstall_strdup_multi(LPCSTR str)
116 {
117 LPCSTR p;
118 LPWSTR ret = NULL;
119 DWORD len;
120
121 if (!str)
122 return ret;
123
124 for (p = str; *p; p += lstrlenA(p) + 1)
125 ;
126
127 len = MultiByteToWideChar(CP_ACP, 0, str, p - str, NULL, 0 );
128 ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
129 MultiByteToWideChar(CP_ACP, 0, str, p - str, ret, len );
130 ret[len] = 0;
131
132 return ret;
133 }
134
135 static LPWSTR SQLInstall_strdup(LPCSTR str)
136 {
137 DWORD len;
138 LPWSTR ret = NULL;
139
140 if (!str)
141 return ret;
142
143 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0 );
144 ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
145 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len );
146
147 return ret;
148 }
149
150 /* Convert the wide string or zero-length-terminated list of wide strings to a
151 * narrow string or zero-length-terminated list of narrow strings.
152 * Do not try to emulate windows undocumented excesses (e.g. adding a third \0
153 * to a list)
154 * Arguments
155 * mode Indicates the sort of string.
156 * 1 denotes that the buffers contain strings terminated by a single nul
157 * character
158 * 2 denotes that the buffers contain zero-length-terminated lists
159 * (frequently erroneously referred to as double-null-terminated)
160 * buffer The narrow-character buffer into which to place the result. This
161 * must be a non-null pointer to the first element of a buffer whose
162 * length is passed in buffer_length.
163 * str The wide-character buffer containing the string or list of strings to
164 * be converted. str_length defines how many wide characters in the
165 * buffer are to be converted, including all desired terminating nul
166 * characters.
167 * str_length Effective length of str
168 * buffer_length Length of buffer
169 * returned_length A pointer to a variable that will receive the number of
170 * narrow characters placed into the buffer. This pointer
171 * may be NULL.
172 */
173 static BOOL SQLInstall_narrow(int mode, LPSTR buffer, LPCWSTR str, WORD str_length, WORD buffer_length, WORD *returned_length)
174 {
175 LPSTR pbuf; /* allows us to allocate a temporary buffer only if needed */
176 int len; /* Length of the converted list */
177 BOOL success = FALSE;
178 assert(mode == 1 || mode == 2);
179 assert(buffer_length);
180 len = WideCharToMultiByte(CP_ACP, 0, str, str_length, 0, 0, NULL, NULL);
181 if (len > 0)
182 {
183 if (len > buffer_length)
184 {
185 pbuf = HeapAlloc(GetProcessHeap(), 0, len);
186 }
187 else
188 {
189 pbuf = buffer;
190 }
191 len = WideCharToMultiByte(CP_ACP, 0, str, str_length, pbuf, len, NULL, NULL);
192 if (len > 0)
193 {
194 if (pbuf != buffer)
195 {
196 if (buffer_length > (mode - 1))
197 {
198 memcpy (buffer, pbuf, buffer_length-mode);
199 *(buffer+buffer_length-mode) = '\0';
200 }
201 *(buffer+buffer_length-1) = '\0';
202 }
203 if (returned_length)
204 {
205 *returned_length = pbuf == buffer ? len : buffer_length;
206 }
207 success = TRUE;
208 }
209 else
210 {
211 ERR("transferring wide to narrow\n");
212 }
213 if (pbuf != buffer)
214 {
215 HeapFree(GetProcessHeap(), 0, pbuf);
216 }
217 }
218 else
219 {
220 ERR("measuring wide to narrow\n");
221 }
222 return success;
223 }
224
225 BOOL WINAPI SQLConfigDataSourceW(HWND hwndParent, WORD fRequest,
226 LPCWSTR lpszDriver, LPCWSTR lpszAttributes)
227 {
228 LPCWSTR p;
229
230 clear_errors();
231 FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_w(lpszDriver),
232 debugstr_w(lpszAttributes));
233
234 for (p = lpszAttributes; *p; p += lstrlenW(p) + 1)
235 FIXME("%s\n", debugstr_w(p));
236
237 return TRUE;
238 }
239
240 BOOL WINAPI SQLConfigDataSource(HWND hwndParent, WORD fRequest,
241 LPCSTR lpszDriver, LPCSTR lpszAttributes)
242 {
243 FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_a(lpszDriver),
244 debugstr_a(lpszAttributes));
245 clear_errors();
246 return TRUE;
247 }
248
249 BOOL WINAPI SQLConfigDriverW(HWND hwndParent, WORD fRequest, LPCWSTR lpszDriver,
250 LPCWSTR lpszArgs, LPWSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut)
251 {
252 clear_errors();
253 FIXME("(%p %d %s %s %p %d %p)\n", hwndParent, fRequest, debugstr_w(lpszDriver),
254 debugstr_w(lpszArgs), lpszMsg, cbMsgMax, pcbMsgOut);
255 return TRUE;
256 }
257
258 BOOL WINAPI SQLConfigDriver(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver,
259 LPCSTR lpszArgs, LPSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut)
260 {
261 clear_errors();
262 FIXME("(%p %d %s %s %p %d %p)\n", hwndParent, fRequest, debugstr_a(lpszDriver),
263 debugstr_a(lpszArgs), lpszMsg, cbMsgMax, pcbMsgOut);
264 return TRUE;
265 }
266
267 BOOL WINAPI SQLCreateDataSourceW(HWND hwnd, LPCWSTR lpszDS)
268 {
269 clear_errors();
270 FIXME("%p %s\n", hwnd, debugstr_w(lpszDS));
271 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
272 return FALSE;
273 }
274
275 BOOL WINAPI SQLCreateDataSource(HWND hwnd, LPCSTR lpszDS)
276 {
277 clear_errors();
278 FIXME("%p %s\n", hwnd, debugstr_a(lpszDS));
279 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
280 return FALSE;
281 }
282
283 BOOL WINAPI SQLGetAvailableDriversW(LPCWSTR lpszInfFile, LPWSTR lpszBuf,
284 WORD cbBufMax, WORD *pcbBufOut)
285 {
286 clear_errors();
287 FIXME("%s %p %d %p\n", debugstr_w(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
288 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
289 return FALSE;
290 }
291
292 BOOL WINAPI SQLGetAvailableDrivers(LPCSTR lpszInfFile, LPSTR lpszBuf,
293 WORD cbBufMax, WORD *pcbBufOut)
294 {
295 clear_errors();
296 FIXME("%s %p %d %p\n", debugstr_a(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
297 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
298 return FALSE;
299 }
300
301 BOOL WINAPI SQLGetConfigMode(UWORD *pwConfigMode)
302 {
303 clear_errors();
304 TRACE("%p\n", pwConfigMode);
305 if (pwConfigMode)
306 *pwConfigMode = config_mode;
307 return TRUE;
308 }
309
310 /* This is implemented sensibly rather than according to exact conformance to Microsoft's buggy implementations
311 * e.g. The Microsoft one occasionally actually adds a third nul character (possibly beyond the buffer).
312 * e.g. If the key has no drivers then version 3.525.1117.0 does not modify the buffer at all, not even a nul character.
313 */
314 BOOL WINAPI SQLGetInstalledDriversW(LPWSTR lpszBuf, WORD cbBufMax,
315 WORD *pcbBufOut)
316 {
317 HKEY hDrivers; /* Registry handle to the Drivers key */
318 LONG reg_ret; /* Return code from registry functions */
319 BOOL success = FALSE; /* The value we will return */
320
321 clear_errors();
322
323 TRACE("%p %d %p\n", lpszBuf, cbBufMax, pcbBufOut);
324
325 if (!lpszBuf || cbBufMax == 0)
326 {
327 push_error(ODBC_ERROR_INVALID_BUFF_LEN, odbc_error_invalid_buff_len);
328 }
329 else if ((reg_ret = RegOpenKeyExW (HKEY_LOCAL_MACHINE /* The drivers does not depend on the config mode */,
330 drivers_key, 0, KEY_READ /* Maybe overkill */,
331 &hDrivers)) == ERROR_SUCCESS)
332 {
333 DWORD index = 0;
334 cbBufMax--;
335 success = TRUE;
336 while (cbBufMax > 0)
337 {
338 DWORD size_name;
339 size_name = cbBufMax;
340 if ((reg_ret = RegEnumValueW(hDrivers, index, lpszBuf, &size_name, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
341 {
342 index++;
343 assert (size_name < cbBufMax && *(lpszBuf + size_name) == 0);
344 size_name++;
345 cbBufMax-= size_name;
346 lpszBuf+=size_name;
347 }
348 else
349 {
350 if (reg_ret != ERROR_NO_MORE_ITEMS)
351 {
352 success = FALSE;
353 push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err);
354 }
355 break;
356 }
357 }
358 *lpszBuf = 0;
359 if ((reg_ret = RegCloseKey (hDrivers)) != ERROR_SUCCESS)
360 TRACE ("Error %d closing ODBC Drivers key\n", reg_ret);
361 }
362 else
363 {
364 /* MSDN states that it returns failure with COMPONENT_NOT_FOUND in this case.
365 * Version 3.525.1117.0 (Windows 2000) does not; it actually returns success.
366 * I doubt if it will actually be an issue.
367 */
368 push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
369 }
370 return success;
371 }
372
373 BOOL WINAPI SQLGetInstalledDrivers(LPSTR lpszBuf, WORD cbBufMax,
374 WORD *pcbBufOut)
375 {
376 BOOL ret;
377 int size_wbuf = cbBufMax;
378 LPWSTR wbuf;
379 WORD size_used;
380
381 TRACE("%p %d %p\n", lpszBuf, cbBufMax, pcbBufOut);
382
383 wbuf = HeapAlloc(GetProcessHeap(), 0, size_wbuf*sizeof(WCHAR));
384 if (wbuf)
385 {
386 ret = SQLGetInstalledDriversW(wbuf, size_wbuf, &size_used);
387 if (ret)
388 {
389 if (!(ret = SQLInstall_narrow(2, lpszBuf, wbuf, size_used, cbBufMax, pcbBufOut)))
390 {
391 push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err);
392 }
393 }
394 HeapFree(GetProcessHeap(), 0, wbuf);
395 /* ignore failure; we have achieved the aim */
396 }
397 else
398 {
399 push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
400 ret = FALSE;
401 }
402 return ret;
403 }
404
405 static HKEY get_privateprofile_sectionkey(LPCWSTR section, LPCWSTR filename)
406 {
407 HKEY hkey, hkeyfilename, hkeysection;
408 LONG ret;
409
410 if (RegOpenKeyW(HKEY_CURRENT_USER, odbcW, &hkey))
411 return NULL;
412
413 ret = RegOpenKeyW(hkey, filename, &hkeyfilename);
414 RegCloseKey(hkey);
415 if (ret)
416 return NULL;
417
418 ret = RegOpenKeyW(hkeyfilename, section, &hkeysection);
419 RegCloseKey(hkeyfilename);
420
421 return ret ? NULL : hkeysection;
422 }
423
424 int WINAPI SQLGetPrivateProfileStringW(LPCWSTR section, LPCWSTR entry,
425 LPCWSTR defvalue, LPWSTR buff, int buff_len, LPCWSTR filename)
426 {
427 BOOL usedefault = TRUE;
428 HKEY sectionkey;
429 LONG ret = 0;
430
431 TRACE("%s %s %s %p %d %s\n", debugstr_w(section), debugstr_w(entry),
432 debugstr_w(defvalue), buff, buff_len, debugstr_w(filename));
433
434 clear_errors();
435
436 if (buff_len <= 0 || !section)
437 return 0;
438
439 if(buff)
440 buff[0] = 0;
441
442 if (!defvalue || !buff)
443 return 0;
444
445 sectionkey = get_privateprofile_sectionkey(section, filename);
446 if (sectionkey)
447 {
448 DWORD type, size;
449
450 if (entry)
451 {
452 size = buff_len * sizeof(*buff);
453 if (RegGetValueW(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
454 {
455 usedefault = FALSE;
456 ret = (size / sizeof(*buff)) - 1;
457 }
458 }
459 else
460 {
461 WCHAR name[MAX_PATH];
462 DWORD index = 0;
463 DWORD namelen;
464
465 usedefault = FALSE;
466
467 memset(buff, 0, buff_len);
468
469 namelen = sizeof(name);
470 while (RegEnumValueW(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
471 {
472 if ((ret + namelen+1) > buff_len)
473 break;
474
475 lstrcpyW(buff+ret, name);
476 ret += namelen+1;
477 namelen = sizeof(name);
478 index++;
479 }
480 }
481
482 RegCloseKey(sectionkey);
483 }
484 else
485 usedefault = entry != NULL;
486
487 if (usedefault)
488 {
489 lstrcpynW(buff, defvalue, buff_len);
490 ret = lstrlenW(buff);
491 }
492
493 return ret;
494 }
495
496 int WINAPI SQLGetPrivateProfileString(LPCSTR section, LPCSTR entry,
497 LPCSTR defvalue, LPSTR buff, int buff_len, LPCSTR filename)
498 {
499 WCHAR *sectionW, *filenameW;
500 BOOL usedefault = TRUE;
501 HKEY sectionkey;
502 LONG ret = 0;
503
504 TRACE("%s %s %s %p %d %s\n", debugstr_a(section), debugstr_a(entry),
505 debugstr_a(defvalue), buff, buff_len, debugstr_a(filename));
506
507 clear_errors();
508
509 if (buff_len <= 0)
510 return 0;
511
512 if (buff)
513 buff[0] = 0;
514
515 if (!section || !defvalue || !buff)
516 return 0;
517
518 sectionW = heap_strdupAtoW(section);
519 filenameW = heap_strdupAtoW(filename);
520
521 sectionkey = get_privateprofile_sectionkey(sectionW, filenameW);
522
523 heap_free(sectionW);
524 heap_free(filenameW);
525
526 if (sectionkey)
527 {
528 DWORD type, size;
529
530 if (entry)
531 {
532 size = buff_len * sizeof(*buff);
533 if (RegGetValueA(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
534 {
535 usedefault = FALSE;
536 ret = (size / sizeof(*buff)) - 1;
537 }
538 }
539 else
540 {
541 char name[MAX_PATH] = {0};
542 DWORD index = 0;
543 DWORD namelen;
544
545 usedefault = FALSE;
546
547 memset(buff, 0, buff_len);
548
549 namelen = sizeof(name);
550 while (RegEnumValueA(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
551 {
552 if ((ret + namelen+1) > buff_len)
553 break;
554
555 lstrcpyA(buff+ret, name);
556
557 ret += namelen+1;
558 namelen = sizeof(name);
559 index++;
560 }
561 }
562
563 RegCloseKey(sectionkey);
564 }
565 else
566 usedefault = entry != NULL;
567
568 if (usedefault)
569 {
570 lstrcpynA(buff, defvalue, buff_len);
571 ret = strlen(buff);
572 }
573
574 return ret;
575 }
576
577 BOOL WINAPI SQLGetTranslatorW(HWND hwndParent, LPWSTR lpszName, WORD cbNameMax,
578 WORD *pcbNameOut, LPWSTR lpszPath, WORD cbPathMax,
579 WORD *pcbPathOut, DWORD *pvOption)
580 {
581 clear_errors();
582 FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_w(lpszName), cbNameMax,
583 pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
584 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
585 return FALSE;
586 }
587
588 BOOL WINAPI SQLGetTranslator(HWND hwndParent, LPSTR lpszName, WORD cbNameMax,
589 WORD *pcbNameOut, LPSTR lpszPath, WORD cbPathMax,
590 WORD *pcbPathOut, DWORD *pvOption)
591 {
592 clear_errors();
593 FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_a(lpszName), cbNameMax,
594 pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
595 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
596 return FALSE;
597 }
598
599 BOOL WINAPI SQLInstallDriverW(LPCWSTR lpszInfFile, LPCWSTR lpszDriver,
600 LPWSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
601 {
602 DWORD usage;
603
604 clear_errors();
605 TRACE("%s %s %p %d %p\n", debugstr_w(lpszInfFile),
606 debugstr_w(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
607
608 if (lpszInfFile)
609 return FALSE;
610
611 return SQLInstallDriverExW(lpszDriver, NULL, lpszPath, cbPathMax,
612 pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
613 }
614
615 BOOL WINAPI SQLInstallDriver(LPCSTR lpszInfFile, LPCSTR lpszDriver,
616 LPSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
617 {
618 DWORD usage;
619
620 clear_errors();
621 TRACE("%s %s %p %d %p\n", debugstr_a(lpszInfFile),
622 debugstr_a(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
623
624 if (lpszInfFile)
625 return FALSE;
626
627 return SQLInstallDriverEx(lpszDriver, NULL, lpszPath, cbPathMax,
628 pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
629 }
630
631 BOOL WINAPI SQLInstallDriverExW(LPCWSTR lpszDriver, LPCWSTR lpszPathIn,
632 LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
633 WORD fRequest, LPDWORD lpdwUsageCount)
634 {
635 UINT len;
636 LPCWSTR p;
637 WCHAR path[MAX_PATH];
638
639 clear_errors();
640 TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszDriver),
641 debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
642 fRequest, lpdwUsageCount);
643
644 for (p = lpszDriver; *p; p += lstrlenW(p) + 1)
645 TRACE("%s\n", debugstr_w(p));
646
647 len = GetSystemDirectoryW(path, MAX_PATH);
648
649 if (pcbPathOut)
650 *pcbPathOut = len;
651
652 len = GetSystemDirectoryW(path, MAX_PATH);
653
654 if (lpszPathOut && cbPathOutMax > len)
655 {
656 lstrcpyW(lpszPathOut, path);
657 return TRUE;
658 }
659 return FALSE;
660 }
661
662 BOOL WINAPI SQLInstallDriverEx(LPCSTR lpszDriver, LPCSTR lpszPathIn,
663 LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
664 WORD fRequest, LPDWORD lpdwUsageCount)
665 {
666 LPCSTR p;
667 LPWSTR driver, pathin;
668 WCHAR pathout[MAX_PATH];
669 BOOL ret;
670 WORD cbOut = 0;
671
672 clear_errors();
673 TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszDriver),
674 debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
675 fRequest, lpdwUsageCount);
676
677 for (p = lpszDriver; *p; p += lstrlenA(p) + 1)
678 TRACE("%s\n", debugstr_a(p));
679
680 driver = SQLInstall_strdup_multi(lpszDriver);
681 pathin = SQLInstall_strdup(lpszPathIn);
682
683 ret = SQLInstallDriverExW(driver, pathin, pathout, MAX_PATH, &cbOut,
684 fRequest, lpdwUsageCount);
685 if (ret)
686 {
687 int len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
688 0, NULL, NULL);
689 if (len)
690 {
691 if (pcbPathOut)
692 *pcbPathOut = len - 1;
693
694 if (!lpszPathOut || cbPathOutMax < len)
695 {
696 ret = FALSE;
697 goto out;
698 }
699 len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
700 cbPathOutMax, NULL, NULL);
701 }
702 }
703
704 out:
705 HeapFree(GetProcessHeap(), 0, driver);
706 HeapFree(GetProcessHeap(), 0, pathin);
707 return ret;
708 }
709
710 BOOL WINAPI SQLInstallDriverManagerW(LPWSTR lpszPath, WORD cbPathMax,
711 WORD *pcbPathOut)
712 {
713 UINT len;
714 WCHAR path[MAX_PATH];
715
716 TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
717
718 if (cbPathMax < MAX_PATH)
719 return FALSE;
720
721 clear_errors();
722
723 len = GetSystemDirectoryW(path, MAX_PATH);
724
725 if (pcbPathOut)
726 *pcbPathOut = len;
727
728 if (lpszPath && cbPathMax > len)
729 {
730 lstrcpyW(lpszPath, path);
731 return TRUE;
732 }
733 return FALSE;
734 }
735
736 BOOL WINAPI SQLInstallDriverManager(LPSTR lpszPath, WORD cbPathMax,
737 WORD *pcbPathOut)
738 {
739 BOOL ret;
740 WORD len, cbOut = 0;
741 WCHAR path[MAX_PATH];
742
743 TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
744
745 if (cbPathMax < MAX_PATH)
746 return FALSE;
747
748 clear_errors();
749
750 ret = SQLInstallDriverManagerW(path, MAX_PATH, &cbOut);
751 if (ret)
752 {
753 len = WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath, 0,
754 NULL, NULL);
755 if (len)
756 {
757 if (pcbPathOut)
758 *pcbPathOut = len - 1;
759
760 if (!lpszPath || cbPathMax < len)
761 return FALSE;
762
763 len = WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath,
764 cbPathMax, NULL, NULL);
765 }
766 }
767 return ret;
768 }
769
770 BOOL WINAPI SQLInstallODBCW(HWND hwndParent, LPCWSTR lpszInfFile,
771 LPCWSTR lpszSrcPath, LPCWSTR lpszDrivers)
772 {
773 clear_errors();
774 FIXME("%p %s %s %s\n", hwndParent, debugstr_w(lpszInfFile),
775 debugstr_w(lpszSrcPath), debugstr_w(lpszDrivers));
776 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
777 return FALSE;
778 }
779
780 BOOL WINAPI SQLInstallODBC(HWND hwndParent, LPCSTR lpszInfFile,
781 LPCSTR lpszSrcPath, LPCSTR lpszDrivers)
782 {
783 clear_errors();
784 FIXME("%p %s %s %s\n", hwndParent, debugstr_a(lpszInfFile),
785 debugstr_a(lpszSrcPath), debugstr_a(lpszDrivers));
786 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
787 return FALSE;
788 }
789
790 SQLRETURN WINAPI SQLInstallerErrorW(WORD iError, DWORD *pfErrorCode,
791 LPWSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
792 {
793 TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
794 cbErrorMsgMax, pcbErrorMsg);
795
796 if (iError == 0)
797 {
798 return SQL_ERROR;
799 }
800 else if (iError <= num_errors)
801 {
802 BOOL truncated = FALSE;
803 WORD len;
804 LPCWSTR msg;
805 iError--;
806 if (pfErrorCode)
807 *pfErrorCode = error_code[iError];
808 msg = error_msg[iError];
809 len = msg ? lstrlenW(msg) : 0;
810 if (pcbErrorMsg)
811 *pcbErrorMsg = len;
812 len++;
813 if (cbErrorMsgMax < len)
814 {
815 len = cbErrorMsgMax;
816 truncated = TRUE;
817 }
818 if (lpszErrorMsg && len)
819 {
820 if (msg)
821 {
822 memcpy (lpszErrorMsg, msg, len * sizeof(WCHAR));
823 }
824 else
825 {
826 assert(len==1);
827 *lpszErrorMsg = 0;
828 }
829 }
830 else
831 {
832 /* Yes. If you pass a null pointer and a large length it is not an error! */
833 truncated = TRUE;
834 }
835
836 return truncated ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
837 }
838
839 /* At least on Windows 2000 , the buffers are not altered in this case. However that is a little too dangerous a test for just now */
840 if (pcbErrorMsg)
841 *pcbErrorMsg = 0;
842
843 if (lpszErrorMsg && cbErrorMsgMax > 0)
844 *lpszErrorMsg = '\0';
845
846 return SQL_NO_DATA;
847 }
848
849 SQLRETURN WINAPI SQLInstallerError(WORD iError, DWORD *pfErrorCode,
850 LPSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
851 {
852 SQLRETURN ret;
853 LPWSTR wbuf;
854 WORD cbwbuf;
855 TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
856 cbErrorMsgMax, pcbErrorMsg);
857
858 wbuf = 0;
859 if (lpszErrorMsg && cbErrorMsgMax)
860 {
861 wbuf = HeapAlloc(GetProcessHeap(), 0, cbErrorMsgMax*sizeof(WCHAR));
862 if (!wbuf)
863 return SQL_ERROR;
864 }
865 ret = SQLInstallerErrorW(iError, pfErrorCode, wbuf, cbErrorMsgMax, &cbwbuf);
866 if (wbuf)
867 {
868 WORD cbBuf = 0;
869 SQLInstall_narrow(1, lpszErrorMsg, wbuf, cbwbuf+1, cbErrorMsgMax, &cbBuf);
870 HeapFree(GetProcessHeap(), 0, wbuf);
871 if (pcbErrorMsg)
872 *pcbErrorMsg = cbBuf-1;
873 }
874 return ret;
875 }
876
877 BOOL WINAPI SQLInstallTranslatorExW(LPCWSTR lpszTranslator, LPCWSTR lpszPathIn,
878 LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
879 WORD fRequest, LPDWORD lpdwUsageCount)
880 {
881 UINT len;
882 LPCWSTR p;
883 WCHAR path[MAX_PATH];
884
885 clear_errors();
886 TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszTranslator),
887 debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
888 fRequest, lpdwUsageCount);
889
890 for (p = lpszTranslator; *p; p += lstrlenW(p) + 1)
891 TRACE("%s\n", debugstr_w(p));
892
893 len = GetSystemDirectoryW(path, MAX_PATH);
894
895 if (pcbPathOut)
896 *pcbPathOut = len;
897
898 if (lpszPathOut && cbPathOutMax > len)
899 {
900 lstrcpyW(lpszPathOut, path);
901 return TRUE;
902 }
903 return FALSE;
904 }
905
906 BOOL WINAPI SQLInstallTranslatorEx(LPCSTR lpszTranslator, LPCSTR lpszPathIn,
907 LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
908 WORD fRequest, LPDWORD lpdwUsageCount)
909 {
910 LPCSTR p;
911 LPWSTR translator, pathin;
912 WCHAR pathout[MAX_PATH];
913 BOOL ret;
914 WORD cbOut = 0;
915
916 clear_errors();
917 TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszTranslator),
918 debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
919 fRequest, lpdwUsageCount);
920
921 for (p = lpszTranslator; *p; p += lstrlenA(p) + 1)
922 TRACE("%s\n", debugstr_a(p));
923
924 translator = SQLInstall_strdup_multi(lpszTranslator);
925 pathin = SQLInstall_strdup(lpszPathIn);
926
927 ret = SQLInstallTranslatorExW(translator, pathin, pathout, MAX_PATH,
928 &cbOut, fRequest, lpdwUsageCount);
929 if (ret)
930 {
931 int len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
932 0, NULL, NULL);
933 if (len)
934 {
935 if (pcbPathOut)
936 *pcbPathOut = len - 1;
937
938 if (!lpszPathOut || cbPathOutMax < len)
939 {
940 ret = FALSE;
941 goto out;
942 }
943 len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
944 cbPathOutMax, NULL, NULL);
945 }
946 }
947
948 out:
949 HeapFree(GetProcessHeap(), 0, translator);
950 HeapFree(GetProcessHeap(), 0, pathin);
951 return ret;
952 }
953
954 BOOL WINAPI SQLInstallTranslator(LPCSTR lpszInfFile, LPCSTR lpszTranslator,
955 LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax,
956 WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
957 {
958 clear_errors();
959 TRACE("%s %s %s %p %d %p %d %p\n", debugstr_a(lpszInfFile),
960 debugstr_a(lpszTranslator), debugstr_a(lpszPathIn), lpszPathOut,
961 cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
962
963 if (lpszInfFile)
964 return FALSE;
965
966 return SQLInstallTranslatorEx(lpszTranslator, lpszPathIn, lpszPathOut,
967 cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
968 }
969
970 BOOL WINAPI SQLInstallTranslatorW(LPCWSTR lpszInfFile, LPCWSTR lpszTranslator,
971 LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax,
972 WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
973 {
974 clear_errors();
975 TRACE("%s %s %s %p %d %p %d %p\n", debugstr_w(lpszInfFile),
976 debugstr_w(lpszTranslator), debugstr_w(lpszPathIn), lpszPathOut,
977 cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
978
979 if (lpszInfFile)
980 return FALSE;
981
982 return SQLInstallTranslatorExW(lpszTranslator, lpszPathIn, lpszPathOut,
983 cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
984 }
985
986 BOOL WINAPI SQLManageDataSources(HWND hwnd)
987 {
988 clear_errors();
989 FIXME("%p\n", hwnd);
990 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
991 return FALSE;
992 }
993
994 SQLRETURN WINAPI SQLPostInstallerErrorW(DWORD fErrorCode, LPCWSTR szErrorMsg)
995 {
996 FIXME("%u %s\n", fErrorCode, debugstr_w(szErrorMsg));
997 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
998 return FALSE;
999 }
1000
1001 SQLRETURN WINAPI SQLPostInstallerError(DWORD fErrorCode, LPCSTR szErrorMsg)
1002 {
1003 FIXME("%u %s\n", fErrorCode, debugstr_a(szErrorMsg));
1004 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1005 return FALSE;
1006 }
1007
1008 BOOL WINAPI SQLReadFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1009 LPCWSTR lpszKeyName, LPWSTR lpszString, WORD cbString,
1010 WORD *pcbString)
1011 {
1012 clear_errors();
1013 FIXME("%s %s %s %s %d %p\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1014 debugstr_w(lpszKeyName), debugstr_w(lpszString), cbString, pcbString);
1015 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1016 return FALSE;
1017 }
1018
1019 BOOL WINAPI SQLReadFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1020 LPCSTR lpszKeyName, LPSTR lpszString, WORD cbString,
1021 WORD *pcbString)
1022 {
1023 clear_errors();
1024 FIXME("%s %s %s %s %d %p\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1025 debugstr_a(lpszKeyName), debugstr_a(lpszString), cbString, pcbString);
1026 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1027 return FALSE;
1028 }
1029
1030 BOOL WINAPI SQLRemoveDefaultDataSource(void)
1031 {
1032 clear_errors();
1033 FIXME("\n");
1034 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1035 return FALSE;
1036 }
1037
1038 BOOL WINAPI SQLRemoveDriverW(LPCWSTR lpszDriver, BOOL fRemoveDSN,
1039 LPDWORD lpdwUsageCount)
1040 {
1041 clear_errors();
1042 FIXME("%s %d %p\n", debugstr_w(lpszDriver), fRemoveDSN, lpdwUsageCount);
1043 if (lpdwUsageCount) *lpdwUsageCount = 1;
1044 return TRUE;
1045 }
1046
1047 BOOL WINAPI SQLRemoveDriver(LPCSTR lpszDriver, BOOL fRemoveDSN,
1048 LPDWORD lpdwUsageCount)
1049 {
1050 clear_errors();
1051 FIXME("%s %d %p\n", debugstr_a(lpszDriver), fRemoveDSN, lpdwUsageCount);
1052 if (lpdwUsageCount) *lpdwUsageCount = 1;
1053 return TRUE;
1054 }
1055
1056 BOOL WINAPI SQLRemoveDriverManager(LPDWORD pdwUsageCount)
1057 {
1058 clear_errors();
1059 FIXME("%p\n", pdwUsageCount);
1060 if (pdwUsageCount) *pdwUsageCount = 1;
1061 return TRUE;
1062 }
1063
1064 BOOL WINAPI SQLRemoveDSNFromIniW(LPCWSTR lpszDSN)
1065 {
1066 clear_errors();
1067 FIXME("%s\n", debugstr_w(lpszDSN));
1068 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1069 return FALSE;
1070 }
1071
1072 BOOL WINAPI SQLRemoveDSNFromIni(LPCSTR lpszDSN)
1073 {
1074 clear_errors();
1075 FIXME("%s\n", debugstr_a(lpszDSN));
1076 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1077 return FALSE;
1078 }
1079
1080 BOOL WINAPI SQLRemoveTranslatorW(LPCWSTR lpszTranslator, LPDWORD lpdwUsageCount)
1081 {
1082 clear_errors();
1083 FIXME("%s %p\n", debugstr_w(lpszTranslator), lpdwUsageCount);
1084 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1085 return FALSE;
1086 }
1087
1088 BOOL WINAPI SQLRemoveTranslator(LPCSTR lpszTranslator, LPDWORD lpdwUsageCount)
1089 {
1090 clear_errors();
1091 FIXME("%s %p\n", debugstr_a(lpszTranslator), lpdwUsageCount);
1092 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1093 return FALSE;
1094 }
1095
1096 BOOL WINAPI SQLSetConfigMode(UWORD wConfigMode)
1097 {
1098 clear_errors();
1099 TRACE("%u\n", wConfigMode);
1100
1101 if (wConfigMode > ODBC_SYSTEM_DSN)
1102 {
1103 push_error(ODBC_ERROR_INVALID_PARAM_SEQUENCE, odbc_error_invalid_param_sequence);
1104 return FALSE;
1105 }
1106 else
1107 {
1108 config_mode = wConfigMode;
1109 return TRUE;
1110 }
1111 }
1112
1113 BOOL WINAPI SQLValidDSNW(LPCWSTR lpszDSN)
1114 {
1115 clear_errors();
1116 FIXME("%s\n", debugstr_w(lpszDSN));
1117 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1118 return FALSE;
1119 }
1120
1121 BOOL WINAPI SQLValidDSN(LPCSTR lpszDSN)
1122 {
1123 clear_errors();
1124 FIXME("%s\n", debugstr_a(lpszDSN));
1125 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1126 return FALSE;
1127 }
1128
1129 BOOL WINAPI SQLWriteDSNToIniW(LPCWSTR lpszDSN, LPCWSTR lpszDriver)
1130 {
1131 clear_errors();
1132 FIXME("%s %s\n", debugstr_w(lpszDSN), debugstr_w(lpszDriver));
1133 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1134 return FALSE;
1135 }
1136
1137 BOOL WINAPI SQLWriteDSNToIni(LPCSTR lpszDSN, LPCSTR lpszDriver)
1138 {
1139 clear_errors();
1140 FIXME("%s %s\n", debugstr_a(lpszDSN), debugstr_a(lpszDriver));
1141 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1142 return FALSE;
1143 }
1144
1145 BOOL WINAPI SQLWriteFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1146 LPCWSTR lpszKeyName, LPCWSTR lpszString)
1147 {
1148 clear_errors();
1149 FIXME("%s %s %s %s\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1150 debugstr_w(lpszKeyName), debugstr_w(lpszString));
1151 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1152 return FALSE;
1153 }
1154
1155 BOOL WINAPI SQLWriteFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1156 LPCSTR lpszKeyName, LPCSTR lpszString)
1157 {
1158 clear_errors();
1159 FIXME("%s %s %s %s\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1160 debugstr_a(lpszKeyName), debugstr_a(lpszString));
1161 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1162 return FALSE;
1163 }
1164
1165 BOOL WINAPI SQLWritePrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry,
1166 LPCWSTR lpszString, LPCWSTR lpszFilename)
1167 {
1168 LONG ret;
1169 HKEY hkey;
1170
1171 clear_errors();
1172 TRACE("%s %s %s %s\n", debugstr_w(lpszSection), debugstr_w(lpszEntry),
1173 debugstr_w(lpszString), debugstr_w(lpszFilename));
1174
1175 if(!lpszFilename || !*lpszFilename)
1176 {
1177 push_error(ODBC_ERROR_INVALID_STR, odbc_error_invalid_param_string);
1178 return FALSE;
1179 }
1180
1181 if ((ret = RegCreateKeyW(HKEY_CURRENT_USER, odbcW, &hkey)) == ERROR_SUCCESS)
1182 {
1183 HKEY hkeyfilename;
1184
1185 if ((ret = RegCreateKeyW(hkey, lpszFilename, &hkeyfilename)) == ERROR_SUCCESS)
1186 {
1187 HKEY hkey_section;
1188
1189 if ((ret = RegCreateKeyW(hkeyfilename, lpszSection, &hkey_section)) == ERROR_SUCCESS)
1190 {
1191 ret = RegSetValueExW(hkey_section, lpszEntry, 0, REG_SZ, (BYTE*)lpszString, (lstrlenW(lpszString)+1)*sizeof(WCHAR));
1192 RegCloseKey(hkey_section);
1193 }
1194
1195 RegCloseKey(hkeyfilename);
1196 }
1197
1198 RegCloseKey(hkey);
1199 }
1200
1201 return ret == ERROR_SUCCESS;
1202 }
1203
1204 BOOL WINAPI SQLWritePrivateProfileString(LPCSTR lpszSection, LPCSTR lpszEntry,
1205 LPCSTR lpszString, LPCSTR lpszFilename)
1206 {
1207 BOOL ret;
1208 WCHAR *sect, *entry, *string, *file;
1209 clear_errors();
1210 TRACE("%s %s %s %s\n", lpszSection, lpszEntry, lpszString, lpszFilename);
1211
1212 sect = heap_strdupAtoW(lpszSection);
1213 entry = heap_strdupAtoW(lpszEntry);
1214 string = heap_strdupAtoW(lpszString);
1215 file = heap_strdupAtoW(lpszFilename);
1216
1217 ret = SQLWritePrivateProfileStringW(sect, entry, string, file);
1218
1219 heap_free(sect);
1220 heap_free(entry);
1221 heap_free(string);
1222 heap_free(file);
1223
1224 return ret;
1225 }