1 /*
2 Copyright (C) 2012-2021, Dirk Krause
3 SPDX-License-Identifier: BSD-3-Clause
4 */
5 
6 /*
7 	WARNING: This file was generated by the dkct program (see
8 	http://dktools.sourceforge.net/ for details).
9 	Changes you make here will be lost if dkct is run again!
10 	You should modify the original source and run dkct on it.
11 	Original source: itaconf.ctr
12 */
13 
14 /**	@file itaconf.c The itaconf module.
15 */
16 
17 
18 #include <libdk3c/dk3all.h>
19 #include <itadmin/itadmin.h>
20 
21 
22 
23 
24 
25 
26 
27 /**	Key names in configuration file.
28 */
29 static dkChar const * const	itadmin_config_keys[] = {
30 /* 0 */
31 dkT("database-host"),
32 
33 /* 1 */
34 dkT("database-name"),
35 
36 /* 2 */
37 dkT("database-credentials-file"),
38 
39 /* 3 */
40 dkT("dhcpd.conf-vlan"),
41 
42 /* 4 */
43 dkT("organization"),
44 
45 /* 5 */
46 dkT("organizational-unit"),
47 
48 /* 6 */
49 dkT("administrator-name"),
50 
51 /* 7 */
52 dkT("create-dhcpd.conf"),
53 
54 /* 8 */
55 dkT("database-type"),
56 
57 /* 9 */
58 dkT("ldap-base"),
59 
60 /* 10 */
61 dkT("use-koma-script"),
62 
63 /* 11 */
64 dkT("msft-release-lease-on-shutdown"),
65 
66 /* 12 */
67 dkT("marker-point"),
68 
69 /* 13 */
70 dkT("marker-vlan"),
71 
72 /* 14 */
73 dkT("marker-subnet"),
74 
75 /* 15 */
76 dkT("marker-group"),
77 
78 /* 16 */
79 dkT("marker-pool"),
80 
81 /* 17 */
82 dkT("marker-host"),
83 
84 NULL
85 
86 };
87 
88 
89 
90 /**	Items for "msft release lease on shutdown".
91 */
92 static const char * const	msft_release_keys[] = {
93 "none", "pool", "all", NULL
94 };
95 
96 
97 /**	Names of SQL database types.
98 */
99 static dkChar const * const	itadmin_config_database_types[] = {
100 /* 0 */
101 dkT("MySQL"),
102 
103 NULL
104 
105 };
106 
107 
108 
109 /**	Keywords to watch out for when processing a credential file.
110 	Typically this is a .my.cnf file.
111 */
112 static dkChar const * const itadmin_config_credential_keys[] = {
113 /* 0 */
114 dkT("client"),
115 
116 /* 1 */
117 dkT("user"),
118 
119 /* 2 */
120 dkT("password"),
121 
122 NULL
123 
124 };
125 
126 
127 
128 /**	Set one string entry in job structure.
129 	@param	job	Job structure to modify.
130 	@param	resptr	Pointer to result pointer.
131 	@param	srcptr	Source string.
132 	@return	1 on success, 0 on error.
133 */
134 static
135 int
itadmin_config_set_string(itadmin_job * job,dkChar const ** resptr,dkChar const * srcptr)136 itadmin_config_set_string(
137   itadmin_job	 *job,
138   dkChar const	**resptr,
139   dkChar const	 *srcptr
140 )
141 {
142   dkChar const	*np;		/* New string duplicate. */
143   int		 back	= 0;
144 
145   np = dk3str_dup_app(srcptr,job->app);
146   if(np) {
147     dk3_release(*resptr);
148     *resptr = np;
149     back = 1;
150   } else {
151     /* ERROR: Memory allocation failed! */
152   }
153   return back;
154 }
155 
156 
157 
158 /**	Configure a boolean value setting.
159 	@param	job	Job structure.
160 	@param	bp	Pointer to result variable.
161 	@param	defv	Default value, used for empty or non-boolean strings.
162 	@param	txt	Configuration text.
163 	@return	1 on success, 0 on error.
164 */
165 static
166 int
itadmin_config_set_bool(itadmin_job * job,int * bp,int defv,dkChar const * txt)167 itadmin_config_set_bool(itadmin_job *job, int *bp, int defv, dkChar const *txt)
168 {
169   dkChar	 lb[ITADMIN_CONFIG_LINE_SIZE];	/* Copy of value. */
170   dkChar	*p1;				/* Start of text in copy. */
171   int		 back	= 1;
172 
173   if(txt) {
174     p1 = dk3str_start(txt, NULL);
175     if(p1) {
176       if(dk3str_len(p1) < DK3_SIZEOF(lb,dkChar)) {
177         dk3str_cpy_not_overlapped(lb, p1);
178         dk3str_normalize(lb,NULL,dkT(' '));
179 	if(dk3str_is_bool(lb)) {
180 	  *bp = ((dk3str_is_on(lb)) ? 1 : 0);
181 	} else {
182 	  back = 0;
183 	  *bp = defv;
184 	  /* ERROR: Not a boolean! */
185 	  dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 106, 107, lb);
186 	}
187       } else {
188         back = 0;
189 	*bp = defv;
190 	/* ERROR: Text too long! */
191 	dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 108, 109, p1);
192       }
193     } else {
194       *bp = defv;
195     }
196   } else {
197     *bp = defv;
198   }
199   return back;
200 }
201 
202 
203 
204 /**	Set database type.
205 	@param	job	Job structure.
206 	@param	txt	Text for database type (at this time only "MySQL"
207 	can be used).
208 	@return	1 on success, 0 on error.
209 */
210 static
211 int
itadmin_config_set_dbtype(itadmin_job * job,dkChar const * txt)212 itadmin_config_set_dbtype(itadmin_job *job, dkChar const *txt)
213 {
214   int		back	= 0;
215   int		i;		/* Index of type in array of known types. */
216   if(txt) {
217     i = dk3str_array_index(itadmin_config_database_types, txt, 0);
218     if(i >= 0) {
219       job->dbt = i;
220       back = 1;
221     } else {
222       /* ERROR: No such database type! */
223       dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 110, 111, txt);
224     }
225   } else {
226     /* ERROR: Empty text. */
227     dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 112);
228   }
229   return back;
230 }
231 
232 
233 
234 
235 /**	Handler function for one configuration file line.
236 	@param	obj	Reader object.
237 	@param	il	Input line to process (constant text).
238 	@return	1 for OK, 0 for recoverable error, -1 for abort.
239 */
240 static
241 int
itadmin_config_const_line_handler(void * obj,dkChar const * il)242 itadmin_config_const_line_handler(void *obj, dkChar const *il)
243 {
244   dkChar	 line[ITADMIN_CONFIG_LINE_SIZE]; /* Private copy of line.*/
245   itadmin_job	*job;
246   dkChar	*p1;			/* Start of line. */
247   dkChar	*p2;			/* Start of value in line. */
248   int		 back = 0;
249 
250   if((obj) && (il)) {
251     job = (itadmin_job *)obj;
252     if(dk3str_len(il) < DK3_SIZEOF(line,dkChar)) {
253       dk3str_cpy_not_overlapped(line, il);
254       dk3str_delnl(line);
255       p1 = dk3str_start(line, NULL);
256       if(p1) {
257         if(*p1 == dkT('#')) {
258 	  back = 1;	/* Comment line. */
259 	} else {
260 	  p2 = dk3str_chr(p1, dkT('='));
261 	  if(p2) {
262 	    *(p2++) = dkT('\0');
263 	    p2 = dk3str_start(p2, NULL);
264 	    if(p2) {
265 	      dk3str_normalize(p1, NULL, dkT('-'));
266 	      switch(dk3str_array_index(itadmin_config_keys, p1, 0)) {
267 	        case 0: {	/* database host */
268 		  back = itadmin_config_set_string(job, &(job->dbhn), p2);
269 		} break;
270 		case 1: {	/* database name */
271 		  back = itadmin_config_set_string(job, &(job->dbn), p2);
272 		} break;
273 		case 2: {	/* credentials file */
274 		  back = itadmin_config_set_string(job, &(job->dbcf), p2);
275 		} break;
276 		case 3: {	/* VLAN name */
277 		  back = itadmin_config_set_string(job, &(job->vlan), p2);
278 		  if(back) {
279 		    job->f_dh = 1;
280 		  }
281 		} break;
282 		case 4: {	/* organization */
283 		  back = itadmin_config_set_string(job, &(job->org), p2);
284 		} break;
285 		case 5: {	/* organizational unit */
286 		  back = itadmin_config_set_string(job, &(job->ou), p2);
287 		} break;
288 		case 6: {	/* administrator name */
289 		  back = itadmin_config_set_string(job, &(job->admn), p2);
290 		} break;
291 		case 7: {
292 		  back = itadmin_config_set_bool(job, &(job->f_dh), 1, p2);
293 		} break;
294 		case 8: {
295 		  back = itadmin_config_set_dbtype(job, p2);
296 		} break;
297 		case 9: {
298 		  dk3str_normalize(p2, NULL, ' ');
299 		  back = itadmin_config_set_string(job, &(job->ldapb), p2);
300 		} break;
301 		case 10: {
302 		  back = itadmin_config_set_bool(job, &(job->ukoma), 0, p2);
303 		} break;
304 		case 11: {
305 	          switch(dk3str_array_index(msft_release_keys, p2, 0)) {
306 		    case 0: {
307 		      job->rel = 0; back = 1;
308 		    } break;
309 		    case 1: {
310 		      job->rel = 1; back = 1;
311 		    } break;
312 		    case 2: {
313 		      job->rel = 2; back = 1;
314 		    } break;
315 		    default: {
316 		      /* ##### ERROR: Illegal keyword */
317 		    } break;
318 		  }
319 		} break;
320 		case 12: {
321 		  back = itadmin_config_set_bool(job, &(job->m_p), 1, p2);
322 		} break;
323 		case 13: {
324 		  back = itadmin_config_set_bool(job, &(job->m_vl), 1, p2);
325 		} break;
326 		case 14: {
327 		  back = itadmin_config_set_bool(job, &(job->m_sn), 1, p2);
328 		} break;
329 		case 15: {
330 		  back = itadmin_config_set_bool(job, &(job->m_gr), 1, p2);
331 		} break;
332 		case 16: {
333 		  back = itadmin_config_set_bool(job, &(job->m_po), 1, p2);
334 		} break;
335 		case 17: {
336 		  back = itadmin_config_set_bool(job, &(job->m_ho), 0, p2);
337 		} break;
338 		default: {
339 		  /* ERROR: Unknown option ... */
340 		  dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 113, 114, p1);
341 		} break;
342 	      }
343 	    } else {
344 	      /* ERROR: No value! */
345 	      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 115);
346 	    }
347 	  } else {
348 	    /* ERROR: Missing '=' */
349 	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 115);
350 	  }
351 	}
352       } else {
353         back = 1;	/* Empty line. */
354       }
355     } else {
356       /* ERROR: Input line too long! */
357       dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 116);
358     }
359   }
360   return back;
361 }
362 
363 
364 
365 /**	Handler function for one configuration file line.
366 	@param	obj	Reader object.
367 	@param	il	Input line to process.
368 	@return	1 for OK, 0 for recoverable error, -1 for abort.
369 */
370 static
371 int
itadmin_cred_file_const_line_handler(void * obj,dkChar const * il)372 itadmin_cred_file_const_line_handler(void *obj, dkChar const *il)
373 {
374   dkChar		 line[ITADMIN_CONFIG_LINE_SIZE]; /* Copy of line. */
375   dkChar		*p1;		/* Start of key. */
376   dkChar		*p2;		/* Start of value. */
377   itadmin_cred_reader	*reader;	/* Help structure to read creds. */
378   int			 back	= 0;
379 
380   if((obj) && (il)) {
381     reader = (itadmin_cred_reader *)obj;
382     if(dk3str_len(il) < DK3_SIZEOF(line,dkChar)) {
383       dk3str_cpy_not_overlapped(line, il);
384       dk3str_delnl(line);
385       p1 = dk3str_start(line, NULL);
386       if(p1) {
387         if(*p1 == dkT('#')) {
388 	  back = 1;		/* Ignore comment. */
389 	} else {
390 	  if(*p1 == dkT('[')) {
391 	    reader->f_clnt = 0;
392 	    p1++;
393 	    p2 = dk3str_chr(p1, dkT(']'));
394 	    if(p2) {
395 	      back = 1;
396 	      *p2 = dkT('\0');
397 	      if(dk3str_cmp(p1, itadmin_config_credential_keys[0]) == 0) {
398 	        reader->f_clnt = 1;
399 	      }
400 	    } else {
401 	      /* ERROR: Syntax! */
402 	      dk3app_log_1(
403 	        (reader->job)->app, DK3_LL_ERROR, (reader->job)->msg, 117
404 	      );
405 	    }
406 	  } else {
407 	    back = 1;
408 	    p2 = dk3str_chr(p1, dkT('='));
409 	    if(p2) {
410 	      *(p2++) = dkT('\0');
411 	      p2 = dk3str_start(p2, NULL);
412 	      if(p2) {
413 	        dk3str_normalize(p1,NULL,dkT(' '));
414 		switch(dk3str_array_index(itadmin_config_credential_keys,p1,0))
415 		{
416 		  case 1: {	/* user */
417 		    back = itadmin_config_set_string(
418 		      reader->job, &((reader->job)->dbus), p2
419 		    );
420 		  } break;
421 		  case 2: {	/* password */
422 		    back = itadmin_config_set_string(
423 		      reader->job, &((reader->job)->dbpw), p2
424 		    );
425 		  } break;
426 		}
427 	      } else {
428 	        /* Empty key. */
429 	      }
430 	    } else {
431 	      /* Not a key=value line. */
432 	    }
433 	  }
434 	}
435       } else {
436         back = 1;		/* Ignore empty line. */
437       }
438     } else {
439       /* ERROR: Line too long! */
440       dk3app_log_1(
441         (reader->job)->app, DK3_LL_ERROR, (reader->job)->msg, 116
442       );
443     }
444   } else {
445   }
446   return back;
447 }
448 
449 
450 
451 /**	Handler function for one configuration file line.
452 	@param	obj	Reader object.
453 	@param	il	Input line to process.
454 	@return	1 for OK, 0 for recoverable error, -1 for abort.
455 */
456 static
457 int
itadmin_cred_file_line_handler(void * obj,dkChar * il)458 itadmin_cred_file_line_handler(void *obj, dkChar *il)
459 {
460   int back;
461   back = itadmin_cred_file_const_line_handler(obj, il);
462   return back;
463 }
464 
465 
466 
467 /**	Handler function for one configuration file line.
468 	@param	obj	Reader object.
469 	@param	il	Input line to process.
470 	@return	1 for OK, 0 for recoverable error, -1 for abort.
471 */
472 static
473 int
itadmin_config_line_handler(void * obj,dkChar * il)474 itadmin_config_line_handler(void *obj, dkChar *il)
475 {
476   int		back;
477   back = itadmin_config_const_line_handler(obj, il);
478   return back;
479 }
480 
481 
482 
483 /**	Process one configuration file.
484 	@param	job	Job structure.
485 	@param	fn	File name to process.
486 	@return	1 on success, 0 on error.
487 */
488 static
489 int
itadmin_config_process_one_file(itadmin_job * job,dkChar const * fn)490 itadmin_config_process_one_file(itadmin_job *job, dkChar const *fn)
491 {
492   dkChar	line[ITADMIN_CONFIG_LINE_SIZE];	/* Copy of line. */
493   int		back	= 0;
494 
495   back = dk3stream_process_filename_lines_app(
496     (void *)job,
497     itadmin_config_line_handler,
498     fn,
499     line,
500     DK3_SIZEOF(line,dkChar),
501     dk3app_get_encoding(job->app),
502     dk3app_get_input_file_encoding(job->app),
503     job->app
504   );
505   if(back < 1) {
506     back = 0;
507   }
508   return back;
509 }
510 
511 
512 
513 /**	Read credentials file to obtain database user name and password.
514 	@param	job	Job structure.
515 	@return	1 on success, 0 on error.
516 */
517 static
518 int
itadmin_config_read_credentials(itadmin_job * job)519 itadmin_config_read_credentials(itadmin_job *job)
520 {
521   dkChar		fnb[DK3_MAX_PATH];		/* File name buffer. */
522   dkChar		line[ITADMIN_CONFIG_LINE_SIZE];	/* Input line buffer. */
523   itadmin_cred_reader	reader;		/* Helper structure to read creds. */
524   int			back	= 0;
525 
526   if(dk3str_len(job->dbcf) < DK3_SIZEOF(fnb,dkChar)) {
527     dk3str_cpy_not_overlapped(fnb, job->dbcf);
528     dk3str_correct_filename(fnb);
529     if(dk3sf_must_expand(fnb)) {
530       /* ERROR: File name must not contain wildcards! */
531       dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 118, 119, fnb);
532     } else {
533       if(!dk3str_is_abs_path(fnb)) {
534         /* WARNING: File name should be specified as absolute path! */
535 	dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 120, 121, fnb);
536       }
537       reader.job = job;
538       reader.f_clnt = 1;
539       back = dk3stream_process_filename_sanitized_lines_app(
540         (void *)(&reader),
541         itadmin_cred_file_line_handler,
542         fnb,
543         line,
544         DK3_SIZEOF(line,dkChar),
545         dk3app_get_encoding(job->app),
546         dk3app_get_input_file_encoding(job->app),
547         job->app
548       );
549     }
550   } else {
551     /* ERROR: File name too long! */
552     dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, job->dbcf);
553   }
554   if(job->ec) {
555     back = 0;
556   }
557   return back;
558 }
559 
560 
561 
562 int
itadmin_config_read(itadmin_job * job)563 itadmin_config_read(itadmin_job *job)
564 {
565   dkChar		 fnb[DK3_MAX_PATH];	/* Copy of file name. */
566   dk3_search_t		*res;			/* Search result, config. */
567   dk3_dir_t		*fne;			/* File name expander. */
568   dk3_stat_t const	*es;			/* Expandend file status. */
569   dkChar const		*fn;			/* File name. */
570   dkChar const		*en;			/* Expanded file name. */
571   dkChar const		*fnorig;		/* Original file name. */
572   int			 back	= 0;
573   int			 have_file = 0;		/* Flag: File found. */
574   int			 have_error = 0;	/* Flag: Error occured. */
575   int			 argc;			/* Number of file names. */
576   int			 i;			/* Current file name index. */
577 
578   /*
579   	Process default configuration files.
580   */
581   res = dk3app_find_config_file(job->app, (job->noloc)[4], 0);
582   if(res) {
583     dk3search_reset(res);
584     while((fn = dk3search_next(res)) != NULL) {
585       have_file = 1;
586       if(!itadmin_config_process_one_file(job, fn)) {
587         have_error = 1;
588       }
589     }
590     dk3search_close(res);
591   }
592   /*
593   	Process configuration file(s) specified on command line.
594   */
595   argc = dk3opt_get_num_args(job->opt);
596   for(i = 0; i < argc; i++) {
597     fnorig = dk3opt_get_arg(job->opt, i);
598     if(fnorig) {
599       if(dk3str_len(fnorig) < DK3_SIZEOF(fnb,dkChar)) {
600         dk3str_cpy_not_overlapped(fnb, fnorig);
601 	dk3str_correct_filename(fnb);
602 	if(dk3sf_must_expand(fnb)) {
603 	  fne = dk3dir_fne_open_app(fnb, job->app);
604 	  if(fne) {
605 	    while(dk3dir_get_next_file(fne)) {
606 	      en = dk3dir_get_fullname(fne);
607 	      es = dk3dir_get_stat(fne);
608 	      if((en) && (es)) {
609 	        switch((es->ft) & (~(DK3_FT_SYMLINK))) {
610 		  case DK3_FT_REGULAR: {
611 	            have_file = 1;
612 	            if(!itadmin_config_process_one_file(job, fnb)) {
613 	              have_error = 1;
614 	            }
615 		  } break;
616 		  default: {
617 		    have_error = 1;
618 		    /* ERROR: Wrong file type! */
619 		    dk3app_log_i3(job->app, DK3_LL_ERROR, 255, 256, en);
620 		  } break;
621 		}
622 	      } else {
623 	        /* BUG: No complete information available! */
624 	      }
625 	    }
626 	    dk3dir_close(fne);
627 	  } else {
628 	    /* ERROR: Failed to expand file name! */
629 	  }
630 	} else {
631 	  have_file = 1;
632 	  if(!itadmin_config_process_one_file(job, fnb)) {
633 	    have_error = 1;
634 	  }
635 	}
636       } else {
637         /* ERROR: File name fnorig too long! */
638 	dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, fnorig);
639       }
640     } else {
641       /* BUG: No pointer returned! */
642     }
643   }
644   /*
645   	Final tests and error messages.
646   */
647   if(have_file) {
648     if(!(have_error)) {
649       back = 1;
650       if(!(job->dbhn)) {
651         /* ERROR: Missing database host name! */
652 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 122);
653 	back = 0;
654       }
655       if(!(job->dbn)) {
656         /* ERROR: Missing database name! */
657 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 123);
658 	back = 0;
659       }
660       if(!(job->vlan)) {
661         /* Warning: Missing VLAN name! */
662 	dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 124);
663       }
664       if(!(job->org)) {
665         /* ERROR: Missing organization name! */
666 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 125);
667 	back = 0;
668       }
669       if(!(job->ou)) {
670         /* ERROR: Missing organizational unit name! */
671 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 126);
672 	back = 0;
673       }
674       if(!(job->admn)) {
675         /* ERROR: Missing administrator name! */
676 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 127);
677 	back = 0;
678       }
679       if(!(job->dbcf)) {
680         /* ERROR: Missing database credentials file name! */
681 	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 128);
682 	back = 0;
683       } else {
684         if(back) {
685 	  back = itadmin_config_read_credentials(job);
686 	  if(back) {
687 	    if(!(job->dbus)) {
688 	      /* ERROR: Missing database user name! */
689 	      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 129);
690 	      back = 0;
691 	    }
692 	    if(!(job->dbpw)) {
693 	      /* ERROR: Missing database user password! */
694 	      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 130);
695 	      back = 0;
696 	    }
697 	  }
698 	}
699       }
700       if(back) {
701         if(!(job->ukoma)) {
702 	  if(dk3str_casecmp(dkT("de"), dk3app_get_language(job->app)) == 0) {
703 	    dk3app_log_1(job->app, DK3_LL_INFO, job->msg, 302);
704 	  }
705 	}
706       }
707       if(!(back)) {
708         dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 132);
709       }
710     } else {
711     }
712   } else {
713     /* ERROR: No configuration file found for processing! */
714     dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 131);
715   }
716   return back;
717 }
718 
719