AmendHub

Download

jcs

/

subtext

/

sysop.c

 

(View History)

jcs   sysop: Add menu option to reboot the system through Shutdown Manager Latest amendment: 465 on 2023-04-07

1 /*
2 * Copyright (c) 2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <string.h>
18 #include <ShutDown.h>
19
20 #include "subtext.h"
21 #include "ansi.h"
22 #include "binkp.h"
23 #include "board.h"
24 #include "serial_local.h"
25 #include "session.h"
26 #include "sysop.h"
27
28 #define USERS_PER_PAGE 20
29
30 void sysop_edit_boards(struct session *s);
31 void sysop_edit_folders(struct session *s);
32 void sysop_edit_motd(struct session *s);
33 void sysop_edit_users(struct session *s);
34 size_t sysop_find_user_ids(size_t nall_user_ids,
35 unsigned long *all_user_ids, unsigned long **user_ids, size_t offset,
36 size_t limit);
37 void sysop_edit_user(struct session *s, unsigned long id);
38
39 void
40 sysop_menu(struct session *s)
41 {
42 static const struct session_menu_option opts[] = {
43 { 'm', "Mm", "Edit Message of the Day" },
44 { 'i', "Ii", "Trigger FTN Binkp poll" },
45 { 'h', "Hh", "Hang up modem" },
46 { 'b', "Bb", "Manage boards" },
47 { 'f', "Ff", "Manage file folders" },
48 { 'u', "Uu", "Manage users" },
49 { 's', "Ss", "Change BBS settings" },
50 { 'r', "Rr", "Reboot system" },
51 { 'q', "QqXx", "Return to main menu" },
52 { '?', "?", "List menu options" },
53 };
54 static const char prompt_help[] =
55 "M:MOTD I:Binkp H:Hang Up B:Boards F:Folders U:Users S:Settings";
56 char c;
57 bool show_help = true;
58 bool done = false;
59
60 if (!s->user || !s->user->is_sysop)
61 return;
62
63 session_logf(s, "Entered sysop menu");
64
65 while (!done && !s->ending) {
66 c = session_menu(s, "Sysop Menu", "Sysop", (char *)prompt_help,
67 opts, nitems(opts), show_help, NULL, NULL);
68 show_help = false;
69
70 switch (c) {
71 case 'b':
72 sysop_edit_boards(s);
73 break;
74 case 'f':
75 sysop_edit_folders(s);
76 break;
77 case 'h':
78 session_printf(s, "Hanging up modem...\r\n");
79 session_flush(s);
80 serial_hangup();
81 serial_init();
82 break;
83 case 'i':
84 session_printf(s, "Requesting binkp poll...\r\n");
85 session_flush(s);
86 binkp_next_poll = 0;
87 uthread_wakeup(periodic_job_thread);
88 break;
89 case 'm':
90 sysop_edit_motd(s);
91 break;
92 case 's':
93 sysop_edit_settings(s);
94 break;
95 case 'u':
96 sysop_edit_users(s);
97 break;
98 case 'r': {
99 IOParam pb;
100 long m;
101 short sc;
102
103 session_printf(s,
104 "Are you sure you want to reboot the system? [y/N] ");
105 session_flush(s);
106 sc = session_input_char(s);
107 if (sc != 'Y' && sc != 'y') {
108 session_output(s, "\r\n", 2);
109 session_flush(s);
110 break;
111 }
112 session_printf(s, "%c\r\n", sc == 'Y' ? 'Y' : 'y');
113 session_flush(s);
114
115 _exiting(0); /* call atexit list, from atexit.c */
116
117 /* flush the disk and give it a couple seconds */
118 memset(&pb, 0, sizeof(pb));
119 PBFlushVol(&pb, false);
120 Delay(120, &m);
121
122 ShutDwnStart();
123 /* we should never get here */
124 ExitToShell();
125 break;
126 }
127 case '?':
128 show_help = true;
129 break;
130 default:
131 done = true;
132 break;
133 }
134 }
135 }
136
137 void
138 sysop_edit_settings(struct session *s)
139 {
140 struct config *new_config, old_config;
141 short ret;
142
143 if (!s->user || !s->user->is_sysop)
144 return;
145
146 ret = struct_editor(s, config_fields, nconfig_fields, NULL, 0,
147 &db->config, sizeof(struct config), (void *)&new_config,
148 "BBS Settings", "Sysop:Settings");
149 if (ret != 0) {
150 session_printf(s, "No changes made\r\n");
151 return;
152 }
153
154 memcpy(&old_config, &db->config, sizeof(db->config));
155 memcpy(&db->config, new_config, sizeof(db->config));
156 db_config_save(db);
157 bile_flush(db->bile, true);
158 memcpy(&db->config, &old_config, sizeof(db->config));
159
160 session_logf(s, "Changed BBS settings");
161 session_printf(s, "Successfully saved changes to BBS Settings, "
162 "restart to take effect\r\n");
163 xfree(&new_config);
164 }
165
166 void
167 sysop_edit_boards(struct session *s)
168 {
169 static const struct session_menu_option opts[] = {
170 { '#', "", "Enter board to edit" },
171 { 'l', "Ll", "List boards" },
172 { 'n', "Nn", "Create new board" },
173 { 'q', "QqXx", "Return to sysop menu" },
174 { '?', "?", "List menu options" },
175 };
176 static const struct session_menu_option edit_opts[] = {
177 { 'd', "Dd", "Delete board" },
178 { 'i', "Ii", "Re-index posts" },
179 };
180 static const char prompt_help[] =
181 "#:Edit Board L:List N:New Q:Return ?:Help";
182 char prompt[30];
183 struct board *board, *new_board;
184 struct bile *new_board_bile;
185 size_t n, size;
186 short bn, ret, id, sc;
187 char c, *data = NULL, *name;
188 bool done, show_list, show_help;
189
190 show_list = true;
191 show_help = false;
192 done = false;
193
194 while (!done && !s->ending) {
195 if (show_list) {
196 session_printf(s, "{{B}}Boards{{/B}}\r\n");
197 session_printf(s, "%s# Name Description%s\r\n",
198 ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END));
199 session_flush(s);
200
201 for (n = 0; n < db->nboards; n++) {
202 session_printf(s, "%2ld %- 10.10s %s\r\n",
203 n + 1,
204 db->boards[n].name,
205 db->boards[n].description);
206 }
207 session_flush(s);
208
209 show_list = false;
210 }
211
212 c = session_menu(s, "Board Editor", "Sysop:Boards",
213 (char *)prompt_help, opts, nitems(opts), show_help, "Board #",
214 &bn);
215 show_help = false;
216
217 switch (c) {
218 case 'l':
219 show_list = true;
220 break;
221 case 'n':
222 board = xmalloczero(sizeof(struct board));
223 if (board == NULL)
224 continue;
225 board->id = bile_next_id(db->bile, DB_BOARD_RTYPE);
226 board->restricted_posting = false;
227 board->restricted_viewing = false;
228 ret = struct_editor(s, board_fields, nboard_fields, NULL, 0,
229 board, sizeof(struct board), (void *)&new_board,
230 "New Board", "Sysop:Boards:New");
231 if (ret != 0) {
232 xfree(&board);
233 continue;
234 }
235
236 new_board_bile = db_board_create(db, new_board, false);
237 if (new_board_bile == NULL) {
238 xfree(&board);
239 continue;
240 }
241 bile_close(new_board_bile);
242 bile_flush(db->bile, true);
243 db_cache_boards(db);
244
245 session_logf(s, "Created new board %ld: %s", new_board->id,
246 new_board->name);
247 xfree(&board);
248 xfree(&new_board);
249 break;
250 case '#':
251 if (bn < 1 || bn > db->nboards) {
252 session_printf(s, "Invalid board ID\r\n");
253 session_flush(s);
254 break;
255 }
256
257 board = &db->boards[bn - 1];
258 snprintf(prompt, sizeof(prompt), "Sysop:Boards:%ld",
259 board->id);
260 edit_board:
261 ret = struct_editor(s, board_fields, nboard_fields, edit_opts,
262 nitems(edit_opts), board, sizeof(struct board),
263 (void *)&new_board, "Edit Board", prompt);
264 switch (ret) {
265 case 'i':
266 board_index_sorted_post_ids(board, NULL);
267 goto edit_board;
268 case 'd':
269 session_printf(s,
270 "Really delete board %s? [y/N] ", board->name);
271 session_flush(s);
272
273 sc = session_input_char(s);
274 if (s->ending)
275 return;
276 if (sc != 'Y' && sc != 'y') {
277 session_output(s, "\r\n", 2);
278 session_flush(s);
279 break;
280 }
281 session_printf(s, "%c\r\n", sc == 'Y' ? 'Y' : 'y');
282 session_flush(s);
283
284 session_logf(s, "Deleting board %ld (%s)!",
285 board->id, board->name);
286
287 db_board_delete(db, board);
288 bile_flush(db->bile, true);
289 db_cache_boards(db);
290 break;
291 case 0:
292 ret = bile_marshall_object(db->bile, board_object_fields,
293 nboard_object_fields, new_board, &data, &size);
294 if (ret != 0 || size == 0)
295 panic("board: failed to marshall object");
296
297 if (bile_write(db->bile, DB_BOARD_RTYPE, new_board->id,
298 data, size) != size)
299 panic("save of board failed: %d", bile_error(db->bile));
300 xfree(&data);
301 bile_flush(db->bile, true);
302
303 session_logf(s, "Saved changes to board %ld: %s",
304 new_board->id, new_board->name);
305 xfree(&new_board);
306
307 db_cache_boards(db);
308 break;
309 }
310 break;
311 case '?':
312 show_help = true;
313 break;
314 default:
315 done = true;
316 break;
317 }
318 }
319 }
320
321 void
322 sysop_edit_folders(struct session *s)
323 {
324 static const struct session_menu_option opts[] = {
325 { '#', "", "Enter folder to edit" },
326 { 'l', "Ll", "List folders" },
327 { 'n', "Nn", "Create new folder" },
328 { 'q', "QqXx", "Return to sysop menu" },
329 { '?', "?", "List menu options" },
330 };
331 static const struct session_menu_option edit_opts[] = {
332 { 'd', "Dd", "Delete folder" },
333 };
334 static const char prompt_help[] =
335 "#:Edit Folder L:List N:New Q:Return ?:Help";
336 char prompt[30];
337 struct folder *folder, *new_folder;
338 struct bile *new_folder_bile;
339 size_t n, size;
340 short ret, fn, sc;
341 char c, *data = NULL;
342 bool done, show_list, show_help;
343
344 show_list = true;
345 show_help = false;
346 done = false;
347
348 while (!done && !s->ending) {
349 if (show_list) {
350 session_printf(s, "{{B}}Folders{{/B}}\r\n");
351 session_printf(s, "%s# Name Description%s\r\n",
352 ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END));
353 session_flush(s);
354
355 for (n = 0; n < db->nfolders; n++) {
356 session_printf(s, "%2ld %- 15.15s %s\r\n",
357 n + 1,
358 db->folders[n].name,
359 db->folders[n].description);
360 }
361 session_flush(s);
362
363 show_list = false;
364 }
365
366 c = session_menu(s, "Folder Editor", "Sysop:Folders",
367 (char *)prompt_help, opts, nitems(opts), show_help, "Folder #",
368 &fn);
369 show_help = false;
370
371 switch (c) {
372 case 'l':
373 show_list = true;
374 break;
375 case 'n':
376 folder = xmalloczero(sizeof(struct folder));
377 if (folder == NULL)
378 continue;
379 folder->id = bile_next_id(db->bile, DB_FOLDER_RTYPE);
380 ret = struct_editor(s, folder_fields, nfolder_fields, NULL, 0,
381 folder, sizeof(struct folder), (void *)&new_folder,
382 "New Folder", "Sysop:Folders:New");
383 if (ret != 0) {
384 xfree(&folder);
385 continue;
386 }
387
388 new_folder_bile = db_folder_create(db, new_folder, false);
389 if (new_folder_bile == NULL) {
390 xfree(&folder);
391 xfree(&new_folder);
392 continue;
393 }
394 bile_close(new_folder_bile);
395 bile_flush(db->bile, true);
396 db_cache_folders(db);
397
398 session_logf(s, "Created new folder %ld: %s", new_folder->id,
399 new_folder->name);
400 xfree(&folder);
401 xfree(&new_folder);
402 break;
403 case '#':
404 if (fn < 1 || fn > db->nfolders) {
405 session_printf(s, "Invalid folder ID\r\n");
406 session_flush(s);
407 break;
408 }
409
410 folder = &db->folders[fn - 1];
411 snprintf(prompt, sizeof(prompt), "Sysop:Folders:%ld",
412 folder->id);
413 edit_folder:
414 ret = struct_editor(s, folder_fields, nfolder_fields,
415 edit_opts, nitems(edit_opts), folder, sizeof(struct folder),
416 (void *)&new_folder, "Edit Folder", prompt);
417 switch (ret) {
418 case 'd':
419 session_printf(s,
420 "Really delete folder %s? [y/N] ", folder->name);
421 session_flush(s);
422
423 sc = session_input_char(s);
424 if (s->ending)
425 return;
426 if (sc != 'Y' && sc != 'y') {
427 session_output(s, "\r\n", 2);
428 session_flush(s);
429 break;
430 }
431 session_printf(s, "%c\r\n", sc == 'Y' ? 'Y' : 'y');
432 session_flush(s);
433
434 session_logf(s, "Deleting folder %ld (%s)!",
435 folder->id, folder->name);
436
437 db_folder_delete(db, folder);
438 bile_flush(db->bile, true);
439 db_cache_folders(db);
440 break;
441 case 0:
442 ret = bile_marshall_object(db->bile, folder_object_fields,
443 nfolder_object_fields, new_folder, &data, &size);
444 if (ret != 0 || size == 0)
445 panic("folder: failed to marshall object");
446
447 if (bile_write(db->bile, DB_FOLDER_RTYPE, new_folder->id,
448 data, size) != size)
449 panic("save of folder failed: %d", bile_error(db->bile));
450 bile_flush(db->bile, true);
451 xfree(&data);
452
453 session_logf(s, "Saved changes to folder %ld: %s",
454 new_folder->id, new_folder->name);
455 xfree(&new_folder);
456
457 db_cache_folders(db);
458 break;
459 }
460 break;
461 case '?':
462 show_help = true;
463 break;
464 default:
465 done = true;
466 break;
467 }
468 }
469 }
470
471 void
472 sysop_edit_motd(struct session *s)
473 {
474 static const struct session_menu_option opts[] = {
475 { 'n', "Nn", "Create new MOTD" },
476 { 's', "Ss", "Show latest MOTD" },
477 { 'q', "QqXx", "Return to sysop menu" },
478 { '?', "?", "List menu options" },
479 };
480 static const char prompt_help[] =
481 "N:New S:Show Q:Return ?:Help";
482 char c;
483 bool show_help = true;
484 bool done = false;
485
486 while (!done && !s->ending) {
487 c = session_menu(s, "MOTD Editor", "Sysop:MOTD", (char *)prompt_help,
488 opts, nitems(opts), show_help, NULL, NULL);
489 show_help = false;
490
491 switch (c) {
492 case 's': {
493 session_print_motd(s, true);
494 break;
495 }
496 case 'n': {
497 char *motd = NULL, *tmp = NULL;
498 struct bile_object *old_motd;
499 unsigned long new_id;
500
501 motd_write:
502 session_printf(s, "MOTD Text:\r\n");
503 session_flush(s);
504
505 tmp = session_field_input(s, 2048, s->terminal_columns - 1,
506 motd, true, 0);
507 session_output(s, "\r\n", 2);
508 session_flush(s);
509
510 rtrim(tmp, "\r\n\t ");
511
512 if (motd)
513 xfree(&motd);
514 motd = tmp;
515
516 session_printf(s, "\r\n{{B}}(S){{/B}}ave MOTD, "
517 "{{B}}(E){{/B}}dit again, or {{B}}(C){{/B}}ancel? ");
518 session_flush(s);
519
520 c = session_input_char(s);
521 if (c == 0 && s->obuflen > 0) {
522 s->node_funcs->output(s);
523 uthread_yield();
524 xfree(&motd);
525 break;
526 }
527
528 session_printf(s, "%c\r\n", c);
529 session_flush(s);
530
531 switch (c) {
532 case 's':
533 case 'S':
534 case 'y':
535 case '\n':
536 case '\r':
537 /* save */
538 session_printf(s, "Saving MOTD...");
539 session_flush(s);
540
541 new_id = bile_next_id(db->bile, DB_MOTD_RTYPE);
542 old_motd = bile_get_nth_of_type(db->bile, 0,
543 DB_MOTD_RTYPE);
544 if (old_motd) {
545 bile_delete(db->bile, DB_MOTD_RTYPE, old_motd->id,
546 BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE);
547 xfree(&old_motd);
548 }
549 bile_write(db->bile, DB_MOTD_RTYPE, new_id, motd,
550 strlen(motd) + 1);
551 bile_flush(db->bile, true);
552
553 session_printf(s, " saved!\r\n");
554 session_flush(s);
555 break;
556 case 'e':
557 case 'E':
558 goto motd_write;
559 break;
560 case 'c':
561 case 'C':
562 case CONTROL_C:
563 xfree(&motd);
564 break;
565 }
566 break;
567 }
568 case '?':
569 show_help = true;
570 break;
571 default:
572 done = true;
573 break;
574 }
575 }
576 }
577
578 void
579 sysop_edit_users(struct session *s)
580 {
581 static const struct session_menu_option opts[] = {
582 { '#', "#", "Edit user id [#]" },
583 { 'u', "Uu", "Edit user by username" },
584 { '<', "<", "Previous page of users" },
585 { 'l', "Ll", "List users" },
586 { '>', ">", "Next page of users" },
587 { 'q', "QqXx", "Return to sysop menu" },
588 { '?', "?", "List menu options" },
589 };
590 static const char prompt_help[] =
591 "#:Edit User <:Prev >:Next L:List Q:Return ?:Help";
592 unsigned long *all_user_ids = NULL, *user_ids = NULL;
593 struct user user, *fuser;
594 size_t pages, page, n, size, nall_user_ids, nuser_ids;
595 short suser_id, upp;
596 char c, time[30], *username;
597 bool show_help = false;
598 bool show_list = true;
599 bool done = false;
600 bool find_user_ids = true;
601
602 nall_user_ids = bile_sorted_ids_by_type(db->bile, DB_USER_RTYPE,
603 &all_user_ids);
604 page = 0;
605
606 while (!done && !s->ending) {
607 if (find_user_ids) {
608 upp = USERS_PER_PAGE;
609 if (s->terminal_lines < upp + 3)
610 upp = BOUND(upp, 5, s->terminal_lines - 3);
611 nuser_ids = sysop_find_user_ids(nall_user_ids, all_user_ids,
612 &user_ids, page * upp, upp);
613 /* ceil(nall_user_ids / upp) */
614 pages = (nall_user_ids + upp - 1) / upp;
615
616 if (page >= pages)
617 page = pages - 1;
618
619 find_user_ids = false;
620 }
621
622 if (show_list) {
623 session_printf(s, "{{B}}Users (Page %ld of %ld){{/B}}\r\n",
624 page + 1, pages);
625 session_printf(s, "%s # Username Active Sysop Last Login%s\r\n",
626 ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END));
627 session_flush(s);
628
629 for (n = 0; n < nuser_ids; n++) {
630 size = bile_read(db->bile, DB_USER_RTYPE, user_ids[n],
631 (char *)&user, sizeof(struct user));
632 if (size == 0)
633 continue;
634 if (user.last_seen_at)
635 strftime(time, sizeof(time), "%Y-%m-%d %H:%M",
636 localtime(&user.last_seen_at));
637 else
638 snprintf(time, sizeof(time), "Never");
639 session_printf(s, "%3ld %-14s %c %c %s\r\n",
640 user.id,
641 user.username,
642 user.is_enabled ? 'Y' : '-',
643 user.is_sysop ? 'Y' : '-',
644 time);
645 }
646 session_flush(s);
647 show_list = false;
648 }
649
650 c = session_menu(s, "Edit Users", "Sysop:Users", (char *)prompt_help,
651 opts, nitems(opts), show_help, "User ID", &suser_id);
652 show_help = false;
653
654 handle_opt:
655 switch (c) {
656 case 'l':
657 show_list = true;
658 break;
659 case '>':
660 case '<':
661 if (c == '>' && page == pages - 1) {
662 session_printf(s, "You are at the last page of posts\r\n");
663 session_flush(s);
664 break;
665 }
666 if (c == '<' && page == 0) {
667 session_printf(s, "You are already at the first page\r\n");
668 session_flush(s);
669 break;
670 }
671 if (c == '>')
672 page++;
673 else
674 page--;
675 find_user_ids = true;
676 show_list = true;
677 break;
678 case '#':
679 sysop_edit_user(s, suser_id);
680 break;
681 case 'u':
682 session_printf(s, "Username: ");
683 session_flush(s);
684
685 username = session_field_input(s, 30, 30, NULL, false, 0);
686 session_output(s, "\r\n", 2);
687 session_flush(s);
688 if (username == NULL)
689 break;
690 if (username[0] == '\0') {
691 xfree(&username);
692 break;
693 }
694
695 fuser = user_find_by_username(username);
696 xfree(&username);
697 if (!fuser) {
698 session_printf(s, "Error: No such user.\r\n");
699 session_flush(s);
700 break;
701 }
702
703 sysop_edit_user(s, fuser->id);
704 xfree(&fuser);
705 break;
706 case '?':
707 show_help = true;
708 break;
709 default:
710 done = true;
711 break;
712 }
713 }
714
715 if (all_user_ids != NULL)
716 xfree(&all_user_ids);
717 }
718
719 size_t
720 sysop_find_user_ids(size_t nall_user_ids, unsigned long *all_user_ids,
721 unsigned long **user_ids, size_t offset, size_t limit)
722 {
723 size_t nuser_ids, n;
724
725 if (nall_user_ids < offset)
726 return 0;
727
728 nuser_ids = nall_user_ids - offset;
729 if (nuser_ids > limit)
730 nuser_ids = limit;
731
732 *user_ids = xcalloc(sizeof(unsigned long), nuser_ids);
733 if (*user_ids == NULL)
734 return 0;
735
736 for (n = 0; n < nuser_ids; n++) {
737 (*user_ids)[n] = all_user_ids[offset + n];
738 }
739
740 return nuser_ids;
741 }
742
743 void
744 sysop_edit_user(struct session *s, unsigned long id)
745 {
746 static const struct session_menu_option opts[] = {
747 { 'p', "Pp", "Change password" },
748 { 'e', "Ee", "Enable/disable account" },
749 { 'd', "Dd", "Delete account" },
750 { 's', "Ss", "Toggle sysop flag" },
751 { 'q', "QqXx", "Return to users menu" },
752 { '?', "?", "List menu options" },
753 };
754 static const char prompt_help[] =
755 "P:Password E:Enable/Disable D:Delete S:Sysop Q:Return ?:Help";
756 char title[50];
757 char prompt[25];
758 struct user user;
759 size_t size;
760 bool done = false;
761 bool show_help = true;
762 char c;
763 short cc;
764
765 size = bile_read(db->bile, DB_USER_RTYPE, id, (char *)&user,
766 sizeof(struct user));
767 if (!size) {
768 session_printf(s, "Error: Failed to find user %ld\r\n", id);
769 session_flush(s);
770 return;
771 }
772
773 snprintf(title, sizeof(title), "Edit User %s", user.username);
774 snprintf(prompt, sizeof(prompt), "Sysop:Users:%ld", user.id);
775
776 while (!done && !s->ending) {
777 c = session_menu(s, title, prompt, (char *)prompt_help, opts,
778 nitems(opts), show_help, NULL, NULL);
779 show_help = false;
780
781 handle_opt:
782 switch (c) {
783 case 'd':
784 session_printf(s, "Are you sure you want to permanently "
785 "delete {{B}}%s{{/B}}? [y/N] ", user.username);
786 session_flush(s);
787
788 cc = session_input_char(s);
789 if (cc == 'y' || c == 'Y') {
790 session_printf(s, "%c\r\n", cc);
791 session_flush(s);
792 user_delete(&user);
793 session_printf(s, "\r\n{{B}}User deleted!{{/B}}\r\n");
794 done = true;
795 } else {
796 session_printf(s, "\r\nUser not deleted.\r\n");
797 session_flush(s);
798 }
799 break;
800 case 'e':
801 user.is_enabled = !user.is_enabled;
802 user_save(&user);
803 session_printf(s, "User %s is now %sabled.\r\n", user.username,
804 user.is_enabled ? "en" : "dis");
805 session_flush(s);
806 break;
807 case 'p':
808 user_change_password(s, &user);
809 break;
810 case 's':
811 if (strcmp(s->user->username, user.username) == 0) {
812 session_printf(s, "You cannot remove your own sysop "
813 "flag.\r\n");
814 session_flush(s);
815 break;
816 }
817 user.is_sysop = !user.is_sysop;
818 user_save(&user);
819 session_printf(s, "User %s is now %sa sysop.\r\n",
820 user.username, user.is_sysop ? "" : "no longer ");
821 session_flush(s);
822 break;
823 case '?':
824 show_help = true;
825 break;
826 default:
827 done = true;
828 break;
829 }
830 }
831 }