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 | } |