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-10-03 20:03:20 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         862 : int msetting_parse_bool(const char *text)
      42             : {
      43         862 :         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        4170 :         for (size_t i = 0; i < sizeof(variants) / sizeof(variants[0]); i++)
      52        4156 :                 if (strcasecmp(text, variants[i].word) == 0)
      53         848 :                         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         680 : allocprintf(const char *fmt, ...)
      62             : {
      63         680 :         va_list ap;
      64         680 :         char *buf = NULL;
      65         680 :         size_t pos = 0, cap = 0;
      66         680 :         int n;
      67             : 
      68         680 :         va_start(ap, fmt);
      69         680 :         n = vreallocprintf(&buf, &pos, &cap, fmt, ap);
      70         680 :         va_end(ap);
      71             : 
      72         680 :         if (n >= 0)
      73         680 :                 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          85 : msettings *msettings_clone(const msettings *orig)
     285             : {
     286          85 :         msettings *mp = malloc(sizeof(*mp));
     287          85 :         char **unknowns = orig->nr_unknown > 0 ? calloc(2 * orig->nr_unknown, sizeof(char*)) : NULL;
     288          85 :         const char *namebuf = orig->unix_sock_name_buffer;
     289          85 :         char *cloned_name_buffer = namebuf ? strdup(namebuf) : NULL;
     290          85 :         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          85 :         *mp = *orig;
     297          85 :         mp->unknown_parameters = unknowns;
     298          85 :         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          85 :         struct string *start = &mp->dummy_start_string;
     304          85 :         struct string *end = &mp->dummy_end_string;
     305          85 :         struct string *p = start;
     306        1700 :         while (p < end) {
     307        1615 :                 if (p->must_free) {
     308         148 :                         p->str = strdup(p->str);
     309         148 :                         if (p->str == NULL)
     310           0 :                                 goto bailout;
     311             :                 }
     312        1615 :                 p++;
     313             :         }
     314             : 
     315          85 :         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         690 : msettings_destroy(msettings *mp)
     369             : {
     370         690 :         if (mp == NULL)
     371             :                 return NULL;
     372             : 
     373       12198 :         for (struct string *p = &mp->dummy_start_string + 1; p < &mp->dummy_end_string; p++) {
     374       11556 :                 if (p->must_free)
     375        2425 :                         free(p->str);
     376             :         }
     377         648 :         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         642 :         free(mp->unknown_parameters);
     382         642 :         free(mp->unix_sock_name_buffer);
     383         642 :         free(mp);
     384             : 
     385         642 :         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       30308 : msetting_string(const msettings *mp, mparm parm)
     420             : {
     421       30308 :         if (mparm_classify(parm) != MPCLASS_STRING) {
     422           1 :                 FATAL();
     423             :                 return "";
     424             :         }
     425       30307 :         int i = parm - MP__STRING_START;
     426       30307 :         struct string const *p = &mp->dummy_start_string + 1 + i;
     427       30307 :         if (p >=  &mp->dummy_end_string) {
     428           0 :                 FATAL();
     429             :                 return "";
     430             :         }
     431       30307 :         char *s = p->str;
     432             : 
     433       30307 :         if (s == NULL) {
     434       15828 :                 if (parm == MP_LANGUAGE)
     435             :                         return "sql";
     436       15784 :                 else if (parm == MP_BINARY)
     437             :                         return "on";
     438       15784 :                 return "";
     439             :         }
     440             :         return s;
     441             : }
     442             : 
     443             : 
     444             : msettings_error
     445        3844 : msetting_set_string(msettings *mp, mparm parm, const char* value)
     446             : {
     447        3844 :         assert(value != NULL);
     448             : 
     449        3844 :         if (mparm_classify(parm) != MPCLASS_STRING)
     450           0 :                 FATAL();
     451        3845 :         int i = parm - MP__STRING_START;
     452        3845 :         struct string *p = &mp->dummy_start_string + 1 + i;
     453        3845 :         if (p >=  &mp->dummy_end_string)
     454           0 :                 FATAL();
     455             : 
     456        3845 :         char *v = strdup(value);
     457        3845 :         if (!v)
     458             :                 return MALLOC_FAILED;
     459        3845 :         if (p->must_free)
     460        1321 :                 free(p->str);
     461        3845 :         p->str = v;
     462        3845 :         p->must_free = true;
     463             : 
     464        3845 :         switch (parm) {
     465         475 :                 case MP_USER:
     466         475 :                         mp->user_generation++;
     467         475 :                         break;
     468         711 :                 case MP_PASSWORD:
     469         711 :                         mp->password_generation++;
     470         711 :                         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             :                         else if (strcmp(value, "`"))
     481             :                         break;
     482             :                 default:
     483             :                         break;
     484             :         }
     485             : 
     486        3845 :         mp->validated = false;
     487        3845 :         return NULL;
     488             : }
     489             : 
     490             : 
     491             : long
     492       47015 : msetting_long(const msettings *mp, mparm parm)
     493             : {
     494       47015 :         if (mparm_classify(parm) != MPCLASS_LONG)
     495           0 :                 FATAL();
     496       47015 :         int i = parm - MP__LONG_START;
     497       47015 :         const long * p = &mp->dummy_start_long + 1 + i;
     498       47015 :         if (p >=  &mp->dummy_end_long)
     499           0 :                 FATAL();
     500             : 
     501       47015 :         return *p;
     502             : }
     503             : 
     504             : 
     505             : msettings_error
     506        1610 : msetting_set_long(msettings *mp, mparm parm, long value)
     507             : {
     508        1610 :         if (mparm_classify(parm) != MPCLASS_LONG)
     509           0 :                 FATAL();
     510        1610 :         int i = parm - MP__LONG_START;
     511        1610 :         long *p = &mp->dummy_start_long + 1 + i;
     512        1610 :         if (p >=  &mp->dummy_end_long)
     513           0 :                 FATAL();
     514             : 
     515        1610 :         *p = value;
     516             : 
     517        1610 :         mp->validated = false;
     518        1610 :         return NULL;
     519             : }
     520             : 
     521             : 
     522             : bool
     523        8379 : msetting_bool(const msettings *mp, mparm parm)
     524             : {
     525        8379 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     526           0 :                 FATAL();
     527        8379 :         int i = parm - MP__BOOL_START;
     528        8379 :         const bool *p = &mp->dummy_start_bool + 1 + i;
     529        8379 :         if (p >=  &mp->dummy_end_bool)
     530           0 :                 FATAL();
     531        8379 :         return *p;
     532             : }
     533             : 
     534             : 
     535             : msettings_error
     536        1067 : msetting_set_bool(msettings *mp, mparm parm, bool value)
     537             : {
     538        1067 :         if (mparm_classify(parm) != MPCLASS_BOOL)
     539           0 :                 FATAL();
     540        1067 :         int i = parm - MP__BOOL_START;
     541        1067 :         bool *p = &mp->dummy_start_bool + 1 + i;
     542        1067 :         if (p >=  &mp->dummy_end_bool)
     543           0 :                 FATAL();
     544        1067 :         *p = value;
     545             : 
     546        1067 :         mp->validated = false;
     547        1067 :         return NULL;
     548             : }
     549             : 
     550             : msettings_error
     551        1279 : msetting_parse(msettings *mp, mparm parm, const char *text)
     552             : {
     553        1279 :         int b; // int not bool because we need to allow for parse errors
     554        1279 :         switch (mparm_classify(parm)) {
     555             :                 case MPCLASS_BOOL:
     556          26 :                         b = msetting_parse_bool(text);
     557          26 :                         if (b < 0)
     558           4 :                                 return format_error(mp, "%s: invalid boolean value", msetting_parm_name(mp, parm));
     559          22 :                         return msetting_set_bool(mp, parm, b);
     560             :                 case MPCLASS_LONG:
     561          92 :                         if (text[0] == '\0')
     562           3 :                                 return format_error(mp, "%s: integer parameter cannot be empty string", msetting_parm_name(mp, parm));
     563          89 :                         char *end;
     564          89 :                         long l = strtol(text, &end, 10);
     565          89 :                         if (*end != '\0')
     566           1 :                                 return format_error(mp, "%s: invalid integer", msetting_parm_name(mp, parm));
     567          88 :                         return msetting_set_long(mp, parm, l);
     568             :                 case MPCLASS_STRING:
     569        1161 :                         return msetting_set_string(mp, parm, text);
     570             :                 default:
     571             :                         assert(0 && "unreachable");
     572             :                         return "internal error, unclassified parameter type";
     573             :         }
     574             : }
     575             : 
     576             : char *
     577        1634 : msetting_as_string(const msettings *mp, mparm parm)
     578             : {
     579        1634 :         bool b;
     580        1634 :         long l;
     581        1634 :         const char *s;
     582        1634 :         switch (mparm_classify(parm)) {
     583             :                 case MPCLASS_BOOL:
     584         215 :                         b = msetting_bool(mp, parm);
     585         259 :                         return strdup(b ? "true" : "false");
     586             :                 case MPCLASS_LONG:
     587         473 :                         l = msetting_long(mp, parm);
     588         473 :                         int n = 40;
     589         473 :                         char *buf = malloc(n);
     590         473 :                         if (!buf)
     591             :                                 return NULL;
     592         473 :                         snprintf(buf, n, "%ld", l);
     593         473 :                         return buf;
     594             :                 case MPCLASS_STRING:
     595         946 :                         s = msetting_string(mp, parm);
     596         946 :                         return strdup(s);
     597             :                 default:
     598             :                         assert(0 && "unreachable");
     599             :                         return NULL;
     600             :         }
     601             : }
     602             : 
     603             : msettings_error
     604           6 : msetting_set_ignored(msettings *mp, const char *key, const char *value)
     605             : {
     606           6 :         char *my_key = strdup(key);
     607           6 :         char *my_value = strdup(value);
     608             : 
     609           6 :         size_t n = mp->nr_unknown;
     610           6 :         size_t new_size = (2 * n + 2) * sizeof(char*);
     611           6 :         char **new_unknowns = realloc(mp->unknown_parameters, new_size);
     612             : 
     613           6 :         if (!my_key || !my_value || !new_unknowns) {
     614           0 :                 free(my_key);
     615           0 :                 free(my_value);
     616           0 :                 free(new_unknowns);
     617           0 :                 return MALLOC_FAILED;
     618             :         }
     619             : 
     620           6 :         new_unknowns[2 * n] = my_key;
     621           6 :         new_unknowns[2 * n + 1] = my_value;
     622           6 :         mp->unknown_parameters = new_unknowns;
     623           6 :         mp->nr_unknown += 1;
     624             : 
     625           6 :         return NULL;
     626             : }
     627             : 
     628             : /* store named parameter */
     629             : msettings_error
     630         283 : msetting_set_named(msettings *mp, bool allow_core, const char *key, const char *value)
     631             : {
     632         283 :         mparm parm = mparm_parse(key);
     633         283 :         if (parm == MP_UNKNOWN)
     634           1 :                 return format_error(mp, "%s: unknown parameter", key);
     635             : 
     636         282 :         if (parm == MP_IGNORE)
     637           6 :                 return msetting_set_ignored(mp, key, value);
     638             : 
     639         276 :         if (!allow_core && mparm_is_core(parm))
     640           6 :                 return format_error(mp, "%s: parameter not allowed here", msetting_parm_name(mp, parm));
     641             : 
     642         270 :         return msetting_parse(mp, parm, value);
     643             : }
     644             : 
     645             : 
     646             : static bool
     647        5759 : empty(const msettings *mp, mparm parm)
     648             : {
     649        5759 :         const char *value = msetting_string(mp, parm);
     650        5759 :         assert(value);
     651        5759 :         return *value == '\0';
     652             : }
     653             : 
     654             : static bool
     655        4268 : nonempty(const msettings *mp, mparm parm)
     656             : {
     657         675 :         return !empty(mp, parm);
     658             : }
     659             : 
     660             : static msettings_error
     661         679 : validate_certhash(msettings *mp)
     662             : {
     663         679 :         mp->certhash_digits_buffer[0] = '\0';
     664             : 
     665         679 :         const char *full_certhash = msetting_string(mp, MP_CERTHASH);
     666         679 :         const char *certhash = full_certhash;
     667         679 :         if (*certhash == '\0')
     668             :                 return NULL;
     669             : 
     670          28 :         if (strncmp(certhash, "sha256:", 7) == 0) {
     671          19 :                 certhash += 7;
     672             :         } else {
     673           9 :                 return format_error(mp, "%s: expected to start with 'sha256:'", msetting_parm_name(mp, MP_CERTHASH));
     674             :         }
     675             : 
     676          19 :         size_t i = 0;
     677         367 :         for (const char *r = certhash; *r != '\0'; r++) {
     678         349 :                 if (*r == ':')
     679          21 :                         continue;
     680         328 :                 if (!isxdigit(*r))
     681           1 :                         return format_error(mp, "%s: invalid hex digit", msetting_parm_name(mp, MP_CERTHASH));
     682         327 :                 if (i < sizeof(mp->certhash_digits_buffer) - 1)
     683         287 :                         mp->certhash_digits_buffer[i++] = tolower(*r);
     684             :         }
     685          18 :         mp->certhash_digits_buffer[i] = '\0';
     686          18 :         if (i == 0)
     687           0 :                 return format_error(mp, "%s: need at least one digit", msetting_parm_name(mp, MP_CERTHASH));
     688             : 
     689             :         return NULL;
     690             : }
     691             : 
     692             : static bool
     693        1970 : validate_identifier(const char *name)
     694             : {
     695        1970 :         int first = name[0];
     696        1970 :         if (first == '\0')
     697             :                 return true;
     698         559 :         if (first != '_' && !isalpha(first))
     699             :                 return false;
     700       10644 :         for (const char *p = name; *p; p++) {
     701       10105 :                 bool ok = (isalnum(*p) || *p == '.' || *p == '-' || *p == '_');
     702       10105 :                 if (!ok)
     703             :                         return false;
     704             :         }
     705             :         return true;
     706             : }
     707             : 
     708             : bool
     709        1865 : msettings_validate(msettings *mp, char **errmsg)
     710             : {
     711        1865 :         if (mp->validated)
     712             :                 return true;
     713             : 
     714             :         // 1. The parameters have the types listed in the table in [Section
     715             :         //    Parameters](#parameters).
     716             :         // (this has already been checked)
     717             : 
     718             :         // 2. At least one of **sock** and **host** must be empty.
     719         721 :         if (nonempty(mp, MP_SOCK) && nonempty(mp, MP_HOST)) {
     720           8 :                 *errmsg = allocprintf(
     721             :                         "With sock='%s', host must be 'localhost', not '%s'",
     722             :                         msetting_string(mp, MP_SOCK),
     723             :                         msetting_string(mp, MP_HOST));
     724           8 :                 return false;
     725             :         }
     726             : 
     727             :         // 3. The string parameter **binary** must either parse as a boolean or as a
     728             :         //    non-negative integer.
     729             :         // (pretend valid so we can use msettings_connect_binary() to see if it parses)
     730         687 :         mp->validated = true;
     731         687 :         long level = msettings_connect_binary(mp);
     732         687 :         mp->validated = false;
     733         687 :         if (level < 0) {
     734           4 :                 *errmsg = allocprintf("invalid value '%s' for parameter 'binary'", msetting_string(mp, MP_BINARY));
     735           4 :                 return false;
     736             :         }
     737             : 
     738             :         // 4. If **sock** is not empty, **tls** must be 'off'.
     739         683 :         if (nonempty(mp, MP_SOCK) && msetting_bool(mp, MP_TLS)) {
     740           4 :                 *errmsg = allocprintf("TLS cannot be used with Unix domain sockets");
     741           4 :                 return false;
     742             :         }
     743             : 
     744             :         // 5. If **certhash** is not empty, it must be of the form `sha256:hexdigits`
     745             :         //    where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons.
     746         679 :         const char *certhash_msg = validate_certhash(mp);
     747         679 :         if (certhash_msg) {
     748          10 :                 *errmsg = strdup(certhash_msg);
     749          10 :                 return false;
     750             :         }
     751             :         // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well.
     752        1318 :         if (nonempty(mp, MP_CERT) || nonempty(mp, MP_CERTHASH))
     753          32 :                 if (!msetting_bool(mp, MP_TLS)) {
     754           5 :                         *errmsg = strdup("'cert' and 'certhash' can only be used with monetdbs://");
     755           5 :                         return false;
     756             :                 }
     757             : 
     758             :         // 7. Parameters **database**, **tableschema** and **table** must consist only of
     759             :         //    upper- and lowercase letters, digits, dashes and underscores. They must not
     760             :         //    start with a dash.
     761         664 :         const char *database = msetting_string(mp, MP_DATABASE);
     762         664 :         if (!validate_identifier(database)) {
     763           8 :                 *errmsg = allocprintf("invalid database name '%s'", database);
     764           8 :                 return false;
     765             :         }
     766         656 :         const char *tableschema = msetting_string(mp, MP_TABLESCHEMA);
     767         656 :         if (!validate_identifier(tableschema)) {
     768           6 :                 *errmsg = allocprintf("invalid schema name '%s'", tableschema);
     769           6 :                 return false;
     770             :         }
     771         650 :         const char *table = msetting_string(mp, MP_TABLE);
     772         650 :         if (!validate_identifier(table)) {
     773           6 :                 *errmsg = allocprintf("invalid table name '%s'", table);
     774           6 :                 return false;
     775             :         }
     776             : 
     777             :         // 8. Parameter **port** must be -1 or in the range 1-65535.
     778         644 :         long port = msetting_long(mp, MP_PORT);
     779         644 :         bool port_ok = (port == -1 || (port >= 1 && port <= 65535));
     780         644 :         if (!port_ok) {
     781           3 :                 *errmsg = allocprintf("invalid port '%ld'", port);
     782           3 :                 return false;
     783             :         }
     784             : 
     785             :         // 9. If **clientcert** is set, **clientkey** must also be set.
     786         641 :         if (nonempty(mp, MP_CLIENTCERT) && empty(mp, MP_CLIENTKEY)) {
     787           1 :                 *errmsg = allocprintf("clientcert can only be set together with clientkey");
     788           1 :                 return false;
     789             :         }
     790             : 
     791             :         // compute this here so the getter function can take const msettings*
     792         640 :         const char *sockdir = msetting_string(mp, MP_SOCKDIR);
     793         640 :         long effective_port = msettings_connect_port(mp);
     794         640 :         free(mp->unix_sock_name_buffer);
     795         640 :         mp->unix_sock_name_buffer = allocprintf("%s/.s.monetdb.%ld", sockdir, effective_port);
     796         640 :         if (mp->unix_sock_name_buffer == NULL)
     797             :                 return false;
     798             : 
     799         640 :         mp->validated = true;
     800         640 :         return true;
     801             : }
     802             : 
     803             : bool
     804        1485 : msettings_connect_scan(const msettings *mp)
     805             : {
     806        1485 :         if (empty(mp, MP_DATABASE))
     807             :                 return false;
     808         455 :         if (nonempty(mp, MP_SOCK))
     809             :                 return false;
     810         450 :         if (nonempty(mp, MP_HOST))
     811             :                 return false;
     812          99 :         long port = msetting_long(mp, MP_PORT);
     813          99 :         if (port != -1)
     814             :                 return false;
     815          10 :         bool tls = msetting_bool(mp, MP_TLS);
     816          10 :         if (tls)
     817             :                 return false;
     818             : 
     819             :         return true;
     820             : }
     821             : 
     822             : const char *
     823        1571 : msettings_connect_unix(const msettings *mp)
     824             : {
     825        1571 :         assert(mp->validated);
     826        1571 :         const char *sock = msetting_string(mp, MP_SOCK);
     827        1571 :         const char *host = msetting_string(mp, MP_HOST);
     828        1571 :         bool tls = msetting_bool(mp, MP_TLS);
     829             : 
     830        1571 :         if (*sock)
     831             :                 return sock;
     832        1560 :         if (tls)
     833             :                 return "";
     834        1536 :         if (*host == '\0') {
     835             :                 // This was precomputed in msettings_validate(),
     836             :                 // {sockdir}/.s.monetdb.{port}
     837         172 :                 return mp->unix_sock_name_buffer;
     838             :         }
     839             :         return "";
     840             : }
     841             : 
     842             : 
     843             : const char *
     844        2868 : msettings_connect_tcp(const msettings *mp)
     845             : {
     846        2868 :         assert(mp->validated);
     847        2868 :         const char *sock = msetting_string(mp, MP_SOCK);
     848        2868 :         const char *host = msetting_string(mp, MP_HOST);
     849             :         // bool tls = msetting_bool(mp, MP_TLS);
     850             : 
     851        2868 :         if (*sock)
     852             :                 return "";
     853        2858 :         if (!*host)
     854         137 :                 return "localhost";
     855             :         return host;
     856             : }
     857             : 
     858             : long
     859        2034 : msettings_connect_port(const msettings *mp)
     860             : {
     861        2034 :         long port = msetting_long(mp, MP_PORT);
     862        2034 :         if (port == -1)
     863             :                 return 50000;
     864             :         else
     865        1869 :                 return port;
     866             : }
     867             : 
     868             : enum msetting_tls_verify
     869          36 : msettings_connect_tls_verify(const msettings *mp)
     870             : {
     871          36 :         assert(mp->validated);
     872          36 :         bool tls = msetting_bool(mp, MP_TLS);
     873          36 :         const char *cert = msetting_string(mp, MP_CERT);
     874          36 :         const char *certhash = msetting_string(mp, MP_CERTHASH);
     875             : 
     876          36 :         if (!tls)
     877             :                 return verify_none;
     878          35 :         if (*certhash) // certhash comes before cert
     879             :                 return verify_hash;
     880          24 :         if (*cert)
     881          17 :                 return verify_cert;
     882             :         return verify_system;
     883             : }
     884             : 
     885             : const char*
     886          15 : msettings_connect_clientkey(const msettings *mp)
     887             : {
     888          15 :         return msetting_string(mp, MP_CLIENTKEY);
     889             : }
     890             : 
     891             : const char*
     892          15 : msettings_connect_clientcert(const msettings *mp)
     893             : {
     894          15 :         const char *cert = msetting_string(mp, MP_CLIENTCERT);
     895          15 :         if (*cert)
     896             :                 return cert;
     897             :         else
     898          13 :                 return msetting_string(mp, MP_CLIENTKEY);
     899             : }
     900             : 
     901             : const char*
     902           4 : msettings_connect_certhash_digits(const msettings *mp)
     903             : {
     904           4 :         return mp->certhash_digits_buffer;
     905             : }
     906             : 
     907             : // also used as a validator, returns < 0 on invalid
     908             : long
     909         698 : msettings_connect_binary(const msettings *mp)
     910             : {
     911         698 :         const long sufficiently_large = 65535;
     912         698 :         const char *binary = msetting_string(mp, MP_BINARY);
     913             : 
     914             :         // may be bool
     915         698 :         int b = msetting_parse_bool(binary);
     916         698 :         if (b == 0)
     917             :                 return 0;
     918         692 :         if (b == 1)
     919             :                 return sufficiently_large;
     920          10 :         assert(b < 0);
     921             : 
     922          10 :         char *end;
     923          10 :         long level = strtol(binary, &end, 10);
     924          10 :         if (end != binary && *end == '\0')
     925           7 :                 return level;
     926             : 
     927             :         return -1;
     928             : }
     929             : 
     930             : 
     931             : /* automatically incremented each time the corresponding field is updated */
     932             : long
     933         777 : msettings_user_generation(const msettings *mp)
     934             : {
     935         777 :         return mp->user_generation;
     936             : }
     937             : 
     938             : /* automatically incremented each time the corresponding field is updated */
     939             : long
     940         777 : msettings_password_generation(const msettings *mp)
     941             : {
     942         777 :         return mp->password_generation;
     943             : }
     944             : 
     945             : 
     946             : bool
     947          12 : msettings_lang_is_mal(const msettings *mp)
     948             : {
     949          12 :         return mp->lang_is_mal;
     950             : }
     951             : 
     952             : bool
     953     1439609 : msettings_lang_is_sql(const msettings *mp)
     954             : {
     955     1439609 :         return mp->lang_is_sql;
     956             : }

Generated by: LCOV version 1.14