1 /*
2  *  misc.c
3  *
4  *  $Id$
5  *
6  *  Miscellaneous functions
7  *
8  *  The iODBC driver manager.
9  *
10  *  Copyright (C) 1996-2021 OpenLink Software <iodbc@openlinksw.com>
11  *  All Rights Reserved.
12  *
13  *  This software is released under the terms of either of the following
14  *  licenses:
15  *
16  *      - GNU Library General Public License (see LICENSE.LGPL)
17  *      - The BSD License (see LICENSE.BSD).
18  *
19  *  Note that the only valid version of the LGPL license as far as this
20  *  project is concerned is the original GNU Library General Public License
21  *  Version 2, dated June 1991.
22  *
23  *  While not mandated by the BSD license, any patches you make to the
24  *  iODBC source code may be contributed back into the iODBC project
25  *  at your discretion. Contributions will benefit the Open Source and
26  *  Data Access community as a whole. Submissions may be made at:
27  *
28  *      http://www.iodbc.org
29  *
30  *
31  *  GNU Library Generic Public License Version 2
32  *  ============================================
33  *  This library is free software; you can redistribute it and/or
34  *  modify it under the terms of the GNU Library General Public
35  *  License as published by the Free Software Foundation; only
36  *  Version 2 of the License dated June 1991.
37  *
38  *  This library is distributed in the hope that it will be useful,
39  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
40  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  *  Library General Public License for more details.
42  *
43  *  You should have received a copy of the GNU Library General Public
44  *  License along with this library; if not, write to the Free
45  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
46  *
47  *
48  *  The BSD License
49  *  ===============
50  *  Redistribution and use in source and binary forms, with or without
51  *  modification, are permitted provided that the following conditions
52  *  are met:
53  *
54  *  1. Redistributions of source code must retain the above copyright
55  *     notice, this list of conditions and the following disclaimer.
56  *  2. Redistributions in binary form must reproduce the above copyright
57  *     notice, this list of conditions and the following disclaimer in
58  *     the documentation and/or other materials provided with the
59  *     distribution.
60  *  3. Neither the name of OpenLink Software Inc. nor the names of its
61  *     contributors may be used to endorse or promote products derived
62  *     from this software without specific prior written permission.
63  *
64  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
65  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
66  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
67  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENLINK OR
68  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
69  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
70  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
71  *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
72  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
73  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
74  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75  */
76 
77 
78 #include <iodbc.h>
79 #include <odbcinst.h>
80 
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85 #include <sys/types.h>
86 #include <sys/stat.h>
87 
88 #include "inifile.h"
89 #include "misc.h"
90 
91 #ifdef _MAC
92 # include <getfpn.h>
93 #endif /* endif _MAC */
94 
95 WORD wSystemDSN = USERDSN_ONLY;
96 WORD configMode = ODBC_BOTH_DSN;
97 
98 
99 #if !defined(WINDOWS) && !defined(WIN32) && !defined(OS2) && !defined(macintosh)
100 # include <pwd.h>
101 # define UNIX_PWD
102 #endif
103 
104 
105 /*
106  * Algorithm for resolving an odbc.ini reference
107  *
108  * For UNIX :    1. Check for $ODBCINI variable, if exists return $ODBCINI.
109  *               2. Check for $HOME/.odbc.ini or ~/.odbc.ini file,
110  *                  if exists return it.
111  *               3. Check for SYS_ODBC_INI build variable, if exists return
112  *                  it. (ie : /etc/odbc.ini).
113  *               4. No odbc.ini presence, return NULL.
114  *
115  * For WINDOWS, WIN32, OS2 :
116  *               1. Check for the system odbc.ini file, if exists return it.
117  *               2. No odbc.ini presence, return NULL.
118  *
119  * For VMS:      1. Check for $ODBCINI variable, if exists return $ODBCINI.
120  *               2. Check for SYS$LOGIN:ODBC.INI file, if exists return it.
121  *               3. No odbc.ini presence, return NULL.
122  *
123  * For Mac:      1. On powerPC, file is ODBC Preferences PPC
124  *                  On 68k, file is ODBC Preferences
125  *               2. Check for ...:System Folder:Preferences:ODBC Preferences
126  *                  file, if exists return it.
127  *               3. No odbc.ini presence, return NULL.
128  *
129  * For MacX:     1. Check for $ODBCINI variable, if exists return $ODBCINI.
130  *               2. Check for $HOME/Library/ODBC/odbc.ini,
131  *                  if exists return it.
132  *               3. Check for SYS_ODBC_INI build variable, if exists return
133  *                  it. (ie : /etc/odbc.ini).
134  *               4. Check for /Library/ODBC/odbc.ini
135  *                  file, if exists return it.
136  *               5. No odbc.ini presence, return NULL.
137 **/
138 char *
_iodbcadm_getinifile(char * buf,int size,int bIsInst,int doCreate)139 _iodbcadm_getinifile (char *buf, int size, int bIsInst, int doCreate)
140 {
141 #ifdef _MAC
142   HParamBlockRec hp;
143   long fldrDid;
144   short fldrRef;
145   OSErr result;
146 #endif /* endif _MAC */
147   int i, j;
148   char *ptr;
149 
150 #ifdef _MAC
151 #  ifdef __POWERPC__
152   j = STRLEN (bIsInst ? ":ODBC Installer Preferences PPC" :
153       ":ODBC Preferences PPC") + 1;
154 #  else
155   j = STRLEN (bIsInst ? ":ODBC Installer Preferences" : ":ODBC Preferences") +
156       1;
157 #  endif /* endif __POWERPC__ */
158 #else
159   j = STRLEN (bIsInst ? "/odbcinst.ini" : "/odbc.ini") + 1;
160 #endif /* endif _MAC */
161 
162   if (size < j)
163     return NULL;
164 
165 #if !defined(UNIX_PWD)
166 #  ifdef _MAC
167   result =
168       FindFolder (kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
169       &fldrRef, &fldrDid);
170   if (result != noErr)
171     return NULL;
172   ptr = get_full_pathname (fldrDid, fldrRef);
173 
174   i = (ptr) ? STRLEN (ptr) : 0;
175   if (i == 0 || i > size - j)
176     {
177       if (ptr)
178 	free (ptr);
179       return NULL;
180     }
181 
182 #    ifdef __POWERPC__
183   STRCPY (buf, ptr);
184   STRCAT (buf,
185       bIsInst ? ":ODBC Installer Preferences PPC" : ":ODBC Preferences PPC");
186 #    else
187   STRCPY (buf, ptr);
188   STRCAT (buf, bIsInst ? ":ODBC Installer Preferences" : ":ODBC Preferences");
189 #    endif /* endif __POWERPC__ */
190 
191   if (doCreate)
192     {
193       hp.fileParam.ioCompletion = NULL;
194       hp.fileParam.ioVRefNum = fldrRef;
195       hp.fileParam.ioDirID = fldrDid;
196 #    ifdef __POWERPC__
197       hp.fileParam.ioNamePtr =
198 	  bIsInst ? "\pODBC Installer Preferences PPC" :
199 	  "\pODBC Preferences PPC";
200 #    else
201       hp.fileParam.ioNamePtr =
202 	  bIsInst ? "\pODBC Installer Preferences" : "\pODBC Preferences";
203 #    endif
204       PBHCreate (&hp, FALSE);
205     }
206 
207   free (ptr);
208 
209   return buf;
210 
211 #  else	/* else _MAC */
212 
213   /*
214    *  On Windows, there is only one place to look
215    */
216   i = GetWindowsDirectory ((LPSTR) buf, size);
217 
218   if (i == 0 || i > size - j)
219     return NULL;
220 
221   snprintf (buf + i, size - i, bIsInst ? "/odbcinst.ini" : "/odbc.ini");
222 
223   return buf;
224 #  endif /* endif _MAC */
225 #else
226   if (wSystemDSN == USERDSN_ONLY)
227     {
228       /*
229        *  1. Check $ODBCINI environment variable
230        */
231       if ((ptr = getenv (bIsInst ? "ODBCINSTINI" : "ODBCINI")) != NULL)
232 	{
233 	  STRNCPY (buf, ptr, size);
234 
235 	  if (access (buf, R_OK) == 0)
236 	    return buf;
237 	  else if (doCreate)
238 	    {
239 	      int f = open ((char *) buf, O_CREAT,
240 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
241 	      if (f != -1)
242 		{
243 		  close (f);
244 		  return buf;
245 		}
246 	    }
247 	}
248 
249 #  ifdef VMS
250       /*
251        *  2a. VMS calls this HOME
252        */
253       STRNCPY (buf, bIsInst ? "SYS$LOGIN:ODBCINST.INI" : "SYS$LOGIN:ODBC.INI",
254 	  size);
255 
256       if (doCreate || access (buf, R_OK) == 0)
257 	return buf;
258 #  else	/* else VMS */
259 
260       if ((ptr = getenv ("HOME")) == NULL)
261 	{
262 	  ptr = (char *) getpwuid (getuid ());
263 
264 	  if (ptr != NULL)
265 	    ptr = ((struct passwd *) ptr)->pw_dir;
266 	}
267 
268       if (ptr != NULL)
269         {
270 #if defined(__APPLE__)
271 	  /*
272 	   * 2b. Try to check the ~/Library/ODBC/odbc.ini
273 	   */
274 	  snprintf (buf, size,
275 	      bIsInst ? "%s" ODBCINST_INI_APP : "%s" ODBC_INI_APP, ptr);
276 
277 	  if (access (buf, R_OK) == 0)
278 	    return buf;
279 	  else if (doCreate)
280 	    {
281 	      int f = open ((char *) buf, O_CREAT,
282 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
283 	      if (f != -1)
284 		{
285 		  close (f);
286 		  return buf;
287 		}
288 	    }
289 #   else /* else __APPLE__ */
290 
291           /*
292            *  2b. Check either $HOME/.odbc.ini or ~/.odbc.ini
293            */
294 	  snprintf (buf, size, bIsInst ? "%s/.odbcinst.ini" : "%s/.odbc.ini",
295 	      ptr);
296 
297 	  if (doCreate || access (buf, R_OK) == 0)
298 	    return buf;
299 #   endif /* endif __APPLE__ */
300 #  endif /* endif VMS */
301         }
302     }
303 
304   /*
305    *  3. Try SYS_ODBC_INI as the last resort
306    */
307   if (wSystemDSN == SYSTEMDSN_ONLY || bIsInst)
308     {
309       /*
310        *  1. Check $SYSODBCINI environment variable
311        */
312       if ((ptr = getenv (bIsInst ? "SYSODBCINSTINI" : "SYSODBCINI")) != NULL)
313 	{
314 	  STRNCPY (buf, ptr, size);
315 
316 	  if (access (buf, R_OK) == 0)
317 	    return buf;
318 	  else if (doCreate)
319 	    {
320 	      int f = open ((char *) buf, O_CREAT,
321 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
322 	      if (f != -1)
323 		{
324 		  close (f);
325 		  return buf;
326 		}
327 	    }
328 	}
329 
330 #if defined(__APPLE__)
331       /*
332        * Try to check the /Library/ODBC/odbc.ini
333        */
334       snprintf (buf, size, "%s", bIsInst ? ODBCINST_INI_APP : ODBC_INI_APP);
335 
336       if (access (buf, R_OK) == 0)
337 	return buf;
338       else if (doCreate)
339 	{
340 	  int f = open ((char *) buf, O_CREAT,
341 	      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
342 	  if (f != -1)
343 	    {
344 	      close (f);
345 	      return buf;
346 	    }
347 	}
348 #   endif /* endif __APPLE__ */
349 
350       STRNCPY (buf, bIsInst ? SYS_ODBCINST_INI : SYS_ODBC_INI, size);
351       return buf;
352     }
353 
354   /*
355    *  No ini file found or accessible
356    */
357   return NULL;
358 #endif /* UNIX_PWD */
359 }
360 
361 const char *
_iodbcdm_check_for_string(const char * szList,const char * szString,int bContains)362 _iodbcdm_check_for_string(const char *szList, const char *szString, int bContains)
363 {
364   const char *currP = szList;
365 
366   while (*currP)
367     {
368       if (bContains)
369 	{
370 	  if (strstr (currP, szString))
371 	    return currP;
372 	}
373       else
374 	{
375 	  if (!strcmp (currP, szString))
376 	    return currP;
377 	}
378     }
379   return NULL;
380 }
381 
382 
383 char *
_iodbcdm_remove_quotes(const char * szString)384 _iodbcdm_remove_quotes(const char *szString)
385 {
386   char *szWork, *szPtr;
387 
388   while (*szString == '\'' || *szString == '\"')
389     szString += 1;
390 
391   if (!*szString)
392     return NULL;
393   szWork = strdup (szString);
394   szPtr = strchr (szWork, '\'');
395   if (szPtr)
396     *szPtr = 0;
397   szPtr = strchr (szWork, '\"');
398   if (szPtr)
399     *szPtr = 0;
400 
401   return szWork;
402 }
403 
404 /*
405  * Get FILEDSN file name
406  *
407  * If file name does not contain path component, the default directory for
408  * saving and loading a .dsn file will be defined as follows:
409  * 1. if FILEDSNPATH is set in environment: use environment
410  * 2. if odbcinst.ini [odbc] FILEDSNPATH is set: use this
411  * 3. else use DEFAULT_FILEDSNPATH
412  *
413  * ".dsn" extension is always appended to the resulting filename
414  * (if not already exists).
415  */
416 void
_iodbcdm_getdsnfile(const char * filedsn,char * buf,size_t buf_sz)417 _iodbcdm_getdsnfile(const char *filedsn, char *buf, size_t buf_sz)
418 {
419   char *p;
420   char *default_path;
421 
422   if (strchr (filedsn, '/') != NULL)
423     {
424       /* has path component -- copy as is */
425       _iodbcdm_strlcpy (buf, filedsn, buf_sz);
426       goto done;
427     }
428 
429   /* get default path */
430   if ((default_path = getenv ("FILEDSNPATH")) != NULL)
431     _iodbcdm_strlcpy (buf, default_path, buf_sz);
432   else
433     {
434       SQLSetConfigMode (ODBC_BOTH_DSN);
435       if (!SQLGetPrivateProfileString ("ODBC", "FileDSNPath", "",
436 				       buf, buf_sz, "odbcinst.ini"))
437         _iodbcdm_strlcpy (buf, DEFAULT_FILEDSNPATH, buf_sz);
438     }
439 
440   /* append filedsn file name */
441   _iodbcdm_strlcat (buf, "/", buf_sz);
442   _iodbcdm_strlcat (buf, filedsn, buf_sz);
443 
444 done:
445   /* append ".dsn" if extension is not ".dsn" */
446   if ((p = strrchr (buf, '.')) == NULL ||
447       strcasecmp (p, ".dsn") != 0)
448     _iodbcdm_strlcat (buf, ".dsn", buf_sz);
449 }
450 
451 
452 /*
453  * Copy src to string dst of size siz.  At most siz-1 characters
454  * will be copied.  Always NUL terminates (unless siz == 0).
455  * Returns strlen(src); if retval >= siz, truncation occurred.
456  *
457  * Taken from FreeBSD libc.
458  */
459 size_t
_iodbcdm_strlcpy(char * dst,const char * src,size_t siz)460 _iodbcdm_strlcpy(char *dst, const char *src, size_t siz)
461 {
462   char *d = dst;
463   const char *s = src;
464   size_t n = siz;
465 
466   /* Copy as many bytes as will fit */
467   if (n != 0 && --n != 0)
468     {
469       do {
470         if ((*d++ = *s++) == 0)
471           break;
472       } while (--n != 0);
473     }
474 
475   /* Not enough room in dst, add NUL and traverse rest of src */
476   if (n == 0)
477     {
478       if (siz != 0)
479         *d = '\0';		/* NUL-terminate dst */
480       while (*s++)
481         ;
482     }
483 
484    return(s - src - 1);		/* count does not include NUL */
485 }
486 
487 
488 /*
489  * Appends src to string dst of size siz (unlike strncat, siz is the
490  * full size of dst, not space left).  At most siz-1 characters
491  * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
492  * Returns strlen(src) + MIN(siz, strlen(initial dst)).
493  * If retval >= siz, truncation occurred.
494  *
495  * Taken from FreeBSD libc.
496  */
497 size_t
_iodbcdm_strlcat(char * dst,const char * src,size_t siz)498 _iodbcdm_strlcat(char *dst, const char *src, size_t siz)
499 {
500   char *d = dst;
501   const char *s = src;
502   size_t n = siz;
503   size_t dlen;
504 
505   /* Find the end of dst and adjust bytes left but don't go past end */
506   while (n-- != 0 && *d != '\0')
507     d++;
508   dlen = d - dst;
509   n = siz - dlen;
510 
511   if (n == 0)
512     return(dlen + strlen(s));
513   while (*s != '\0')
514     {
515       if (n != 1)
516         {
517           *d++ = *s;
518           n--;
519         }
520       s++;
521     }
522   *d = '\0';
523 
524   return(dlen + (s - src));	/* count does not include NUL */
525 }
526