conf.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /*! \file
3  * \brief Configuration handling
4  *
5  * Copyright (c) 2012-2023 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 (posix_mode_t) (POSIX_S_IRUSR | 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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*) 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  /* Colors of thread citation level */
392  conf_add_entry_int(cfg, CONF_COLOR_LEVEL1, "color_level1:", 140);
393  conf_add_entry_int(cfg, CONF_COLOR_LEVEL2, "color_level2:", 152);
394  conf_add_entry_int(cfg, CONF_COLOR_LEVEL3, "color_level3:", 91);
395  conf_add_entry_int(cfg, CONF_COLOR_LEVEL4, "color_level4:", 75);
396 
397  /* On success, store flag that configuration was initialized */
398  if(error) { PRINT_ERROR("Initializing configuration failed"); }
399  else { conf_initialized = 1; }
400 }
401 
402 
403 /* ========================================================================== */
404 /* Get config file pathname
405  *
406  * The caller is responsible to free the memory for the buffer on success.
407  */
408 
409 static int conf_get_confpathname(const char** confpathname,
410  const char* conffile)
411 {
412  int res = -1;
413  int rv;
414 
415  *confpathname = xdg_get_confdir(CFG_NAME);
416  if(NULL != *confpathname)
417  {
418  rv = fu_create_path(*confpathname, (posix_mode_t) POSIX_S_IRWXU);
419  if(0 == rv)
420  {
421  /* Store config file pathname */
422  rv = xdg_append_to_path(confpathname, conffile);
423  if(0 == rv)
424  {
425  res = 0;
426  }
427  }
428  }
429 
430  /* Free memory on error */
431  if(0 != res)
432  {
433  PRINT_ERROR("Cannot create config file pathname");
434  posix_free((void*) *confpathname);
435  *confpathname = NULL;
436  }
437 
438  return(res);
439 }
440 
441 
442 /* ========================================================================== */
443 /* Extract integer value from configuration data */
444 
445 static int conf_extract_entry(char* line, struct conf* target)
446 {
447  int res = -1;
448  size_t labelsize = strlen(target->label);
449  const char* string;
450  char* sbuf;
451  int val;
452  size_t i;
453 
454  /* Check line length */
455  if(strlen(line) > labelsize)
456  {
457  /* Search for label */
458  if(!strncmp(target->label, line, labelsize))
459  {
460  /* Found */
461  res = 0;
462  if(CONF_TYPE_INT == target->type)
463  {
464  /* Extract integer value */
465  string = &line[labelsize];
466  if(1 == sscanf(string, "%d", &val)) target->val.i = val;
467  }
468  else
469  {
470  /* Extract string */
471  i = labelsize;
472  while(line[i])
473  {
474  if(' ' != line[i]) { break; }
475  else ++i;
476  }
477  string = &line[i];
478  while(line[i])
479  {
480  if('\n' == line[i])
481  {
482  line[i] = 0;
483  break;
484  }
485  else ++i;
486  }
487  /*
488  * Allocate memory for string entry
489  * This memory is not released until program exit.
490  */
491  sbuf = (char*) posix_malloc(strlen(string) + (size_t) 1);
492  if(NULL == sbuf)
493  {
494  PRINT_ERROR("Cannot allocate memory for config file entry");
495  target->val.s = NULL;
496  res = -1;
497  }
498  else
499  {
500  strcpy(sbuf, string);
501  secure_clear_string(target->val.s);
502  posix_free((void*) target->val.s);
503  target->val.s = sbuf;
504  }
505  }
506  }
507  }
508 
509  return(res);
510 }
511 
512 
513 /* ========================================================================== */
514 /* Update configuration data */
515 
516 static int conf_update_entry(char** line, size_t len, struct conf* target)
517 {
518  int res = -1;
519  size_t labelsize = strlen(target->label);
520  char* string;
521  int reqd;
522 
523  /* Check line length */
524  if(strlen(*line) >= labelsize)
525  {
526  /* Search for label */
527  if(!strncmp(target->label, *line, labelsize))
528  {
529  /* Found => Insert new value */
530  res = 0;
531  target->found = 1;
532  do
533  {
534  reqd = -1;
535  if(CONF_TYPE_INT == target->type)
536  {
537  /* Update integer entry */
538  reqd = posix_snprintf(*line, len, "%s %d\n",
539  target->label, target->val.i);
540  }
541  if(CONF_TYPE_STRING == target->type)
542  {
543  /* Update string entry */
544  reqd = posix_snprintf(*line, len, "%s %s\n",
545  target->label, target->val.s);
546  }
547  if(0 > reqd) { break; }
548  else if(len <= (size_t) reqd)
549  {
550  /* Allocate more memory */
551  if(POSIX_SIZE_MAX <= (size_t) reqd) { break; }
552  string = (char*) posix_realloc(*line, (size_t) (reqd + 1));
553  if(NULL == string) { break; }
554  else { *line = string; }
555  }
556  }
557  while(len <= (size_t) reqd);
558  }
559  }
560 
561  return(res);
562 }
563 
564 
565 /* ========================================================================== */
566 /* Import configuration */
567 
568 static int conf_import_configuration(struct conf* cfg, FILE* fs)
569 {
570  int res = -1;
571  char* line = NULL;
572  size_t len = 0;
573  posix_ssize_t readlen;
574  unsigned int i;
575 
576  while(1)
577  {
578  /* Read line */
579  readlen = posix_getline(&line, &len, fs);
580  if(-1 == readlen)
581  {
582  if(POSIX_ENOMEM == posix_errno)
583  {
584  PRINT_ERROR("Cannot assign memory for config file parser");
585  }
586  else
587  {
588  /* Check for error */
589  if(ferror(fs))
590  {
591  PRINT_ERROR("Parse error in config file");
592  break;
593  }
594  /* Check for EOF */
595  if(feof(fs))
596  {
597  res = 0;
598  break;
599  }
600  }
601  }
602  if(0 >= readlen) { break; }
603  else
604  {
605  /* Extract data */
606  if(!strncmp("#", line, 1)) { continue; }
607  for(i = 0; i < CONF_NUM; ++i)
608  {
609  if(!conf_extract_entry(line, &cfg[i])) { break; }
610  }
611  }
612  }
613 
614  /* Release memory for line buffer */
615  secure_clear_string(line);
616  posix_free((void*) line);
617 
618  return(res);
619 }
620 
621 
622 /* ========================================================================== */
623 /* Export configuration */
624 
625 static int conf_export_configuration(struct conf* cfg,
626  FILE* fs, FILE* fs_tmp)
627 {
628  int res = -1;
629  char* line = NULL;
630  size_t len = 0;
631  posix_ssize_t readlen;
632  int rv;
633  unsigned int i;
634  size_t reqd;
635  char* p;
636 
637  while(1)
638  {
639  /* Read line */
640  readlen = posix_getline(&line, &len, fs);
641  if(-1 == readlen)
642  {
643  if(POSIX_ENOMEM == posix_errno)
644  {
645  PRINT_ERROR("Cannot assign memory for config file parser");
646  }
647  else
648  {
649  /* Check for error */
650  if(ferror(fs))
651  {
652  PRINT_ERROR("Parse error in config file");
653  break;
654  }
655  /* Check for EOF */
656  if(feof(fs))
657  {
658  res = 0;
659  break;
660  }
661  }
662  }
663  if(0 >= readlen) { break; }
664  else
665  {
666  /* Update data */
667  for(i = 0; i < CONF_NUM; ++i)
668  {
669  if(!conf_update_entry(&line, len, &cfg[i])) { break; }
670  }
671 
672  /* Write line to new config file */
673  rv = fprintf(fs_tmp, "%s", line);
674  if(0 > rv) { break; }
675  }
676  }
677 
678  /* Add missing entries to end of config file */
679  if(!res)
680  {
681  for(i = 0; i < CONF_NUM; ++i)
682  {
683  if(!cfg[i].found)
684  {
685  /* Create new entry */
686  reqd = strlen(cfg[i].label);
687  if(POSIX_SIZE_MAX <= reqd) { break; }
688  if(reqd + (size_t) 1 > len)
689  {
690  /* Allocate more memory */
691  p = (char*) posix_realloc(line, reqd + (size_t) 1);
692  if (NULL != p) line = p;
693  }
694  strncpy(line, cfg[i].label, len);
695  rv = conf_update_entry(&line, len, &cfg[i]);
696  /* Write new entry */
697  if(!rv)
698  {
699  rv = fprintf(fs_tmp, "%s", line);
700  if(0 > rv)
701  {
702  res = -1;
703  break;
704  }
705  }
706  }
707  }
708  }
709 
710  /* Release memory for line buffer */
711  posix_free((void*) line);
712 
713  return(res);
714 }
715 
716 
717 /* ========================================================================== */
718 /*! \brief Delete configuration
719  *
720  * This function will free the memory allocated for the configuration array
721  * entries with string type.
722  *
723  * \param[in] cfg Pointer to configuration array
724  */
725 
726 void conf_delete(struct conf* cfg)
727 {
728  unsigned int i;
729 
730  if(conf_initialized)
731  {
732  for(i = 0; i < CONF_NUM; ++i)
733  {
734  if(CONF_TYPE_STRING == cfg[i].type)
735  {
737  posix_free((void*) cfg[i].val.s);
738  cfg[i].val.s = NULL;
739  }
740  }
741  }
742 
743  /* Clear flag that configuration is initialized */
744  conf_initialized = 0;
745 }
746 
747 
748 /* ========================================================================== */
749 /*! \brief Load configuration from config file
750  *
751  * \param[in] cfg Pointer to configuration array
752  *
753  * For configuration entries with string type, this function allocates memory.
754  * On success the caller is responsible to free this memory with the function
755  * \ref conf_delete() .
756  *
757  * \return
758  * - 0 on success
759  * - Negative value on error
760  */
761 
762 int conf_load(struct conf* cfg)
763 {
764  int res = -1;
765  int fd = -1;
766  FILE* fs = NULL;
767  const char* confpathname = NULL;
768 
769  /* Init configuration with default values */
770  conf_delete(cfg);
771  conf_init_configuration(cfg);
772  if(conf_initialized)
773  {
774  /* Get config file pathname */
775  res = conf_get_confpathname(&confpathname, confname);
776  if(!res)
777  {
778  if(main_debug)
779  {
780  printf("%s: %sLoad from: %s\n", CFG_NAME, MAIN_ERR_PREFIX,
781  confpathname);
782  }
783 
784  /* Open and lock config file */
785  res = fu_open_file(confpathname, &fd, POSIX_O_RDWR | POSIX_O_CREAT,
786  CONF_PERM);
787  if(!res) { res = fu_lock_file(fd); }
788  if(!res) { res = fu_assign_stream(fd, &fs, "r"); }
789 
790  /* Import configuration from config file */
791  if(!res) { res = conf_import_configuration(cfg, fs); }
792  }
793 
794  /* Close config file (this will also release the lock) */
795  fu_close_file(&fd, &fs);
796 
797  /* Release memory for pathname */
798  posix_free((void*) confpathname);
799 
800  /* Delete configuration again on error */
801  if(res) { conf_delete(cfg); }
802  }
803 
804  return(res);
805 }
806 
807 
808 /* ========================================================================== */
809 /*! \brief Store configuration to config file
810  *
811  * \param[in] cfg Pointer to configuration array
812  *
813  * \return
814  * - 0 on success
815  * - Negative value on error
816  */
817 
818 int conf_store(struct conf* cfg)
819 {
820  int res = -1;
821  int fd = -1;
822  FILE* fs = NULL;
823  int fd_tmp = -1;
824  FILE* fs_tmp = NULL;
825  const char* confpathname = NULL;
826  const char* tmppathname = NULL;
827 
828  /* Get file pathnames */
829  res = conf_get_confpathname(&confpathname, confname);
830  if(!res) { res = conf_get_confpathname(&tmppathname, tmpname); }
831 
832  /* Open and lock config file */
833  if(!res)
834  {
835  res = fu_open_file(confpathname, &fd, POSIX_O_RDWR | POSIX_O_CREAT,
836  CONF_PERM);
837  if(!res) { res = fu_lock_file(fd); }
838  if(!res) { res = fu_assign_stream(fd, &fs, "r"); }
839  }
840 
841  /* Open temporary file for new configuration */
842  if(!res)
843  {
844  res = fu_open_file(tmppathname, &fd_tmp,
845  POSIX_O_WRONLY | POSIX_O_CREAT | POSIX_O_TRUNC,
846  CONF_PERM);
847  /*
848  * Because we have the lock for the config file, it is allowed to
849  * assume that no other instance of the program currently use the
850  * temporary filename.
851  */
852  if(!res) { res = fu_assign_stream(fd_tmp, &fs_tmp, "w"); }
853  }
854 
855  /* Export configuration to temporary file */
856  if(!res)
857  {
859  {
860  /* Delete password from configuration */
862  }
863  res = conf_export_configuration(cfg, fs, fs_tmp);
864  }
865 
866  /* Flush stream of temporary file */
867  if(!res) { res = fu_sync(fd_tmp, fs_tmp); }
868 
869  /* Rename temporary file to config file */
870  if(!res) { res = posix_rename(tmppathname, confpathname); }
871 
872  /* Try to unlink temporary file after error */
873  if(res)
874  {
875  PRINT_ERROR("Failed to store configuration in config file");
876  if(tmppathname) { (void) fu_unlink_file(tmppathname); }
877  }
878 
879  /* The tmp file must be removed before releasing the config file lock! */
880  fu_close_file(&fd_tmp, &fs_tmp);
881 
882  /* Close config file (this will also release the lock) */
883  fu_close_file(&fd, &fs);
884 
885  if(main_debug)
886  {
887  printf("%s: %sStore to: %s\n", CFG_NAME, MAIN_ERR_PREFIX, confpathname);
888  }
889 
890  /* Release memory for file pathnames */
891  posix_free((void*) tmppathname);
892  posix_free((void*) confpathname);
893 
894  return(res);
895 }
896 
897 
898 /* ========================================================================== */
899 /*! \brief Replace configuration string
900  *
901  * \param[in] cfg Pointer to configuration array entry
902  * \param[in] s Pointer to new string
903  *
904  * \e cfg must point to a configuration entry of string type.
905  * The current string will be replaced with \e s .
906  *
907  * \return
908  * - 0 on success
909  * - Negative value on error
910  */
911 
912 int conf_string_replace(struct conf* cfg, const char* s)
913 {
914  int res = -1;
915  size_t len;
916  char* p;
917 
918  if(NULL != cfg && NULL != cfg->val.s && NULL != s)
919  {
920  if(CONF_TYPE_STRING == cfg->type)
921  {
922  len = strlen(s);
923  p = posix_malloc(++len);
924  if(NULL != p)
925  {
926  strcpy(p, s);
927  secure_clear_string(cfg->val.s);
928  posix_free((void*) cfg->val.s);
929  cfg->val.s = p;
930  res = 0;
931  }
932  }
933  }
934 
935  return(res);
936 }
937 
938 
939 /* ========================================================================== */
940 /*! \brief Check integer value against lower and upper bounds
941  *
942  * \param[in] id ID of integer value to check
943  * \param[in] min Lower limit
944  * \param[in] max Upper limit
945  *
946  * \attention The parameter \e id must be associated with an integer value!
947  *
948  * The integer value associated with \e id is compared against the specified
949  * limits.
950  * <br>
951  * If the value is lower than \e min or greater than \e max , it is clamped to
952  * the limits. Otherwise the value is unchanged.
953  *
954  * \return
955  * - New value
956  */
957 
958 int conf_integer_check(enum conf_entry id, int min, int max)
959 {
960  if(min > config[id].val.i)
961  {
962  config[id].val.i = min;
963  }
964 
965  if(max < config[id].val.i)
966  {
967  config[id].val.i = max;
968  }
969 
970  return(config[id].val.i);
971 }
972 
973 
974 /*! @} */
975 
976 /* 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:116
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:86
fu_assign_stream
int fu_assign_stream(int filedesc, FILE **stream, const char *mode)
Assign I/O stream to open file.
Definition: fileutils.c:373
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:110
fu_lock_file
int fu_lock_file(int filedesc)
Lock file for writing.
Definition: fileutils.c:328
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:103
conf_store
int conf_store(struct conf *cfg)
Store configuration to config file.
Definition: conf.c:818
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:912
fu_create_path
int fu_create_path(const char *path, posix_mode_t perm)
Create path.
Definition: fileutils.c:119
CONF_SCORERC
Definition: conf.h:50
CONF_REFRESH_INTERVAL
Definition: conf.h:81
CONF_COLOR_LEVEL1
Definition: conf.h:85
main_debug
int main_debug
Enable additional debug output if nonzero.
Definition: main.cxx:64
conf::found
int found
Definition: conf.h:112
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:87
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:102
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:726
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:355
CONF_INITIAL_GREETING
Definition: conf.h:80
conf
Configuration array entry.
Definition: conf.h:107
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:958
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:115
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:762
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:88
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:402
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:290
CONF_INV_ORDER
Definition: conf.h:69
conf::val
union conf_entry_val val
Definition: conf.h:111
CONF_TYPE_STRING
Definition: conf.h:96
CONF_SEARCH_CASE_IS
Definition: conf.h:78
CONF_ENABLE_UAGENT
Definition: conf.h:79
CONF_TYPE_INT
Definition: conf.h:95
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, posix_mode_t perm)
Open file.
Definition: fileutils.c:243
conf::label
const char * label
Definition: conf.h:109

Generated at 2024-04-27 using  doxygen