LCOV - code coverage report
Current view: top level - clients/mapilib - msettings.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 361 426 84.7 %
Date: 2024-11-11 20:03:36 Functions: 39 41 95.1 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : 
      15             : #include "msettings.h"
      16             : #include "mstring.h"
      17             : 
      18             : #include <assert.h>
      19             : #include <ctype.h>
      20             : #include <limits.h>
      21             : #include <stdarg.h>
      22             : #include <stdio.h>
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : #ifdef HAVE_STRINGS_H
      26             : #include <strings.h>
      27             : #endif
      28             : 
      29             : #define FATAL() do { fprintf(stderr, "\n\n abort in msettings.c: %s\n\n", __func__); abort(); } while (0)
      30             : 
      31             : static const char * const MALLOC_FAILED = "malloc failed";
      32             : 
      33             : bool
      34           0 : msettings_malloc_failed(msettings_error err)
      35             : {
      36           0 :         return ((const char*)err == (const char*)MALLOC_FAILED);
      37             : }
      38             : 
      39             : 
      40             : 
      41         864 : int msetting_parse_bool(const char *text)
      42             : {
      43         864 :         static struct { const char *word; bool value; } variants[] = {
      44             :                 { "true", true },
      45             :                 { "false", false },
      46             :                 { "yes", true },
      47             :                 { "no", false },
      48             :                 { "on", true },
      49             :                 { "off", false },
      50             :         };
      51        4180 :         for (size_t i = 0; i < sizeof(variants) / sizeof(variants[0]); i++)
      52        4166 :                 if (strcasecmp(text, variants[i].word) == 0)
      53         850 :                         return variants[i].value;
      54             :         return -1;
      55             : }
      56             : 
      57             : char* allocprintf(const char *fmt, ...)
      58             :         __attribute__((__format__(__printf__, 1, 2)));
      59             : 
      60             : char *
      61         682 : allocprintf(const char *fmt, ...)
      62             : {
      63         682 :         va_list ap;
      64         682 :         char *buf = NULL;
      65         682 :         size_t pos = 0, cap = 0;
      66         682 :         int n;
      67             : 
      68         682 :         va_start(ap, fmt);
      69         682 :         n = vreallocprintf(&buf, &pos, &cap, fmt, ap);
      70         682 :         va_end(ap);
      71             : 
      72         682 :         if (n >= 0)
      73         682 :                 return buf;
      74           0 :         free(buf);
      75           0 :         return NULL;
      76             : }
      77             : 
      78             : 
      79             : 
      80             : 
      81             : static const struct { const char *name;  mparm parm; }
      82             : by_name[] = {
      83             :         { .name="autocommit", .parm=MP_AUTOCOMMIT },
      84             :         { .name="binary", .parm=MP_BINARY },
      85             :         { .name="cert", .parm=MP_CERT },
      86             :         { .name="certhash", .parm=MP_CERTHASH },
      87             :         { .name="client_application", .parm=MP_CLIENT_APPLICATION },
      88             :         { .name="client_info", .parm=MP_CLIENT_INFO },
      89             :         { .name="client_remark", .parm=MP_CLIENT_REMARK },
      90             :         { .name="clientcert", .parm=MP_CLIENTCERT },
      91             :         { .name="clientkey", .parm=MP_CLIENTKEY },
      92             :         { .name="connect_timeout", .parm=MP_CONNECT_TIMEOUT },
      93             :         { .name="database", .parm=MP_DATABASE },
      94             :         { .name="host", .parm=MP_HOST },
      95             :         { .name="language", .parm=MP_LANGUAGE },
      96             :         { .name="map_to_long_varchar", .parm=MP_MAPTOLONGVARCHAR },
      97             :         { .name="password", .parm=MP_PASSWORD },
      98             :         { .name="port", .parm=MP_PORT },
      99             :         { .name="reply_timeout", .parm=MP_REPLY_TIMEOUT },
     100             :         { .name="replysize", .parm=MP_REPLYSIZE },
     101             :         { .name="fetchsize", .parm=MP_REPLYSIZE },
     102             :         { .name="schema", .parm=MP_SCHEMA },
     103             :         { .name="sock", .parm=MP_SOCK },
     104             :         { .name="sockdir", .parm=MP_SOCKDIR},
     105             :         { .name="table", .parm=MP_TABLE },
     106             :         { .name="tableschema", .parm=MP_TABLESCHEMA },
     107             :         { .name="timezone", .parm=MP_TIMEZONE },
     108             :         { .name="tls", .parm=MP_TLS },
     109             :         { .name="user", .parm=MP_USER },
     110             :         //
     111             :         { .name="logfile", .parm=MP_LOGFILE },
     112             :         //
     113             :         { .name="hash", .parm=MP_IGNORE },
     114             :         { .name="debug", .parm=MP_IGNORE },
     115             : };
     116             : 
     117             : mparm
     118         521 : mparm_parse(const char *name)
     119             : {
     120         521 :         int n = sizeof(by_name) / sizeof(by_name[0]);
     121             :         // could use a binary search but this is not going to be a bottleneck
     122        7970 :         for (int i = 0; i < n; i++)
     123        7965 :                 if (strcmp(by_name[i].name, name) == 0)
     124         516 :                         return by_name[i].parm;
     125             : 
     126           5 :         return strchr(name, '_') ? MP_IGNORE : MP_UNKNOWN;
     127             : }
     128             : 
     129             : mparm
     130           0 : mparm_enumerate(int i)
     131             : {
     132           0 :         int n = sizeof(by_name) / sizeof(by_name[0]);
     133           0 :         if (i < 0 || i >= n)
     134             :                 return MP_UNKNOWN;
     135           0 :         return by_name[i].parm;
     136             : }
     137             : 
     138             : const char *
     139          24 : mparm_name(mparm parm)
     140             : {
     141          24 :         switch (parm) {
     142             :                 case MP_AUTOCOMMIT: return "autocommit";
     143           0 :                 case MP_BINARY: return "binary";
     144           0 :                 case MP_CERT: return "cert";
     145          10 :                 case MP_CERTHASH: return "certhash";
     146           0 :                 case MP_CLIENT_APPLICATION: return "client_application";
     147           0 :                 case MP_CLIENT_INFO: return "client_info";
     148           0 :                 case MP_CLIENT_REMARK: return "client_remark";
     149           0 :                 case MP_CLIENTCERT: return "clientcert";
     150           0 :                 case MP_CLIENTKEY: return "clientkey";
     151           0 :                 case MP_CONNECT_TIMEOUT: return "connect_timeout";
     152           1 :                 case MP_DATABASE: return "database";
     153           1 :                 case MP_HOST: return "host";
     154           0 :                 case MP_LANGUAGE: return "language";
     155           0 :                 case MP_LOGFILE: return "logfile";
     156           0 :                 case MP_MAPTOLONGVARCHAR: return "map_to_long_varchar";
     157           0 :                 case MP_PASSWORD: return "password";
     158           1 :                 case MP_PORT: return "port";
     159           0 :                 case MP_REPLY_TIMEOUT: return "reply_timeout";  // underscore present means specific to this client library
     160           2 :                 case MP_REPLYSIZE: return "replysize";  // no underscore means mandatory for all client libraries
     161           0 :                 case MP_SCHEMA: return "schema";
     162           0 :                 case MP_SOCK: return "sock";
     163           0 :                 case MP_SOCKDIR: return "sockdir";
     164           1 :                 case MP_TABLE: return "table";
     165           1 :                 case MP_TABLESCHEMA: return "tableschema";
     166           2 :                 case MP_TIMEZONE: return "timezone";
     167           1 :                 case MP_TLS: return "tls";
     168           0 :                 case MP_USER: return "user";
     169           0 :                 default: FATAL();
     170             :         }
     171             : }
     172             : 
     173             : bool
     174         960 : mparm_is_core(mparm parm)
     175             : {
     176         960 :         switch (parm) {
     177             :                 case MP_TLS:
     178             :                 case MP_HOST:
     179             :                 case MP_PORT:
     180             :                 case MP_DATABASE:
     181             :                 case MP_TABLESCHEMA:
     182             :                 case MP_TABLE:
     183             :                         return true;
     184         831 :                 default:
     185         831 :                         return false;
     186             :         }
     187             : }
     188             : 
     189             : struct string {
     190             :         char *str;
     191             :         bool must_free;
     192             : };
     193             : 
     194             : struct msettings {
     195             :         // Must match EXACTLY the order of enum mparm
     196             :         bool dummy_start_bool;
     197             :         bool tls;
     198             :         bool autocommit;
     199             :         bool client_info;
     200             :         bool dummy_end_bool;
     201             : 
     202             :         // Must match EXACTLY the order of enum mparm
     203             :         long dummy_start_long;
     204             :         long port;
     205             :         long timezone;
     206             :         long replysize;
     207             :         long map_to_long_varchar;
     208             :         long connect_timeout;
     209             :         long reply_timeout;
     210             :         long dummy_end_long;
     211             : 
     212             :         // Must match EXACTLY the order of enum mparm
     213             :         struct string dummy_start_string;
     214             :         struct string sock;
     215             :         struct string sockdir;
     216             :         struct string cert;
     217             :         struct string clientkey;
     218             :         struct string clientcert;
     219             :         struct string host;
     220             :         struct string database;
     221             :         struct string tableschema;
     222             :         struct string table;
     223             :         struct string certhash;
     224             :         struct string user;
     225             :         struct string password;
     226             :         struct string language;
     227             :         struct string schema;
     228             :         struct string binary;
     229             :         struct string logfile;
     230             :         struct string client_application;
     231             :         struct string client_remark;
     232             :         struct string dummy_end_string;
     233             : 
     234             :         char **unknown_parameters;
     235             :         size_t nr_unknown;
     236             : 
     237             :         bool lang_is_mal;
     238             :         bool lang_is_sql;
     239             :         long user_generation;
     240             :         long password_generation;
     241             :         char *unix_sock_name_buffer;
     242             :         char certhash_digits_buffer[64 + 2 + 1]; // fit more than required plus trailing '\0'
     243             :         bool validated;
     244             :         const char* (*localizer)(const void *data, mparm parm);
     245             :         void *localizer_data;
     246             :         char error_message[256];
     247             : };
     248             : 
     249             : static
     250             : const msettings msettings_default_values = {
     251             :         .tls = false,
     252             :         .autocommit = true,
     253             :         .client_info = true,
     254             : 
     255             :         .port = -1 ,
     256             :         .timezone = 0,
     257             :         .replysize = 100,
     258             : 
     259             :         .sockdir = { "/tmp", false },
     260             :         .binary = { "on", false },
     261             : 
     262             :         .unknown_parameters = NULL,
     263             :         .nr_unknown = 0,
     264             : 
     265             :         .lang_is_mal = false,
     266             :         .lang_is_sql = true,
     267             :         .unix_sock_name_buffer = NULL,
     268             : 
     269             :         .validated = false,
     270             : };
     271             : 
     272             : const msettings *msettings_default = &msettings_default_values;
     273             : 
     274         580 : msettings *msettings_create(void)
     275             : {
     276         580 :         msettings *mp = malloc(sizeof(*mp));
     277         580 :         if (!mp) {
     278             :                 return NULL;
     279             :         }
     280         580 :         *mp = msettings_default_values;
     281         580 :         return mp;
     282             : }
     283             : 
     284          87 : msettings *msettings_clone(const msettings *orig)
     285             : {
     286          87 :         msettings *mp = malloc(sizeof(*mp));
     287          87 :         char **unknowns = orig->nr_unknown > 0 ? calloc(2 * orig->nr_unknown, sizeof(char*)) : NULL;
     288          87 :         const char *namebuf = orig->unix_sock_name_buffer;
     289          87 :         char *cloned_name_buffer = namebuf ? strdup(namebuf) : NULL;
     290          87 :         if (!mp || (orig->nr_unknown > 0 && !unknowns) || (namebuf && !cloned_name_buffer)) {
     291           0 :                 free(mp);
     292           0 :                 free(unknowns);
     293           0 :                 free(cloned_name_buffer);
     294           0 :                 return NULL;
     295             :         }
     296          87 :         *mp = *orig;
     297          87 :         mp->unknown_parameters = unknowns;
     298          87 :         mp->unix_sock_name_buffer = cloned_name_buffer;
     299             : 
     300             :         // now we have to very carefully duplicate the strings.
     301             :         // taking care to only free our own ones if that fails
     302             : 
     303          87 :         struct string *start = &mp->dummy_start_string;
     304          87 :         struct string *end = &mp->dummy_end_string;
     305          87 :         struct string *p = start;
     306        1740 :         while (p < end) {
     307        1653 :                 if (p->must_free) {
     308         154 :                         p->str = strdup(p->str);
     309         154 :                         if (p->str == NULL)
     310           0 :                                 goto bailout;
     311             :                 }
     312        1653 :                 p++;
     313             :         }
     314             : 
     315          87 :         for (size_t i = 0; i < 2 * mp->nr_unknown; i++) {
     316           0 :                 assert(orig->unknown_parameters[i]);
     317           0 :                 char *u = strdup(orig->unknown_parameters[i]);
     318           0 :                 if (u == NULL)
     319           0 :                         goto bailout;
     320           0 :                 mp->unknown_parameters[i] = u;
     321             :         }
     322             : 
     323             :         return mp;
     324             : 
     325           0 : bailout:
     326           0 :         for (struct string *q = start; q < p; q++)
     327           0 :                 if (q->must_free)
     328           0 :                         free(q->str);
     329           0 :         for (size_t i = 0; i < 2 * mp->nr_unknown; i++)
     330           0 :                 free(mp->unknown_parameters[i]);
     331           0 :         free(mp->unix_sock_name_buffer);
     332           0 :         free(mp);
     333           0 :         return NULL;
     334             : }
     335             : 
     336             : void
     337          32 : msettings_reset(msettings *mp)
     338             : {
     339             :         // free modified string settings
     340          32 :         struct string *start = &mp->dummy_start_string;
     341          32 :         struct string *end = &mp->dummy_end_string;
     342         640 :         for (struct string *p = start; p < end; p++) {
     343         608 :                 if (p->must_free)
     344         131 :                         free(p->str);
     345             :         }
     346             : 
     347             :         // free unknown parameters
     348          32 :         if (mp->nr_unknown) {
     349           0 :                 for (size_t i = 0; i < 2 * mp->nr_unknown; i++)
     350           0 :                         free(mp->unknown_parameters[i]);
     351           0 :                 free(mp->unknown_parameters);
     352             :         }
     353             : 
     354             :         // free the buffer
     355          32 :         free(mp->unix_sock_name_buffer);
     356             : 
     357             :         // keep the localizer
     358          32 :         void *localizer = mp->localizer;
     359          32 :         void *localizer_data = mp->localizer_data;
     360             : 
     361             :         // now overwrite the whole thing
     362          32 :         *mp = *msettings_default;
     363          32 :         mp->localizer = localizer;
     364          32 :         mp->localizer_data = localizer_data;
     365          32 : }
     366             : 
     367             : msettings *
     368         692 : msettings_destroy(msettings *mp)
     369             : {
     370         692 :         if (mp == NULL)
     371             :                 return NULL;
     372             : 
     373       12236 :         for (struct string *p = &mp->dummy_start_string + 1; p < &mp->dummy_end_string; p++) {
     374       11592 :                 if (p->must_free)
     375        2431 :                         free(p->str);
     376             :         }
     377         650 :         for (size_t i = 0; i < mp->nr_unknown; i++) {
     378           6 :                 free(mp->unknown_parameters[2 * i]);
     379           6 :                 free(mp->unknown_parameters[2 * i + 1]);
     380             :         }
     381         644 :         free(mp->unknown_parameters);
     382         644 :         free(mp->unix_sock_name_buffer);
     383         644 :         free(mp);
     384             : 
     385         644 :         return NULL;
     386             : }
     387             : 
     388             : static const char *format_error(msettings *mp, const char *fmt, ...)
     389             :         __attribute__((__format__(__printf__, 2, 3)));
     390             : 
     391             : static const char *
     392          25 : format_error(msettings *mp, const char *fmt, ...)
     393             : {
     394          25 :         va_list ap;
     395          25 :         va_start(ap, fmt);
     396          25 :         vsnprintf(mp->error_message, sizeof(mp->error_message), fmt, ap);
     397          25 :         va_end(ap);
     398             : 
     399          25 :         return mp->error_message;
     400             : }
     401             : 
     402          24 : const char *msetting_parm_name(const msettings *mp, mparm parm)
     403             : {
     404          24 :         const char *localized = NULL;
     405          24 :         if (mp->localizer)
     406           0 :                 localized = (mp->localizer)(mp->localizer_data, parm);
     407          24 :         return localized ? localized : mparm_name(parm);
     408             : }
     409             : 
     410          41 : void msettings_set_localizer(msettings *mp, const char* (*localizer)(const void *data, mparm parm), void *data)
     411             : {
     412          41 :         mp->localizer = localizer;
     413          41 :         mp->localizer_data = data;
     414          41 : }
     415             : 
     416             : 
     417             : 
     418             : const char*
     419       30353 : msetting_string(const msettings *mp, mparm parm)
     420             : {
     421       30353 :         if (mparm_classify(parm) != MPCLASS_STRING) {
     422           1 :                 FATAL();
     423             :                 return "";
     424             :         }
     425       30352 :         int i = parm - MP__STRING_START;
     426       30352 :         struct string const *p = &mp->dummy_start_string + 1 + i;
     427       30352 :         if (p >=  &mp->dummy_end_string) {
     428           0 :                 FATAL();
     429             :                 return "";
     430             :         }
     431       30352 :         char *s = p->str;
     432             : 
     433       30352 :         if (s == NULL) {
     434       15858 :                 if (parm == MP_LANGUAGE)
     435             :                         return "sql";
     436       15812 :                 else if (parm == MP_BINARY)
     437             :                         return "on";
     438       15812 :                 return "";
     439             :         }
     440             :         return s;
     441             : }
     442             : 
     443             : 
     444             : msettings_error
     445        3847 : msetting_set_string(msettings *mp, mparm parm, const char* value)
     446             : {
     447        3847 :         assert(value != NULL);
     448             : 
     449        3847 :         if (mparm_classify(parm) != MPCLASS_STRING)
     450           0 :                 FATAL();
     451        3848 :         int i = parm - MP__STRING_START;
     452        3848 :         struct string *p = &mp->dummy_start_string + 1 + i;
     453        3848 :         if (p >=  &mp->dummy_end_string)
     454           0 :                 FATAL();
     455             : 
     456        3848 :         char *v = strdup(value);
     457        3848 :         if (!v)
     458             :                 return MALLOC_FAILED;
     459        3848 :         if (p->must_free)
     460        1323 :                 free(p->str);
     461        3848 :         p->str = v;
     462        3848 :         p->must_free = true;
     463             : 
     464        3848 :         switch (parm) {
     465         475 :                 case MP_USER:
     466         475 :                         mp->user_generation++;
     467         475 :                         break;
     468         713 :                 case MP_PASSWORD:
     469         713 :                         mp->password_generation++;
     470         713 :                         break;
     471         412 :                 case MP_LANGUAGE:
     472         412 :                         mp->lang_is_mal = false;
     473         412 :                         mp->lang_is_sql = false;
     474             :                         // Tricky logic, a mixture of strstr==val and strcmp
     475             :                         // strstr==val is a clever way to compute 'startswith'
     476         412 :                         if (strcmp(value, "mal") == 0 || strcmp(value, "msql") == 0)
     477         201 :                                 mp->lang_is_mal = true;
     478         211 :                         else if (strstr(value, "sql") == value)
     479         209 :                                 mp->lang_is_sql = true;
     480             :                         break;
     481             :                 default:
     482             :                         break;
     483             :         }
     484             : 
     485        3848 :         mp->validated = false;
     486        3848 :         return NULL;
     487             : }
     488             : 
     489             : 
     490             : long
     491       47289 : msetting_long(const msettings *mp, mparm parm)
     492             : {
     493       47289 :         if (mparm_classify(parm) != MPCLASS_LONG)
     494           0 :                 FATAL();
     495       47289 :         int i = parm - MP__LONG_START;
     496       47289 :         const long * p = &mp->dummy_start_long + 1 + i;
     497       47289 :         if (p >=  &mp->dummy_end_long)
     498           0 :                 FATAL();
     499             : 
     500       47289 :         return *p;
     501             : }
     502             : 
     503             : 
     504             : msettings_error
     505        1611 : msetting_set_long(msettings *mp, mparm parm, long value)
     506             : {
     507        1611 :         if (mparm_classify(parm) != MPCLASS_LONG)
     508           0 :                 FATAL();
     509        1611 :         int i = parm - MP__LONG_START;
     510        1611 :         long *p = &mp->dummy_start_long + 1 + i;
     511        1611 :         if (p >=  &mp->dummy_end_long)
     512           0 :                 FATAL();
     513             : 
     514        1611 :         *p = value;
     515             : 
     516        1611 :         mp->validated = false;
     517        1611 :         return NULL;
     518             : }
     519             : 
     520             : 
     521             : bool
     522        8383 : msetting_bool(const msettings *mp, mparm parm)
     523             : {
     524        8383 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     525           0 :                 FATAL();
     526        8383 :         int i = parm - MP__BOOL_START;
     527        8383 :         const bool *p = &mp->dummy_start_bool + 1 + i;
     528        8383 :         if (p >=  &mp->dummy_end_bool)
     529           0 :                 FATAL();
     530        8383 :         return *p;
     531             : }
     532             : 
     533             : 
     534             : msettings_error
     535        1067 : msetting_set_bool(msettings *mp, mparm parm, bool value)
     536             : {
     537        1067 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     538           0 :                 FATAL();
     539        1067 :         int i = parm - MP__BOOL_START;
     540        1067 :         bool *p = &mp->dummy_start_bool + 1 + i;
     541        1067 :         if (p >=  &mp->dummy_end_bool)
     542           0 :                 FATAL();
     543        1067 :         *p = value;
     544             : 
     545        1067 :         mp->validated = false;
     546        1067 :         return NULL;
     547             : }
     548             : 
     549             : msettings_error
     550        1279 : msetting_parse(msettings *mp, mparm parm, const char *text)
     551             : {
     552        1279 :         int b; // int not bool because we need to allow for parse errors
     553        1279 :         switch (mparm_classify(parm)) {
     554             :                 case MPCLASS_BOOL:
     555          26 :                         b = msetting_parse_bool(text);
     556          26 :                         if (b < 0)
     557           4 :                                 return format_error(mp, "%s: invalid boolean value", msetting_parm_name(mp, parm));
     558          22 :                         return msetting_set_bool(mp, parm, b);
     559             :                 case MPCLASS_LONG:
     560          92 :                         if (text[0] == '\0')
     561           3 :                                 return format_error(mp, "%s: integer parameter cannot be empty string", msetting_parm_name(mp, parm));
     562          89 :                         char *end;
     563          89 :                         long l = strtol(text, &end, 10);
     564          89 :                         if (*end != '\0')
     565           1 :                                 return format_error(mp, "%s: invalid integer", msetting_parm_name(mp, parm));
     566          88 :                         return msetting_set_long(mp, parm, l);
     567             :                 case MPCLASS_STRING:
     568        1161 :                         return msetting_set_string(mp, parm, text);
     569             :                 default:
     570             :                         assert(0 && "unreachable");
     571             :                         return "internal error, unclassified parameter type";
     572             :         }
     573             : }
     574             : 
     575             : char *
     576        1634 : msetting_as_string(const msettings *mp, mparm parm)
     577             : {
     578        1634 :         bool b;
     579        1634 :         long l;
     580        1634 :         const char *s;
     581        1634 :         switch (mparm_classify(parm)) {
     582             :                 case MPCLASS_BOOL:
     583         215 :                         b = msetting_bool(mp, parm);
     584         259 :                         return strdup(b ? "true" : "false");
     585             :                 case MPCLASS_LONG:
     586         473 :                         l = msetting_long(mp, parm);
     587         473 :                         int n = 40;
     588         473 :                         char *buf = malloc(n);
     589         473 :                         if (!buf)
     590             :                                 return NULL;
     591         473 :                         snprintf(buf, n, "%ld", l);
     592         473 :                         return buf;
     593             :                 case MPCLASS_STRING:
     594         946 :                         s = msetting_string(mp, parm);
     595         946 :                         return strdup(s);
     596             :                 default:
     597             :                         assert(0 && "unreachable");
     598             :                         return NULL;
     599             :         }
     600             : }
     601             : 
     602             : msettings_error
     603           6 : msetting_set_ignored(msettings *mp, const char *key, const char *value)
     604             : {
     605           6 :         char *my_key = strdup(key);
     606           6 :         char *my_value = strdup(value);
     607             : 
     608           6 :         size_t n = mp->nr_unknown;
     609           6 :         size_t new_size = (2 * n + 2) * sizeof(char*);
     610           6 :         char **new_unknowns = realloc(mp->unknown_parameters, new_size);
     611             : 
     612           6 :         if (!my_key || !my_value || !new_unknowns) {
     613           0 :                 free(my_key);
     614           0 :                 free(my_value);
     615           0 :                 free(new_unknowns);
     616           0 :                 return MALLOC_FAILED;
     617             :         }
     618             : 
     619           6 :         new_unknowns[2 * n] = my_key;
     620           6 :         new_unknowns[2 * n + 1] = my_value;
     621           6 :         mp->unknown_parameters = new_unknowns;
     622           6 :         mp->nr_unknown += 1;
     623             : 
     624           6 :         return NULL;
     625             : }
     626             : 
     627             : /* store named parameter */
     628             : msettings_error
     629         283 : msetting_set_named(msettings *mp, bool allow_core, const char *key, const char *value)
     630             : {
     631         283 :         mparm parm = mparm_parse(key);
     632         283 :         if (parm == MP_UNKNOWN)
     633           1 :                 return format_error(mp, "%s: unknown parameter", key);
     634             : 
     635         282 :         if (parm == MP_IGNORE)
     636           6 :                 return msetting_set_ignored(mp, key, value);
     637             : 
     638         276 :         if (!allow_core && mparm_is_core(parm))
     639           6 :                 return format_error(mp, "%s: parameter not allowed here", msetting_parm_name(mp, parm));
     640             : 
     641         270 :         return msetting_parse(mp, parm, value);
     642             : }
     643             : 
     644             : 
     645             : static bool
     646        5769 : empty(const msettings *mp, mparm parm)
     647             : {
     648        5769 :         const char *value = msetting_string(mp, parm);
     649        5769 :         assert(value);
     650        5769 :         return *value == '\0';
     651             : }
     652             : 
     653             : static bool
     654        4278 : nonempty(const msettings *mp, mparm parm)
     655             : {
     656         677 :         return !empty(mp, parm);
     657             : }
     658             : 
     659             : static msettings_error
     660         681 : validate_certhash(msettings *mp)
     661             : {
     662         681 :         mp->certhash_digits_buffer[0] = '\0';
     663             : 
     664         681 :         const char *full_certhash = msetting_string(mp, MP_CERTHASH);
     665         681 :         const char *certhash = full_certhash;
     666         681 :         if (*certhash == '\0')
     667             :                 return NULL;
     668             : 
     669          28 :         if (strncmp(certhash, "sha256:", 7) == 0) {
     670          19 :                 certhash += 7;
     671             :         } else {
     672           9 :                 return format_error(mp, "%s: expected to start with 'sha256:'", msetting_parm_name(mp, MP_CERTHASH));
     673             :         }
     674             : 
     675          19 :         size_t i = 0;
     676         367 :         for (const char *r = certhash; *r != '\0'; r++) {
     677         349 :                 if (*r == ':')
     678          21 :                         continue;
     679         328 :                 if (!isxdigit(*r))
     680           1 :                         return format_error(mp, "%s: invalid hex digit", msetting_parm_name(mp, MP_CERTHASH));
     681         327 :                 if (i < sizeof(mp->certhash_digits_buffer) - 1)
     682         287 :                         mp->certhash_digits_buffer[i++] = tolower(*r);
     683             :         }
     684          18 :         mp->certhash_digits_buffer[i] = '\0';
     685          18 :         if (i == 0)
     686           0 :                 return format_error(mp, "%s: need at least one digit", msetting_parm_name(mp, MP_CERTHASH));
     687             : 
     688             :         return NULL;
     689             : }
     690             : 
     691             : static bool
     692        1976 : validate_identifier(const char *name)
     693             : {
     694        1976 :         int first = name[0];
     695        1976 :         if (first == '\0')
     696             :                 return true;
     697         561 :         if (first != '_' && !isalpha(first))
     698             :                 return false;
     699       10700 :         for (const char *p = name; *p; p++) {
     700       10159 :                 bool ok = (isalnum(*p) || *p == '.' || *p == '-' || *p == '_');
     701       10159 :                 if (!ok)
     702             :                         return false;
     703             :         }
     704             :         return true;
     705             : }
     706             : 
     707             : bool
     708        1867 : msettings_validate(msettings *mp, char **errmsg)
     709             : {
     710        1867 :         if (mp->validated)
     711             :                 return true;
     712             : 
     713             :         // 1. The parameters have the types listed in the table in [Section
     714             :         //    Parameters](#parameters).
     715             :         // (this has already been checked)
     716             : 
     717             :         // 2. At least one of **sock** and **host** must be empty.
     718         723 :         if (nonempty(mp, MP_SOCK) && nonempty(mp, MP_HOST)) {
     719           8 :                 *errmsg = allocprintf(
     720             :                         "With sock='%s', host must be 'localhost', not '%s'",
     721             :                         msetting_string(mp, MP_SOCK),
     722             :                         msetting_string(mp, MP_HOST));
     723           8 :                 return false;
     724             :         }
     725             : 
     726             :         // 3. The string parameter **binary** must either parse as a boolean or as a
     727             :         //    non-negative integer.
     728             :         // (pretend valid so we can use msettings_connect_binary() to see if it parses)
     729         689 :         mp->validated = true;
     730         689 :         long level = msettings_connect_binary(mp);
     731         689 :         mp->validated = false;
     732         689 :         if (level < 0) {
     733           4 :                 *errmsg = allocprintf("invalid value '%s' for parameter 'binary'", msetting_string(mp, MP_BINARY));
     734           4 :                 return false;
     735             :         }
     736             : 
     737             :         // 4. If **sock** is not empty, **tls** must be 'off'.
     738         685 :         if (nonempty(mp, MP_SOCK) && msetting_bool(mp, MP_TLS)) {
     739           4 :                 *errmsg = allocprintf("TLS cannot be used with Unix domain sockets");
     740           4 :                 return false;
     741             :         }
     742             : 
     743             :         // 5. If **certhash** is not empty, it must be of the form `sha256:hexdigits`
     744             :         //    where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons.
     745         681 :         const char *certhash_msg = validate_certhash(mp);
     746         681 :         if (certhash_msg) {
     747          10 :                 *errmsg = strdup(certhash_msg);
     748          10 :                 return false;
     749             :         }
     750             :         // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well.
     751        1322 :         if (nonempty(mp, MP_CERT) || nonempty(mp, MP_CERTHASH))
     752          32 :                 if (!msetting_bool(mp, MP_TLS)) {
     753           5 :                         *errmsg = strdup("'cert' and 'certhash' can only be used with monetdbs://");
     754           5 :                         return false;
     755             :                 }
     756             : 
     757             :         // 7. Parameters **database**, **tableschema** and **table** must consist only of
     758             :         //    upper- and lowercase letters, digits, dashes and underscores. They must not
     759             :         //    start with a dash.
     760         666 :         const char *database = msetting_string(mp, MP_DATABASE);
     761         666 :         if (!validate_identifier(database)) {
     762           8 :                 *errmsg = allocprintf("invalid database name '%s'", database);
     763           8 :                 return false;
     764             :         }
     765         658 :         const char *tableschema = msetting_string(mp, MP_TABLESCHEMA);
     766         658 :         if (!validate_identifier(tableschema)) {
     767           6 :                 *errmsg = allocprintf("invalid schema name '%s'", tableschema);
     768           6 :                 return false;
     769             :         }
     770         652 :         const char *table = msetting_string(mp, MP_TABLE);
     771         652 :         if (!validate_identifier(table)) {
     772           6 :                 *errmsg = allocprintf("invalid table name '%s'", table);
     773           6 :                 return false;
     774             :         }
     775             : 
     776             :         // 8. Parameter **port** must be -1 or in the range 1-65535.
     777         646 :         long port = msetting_long(mp, MP_PORT);
     778         646 :         bool port_ok = (port == -1 || (port >= 1 && port <= 65535));
     779         646 :         if (!port_ok) {
     780           3 :                 *errmsg = allocprintf("invalid port '%ld'", port);
     781           3 :                 return false;
     782             :         }
     783             : 
     784             :         // 9. If **clientcert** is set, **clientkey** must also be set.
     785         643 :         if (nonempty(mp, MP_CLIENTCERT) && empty(mp, MP_CLIENTKEY)) {
     786           1 :                 *errmsg = allocprintf("clientcert can only be set together with clientkey");
     787           1 :                 return false;
     788             :         }
     789             : 
     790             :         // compute this here so the getter function can take const msettings*
     791         642 :         const char *sockdir = msetting_string(mp, MP_SOCKDIR);
     792         642 :         long effective_port = msettings_connect_port(mp);
     793         642 :         free(mp->unix_sock_name_buffer);
     794         642 :         mp->unix_sock_name_buffer = allocprintf("%s/.s.monetdb.%ld", sockdir, effective_port);
     795         642 :         if (mp->unix_sock_name_buffer == NULL)
     796             :                 return false;
     797             : 
     798         642 :         mp->validated = true;
     799         642 :         return true;
     800             : }
     801             : 
     802             : bool
     803        1485 : msettings_connect_scan(const msettings *mp)
     804             : {
     805        1485 :         if (empty(mp, MP_DATABASE))
     806             :                 return false;
     807         455 :         if (nonempty(mp, MP_SOCK))
     808             :                 return false;
     809         450 :         if (nonempty(mp, MP_HOST))
     810             :                 return false;
     811          99 :         long port = msetting_long(mp, MP_PORT);
     812          99 :         if (port != -1)
     813             :                 return false;
     814          10 :         bool tls = msetting_bool(mp, MP_TLS);
     815          10 :         if (tls)
     816             :                 return false;
     817             : 
     818             :         return true;
     819             : }
     820             : 
     821             : const char *
     822        1575 : msettings_connect_unix(const msettings *mp)
     823             : {
     824        1575 :         assert(mp->validated);
     825        1575 :         const char *sock = msetting_string(mp, MP_SOCK);
     826        1575 :         const char *host = msetting_string(mp, MP_HOST);
     827        1575 :         bool tls = msetting_bool(mp, MP_TLS);
     828             : 
     829        1575 :         if (*sock)
     830             :                 return sock;
     831        1564 :         if (tls)
     832             :                 return "";
     833        1540 :         if (*host == '\0') {
     834             :                 // This was precomputed in msettings_validate(),
     835             :                 // {sockdir}/.s.monetdb.{port}
     836         176 :                 return mp->unix_sock_name_buffer;
     837             :         }
     838             :         return "";
     839             : }
     840             : 
     841             : 
     842             : const char *
     843        2870 : msettings_connect_tcp(const msettings *mp)
     844             : {
     845        2870 :         assert(mp->validated);
     846        2870 :         const char *sock = msetting_string(mp, MP_SOCK);
     847        2870 :         const char *host = msetting_string(mp, MP_HOST);
     848             :         // bool tls = msetting_bool(mp, MP_TLS);
     849             : 
     850        2870 :         if (*sock)
     851             :                 return "";
     852        2860 :         if (!*host)
     853         139 :                 return "localhost";
     854             :         return host;
     855             : }
     856             : 
     857             : long
     858        2036 : msettings_connect_port(const msettings *mp)
     859             : {
     860        2036 :         long port = msetting_long(mp, MP_PORT);
     861        2036 :         if (port == -1)
     862             :                 return 50000;
     863             :         else
     864        1871 :                 return port;
     865             : }
     866             : 
     867             : enum msetting_tls_verify
     868          36 : msettings_connect_tls_verify(const msettings *mp)
     869             : {
     870          36 :         assert(mp->validated);
     871          36 :         bool tls = msetting_bool(mp, MP_TLS);
     872          36 :         const char *cert = msetting_string(mp, MP_CERT);
     873          36 :         const char *certhash = msetting_string(mp, MP_CERTHASH);
     874             : 
     875          36 :         if (!tls)
     876             :                 return verify_none;
     877          35 :         if (*certhash) // certhash comes before cert
     878             :                 return verify_hash;
     879          24 :         if (*cert)
     880          17 :                 return verify_cert;
     881             :         return verify_system;
     882             : }
     883             : 
     884             : const char*
     885          15 : msettings_connect_clientkey(const msettings *mp)
     886             : {
     887          15 :         return msetting_string(mp, MP_CLIENTKEY);
     888             : }
     889             : 
     890             : const char*
     891          15 : msettings_connect_clientcert(const msettings *mp)
     892             : {
     893          15 :         const char *cert = msetting_string(mp, MP_CLIENTCERT);
     894          15 :         if (*cert)
     895             :                 return cert;
     896             :         else
     897          13 :                 return msetting_string(mp, MP_CLIENTKEY);
     898             : }
     899             : 
     900             : const char*
     901           4 : msettings_connect_certhash_digits(const msettings *mp)
     902             : {
     903           4 :         return mp->certhash_digits_buffer;
     904             : }
     905             : 
     906             : // also used as a validator, returns < 0 on invalid
     907             : long
     908         700 : msettings_connect_binary(const msettings *mp)
     909             : {
     910         700 :         const long sufficiently_large = 65535;
     911         700 :         const char *binary = msetting_string(mp, MP_BINARY);
     912             : 
     913             :         // may be bool
     914         700 :         int b = msetting_parse_bool(binary);
     915         700 :         if (b == 0)
     916             :                 return 0;
     917         694 :         if (b == 1)
     918             :                 return sufficiently_large;
     919          10 :         assert(b < 0);
     920             : 
     921          10 :         char *end;
     922          10 :         long level = strtol(binary, &end, 10);
     923          10 :         if (end != binary && *end == '\0')
     924           7 :                 return level;
     925             : 
     926             :         return -1;
     927             : }
     928             : 
     929             : 
     930             : /* automatically incremented each time the corresponding field is updated */
     931             : long
     932         778 : msettings_user_generation(const msettings *mp)
     933             : {
     934         778 :         return mp->user_generation;
     935             : }
     936             : 
     937             : /* automatically incremented each time the corresponding field is updated */
     938             : long
     939         778 : msettings_password_generation(const msettings *mp)
     940             : {
     941         778 :         return mp->password_generation;
     942             : }
     943             : 
     944             : 
     945             : bool
     946          12 : msettings_lang_is_mal(const msettings *mp)
     947             : {
     948          12 :         return mp->lang_is_mal;
     949             : }
     950             : 
     951             : bool
     952     1440337 : msettings_lang_is_sql(const msettings *mp)
     953             : {
     954     1440337 :         return mp->lang_is_sql;
     955             : }

Generated by: LCOV version 1.14