LCOV - code coverage report
Current view: top level - clients/mapilib - msettings.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 353 392 90.1 %
Date: 2025-03-25 21:27:32 Functions: 41 42 97.6 %

          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, 2025 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 "msettings_internal.h"
      17             : #include "mstring.h"
      18             : 
      19             : #include <assert.h>
      20             : #include <ctype.h>
      21             : #include <limits.h>
      22             : #include <stdarg.h>
      23             : #include <stdio.h>
      24             : #include <stdlib.h>
      25             : #include <string.h>
      26             : #ifdef HAVE_STRINGS_H
      27             : #include <strings.h>
      28             : #endif
      29             : 
      30             : #define FATAL() do { fprintf(stderr, "\n\n abort in msettings.c: %s\n\n", __func__); abort(); } while (0)
      31             : 
      32             : const char MALLOC_FAILED[] = "malloc failed";
      33             : 
      34             : bool
      35           0 : msettings_malloc_failed(msettings_error err)
      36             : {
      37           0 :         return (err == MALLOC_FAILED);
      38             : }
      39             : 
      40             : 
      41        1595 : int msetting_parse_bool(const char *text)
      42             : {
      43        1595 :         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        7674 :         for (size_t i = 0; i < sizeof(variants) / sizeof(variants[0]); i++)
      52        7646 :                 if (strcasecmp(text, variants[i].word) == 0)
      53        1567 :                         return variants[i].value;
      54             :         return -1;
      55             : }
      56             : 
      57             : 
      58             : static const struct { const char *name;  mparm parm; }
      59             : by_name[] = {
      60             :         { .name="autocommit", .parm=MP_AUTOCOMMIT },
      61             :         { .name="binary", .parm=MP_BINARY },
      62             :         { .name="cert", .parm=MP_CERT },
      63             :         { .name="certhash", .parm=MP_CERTHASH },
      64             :         { .name="client_application", .parm=MP_CLIENT_APPLICATION },
      65             :         { .name="client_info", .parm=MP_CLIENT_INFO },
      66             :         { .name="client_remark", .parm=MP_CLIENT_REMARK },
      67             :         { .name="clientcert", .parm=MP_CLIENTCERT },
      68             :         { .name="clientkey", .parm=MP_CLIENTKEY },
      69             :         { .name="connect_timeout", .parm=MP_CONNECT_TIMEOUT },
      70             :         { .name="database", .parm=MP_DATABASE },
      71             :         { .name="host", .parm=MP_HOST },
      72             :         { .name="language", .parm=MP_LANGUAGE },
      73             :         { .name="map_to_long_varchar", .parm=MP_MAPTOLONGVARCHAR },
      74             :         { .name="password", .parm=MP_PASSWORD },
      75             :         { .name="port", .parm=MP_PORT },
      76             :         { .name="reply_timeout", .parm=MP_REPLY_TIMEOUT },
      77             :         { .name="replysize", .parm=MP_REPLYSIZE },
      78             :         { .name="fetchsize", .parm=MP_REPLYSIZE },
      79             :         { .name="schema", .parm=MP_SCHEMA },
      80             :         { .name="sock", .parm=MP_SOCK },
      81             :         { .name="sockdir", .parm=MP_SOCKDIR},
      82             :         { .name="table", .parm=MP_TABLE },
      83             :         { .name="tableschema", .parm=MP_TABLESCHEMA },
      84             :         { .name="timezone", .parm=MP_TIMEZONE },
      85             :         { .name="tls", .parm=MP_TLS },
      86             :         { .name="user", .parm=MP_USER },
      87             :         //
      88             :         { .name="logfile", .parm=MP_LOGFILE },
      89             :         //
      90             :         { .name="hash", .parm=MP_IGNORE },
      91             :         { .name="debug", .parm=MP_IGNORE },
      92             : };
      93             : 
      94             : mparm
      95        1309 : mparm_parse(const char *name)
      96             : {
      97        1309 :         int n = sizeof(by_name) / sizeof(by_name[0]);
      98             :         // could use a binary search but this is not going to be a bottleneck
      99       20271 :         for (int i = 0; i < n; i++)
     100       20261 :                 if (strcmp(by_name[i].name, name) == 0)
     101        1299 :                         return by_name[i].parm;
     102             : 
     103          10 :         return strchr(name, '_') ? MP_IGNORE : MP_UNKNOWN;
     104             : }
     105             : 
     106             : mparm
     107      535432 : mparm_enumerate(int i)
     108             : {
     109      535432 :         int n = sizeof(by_name) / sizeof(by_name[0]);
     110      535432 :         if (i < 0 || i >= n)
     111             :                 return MP_UNKNOWN;
     112      518160 :         return by_name[i].parm;
     113             : }
     114             : 
     115             : const char *
     116       13750 : mparm_name(mparm parm)
     117             : {
     118       13750 :         switch (parm) {
     119             :                 case MP_AUTOCOMMIT: return "autocommit";
     120         556 :                 case MP_BINARY: return "binary";
     121        1032 :                 case MP_CERT: return "cert";
     122        1714 :                 case MP_CERTHASH: return "certhash";
     123           0 :                 case MP_CLIENT_APPLICATION: return "client_application";
     124           0 :                 case MP_CLIENT_INFO: return "client_info";
     125           0 :                 case MP_CLIENT_REMARK: return "client_remark";
     126         596 :                 case MP_CLIENTCERT: return "clientcert";
     127        1192 :                 case MP_CLIENTKEY: return "clientkey";
     128           0 :                 case MP_CONNECT_TIMEOUT: return "connect_timeout";
     129           2 :                 case MP_DATABASE: return "database";
     130           2 :                 case MP_HOST: return "host";
     131         368 :                 case MP_LANGUAGE: return "language";
     132         140 :                 case MP_LOGFILE: return "logfile";
     133           0 :                 case MP_MAPTOLONGVARCHAR: return "map_to_long_varchar";
     134        1486 :                 case MP_PASSWORD: return "password";
     135           2 :                 case MP_PORT: return "port";
     136           0 :                 case MP_REPLY_TIMEOUT: return "reply_timeout";  // underscore present means specific to this client library
     137        1732 :                 case MP_REPLYSIZE: return "replysize";  // no underscore means mandatory for all client libraries
     138         316 :                 case MP_SCHEMA: return "schema";
     139        1308 :                 case MP_SOCK: return "sock";
     140         312 :                 case MP_SOCKDIR: return "sockdir";
     141           2 :                 case MP_TABLE: return "table";
     142           2 :                 case MP_TABLESCHEMA: return "tableschema";
     143         504 :                 case MP_TIMEZONE: return "timezone";
     144           2 :                 case MP_TLS: return "tls";
     145        2252 :                 case MP_USER: return "user";
     146           0 :                 default: FATAL();
     147             :         }
     148             : }
     149             : 
     150             : bool
     151      475169 : mparm_is_core(mparm parm)
     152             : {
     153      475169 :         switch (parm) {
     154             :                 case MP_TLS:
     155             :                 case MP_HOST:
     156             :                 case MP_PORT:
     157             :                 case MP_DATABASE:
     158             :                 case MP_TABLESCHEMA:
     159             :                 case MP_TABLE:
     160             :                         return true;
     161      373658 :                 default:
     162      373658 :                         return false;
     163             :         }
     164             : }
     165             : 
     166             : static
     167             : const msettings msettings_default_values = {
     168             :         .tls = false,
     169             :         .autocommit = true,
     170             :         .client_info = true,
     171             : 
     172             :         .port = -1 ,
     173             :         .timezone = 0,
     174             :         .replysize = 100,
     175             : 
     176             :         .sockdir = { "/tmp", false },
     177             :         .binary = { "on", false },
     178             : 
     179             :         .lang_is_mal = false,
     180             :         .lang_is_sql = true,
     181             :         .unix_sock_name_buffer = NULL,
     182             : 
     183             :         .validated = false,
     184             : };
     185             : 
     186             : const msettings *msettings_default = &msettings_default_values;
     187             : 
     188        3004 : msettings *msettings_create_with(msettings_allocator alloc, void *allocator_state)
     189             : {
     190        3004 :         if (alloc == NULL) {
     191        1134 :                 allocator_state = NULL;
     192             :         }
     193             : 
     194        3004 :         msettings *mp = realloc_with_fallback(alloc, allocator_state, NULL, sizeof(*mp));
     195        3004 :         if (!mp)
     196             :                 return NULL;
     197             : 
     198        3004 :         *mp = msettings_default_values;
     199        3004 :         mp->alloc = alloc;
     200        3004 :         mp->alloc_state = allocator_state;
     201             : 
     202        3004 :         return mp;
     203             : }
     204             : 
     205         478 : msettings *msettings_create(void)
     206             : {
     207         478 :         return msettings_create_with(NULL, NULL);
     208             : }
     209             : 
     210             : msettings_allocator
     211         110 : msettings_get_allocator(const msettings *mp, void **put_alloc_state_here)
     212             : {
     213         110 :         if (mp->alloc == NULL)
     214             :                 return NULL;
     215           0 :         if (put_alloc_state_here)
     216           0 :                 *put_alloc_state_here = mp->alloc_state;
     217           0 :         return mp->alloc;
     218             : }
     219             : 
     220         295 : msettings *msettings_clone_with(msettings_allocator alloc, void *alloc_state, const msettings *orig)
     221             : {
     222         295 :         bool free_unix_sock_name_buffer = false;
     223         295 :         struct string *copy_start = NULL;
     224         295 :         struct string *copy_pos = NULL;
     225             : 
     226         295 :         msettings *mp = msettings_create_with(alloc, alloc_state);
     227         295 :         if (!mp)
     228             :                 return NULL;
     229             : 
     230             :         // Before we copy orig over mp, remember mp's allocator as decided by msettings_create_with.
     231         295 :         alloc = mp->alloc;
     232         295 :         alloc_state = mp->alloc_state;
     233             : 
     234             :         // Copy the whole struct, including the pointers to data owned by orig.
     235             :         // This means we must be really careful when we abort halfway and need to free 'mp'.
     236             :         // We will use 'start' and 'pos' to keep track of which strings must be free'd BY US
     237         295 :         *mp = *orig;
     238         295 :         mp->alloc = alloc;
     239         295 :         mp->alloc_state = alloc_state;
     240         295 :         copy_start = &mp->dummy_start_string;
     241         295 :         copy_pos = copy_start;
     242             : 
     243         295 :         if (orig->unix_sock_name_buffer) {
     244          96 :                 mp->unix_sock_name_buffer = msettings_strdup(mp, orig->unix_sock_name_buffer);
     245          96 :                 if (mp->unix_sock_name_buffer == NULL)
     246           0 :                         goto bailout;
     247         295 :                 free_unix_sock_name_buffer = true;
     248             :         }
     249             : 
     250             :         // Duplicate the strings that need to be duplicated
     251        5900 :         for (; copy_pos < &mp->dummy_end_string; copy_pos++) {
     252        5605 :                 if (copy_pos->must_free) {
     253         428 :                         copy_pos->str = msettings_strdup(mp, copy_pos->str);
     254         428 :                         if (copy_pos->str == NULL)
     255           0 :                                 goto bailout;
     256             :                 }
     257             :         }
     258             : 
     259             :         // Now all references to data allocated by 'orig' has been copied.
     260             :         return mp;
     261             : 
     262           0 : bailout:
     263           0 :         if (free_unix_sock_name_buffer)
     264           0 :                 msettings_dealloc(mp, mp->unix_sock_name_buffer);
     265           0 :         while (copy_start != copy_pos) {
     266           0 :                 msettings_dealloc(mp, copy_start->str);
     267           0 :                 copy_start++;
     268             :         }
     269           0 :         msettings_dealloc(mp, mp);
     270           0 :         return NULL;
     271             : }
     272             : 
     273         295 : msettings *msettings_clone(const msettings *orig)
     274             : {
     275         295 :         return msettings_clone_with(NULL, NULL, orig);
     276             : }
     277             : 
     278             : void
     279         102 : msettings_reset(msettings *mp)
     280             : {
     281             :         // free modified string settings
     282         102 :         struct string *start = &mp->dummy_start_string;
     283         102 :         struct string *end = &mp->dummy_end_string;
     284        2040 :         for (struct string *p = start; p < end; p++) {
     285        1938 :                 if (p->must_free)
     286         411 :                         msettings_dealloc(mp, p->str);
     287             :         }
     288             : 
     289             :         // free the buffer
     290         102 :         msettings_dealloc(mp, mp->unix_sock_name_buffer);
     291             : 
     292             :         // keep the localizer and the allocator
     293         102 :         void *localizer = mp->localizer;
     294         102 :         void *localizer_data = mp->localizer_data;
     295         102 :         msettings_allocator alloc = mp->alloc;
     296         102 :         void *alloc_state = mp->alloc_state;
     297             : 
     298             :         // now overwrite the whole thing
     299         102 :         *mp = *msettings_default;
     300         102 :         mp->localizer = localizer;
     301         102 :         mp->localizer_data = localizer_data;
     302         102 :         mp->alloc = alloc;
     303         102 :         mp->alloc_state = alloc_state;
     304         102 : }
     305             : 
     306             : msettings *
     307        1589 : msettings_destroy(msettings *mp)
     308             : {
     309        1589 :         if (mp == NULL)
     310             :                 return NULL;
     311             : 
     312       27949 :         for (struct string *p = &mp->dummy_start_string + 1; p < &mp->dummy_end_string; p++) {
     313       26478 :                 if (p->must_free)
     314        4350 :                         msettings_dealloc(mp, p->str);
     315             :         }
     316        1471 :         msettings_dealloc(mp, mp->unix_sock_name_buffer);
     317        1471 :         msettings_dealloc(mp, mp);
     318             : 
     319        1471 :         return NULL;
     320             : }
     321             : 
     322             : const char *
     323         242 : format_error(msettings *mp, const char *fmt, ...)
     324             : {
     325         242 :         va_list ap;
     326         242 :         va_start(ap, fmt);
     327         242 :         vsnprintf(mp->error_message, sizeof(mp->error_message), fmt, ap);
     328         242 :         va_end(ap);
     329             : 
     330         242 :         return mp->error_message;
     331             : }
     332             : 
     333          48 : const char *msetting_parm_name(const msettings *mp, mparm parm)
     334             : {
     335          48 :         const char *localized = NULL;
     336          48 :         if (mp->localizer)
     337           0 :                 localized = (mp->localizer)(mp->localizer_data, parm);
     338          48 :         return localized ? localized : mparm_name(parm);
     339             : }
     340             : 
     341         111 : void msettings_set_localizer(msettings *mp, const char* (*localizer)(const void *data, mparm parm), void *data)
     342             : {
     343         111 :         mp->localizer = localizer;
     344         111 :         mp->localizer_data = data;
     345         111 : }
     346             : 
     347             : 
     348             : 
     349             : const char*
     350      595712 : msetting_string(const msettings *mp, mparm parm)
     351             : {
     352      595712 :         if (mparm_classify(parm) != MPCLASS_STRING) {
     353           0 :                 FATAL();
     354             :                 return "";
     355             :         }
     356      595713 :         int i = parm - MP__STRING_START;
     357      595713 :         struct string const *p = &mp->dummy_start_string + 1 + i;
     358      595713 :         if (p >=  &mp->dummy_end_string) {
     359           0 :                 FATAL();
     360             :                 return "";
     361             :         }
     362      595713 :         char *s = p->str;
     363             : 
     364      595713 :         if (s == NULL) {
     365      466402 :                 if (parm == MP_LANGUAGE)
     366             :                         return "sql";
     367      432425 :                 else if (parm == MP_BINARY)
     368             :                         return "on";
     369      432425 :                 return "";
     370             :         }
     371             :         return s;
     372             : }
     373             : 
     374             : 
     375             : msettings_error
     376       15911 : msetting_set_string(msettings *mp, mparm parm, const char* value)
     377             : {
     378       15911 :         assert(value != NULL);
     379             : 
     380       15911 :         if (mparm_classify(parm) != MPCLASS_STRING)
     381           0 :                 FATAL();
     382       15911 :         int i = parm - MP__STRING_START;
     383       15911 :         struct string *p = &mp->dummy_start_string + 1 + i;
     384       15911 :         if (p >=  &mp->dummy_end_string)
     385           0 :                 FATAL();
     386             : 
     387       15911 :         char *v = msettings_strdup(mp, value);
     388       15914 :         if (!v)
     389             :                 return MALLOC_FAILED;
     390       15914 :         if (p->must_free)
     391        6289 :                 msettings_dealloc(mp, p->str);
     392       15914 :         p->str = v;
     393       15914 :         p->must_free = true;
     394             : 
     395       15914 :         switch (parm) {
     396         656 :                 case MP_USER:
     397         656 :                         mp->user_generation++;
     398         656 :                         break;
     399         927 :                 case MP_PASSWORD:
     400         927 :                         mp->password_generation++;
     401         927 :                         break;
     402         397 :                 case MP_LANGUAGE:
     403         397 :                         mp->lang_is_mal = false;
     404         397 :                         mp->lang_is_sql = false;
     405             :                         // Tricky logic, a mixture of strstr==val and strcmp
     406             :                         // strstr==val is a clever way to compute 'startswith'
     407         397 :                         if (strcmp(value, "mal") == 0 || strcmp(value, "msql") == 0)
     408         211 :                                 mp->lang_is_mal = true;
     409         186 :                         else if (strstr(value, "sql") == value)
     410         180 :                                 mp->lang_is_sql = true;
     411             :                         break;
     412             :                 default:
     413             :                         break;
     414             :         }
     415             : 
     416       15914 :         mp->validated = false;
     417       15914 :         return NULL;
     418             : }
     419             : 
     420             : 
     421             : long
     422      275018 : msetting_long(const msettings *mp, mparm parm)
     423             : {
     424      275018 :         if (mparm_classify(parm) != MPCLASS_LONG)
     425           0 :                 FATAL();
     426      275018 :         int i = parm - MP__LONG_START;
     427      275018 :         const long * p = &mp->dummy_start_long + 1 + i;
     428      275018 :         if (p >=  &mp->dummy_end_long)
     429           0 :                 FATAL();
     430             : 
     431      275018 :         return *p;
     432             : }
     433             : 
     434             : 
     435             : msettings_error
     436        5613 : msetting_set_long(msettings *mp, mparm parm, long value)
     437             : {
     438        5613 :         if (mparm_classify(parm) != MPCLASS_LONG)
     439           0 :                 FATAL();
     440        5613 :         int i = parm - MP__LONG_START;
     441        5613 :         long *p = &mp->dummy_start_long + 1 + i;
     442        5613 :         if (p >=  &mp->dummy_end_long)
     443           0 :                 FATAL();
     444             : 
     445        5613 :         *p = value;
     446             : 
     447        5613 :         mp->validated = false;
     448        5613 :         return NULL;
     449             : }
     450             : 
     451             : 
     452             : bool
     453       96324 : msetting_bool(const msettings *mp, mparm parm)
     454             : {
     455       96324 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     456           0 :                 FATAL();
     457       96324 :         int i = parm - MP__BOOL_START;
     458       96324 :         const bool *p = &mp->dummy_start_bool + 1 + i;
     459       96324 :         if (p >=  &mp->dummy_end_bool)
     460           0 :                 FATAL();
     461       96324 :         return *p;
     462             : }
     463             : 
     464             : 
     465             : msettings_error
     466        5360 : msetting_set_bool(msettings *mp, mparm parm, bool value)
     467             : {
     468        5360 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     469           0 :                 FATAL();
     470        5361 :         int i = parm - MP__BOOL_START;
     471        5361 :         bool *p = &mp->dummy_start_bool + 1 + i;
     472        5361 :         if (p >=  &mp->dummy_end_bool)
     473           0 :                 FATAL();
     474        5361 :         *p = value;
     475             : 
     476        5361 :         mp->validated = false;
     477        5361 :         return NULL;
     478             : }
     479             : 
     480             : msettings_error
     481        8256 : msetting_parse(msettings *mp, mparm parm, const char *text)
     482             : {
     483        8256 :         int b; // int not bool because we need to allow for parse errors
     484        8256 :         switch (mparm_classify(parm)) {
     485          54 :                 case MPCLASS_BOOL:
     486          54 :                         b = msetting_parse_bool(text);
     487          54 :                         if (b < 0)
     488           8 :                                 return format_error(mp, "%s: invalid boolean value", msetting_parm_name(mp, parm));
     489          46 :                         return msetting_set_bool(mp, parm, b);
     490         320 :                 case MPCLASS_LONG:
     491         320 :                         if (text[0] == '\0')
     492           6 :                                 return format_error(mp, "%s: integer parameter cannot be empty string", msetting_parm_name(mp, parm));
     493         314 :                         char *end;
     494         314 :                         long l = strtol(text, &end, 10);
     495         314 :                         if (*end != '\0')
     496           2 :                                 return format_error(mp, "%s: invalid integer", msetting_parm_name(mp, parm));
     497         312 :                         return msetting_set_long(mp, parm, l);
     498        7882 :                 case MPCLASS_STRING:
     499        7882 :                         return msetting_set_string(mp, parm, text);
     500             :                 default:
     501           0 :                         assert(0 && "unreachable");
     502             :                         return "internal error, unclassified parameter type";
     503             :         }
     504             : }
     505             : 
     506             : const char *
     507      772274 : msetting_as_string(const msettings *mp, mparm parm, char *scratch, size_t scratch_size)
     508             : {
     509      772274 :         long l;
     510      772274 :         switch (mparm_classify(parm)) {
     511       70895 :                 case MPCLASS_BOOL:
     512       70895 :                         return msetting_bool(mp, parm) ? "true" : "false";
     513      210169 :                 case MPCLASS_LONG:
     514      210169 :                         l = msetting_long(mp, parm);
     515      210169 :                         int n = snprintf(scratch, scratch_size, "%ld", l);
     516      210169 :                         if (n > 0 && scratch_size >= (size_t)n + 1)
     517             :                                 return scratch;
     518             :                         else
     519           0 :                                 return NULL;
     520      491210 :                 case MPCLASS_STRING:
     521      491210 :                         return msetting_string(mp, parm);
     522             :                 default:
     523           0 :                         assert(0 && "unreachable");
     524             :                         return NULL;
     525             :         }
     526             : }
     527             : 
     528             : /* store named parameter */
     529             : msettings_error
     530         838 : msetting_set_named(msettings *mp, bool allow_core, const char *key, const char *value)
     531             : {
     532         838 :         mparm parm = mparm_parse(key);
     533         838 :         if (parm == MP_UNKNOWN)
     534           2 :                 return format_error(mp, "%s: unknown parameter", key);
     535             : 
     536         836 :         if (parm == MP_IGNORE)
     537             :                 return NULL;
     538             : 
     539         824 :         if (!allow_core && mparm_is_core(parm))
     540          12 :                 return format_error(mp, "%s: parameter not allowed here", msetting_parm_name(mp, parm));
     541             : 
     542         812 :         return msetting_parse(mp, parm, value);
     543             : }
     544             : 
     545             : 
     546             : static bool
     547        8679 : empty(const msettings *mp, mparm parm)
     548             : {
     549        8679 :         const char *value = msetting_string(mp, parm);
     550        8679 :         assert(value);
     551        8679 :         return *value == '\0';
     552             : }
     553             : 
     554             : static bool
     555        7101 : nonempty(const msettings *mp, mparm parm)
     556             : {
     557        1241 :         return !empty(mp, parm);
     558             : }
     559             : 
     560             : static msettings_error
     561        1227 : validate_certhash(msettings *mp)
     562             : {
     563        1227 :         mp->certhash_digits_buffer[0] = '\0';
     564             : 
     565        1227 :         const char *full_certhash = msetting_string(mp, MP_CERTHASH);
     566        1227 :         const char *certhash = full_certhash;
     567        1227 :         if (*certhash == '\0')
     568             :                 return NULL;
     569             : 
     570          57 :         if (strncmp(certhash, "sha256:", 7) == 0) {
     571          39 :                 certhash += 7;
     572             :         } else {
     573          18 :                 return format_error(mp, "%s: expected to start with 'sha256:'", msetting_parm_name(mp, MP_CERTHASH));
     574             :         }
     575             : 
     576          39 :         size_t i = 0;
     577         741 :         for (const char *r = certhash; *r != '\0'; r++) {
     578         704 :                 if (*r == ':')
     579          42 :                         continue;
     580         662 :                 if (!isxdigit(*r))
     581           2 :                         return format_error(mp, "%s: invalid hex digit", msetting_parm_name(mp, MP_CERTHASH));
     582         660 :                 if (i < sizeof(mp->certhash_digits_buffer) - 1)
     583         580 :                         mp->certhash_digits_buffer[i++] = tolower(*r);
     584             :         }
     585          37 :         mp->certhash_digits_buffer[i] = '\0';
     586          37 :         if (i == 0)
     587           0 :                 return format_error(mp, "%s: need at least one digit", msetting_parm_name(mp, MP_CERTHASH));
     588             : 
     589             :         return NULL;
     590             : }
     591             : 
     592             : static bool
     593        3481 : validate_identifier(const char *name)
     594             : {
     595        3481 :         int first = name[0];
     596        3481 :         if (first == '\0')
     597             :                 return true;
     598         949 :         if (first != '_' && !isalpha(first))
     599             :                 return false;
     600       18268 :         for (const char *p = name; *p; p++) {
     601       17395 :                 bool ok = (isalnum(*p) || *p == '.' || *p == '-' || *p == '_');
     602       17395 :                 if (!ok)
     603             :                         return false;
     604             :         }
     605             :         return true;
     606             : }
     607             : 
     608             : msettings_error
     609        2697 : msettings_validate(msettings *mp)
     610             : {
     611        2697 :         if (mp->validated)
     612             :                 return NULL;
     613             : 
     614             :         // 1. The parameters have the types listed in the table in [Section
     615             :         //    Parameters](#parameters).
     616             :         // (this has already been checked)
     617             : 
     618             :         // 2. At least one of **sock** and **host** must be empty.
     619        1367 :         if (nonempty(mp, MP_SOCK) && nonempty(mp, MP_HOST)) {
     620          44 :                 return format_error(mp,
     621             :                         "With sock='%s', host must be 'localhost', not '%s'",
     622             :                         msetting_string(mp, MP_SOCK),
     623             :                         msetting_string(mp, MP_HOST));
     624             :         }
     625             : 
     626             :         // 3. The string parameter **binary** must either parse as a boolean or as a
     627             :         //    non-negative integer.
     628             :         // (pretend valid so we can use msettings_connect_binary() to see if it parses)
     629        1243 :         mp->validated = true;
     630        1243 :         long level = msettings_connect_binary(mp);
     631        1243 :         mp->validated = false;
     632        1243 :         if (level < 0) {
     633           8 :                 return format_error(mp, "invalid value '%s' for parameter 'binary'", msetting_string(mp, MP_BINARY));
     634             :         }
     635             : 
     636             :         // 4. If **sock** is not empty, **tls** must be 'off'.
     637        1235 :         if (nonempty(mp, MP_SOCK) && msetting_bool(mp, MP_TLS)) {
     638           8 :                 return format_error(mp, "TLS cannot be used with Unix domain sockets");
     639             :         }
     640             : 
     641             :         // 5. If **certhash** is not empty, it must be of the form `sha256:hexdigits`
     642             :         //    where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons.
     643        1227 :         const char *certhash_msg = validate_certhash(mp);
     644        1227 :         if (certhash_msg) {
     645          20 :                 return format_error(mp, "%s", certhash_msg);
     646             :         }
     647             : 
     648             :         // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well.
     649        2368 :         if (nonempty(mp, MP_CERT) || nonempty(mp, MP_CERTHASH))
     650          69 :                 if (!msetting_bool(mp, MP_TLS)) {
     651          20 :                         return format_error(mp, "'cert' and 'certhash' can only be used with monetdbs://");
     652             :                 }
     653             : 
     654             :         // 7. Parameters **database**, **tableschema** and **table** must consist only of
     655             :         //    upper- and lowercase letters, digits, dashes and underscores. They must not
     656             :         //    start with a dash.
     657        1187 :         const char *database = msetting_string(mp, MP_DATABASE);
     658        1187 :         if (!validate_identifier(database)) {
     659          28 :                 return format_error(mp, "invalid database name '%s'", database);
     660             :         }
     661        1159 :         const char *tableschema = msetting_string(mp, MP_TABLESCHEMA);
     662        1159 :         if (!validate_identifier(tableschema)) {
     663          24 :                 return format_error(mp, "invalid schema name '%s'", tableschema);
     664             :         }
     665        1135 :         const char *table = msetting_string(mp, MP_TABLE);
     666        1135 :         if (!validate_identifier(table)) {
     667          24 :                 return format_error(mp, "invalid table name '%s'", table);
     668             :         }
     669             : 
     670             :         // 8. Parameter **port** must be -1 or in the range 1-65535.
     671        1111 :         long port = msetting_long(mp, MP_PORT);
     672        1111 :         bool port_ok = (port == -1 || (port >= 1 && port <= 65535));
     673        1111 :         if (!port_ok) {
     674          12 :                 return format_error(mp, "invalid port '%ld'", port);
     675             :         }
     676             : 
     677             :         // 9. If **clientcert** is set, **clientkey** must also be set.
     678        1099 :         if (nonempty(mp, MP_CLIENTCERT) && empty(mp, MP_CLIENTKEY)) {
     679           4 :                 return format_error(mp, "clientcert can only be set together with clientkey");
     680             :         }
     681             : 
     682             :         // compute this here so the getter function can take const msettings*
     683        1095 :         const char *sockdir = msetting_string(mp, MP_SOCKDIR);
     684        1095 :         long effective_port = msettings_connect_port(mp);
     685        1095 :         msettings_dealloc(mp, mp->unix_sock_name_buffer);
     686        1095 :         mp->unix_sock_name_buffer = msettings_allocprintf(mp, "%s/.s.monetdb.%ld", sockdir, effective_port);
     687        1095 :         if (mp->unix_sock_name_buffer == NULL)
     688             :                 return false;
     689             : 
     690        1095 :         mp->validated = true;
     691        1095 :         return NULL;
     692             : }
     693             : 
     694             : bool
     695        1565 : msettings_connect_scan(const msettings *mp)
     696             : {
     697        1565 :         if (empty(mp, MP_DATABASE))
     698             :                 return false;
     699         521 :         if (nonempty(mp, MP_SOCK))
     700             :                 return false;
     701         511 :         if (nonempty(mp, MP_HOST))
     702             :                 return false;
     703         108 :         long port = msetting_long(mp, MP_PORT);
     704         108 :         if (port != -1)
     705             :                 return false;
     706          18 :         bool tls = msetting_bool(mp, MP_TLS);
     707          18 :         if (tls)
     708             :                 return false;
     709             : 
     710             :         return true;
     711             : }
     712             : 
     713             : const char *
     714        1654 : msettings_connect_unix(const msettings *mp)
     715             : {
     716        1654 :         assert(mp->validated);
     717        1654 :         const char *sock = msetting_string(mp, MP_SOCK);
     718        1654 :         const char *host = msetting_string(mp, MP_HOST);
     719        1654 :         bool tls = msetting_bool(mp, MP_TLS);
     720             : 
     721        1654 :         if (*sock)
     722             :                 return sock;
     723        1632 :         if (tls)
     724             :                 return "";
     725        1595 :         if (*host == '\0') {
     726             :                 // This was precomputed in msettings_validate(),
     727             :                 // {sockdir}/.s.monetdb.{port}
     728         178 :                 return mp->unix_sock_name_buffer;
     729             :         }
     730             :         return "";
     731             : }
     732             : 
     733             : 
     734             : const char *
     735        2987 : msettings_connect_tcp(const msettings *mp)
     736             : {
     737        2987 :         assert(mp->validated);
     738        2987 :         const char *sock = msetting_string(mp, MP_SOCK);
     739        2987 :         const char *host = msetting_string(mp, MP_HOST);
     740             :         // bool tls = msetting_bool(mp, MP_TLS);
     741             : 
     742        2987 :         if (*sock)
     743             :                 return "";
     744        2967 :         if (!*host)
     745         150 :                 return "localhost";
     746             :         return host;
     747             : }
     748             : 
     749             : long
     750        2539 : msettings_connect_port(const msettings *mp)
     751             : {
     752        2539 :         long port = msetting_long(mp, MP_PORT);
     753        2539 :         if (port == -1)
     754             :                 return 50000;
     755             :         else
     756        2135 :                 return port;
     757             : }
     758             : 
     759             : enum msetting_tls_verify
     760          50 : msettings_connect_tls_verify(const msettings *mp)
     761             : {
     762          50 :         assert(mp->validated);
     763          50 :         bool tls = msetting_bool(mp, MP_TLS);
     764          50 :         const char *cert = msetting_string(mp, MP_CERT);
     765          50 :         const char *certhash = msetting_string(mp, MP_CERTHASH);
     766             : 
     767          50 :         if (!tls)
     768             :                 return verify_none;
     769          48 :         if (*certhash) // certhash comes before cert
     770             :                 return verify_hash;
     771          32 :         if (*cert)
     772          22 :                 return verify_cert;
     773             :         return verify_system;
     774             : }
     775             : 
     776             : const char*
     777          19 : msettings_connect_clientkey(const msettings *mp)
     778             : {
     779          19 :         return msetting_string(mp, MP_CLIENTKEY);
     780             : }
     781             : 
     782             : const char*
     783          19 : msettings_connect_clientcert(const msettings *mp)
     784             : {
     785          19 :         const char *cert = msetting_string(mp, MP_CLIENTCERT);
     786          19 :         if (*cert)
     787             :                 return cert;
     788             :         else
     789          16 :                 return msetting_string(mp, MP_CLIENTKEY);
     790             : }
     791             : 
     792             : const char*
     793           5 : msettings_connect_certhash_digits(const msettings *mp)
     794             : {
     795           5 :         return mp->certhash_digits_buffer;
     796             : }
     797             : 
     798             : // also used as a validator, returns < 0 on invalid
     799             : long
     800        1265 : msettings_connect_binary(const msettings *mp)
     801             : {
     802        1265 :         const long sufficiently_large = 65535;
     803        1265 :         const char *binary = msetting_string(mp, MP_BINARY);
     804             : 
     805             :         // may be bool
     806        1265 :         int b = msetting_parse_bool(binary);
     807        1265 :         if (b == 0)
     808             :                 return 0;
     809        1253 :         if (b == 1)
     810             :                 return sufficiently_large;
     811          20 :         assert(b < 0);
     812             : 
     813          20 :         char *end;
     814          20 :         long level = strtol(binary, &end, 10);
     815          20 :         if (end != binary && *end == '\0')
     816          14 :                 return level;
     817             : 
     818             :         return -1;
     819             : }
     820             : 
     821             : 
     822             : /* automatically incremented each time the corresponding field is updated */
     823             : long
     824        5058 : msettings_user_generation(const msettings *mp)
     825             : {
     826        5058 :         return mp->user_generation;
     827             : }
     828             : 
     829             : /* automatically incremented each time the corresponding field is updated */
     830             : long
     831        5059 : msettings_password_generation(const msettings *mp)
     832             : {
     833        5059 :         return mp->password_generation;
     834             : }
     835             : 
     836             : 
     837             : bool
     838          12 : msettings_lang_is_mal(const msettings *mp)
     839             : {
     840          12 :         return mp->lang_is_mal;
     841             : }
     842             : 
     843             : bool
     844     1439334 : msettings_lang_is_sql(const msettings *mp)
     845             : {
     846     1439334 :         return mp->lang_is_sql;
     847             : }

Generated by: LCOV version 1.14