1 // Client-side support functions.
3 // Copyright (c) 1987-2016 by the citadel.org team
5 // This program is open source software. Use, duplication, and/or
6 // disclosure is subject to the GNU General Purpose License version 3.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 #include "textclient.h"
15 #define IFAIDE if(axlevel>=AxAideU)
16 #define IFNAIDE if (axlevel<AxAideU)
18 extern unsigned userflags;
19 extern char sigcaught;
20 extern char rc_floor_mode;
21 extern int rc_ansi_color;
22 extern int rc_prompt_control;
24 /* Destructive backspace */
25 void back(int spaces) {
27 for (a = 0; a < spaces; ++a) {
35 * Edit a user's Internet email addresses
37 void edit_user_internet_email_addresses(CtdlIPC * ipc, char *who) {
49 r = CtdlIPCAideGetEmailAddresses(ipc, who, emailaddrs, buf);
51 while (!IsEmptyStr(emailaddrs)) {
52 extract_token(buf, emailaddrs, 0, '\n', sizeof buf);
53 remove_token(emailaddrs, 0, '\n');
54 if (!IsEmptyStr(buf)) {
57 recs = malloc(sizeof(char *));
59 recs = realloc(recs, (sizeof(char *)) * num_recs);
60 recs[num_recs - 1] = malloc(strlen(buf) + 1);
61 strcpy(recs[num_recs - 1], buf);
69 scr_printf(" Internet email addresses for %s\n", who);
71 scr_printf("--- --------------------------------------------------\n");
72 for (i = 0; i < num_recs; ++i) {
74 scr_printf("%3d ", i + 1);
76 scr_printf("%s\n", recs[i]);
80 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
83 newprompt("Enter new email address: ", buf, 50);
85 if (!IsEmptyStr(buf)) {
86 // FIXME validate the email address (format, our own domain, addr does not belong to another user)
89 recs = malloc(sizeof(char *));
92 recs = realloc(recs, (sizeof(char *)) * num_recs);
94 recs[num_recs - 1] = strdup(buf);
99 i = intprompt("Delete which address", 1, 1, num_recs) - 1;
102 for (j = i; j < num_recs; ++j) {
103 recs[j] = recs[j + 1];
109 for (i = 0; i < num_recs; i++)
110 r += 1 + strlen(recs[i]);
111 resp = (char *) calloc(1, r);
113 scr_printf("Can't save config - out of memory!\n");
117 for (i = 0; i < num_recs; i++) {
118 strcat(resp, recs[i]);
121 r = CtdlIPCAideSetEmailAddresses(ipc, who, resp, buf);
123 scr_printf("%s\n", buf);
126 scr_printf("Saved %d addresses.\n", num_recs);
133 quitting = !modified || boolprompt("Quit without saving", 0);
141 for (i = 0; i < num_recs; ++i)
149 * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
151 void edituser(CtdlIPC * ipc, int cmd) {
153 char who[USERNAME_SIZE];
154 char newname[USERNAME_SIZE];
155 struct ctdluser *user = NULL;
157 int r; /* IPC response code */
162 newprompt("User name: ", who, 29);
163 while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
164 scr_printf("%s\n", buf);
166 scr_printf("Do you want to create this user? ");
168 r = CtdlIPCCreateUser(ipc, who, 0, buf);
173 scr_printf("%s\n", buf);
180 if (cmd == 25) { // user edit
182 /* val_user(ipc, user->fullname, 0); we used to display the vCard here but there's really no need */
186 while (change_name == 1) {
187 if (boolprompt("Change name", 0)) {
188 strprompt("New name", newname, USERNAME_SIZE - 1);
189 r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
191 scr_printf("%s\n", buf);
194 strcpy(user->fullname, newname);
204 if (newnow || boolprompt("Change password", 0)) {
205 strprompt("Password", user->password, -19);
208 user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
209 if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET))) {
210 user->flags |= US_INTERNET;
213 user->flags &= ~US_INTERNET;
215 if (boolprompt("Ask user to register again", !(user->flags & US_REGIS))) {
216 user->flags &= ~US_REGIS;
219 user->flags |= US_REGIS;
221 user->lastcall = boolprompt("Set last login to now", 0) ? time(NULL) : user->lastcall;
222 user->USuserpurge = intprompt("Purge time (in days, 0 for system default", user->USuserpurge, 0, INT_MAX);
226 scr_printf("Do you want to delete this user? ");
231 user->axlevel = AxDeleted;
234 r = CtdlIPCAideSetUserParameters(ipc, user, buf);
236 scr_printf("%s\n", buf);
240 if (cmd == 25) { // user edit
241 if (boolprompt("Edit this user's Internet email addresses", 0)) {
242 edit_user_internet_email_addresses(ipc, who);
249 /* Display a prompt and flip a bit based on whether the user answers
250 * yes or no. Yes=1 and No=0, unless 'backwards' is set to a nonzero value
251 * in which case No=1 and Yes=0.
253 int set_attr(CtdlIPC * ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards) {
259 scr_printf("%50s ", prompt);
262 color(BRIGHT_MAGENTA);
265 scr_printf("%3s", ((temp & sbit) ? "No" : "Yes"));
268 scr_printf("%3s", ((temp & sbit) ? "Yes" : "No"));
283 temp = (temp | sbit);
285 temp = (temp ^ sbit);
290 * modes are: 0 - .EC command, 1 - .EC for new user,
291 * 2 - toggle Xpert mode 3 - toggle floor mode
293 void enter_config(CtdlIPC * ipc, int mode) {
295 struct ctdluser *user = NULL;
296 int r; /* IPC response code */
298 r = CtdlIPCGetConfig(ipc, &user, buf);
300 scr_printf("%s\n", buf);
305 if (mode == 0 || mode == 1) {
307 user->flags = set_attr(ipc, user->flags, "Are you an experienced Citadel user", US_EXPERT, 0);
308 if ((user->flags & US_EXPERT) == 0 && mode == 1) {
313 user->flags = set_attr(ipc, user->flags, "Print last old message on New message request", US_LASTOLD, 0);
315 user->flags = set_attr(ipc, user->flags, "Prompt after each message", US_NOPROMPT, 1);
317 if ((user->flags & US_NOPROMPT) == 0) {
318 user->flags = set_attr(ipc, user->flags, "Use 'disappearing' prompts", US_DISAPPEAR, 0);
321 user->flags = set_attr(ipc, user->flags, "Pause after each screenful of text", US_PAGINATOR, 0);
323 if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
324 user->flags = set_attr(ipc, user->flags, "<N>ext and <S>top work at paginator prompt", US_PROMPTCTL, 0);
327 if (rc_floor_mode == RC_DEFAULT) {
328 user->flags = set_attr(ipc, user->flags, "View rooms by floor", US_FLOORS, 0);
331 if (rc_ansi_color == 3) {
332 user->flags = set_attr(ipc, user->flags, "Enable color support", US_COLOR, 0);
335 if ((user->flags & US_EXPERT) == 0) {
336 formout(ipc, "unlisted");
339 user->flags = set_attr(ipc, user->flags, "Be unlisted in userlog", US_UNLISTED, 0);
341 if (!IsEmptyStr(editor_path)) {
342 user->flags = set_attr(ipc,
343 user->flags, "Always enter messages with the full-screen editor", US_EXTEDIT, 0);
349 if (user->flags & US_EXPERT) {
350 user->flags ^= US_EXPERT;
351 scr_printf("Expert mode now OFF\n");
354 user->flags |= US_EXPERT;
355 scr_printf("Expert mode now ON\n");
360 if (user->flags & US_FLOORS) {
361 user->flags ^= US_FLOORS;
362 scr_printf("Floor mode now OFF\n");
365 user->flags |= US_FLOORS;
366 scr_printf("Floor mode now ON\n");
370 r = CtdlIPCSetConfig(ipc, user, buf);
372 scr_printf("%s\n", buf);
373 userflags = user->flags;
378 * getstring() - get a line of text from a file
379 * ignores lines beginning with "#"
381 int getstring(FILE * fp, char *string) {
395 } while (string[0] == '#');
396 return (strlen(string));
400 /* Searches for patn in search string */
401 int pattern(char *search, char *patn) {
405 for (a = 0; !IsEmptyStr(&search[a]); ++a) {
406 b = strncasecmp(&search[a], patn, len);
414 void strproc(char *string) {
417 if (IsEmptyStr(string))
420 /* Convert non-printable characters to blanks */
421 for (a = 0; !IsEmptyStr(&string[a]); ++a) {
428 /* Remove leading and trailing blanks */
429 while (string[0] < 33)
430 strcpy(string, &string[1]);
431 while (string[strlen(string) - 1] < 33)
432 string[strlen(string) - 1] = 0;
434 /* Remove double blanks */
435 for (a = 0; a < strlen(string); ++a) {
436 if ((string[a] == 32) && (string[a + 1] == 32)) {
437 strcpy(&string[a], &string[a + 1]);
442 /* remove characters which would interfere with the network */
443 for (a = 0; a < strlen(string); ++a) {
444 if (string[a] == '!')
445 strcpy(&string[a], &string[a + 1]);
446 if (string[a] == '@')
447 strcpy(&string[a], &string[a + 1]);
448 if (string[a] == '_')
449 strcpy(&string[a], &string[a + 1]);
450 if (string[a] == ',')
451 strcpy(&string[a], &string[a + 1]);
452 if (string[a] == '%')
453 strcpy(&string[a], &string[a + 1]);
454 if (string[a] == '|')
455 strcpy(&string[a], &string[a + 1]);
461 void progress(CtdlIPC * ipc, unsigned long curr, unsigned long cmax) {
462 static char dots[] = "**************************************************";
463 char dots_printed[51];
468 scr_printf("\r%79s\r", "");
471 /* a will be range 0-50 rather than 0-100 */
472 a = (curr * 50) / cmax;
473 sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
474 strncpy(dots_printed, dots, a);
476 scr_printf(fmt, dots_printed, "", curr * 100 / cmax, curr, cmax);
483 * NOT the same locate_host() in locate_host.c. This one just does a
484 * 'who am i' to try to discover where the user is...
486 void locate_host(CtdlIPC * ipc, char *hbuf) {
487 FILE *who = (FILE *) popen("who am i", "r");
489 strcpy(hbuf, ipc->ServInfo.fqdn);
492 fgets(hbuf, SIZ, who);
493 if (hbuf[strlen(hbuf) - 1] == '\n') {
494 hbuf[strlen(hbuf) - 1] = 0;
497 stripallbut(hbuf, '(', ')');
501 * miscellaneous server commands (testing, etc.)
503 void misc_server_cmd(CtdlIPC * ipc, char *cmd) {
506 CtdlIPC_chat_send(ipc, cmd);
507 CtdlIPC_chat_recv(ipc, buf);
508 scr_printf("%s\n", buf);
510 set_keepalives(KA_HALF);
511 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
512 scr_printf("%s\n", buf);
514 set_keepalives(KA_YES);
519 newprompt("> ", buf, 255);
520 CtdlIPC_chat_send(ipc, buf);
521 } while (strcmp(buf, "000"));
528 * compute the checksum of a file
530 int file_checksum(char *filename) {
535 fp = fopen(filename, "r");
539 /* yes, this algorithm may allow cksum to overflow, but that's ok
540 * as long as it overflows consistently, which it will.
542 while (ch = getc(fp), ch >= 0) {
543 cksum = (cksum + ch);
551 * nuke a directory and its contents
553 int nukedir(char *dirname) {
558 dp = opendir(dirname);
563 while (d = readdir(dp), d != NULL) {
564 snprintf(filename, sizeof filename, "%s/%s", dirname, d->d_name);
569 return (rmdir(dirname));