conf.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /*! \file
3  * \brief Configuration handling
4  *
5  * Copyright (c) 2012-2024 by the developers. See the LICENSE file for details.
6  *
7  * If nothing else is specified, functions return zero to indicate success
8  * and a negative value to indicate an error.
9  */
10 
11 
12 /* ========================================================================== */
13 /* Include headers */
14 
15 #include "posix.h" /* Include this first because of feature test macros */
16 
17 #include <stdio.h>
18 #include <string.h>
19 
20 #include "conf.h"
21 #include "config.h"
22 #include "fileutils.h"
23 #include "main.h"
24 #include "secure.h"
25 #include "xdg.h"
26 
27 
28 /* ========================================================================== */
29 /*! \defgroup CONF CONF: Configuration handling
30  *
31  * Location of configfile: \c $XDG_CONFIG_HOME/$CFG_NAME/configfile
32  *
33  * Any line starting with \c # is treated as a comment (not parsed and ignored).
34  *
35  * To add a new entry:
36  * - Add new entry type in file \ref conf.h to enum \ref conf_entry
37  * - Increment \ref CONF_NUM in file \ref conf.h
38  * - Modify static function \c conf_init_configuration() to init the new entry
39  * to a default
40  */
41 /*! @{ */
42 
43 
44 /* ========================================================================== */
45 /* Constants */
46 
47 /*! \brief Message prefix for CONF module */
48 #define MAIN_ERR_PREFIX "CONF: "
49 
50 /*! \brief Permissions for config file */
51 #define CONF_PERM (api_posix_mode_t) (API_POSIX_S_IRUSR | API_POSIX_S_IWUSR)
52 
53 
54 /* ========================================================================== */
55 /* Variables */
56 
57 /*! \brief Global configuration
58  *
59  * There must be \ref CONF_NUM entries of type \ref conf in ascending order
60  * starting from zero without holes, so that \ref conf_entry IDs can be used
61  * as index.
62  */
64 
65 /*! \brief Flag indicating that password should not be stored in configfile
66  *
67  * If this flag is nonzero a call to \ref conf_store() will delete the password
68  * from the configuration before writing it to disk.
69  */
71 
72 static int conf_initialized = 0;
73 static const char confname[] = "configfile";
74 static const char tmpname[] = "configfile.new";
75 
76 
77 /* ========================================================================== */
78 /* Add integer configuration entry */
79 
80 static void conf_add_entry_int(struct conf* conf, enum conf_entry entry,
81  const char* label, int val)
82 {
83  conf[entry].label = label;
84  conf[entry].type = CONF_TYPE_INT;
85  conf[entry].val.i = val;
86  conf[entry].found = 0;
87 }
88 
89 
90 /* ========================================================================== */
91 /* Add string configuration entry */
92 
93 static void conf_add_entry_string(struct conf* conf, enum conf_entry entry,
94  const char* label, char* val)
95 {
96  conf[entry].label = label;
97  conf[entry].type = CONF_TYPE_STRING;
98  conf[entry].val.s = val;
99  conf[entry].found = 0;
100 }
101 
102 
103 /* ========================================================================== */
104 /* Init configuration with default values (to use if there is no config file)
105  *
106  * \attention
107  * The content of entries with string type must be stored in dynamic memory!
108  * For empty strings, a memory block with NUL termination inside must be used.
109  */
110 
111 static void conf_init_configuration(struct conf* cfg)
112 {
113  const char server_default[] = "localhost";
114  const char service_default[] = "nntp";
115  const char test_ere_default[] = "\\.test$|^test$";
116  const char test_kwords_default[] = "ignore,no-reply";
117  const char crl_upd_ts_default[] = "1970-01-01T00:00:00Z";
118  const char sigfile_default[] = ".signature";
119  /* This is a format string, '%s' will be replaced with cited authors name */
120  const char intro_default[] = "%s wrote:";
121  char* string;
122  char* empty;
123  int error = 0;
124 
125  /* Main window size, position and tiling */
126  conf_add_entry_int(cfg, CONF_POS_X, "pos_x:", 10);
127  conf_add_entry_int(cfg, CONF_POS_Y, "pos_y:", 40);
128  conf_add_entry_int(cfg, CONF_SIZE_X, "size_x:", 750);
129  conf_add_entry_int(cfg, CONF_SIZE_Y, "size_y:", 350);
130  conf_add_entry_int(cfg, CONF_TILE_X, "tile_x:", 200);
131  conf_add_entry_int(cfg, CONF_TILE_Y, "tile_y:", 120);
132 
133  /* NNTP server */
134  string = (char*) api_posix_malloc(strlen(server_default) + (size_t) 1);
135  if(NULL == string) { error = 1; }
136  else
137  {
138  strcpy(string, server_default);
139  conf_add_entry_string(cfg, CONF_SERVER, "server:", string);
140  }
141 
142  /* NNTP service (port) */
143  string = (char*) api_posix_malloc(strlen(service_default) + (size_t) 1);
144  if(NULL == string) { error = 1; }
145  else
146  {
147  strcpy(string, service_default);
148  conf_add_entry_string(cfg, CONF_SERVICE, "service:", string);
149  }
150 
151  /* NNTP connection encryption */
152  conf_add_entry_int(cfg, CONF_ENC, "enc:", 0);
153 
154  /* Authentication against NNTP server */
155  conf_add_entry_int(cfg, CONF_AUTH, "auth:", 0);
156  conf_add_entry_int(cfg, CONF_IMMEDAUTH, "immedauth:", 1);
157  empty = (char*) api_posix_malloc((size_t) 1);
158  if(NULL == empty) { error = 1; }
159  else
160  {
161  empty[0] = 0;
162  conf_add_entry_string(cfg, CONF_USER, "user:", empty);
163  }
164  empty = (char*) api_posix_malloc((size_t) 1);
165  if(NULL == empty) { error = 1; }
166  else
167  {
168  empty[0] = 0;
169  conf_add_entry_string(cfg, CONF_PASS, "pass:", empty);
170  }
171 
172  /* Clamp article count limit */
173  conf_add_entry_int(cfg, CONF_CAC, "cac:", 250);
174 
175  /* "From" name and E-Mail address */
176  empty = (char*) api_posix_malloc((size_t) 1);
177  if(NULL == empty) { error = 1; }
178  else
179  {
180  empty[0] = 0;
181  conf_add_entry_string(cfg, CONF_FROM, "from:", empty);
182  }
183 
184  /* "Reply-To" name and E-Mail address */
185  empty = (char*) api_posix_malloc((size_t) 1);
186  if(NULL == empty) { error = 1; }
187  else
188  {
189  empty[0] = 0;
190  conf_add_entry_string(cfg, CONF_REPLYTO, "replyto:", empty);
191  }
192 
193  /* Fully qualified domain name (FQDN) */
194  empty = (char*) api_posix_malloc((size_t) 1);
195  if(NULL == empty) { error = 1; }
196  else
197  {
198  empty[0] = 0;
199  conf_add_entry_string(cfg, CONF_FQDN, "fqdn:", empty);
200  }
201 
202  /* Pathname of shared newsrc file */
203  empty = (char*) api_posix_malloc((size_t) 1);
204  if(NULL == empty) { error = 1; }
205  else
206  {
207  empty[0] = 0;
208  conf_add_entry_string(cfg, CONF_NEWSRC, "newsrc:", empty);
209  }
210 
211  /* Pathname of shared scorerc file */
212  empty = (char*) api_posix_malloc((size_t) 1);
213  if(NULL == empty) { error = 1; }
214  else
215  {
216  empty[0] = 0;
217  conf_add_entry_string(cfg, CONF_SCORERC, "scorerc:", empty);
218  }
219 
220  /* Introduction line format */
221  string = (char*) api_posix_malloc(strlen(intro_default) + (size_t) 1);
222  if(NULL == string) { error = 1; }
223  else
224  {
225  strcpy(string, intro_default);
226  conf_add_entry_string(cfg, CONF_INTRO, "intro:", string);
227  }
228 
229  /* Organization name */
230  empty = (char*) api_posix_malloc((size_t) 1);
231  if(NULL == empty) { error = 1; }
232  else
233  {
234  empty[0] = 0;
235  conf_add_entry_string(cfg, CONF_ORGANIZATION, "organization:", empty);
236  }
237 
238  /* Threaded view */
239  conf_add_entry_int(cfg, CONF_TVIEW, "tview:", 1);
240 
241  /* Show only unread articles (or threads containing unread articles) */
242  conf_add_entry_int(cfg, CONF_ONLYUR, "onlyur:", 0);
243 
244  /* Secret for Cancel-Key */
245  empty = (char*) api_posix_malloc((size_t) 1);
246  if(NULL == empty) { error = 1; }
247  else
248  {
249  empty[0] = 0;
250  conf_add_entry_string(cfg, CONF_CANCELKEY, "cancelkey:", empty);
251  }
252 
253  /* Pathname of external article editor */
254  empty = (char*) api_posix_malloc((size_t) 1);
255  if(NULL == empty) { error = 1; }
256  else
257  {
258  empty[0] = 0;
259  conf_add_entry_string(cfg, CONF_EDITOR, "editor:", empty);
260  }
261 
262  /* Pathname of external article postprocessor */
263  empty = (char*) api_posix_malloc((size_t) 1);
264  if(NULL == empty) { error = 1; }
265  else
266  {
267  empty[0] = 0;
268  conf_add_entry_string(cfg, CONF_PPROC, "post_proc:", empty);
269  }
270 
271  /* Pathname of external inews */
272  empty = (char*) api_posix_malloc((size_t) 1);
273  if(NULL == empty) { error = 1; }
274  else
275  {
276  empty[0] = 0;
277  conf_add_entry_string(cfg, CONF_INEWS, "inews:", empty);
278  }
279 
280  /* Quoting style */
281  conf_add_entry_int(cfg, CONF_QUOTESTYLE, "quote_style:", 1);
282 
283  /* Unification of quoting */
284  conf_add_entry_int(cfg, CONF_QUOTEUNIFY, "quote_unify:", 1);
285 
286  /* POSIX ERE (Extended Regular Expression) to match test groups */
287  string = (char*) api_posix_malloc(strlen(test_ere_default) + (size_t) 1);
288  if(NULL == string) { error = 1; }
289  else
290  {
291  strcpy(string, test_ere_default);
292  conf_add_entry_string(cfg, CONF_TESTGRP_ERE, "testgrp_ere:", string);
293  }
294 
295  /* Keywords to set for test groups (on ERE match) */
296  string = (char*) api_posix_malloc(strlen(test_kwords_default) + (size_t) 1);
297  if(NULL == string) { error = 1; }
298  else
299  {
300  strcpy(string, test_kwords_default);
301  conf_add_entry_string(cfg, CONF_TESTGRP_KWORDS, "testgrp_kwords:",
302  string);
303  }
304 
305  /* TLS certificate checks use local anchors (root certificates) */
306  conf_add_entry_int(cfg, CONF_TLS_OWNCERTS, "tls_owncerts:", 0);
307 
308  /* TLS certificate CRL update interval (in hours) */
309  conf_add_entry_int(cfg, CONF_CRL_UPD_IV, "crl_upd_iv:", 20);
310 
311  /* TLS certificate CRL update timestamp */
312  string = (char*) api_posix_malloc(strlen(crl_upd_ts_default) + (size_t) 1);
313  if(NULL == string) { error = 1; }
314  else
315  {
316  strcpy(string, crl_upd_ts_default);
317  conf_add_entry_string(cfg, CONF_CRL_UPD_TS, "crl_upd_ts:", string);
318  }
319 
320  /* TLS certificate CRL check */
321  conf_add_entry_int(cfg, CONF_CRL_CHECK, "crl_check:", 0);
322 
323  /* TLS certificate CRL update choice (Allow user to skip update) */
324  conf_add_entry_int(cfg, CONF_CRL_UPD_C, "crl_upd_c:", 0);
325 
326  /* Sort unthreaded view in GUI using article numbers */
327  conf_add_entry_int(cfg, CONF_UTVIEW_AN, "utview_an:", 0);
328 
329  /* Sort articles in GUI with inverted order */
330  conf_add_entry_int(cfg, CONF_INV_ORDER, "inv_order:", 0);
331 
332  /* NNTP driver doesn't use overview (OVER command) if nonzero */
333  conf_add_entry_int(cfg, CONF_NO_OVER, "nntp_no_over:", 0);
334 
335  /* Use distribution suggestions from server (if available) */
336  conf_add_entry_int(cfg, CONF_DIST_SUGG, "dist_suggestions:", 0);
337 
338  /* Negotiate compression (if available) */
339  conf_add_entry_int(cfg, CONF_COMPRESSION, "nntp_compression:", 0);
340 
341  /* Signature file */
342  string = (char*) api_posix_malloc(strlen(sigfile_default) + (size_t) 1);
343  if(NULL == string) { error = 1; }
344  else
345  {
346  strcpy(string, sigfile_default);
347  conf_add_entry_string(cfg, CONF_SIGFILE, "signature_file:", string);
348  }
349 
350  /* Use local time for timestamp generation (if timezone is available) */
351  conf_add_entry_int(cfg, CONF_TS_LTIME, "timestamp_ltime:", 1);
352 
353  /* Generate timestamps with timezone comment (if timezone is available) */
354  conf_add_entry_int(cfg, CONF_TS_COMMENT, "timestamp_comment:", 0);
355 
356  /* Generate empty line after paragraphs (format=flowed) */
357  conf_add_entry_int(cfg, CONF_FLOWED_CRLF, "flowed_insert_crlf:", 0);
358 
359  /* Force Unicode for outgoing articles */
360  conf_add_entry_int(cfg, CONF_FORCE_UNICODE, "force_unicode:", 0);
361 
362  /* Search case-insensitive */
363  conf_add_entry_int(cfg, CONF_SEARCH_CASE_IS, "search_case_insensitive:", 0);
364 
365  /* Enable User-Agent header field */
366  conf_add_entry_int(cfg, CONF_ENABLE_UAGENT, "enable_uagent_hfield:", 1);
367 
368  /* Initial greeting */
369  empty = (char*) api_posix_malloc((size_t) 1);
370  if(NULL == empty) { error = 1; }
371  else
372  {
373  empty[0] = 0;
374  conf_add_entry_string(cfg, CONF_INITIAL_GREETING, "initial_greeting:",
375  empty);
376  }
377 
378  /* Group list refresh interval */
379  conf_add_entry_int(cfg, CONF_REFRESH_INTERVAL, "refresh_interval:", 0);
380 
381  /* Skip to next group for unread article */
382  conf_add_entry_int(cfg, CONF_UNREAD_IN_NEXT_GROUP, "unread_in_next_group:",
383  0);
384 
385  /* Color for signature */
386  conf_add_entry_int(cfg, CONF_COLOR_SIGNATURE, "color_signature:", 43);
387 
388  /* Color for external citation */
389  conf_add_entry_int(cfg, CONF_COLOR_EXTERNAL, "color_external:", 88);
390 
391  /* Color for hyperlink */
392  conf_add_entry_int(cfg, CONF_COLOR_HYPERLINK, "color_hyperlink:", 216);
393 
394  /* Colors of thread citation level */
395  conf_add_entry_int(cfg, CONF_COLOR_LEVEL1, "color_level1:", 140);
396  conf_add_entry_int(cfg, CONF_COLOR_LEVEL2, "color_level2:", 152);
397  conf_add_entry_int(cfg, CONF_COLOR_LEVEL3, "color_level3:", 91);
398  conf_add_entry_int(cfg, CONF_COLOR_LEVEL4, "color_level4:", 75);
399 
400  /* Kill threshold (articles below that score will be killed) */
401  conf_add_entry_int(cfg, CONF_SCORE_KILL_TH, "kill_threshold:", -9999);
402 
403  /* On success, store flag that configuration was initialized */
404  if(error) { PRINT_ERROR("Initializing configuration failed"); }
405  else { conf_initialized = 1; }
406 }
407 
408 
409 /* ========================================================================== */
410 /* Get config file pathname
411  *
412  * The caller is responsible to free the memory for the buffer on success.
413  */
414 
415 static int conf_get_confpathname(const char** confpathname,
416  const char* conffile)
417 {
418  int res = -1;
419  int rv;
420 
421  *confpathname = xdg_get_confdir(CFG_NAME);
422  if(NULL != *confpathname)
423  {
424  rv = fu_create_path(*confpathname, (api_posix_mode_t) API_POSIX_S_IRWXU);
425  if(0 == rv)
426  {
427  /* Store config file pathname */
428  rv = xdg_append_to_path(confpathname, conffile);
429  if(0 == rv)
430  {
431  res = 0;
432  }
433  }
434  }
435 
436  /* Free memory on error */
437  if(0 != res)
438  {
439  PRINT_ERROR("Cannot create config file pathname");
440  api_posix_free((void*) *confpathname);
441  *confpathname = NULL;
442  }
443 
444  return(res);
445 }
446 
447 
448 /* ========================================================================== */
449 /* Extract integer value from configuration data */
450 
451 static int conf_extract_entry(char* line, struct conf* target)
452 {
453  int res = -1;
454  size_t labelsize = strlen(target->label);
455  const char* string;
456  char* sbuf;
457  int val;
458  size_t i;
459 
460  /* Check line length */
461  if(strlen(line) > labelsize)
462  {
463  /* Search for label */
464  if(!strncmp(target->label, line, labelsize))
465  {
466  /* Found */
467  res = 0;
468  if(CONF_TYPE_INT == target->type)
469  {
470  /* Extract integer value */
471  string = &line[labelsize];
472  if(1 == sscanf(string, "%d", &val)) target->val.i = val;
473  }
474  else
475  {
476  /* Extract string */
477  i = labelsize;
478  while(line[i])
479  {
480  if(' ' != line[i]) { break; }
481  else ++i;
482  }
483  string = &line[i];
484  while(line[i])
485  {
486  if('\n' == line[i])
487  {
488  line[i] = 0;
489  break;
490  }
491  else ++i;
492  }
493  /*
494  * Allocate memory for string entry
495  * This memory is not released until program exit.
496  */
497  sbuf = (char*) api_posix_malloc(strlen(string) + (size_t) 1);
498  if(NULL == sbuf)
499  {
500  PRINT_ERROR("Cannot allocate memory for config file entry");
501  target->val.s = NULL;
502  res = -1;
503  }
504  else
505  {
506  strcpy(sbuf, string);
507  secure_clear_string(target->val.s);
508  api_posix_free((void*) target->val.s);
509  target->val.s = sbuf;
510  }
511  }
512  }
513  }
514 
515  return(res);
516 }
517 
518 
519 /* ========================================================================== */
520 /* Update configuration data */
521 
522 static int conf_update_entry(char** line, size_t len, struct conf* target)
523 {
524  int res = -1;
525  size_t labelsize = strlen(target->label);
526  char* string;
527  int reqd;
528 
529  /* Check line length */
530  if(strlen(*line) >= labelsize)
531  {
532  /* Search for label */
533  if(!strncmp(target->label, *line, labelsize))
534  {
535  /* Found => Insert new value */
536  res = 0;
537  target->found = 1;
538  do
539  {
540  reqd = -1;
541  if(CONF_TYPE_INT == target->type)
542  {
543  /* Update integer entry */
544  reqd = api_posix_snprintf(*line, len, "%s %d\n",
545  target->label, target->val.i);
546  }
547  if(CONF_TYPE_STRING == target->type)
548  {
549  /* Update string entry */
550  reqd = api_posix_snprintf(*line, len, "%s %s\n",
551  target->label, target->val.s);
552  }
553  if(0 > reqd) { break; }
554  else if(len <= (size_t) reqd)
555  {
556  /* Allocate more memory */
557  if(API_POSIX_SIZE_MAX <= (size_t) reqd) { break; }
558  string = (char*) api_posix_realloc(*line, (size_t) (reqd + 1));
559  if(NULL == string) { break; }
560  else { *line = string; }
561  }
562  }
563  while(len <= (size_t) reqd);
564  }
565  }
566 
567  return(res);
568 }
569 
570 
571 /* ========================================================================== */
572 /* Import configuration */
573 
574 static int conf_import_configuration(struct conf* cfg, FILE* fs)
575 {
576  int res = -1;
577  char* line = NULL;
578  size_t len = 0;
579  api_posix_ssize_t readlen;
580  unsigned int i;
581 
582  while(1)
583  {
584  /* Read line */
585  readlen = api_posix_getline(&line, &len, fs);
586  if(-1 == readlen)
587  {
588  if(API_POSIX_ENOMEM == api_posix_errno)
589  {
590  PRINT_ERROR("Cannot assign memory for config file parser");
591  }
592  else
593  {
594  /* Check for error */
595  if(ferror(fs))
596  {
597  PRINT_ERROR("Parse error in config file");
598  break;
599  }
600  /* Check for EOF */
601  if(feof(fs))
602  {
603  res = 0;
604  break;
605  }
606  }
607  }
608  if(0 >= readlen) { break; }
609  else
610  {
611  /* Extract data */
612  if(!strncmp("#", line, 1)) { continue; }
613  for(i = 0; i < CONF_NUM; ++i)
614  {
615  if(!conf_extract_entry(line, &cfg[i])) { break; }
616  }
617  }
618  }
619 
620  /* Release memory for line buffer */
621  secure_clear_string(line);
622  api_posix_free((void*) line);
623 
624  return(res);
625 }
626 
627 
628 /* ========================================================================== */
629 /* Export configuration */
630 
631 static int conf_export_configuration(struct conf* cfg,
632  FILE* fs, FILE* fs_tmp)
633 {
634  int res = -1;
635  char* line = NULL;
636  size_t len = 0;
637  api_posix_ssize_t readlen;
638  int rv;
639  unsigned int i;
640  size_t reqd;
641  char* p;
642 
643  while(1)
644  {
645  /* Read line */
646  readlen = api_posix_getline(&line, &len, fs);
647  if(-1 == readlen)
648  {
649  if(API_POSIX_ENOMEM == api_posix_errno)
650  {
651  PRINT_ERROR("Cannot assign memory for config file parser");
652  }
653  else
654  {
655  /* Check for error */
656  if(ferror(fs))
657  {
658  PRINT_ERROR("Parse error in config file");
659  break;
660  }
661  /* Check for EOF */
662  if(feof(fs))
663  {
664  res = 0;
665  break;
666  }
667  }
668  }
669  if(0 >= readlen) { break; }
670  else
671  {
672  /* Update data */
673  for(i = 0; i < CONF_NUM; ++i)
674  {
675  if(!conf_update_entry(&line, len, &cfg[i])) { break; }
676  }
677 
678  /* Write line to new config file */
679  rv = fprintf(fs_tmp, "%s", line);
680  if(0 > rv) { break; }
681  }
682  }
683 
684  /* Add missing entries to end of config file */
685  if(!res)
686  {
687  for(i = 0; i < CONF_NUM; ++i)
688  {
689  if(!cfg[i].found)
690  {
691  /* Create new entry */
692  reqd = strlen(cfg[i].label);
693  if(API_POSIX_SIZE_MAX <= reqd) { break; }
694  if(reqd + (size_t) 1 > len)
695  {
696  /* Allocate more memory */
697  p = (char*) api_posix_realloc(line, reqd + (size_t) 1);
698  if (NULL != p) line = p;
699  }
700  strncpy(line, cfg[i].label, len);
701  rv = conf_update_entry(&line, len, &cfg[i]);
702  /* Write new entry */
703  if(!rv)
704  {
705  rv = fprintf(fs_tmp, "%s", line);
706  if(0 > rv)
707  {
708  res = -1;
709  break;
710  }
711  }
712  }
713  }
714  }
715 
716  /* Release memory for line buffer */
717  api_posix_free((void*) line);
718 
719  return(res);
720 }
721 
722 
723 /* ========================================================================== */
724 /*! \brief Delete configuration
725  *
726  * This function will free the memory allocated for the configuration array
727  * entries with string type.
728  *
729  * \param[in] cfg Pointer to configuration array
730  */
731 
732 void conf_delete(struct conf* cfg)
733 {
734  unsigned int i;
735 
736  if(conf_initialized)
737  {
738  for(i = 0; i < CONF_NUM; ++i)
739  {
740  if(CONF_TYPE_STRING == cfg[i].type)
741  {
743  api_posix_free((void*) cfg[i].val.s);
744  cfg[i].val.s = NULL;
745  }
746  }
747  }
748 
749  /* Clear flag that configuration is initialized */
750  conf_initialized = 0;
751 }
752 
753 
754 /* ========================================================================== */
755 /*! \brief Load configuration from config file
756  *
757  * \param[in] cfg Pointer to configuration array
758  *
759  * For configuration entries with string type, this function allocates memory.
760  * On success the caller is responsible to free this memory with the function
761  * \ref conf_delete() .
762  *
763  * \return
764  * - 0 on success
765  * - Negative value on error
766  */
767 
768 int conf_load(struct conf* cfg)
769 {
770  int res = -1;
771  int fd = -1;
772  FILE* fs = NULL;
773  const char* confpathname = NULL;
774 
775  /* Init configuration with default values */
776  conf_delete(cfg);
777  conf_init_configuration(cfg);
778  if(conf_initialized)
779  {
780  /* Get config file pathname */
781  res = conf_get_confpathname(&confpathname, confname);
782  if(!res)
783  {
784  if(main_debug)
785  {
786  printf("%s: %sLoad from: %s\n", CFG_NAME, MAIN_ERR_PREFIX,
787  confpathname);
788  }
789 
790  /* Open and lock config file */
791  res = fu_open_file(confpathname, &fd,
792  API_POSIX_O_RDWR | API_POSIX_O_CREAT, CONF_PERM);
793  if(!res) { res = fu_lock_file(fd); }
794  if(!res) { res = fu_assign_stream(fd, &fs, "r"); }
795 
796  /* Import configuration from config file */
797  if(!res) { res = conf_import_configuration(cfg, fs); }
798  }
799 
800  /* Close config file (this will also release the lock) */
801  fu_close_file(&fd, &fs);
802 
803  /* Release memory for pathname */
804  api_posix_free((void*) confpathname);
805 
806  /* Delete configuration again on error */
807  if(res) { conf_delete(cfg); }
808  }
809 
810  return(res);
811 }
812 
813 
814 /* ========================================================================== */
815 /*! \brief Store configuration to config file
816  *
817  * \param[in] cfg Pointer to configuration array
818  *
819  * \return
820  * - 0 on success
821  * - Negative value on error
822  */
823 
824 int conf_store(struct conf* cfg)
825 {
826  int res = -1;
827  int fd = -1;
828  FILE* fs = NULL;
829  int fd_tmp = -1;
830  FILE* fs_tmp = NULL;
831  const char* confpathname = NULL;
832  const char* tmppathname = NULL;
833 
834  /* Get file pathnames */
835  res = conf_get_confpathname(&confpathname, confname);
836  if(!res) { res = conf_get_confpathname(&tmppathname, tmpname); }
837 
838  /* Open and lock config file */
839  if(!res)
840  {
841  res = fu_open_file(confpathname, &fd,
842  API_POSIX_O_RDWR | API_POSIX_O_CREAT, CONF_PERM);
843  if(!res) { res = fu_lock_file(fd); }
844  if(!res) { res = fu_assign_stream(fd, &fs, "r"); }
845  }
846 
847  /* Open temporary file for new configuration */
848  if(!res)
849  {
850  res = fu_open_file(tmppathname, &fd_tmp,
851  API_POSIX_O_WRONLY | API_POSIX_O_CREAT
852  | API_POSIX_O_TRUNC, CONF_PERM);
853  /*
854  * Because we have the lock for the config file, it is allowed to
855  * assume that no other instance of the program currently use the
856  * temporary filename.
857  */
858  if(!res) { res = fu_assign_stream(fd_tmp, &fs_tmp, "w"); }
859  }
860 
861  /* Export configuration to temporary file */
862  if(!res)
863  {
865  {
866  /* Delete password from configuration */
868  }
869  res = conf_export_configuration(cfg, fs, fs_tmp);
870  }
871 
872  /* Flush stream of temporary file */
873  if(!res) { res = fu_sync(fd_tmp, fs_tmp); }
874 
875  /* Rename temporary file to config file */
876  if(!res) { res = api_posix_rename(tmppathname, confpathname); }
877 
878  /* Try to unlink temporary file after error */
879  if(res)
880  {
881  PRINT_ERROR("Failed to store configuration in config file");
882  if(tmppathname) { (void) fu_unlink_file(tmppathname); }
883  }
884 
885  /* The tmp file must be removed before releasing the config file lock! */
886  fu_close_file(&fd_tmp, &fs_tmp);
887 
888  /* Close config file (this will also release the lock) */
889  fu_close_file(&fd, &fs);
890 
891  if(main_debug)
892  {
893  printf("%s: %sStore to: %s\n", CFG_NAME, MAIN_ERR_PREFIX, confpathname);
894  }
895 
896  /* Release memory for file pathnames */
897  api_posix_free((void*) tmppathname);
898  api_posix_free((void*) confpathname);
899 
900  return(res);
901 }
902 
903 
904 /* ========================================================================== */
905 /*! \brief Replace configuration string
906  *
907  * \param[in] cfg Pointer to configuration array entry
908  * \param[in] s Pointer to new string
909  *
910  * \e cfg must point to a configuration entry of string type.
911  * The current string will be replaced with \e s .
912  *
913  * \return
914  * - 0 on success
915  * - Negative value on error
916  */
917 
918 int conf_string_replace(struct conf* cfg, const char* s)
919 {
920  int res = -1;
921  size_t len;
922  char* p;
923 
924  if(NULL != cfg && NULL != cfg->val.s && NULL != s)
925  {
926  if(CONF_TYPE_STRING == cfg->type)
927  {
928  len = strlen(s);
929  p = api_posix_malloc(++len);
930  if(NULL != p)
931  {
932  strcpy(p, s);
933  secure_clear_string(cfg->val.s);
934  api_posix_free((void*) cfg->val.s);
935  cfg->val.s = p;
936  res = 0;
937  }
938  }
939  }
940 
941  return(res);
942 }
943 
944 
945 /* ========================================================================== */
946 /*! \brief Check integer value against lower and upper bounds
947  *
948  * \param[in] id ID of integer value to check
949  * \param[in] min Lower limit
950  * \param[in] max Upper limit
951  *
952  * \attention The parameter \e id must be associated with an integer value!
953  *
954  * The integer value associated with \e id is compared against the specified
955  * limits.
956  * <br>
957  * If the value is lower than \e min or greater than \e max , it is clamped to
958  * the limits. Otherwise the value is unchanged.
959  *
960  * \return
961  * - New value
962  */
963 
964 int conf_integer_check(enum conf_entry id, int min, int max)
965 {
966  if(min > config[id].val.i)
967  {
968  config[id].val.i = min;
969  }
970 
971  if(max < config[id].val.i)
972  {
973  config[id].val.i = max;
974  }
975 
976  return(config[id].val.i);
977 }
978 
979 
980 /*! @} */
981 
982 /* EOF */
CONF_COLOR_SIGNATURE
Definition: conf.h:83
CONF_INTRO
Definition: conf.h:51
CONF_FORCE_UNICODE
Definition: conf.h:77
CONF_AUTH
Definition: conf.h:41
secure_clear_string
void secure_clear_string(char *)
Remove string from memory.
Definition: secure.c:117
CONF_ORGANIZATION
Definition: conf.h:52
CONF_DIST_SUGG
Definition: conf.h:71
CONF_ONLYUR
Definition: conf.h:54
CONF_SERVICE
Definition: conf.h:39
CONF_CRL_CHECK
Definition: conf.h:64
CONF_COLOR_LEVEL2
Definition: conf.h:87
fu_assign_stream
int fu_assign_stream(int filedesc, FILE **stream, const char *mode)
Assign I/O stream to open file.
Definition: fileutils.c:380
CONF_EDITOR
Definition: conf.h:56
CONF_SIGFILE
Definition: conf.h:73
CONF_NUM
#define CONF_NUM
Number of entries in global configuration array.
Definition: conf.h:13
conf::type
enum conf_entry_type type
Definition: conf.h:112
CONF_COLOR_HYPERLINK
Definition: conf.h:85
fu_lock_file
int fu_lock_file(int filedesc)
Lock file for writing.
Definition: fileutils.c:335
CONF_POS_Y
Definition: conf.h:33
config
struct conf config[CONF_NUM]
Global configuration.
Definition: conf.c:63
MAIN_ERR_PREFIX
#define MAIN_ERR_PREFIX
Message prefix for CONF module.
Definition: conf.c:48
conf_entry_val::s
char * s
Definition: conf.h:105
conf_store
int conf_store(struct conf *cfg)
Store configuration to config file.
Definition: conf.c:824
CONF_FROM
Definition: conf.h:46
CONF_SIZE_X
Definition: conf.h:34
conf_string_replace
int conf_string_replace(struct conf *cfg, const char *s)
Replace configuration string.
Definition: conf.c:918
CONF_SCORERC
Definition: conf.h:50
CONF_REFRESH_INTERVAL
Definition: conf.h:81
CONF_COLOR_LEVEL1
Definition: conf.h:86
main_debug
int main_debug
Enable additional debug output if nonzero.
Definition: main.cxx:64
conf::found
int found
Definition: conf.h:114
CONF_COLOR_EXTERNAL
Definition: conf.h:84
CONF_REPLYTO
Definition: conf.h:47
CONF_QUOTESTYLE
Definition: conf.h:59
CONF_COLOR_LEVEL3
Definition: conf.h:88
CONF_UTVIEW_AN
Definition: conf.h:68
CONF_UNREAD_IN_NEXT_GROUP
Definition: conf.h:82
conf_entry_val::i
int i
Definition: conf.h:104
CONF_SCORE_KILL_TH
Definition: conf.h:90
fu_create_path
int fu_create_path(const char *path, api_posix_mode_t perm)
Create path.
Definition: fileutils.c:122
CONF_CAC
Definition: conf.h:45
CONF_INEWS
Definition: conf.h:58
conf_delete
void conf_delete(struct conf *cfg)
Delete configuration.
Definition: conf.c:732
CONF_COMPRESSION
Definition: conf.h:72
conf_entry
conf_entry
IDs for use as index in configuration array.
Definition: conf.h:30
fu_unlink_file
int fu_unlink_file(const char *pathname)
Unlink file.
Definition: fileutils.c:362
CONF_INITIAL_GREETING
Definition: conf.h:80
conf
Configuration array entry.
Definition: conf.h:109
conf_integer_check
int conf_integer_check(enum conf_entry id, int min, int max)
Check integer value against lower and upper bounds.
Definition: conf.c:964
PRINT_ERROR
#define PRINT_ERROR(s)
Prepend module prefix and print error message.
Definition: main.h:19
CONF_TS_LTIME
Definition: conf.h:74
CONF_TVIEW
Definition: conf.h:53
CONF_NEWSRC
Definition: conf.h:49
CONF_PASS
Definition: conf.h:44
CONF_USER
Definition: conf.h:43
xdg_get_confdir
const char * xdg_get_confdir(const char *)
Get configuration directory.
Definition: xdg.c:116
conf_ephemeral_passwd
int conf_ephemeral_passwd
Flag indicating that password should not be stored in configfile.
Definition: conf.c:70
conf_load
int conf_load(struct conf *cfg)
Load configuration from config file.
Definition: conf.c:768
CONF_POS_X
Definition: conf.h:32
xdg_append_to_path
int xdg_append_to_path(const char **, const char *)
Append path component to buffer.
Definition: xdg.c:55
CONF_COLOR_LEVEL4
Definition: conf.h:89
CONF_TILE_X
Definition: conf.h:36
CONF_TS_COMMENT
Definition: conf.h:75
fu_sync
int fu_sync(int filedesc, FILE *stream)
Flush buffers of file.
Definition: fileutils.c:409
CONF_TILE_Y
Definition: conf.h:37
CONF_CRL_UPD_TS
Definition: conf.h:66
fu_close_file
void fu_close_file(int *filedesc, FILE **stream)
Close file (and potentially associated I/O stream)
Definition: fileutils.c:297
CONF_INV_ORDER
Definition: conf.h:69
conf::val
union conf_entry_val val
Definition: conf.h:113
CONF_TYPE_STRING
Definition: conf.h:98
CONF_SEARCH_CASE_IS
Definition: conf.h:78
CONF_ENABLE_UAGENT
Definition: conf.h:79
CONF_TYPE_INT
Definition: conf.h:97
CONF_NO_OVER
Definition: conf.h:70
CONF_QUOTEUNIFY
Definition: conf.h:60
CONF_TESTGRP_ERE
Definition: conf.h:61
CONF_ENC
Definition: conf.h:40
CONF_CRL_UPD_C
Definition: conf.h:67
CONF_PERM
#define CONF_PERM
Permissions for config file.
Definition: conf.c:51
CONF_CANCELKEY
Definition: conf.h:55
CONF_SERVER
Definition: conf.h:38
CONF_FLOWED_CRLF
Definition: conf.h:76
CONF_TLS_OWNCERTS
Definition: conf.h:63
CONF_FQDN
Definition: conf.h:48
CONF_IMMEDAUTH
Definition: conf.h:42
CONF_CRL_UPD_IV
Definition: conf.h:65
CONF_TESTGRP_KWORDS
Definition: conf.h:62
CONF_SIZE_Y
Definition: conf.h:35
CONF_PPROC
Definition: conf.h:57
fu_open_file
int fu_open_file(const char *pathname, int *filedesc, int mode, api_posix_mode_t perm)
Open file.
Definition: fileutils.c:246
conf::label
const char * label
Definition: conf.h:111

Generated at 2026-01-27 using  doxygen