1 /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2                  2016 MariaDB Corporation AB
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17    MA 02111-1301, USA */
18 
19 #include <ma_global.h>
20 #include <ma_sys.h>
21 #include "ma_string.h"
22 #include <ctype.h>
23 #include "mariadb_ctype.h"
24 #include <mysql.h>
25 #include <ma_common.h>
26 #include <mariadb/ma_io.h>
27 
28 #ifdef _WIN32
29 #include <io.h>
30 #include "shlwapi.h"
31 
32 static const char *ini_exts[]= {"ini", "cnf", 0};
33 #define R_OK 4
34 #else
35 #include <unistd.h>
36 static const char *ini_exts[]= {"cnf", 0};
37 #endif
38 
39 char **configuration_dirs= NULL;
40 #define MAX_CONFIG_DIRS 6
41 
42 my_bool _mariadb_read_options(MYSQL *mysql,
43                               const char *config_dir,
44                               const char *config_file,
45                               const char *group,
46                               unsigned int recursion);
47 
add_cfg_dir(char ** cfg_dirs,const char * directory)48 static int add_cfg_dir(char **cfg_dirs, const char *directory)
49 {
50   int i;
51 
52   for (i = 0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++)
53     if (!strcmp(cfg_dirs[i], directory)) /* already present */
54       return 0;
55 
56   if (i < MAX_CONFIG_DIRS) {
57     cfg_dirs[i]= strdup(directory);
58     return 0;
59   }
60   return 1;
61 }
62 
release_configuration_dirs()63 void release_configuration_dirs()
64 {
65   if (configuration_dirs)
66   {
67     int i= 0;
68     while (configuration_dirs[i])
69       free(configuration_dirs[i++]);
70     free(configuration_dirs);
71   }
72 }
73 
get_default_configuration_dirs()74 char **get_default_configuration_dirs()
75 {
76 #ifdef _WIN32
77   char dirname[FN_REFLEN];
78 #endif
79   char *env;
80 
81   configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *));
82   if (!configuration_dirs)
83     goto end;
84 
85 #ifdef _WIN32
86   /* On Windows operating systems configuration files are stored in
87      1. System Windows directory
88      2. System directory
89      3. Windows directory
90      4. C:\
91   */
92 
93   if (!GetSystemWindowsDirectory(dirname, FN_REFLEN) ||
94       add_cfg_dir(configuration_dirs, dirname))
95     goto error;
96 
97   if (!GetWindowsDirectory(dirname, FN_REFLEN) ||
98       add_cfg_dir(configuration_dirs, dirname))
99     goto error;
100 
101   if (add_cfg_dir(configuration_dirs, "C:"))
102     goto error;
103 
104   if (GetModuleFileName(NULL, dirname, FN_REFLEN))
105   {
106     PathRemoveFileSpec(dirname);
107     if (add_cfg_dir(configuration_dirs, dirname))
108       goto error;
109   }
110 #else
111   /* on *nix platforms configuration files are stored in
112      1. SYSCONFDIR (if build happens inside server package, or
113         -DDEFAULT_SYSCONFDIR was specified
114      2. /etc
115      3. /etc/mysql
116   */
117 #ifdef DEFAULT_SYSCONFDIR
118   if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR))
119     goto error;
120 #else
121   if (add_cfg_dir(configuration_dirs, "/etc"))
122     goto error;
123   if (add_cfg_dir(configuration_dirs, "/etc/mysql"))
124     goto error;
125 #endif
126 #endif
127   /* CONC-537: Read configuration files from MYSQL_HOME directory only if
128      MARIADB_HOME was not set */
129   if (!(env= getenv("MARIADB_HOME")))
130     env= getenv("MYSQL_HOME");
131   if (env && add_cfg_dir(configuration_dirs, env))
132     goto error;
133 end:
134   return configuration_dirs;
135 error:
136   return NULL;
137 }
138 
139 extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
140 
is_group(char * ptr,const char ** groups)141 static my_bool is_group(char *ptr, const char **groups)
142 {
143   while (*groups)
144   {
145     if (!strcmp(ptr, *groups))
146       return 1;
147     groups++;
148   }
149   return 0;
150 }
151 
_mariadb_read_options_from_file(MYSQL * mysql,const char * config_file,const char * group,unsigned int recursion)152 static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
153                                                const char *config_file,
154                                                const char *group,
155                                                unsigned int recursion)
156 {
157   uint line=0;
158   my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
159   char buff[4096],*ptr,*end,*value, *key= 0, *optval;
160   MA_FILE *file= NULL;
161   my_bool rc= 1;
162   const char *groups[5]= {"client",
163                           "client-server",
164                           "client-mariadb",
165                           group,
166                           NULL};
167   my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
168 
169 
170   /* if a plugin registered a hook we will call this hook, otherwise
171    * default (_mariadb_set_conf_option) will be called */
172   if (mysql->options.extension && mysql->options.extension->set_option)
173     set_option= mysql->options.extension->set_option;
174   else
175     set_option= _mariadb_set_conf_option;
176 
177   if (!(file = ma_open(config_file, "r", NULL)))
178     goto err;
179 
180   while (ma_gets(buff,sizeof(buff)-1,file))
181   {
182     line++;
183     key= 0;
184     /* Ignore comment and empty lines */
185     for (ptr=buff ; isspace(*ptr) ; ptr++ );
186     if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
187     {
188       is_quoted= !is_quoted;
189       continue;
190     }
191     /* CONC- 327: !includedir and !include */
192     if (*ptr == '!')
193     {
194       char *val;
195       ptr++;
196       if (!(val= strchr(ptr, ' ')))
197         continue;
198       *val++= 0;
199       end= strchr(val, 0);
200       for ( ; isspace(end[-1]) ; end--) ;	/* Remove end space */
201       *end= 0;
202       if (!strcmp(ptr, "includedir"))
203         _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1);
204       else if (!strcmp(ptr, "include"))
205         _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1);
206       continue;
207     }
208     if (*ptr == '#' || *ptr == ';' || !*ptr)
209       continue;
210     is_escaped= (*ptr == '\\');
211     if (*ptr == '[')				/* Group name */
212     {
213       found_group=1;
214       if (!(end=(char *) strchr(++ptr,']')))
215       {
216         /* todo: set error */
217         goto err;
218       }
219       for ( ; isspace(end[-1]) ; end--) ;	/* Remove end space */
220       end[0]=0;
221       read_values= is_group(ptr, groups);
222       continue;
223     }
224     if (!found_group)
225     {
226       /* todo: set error */
227       goto err;
228     }
229     if (!read_values)
230       continue;
231     if (!(end=value=strchr(ptr,'=')))
232     {
233       end=strchr(ptr, '\0');				/* Option without argument */
234       set_option(mysql, ptr, NULL);
235     }
236     if (!key)
237       key= ptr;
238     for ( ; isspace(end[-1]) ; end--) ;
239     *end= 0;
240     if (value)
241     {
242       /* Remove pre- and end space */
243       char *value_end;
244       *value= 0;
245       value++;
246       ptr= value;
247       for ( ; isspace(*value); value++) ;
248       value_end=strchr(value, '\0');
249       *value_end= 0;
250       optval= ptr;
251       for ( ; isspace(value_end[-1]) ; value_end--) ;
252       /* remove possible quotes */
253       if (*value == '\'' || *value == '\"')
254       {
255         value++;
256         if (value_end[-1] == '\'' || value_end[-1] == '\"')
257           value_end--;
258       }
259       if (value_end < value)			/* Empty string */
260         value_end=value;
261       for ( ; value != value_end; value++)
262       {
263         if (*value == '\\' && value != value_end-1)
264         {
265           switch(*++value) {
266             case 'n':
267               *ptr++='\n';
268               break;
269             case 't':
270               *ptr++= '\t';
271               break;
272             case 'r':
273               *ptr++ = '\r';
274               break;
275             case 'b':
276               *ptr++ = '\b';
277               break;
278             case 's':
279               *ptr++= ' ';			/* space */
280               break;
281             case '\"':
282               *ptr++= '\"';
283               break;
284             case '\'':
285               *ptr++= '\'';
286               break;
287             case '\\':
288               *ptr++= '\\';
289               break;
290             default:				/* Unknown; Keep '\' */
291               *ptr++= '\\';
292               *ptr++= *value;
293               break;
294           }
295         }
296         else
297           *ptr++= *value;
298       }
299       *ptr=0;
300       set_option(mysql, key, optval);
301       key= optval= 0;
302     }
303   }
304   rc= 0;
305 
306 err:
307   if (file)
308     ma_close(file);
309   return rc;
310 }
311 
312 
_mariadb_read_options(MYSQL * mysql,const char * config_dir,const char * config_file,const char * group,unsigned int recursion)313 my_bool _mariadb_read_options(MYSQL *mysql,
314                               const char *config_dir,
315                               const char *config_file,
316                               const char *group,
317                               unsigned int recursion)
318 {
319   int i= 0,
320       exts,
321       errors= 0;
322   char filename[FN_REFLEN + 1];
323   unsigned int recursion_stop= 64;
324 #ifndef _WIN32
325   char *env;
326 #endif
327 
328   if (recursion >= recursion_stop)
329     return 1;
330 
331   if (config_file && config_file[0])
332     return _mariadb_read_options_from_file(mysql, config_file, group, recursion);
333 
334   if (config_dir && config_dir[0])
335   {
336     for (exts= 0; ini_exts[exts]; exts++)
337     {
338       snprintf(filename, FN_REFLEN,
339                "%s%cmy.%s", config_dir, FN_LIBCHAR, ini_exts[exts]);
340       if (!access(filename, R_OK))
341         errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
342     }
343     return errors;
344   }
345 
346   for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
347   {
348     for (exts= 0; ini_exts[exts]; exts++)
349     {
350       snprintf(filename, FN_REFLEN,
351                "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
352       if (!access(filename, R_OK))
353         errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
354     }
355   }
356 #ifndef _WIN32
357   /* special case: .my.cnf in Home directory */
358   if ((env= getenv("HOME")))
359   {
360     for (exts= 0; ini_exts[exts]; exts++)
361     {
362       snprintf(filename, FN_REFLEN,
363                "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
364       if (!access(filename, R_OK))
365         errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
366     }
367   }
368 #endif
369   return errors;
370 }
371