5 * \defgroup AdminTasks Administrative screen to add/change/delete user accounts
6 * \ingroup CitadelConfig
12 #include "webserver.h"
16 * \brief show a list of available users to edit them
17 * \param message the header message???
18 * \param preselect which user should be selected in the browser
20 void select_user_to_edit(char *message, char *preselect)
25 output_headers(1, 0, 0, 0, 1, 0);
26 do_template("edituser_select", NULL);
31 output_headers(1, 1, 2, 0, 0, 0);
32 wprintf("<div id=\"banner\">\n");
33 wprintf("<img src=\"static/usermanag_48x.gif\">");
35 wprintf(_("Edit or delete users"));
39 wprintf("<div id=\"content\" class=\"service\">\n");
41 if (message != NULL) wprintf(message);
43 wprintf("<table border=0 cellspacing=10><tr valign=top><td>\n");
45 svput("BOXTITLE", WCS_STRING, _("Add users"));
46 do_template("beginbox", NULL);
48 wprintf(_("To create a new user account, enter the desired "
49 "user name in the box below and click 'Create'."));
50 wprintf("<br /><br />");
52 wprintf("<center><form method=\"POST\" action=\"create_user\">\n");
53 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
54 wprintf(_("New user: "));
55 wprintf("<input type=\"text\" name=\"username\"><br />\n"
56 "<input type=\"submit\" name=\"create_button\" value=\"%s\">"
57 "</form></center>\n", _("Create"));
59 do_template("endbox", NULL);
63 svput("BOXTITLE", WCS_STRING, _("Edit or Delete users"));
64 do_template("beginbox", NULL);
66 wprintf(_("To edit an existing user account, select the user "
67 "name from the list and click 'Edit'."));
68 wprintf("<br /><br />");
71 "<form method=\"POST\" action=\"display_edituser\">\n");
72 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
73 wprintf("<select name=\"username\" size=10 style=\"width:100%%\">\n");
75 serv_getln(buf, sizeof buf);
77 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
78 extract_token(username, buf, 0, '|', sizeof username);
80 if (preselect != NULL)
81 if (!strcasecmp(username, preselect))
88 wprintf("</select><br />\n");
90 wprintf("<input type=\"submit\" name=\"edit_config_button\" value=\"%s\">", _("Edit configuration"));
91 wprintf("<input type=\"submit\" name=\"edit_abe_button\" value=\"%s\">", _("Edit address book entry"));
92 wprintf("<input type=\"submit\" name=\"delete_button\" value=\"%s\" "
93 "onClick=\"return confirm('%s');\">", _("Delete user"), _("Delete this user?"));
94 wprintf("</form></center>\n");
95 do_template("endbox", NULL);
97 wprintf("</td></tr></table>\n");
104 typedef struct _UserListEntry {
115 void DeleteUserListEntry(void *vUserList)
117 UserListEntry *ul = (UserListEntry*) vUserList;
118 FreeStrBuf(&ul->UserName);
119 FreeStrBuf(&ul->LastLogon);
120 FreeStrBuf(&ul->Passvoid);
124 UserListEntry* NewUserListEntry(StrBuf *SerializedUserList)
128 if (StrLength(SerializedUserList) < 8)
131 ul = (UserListEntry*) malloc(sizeof(UserListEntry));
132 ul->UserName = NewStrBuf();
133 ul->LastLogon = NewStrBuf();
134 ul->Passvoid = NewStrBuf();
136 StrBufExtract_token(ul->UserName, SerializedUserList, 0, '|');
137 ul->AccessLevel = StrBufExtract_int(SerializedUserList, 1, '|');
138 ul->UID = StrBufExtract_int(SerializedUserList, 2, '|');
139 StrBufExtract_token(ul->LastLogon, SerializedUserList, 3, '|');
140 /// TODO: ul->LastLogon -> ulLastLogonT
141 ul->nLogons = StrBufExtract_int(SerializedUserList, 4, '|');
142 ul->nPosts = StrBufExtract_int(SerializedUserList, 5, '|');
143 StrBufExtract_token(ul->Passvoid, SerializedUserList, 6, '|');
151 int CompareUserListName(const void *vUser1, const void *vUser2)
153 UserListEntry *u1 = (UserListEntry*) vUser1;
154 UserListEntry *u2 = (UserListEntry*) vUser2;
156 return strcmp(ChrPtr(u1->UserName), ChrPtr(u2->UserName));
158 int CompareUserListNameRev(const void *vUser1, const void *vUser2)
160 UserListEntry *u1 = (UserListEntry*) vUser1;
161 UserListEntry *u2 = (UserListEntry*) vUser2;
162 return strcmp(ChrPtr(u2->UserName), ChrPtr(u1->UserName));
166 * Sort by AccessLevel
168 int CompareAccessLevel(const void *vUser1, const void *vUser2)
170 UserListEntry *u1 = (UserListEntry*) vUser1;
171 UserListEntry *u2 = (UserListEntry*) vUser2;
173 return (u1->AccessLevel > u2->AccessLevel);
175 int CompareAccessLevelRev(const void *vUser1, const void *vUser2)
177 UserListEntry *u1 = (UserListEntry*) vUser1;
178 UserListEntry *u2 = (UserListEntry*) vUser2;
180 return (u2->AccessLevel > u1->AccessLevel);
187 int CompareUID(const void *vUser1, const void *vUser2)
189 UserListEntry *u1 = (UserListEntry*) vUser1;
190 UserListEntry *u2 = (UserListEntry*) vUser2;
192 return (u1->UID > u2->UID);
194 int CompareUIDRev(const void *vUser1, const void *vUser2)
196 UserListEntry *u1 = (UserListEntry*) vUser1;
197 UserListEntry *u2 = (UserListEntry*) vUser2;
199 return (u2->UID > u1->UID);
203 * Sort By Date /// TODO!
205 int CompareLastLogon(const void *vUser1, const void *vUser2)
207 UserListEntry *u1 = (UserListEntry*) vUser1;
208 UserListEntry *u2 = (UserListEntry*) vUser2;
210 return (u1->LastLogonT > u2->LastLogonT);
212 int CompareLastLogonRev(const void *vUser1, const void *vUser2)
214 UserListEntry *u1 = (UserListEntry*) vUser1;
215 UserListEntry *u2 = (UserListEntry*) vUser2;
217 return (u2->LastLogonT > u1->LastLogonT);
221 * Sort By Number of Logons
223 int ComparenLogons(const void *vUser1, const void *vUser2)
225 UserListEntry *u1 = (UserListEntry*) vUser1;
226 UserListEntry *u2 = (UserListEntry*) vUser2;
228 return (u1->nLogons > u2->nLogons);
230 int ComparenLogonsRev(const void *vUser1, const void *vUser2)
232 UserListEntry *u1 = (UserListEntry*) vUser1;
233 UserListEntry *u2 = (UserListEntry*) vUser2;
235 return (u2->nLogons > u1->nLogons);
239 * Sort By Number of Posts
241 int ComparenPosts(const void *vUser1, const void *vUser2)
243 UserListEntry *u1 = (UserListEntry*) vUser1;
244 UserListEntry *u2 = (UserListEntry*) vUser2;
246 return (u1->nPosts > u2->nPosts);
248 int ComparenPostsRev(const void *vUser1, const void *vUser2)
250 UserListEntry *u1 = (UserListEntry*) vUser1;
251 UserListEntry *u2 = (UserListEntry*) vUser2;
253 return (u2->nPosts > u1->nPosts);
257 HashList *iterate_load_userlist(WCTemplateToken *Token)
269 serv_getln(buf, sizeof buf);
271 Hash = NewHash(1, NULL);
274 while ((len = StrBuf_ServGetln(Buf),
275 strcmp(ChrPtr(Buf), "000"))) {
276 ul = NewUserListEntry(Buf);
279 nUsed = GetCount(Hash);
280 nUsed = snprintf(nnn, sizeof(nnn), "%d", nUsed+1);
281 Put(Hash, nnn, nUsed, ul, DeleteUserListEntry);
284 Order = ibstr("SortOrder");
285 switch (ibstr("SortBy")){
287 SortByPayload(Hash, (Order)?
289 CompareUserListNameRev);
291 case 2: /*AccessLevel*/
292 SortByPayload(Hash, (Order)?
294 CompareAccessLevelRev);
297 SortByPayload(Hash, (Order)?
302 SortByPayload(Hash, (Order)?
306 case 5: /*LastLogon*/
307 SortByPayload(Hash, (Order)?
309 CompareLastLogonRev);
311 case 6: /* nLogons */
312 SortByPayload(Hash, (Order)?
317 SortByPayload(Hash, (Order)?
328 void tmplput_USERLIST_UserName(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
330 UserListEntry *ul = (UserListEntry*) Context;
332 StrBufAppendBuf(Target, ul->UserName, 0);
335 void tmplput_USERLIST_AccessLevelNo(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
337 UserListEntry *ul = (UserListEntry*) Context;
339 StrBufAppendPrintf(Target, "%d", ul->AccessLevel, 0);
342 void tmplput_USERLIST_AccessLevelStr(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
344 UserListEntry *ul = (UserListEntry*) Context;
346 StrBufAppendBufPlain(Target, _(axdefs[ul->AccessLevel]), -1, 0);
349 void tmplput_USERLIST_UID(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
351 UserListEntry *ul = (UserListEntry*) Context;
353 StrBufAppendPrintf(Target, "%d", ul->UID, 0);
356 void tmplput_USERLIST_LastLogon(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
358 UserListEntry *ul = (UserListEntry*) Context;
360 StrBufAppendBuf(Target, ul->LastLogon, 0);
363 void tmplput_USERLIST_nLogons(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
365 UserListEntry *ul = (UserListEntry*) Context;
367 StrBufAppendPrintf(Target, "%d", ul->nLogons, 0);
370 void tmplput_USERLIST_nPosts(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType)
372 UserListEntry *ul = (UserListEntry*) Context;
374 StrBufAppendPrintf(Target, "%d", ul->nPosts, 0);
377 int ConditionalUser(WCTemplateToken *Tokens, void *Context, int ContextType)
379 UserListEntry *ul = (UserListEntry*) Context;
380 if (havebstr("usernum")) {
381 return ibstr("usernum") == ul->UID;
383 else if (havebstr("username")) {
384 return strcmp(bstr("username"), ChrPtr(ul->UserName)) == 0;
391 * \brief Locate the message number of a user's vCard in the current room
392 * \param username the plaintext name of the user
393 * \param usernum the number of the user on the citadel server
394 * \return the message id of his vcard
396 long locate_user_vcard(char *username, long usernum) {
398 long vcard_msgnum = (-1L);
399 char content_type[SIZ];
401 int already_tried_creating_one = 0;
404 struct stuff_t *next;
408 struct stuff_t *stuff = NULL;
412 /** Search for the user's vCard */
413 serv_puts("MSGS ALL");
414 serv_getln(buf, sizeof buf);
415 if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
416 ptr = malloc(sizeof(struct stuff_t));
417 ptr->msgnum = atol(buf);
422 /** Iterate through the message list looking for vCards */
423 while (stuff != NULL) {
424 serv_printf("MSG0 %ld|2", stuff->msgnum);
425 serv_getln(buf, sizeof buf);
427 while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
428 if (!strncasecmp(buf, "part=", 5)) {
429 extract_token(partnum, &buf[5], 2, '|', sizeof partnum);
430 extract_token(content_type, &buf[5], 4, '|', sizeof content_type);
431 if ( (!strcasecmp(content_type, "text/x-vcard"))
432 || (!strcasecmp(content_type, "text/vcard")) ) {
433 vcard_msgnum = stuff->msgnum;
444 /** If there's no vcard, create one */
445 if (vcard_msgnum < 0) if (already_tried_creating_one == 0) {
446 already_tried_creating_one = 1;
447 serv_puts("ENT0 1|||4");
448 serv_getln(buf, sizeof buf);
450 serv_puts("Content-type: text/x-vcard");
452 serv_puts("begin:vcard");
453 serv_puts("end:vcard");
459 return(vcard_msgnum);
464 * \brief Display the form for editing a user's address book entry
465 * \param username the name of the user
466 * \param usernum the citadel-uid of the user
468 void display_edit_address_book_entry(char *username, long usernum) {
471 char error_message[SIZ];
472 long vcard_msgnum = (-1L);
474 /** Locate the user's config room, creating it if necessary */
475 sprintf(roomname, "%010ld.%s", usernum, USERCONFIGROOM);
476 serv_printf("GOTO %s||1", roomname);
477 serv_getln(buf, sizeof buf);
479 serv_printf("CRE8 1|%s|5|||1|", roomname);
480 serv_getln(buf, sizeof buf);
481 serv_printf("GOTO %s||1", roomname);
482 serv_getln(buf, sizeof buf);
484 sprintf(error_message,
485 "<img src=\"static/error.gif\" align=center>"
486 "%s<br /><br />\n", &buf[4]);
487 select_user_to_edit(error_message, username);
492 vcard_msgnum = locate_user_vcard(username, usernum);
494 if (vcard_msgnum < 0) {
495 sprintf(error_message,
496 "<img src=\"static/error.gif\" align=center>%s<br /><br />\n",
497 _("An error occurred while trying to create or edit this address book entry.")
499 select_user_to_edit(error_message, username);
503 do_edit_vcard(vcard_msgnum, "1", "select_user_to_edit", roomname);
510 * \brief Edit a user.
511 * If supplied_username is null, look in the "username"
512 * web variable for the name of the user to edit.
514 * If "is_new" is set to nonzero, this screen will set the web variables
515 * to send the user to the vCard editor next.
516 * \param supplied_username user to look up or NULL if to search in the environment
517 * \param is_new should we create the user?
519 void display_edituser(char *supplied_username, int is_new) {
521 char error_message[1024];
535 if (supplied_username != NULL) {
536 safestrncpy(username, supplied_username, sizeof username);
539 safestrncpy(username, bstr("username"), sizeof username);
542 serv_printf("AGUP %s", username);
543 serv_getln(buf, sizeof buf);
545 sprintf(error_message,
546 "<img src=\"static/error.gif\" align=center>"
547 "%s<br /><br />\n", &buf[4]);
548 select_user_to_edit(error_message, username);
552 extract_token(username, &buf[4], 0, '|', sizeof username);
553 extract_token(password, &buf[4], 1, '|', sizeof password);
554 flags = extract_int(&buf[4], 2);
555 timescalled = extract_int(&buf[4], 3);
556 msgsposted = extract_int(&buf[4], 4);
557 axlevel = extract_int(&buf[4], 5);
558 usernum = extract_long(&buf[4], 6);
559 lastcall = extract_long(&buf[4], 7);
560 purgedays = extract_long(&buf[4], 8);
562 if (havebstr("edit_abe_button")) {
563 display_edit_address_book_entry(username, usernum);
567 if (havebstr("delete_button")) {
568 delete_user(username);
572 output_headers(1, 1, 2, 0, 0, 0);
573 wprintf("<div id=\"banner\">\n");
575 wprintf(_("Edit user account: "));
580 wprintf("<div id=\"content\" class=\"service\">\n");
582 wprintf("<div class=\"fix_scrollbar_bug\">"
583 "<table class=\"useredit_background\"><tr><td>\n");
584 wprintf("<form method=\"POST\" action=\"edituser\">\n"
585 "<input type=\"hidden\" name=\"username\" value=\"");
588 wprintf("<input type=\"hidden\" name=\"is_new\" value=\"%d\">\n"
589 "<input type=\"hidden\" name=\"usernum\" value=\"%ld\">\n",
591 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
593 wprintf("<input type=\"hidden\" name=\"flags\" value=\"%d\">\n", flags);
595 wprintf("<center><table>");
598 wprintf(_("User name:"));
600 "<input type=\"text\" name=\"newname\" value=\"");
602 wprintf("\" maxlength=\"63\"></td></tr>\n");
605 wprintf(_("Password"));
607 "<input type=\"password\" name=\"password\" value=\"");
609 wprintf("\" maxlength=\"20\"></td></tr>\n");
612 wprintf(_("Permission to send Internet mail"));
613 wprintf("</td><td>");
614 wprintf("<input type=\"checkbox\" name=\"inetmail\" value=\"yes\" ");
615 if (flags & US_INTERNET) {
618 wprintf("></td></tr>\n");
621 wprintf(_("Number of logins"));
623 "<input type=\"text\" name=\"timescalled\" value=\"");
624 wprintf("%d", timescalled);
625 wprintf("\" maxlength=\"6\"></td></tr>\n");
628 wprintf(_("Messages submitted"));
630 "<input type=\"text\" name=\"msgsposted\" value=\"");
631 wprintf("%d", msgsposted);
632 wprintf("\" maxlength=\"6\"></td></tr>\n");
635 wprintf(_("Access level"));
637 "<select name=\"axlevel\">\n");
638 for (i=0; i<7; ++i) {
641 wprintf("selected ");
643 wprintf("value=\"%d\">%d - %s</option>\n",
646 wprintf("</select></td></tr>\n");
649 wprintf(_("User ID number"));
651 "<input type=\"text\" name=\"usernum\" value=\"");
652 wprintf("%ld", usernum);
653 wprintf("\" maxlength=\"7\"></td></tr>\n");
657 wprintf(_("Date and time of last login"));
659 "<select name=\"lastcall\">\n");
661 wprintf("<option selected value=\"%ld\">", lastcall);
662 escputs(asctime(localtime(&lastcall)));
663 wprintf("</option>\n");
665 wprintf("<option value=\"%ld\">", now);
666 escputs(asctime(localtime(&now)));
667 wprintf("</option>\n");
669 wprintf("</select></td></tr>");
672 wprintf(_("Auto-purge after this many days"));
674 "<input type=\"text\" name=\"purgedays\" value=\"");
675 wprintf("%d", purgedays);
676 wprintf("\" maxlength=\"5\"></td></tr>\n");
678 wprintf("</table>\n");
680 wprintf("<input type=\"submit\" name=\"ok_button\" value=\"%s\">\n"
682 "<input type=\"submit\" name=\"cancel\" value=\"%s\">\n"
683 "<br /><br /></form>\n", _("Save changes"), _("Cancel"));
685 wprintf("</center>\n");
686 wprintf("</td></tr></table></div>\n");
693 * \brief do the backend operation of the user edit on the server
695 void edituser(void) {
699 unsigned int flags = 0;
702 is_new = ibstr("is_new");
703 safestrncpy(message, "", sizeof message);
704 username = bstr("username");
706 if (!havebstr("ok_button")) {
707 safestrncpy(message, _("Changes were not saved."), sizeof message);
711 flags = ibstr("flags");
712 if (yesbstr("inetmail")) {
713 flags |= US_INTERNET;
716 flags &= ~US_INTERNET ;
719 if ((havebstr("newname")) && (strcasecmp(bstr("username"), bstr("newname")))) {
720 serv_printf("RENU %s|%s", bstr("username"), bstr("newname"));
721 serv_getln(buf, sizeof buf);
723 sprintf(&message[strlen(message)],
724 "<img src=\"static/error.gif\" align=center>"
725 "%s<br /><br />\n", &buf[4]);
728 username = bstr("newname");
732 serv_printf("ASUP %s|%s|%d|%s|%s|%s|%s|%s|%s|",
743 serv_getln(buf, sizeof buf);
745 sprintf(&message[strlen(message)],
746 "<img src=\"static/error.gif\" align=center>"
747 "%s<br /><br />\n", &buf[4]);
752 * If we are in the middle of creating a new user, move on to
753 * the vCard edit screen.
756 display_edit_address_book_entry(username, lbstr("usernum") );
759 select_user_to_edit(message, username);
764 * \brief burge a user
765 * \param username the name of the user to remove
767 void delete_user(char *username) {
771 serv_printf("ASUP %s|0|0|0|0|0|", username);
772 serv_getln(buf, sizeof buf);
775 "<img src=\"static/error.gif\" align=center>"
776 "%s<br /><br />\n", &buf[4]);
779 safestrncpy(message, "", sizeof message);
781 select_user_to_edit(message, bstr("username"));
787 * \brief create a new user
788 * take the web environment username and create it on the citadel server
790 void create_user(void) {
792 char error_message[SIZ];
795 safestrncpy(username, bstr("username"), sizeof username);
797 serv_printf("CREU %s", username);
798 serv_getln(buf, sizeof buf);
801 sprintf(WC->ImportantMessage, _("A new user has been created."));
802 display_edituser(username, 1);
804 else if (!strncmp(buf, "570", 3)) {
805 sprintf(error_message,
806 "<img src=\"static/error.gif\" align=center>"
808 _("You are attempting to create a new user from within Citadel "
809 "while running in host based authentication mode. In this mode, "
810 "you must create new users on the host system, not within Citadel.")
812 select_user_to_edit(error_message, NULL);
815 sprintf(error_message,
816 "<img src=\"static/error.gif\" align=center>"
817 "%s<br /><br />\n", &buf[4]);
818 select_user_to_edit(error_message, NULL);
824 void _select_user_to_edit(void){select_user_to_edit(NULL, NULL);}
825 void _display_edituser(void) {display_edituser(NULL, 0);}
831 WebcitAddUrlHandler(HKEY("select_user_to_edit"), _select_user_to_edit, 0);
832 WebcitAddUrlHandler(HKEY("display_edituser"), _display_edituser, 0);
833 WebcitAddUrlHandler(HKEY("edituser"), edituser, 0);
834 WebcitAddUrlHandler(HKEY("create_user"), create_user, 0);
836 RegisterNamespace("USERLIST:USERNAME", 0, 1, tmplput_USERLIST_UserName, CTX_USERLIST);
837 RegisterNamespace("USERLIST:ACCLVLNO", 0, 0, tmplput_USERLIST_AccessLevelNo, CTX_USERLIST);
838 RegisterNamespace("USERLIST:ACCLVLSTR", 0, 0, tmplput_USERLIST_AccessLevelStr, CTX_USERLIST);
839 RegisterNamespace("USERLIST:UID", 0, 0, tmplput_USERLIST_UID, CTX_USERLIST);
840 RegisterNamespace("USERLIST:LASTLOGON", 0, 0, tmplput_USERLIST_LastLogon, CTX_USERLIST);
841 RegisterNamespace("USERLIST:NLOGONS", 0, 0, tmplput_USERLIST_nLogons, CTX_USERLIST);
842 RegisterNamespace("USERLIST:NPOSTS", 0, 0, tmplput_USERLIST_nPosts, CTX_USERLIST);
844 RegisterConditional(HKEY("COND:USERNAME"), 0, ConditionalUser, CTX_USERLIST);
845 RegisterIterator("USERLIST", 0, NULL, iterate_load_userlist, NULL, DeleteHash, CTX_USERLIST);