]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sysinstall/user.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / sysinstall / user.c
1 /*
2  * $FreeBSD$
3  *
4  * Copyright (c) 1996
5  *      Jörg Wunsch. All rights reserved.
6  *
7  * The basic structure has been taken from tcpip.c, which is:
8  *
9  * Copyright (c) 1995
10  *      Gary J Palmer. All rights reserved.
11  *      Jordan K Hubbard. All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer,
18  *    verbatim and that no modifications are made prior to this
19  *    point in the file.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30  * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
32  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <ctype.h>
39 #include <sys/param.h>
40 #include <sysexits.h>
41
42 /* The help file for the user mgmt screen */
43 #define USER_HELPFILE           "usermgmt"
44
45 /* XXX should they be moved out to sysinstall.h? */
46 #define GNAME_FIELD_LEN 32
47 #define GID_FIELD_LEN 11
48 #define GMEMB_FIELD_LEN 64
49 #define UNAME_FIELD_LEN MAXLOGNAME
50 #define UID_FIELD_LEN 11
51 #define UGROUP_FIELD_LEN GNAME_FIELD_LEN
52 #define GECOS_FIELD_LEN 64
53 #define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
54 #define HOMEDIR_FIELD_LEN 48
55 #define SHELL_FIELD_LEN 48
56 #define PASSWD_FIELD_LEN 32
57
58 /* These are nasty, but they make the layout structure a lot easier ... */
59
60 static char gname[GNAME_FIELD_LEN],
61         gid[GID_FIELD_LEN],
62         gmemb[GMEMB_FIELD_LEN],
63         uname[UNAME_FIELD_LEN],
64         passwd[PASSWD_FIELD_LEN],
65         confpasswd[PASSWD_FIELD_LEN],
66         uid[UID_FIELD_LEN],
67         ugroup[UGROUP_FIELD_LEN],
68         gecos[GECOS_FIELD_LEN],
69         umemb[UMEMB_FIELD_LEN],
70         homedir[HOMEDIR_FIELD_LEN],
71         shell[SHELL_FIELD_LEN];
72 #define CLEAR(v)        memset(v, 0, sizeof v)
73
74 static int      okbutton, cancelbutton;
75
76
77 /* What the screen size is meant to be */
78 #define USER_DIALOG_Y           0
79 #define USER_DIALOG_X           8
80 #define USER_DIALOG_WIDTH       COLS - 16
81 #define USER_DIALOG_HEIGHT      LINES - 1
82
83 /* The group configuration menu. */
84 static Layout groupLayout[] = {
85 #define LAYOUT_GNAME            0
86     { 4, 10, 20, GNAME_FIELD_LEN - 1,
87       "Group name:", "The alphanumeric name of the new group (mandatory)",
88       gname, STRINGOBJ, NULL },
89 #define LAYOUT_GID              1
90     { 4, 38, 10, GID_FIELD_LEN - 1,
91       "GID:", "The numerical ID for this group (leave blank for automatic choice)",
92       gid, STRINGOBJ, NULL },
93 #define LAYOUT_GMEMB            2
94     { 11, 10, 40, GMEMB_FIELD_LEN - 1,
95       "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
96       gmemb, STRINGOBJ, NULL },
97 #define LAYOUT_OKBUTTON         3
98     { 18, 15, 0, 0,
99       "OK", "Select this if you are happy with these settings",
100       &okbutton, BUTTONOBJ, NULL },
101 #define LAYOUT_CANCELBUTTON     4
102     { 18, 35, 0, 0,
103       "CANCEL", "Select this if you wish to cancel this screen",
104       &cancelbutton, BUTTONOBJ, NULL },
105     LAYOUT_END,
106 };
107
108 /* The user configuration menu. */
109 static Layout userLayout[] = {
110 #define LAYOUT_UNAME            0
111     { 2, 6, 16, UNAME_FIELD_LEN - 1,
112       "Login ID:", "The login name of the new user (mandatory)",
113       uname, STRINGOBJ, NULL },
114 #define LAYOUT_UID              1
115     { 2, 23, 8, UID_FIELD_LEN - 1,
116       "UID:", "The numerical ID for this user (leave blank for automatic choice)",
117       uid, STRINGOBJ, NULL },
118 #define LAYOUT_UGROUP           2
119     { 2, 33, 8, UGROUP_FIELD_LEN - 1,
120       "Group:", "The login group name for this user (leave blank for automatic choice)",
121       ugroup, STRINGOBJ, NULL },
122 #define LAYOUT_PASSWD           3
123     { 6, 6, 20, PASSWD_FIELD_LEN - 1,
124       "Password:", "The password for this user (enter this field with care!)",
125       passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
126 #define LAYOUT_CONFPASSWD       4
127     { 6, 28, 20, PASSWD_FIELD_LEN - 1,
128       "Confirm Password:", "Confirm what you typed for the password",
129       confpasswd, NO_ECHO_OBJ(STRINGOBJ), NULL },
130 #define LAYOUT_GECOS            5
131     { 10, 6, 33, GECOS_FIELD_LEN - 1,
132       "Full name:", "The user's full name (comment)",
133       gecos, STRINGOBJ, NULL },
134 #define LAYOUT_UMEMB            6
135     { 10, 43, 15, UMEMB_FIELD_LEN - 1,
136       "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
137       umemb, STRINGOBJ, NULL },
138 #define LAYOUT_HOMEDIR          7
139     { 14, 6, 20, HOMEDIR_FIELD_LEN - 1,
140       "Home directory:", "The user's home directory (leave blank for default)",
141       homedir, STRINGOBJ, NULL },
142 #define LAYOUT_SHELL            8
143     { 14, 29, 29, SHELL_FIELD_LEN - 1,
144       "Login shell:", "The user's login shell (leave blank for default)",
145       shell, STRINGOBJ, NULL },
146 #define LAYOUT_U_OKBUTTON       9
147     { 18, 15, 0, 0,
148       "OK", "Select this if you are happy with these settings",
149         &okbutton, BUTTONOBJ, NULL },
150 #define LAYOUT_U_CANCELBUTTON   10
151     { 18, 35, 0, 0,
152       "CANCEL", "Select this if you wish to cancel this screen",
153       &cancelbutton, BUTTONOBJ, NULL },
154     LAYOUT_END,
155 };
156
157 /* whine */
158 static void
159 feepout(char *msg)
160 {
161     beep();
162     dialog_notify(msg);
163 }
164
165 /* Check for the settings on the screen. */
166
167 static int
168 verifyGroupSettings(void)
169 {
170     char tmp[256], *cp;
171     unsigned long lgid;
172
173     if (strlen(gname) == 0) {
174         feepout("The group name field must not be empty!");
175         return 0;
176     }
177     snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
178     if (vsystem("%s", tmp) == 0) {
179         feepout("This group name is already in use.");
180         return 0;
181     }
182     if (strlen(gid) > 0) {
183         lgid = strtoul(gid, &cp, 10);
184         if (lgid == 0 || lgid > GID_MAX || (*cp != '\0' && !isspace(*cp))) {
185             feepout("The GID must be a number between 1 and 4294967295.");
186             return 0;
187         }
188     }
189     if (strlen(gmemb) > 0) {
190         if (strpbrk(gmemb, " \t") != NULL) {
191             feepout("The group member list must not contain any whitespace;\n"
192                     "use commas to separate the names.");
193             return 0;
194         }
195 #ifndef notyet  /* XXX */
196         feepout("Sorry, the group member list feature\n"
197                 "is currently not yet implemented.");
198         return 0;
199 #endif
200     }
201
202     return 1;
203 }
204
205 /*
206  * Ask pw(8) to fill in the blanks for us.
207  * Works solely on the global variables.
208  */
209
210 static void
211 completeGroup(void)
212 {
213     int pfd[2], i;
214     char tmp[256], *cp;
215     ssize_t l;
216     size_t amnt;
217     pid_t pid;
218     char *vec[4] =
219     {
220         "pw", "group", "next", 0
221     };
222
223     pipe (pfd);
224     if ((pid = fork()) == 0)
225     {
226         /* The kiddy. */
227         dup2(pfd[1], 1);
228         dup2(pfd[1], 2);
229         for (i = getdtablesize(); i > 2; i--)
230             close(i);
231
232         execv("/usr/sbin/pw", vec);
233         msgDebug("Cannot execv() /usr/sbin/pw.\n");
234         _exit(99);
235     }
236     else
237     {
238         /* The oldie. */
239         close(pfd[1]);
240         amnt = sizeof tmp;
241         i = 0;
242         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
243         {
244             amnt -= l;
245             i += l;
246             if (amnt == 0)
247             {
248                 close(pfd[0]);
249                 break;
250             }
251         }
252         close(pfd[0]);
253         tmp[i] = '\0';
254         waitpid(pid, &i, 0);
255         if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
256             /* ignore by now */
257             return;
258         if ((cp = strchr(tmp, '\n')) != NULL)
259             *cp = '\0';
260         strncpy(gid, tmp, sizeof gid);
261     }
262 }
263
264 static void
265 addGroup(WINDOW *ds_win)
266 {
267     char tmp[256];
268     int pfd[2], i;
269     ssize_t l;
270     size_t amnt;
271     pid_t pid;
272     char *vec[8] =
273     {
274         "pw", "group", "add", "-n", 0, "-g", 0, 0
275     };
276 #define VEC_GNAME 4
277 #define VEC_GID 6
278
279     msgNotify("Adding group \"%s\"...", gname);
280
281     pipe (pfd);
282     if ((pid = fork()) == 0)
283     {
284         /* The kiddy. */
285         dup2(pfd[1], 1);
286         dup2(pfd[1], 2);
287         for (i = getdtablesize(); i > 2; i--)
288             close(i);
289
290         vec[VEC_GNAME] = gname;
291
292         if (strlen(gid) > 0)
293             vec[VEC_GID] = gid;
294         else
295             vec[VEC_GID - 1] = 0;
296
297         execv("/usr/sbin/pw", vec);
298         msgDebug("Cannot execv() /usr/sbin/pw.\n");
299         _exit(99);
300     }
301     else
302     {
303         /* The oldie. */
304         close(pfd[1]);
305         amnt = sizeof tmp;
306         i = 0;
307         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
308         {
309             amnt -= l;
310             i += l;
311             if (amnt == 0)
312             {
313                 close(pfd[0]);
314                 break;
315             }
316         }
317         close(pfd[0]);
318         tmp[i] = '\0';
319         waitpid(pid, &i, 0);
320         if (WIFSIGNALED(i))
321             msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
322         else if(WEXITSTATUS(i))
323         {
324             i = 0;
325             if(strncmp(tmp, "pw: ", 4) == 0)
326                 i = 4;
327             tmp[sizeof tmp - 1] = '\0'; /* sanity */
328             msgConfirm("The `pw' command exited with an error status.\n"
329                        "Its error message was:\n\n%s",
330                        &tmp[i]);
331         }
332     }
333 #undef VEC_GNAME
334 #undef VEC_GID
335 }
336
337 int
338 userAddGroup(dialogMenuItem *self)
339 {
340     WINDOW              *ds_win, *save;
341     ComposeObj          *obj = NULL;
342     int                 n = 0, cancel = FALSE, ret;
343     int                 max, firsttime = TRUE;
344
345     if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
346         msgConfirm("This option may only be used after the system is installed, sorry!");
347         return DITEM_FAILURE;
348     }
349
350     save = savescr();
351     dialog_clear_norefresh();
352     /* We need a curses window */
353     if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
354                                     USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
355         beep();
356         msgConfirm("Cannot open addgroup dialog window!!");
357         return(DITEM_FAILURE);
358     }
359
360     /* Draw a group entry box */
361     draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
362              USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
363     wattrset(ds_win, dialog_attr);
364     mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
365
366     CLEAR(gname);
367     CLEAR(gid);
368     CLEAR(gmemb);
369
370     /* Some more initialisation before we go into the main input loop */
371     obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
372
373 reenter:
374     cancelbutton = okbutton = 0;
375     if (firsttime) {
376         /* fill in the blanks, well, just the GID */
377         completeGroup();
378         RefreshStringObj(groupLayout[LAYOUT_GID].obj);
379         firsttime = FALSE;
380     }
381
382     while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
383
384     if (!cancel && !verifyGroupSettings())
385         goto reenter;
386
387     /* Clear this crap off the screen */
388     delwin(ds_win);
389     dialog_clear_norefresh();
390     use_helpfile(NULL);
391
392     if (!cancel) {
393         addGroup(ds_win);
394         ret = DITEM_SUCCESS;
395     }
396     else
397         ret = DITEM_FAILURE;
398     restorescr(save);
399     return ret;
400 }
401
402 /* Check for the settings on the screen. */
403
404 static int
405 verifyUserSettings(WINDOW *ds_win)
406 {
407     char tmp[256], *cp;
408     unsigned long luid;
409     WINDOW *save;
410     int rv;
411
412     if (strlen(uname) == 0) {
413         feepout("The user name field must not be empty!");
414         return 0;
415     }
416     snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
417     if (vsystem("%s", tmp) == 0) {
418         feepout("This user name is already in use.");
419         return 0;
420     }
421     if (strlen(uid) > 0) {
422         luid = strtoul(uid, &cp, 10);
423         if (luid == 0 || luid > UID_MAX || (*cp != '\0' && !isspace(*cp))) {
424             feepout("The UID must be a number between 1 and 4294967295.");
425             return 0;
426         }
427     }
428     if (strcmp(passwd, confpasswd)) {
429         feepout("Passwords don't match");
430         return 0;
431     }
432     if ((homedir[0]!=0) && (homedir[0]!='/')) {
433         feepout("The pathname for home directories must begin with a '/'.");
434         return 0;
435     }
436     if (strlen(shell) > 0) {
437         setusershell();
438         while((cp = getusershell()) != NULL)
439             if (strcmp(cp, shell) == 0)
440                 break;
441         endusershell();
442         if (cp == NULL) {
443             save = savescr();
444             rv = msgYesNo("Warning:\n\n"
445                           "The requested shell \"%s\" is not\n"
446                           "a valid user shell.\n\n"
447                           "Use it anyway?\n", shell);
448             restorescr(save);
449             wrefresh(ds_win);
450             if (rv != DITEM_SUCCESS)
451                 return 0;
452         }
453         
454     }
455
456     if (strlen(umemb) > 0) {
457         if (strpbrk(umemb, " \t") != NULL) {
458             feepout("The member groups list must not contain any whitespace;\n"
459                     "use commas to separate the names.");
460             return 0;
461         }
462     }
463
464     return 1;
465 }
466
467 /*
468  * Ask pw(8) to fill in the blanks for us.
469  * Works solely on the global variables.
470  */
471
472 static void
473 completeUser(void)
474 {
475     int pfd[2], i;
476     char tmp[256], *cp, *cp2;
477     ssize_t l;
478     size_t amnt;
479     pid_t pid;
480     char *vec[7] =
481     {
482         "pw", "user", "add", "-N", "-n", 0, 0
483     };
484 #define VEC_UNAME 5
485
486     pipe (pfd);
487     if ((pid = fork()) == 0)
488     {
489         /* The kiddy. */
490         dup2(pfd[1], 1);
491         dup2(pfd[1], 2);
492         for (i = getdtablesize(); i > 2; i--)
493             close(i);
494
495         vec[VEC_UNAME] = uname;
496
497         execv("/usr/sbin/pw", vec);
498         msgDebug("Cannot execv() /usr/sbin/pw.\n");
499         _exit(99);
500     }
501     else
502     {
503         /* The oldie. */
504         close(pfd[1]);
505         amnt = sizeof tmp;
506         i = 0;
507         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
508         {
509             amnt -= l;
510             i += l;
511             if (amnt == 0)
512             {
513                 close(pfd[0]);
514                 break;
515             }
516         }
517         close(pfd[0]);
518         tmp[i] = '\0';
519         waitpid(pid, &i, 0);
520         if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
521             /* ignore by now */
522             return;
523         if ((cp = strchr(tmp, '\n')) != NULL)
524             *cp = '\0';
525         if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
526             return;
527         cp++;
528         if ((cp2 = strchr(cp, ':')) == NULL)
529             return;
530         *cp2++ = '\0';
531         strncpy(uid, cp, sizeof uid);
532         cp = cp2;
533         if ((cp2 = strchr(cp, ':')) == NULL)
534             return;
535         *cp2++ = '\0';
536 #ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
537         strncpy(ugroup, cp, sizeof ugroup);
538 #endif
539         cp = cp2;
540         if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
541             (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
542             return;
543         *cp2++ = '\0';
544         cp++;
545         strncpy(gecos, cp, sizeof gecos);
546         cp = cp2;
547         if ((cp2 = strchr(cp, ':')) == NULL)
548             return;
549         *cp2++ = '\0';
550         if (*cp2)
551             strncpy(shell, cp2, sizeof shell);
552     }
553 #undef VEC_UNAME
554 }
555
556 static void
557 addUser(WINDOW *ds_win)
558 {
559     char tmp[256], *msg;
560     int pfd[2], ipfd[2], i, j;
561     ssize_t l;
562     size_t amnt;
563     pid_t pid;
564     /*
565      * Maximal list:
566      * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
567      */
568     char *vec[21] =
569     {
570         "pw", "user", "add", "-m", "-n", /* ... */
571     };
572 #define VEC_UNAME 5
573
574     msgNotify("Adding user \"%s\"...", uname);
575
576     pipe (pfd);
577     pipe (ipfd);
578     if ((pid = fork()) == 0)
579     {
580         /* The kiddy. */
581         dup2(ipfd[0], 0);
582         dup2(pfd[1], 1);
583         dup2(pfd[1], 2);
584         for (i = getdtablesize(); i > 2; i--)
585             close(i);
586
587         vec[i = VEC_UNAME] = uname;
588         i++;
589 #define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
590         ADDVEC(ugroup, "-g");
591         ADDVEC(uid, "-u");
592         ADDVEC(gecos, "-c");
593         ADDVEC(homedir, "-d");
594         ADDVEC(shell, "-s");
595         ADDVEC(umemb, "-G");
596         if (passwd[0]) {
597             vec[i++] = "-h";
598             vec[i++] = "0";
599         }
600         vec[i] = 0;
601
602         execv("/usr/sbin/pw", vec);
603         msgDebug("Cannot execv() /usr/sbin/pw.\n");
604         _exit(99);
605     }
606     else
607     {
608         /* The oldie. */
609         close(pfd[1]);
610         close(ipfd[0]);
611
612         if (passwd[0])
613             write(ipfd[1], passwd, strlen(passwd));
614         close(ipfd[1]);
615         amnt = sizeof tmp;
616         i = 0;
617         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
618         {
619             amnt -= l;
620             i += l;
621             if (amnt == 0)
622             {
623                 close(pfd[0]);
624                 break;
625             }
626         }
627         close(pfd[0]);
628         tmp[i] = '\0';
629         waitpid(pid, &i, 0);
630         if (WIFSIGNALED(i))
631         {
632             j = WTERMSIG(i);
633             msg = "The `pw' command exited with signal %d.\n";
634             goto sysfail;
635         }
636         else if((j = WEXITSTATUS(i)))
637         {
638             i = 0;
639             if(strncmp(tmp, "pw: ", 4) == 0)
640                 i = 4;
641             tmp[sizeof tmp - 1] = '\0'; /* sanity */
642             if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
643                 msgConfirm("The `pw' command exited with an error status.\n"
644                            "Its error message was:\n\n%s",
645                            &tmp[i]);
646             else
647             {
648                 msg = "The `pw' command exited with unexpected status %d.\n";
649         sysfail:
650                 msgDebug(msg, j);
651                 msgDebug("Command stdout and stderr was:\n\n%s", tmp);
652                 msgConfirm(msg, j);
653             }
654         }
655         else if (!passwd[0])
656             msgConfirm("You will need to enter a password for this user\n"
657                        "later, using the passwd(1) command from the shell.\n\n"
658                        "The account for `%s' is currently still disabled.",
659                        uname);
660     }
661 #undef VEC_UNAME
662 #undef ADDVEC
663 }
664
665 int
666 userAddUser(dialogMenuItem *self)
667 {
668     WINDOW              *ds_win, *save;
669     ComposeObj          *obj = NULL;
670     int                 n = 0, cancel = FALSE, ret;
671     int                 max, firsttime = TRUE, filled=0;
672
673     if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
674         msgConfirm("This option may only be used after the system is installed, sorry!");
675         return DITEM_FAILURE;
676     }
677
678     save = savescr();
679     dialog_clear_norefresh();
680
681     /* We need a curses window */
682     if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
683                                     USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
684         beep();
685         msgConfirm("Cannot open adduser dialog window!!");
686         return(DITEM_FAILURE);
687     }
688
689     /* Draw a user entry box */
690     draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
691              USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
692     wattrset(ds_win, dialog_attr);
693     mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 24, " Add a new user ");
694
695     CLEAR(uname);
696     CLEAR(uid);
697     CLEAR(ugroup);
698     CLEAR(gecos);
699     CLEAR(passwd);
700     CLEAR(confpasswd);
701     CLEAR(umemb);
702     CLEAR(homedir);
703     CLEAR(shell);
704
705     /* Some more initialisation before we go into the main input loop */
706     obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
707     
708 reenter:
709     cancelbutton = okbutton = 0;
710     if (firsttime) {
711         /* fill in the blanks, well, just the GID */
712         completeUser();
713         RefreshStringObj(userLayout[LAYOUT_UID].obj);
714         RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
715         RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
716         RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
717         RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
718         RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
719         firsttime = FALSE;
720     }
721
722     while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
723         /* Prevent this from being irritating if user really means NO */
724         if (filled < 3) {
725           if ((uname[0]) && !homedir[0]) {
726               SAFE_STRCPY(homedir,"/home/");
727               strcat(homedir,uname);
728               RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
729               ++filled;
730             }
731         }
732     };
733
734     if (!cancel && !verifyUserSettings(ds_win))
735         goto reenter;
736
737     /* Clear this crap off the screen */
738     delwin(ds_win);
739     dialog_clear_norefresh();
740     use_helpfile(NULL);
741
742     if (!cancel) {
743         addUser(ds_win);
744         ret = DITEM_SUCCESS;
745     }
746     else
747         ret = DITEM_FAILURE;
748     restorescr(save);
749     return ret;
750 }
751