1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2011-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8
9%%	module
10
11#include <libdk3c/dk3all.h>
12#include <dkwt/dkt.h>
13#include <dkwt/dkwt.h>
14#include <libdk3c/dk3unused.h>
15
16
17
18$!trace-include
19
20
21
22/**	Job structure for dkwt boot/login/logout/shutdown.
23*/
24typedef struct {
25  dk3_app_t		*app;		/**< Application structure. */
26  dkChar const * const	*msg;		/**< Localized message texts. */
27  dkChar const * const	*kwnl;		/**< Keywords, not localized. */
28  dk3_option_set_t	*opt;		/**< Options set. */
29  dkChar const		*userName;	/**< User name. */
30  dkChar const		*profBase;	/**< Profile base directory. */
31  dkChar const		*systemUser;	/**< System user account. */
32  dkChar const		*everyUser;	/**< Everyone user account. */
33  dk3_sto_t		*s_users;	/**< Storage users. */
34  dk3_sto_it_t		*i_users;	/**< Iterator users. */
35  dk3_sto_t		*s_pubdir;	/**< Storage public directories. */
36  dk3_sto_it_t		*i_pubdir;	/**< Iterator public directories. */
37  dk3_sto_t		*s_queues;	/**< Remote queues to clean up. */
38  dk3_sto_it_t		*i_queues;	/**< Iterator for remote queues. */
39  int			 exval;		/**< Exit status code. */
40  int			 event;		/**< Event to process. */
41  int			 curSection;	/**< Current configuration file sect. */
42  int			 useIcacls;	/**< Flag: Use icalcs. */
43  int			 clrProfiles;	/**< Flag: Clean profiles automatically. */
44  int			 testOnly;	/**< Flag: Test only. */
45  int			 clp;		/**< Flag: Clean up local print queues. */
46} DKWT_EV_J;
47
48
49/**	Unknown section type.
50*/
51#define	DKWT_EV_SECTION_UNKNOWN		(-1)
52
53/**	In the options section.
54*/
55#define	DKWT_EV_SECTION_OPTIONS		0
56
57/**	In the profiles section.
58*/
59#define	DKWT_EV_SECTION_PROFILES	1
60
61/**	In the public directories section.
62*/
63#define DKWT_EV_SECTION_PUBLIC		2
64
65
66
67/**	Configuration file section names.
68*/
69static dkChar const * const dkwt_ev_section_names[] = {
70$!string-table	macro=dkT
71#  0
72options
73#  1
74profiles
75#  2
76public
77$!end
78};
79
80
81
82/**	Configuration file option names.
83*/
84static dkChar const * const dkwt_ev_option_names[] = {
85$!string-table	macro=dkT
86# 0
87system-user
88# 1
89everyone-user
90# 2
91print-jobs
92$!end
93};
94
95
96
97/**	Names of profiles to keep.
98*/
99static dkChar const * const dkwt_ev_profiles_to_keep[] = {
100$!text macro=dkT
101All Users
102Default User
103Public
104LocalService
105NetworkService
106Administrator
107$!end
108};
109
110
111
112/**	Options.
113*/
114static dk3_option_t const dkwt_ev_options[] = {
115  { dkT('t'), dkT("test"), 0 }
116};
117
118/**	Number of options in the dkwt_ev_options array.
119*/
120static size_t const dkwt_ev_szoptions =
121sizeof(dkwt_ev_options)/sizeof(dk3_option_t);
122
123
124
125/**	Print queue name for local printers.
126*/
127static dkChar const dkwt_pq_name_local[] = { dkT("local") };
128
129
130/**	Retrieve value of keep flag.
131	@param	kwnl	Keywords, not localized.
132	@param	reset	Reset the flag.
133	@return	Flag value (before reset).
134*/
135static
136int
137dkwt_ev_get_keep_value(
138  dkChar const * const	*kwnl,
139  int			 reset
140)
141{
142  long		result;
143#if VERSION_BEFORE_2019_10_20
144  DWORD		dw0;
145#endif
146  DWORD		dwType;
147  DWORD		dwValue;
148  DWORD		dwSz;
149  int		back = 0;
150  HKEY		hk;
151#if VERSION_BEFORE_2019_10_20
152  REGSAM	regsam;
153#endif
154
155  $? "+ dkwt_ev_get_keep_value"
156#if VERSION_BEFORE_2019_10_20
157  dw0 = (DWORD)0;
158#endif
159  dwType = REG_DWORD;
160  dwValue = 0;
161  dwSz = sizeof(DWORD);
162#if VERSION_BEFORE_2019_10_20
163  /*
164  	KEY_QUERY_VALUE is always used in the
165	dkwt_tool_reg_open_key() function call below.
166  */
167  regsam = KEY_QUERY_VALUE;
168  if(reset) {
169    regsam = (KEY_READ | KEY_WRITE);
170  }
171#endif
172  result = dkwt_tool_reg_open_key(
173    HKEY_LOCAL_MACHINE, kwnl[15], KEY_QUERY_VALUE, &hk
174  );
175  if(result == ERROR_SUCCESS) {
176    result = dkwt_tool_reg_query_value(
177      hk, kwnl[16], &dwType, &dwValue, &dwSz
178    );
179    if(result == ERROR_SUCCESS) {
180      if(dwType == REG_DWORD) {
181        if(dwSz == sizeof(DWORD)) {
182          if(dwValue) {
183            back = 1;
184          }
185        }
186      }
187    }
188    if(reset) {
189      (void)dkwt_tool_reg_delete_value(hk, kwnl[16]);
190    }
191    RegCloseKey(hk);
192  }
193  $? "- dkwt_et_get_keep_value %d", back
194  return back;
195}
196
197
198
199/**	Compare two strings.
200	@param	l	Left string.
201	@param	r	Right string.
202	@param	cr	Comparison criteria (ignored).
203	@return	Comparison result.
204*/
205static
206int
207dkwt_ev_compare_string(void const *l, void const *r, int DK3_ARG_UNUSED(cr))
208{
209  dkChar const	*pl;
210  dkChar const	*pr;
211  int		back = 0;
212  DK3_UNUSED_ARG(cr)
213  if(l) {
214    if(r) {
215      pl = (dkChar const *)l; pr = (dkChar const *)r;
216      back = dk3str_casecmp(pl, pr);
217    } else {
218      back = 1;
219    }
220  } else {
221    if(r) {
222      back = -1;
223    }
224  }
225  if(back >  1) back =  1;
226  if(back < -1) back = -1;
227  return back;
228}
229
230
231
232/**	Initialize job structure.
233	@param	j	Structure to initialize.
234*/
235static
236void
237dkwt_ev_job_init(DKWT_EV_J *j)
238{
239#if (!(_WIN32)) || (!(defined(_WIN32_WINNT))) || (_WIN32_WINNT < 0x0603)
240  OSVERSIONINFO	ovi;
241#endif
242
243  j->app = NULL; j->msg = NULL; j->kwnl = NULL;
244  j->profBase = NULL;
245  j->userName = NULL;
246  j->systemUser = NULL;
247  j->everyUser = NULL;
248  j->s_users = NULL; j->i_users = NULL;
249  j->s_pubdir = NULL; j->i_pubdir = NULL;
250  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
251  j->curSection = -1;
252  j->useIcacls = 0;
253  j->testOnly = 0;
254  j->clp = 0;
255  j->opt = NULL;
256  j->s_queues = NULL;
257  j->i_queues = NULL;
258  j->clrProfiles = 0;
259#if (!(_WIN32)) || (!(defined(_WIN32_WINNT))) || (_WIN32_WINNT < 0x0603)
260  ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
261  ovi.dwMajorVersion = 0L;
262  ovi.dwMinorVersion = 0L;
263  ovi.dwBuildNumber = 0L;
264  ovi.dwPlatformId = 0L;
265  if(GetVersionEx(&ovi)) {
266    if(ovi.dwMajorVersion > 5) {
267      j->useIcacls = 1;		$? ". icacls"
268    } else {
269      if(ovi.dwMajorVersion == 5) {
270        if(ovi.dwMinorVersion > 1) {
271	  j->useIcacls = 1;	$? ". icacls"
272	}
273      }
274    }
275    if(ovi.dwMajorVersion < 6) {
276      j->clrProfiles = 1;
277    }
278  }
279#else
280  if (!IsWindowsVistaOrGreater()) {
281    j->clrProfiles = 1;
282  }
283  if (IsWindowsXPOrGreater()) {
284    j->useIcacls = 1;
285  }
286#endif
287}
288
289
290
291/**	Clean up job structure, release memory.
292	@param	j	Job structure.
293*/
294static
295void
296dkwt_ev_job_cleanup(DKWT_EV_J *j)
297{
298  dkChar *un;
299  void	*vp;
300  if(j) {
301    if(j->s_queues) {
302      if(j->i_queues) {
303        dk3sto_it_reset(j->i_queues);
304	while(NULL != (vp = dk3sto_it_next(j->i_queues))) {
305	  dk3_delete(vp);
306	}
307        dk3sto_it_close(j->i_queues);
308      }
309      dk3sto_close(j->s_queues);
310    } j->s_queues = NULL; j->i_queues = NULL;
311    if(j->s_users) {
312      if(j->i_users) {
313        dk3sto_it_reset(j->i_users);
314	while((un = (dkChar *)dk3sto_it_next(j->i_users)) != NULL)
315	{
316	  dk3_release(un);
317	}
318	dk3sto_it_close(j->i_users);
319      }
320      dk3sto_close(j->s_users);
321    } j->s_users = NULL; j->i_users = NULL;
322    if(j->s_pubdir) {
323      if(j->i_pubdir) {
324        dk3sto_it_reset(j->i_pubdir);
325	while((un = (dkChar *)dk3sto_it_next(j->i_pubdir)) != NULL)
326	{
327	  dk3_release(un);
328	}
329	dk3sto_it_close(j->i_pubdir);
330      }
331      dk3sto_close(j->s_pubdir);
332    } j->s_pubdir = NULL; j->i_pubdir = NULL;
333    dk3_release(j->profBase);
334    dk3_release(j->userName);
335    dk3_release(j->systemUser);
336    dk3_release(j->everyUser);
337    if(j->opt) {
338      dk3opt_close(j->opt); j->opt = NULL;
339    }
340  }
341}
342
343
344
345/**	Handler for one input line.
346	@param	obj	Job structure.
347	@param	il	Input line to process.
348	@return	1 on success, 0 on recoverable error, -1 to abort processing.
349*/
350static
351int
352dkwt_ev_line_handler(void *obj, dkChar *il)
353{
354  DKWT_EV_J	*j;
355  dkChar	*p1;
356  dkChar	*p2;
357  dkChar	*p3;
358  dkChar	*nq;
359  int		 back = 1;
360  $? "+ dkwt_ev_line_handler \"%!ds\"", TR_STR(il)
361  j = (DKWT_EV_J *)obj;
362  p1 = dk3str_start(il, NULL);
363  if(p1) {
364    if(*p1 != dkT('#')) {
365      back = -1;
366      dk3str_delnl(p1);
367      if(*p1 == dkT('[')) {	/* New section header. */
368        p1++;
369	p1 = dk3str_start(p1, NULL);
370	if(p1) {
371          j->curSection = DKWT_EV_SECTION_UNKNOWN;
372          p2 = dk3str_rchr(p1, dkT(']'));
373	  if(p2) {
374	    *p2 = dkT('\0');
375	    p2 = dk3str_next(p1, NULL);
376	    switch(dk3str_array_index(dkwt_ev_section_names, p1, 0)) {
377	      case 0: {
378	        j->curSection = DKWT_EV_SECTION_OPTIONS;
379	        back = 1;
380	      } break;
381	      case 1: {
382	        j->curSection = DKWT_EV_SECTION_PROFILES;
383	        if(p2) {
384	          if(!(j->profBase)) {
385		    j->profBase = dk3str_dup_app(p2, j->app);
386		    if(j->profBase) {
387		      back = 1;
388		    } else {
389		      back = -1;
390		      j->exval = DKT_RESULT_ERR_MEMORY;
391		    }
392		  } else {
393		    back = -1;
394		    j->exval = DKT_RESULT_ERR_INPUT;
395		    /* ERROR: Redefinition of profile base dir! */
396		    dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 66);
397		  }
398	        } else {
399	          j->exval = DKT_RESULT_ERR_INPUT;
400		  /* ERROR: No profile base dir specified! */
401		  dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 67);
402	        }
403	      } break;
404	      case 2: {
405	        j->curSection = DKWT_EV_SECTION_PUBLIC; back = 1;
406	      } break;
407	      default: {
408	        back = -1;
409	        j->exval = DKT_RESULT_ERR_INPUT;
410	        /* ERROR: Unknown section type. */
411		dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 68);
412	      } break;
413	    }
414	  } else {
415	    back = -1;
416	    /* ERROR: Incomplete section header. */
417	    dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 69);
418	    j->exval = DKT_RESULT_ERR_INPUT;
419	  }
420	} else {
421	  back = -1;
422	  /* ERROR: No section header! */
423	  dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 70);
424	  j->exval = DKT_RESULT_ERR_INPUT;
425	}
426      } else {			/* Section contents line. */
427        switch(j->curSection) {
428	  case DKWT_EV_SECTION_OPTIONS: {
429	    p2 = dk3str_chr(p1, dkT('='));
430	    if(p2) {
431	      *(p2++) = dkT('\0');
432	      p2 = dk3str_start(p2, NULL);
433	      if(p2) {
434	        /* dk3str_chomp(p1, NULL); */
435		dk3str_normalize(p1, NULL, dkT('-'));
436#if 0
437		dk3str_normalize(p2, NULL, dkT('-'));
438#endif
439		switch(dk3str_array_index(dkwt_ev_option_names, p1, 0)) {
440		  case 0: {
441		    if(!(j->systemUser)) {
442		      j->systemUser = dk3str_dup_app(p2, j->app);
443		      if(j->systemUser) {
444		        back = 1;
445		      } else {
446		        back = -1;
447			j->exval = DKT_RESULT_ERR_MEMORY;
448		      }
449		    } else {
450		      /* ERROR: Redefinition of system user! */
451		      dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 71);
452		      back = -1;
453		      j->exval = DKT_RESULT_ERR_INPUT;
454		    }
455		  } break;
456		  case 1: {
457		    if(!(j->everyUser)) {
458		      j->everyUser = dk3str_dup_app(p2, j->app);
459		      if(j->everyUser) {
460		        back = 1;
461		      } else {
462		        back = -1;
463			j->exval = DKT_RESULT_ERR_MEMORY;
464		      }
465		    } else {
466		      /* ERROR: Redefinition of everyone user! */
467		      dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 72);
468		      back = -1;
469		      j->exval = DKT_RESULT_ERR_INPUT;
470		    }
471		  } break;
472		  case 2: {
473		    back = 1;
474		    while(p2) {
475		      p3 = dk3str_next(p2, NULL);
476		      if(0 == dk3str_cmp(dkwt_pq_name_local, p2)) {
477		        j->clp = 1;
478		      } else {
479		        if(!(j->s_queues)) {
480			  j->s_queues = dk3sto_open_app(j->app);
481			}
482			if(!(j->i_queues)) {
483			  if(j->s_queues) {
484			    j->i_queues = dk3sto_it_open(j->s_queues);
485			  }
486			}
487			if((j->s_queues) && (j->i_queues)) {
488			  nq = dk3str_dup_app(p2, j->app);
489			  if(nq) {
490			    if(!dk3sto_add(j->s_queues, (void *)nq)) {
491			      dk3_delete(nq);
492			      back = 0;
493			    }
494			  } else {
495			    back = 0;
496			  }
497			} else {
498			  back = 0;
499			}
500		      }
501		      p2 = p3;
502		    }
503		  } break;
504		  default: {
505		    /* ERROR: Unknown option! */
506		    dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 73);
507		    back = -1;
508		    j->exval = DKT_RESULT_ERR_INPUT;
509		  } break;
510		}
511	      } else {
512	        /* ERROR: Not a key/value pair */
513		dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 74);
514		back = -1;
515		j->exval = DKT_RESULT_ERR_INPUT;
516	      }
517	    } else {
518	      /* ERROR: Not a key/value pair */
519	      dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 74);
520	      back = -1;
521	      j->exval = DKT_RESULT_ERR_INPUT;
522	    }
523	  } break;
524	  case DKWT_EV_SECTION_PROFILES: {	$? ". add user name to list"
525	    dk3str_normalize(p1, NULL, dkT(' '));
526	    p2 = dk3str_dup_app(p1, j->app);
527	    if(p2) {
528	      if(dk3sto_add(j->s_users, (void *)p2)) {
529	        back = 1;	$? ". added user name \"%!ds\"", TR_STR(p2)
530	      } else {		$? "! failed to add"
531	        dk3_release(p2);
532		back = -1;
533		j->exval = DKT_RESULT_ERR_MEMORY;
534	      }
535	    } else {		$? "! empty user name"
536	      back = -1;
537	      j->exval = DKT_RESULT_ERR_MEMORY;
538	    }
539	  } break;
540	  case DKWT_EV_SECTION_PUBLIC: {	$? ". add public dir to list"
541	    dk3str_normalize(p1, NULL, dkT(' '));
542	    p2 = dk3str_dup_app(p1, j->app);
543	    if(p2) {			$? ". p2"
544	      if(dk3sto_add(j->s_pubdir, (void *)p2)) {
545	        back = 1;		$? ". added"
546	      } else {			$? "! failed to add"
547	        dk3_release(p2);
548		back = -1;
549		j->exval = DKT_RESULT_ERR_MEMORY;
550	      }
551	    } else {			$? "! p2"
552	      back = -1;
553	      j->exval = DKT_RESULT_ERR_MEMORY;
554	    }
555	  } break;
556	  default: {
557	    back = -1;
558	    j->exval = DKT_RESULT_ERR_INPUT;
559	  } break;
560	}
561      }
562    } else {
563      /* Comment line ok. */
564    }
565  } else {
566    /* Empty line ok. */
567  } $? "- dkwt_ev_line_handler %d", back
568  return back;
569}
570
571
572
573/**	Run a command.
574	@param	j	Job structure.
575	@param	cmd	Command to run.
576*/
577static
578void
579dkwt_ev_execute_command(DKWT_EV_J *j, dkChar const *cmd)
580{
581  int value = 0;
582  /* PROGRESS: Execute command ... */
583  dk3app_log_3(j->app, DK3_LL_PROGRESS, j->msg, 118, 119, cmd);
584  dk3sf_initialize_stdout();
585  dk3sf_fputs(cmd, stdout);
586  dk3sf_fputc(dkT('\n'), stdout);
587  if(!(j->testOnly)) {
588#if DK3_CHAR_SIZE > 1
589    value = _wsystem(cmd);
590#else
591    value = system(cmd);
592#endif
593    if(value != 0) {
594      /* ERROR: Command execution failed! */
595      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 79, 80, cmd);
596      j->exval = DKT_RESULT_ERR_SYSEXEC;
597    }
598  }
599}
600
601
602
603/**	Remove a directory.
604	First attempt to use the RD command, next use own routines.
605	@param	j	Job structure.
606	@param	dn	Directory name.
607	@param	dolog	Flag: Do logging in dk3dir_remove_app().
608*/
609static
610void
611dkwt_ev_remove_directory(DKWT_EV_J *j, dkChar const *dn, int dolog)
612{
613  dkChar	bu[2 * DK3_MAX_PATH];
614  dk3_stat_t	stb;
615  size_t	sz;
616  $? "+ dkwt_ev_remove_directory \"%ls\"", dn
617  /* PROGRESS: Remove directory ... */
618  dk3app_log_3(j->app, DK3_LL_PROGRESS, j->msg, 120, 121, dn);
619  if(!(j->testOnly)) {
620#if 0
621    dk3sf_remove_dir(dn);
622#endif
623    dk3dir_remove_app(dn, ((dolog) ? j->app : NULL));
624    if(dk3sf_stat_app(&stb, dn, NULL)) {
625      sz =  dk3str_len(dn);
626      sz += dk3str_len((j->kwnl)[4]);
627      sz += dk3str_len((j->kwnl)[5]);
628      if(sz < DK3_SIZEOF(bu,dkChar)) {
629        dk3str_cpy_not_overlapped(bu, (j->kwnl)[4]);
630        dk3str_cat(bu, dn);
631        dk3str_cat(bu, (j->kwnl)[5]);
632        dkwt_ev_execute_command(j, bu);
633      } else {	$? "! name too long"
634        /* ERROR: File name too long! */
635        dk3app_log_i3(j->app, DK3_LL_ERROR, 59, 60, dn);
636        j->exval = DKT_RESULT_ERR_FILENAME;
637      }
638    }
639  }
640  $? "- dkwt_ev_remove_directory"
641}
642
643
644
645/**	Remove a file.
646	First attempt to use the DEL command, next use own routines.
647	@param	j	Job structure.
648	@param	fn	File name.
649*/
650static
651void
652dkwt_ev_remove_file(DKWT_EV_J *j, dkChar const *fn)
653{
654  dkChar	bu[2 * DK3_MAX_PATH];
655  dk3_stat_t	stb;
656  size_t	sz;
657
658  /* PROGRESS: Remove file ... */
659  dk3app_log_3(j->app, DK3_LL_PROGRESS, j->msg, 122, 123, fn);
660  if(!(j->testOnly)) {
661    dk3sf_remove_file_app(fn, NULL);
662    if(dk3sf_stat_app(&stb, fn, NULL)) {
663      sz =  dk3str_len(fn);
664      sz += dk3str_len((j->kwnl)[6]);
665      sz += dk3str_len((j->kwnl)[5]);
666      if(sz < DK3_SIZEOF(bu,dkChar)) {
667        dk3str_cpy_not_overlapped(bu, (j->kwnl)[6]);
668        dk3str_cat(bu, fn);
669        dk3str_cat(bu, (j->kwnl)[5]);
670        dkwt_ev_execute_command(j, bu);
671      } else {
672        /* ERROR: File name too long! */
673        dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn);
674        j->exval = DKT_RESULT_ERR_FILENAME;
675      }
676    }
677  }
678}
679
680
681
682/**	Check whether or not to keep a profile subdirectory.
683	@param	j	Job structure.
684	@param	un	User name (short directory name).
685	@return	1 to keep the directory, 0 to delete it.
686*/
687static
688int
689dkwt_ev_must_keep(DKWT_EV_J *j, dkChar const *un)
690{
691  int back = 0;
692  $? "+ dkwt_ev_must_keep"
693  if(j->userName) {
694    if(dk3str_casecmp(j->userName, un) == 0) {
695      back = 1;
696    }
697  }
698  if(!(back)) {
699    if(dk3sto_it_find_like(j->i_users, (void *)un, 0)) {
700      back = 1;
701    }
702  }
703  if(!(back)) {
704    if(dk3str_array_index(dkwt_ev_profiles_to_keep, un, 0) >= 0) {
705      back = 1;
706    }
707  } $? "- dkwt_ev_must_keep %d", back
708  return back;
709}
710
711
712
713#ifdef VERSION_BEFORE_20120320
714/**	Clean up the profile directory.
715	@param	j	Job structure.
716*/
717static
718void
719dkwt_ev_cleanup_profiles(DKWT_EV_J *j)
720{
721  dkChar	bu1[DK3_MAX_PATH];
722  dk3_dir_t	*dir;
723  dkChar const	*en;
724  dkChar const	*es;
725  dkChar	*ptr;
726  int		must_delete = 1;
727  $? "+ dkwt_ev_cleanup_profiles %d", must_delete
728  if(j->profBase) {
729    if(j->clrProfiles != 0) {
730      dir = dk3dir_open_app(j->profBase, j->app);
731      if(dir) {	$? ". directory opened"
732        while(dk3dir_get_next_directory(dir)) {
733          en = dk3dir_get_fullname(dir);
734	  es = dk3dir_get_shortname(dir);
735	  if((en) && (es)) {	$? ". entry \"%ls\"", es
736	    must_delete = 1;
737	    if(dkwt_ev_must_keep(j, es)) {
738	      must_delete = 0;	$? ". must keep"
739	    } else {		$? ". can delete"
740	      if(dk3str_len(es) < DK3_SIZEOF(bu1,dkChar)) {
741	        dk3str_cpy_not_overlapped(bu1, es);
742	        ptr = dk3str_chr(bu1, dkT('.'));
743	        if(ptr) {
744	          *ptr = dkT('\0');
745		  if(dkwt_ev_must_keep(j, bu1)) {
746		    must_delete = 0;	$? ". must keep"
747		  } else {	$? ". can delete"
748		  }
749	        } else {	$? ". no need to test again"
750	        }
751	      } else {	$? "! name too long for test"
752	        must_delete = 0;
753	      }
754	    } $? ". must_delete=%d", must_delete
755	    if(must_delete) {	$? ". must delete \"%ls\"", en
756	      dkwt_ev_remove_directory(j, en, 0);
757	    }
758	  }
759        }
760        dk3dir_close(dir);
761      }
762    } else {
763    }
764  }
765  $? "- dkwt_ev_cleanup_profiles"
766}
767#else
768
769
770
771/**	Directory entry for deletion.
772*/
773typedef struct {
774  dkChar const		*n;	/**< Directory name. */
775  char			 f;	/**< Flag: Can delete. */
776} DKWT_EV_DIR;
777
778
779
780/**	Release directory name.
781	@param	d	Directory name entry to delete.
782*/
783static
784void
785dkwt_ev_ddel_del(DKWT_EV_DIR *d)
786{
787  if(d) {
788    dk3_release(d->n);
789    d->f = 0x00;
790    dk3_delete(d);
791  }
792}
793
794
795
796/**	Create directory name entry.
797	@param	j	Job structure.
798	@param	fn	File name of directory.
799	@return	New entry on success, NULL on error.
800*/
801static
802DKWT_EV_DIR *
803dkwt_ev_ddel_new(DKWT_EV_J *j, dkChar const *fn)
804{
805  DKWT_EV_DIR	*back = NULL;
806  if(fn) {
807    back = dk3_new_app(DKWT_EV_DIR,1,j->app);
808    if(back) {
809      back->f = 0x01;
810      back->n = dk3str_dup_app(fn, j->app);
811      if(!(back->n)) {
812        dkwt_ev_ddel_del(back);
813	back = NULL;
814      }
815    }
816  }
817  return back;
818}
819
820
821
822/**	Compare two entries for directories.
823	@param	l	Left directory object.
824	@param	r	Right directory object.
825	@param	cr	Comparison criteria (0=dir/dir, 1=dir/name).
826	@return	Comparison result.
827*/
828static
829int
830dkwt_ev_ddel_comp(void const *l, void const *r, int cr)
831{
832  int		 back = 0;
833  DKWT_EV_DIR const	*pl;
834  DKWT_EV_DIR const	*pr;
835  if(l) {
836    if(r) {
837      pl = (DKWT_EV_DIR const *)l;
838      switch(cr) {
839        case 1: {
840	  if(pl->n) {
841	    back = dk3str_casecmp(pl->n, (dkChar const *)r);
842	    if(back < -1) back = -1;
843	    if(back >  1) back =  1;
844	  } else back = -1;
845	} break;
846	default: {
847          pr = (DKWT_EV_DIR const *)r;
848          if(pl->n) {
849            if(pr->n) {
850	      back = dk3str_casecmp(pl->n, pr->n);
851	      if(back < -1) back = -1;
852	      if(back >  1) back =  1;
853	    } else back = 1;
854          } else {
855            if(pr->n) back = -1;
856          }
857        } break;
858      }
859    } else back = 1;
860  } else {
861    if(r) back = -1;
862  }
863  return back;
864}
865
866
867
868/**	Delete a registry key structure for a user profile.
869	@param	j	Job structure.
870	@param	kn	Key name (short name).
871*/
872static
873void
874dkwt_ev_del_rk_profile(DKWT_EV_J *j, dkChar const *kn)
875{
876  HKEY			 hk;
877  long			 res;
878  $? "+ dkwt_ev_del_rk_profile \"%!ds\"", kn
879  /*
880  	Log message
881  */
882  /* PROGRESS: Deleting registry key ... */
883  dk3app_log_5(
884    j->app, DK3_LL_PROGRESS,
885    j->msg, 124, 125, 126, (j->kwnl)[19], kn
886  );
887  /*
888  	Delete the key.
889  */
890  if(!(j->testOnly)) {
891    res = dkwt_tool_reg_open_key(
892      HKEY_LOCAL_MACHINE, (j->kwnl)[19], KEY_ALL_ACCESS, &hk
893    );
894    if(ERROR_SUCCESS == res) {		$? ". parent key opened"
895      res = dkwt_tool_reg_delete_tree(hk, kn);
896      if(ERROR_SUCCESS == res) {	$? ". deleted successfully"
897        /* DEBUG: Registry key ... deleted. */
898	dk3app_log_3(j->app, DK3_LL_DEBUG, j->msg, 127, 128, kn);
899      } else {				$? "! failed to delete"
900        /* ERROR: Failed to remove registry hierarchy ... */
901	dk3app_log_5(
902	  j->app, DK3_LL_ERROR, j->msg,
903	  129, 130, 131, (j->kwnl)[19], kn
904	);
905      }
906      RegCloseKey(hk);
907    } else {				$? "! failed to open parent"
908      /* ERROR: Failed to open registry key ... */
909      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 132, 133, (j->kwnl)[19]);
910    }
911  }
912  $? "- dkwt_ev_del_rk_profile"
913}
914
915
916
917/**	Clean up the profile directory.
918	@param	j	Job structure.
919*/
920static
921void
922dkwt_ev_cleanup_profiles(DKWT_EV_J *j)
923{
924  dkChar	 knb[DK3_MAX_PATH];	/* Buffer for key name. */
925  dkChar	 ppb[DK3_MAX_PATH];	/* Profile path buffer. */
926  dkChar	 esb[DK3_MAX_PATH];	/* Expanded string buffer. */
927  dkChar	 bu1[DK3_MAX_PATH];	/* Buffer for full file name. */
928  unsigned char	*ptrppb;
929  void		*vp;	/* void pointer for cleanup. */
930  dk3_sto_t	*s_fn;	/* Storage of directories to delete. */
931  dk3_sto_it_t	*i_fn;	/* Iterator for directory storage. */
932  dk3_sto_t	*s_rk;	/* Storage for registry keys. */
933  dk3_sto_it_t	*i_rk;	/* Iterator for registry keys storage. */
934  dk3_sto_t	*s_rd;	/* Storage for registry keys to delete. */
935  dk3_sto_it_t	*i_rd;	/* Iterator for keys to delete. `*/
936  dk3_dir_t	*dir;	/* Directory. */
937  dkChar const	*en;	/* Entry name. */
938  dkChar const	*es;	/* Short entry name. */
939  dkChar const	*kn;	/* Current key name, copy. */
940  dkChar	*ptr;	/* Pointer to dot in file name. */
941  DKWT_EV_DIR	*ddel;	/* Directory to delete. */
942  HKEY		 hk;	/* Handle for registry key. */
943#if VERSION_BEFORE_2019_10_20
944  REGSAM	 regs;	/* Access type to registry key. */
945#endif
946  long		 res;	/* Result from operation. */
947  size_t	 stlgt;	/* String length. */
948#if VERSION_BEFORE_2019_10_20
949  DWORD		 dw0;	/* Used to open registry keys. */
950#endif
951  DWORD		 dwInd;	/* INdex of current key. */
952  DWORD		 dwSz;	/* Size of knb buffer. */
953  DWORD		 dwEx;	/* Result from string expansion. */
954  DWORD		 dwTp;	/* Result type. */
955  int		 md;	/* Flag: Must delete this entry. */
956  int		 ec;	/* Error code. */
957  $? "+ dkwt_ev_cleanup_profiles"
958  ec = 0;
959  ptrppb = (unsigned char *)ppb;
960  if(j->profBase) {
961    /* PROGRESS: Cleanin up profiles (start) */
962    dk3app_log_1(j->app, DK3_LL_PROGRESS, j->msg, 134);
963    s_fn = dk3sto_open_app(j->app);
964    if(s_fn) {
965    dk3sto_set_comp(s_fn, dkwt_ev_ddel_comp, 0);
966    i_fn = dk3sto_it_open(s_fn);
967    if(i_fn) {
968    s_rk = dk3sto_open_app(j->app);
969    if(s_rk) {
970    dk3sto_set_comp(s_rk, dkwt_ev_compare_string, 0);
971    i_rk = dk3sto_it_open(s_rk);
972    if(i_rk) {
973    s_rd = dk3sto_open_app(j->app);
974    if(s_rd) {
975    i_rd = dk3sto_it_open(s_rd);
976    if(i_rd) {
977      /* DEBUG: Storages ok. */
978      dir = dk3dir_open_app(j->profBase, j->app);
979      if(dir) {
980        /* DEBUG: Directory opened successfully. */
981        while(dk3dir_get_next_directory(dir)) {
982	  en = dk3dir_get_fullname(dir);
983	  es = dk3dir_get_shortname(dir);
984	  if((en) && (es)) {	$? ". entry \"%!ds\"", es
985	    md = 1;
986	    if(dkwt_ev_must_keep(j, es)) {
987	      md = 0;
988	    } else {
989	      if(dk3str_len(es) < DK3_SIZEOF(bu1,dkChar)) {
990	        dk3str_cpy_not_overlapped(bu1, es);
991		ptr = dk3str_chr(bu1, dkT('.'));
992		if(ptr) {
993		  *ptr = dkT('\0');
994		  if(dkwt_ev_must_keep(j, bu1)) {
995		    md = 0;
996		  }
997		}
998	      } else {		$? "! name too long"
999	        /* ERROR: Name too long! */
1000		dk3app_log_i3(j->app, DK3_LL_ERROR, 59, 60, es);
1001		if(!(ec)) { ec = DK3_ERROR_TOO_LARGE; }
1002	        md = 0;
1003	      }
1004	    }	$? ". md = %d", md
1005	    if(md) {
1006	      ddel = dkwt_ev_ddel_new(j, en);
1007	      if(ddel) {
1008	        if(!dk3sto_add(s_fn, (void *)ddel)) {		$? "! add"
1009		  dkwt_ev_ddel_del(ddel);
1010		  if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1011		}
1012	      } else {						$? "! ddel_new"
1013	        if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1014	      }
1015	    }
1016	  } else {		$? "! bug"
1017	    /* ERROR: Failed to obtain name for directory entry! */
1018	    dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 135);
1019	    if(!(ec)) { ec = DK3_ERROR_SYSTEM; }
1020	  }
1021	}
1022        dk3dir_close(dir);
1023	/*
1024		Now search the registry keys
1025	*/
1026#if VERSION_BEFORE_2019_10_20
1027	dw0 = (DWORD)0;
1028#endif
1029#if VERSION_BEFORE_2019_10_20
1030	regs = KEY_READ;
1031#endif
1032	res = dkwt_tool_reg_open_key(
1033	  HKEY_LOCAL_MACHINE, (j->kwnl)[19], KEY_READ, &hk
1034	);
1035	if(ERROR_SUCCESS == res) {
1036	  dwInd = 0;
1037	  do {
1038	    dwSz = DK3_SIZEOF(knb,dkChar);
1039	    res = dkwt_tool_reg_enum_key(
1040	      hk, dwInd, knb, &dwSz, NULL, NULL, NULL
1041	    );
1042	    if(ERROR_SUCCESS == res) {
1043	      if(dwSz > 0) {
1044	        kn = dk3str_dup_app(knb, j->app);
1045		if(kn) {
1046		  if(!dk3sto_add(s_rk, (void *)kn)) {		$? "! add"
1047		    dk3_delete(kn);
1048		    if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1049		  }
1050		} else {					$? "! strdup"
1051		  if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1052		}
1053	      }
1054	    } else {					$? "! reg_enum_key"
1055	      if(res != ERROR_NO_MORE_ITEMS) {
1056	        if(!(ec)) { ec = DK3_ERROR_DURING_READ; }
1057	      }
1058	    }
1059	    dwInd++;
1060	  } while(res != ERROR_NO_MORE_ITEMS);
1061	  RegCloseKey(hk);
1062	  /*
1063	  	For each key found check whether ProfileImagePath is in s_fn.
1064	  */
1065	  dk3sto_it_reset(i_rk);
1066	  while(NULL != (kn = (dkChar const *)dk3sto_it_next(i_rk))) {
1067	    stlgt =  dk3str_len((j->kwnl)[19]);
1068	    stlgt++;
1069	    stlgt += dk3str_len(kn);
1070	    if(stlgt < DK3_SIZEOF(knb,dkChar)) {
1071	      dk3str_cpy_not_overlapped(knb, (j->kwnl)[19]);
1072	      dk3str_cat(knb, (j->kwnl)[20]);
1073	      dk3str_cat(knb, kn);
1074#if VERSION_BEFORE_2019_10_20
1075	      dw0 = 0;
1076#endif
1077#if VERSION_BEFORE_2019_10_20
1078	      regs = KEY_QUERY_VALUE;
1079#endif
1080	      res = dkwt_tool_reg_open_key(
1081	        HKEY_LOCAL_MACHINE, knb, KEY_QUERY_VALUE, &hk
1082	      );
1083	      if(ERROR_SUCCESS == res) {
1084	        dwTp = REG_EXPAND_SZ;
1085		dwSz = sizeof(ppb);
1086		res = dkwt_tool_reg_query_value(
1087		  hk, (j->kwnl)[21], &dwTp, ppb, &dwSz
1088		);
1089		if(ERROR_SUCCESS == res) {
1090#if DK3_CHAR_SIZE > 1
1091		  if(dwSz < (sizeof(ppb) - 1)) {
1092		    ptrppb[dwSz] = '\0';
1093		    ptrppb[dwSz+1] = '\0';
1094		  } else {
1095		    ptrppb[DK3_SIZEOF(ppb,dkChar)-2] = '\0';
1096		    ptrppb[DK3_SIZEOF(ppb,dkChar)-1] = '\0';
1097		  }
1098#else
1099		  if(dwSz < sizeof(ppb)) {
1100		    ppb[dwSz] = '\0';
1101		  } else {
1102		    ppb[sizeof(ppb)-1] = '\0';
1103		  }
1104#endif
1105		  switch(dwTp) {
1106		    case REG_SZ: {
1107		      ddel = (DKWT_EV_DIR *)dk3sto_it_find_like(
1108		        i_fn, (void *)ppb, 1
1109		      );
1110		      if(ddel) {
1111		        /* Save kn as registry key to delete */
1112			if(!dk3sto_add(s_rd, (void *)kn)) {	$? "! add"
1113			  ddel->f = 0x00;
1114			  if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1115			}
1116		      }
1117		    } break;
1118		    case REG_EXPAND_SZ: {
1119#if DK3_CHAR_SIZE > 1
1120		      dwEx = ExpandEnvironmentStringsW(
1121		        ppb,
1122			esb,
1123			DK3_SIZEOF(esb,dkChar)
1124		      );
1125#else
1126		      dwEx = ExpandEnvironmentStringsA(
1127		        ppb,
1128			esb,
1129			DK3_SIZEOF(esb,dkChar)
1130		      );
1131#endif
1132		      if(dwEx > 0) {
1133		        if(dwEx < DK3_SIZEOF(esb,dkChar)) {
1134			  ddel = (DKWT_EV_DIR *)dk3sto_it_find_like(
1135			    i_fn, (void *)esb, 1
1136			  );
1137			  if(ddel) {
1138			    /* Save kn as registry key to delete */
1139			    if(!dk3sto_add(s_rd,(void *)kn)) {	$? "! add"
1140			      ddel->f = 0x00;
1141			      if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1142			    }
1143			  }
1144			} else {		$? "! ExpandEnvironmentStrings"
1145			  /* ERROR: String too long! */
1146			  dk3app_log_3(
1147			    j->app, DK3_LL_ERROR, j->msg,
1148			    136, 137, ppb
1149			  );
1150			  if(!(ec)) { ec = DK3_ERROR_TOO_LARGE; }
1151			}
1152		      } else {
1153		        ddel = (DKWT_EV_DIR *)dk3sto_it_find_like(
1154			  i_fn, (void *)ppb, 1
1155			);
1156			if(ddel) {
1157			  /* Save kn as registry key to delete */
1158			  if(!dk3sto_add(s_rd, (void *)kn)) {	$? "! add"
1159			    ddel->f = 0x00;
1160			    if(!(ec)) { ec = DK3_ERROR_MEMORY; }
1161			  }
1162			}
1163		      }
1164		    } break;
1165		  }
1166		}
1167	        RegCloseKey(hk);
1168	      } else {				$? "! registry open key"
1169	        /* ERROR: Failed to open registry key ... */
1170		dk3app_log_3(
1171		  j->app, DK3_LL_ERROR, j->msg, 132, 133, knb
1172		);
1173		if(!(ec)) { ec = DK3_ERROR_NOT_OPENED_FOR_READING; }
1174	      }
1175	    } else {			$? "! key name too long \"%!ds\"", kn
1176	      /* ERROR: Key name too long! */
1177	      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 138, 139, kn);
1178	      if(!(ec)) { ec = DK3_ERROR_TOO_LARGE; }
1179	    }
1180	  }
1181	} else {		$? "! failed to open registry key"
1182	  /* ERROR: Failed to open registry key! */
1183	  dk3app_log_3(
1184	    j->app, DK3_LL_ERROR, j->msg, 132, 133, (j->kwnl)[19]
1185	  );
1186	  if(!(ec)) { ec = DK3_ERROR_NOT_OPENED_FOR_READING; }
1187	}
1188      } else {			$? "! dir"
1189      }
1190      if(!(ec)) {		$? ". no error, delete registry keys"
1191        dk3sto_it_reset(i_rd);
1192        while(NULL != (kn = (dkChar const *)dk3sto_it_next(i_rd))) {
1193          /*
1194          */
1195          dkwt_ev_del_rk_profile(j, kn);
1196        }
1197      } else {
1198        /* ERROR: Skipping profile cleanup due to previous errors! */
1199	dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 140);
1200      }
1201      /*
1202    	  In s_rd and i_rd we have copies of strings from s_rk and i_rk.
1203	  No deletion necessary.
1204      */
1205      dk3sto_it_close(i_rd);
1206    } else {			$? "! i_rd"
1207    }
1208    dk3sto_close(s_rd);
1209    } else {			$? "! s_rd"
1210    }
1211    dk3sto_it_reset(i_rk);
1212    while(NULL != (vp = dk3sto_it_next(i_rk))) {
1213    dk3_delete(vp);
1214    }
1215    dk3sto_it_close(i_rk);
1216    } else {			$? "! i_rk"
1217    }
1218    dk3sto_close(s_rk);
1219    } else {			$? "! s_rk"
1220    }
1221    dk3sto_it_reset(i_fn);
1222    while(NULL != (ddel = (DKWT_EV_DIR *)dk3sto_it_next(i_fn))) {
1223      /*
1224      	Remove the directory.
1225      */
1226      if(!(ec)) {
1227        if(ddel->f) {
1228          dkwt_ev_remove_directory(j, ddel->n, 0);
1229        }
1230      }
1231      /*
1232      	Free list entry.
1233      */
1234      dkwt_ev_ddel_del(ddel);
1235    }
1236    dk3sto_it_close(i_fn); i_fn = NULL;
1237    } else {			$? "! i_fn"
1238    }
1239    dk3sto_close(s_fn); s_fn = NULL;
1240    } else {			$? "! s_fn"
1241    }
1242    /* PROGRESS: Cleaning up profiles (finished). */
1243    dk3app_log_1(j->app, DK3_LL_PROGRESS, j->msg, 141);
1244  } else {			$? ". no profile base configured"
1245  }
1246  $? "- dkwt_ev_cleanup_profiles"
1247}
1248
1249
1250#endif
1251
1252
1253
1254/**	Clean up public directories.
1255	@param	j	Job structure.
1256*/
1257static
1258void
1259dkwt_ev_cleanup_public(DKWT_EV_J *j)
1260{
1261  dkChar const	*fn;
1262  dkChar const	*en;
1263  dk3_dir_t	*dir;
1264  $? "+ dkwt_ev_cleanup_public"
1265  dk3sto_it_reset(j->i_pubdir);
1266  while((fn = (dkChar const *)dk3sto_it_next(j->i_pubdir)) != NULL) {
1267    dir = dk3dir_open_app(fn, j->app);
1268    if(dir) {
1269      while(dk3dir_get_next_directory(dir)) {
1270        en = dk3dir_get_fullname(dir);
1271	if(en) {
1272	  dkwt_ev_remove_directory(j, en, 1);
1273	}
1274      }
1275      while(dk3dir_get_next_file(dir)) {
1276        en = dk3dir_get_fullname(dir);
1277	if(en) {
1278	  dkwt_ev_remove_file(j, en);
1279	}
1280      }
1281      dk3dir_close(dir);
1282    }
1283  }
1284  $? "- dkwt_ev_cleanup_public"
1285}
1286
1287
1288
1289/**	Change permissions for one path.
1290	@param	j	Job structure.
1291	@param	fn	File name.
1292	@param	un	User name.
1293	@param	isdir	Flag: Is directory.
1294*/
1295static
1296void
1297dkwt_ev_user_permissions(
1298  DKWT_EV_J *j, dkChar const *fn, dkChar const *un, int isdir
1299)
1300{
1301  dkChar	bu[2 * DK3_MAX_PATH];
1302  size_t	sz = 0;
1303  $? "+ dkwt_ev_user_permissions"
1304  if(j->useIcacls) {
1305    if(isdir) {
1306      sz =  dk3str_len((j->kwnl)[10]);
1307      sz += dk3str_len(fn);
1308      sz += dk3str_len((j->kwnl)[11]);
1309      sz += dk3str_len(un);
1310      sz += dk3str_len((j->kwnl)[12]);
1311      if(sz < DK3_SIZEOF(bu,dkChar)) {
1312        dk3str_cpy_not_overlapped(bu, (j->kwnl)[10]);
1313	dk3str_cat(bu, fn);
1314	dk3str_cat(bu, (j->kwnl)[11]);
1315	dk3str_cat(bu, un);
1316	dk3str_cat(bu, (j->kwnl)[12]);
1317	dkwt_ev_execute_command(j, bu);
1318      } else {
1319        /* ERROR: Line too long! */
1320	dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn);
1321	j->exval = DKT_RESULT_ERR_FILENAME;
1322      }
1323    } else {
1324      sz =  dk3str_len((j->kwnl)[10]);
1325      sz += dk3str_len(fn);
1326      sz += dk3str_len((j->kwnl)[11]);
1327      sz += dk3str_len(un);
1328      sz += dk3str_len((j->kwnl)[13]);
1329      if(sz < DK3_SIZEOF(bu,dkChar)) {
1330        dk3str_cpy_not_overlapped(bu, (j->kwnl)[10]);
1331	dk3str_cat(bu, fn);
1332	dk3str_cat(bu, (j->kwnl)[11]);
1333	dk3str_cat(bu, un);
1334	dk3str_cat(bu, (j->kwnl)[13]);
1335	dkwt_ev_execute_command(j, bu);
1336      } else {
1337        /* ERROR: Line too long! */
1338	dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn);
1339	j->exval = DKT_RESULT_ERR_FILENAME;
1340      }
1341    }
1342  } else {
1343    if(isdir) {
1344      sz =  dk3str_len((j->kwnl)[7]);
1345      sz += dk3str_len(fn);
1346      sz += dk3str_len((j->kwnl)[8]);
1347      sz += dk3str_len(un);
1348      sz += dk3str_len((j->kwnl)[9]);
1349      if(sz < DK3_SIZEOF(bu,dkChar)) {
1350        dk3str_cpy_not_overlapped(bu, (j->kwnl)[7]);
1351	dk3str_cat(bu, fn);
1352	dk3str_cat(bu, (j->kwnl)[8]);
1353	dk3str_cat(bu, un);
1354	dk3str_cat(bu, (j->kwnl)[9]);
1355	dkwt_ev_execute_command(j, bu);
1356      } else {
1357        /* ERROR: Line too long! */
1358	dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn);
1359	j->exval = DKT_RESULT_ERR_FILENAME;
1360      }
1361    } else {
1362      sz =  dk3str_len((j->kwnl)[7]);
1363      sz += dk3str_len(fn);
1364      sz += dk3str_len((j->kwnl)[14]);
1365      sz += dk3str_len(un);
1366      sz += dk3str_len((j->kwnl)[9]);
1367      if(sz < DK3_SIZEOF(bu,dkChar)) {
1368        dk3str_cpy_not_overlapped(bu, (j->kwnl)[7]);
1369	dk3str_cat(bu, fn);
1370	dk3str_cat(bu, (j->kwnl)[14]);
1371	dk3str_cat(bu, un);
1372	dk3str_cat(bu, (j->kwnl)[9]);
1373	dkwt_ev_execute_command(j, bu);
1374      } else {
1375        /* ERROR: Line too long! */
1376	dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn);
1377	j->exval = DKT_RESULT_ERR_FILENAME;
1378      }
1379    }
1380  }
1381  $? "- dkwt_ev_user_permissions"
1382}
1383
1384
1385
1386/**	Change permissions for one path.
1387	@param	j	Job structure.
1388	@param	fn	File name.
1389	@param	isdir	Flag: Is directory.
1390*/
1391static
1392void
1393dkwt_ev_permissions(DKWT_EV_J *j, dkChar const *fn, int isdir)
1394{
1395  $? "+ dkwt_ev_permissions"
1396  if(j->systemUser) {
1397    dkwt_ev_user_permissions(j, fn, j->systemUser, isdir);
1398  } else {
1399    dkwt_ev_user_permissions(j, fn, (j->kwnl)[2], isdir);
1400  }
1401  if(j->everyUser) {
1402    dkwt_ev_user_permissions(j, fn, j->everyUser, isdir);
1403  } else {
1404    dkwt_ev_user_permissions(j, fn, (j->kwnl)[3], isdir);
1405  }
1406  $? "- dkwt_ev_permissions"
1407}
1408
1409
1410
1411/**	Change permissions to own profile, grant full access
1412	to system and everyone.
1413	@param	j	Job structure.
1414*/
1415static
1416void
1417dkwt_ev_permissions_profile(DKWT_EV_J *j)
1418{
1419  dkChar	bu[DK3_MAX_PATH];
1420  size_t	sz;
1421  $? "+ dkwt_ev_permissions_profile"
1422  if((j->profBase) && (j->userName)) {
1423    sz =  dk3str_len(j->profBase);
1424    sz += dk3str_len(j->userName);
1425    sz += 1;
1426    if(sz < DK3_SIZEOF(bu,dkChar)) {
1427      dk3str_cpy_not_overlapped(bu, j->profBase);
1428      dk3str_cat(bu, dk3app_not_localized(20));
1429      dk3str_cat(bu, j->userName);
1430      dkwt_ev_permissions(j, bu, 1);
1431    }
1432  }
1433  $? "- dkwt_ev_permissions_profile"
1434}
1435
1436
1437
1438/**	Change permissions to public directory contents, grant full
1439	access to system and everyone.
1440	@param	j	Job structure.
1441*/
1442static
1443void
1444dkwt_ev_permissions_public(DKWT_EV_J *j)
1445{
1446  dkChar const	*fn;
1447  dkChar const	*en;
1448  dk3_dir_t	*dir;
1449  $? "+ dkwt_ev_permissions_public"
1450  dk3sto_it_reset(j->i_pubdir);
1451  while((fn = (dkChar const *)dk3sto_it_next(j->i_pubdir)) != NULL) {
1452    dir = dk3dir_open_app(fn, j->app);
1453    if(dir) {
1454      while(dk3dir_get_next_directory(dir)) {
1455        en = dk3dir_get_fullname(dir);
1456	if(en) {
1457	  dkwt_ev_permissions(j, en, 1);
1458	}
1459      }
1460      while(dk3dir_get_next_file(dir)) {
1461        en = dk3dir_get_fullname(dir);
1462	if(en) {
1463	  dkwt_ev_permissions(j, en, 0);
1464	}
1465      }
1466      dk3dir_close(dir);
1467    }
1468  }
1469  $? "- dkwt_ev_permissions_public"
1470}
1471
1472
1473
1474/**	Do processing.
1475	@param	j	Job structure.
1476*/
1477static
1478void
1479dkwt_ev_do_processing(DKWT_EV_J *j)
1480{
1481  $? "+ dkwt_ev_do_processing"
1482  switch(j->event) {
1483    case 0: {	$? ". boot"
1484      if(!dkwt_ev_get_keep_value(j->kwnl, 0)) {
1485        if(j->clrProfiles != 0) {
1486          dkwt_ev_cleanup_profiles(j);
1487	}
1488        dkwt_ev_cleanup_public(j);
1489      }
1490    } break;
1491    case 1: {	$? ". shutdown"
1492      if(!dkwt_ev_get_keep_value(j->kwnl, 0)) {
1493        if(j->clrProfiles != 0) {
1494          dkwt_ev_cleanup_profiles(j);
1495	}
1496        dkwt_ev_cleanup_public(j);
1497      }
1498    } break;
1499    case 2: {	$? ". login"
1500      if(!dkwt_ev_get_keep_value(j->kwnl, 1)) {
1501        if(j->clrProfiles != 0) {
1502          dkwt_ev_cleanup_profiles(j);
1503	}
1504        dkwt_ev_cleanup_public(j);
1505      }
1506    } break;
1507    case 3: {	$? ". logout"
1508      if(j->clrProfiles != 0) {
1509        dkwt_ev_permissions_profile(j);
1510      }
1511      dkwt_ev_permissions_public(j);
1512      if(!dkwt_ev_get_keep_value(j->kwnl, 0)) {
1513        if(j->clrProfiles != 0) {
1514          dkwt_ev_cleanup_profiles(j);
1515	}
1516        dkwt_ev_cleanup_public(j);
1517      }
1518      if(j->clp) {
1519        dkwt_clear_printers_local(j->app, j->msg, j->kwnl, j->userName);
1520      }
1521      if((j->s_queues) && (j->i_queues)) {
1522        dkwt_clear_printers_remote(j->app, j->msg, j->kwnl, j->userName, j->i_queues);
1523      }
1524    } break;
1525    case 4: {	$? ". cleanup_profiles"
1526      if (1 == dkwt_is_admin()) {
1527        dkwt_ev_cleanup_profiles(j);
1528      } else {
1529	dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 145);
1530      }
1531    } break;
1532  }
1533  $? "- dkwt_ev_do_processing"
1534}
1535
1536
1537
1538/**	Find account name for a SID.
1539	@param	j	Job structure.
1540	@param	dp	Destination buffer.
1541	@param	ind	Index of string SID in message array.
1542*/
1543static
1544void
1545dkwt_find_account_name_for_sid(
1546  DKWT_EV_J		*j,
1547  dkChar const * 	*dp,
1548  size_t		 ind
1549)
1550{
1551  dkChar	b1[DK3_MAX_PATH];
1552  dkChar	b2[DK3_MAX_PATH];
1553  PSID		psid;
1554  DWORD		dwSz1;
1555  DWORD		dwSz2;
1556  int		success = 0;
1557  BOOL		res;
1558  SID_NAME_USE	sidNameUse;
1559
1560  dwSz1	= DK3_SIZEOF(b1,dkChar);
1561  dwSz2 = DK3_SIZEOF(b2,dkChar);
1562  psid  = NULL;
1563#if DK3_CHAR_SIZE > 1
1564  res = ConvertStringSidToSidW((j->kwnl)[ind], &psid);
1565#else
1566  res = ConvertStringSidToSidA((j->kwnl)[ind], &psid);
1567#endif
1568  if(res) {
1569    if(psid) {
1570#if DK3_CHAR_SIZE > 1
1571      res = LookupAccountSidW(
1572        NULL,
1573	psid,
1574	b1,
1575	&dwSz1,
1576	b2,
1577	&dwSz2,
1578	&sidNameUse
1579      );
1580#else
1581      res = LookupAccountSidA(
1582        NULL,
1583	psid,
1584	b1,
1585	&dwSz1,
1586	b2,
1587	&dwSz2,
1588	&sidNameUse
1589      );
1590#endif
1591      if(res) {
1592        if(dwSz1 > 0) {
1593	  if(dwSz1 < DK3_SIZEOF(b1,dkChar)) {
1594	    b1[dwSz1] = dkT('\0');
1595	  } else {
1596	    b1[DK3_SIZEOF(b1,dkChar) - 1] = dkT('\0');
1597	  }
1598	  if(dk3str_len(b1) > 0) {
1599	    *dp = dk3str_dup_app(b1, NULL);
1600	    if(*dp) {
1601	      success = 1;
1602	    } else {		$? "! memory"
1603	      j->exval = DKT_RESULT_ERR_MEMORY;
1604	    }
1605	  } else {		$? "! size"
1606	    j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1607	  }
1608	} else {		$? "! Size"
1609	  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1610	}
1611      } else {			$? "! LookupAccountSid"
1612        j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1613      }
1614      LocalFree(psid);
1615    } else {			$? "! NULL pointer"
1616      j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1617    }
1618  } else {			$? "! ConvertStringSidToSid"
1619    j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1620  }
1621  if(!(success)) {
1622    /* ERROR: Failed to find Group name for SID! */
1623    dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 80, 81, (j->kwnl)[ind]);
1624  }
1625}
1626
1627
1628
1629/**	Complete account names for system and everyone user.
1630	@param	j	Job structure.
1631*/
1632static
1633void
1634dkwt_complete_account_names(
1635  DKWT_EV_J *j
1636)
1637{
1638  if(!(j->systemUser)) {
1639    dkwt_find_account_name_for_sid(j, &(j->systemUser), 18);
1640  }
1641  if(!(j->everyUser)) {
1642    dkwt_find_account_name_for_sid(j, &(j->everyUser), 17);
1643  }
1644}
1645
1646
1647
1648/**	Run with a found configuration file name.
1649	Process the configuration file and call dkwt_ev_do_processing().
1650	@param	j	Job structure.
1651	@param	fn	Configuration file name.
1652*/
1653static
1654void
1655dkwt_ev_run_with_config_file_name(DKWT_EV_J *j, dkChar const *fn)
1656{
1657  dkChar		 bu[DK3_MAX_PATH];
1658  dkwt_account_list_t	*al;
1659  dkwt_account_t	*ac;
1660  dkChar		*p1;
1661  int			 ok = 0;
1662  $? "+ dkwt_ev_run_with_config_file_name"
1663  ok = dk3stream_process_filename_lines_app(
1664    (void *)j,
1665    dkwt_ev_line_handler,
1666    fn,
1667    bu,
1668    DK3_SIZEOF(bu,dkChar),
1669    dk3app_get_encoding(j->app),
1670    dk3app_get_input_file_encoding(j->app),
1671    j->app
1672  );
1673  if(ok) {	$? ". ok"
1674    dkwt_complete_account_names(j);
1675    al = dkwt_tool_open_account_list(j->app, 0);
1676    if(al) {	$? ". al"
1677      dkwt_tool_reset_account_list(al);
1678      while((ac = dkwt_tool_get_account_from_list(al)) != NULL) {
1679        p1 = dk3str_dup_app(ac->logname, j->app);
1680	if(p1) {	$? ". p1"
1681	  if(!(dk3sto_add(j->s_users, (void *)p1))) {
1682	    ok = 0;	$? "! dk3sto_add"
1683	    j->exval = DKT_RESULT_ERR_MEMORY;
1684	    dk3_release(p1);
1685	  }
1686	} else {	$? "! p1"
1687	  j->exval = DKT_RESULT_ERR_MEMORY;
1688	}
1689      }
1690      dkwt_tool_close_account_list(al);
1691      if(ok) {	$? ". ok"
1692        dkwt_ev_do_processing(j);
1693      } else {	$? "! ok"
1694      }
1695    } else {	$? "! al"
1696      j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1697    }
1698  } else {	$? "! ok"
1699  } $? "- dkwt_ev_run_with_config_file_name"
1700}
1701
1702
1703
1704/**	Continue after allocating the storages.
1705	Find the users login name if necessary and one configuration file
1706	and call dkwt_ev_run_with_config_file_name().
1707	@param	j	Job structure.
1708*/
1709static
1710void
1711dkwt_ev_run_with_storages(DKWT_EV_J *j)
1712{
1713  dkChar	bu[DK3_MAX_PATH];	/* Buffer for user name. */
1714  dkChar const	*fn;			/* Configuration file name. */
1715  dk3_search_t	*sr;			/* Search result. */
1716  int		ok = 1;			/* Flag: OK so far. */
1717  $? "+ dkwt_ev_run_with_storages"
1718  switch(j->event) {
1719    case 2: case 3: {
1720      ok = 0;
1721      if(dk3sf_get_logname_app(bu, DK3_SIZEOF(bu,dkChar), j->app)) {
1722        j->userName = dk3str_dup_app(bu, j->app);
1723	if(j->userName) {
1724	  ok = 1;
1725	} else {
1726	  j->exval = DKT_RESULT_ERR_MEMORY;
1727	}
1728      } else {
1729        j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1730      }
1731    } break;
1732  }
1733  if(ok) {	$? ". ok"
1734    sr = dk3app_find_config_file_revers(j->app, (j->kwnl)[1], 1);
1735    if(sr) {	$? ". sr"
1736      dk3search_reset(sr);
1737      fn = dk3search_next(sr);
1738      if(fn) {	$? ". fn"
1739        dkwt_ev_run_with_config_file_name(j, fn);
1740      } else {	$? "! fn"
1741        j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1742      }
1743      dk3search_close(sr);
1744    } else {	$? "! sr"
1745      j->exval = DKT_RESULT_ERR_UNSPECIFIC;
1746    }
1747  } else {	$? "! ok"
1748  } $? "- dkwt_ev_run_with_storages"
1749}
1750
1751
1752
1753/**	Run for event.
1754	Allocate the storages needed later und call dkwt_ev_run_with_storages().
1755	@param	j	Job structure.
1756*/
1757static
1758void
1759dkwt_ev_run(DKWT_EV_J *j)
1760{
1761  dkChar const * const	*xargv;
1762  int			 xargc;
1763  $? "+ dkwt_ev_run"
1764  xargc = dk3app_get_argc(j->app);
1765  xargv = dk3app_get_argv(j->app);
1766  xargv++; xargv++; xargc--; xargc--;
1767  j->opt = dk3opt_open_app(
1768    dkwt_ev_options,
1769    dkwt_ev_szoptions,
1770    dkT('\0'),
1771    NULL,
1772    xargc,
1773    xargv,
1774    j->app
1775  );
1776  if(j->opt) {
1777    if(0 == dk3opt_get_error_code(j->opt)) {
1778      if(dk3opt_is_set(j->opt, dkT('t'))) {
1779        j->testOnly = 1;
1780      }
1781      j->s_users = dk3sto_open_app(j->app);
1782      if(j->s_users) {
1783        dk3sto_set_comp(j->s_users, dkwt_ev_compare_string, 0);
1784        j->i_users = dk3sto_it_open(j->s_users);
1785        if(j->i_users) {
1786          j->s_pubdir= dk3sto_open_app(j->app);
1787          if(j->s_pubdir) {
1788            dk3sto_set_comp(j->s_pubdir, dkwt_ev_compare_string, 0);
1789            j->i_pubdir = dk3sto_it_open(j->s_pubdir);
1790	    if(j->i_pubdir) {
1791	      dkwt_ev_run_with_storages(j);
1792	    } else {
1793	      j->exval = DKT_RESULT_ERR_MEMORY;
1794	    }
1795          } else {
1796            j->exval = DKT_RESULT_ERR_MEMORY;
1797          }
1798        } else {
1799          j->exval = DKT_RESULT_ERR_MEMORY;
1800        }
1801      } else {
1802        j->exval = DKT_RESULT_ERR_MEMORY;
1803      }
1804    } else {
1805      j->exval = DKT_RESULT_ERR_OPTION;
1806    }
1807  } else {
1808    j->exval = DKT_RESULT_ERR_OPTION;
1809  }
1810  $? "- dkwt_ev_run"
1811}
1812
1813
1814
1815int
1816dkwt_event(
1817  dk3_app_t		*app,
1818  dkChar const * const	*msg,
1819  dkChar const * const	*kwnl,
1820  int			 evt
1821)
1822{
1823  DKWT_EV_J		j;
1824  int			back = DKT_RESULT_ERR_UNSPECIFIC;
1825  $? "+ dkwt_event"
1826  dkwt_ev_job_init(&j);
1827  j.app = app; j.msg = msg; j.kwnl = kwnl; j.event = evt;
1828  back = j.exval = DKT_RESULT_OK;
1829  dkwt_ev_run(&j);
1830  back = j.exval;
1831  dkwt_ev_job_cleanup(&j);
1832  switch(evt) {
1833    case 0: case 1: case 2: case 3: {
1834      /*
1835      	Always indicate success, otherwise Windows might shut down
1836	or log the user off.
1837      */
1838      back = 0;
1839    } break;
1840  }
1841  $? "- dkwt_event"
1842  return back;
1843}
1844
1845
1846
1847int
1848dkwt_event_keep(
1849  dk3_app_t		*app,
1850  dkChar const * const	*msg,
1851  dkChar const * const	*kwnl
1852)
1853{
1854  int back = 1;
1855  long	result;
1856  DWORD	dw0;
1857  HKEY	hk;
1858  DWORD	dwDisp;
1859  DWORD dwType;
1860  DWORD dwValue;
1861  DWORD dwSize;
1862
1863  dwDisp = 0L;
1864  dw0 = 0L;
1865
1866  result = dkwt_tool_reg_create_key(
1867    HKEY_LOCAL_MACHINE, kwnl[15], dw0,
1868    (KEY_READ | KEY_SET_VALUE), &hk, &dwDisp
1869  );
1870  if(result == ERROR_SUCCESS) {
1871    dw0 = 0L;
1872    dwType = REG_DWORD;
1873    dwValue = 1L;
1874    dwSize = sizeof(DWORD);
1875    result = dkwt_ev_reg_set_value(
1876      hk, kwnl[16], dwType, &dwValue, dwSize
1877    );
1878    if(result == ERROR_SUCCESS) {
1879      back = 0;
1880    }
1881  }
1882  dk3sf_initialize_stdout();
1883  if(back) {
1884    dk3app_log_1(app, DK3_LL_ERROR, msg, 77);
1885    if (1 != dkwt_is_admin()) {
1886      dk3app_log_1(app, DK3_LL_ERROR, msg, 145);
1887    }
1888  } else {
1889    dk3sf_fputs(msg[78], stdout);
1890  }
1891  dk3sf_fputc(dkT('\n'), stdout);
1892  fflush(stdout);
1893  return back;
1894}
1895
1896
1897
1898int
1899dkwt_event_unkeep(
1900  dk3_app_t		* DK3_ARG_UNUSED(app),
1901  dkChar const * const	* DK3_ARG_UNUSED(msg),
1902  dkChar const * const	*kwnl
1903)
1904{
1905  DK3_UNUSED_ARG(app)
1906  DK3_UNUSED_ARG(msg)
1907  (void)dkwt_ev_get_keep_value(kwnl, 1);
1908  return 0;
1909}
1910
1911