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