3 * This is the file user.c for the Moira Client, which allows users
4 * to quickly and easily maintain most parts of the Moira database.
5 * It Contains: Functions for manipulating user information.
8 * By: Chris D. Peterson
10 * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
11 * For copying and distribution information, please see the file
15 #include <mit-copyright.h>
17 #include <moira_site.h>
32 void CorrectCapitalization(char **name);
33 char **AskUserInfo(char **info, Bool name);
34 struct mqelem *GetUserInfo(int type, char *name1, char *name2);
43 #define DEFAULT_SHELL "/bin/athena/tcsh"
45 #define DEFAULT_SHELL "/bin/csh"
47 #define DEFAULT_CLASS "?"
50 /* Function Name: UserState
51 * Description: Convert a numeric state into a descriptive string.
52 * Arguments: state value
53 * Returns: pointer to statically allocated string.
56 static char *states[] = {
59 "Half Registered (2)",
61 "Not registerable (4)",
62 "Enrolled/Registerable (5)",
63 "Enrolled/Not Registerable (6)",
67 static char *UserState(int state)
69 static char buf[BUFSIZ];
71 if (state < 0 || state >= US_END)
73 sprintf(buf, "Unknown (%d)", state);
80 /* Function Name: PrintUserName
81 * Description: Print name of a user.
82 * Arguments: info - the information about a user.
86 static void PrintUserName(char **info)
88 char buf[BUFSIZ], print_buf[BUFSIZ];
89 sprintf(buf, "%s, %s %s", info[U_LAST], info[U_FIRST], info[U_MIDDLE]);
90 sprintf(print_buf, "%-40s User Name: %s", buf, info[U_NAME]);
91 Put_message(print_buf);
94 /* Function Name: PrintUserInfo
95 * Description: Prints Information about a user.
96 * Arguments: info - an argument list with the user information
101 static void PrintUserInfo(char **info)
103 char name[BUFSIZ], buf[BUFSIZ];
106 sprintf(name, "%s, %s %s", info[U_LAST], info[U_FIRST], info[U_MIDDLE]);
107 sprintf(buf, "Login name: %-20s Full name: %s", info[U_NAME], name);
109 sprintf(buf, "User id: %-23s Login shell %-10s Class: %s",
110 info[U_UID], info[U_SHELL], info[U_CLASS]);
113 sprintf(buf, "Account is: %-20s MIT ID number: %s",
114 UserState(atoi(info[U_STATE])), info[U_MITID]);
116 status = atoi(info[U_STATE]);
117 if (status == 0 || status == 2)
119 sprintf(buf, "User %s secure Account Coupon to register",
120 atoi(info[U_SECURE]) ? "needs" : "does not need");
123 sprintf(buf, "Comments: %s", info[U_COMMENT]);
125 sprintf(buf, MOD_FORMAT, info[U_MODBY], info[U_MODTIME], info[U_MODWITH]);
129 /* Function Name: SetUserDefaults
130 * Description: Sets the default values for add user.
131 * Arguments: info - a blank user info array of char *'s.
132 * Returns: args - the filled info structure.
135 static char **SetUserDefaults(char **info)
137 info[U_NAME] = strdup(UNIQUE_LOGIN);
138 info[U_UID] = strdup(UNIQUE_UID);
139 info[U_SHELL] = strdup(DEFAULT_SHELL);
140 info[U_LAST] = strdup(DEFAULT_NONE);
141 info[U_FIRST] = strdup(DEFAULT_NONE);
142 info[U_MIDDLE] = strdup(DEFAULT_NONE);
143 info[U_STATE] = strdup(DEFAULT_NO);
144 info[U_MITID] = strdup(DEFAULT_NONE);
145 info[U_CLASS] = strdup(DEFAULT_CLASS);
146 info[U_COMMENT] = strdup("");
147 info[U_SIGNATURE] = strdup("");
148 info[U_SECURE] = strdup("0");
149 info[U_MODTIME] = info[U_MODBY] = info[U_MODWITH] = info[U_END] = NULL;
154 /* Check that the supplied name follows the capitalization rules, and
155 * offer to correct it if not.
158 void CorrectCapitalization(char **name)
160 char temp_buf[BUFSIZ], fixname[BUFSIZ];
162 strcpy(fixname, *name);
164 if (strcmp(fixname, *name))
166 Put_message("You entered a name which does not follow the capitalization conventions.");
167 sprintf(temp_buf, "Correct it to \"%s\"", fixname);
168 if (YesNoQuestion(temp_buf, 1) == TRUE)
171 *name = strdup(fixname);
177 /* Function Name: AskUserInfo.
178 * Description: This function askes the user for information about a
179 * machine and saves it into a structure.
180 * Arguments: info - a pointer the the structure to put the info into.
181 * flags - Flags asking us which info we want.
182 * Returns: the args to pass to the query.
183 * NOTES: the return args are not necessarily in the correct order to
184 * use the #defined names (e.g args[UID] is not the uid anymore).
187 char **AskUserInfo(char **info, Bool name)
190 char temp_buf[BUFSIZ], *newname;
194 sprintf(temp_buf, "\nChanging Attributes of user %s.\n", info[U_NAME]);
195 Put_message(temp_buf);
199 struct mqelem *elem = NULL;
202 if (GetValueFromUser("User's last name", &info[U_LAST]) == SUB_ERROR)
204 CorrectCapitalization(&info[U_LAST]);
205 if (GetValueFromUser("User's first name", &info[U_FIRST]) == SUB_ERROR)
207 CorrectCapitalization(&info[U_FIRST]);
208 if (GetValueFromUser("User's middle name", &info[U_MIDDLE]) == SUB_ERROR)
210 CorrectCapitalization(&info[U_MIDDLE]);
211 argv[0] = info[U_FIRST];
212 argv[1] = info[U_LAST];
213 if (do_mr_query("get_user_account_by_name", 2, argv,
214 StoreInfo, &elem) == MR_SUCCESS)
216 Put_message("A user by that name already exists in the database.");
217 Loop(QueueTop(elem), PrintUserInfo);
218 Loop(QueueTop(elem), FreeInfo);
220 if (YesNoQuestion("Add new user anyway", TRUE) != TRUE)
226 newname = strdup(info[U_NAME]);
227 if (GetValueFromUser("The new login name for this user", &newname) ==
231 else if (GetValueFromUser("Login name for this user", &info[U_NAME]) ==
235 strcpy(temp_buf, info[U_UID]);
236 if (GetValueFromUser("User's UID", &info[U_UID]) == SUB_ERROR)
238 if (strcmp(info[U_UID], UNIQUE_UID) && strcmp(info[U_UID], temp_buf))
240 struct mqelem *elem = NULL;
241 if (do_mr_query("get_user_account_by_uid", 1, &info[U_UID],
242 StoreInfo, &elem) == MR_SUCCESS)
244 Put_message("A user with that uid already exists in the database.");
245 Loop(QueueTop(elem), PrintUserInfo);
246 Loop(QueueTop(elem), FreeInfo);
248 if (YesNoQuestion("Add new user anyway", TRUE) != TRUE)
253 if (GetValueFromUser("User's shell", &info[U_SHELL]) == SUB_ERROR)
257 if (GetValueFromUser("User's last name", &info[U_LAST]) == SUB_ERROR)
259 CorrectCapitalization(&info[U_LAST]);
260 if (GetValueFromUser("User's first name", &info[U_FIRST]) == SUB_ERROR)
262 CorrectCapitalization(&info[U_FIRST]);
263 if (GetValueFromUser("User's middle name", &info[U_MIDDLE]) == SUB_ERROR)
265 CorrectCapitalization(&info[U_MIDDLE]);
270 if (GetValueFromUser("User's status (? for help)", &info[U_STATE]) ==
273 if (isdigit(info[U_STATE][0]))
275 Put_message("Valid status numbers:");
276 for (i = 0; i < US_END; i++)
278 sprintf(temp_buf, " %d: %s", i, states[i]);
279 Put_message(temp_buf);
282 if (GetValueFromUser("User's MIT ID number", &info[U_MITID]) == SUB_ERROR)
284 RemoveHyphens(info[U_MITID]);
285 if (GetTypeFromUser("User's MIT Year (class)", "class", &info[U_CLASS]) ==
288 if (GetValueFromUser("Comments", &info[U_COMMENT]) == SUB_ERROR)
291 state = atoi(info[U_STATE]);
292 if (!name || state == 0 || state == 2)
294 if (YesNoQuestion("User needs secure Account Coupon to register",
295 atoi(info[U_SECURE]) ? TRUE : FALSE) == FALSE)
297 free(info[U_SECURE]);
298 info[U_SECURE] = strdup("0");
302 free(info[U_SECURE]);
303 info[U_SECURE] = strdup("1");
307 info[U_SIGNATURE] = strdup("");
309 FreeAndClear(&info[U_MODTIME], TRUE);
310 FreeAndClear(&info[U_MODBY], TRUE);
311 FreeAndClear(&info[U_MODWITH], TRUE);
314 * Slide the newname into the #2 slot, this screws up all future references
315 * to this list, since we slip the pointer into a info list it gets freed
316 * when the rest of the list gets freed.
319 SlipInNewName(info, newname);
324 /* Function Name: GetUserInfo
325 * Description: Stores the user information in a queue.
326 * Arguments: type - type of field given to get info, one of:
327 * LOGIN, UID, BY_NAME, CLASS.
328 * name1 - name of thing specified by type (wildcards okay)
329 * name2 - other name, only used in get user by first and last.
331 * Returns: the first element of the queue containing the user info.
335 struct mqelem *GetUserInfo(int type, char *name1, char *name2)
339 struct mqelem *elem = NULL;
345 if ((status = do_mr_query("get_user_account_by_login", 1, args,
348 com_err(program_name, status,
349 " when attempting to get_user_account_by_login.");
355 if ((status = do_mr_query("get_user_account_by_uid", 1, args,
358 com_err(program_name, status,
359 " when attempting to get_user_account_by_uid.");
366 if ((status = do_mr_query("get_user_account_by_name", 2, args,
369 com_err(program_name, status,
370 " when attempting to get_user_account_by_name.");
376 if ((status = do_mr_query("get_user_account_by_class", 1, args,
379 com_err(program_name, status,
380 " when attempting to get_user_account_by_class.");
386 if ((status = do_mr_query("get_user_account_by_id", 1, args,
389 com_err(program_name, status,
390 " when attempting to get_user_account_by_id.");
395 return QueueTop(elem) ;
398 /* Function Name: AddNewUser
399 * Description: Adds a new user to the database.
401 * Returns: DM_NORMAL.
404 int AddNewUser(int argc, char **argv)
407 char **args, *info[MAX_ARGS_SIZE];
409 if (!(args = AskUserInfo(SetUserDefaults(info), FALSE)))
411 Put_message("Aborted.");
414 if ((status = do_mr_query("add_user_account", CountArgs(args),
416 com_err(program_name, status, " in add_user_account");
418 Put_message("New user added to database.");
424 /* Function Name: GetLoginName
425 * Description: Asks the user for a login name and reserves
428 * Returns: a malloced login name for the user.
431 static char *GetLoginName(void)
436 if (GetValueFromUser("Login name for this user? ", &name) == SUB_ERROR)
438 Put_message("KERBEROS code not added, did not reserve name with kerberos.");
443 /* Function Name: ChooseUser
444 * Description: Choose a user from a list and return the uid.
445 * Arguments: top - a queue of user information.
446 * Returns: uid - the malloced uid of the user that was chosen.
449 static char *ChooseUser(struct mqelem *elem)
453 char **info = elem->q_data;
455 switch (YesNoQuitQuestion("Is this the user you want (y/n/q)", FALSE))
458 return strdup(info[U_UID]);
461 default: /* quit or ^C. */
469 /* Function Name: GetUidNumberFromName
470 * Description: Gets the users uid number, from the name.
472 * Returns: uid - a malloced string containing the uid.
475 static char *GetUidNumberFromName(void)
477 char *args[5], *uid, first[BUFSIZ], last[BUFSIZ];
479 struct mqelem *top = NULL;
481 if (!Prompt_input("First Name: ", first, BUFSIZ))
483 if (!Prompt_input("Last Name: ", last, BUFSIZ))
491 switch ((status = do_mr_query("get_user_account_by_name", 2, args,
497 Put_message("There is no user in the database with that name.");
500 com_err(program_name, status, " in get_account_user_by_name.");
505 if (QueueCount(top) == 1) /* This is a unique name. */
507 char **info = top->q_data;
508 Put_message("User ID Number retrieved for the user: ");
511 uid = strdup(info[U_UID]);
516 Put_message("That name is not unique, choose the user that you want.");
517 uid = ChooseUser(top);
522 /* Function Name: SetUserPassword
523 * Description: Set the new kerberos password for this user.
524 * Arguments: name - kerberos principle name for this user, (login name).
528 static void SetUserPassword(char *name)
530 name = name; /* make saber happy. */
531 Put_message("Kerberos password not changed, code non-existant.");
532 /* clever message to call account_admin, if this fails. */
535 /* Function Name: GiveBackLogin
536 * Description: Gives back previously reserved kerberous principle.
537 * Arguments: name - principle to give back.
541 static void GiveBackLogin(char *name)
543 name = name; /* make saber happy. */
544 Put_message("kerberos code not implemented, name not given back.");
545 /* send mail to db maintainer if this fails. */
548 /* Function Name: RegisterUser
549 * Description: This function registers a user.
551 * Returns: DM_NORMAL.
554 int RegisterUser(int argc, char **argv)
556 char *args[MAX_ARGS_SIZE];
557 char *login, *potype = NULL;
558 char temp_buf[BUFSIZ];
561 Put_message("This function has NO kerberos support, so strange things");
562 Put_message("may happen if you use it to register a user.");
564 switch (YesNoQuestion("Do you know the users UID Number (y/n)", FALSE))
567 Prompt_input("What is the UID number of the user? ", temp_buf, BUFSIZ);
568 args[0] = strdup(temp_buf);
571 if (!(args[0] = GetUidNumberFromName()))
578 sprintf(temp_buf, "u%s", args[0]);
579 login = strdup(temp_buf);
580 if (GetValueFromUser("Login name for this user? ", &login) == SUB_ERROR)
583 FreeInfo(args); /* This work because the NULL temination is ok. */
586 Put_message("KERBEROS code not added, did not reserve name with kerberos.");
589 sprintf(temp_buf, "IMAP");
590 potype = strdup(temp_buf);
591 if (GetValueFromUser("P.O. Box Type for this user? ", &potype) == SUB_ERROR)
597 if (strcmp(potype, "POP") && strcmp(potype, "IMAP"))
599 sprintf(temp_buf, "Unknown P.O. Box type.");
600 Put_message(temp_buf);
607 switch ((status = do_mr_query("register_user", CountArgs(args),
611 sprintf(temp_buf, "User %s successfully registered.", login);
612 Put_message(temp_buf);
613 SetUserPassword(login);
616 GiveBackLogin(login);
617 sprintf(temp_buf, "The username %s is already in use.", login);
618 Put_message(temp_buf);
621 com_err(program_name, status, " in register_user");
628 /* Function Name: RealUpdateUser
629 * Description: actuall updates the user information.
630 * Arguments: info - all current information for the user fields.
631 * junk - an UNUSED boolean.
635 static void RealUpdateUser(char **info, Bool junk)
638 char error_buf[BUFSIZ];
639 char **args = AskUserInfo(info, TRUE);
643 Put_message("Aborted.");
646 if ((status = do_mr_query("update_user_account", CountArgs(args),
649 com_err(program_name, status, " in ModifyFields");
650 sprintf(error_buf, "User %s not updated due to errors.", info[NAME]);
651 Put_message(error_buf);
655 /* Function Name: UpdateUser
656 * Description: Modify some of the information about a user.
657 * Arguments: argc, argv - login name of the user in argv[1].
658 * Returns: DM_NORMAL.
661 int UpdateUser(int argc, char **argv)
665 elem = GetUserInfo(LOGIN, argv[1], NULL);
666 QueryLoop(elem, NullPrint, RealUpdateUser, "Update the user");
672 /* Function Name: RealDeactivateUser
673 * Description: sets the user's status to 3.
674 * Arguments: info - all current information for the user fields
675 * one_item - indicates the user hasn't been queried yet
679 static void RealDeactivateUser(char **info, Bool one_item)
682 char txt_buf[BUFSIZ];
683 char *qargs[2], **args;
684 struct mqelem *elem = NULL;
688 sprintf(txt_buf, "Deactivate user %s (y/n)", info[NAME]);
689 if (YesNoQuestion(txt_buf, FALSE) != TRUE)
693 qargs[0] = info[NAME];
695 if ((status = do_mr_query("update_user_status", 2, qargs, NULL, NULL)))
697 com_err(program_name, status, " in update_user_status");
698 sprintf(txt_buf, "User %s not deactivated due to errors.", info[NAME]);
699 Put_message(txt_buf);
701 else if (YesNoQuestion("Also deactivate matching list and filesystem (y/n)",
704 status = do_mr_query("get_list_info", 1, &(info[NAME]), StoreInfo,
706 if (status == MR_SUCCESS)
708 args = QueueTop(elem)->q_data;
709 free(args[L_ACTIVE]);
710 args[L_ACTIVE] = strdup("0");
711 FreeAndClear(&args[L_MODTIME], TRUE);
712 FreeAndClear(&args[L_MODBY], TRUE);
713 FreeAndClear(&args[L_MODWITH], TRUE);
714 SlipInNewName(args, strdup(args[L_NAME]));
715 if ((status = do_mr_query("update_list", CountArgs(args), args,
718 com_err(program_name, status, " updating list, "
719 "not deactivating list or filesystem");
728 else if (status != MR_NO_MATCH)
730 com_err(program_name, status, " getting list info, "
731 "not deactivating list or filesystem");
735 if ((status = do_mr_query("get_filesys_by_label", 1, &(info[NAME]),
738 com_err(program_name, status, " getting filsys info, "
739 "not deactivating filesystem");
742 args = QueueTop(elem)->q_data;
744 args[FS_TYPE] = strdup("ERR");
745 free(args[FS_COMMENTS]);
746 args[FS_COMMENTS] = strdup("Locker disabled; call 3-1325 for help");
747 FreeAndClear(&args[FS_MODTIME], TRUE);
748 FreeAndClear(&args[FS_MODBY], TRUE);
749 FreeAndClear(&args[FS_MODWITH], TRUE);
750 SlipInNewName(args, strdup(args[FS_NAME]));
751 if ((status = do_mr_query("update_filesys", CountArgs(args), args,
754 com_err(program_name, status, " updating filesystem, "
755 "not deactivating filesystem");
766 /* Function Name: DeactivateUser
767 * Description: sets the user's status to 3.
768 * Arguments: argc, argv - login name of the user in argv[1].
769 * Returns: DM_NORMAL.
772 int DeactivateUser(int argc, char **argv)
776 elem = GetUserInfo(LOGIN, argv[1], NULL);
777 QueryLoop(elem, NullPrint, RealDeactivateUser, "Deactivate user");
784 /* ------------------------- Top Menu ------------------------- */
786 /* DeleteUser() in delete.c */
788 /* Function Name: DeleteUserByUid
789 * Description: Deletes the user given a uid number.
790 * Arguments: argc, argv - uid if user in argv[1].
791 * Returns: DM_NORMAL.
792 * NOTES: This just gets the username from the mr server
793 * and performs a DeleteUser().
796 int DeleteUserByUid(int argc, char **argv)
799 struct mqelem *elem = NULL;
802 if (!ValidName(argv[1]))
805 if ((status = do_mr_query("get_user_account_by_uid", 1, argv + 1, StoreInfo,
807 com_err(program_name, status, " in get_user_account_by_uid");
810 argv[1] = info[U_NAME];
812 DeleteUser(argc, argv);
816 /* ------------------------- Show User Information ------------------------- */
818 /* Function Name: ShowUserByLogin
819 * Description: Shows user information given a login name.
820 * Arguments: argc, argv - login name in argv[1].
824 int ShowUserByLogin(int argc, char *argv[])
826 struct mqelem *top, *elem;
828 elem = top = GetUserInfo(LOGIN, argv[1], NULL);
829 Loop(elem, PrintUserInfo);
835 /* Function Name: RetrieveUserByName
836 * Description: Show information on a user give fist and/or last name.
837 * Arguments: argc, argv - argv[1] - first name.
838 * argv[2] - last name.
839 * Returns: DM_NORMAL.
842 int ShowUserByName(int argc, char *argv[])
847 top = GetUserInfo(BY_NAME, argv[1], argv[2]);
849 if (!top) /* if there was an error then return. */
852 if (!PromptWithDefault("Print full information, or just the names (f/n)?",
860 Loop(top, PrintUserInfo);
864 Loop(top, PrintUserName);
872 /* Function Name: ShowUserByClass
873 * Description: Shows real and login names of all users in class.
874 * Arguments: argc, argv - argv[1] contains the class.
878 int ShowUserByClass(int argc, char **argv)
882 if (YesNoQuestion("This will take a long time. Are you sure", 0) == FALSE)
884 top = GetUserInfo(CLASS, argv[1], NULL);
885 Loop(top, PrintUserName);
892 /* Function Name: ShowUserById
893 * Description: Shows user information given an ID number.
894 * Arguments: argc, argv - ID number in argv[1].
898 int ShowUserById(int argc, char *argv[])
900 struct mqelem *top, *elem;
902 elem = top = GetUserInfo(ID, argv[1], NULL);
903 Loop(elem, PrintUserInfo);
910 /* Function Name: GetKrbmap
911 * Description: Shows user <-> Kerberos mappings
912 * Arguments: argc, argv - argv[1] contains the user login name,
913 * argv[2] contains the principal
917 int GetKrbmap(int argc, char **argv)
920 struct mqelem *elem = NULL, *top;
923 if ((stat = do_mr_query("get_kerberos_user_map", 2, &argv[1],
926 com_err(program_name, stat, " in GetKrbMap.");
930 top = elem = QueueTop(elem);
934 char **info = elem->q_data;
935 sprintf(buf, "User: %-9s Principal: %s",
936 info[KMAP_USER], info[KMAP_PRINCIPAL]);
941 FreeQueue(QueueTop(top));
946 /* Function Name: AddKrbmap
947 * Description: Add a new user <-> Kerberos mapping
948 * Arguments: argc, argv - argv[1] contains the user login name,
949 * argv[2] contains the principal
953 int AddKrbmap(int argc, char **argv)
957 if (!strchr(argv[KMAP_PRINCIPAL + 1], '@'))
959 Put_message("Please specify a realm for the kerberos principal.");
962 if ((stat = do_mr_query("add_kerberos_user_map", 2, &argv[1],
965 com_err(program_name, stat, " in AddKrbMap.");
966 if (stat == MR_EXISTS)
967 Put_message("No user or principal may have more than one mapping.");
973 /* Function Name: DeleteKrbmap
974 * Description: Remove a user <-> Kerberos mapping
975 * Arguments: argc, argv - argv[1] contains the user login name,
976 * argv[2] contains the principal
980 int DeleteKrbmap(int argc, char **argv)
984 if ((stat = do_mr_query("delete_kerberos_user_map", 2, &argv[1],
986 com_err(program_name, stat, " in DeleteKrbMap.");