]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - release/sysinstall/user.c
Defensive coding to prevent a potential segfault.
[FreeBSD/FreeBSD.git] / release / 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     { NULL },
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     { NULL },
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(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(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 (strlen(shell) > 0) {
425         while((cp = getusershell()) != NULL)
426             if (strcmp(cp, shell) == 0)
427                 break;
428         endusershell();
429         if (cp == NULL) {
430             save = savescr();
431             rv = msgYesNo("Warning:\n\n"
432                           "The requested shell \"%s\" is not\n"
433                           "a valid user shell.\n\n"
434                           "Use it anyway?\n", shell);
435             restorescr(save);
436             wrefresh(ds_win);
437             if (rv != DITEM_SUCCESS)
438                 return 0;
439         }
440         
441     }
442
443     if (strlen(umemb) > 0) {
444         if (strpbrk(umemb, " \t") != NULL) {
445             feepout("The member groups list must not contain any whitespace;\n"
446                     "use commas to separate the names.");
447             return 0;
448         }
449     }
450
451     return 1;
452 }
453
454 /*
455  * Ask pw(8) to fill in the blanks for us.
456  * Works solely on the global variables.
457  */
458
459 static void
460 completeUser(void)
461 {
462     int pfd[2], i;
463     char tmp[256], *cp, *cp2;
464     ssize_t l;
465     size_t amnt;
466     pid_t pid;
467     char *vec[7] =
468     {
469         "pw", "user", "add", "-N", "-n", 0, 0
470     };
471 #define VEC_UNAME 5
472
473     pipe (pfd);
474     if ((pid = fork()) == 0)
475     {
476         /* The kiddy. */
477         dup2(pfd[1], 1);
478         dup2(pfd[1], 2);
479         for (i = getdtablesize(); i > 2; i--)
480             close(i);
481
482         vec[VEC_UNAME] = uname;
483
484         execv("/usr/sbin/pw", vec);
485         msgDebug("Cannot execv() /usr/sbin/pw.\n");
486         _exit(99);
487     }
488     else
489     {
490         /* The oldie. */
491         close(pfd[1]);
492         amnt = sizeof tmp;
493         i = 0;
494         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
495         {
496             amnt -= l;
497             i += l;
498             if (amnt == 0)
499             {
500                 close(pfd[0]);
501                 break;
502             }
503         }
504         close(pfd[0]);
505         tmp[i] = '\0';
506         waitpid(pid, &i, 0);
507         if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
508             /* ignore by now */
509             return;
510         if ((cp = strchr(tmp, '\n')) != NULL)
511             *cp = '\0';
512         if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
513             return;
514         cp++;
515         if ((cp2 = strchr(cp, ':')) == NULL)
516             return;
517         *cp2++ = '\0';
518         strncpy(uid, cp, sizeof uid);
519         cp = cp2;
520         if ((cp2 = strchr(cp, ':')) == NULL)
521             return;
522         *cp2++ = '\0';
523 #ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
524         strncpy(ugroup, cp, sizeof ugroup);
525 #endif
526         cp = cp2;
527         if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
528             (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
529             return;
530         *cp2++ = '\0';
531         cp++;
532         strncpy(gecos, cp, sizeof gecos);
533         cp = cp2;
534         if ((cp2 = strchr(cp, ':')) == NULL)
535             return;
536         *cp2++ = '\0';
537         if (*cp2)
538             strncpy(shell, cp2, sizeof shell);
539     }
540 #undef VEC_UNAME
541 }
542
543 static void
544 addUser(WINDOW *ds_win)
545 {
546     char tmp[256], *msg;
547     int pfd[2], ipfd[2], i, j;
548     ssize_t l;
549     size_t amnt;
550     pid_t pid;
551     /*
552      * Maximal list:
553      * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
554      */
555     char *vec[21] =
556     {
557         "pw", "user", "add", "-m", "-n", /* ... */
558     };
559 #define VEC_UNAME 5
560
561     msgNotify("Adding user \"%s\"...", uname);
562
563     pipe (pfd);
564     pipe (ipfd);
565     if ((pid = fork()) == 0)
566     {
567         /* The kiddy. */
568         dup2(ipfd[0], 0);
569         dup2(pfd[1], 1);
570         dup2(pfd[1], 2);
571         for (i = getdtablesize(); i > 2; i--)
572             close(i);
573
574         vec[i = VEC_UNAME] = uname;
575         i++;
576 #define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
577         ADDVEC(ugroup, "-g");
578         ADDVEC(uid, "-u");
579         ADDVEC(gecos, "-c");
580         ADDVEC(homedir, "-d");
581         ADDVEC(shell, "-s");
582         ADDVEC(umemb, "-G");
583         if (passwd[0]) {
584             vec[i++] = "-h";
585             vec[i++] = "0";
586         }
587         vec[i] = 0;
588
589         execv("/usr/sbin/pw", vec);
590         msgDebug("Cannot execv() /usr/sbin/pw.\n");
591         _exit(99);
592     }
593     else
594     {
595         /* The oldie. */
596         close(pfd[1]);
597         close(ipfd[0]);
598
599         if (passwd[0])
600             write(ipfd[1], passwd, strlen(passwd));
601         close(ipfd[1]);
602         amnt = sizeof tmp;
603         i = 0;
604         while((l = read(pfd[0], &tmp[i], amnt)) > 0)
605         {
606             amnt -= l;
607             i += l;
608             if (amnt == 0)
609             {
610                 close(pfd[0]);
611                 break;
612             }
613         }
614         close(pfd[0]);
615         tmp[i] = '\0';
616         waitpid(pid, &i, 0);
617         if (WIFSIGNALED(i))
618         {
619             j = WTERMSIG(i);
620             msg = "The `pw' command exited with signal %d.\n";
621             goto sysfail;
622         }
623         else if((j = WEXITSTATUS(i)))
624         {
625             i = 0;
626             if(strncmp(tmp, "pw: ", 4) == 0)
627                 i = 4;
628             tmp[sizeof tmp - 1] = '\0'; /* sanity */
629             if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
630                 msgConfirm("The `pw' command exited with an error status.\n"
631                            "Its error message was:\n\n%s",
632                            &tmp[i]);
633             else
634             {
635                 msg = "The `pw' command exited with unexpected status %d.\n";
636         sysfail:
637                 msgDebug(msg, j);
638                 msgDebug("Command stdout and stderr was:\n\n%s", tmp);
639                 msgConfirm(msg, j);
640             }
641         }
642         else if (!passwd[0])
643             msgConfirm("You will need to enter a password for this user\n"
644                        "later, using the passwd(1) command from the shell.\n\n"
645                        "The account for `%s' is currently still disabled.",
646                        uname);
647     }
648 #undef VEC_UNAME
649 #undef ADDVEC
650 }
651
652 int
653 userAddUser(dialogMenuItem *self)
654 {
655     WINDOW              *ds_win, *save;
656     ComposeObj          *obj = NULL;
657     int                 n = 0, cancel = FALSE, ret;
658     int                 max, firsttime = TRUE;
659
660     if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
661         msgConfirm("This option may only be used after the system is installed, sorry!");
662         return DITEM_FAILURE;
663     }
664
665     save = savescr();
666     dialog_clear_norefresh();
667
668     /* We need a curses window */
669     if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
670                                     USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
671         beep();
672         msgConfirm("Cannot open adduser dialog window!!");
673         return(DITEM_FAILURE);
674     }
675
676     /* Draw a user entry box */
677     draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
678              USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
679     wattrset(ds_win, dialog_attr);
680     mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 22, " Add a new user ");
681
682     CLEAR(uname);
683     CLEAR(uid);
684     CLEAR(ugroup);
685     CLEAR(gecos);
686     CLEAR(passwd);
687     CLEAR(umemb);
688     CLEAR(homedir);
689     CLEAR(shell);
690
691     /* Some more initialisation before we go into the main input loop */
692     obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
693     
694 reenter:
695     cancelbutton = okbutton = 0;
696     if (firsttime) {
697         /* fill in the blanks, well, just the GID */
698         completeUser();
699         RefreshStringObj(userLayout[LAYOUT_UID].obj);
700         RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
701         RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
702         RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
703         RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
704         RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
705         firsttime = FALSE;
706     }
707
708     while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel));
709
710     if (!cancel && !verifyUserSettings(ds_win))
711         goto reenter;
712
713     /* Clear this crap off the screen */
714     delwin(ds_win);
715     dialog_clear_norefresh();
716     use_helpfile(NULL);
717
718     if (!cancel) {
719         addUser(ds_win);
720         ret = DITEM_SUCCESS;
721     }
722     else
723         ret = DITEM_FAILURE;
724     restorescr(save);
725     return ret;
726 }
727