* When saving a Task, if the status is COMPLETED then also set PERCENT-COMPLETE:100...
[citadel.git] / citadel / routines.c
blobc8e85a7f7d94ecce215e3c6101b3b8a961156c03
1 /*
2 * $Id$
4 * Client-side support functions.
6 */
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/ioctl.h>
17 #include <pwd.h>
18 #include <signal.h>
19 #include <dirent.h>
20 #include <errno.h>
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 # include <sys/time.h>
28 # else
29 # include <time.h>
30 # endif
31 #endif
33 #ifdef HAVE_LIMITS_H
34 #include <limits.h>
35 #endif
36 #ifdef HAVE_UTMP_H
37 #include <utmp.h>
38 #endif
39 #ifdef HAVE_UTMPX_H
40 #include <utmpx.h>
41 #endif
43 #include <libcitadel.h>
44 #include "citadel.h"
45 #include "citadel_ipc.h"
46 #include "screen.h"
48 #ifndef HAVE_GETUTLINE
49 struct utmp *getutline(struct utmp *ut);
50 #endif
52 #define ROUTINES_C
54 #include "citadel.h"
55 #include "routines.h"
56 #include "commands.h"
57 #include "citadel_decls.h"
58 #include "routines2.h"
60 #define IFAIDE if(axlevel>=6)
61 #define IFNAIDE if (axlevel<6)
63 extern unsigned userflags;
64 //extern char *axdefs[8];
65 extern char sigcaught;
66 extern char rc_floor_mode;
67 extern int rc_ansi_color;
68 extern int rc_prompt_control;
70 /* Destructive backspace */
71 void back(int spaces) {
72 int a;
73 for (a=0; a<spaces; ++a) {
74 scr_putc(8);
75 scr_putc(32);
76 scr_putc(8);
80 void hit_any_key(CtdlIPC *ipc) { /* hit any key to continue */
81 int a,b;
83 color(COLOR_PUSH);
84 color(DIM_RED);
85 scr_printf("%s\r", ipc->ServInfo.moreprompt);
86 color(COLOR_POP);
87 stty_ctdl(0);
88 b=inkey();
89 for (a=0; !IsEmptyStr(&ipc->ServInfo.moreprompt[a]); ++a)
90 scr_putc(' ');
91 scr_putc(13);
92 stty_ctdl(1);
93 if ( (rc_prompt_control == 1)
94 || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL)) ) {
95 if (b == 'q' || b == 'Q' || b == 's' || b == 'S')
96 b = STOP_KEY;
97 if (b == 'n' || b == 'N')
98 b = NEXT_KEY;
100 if (b==NEXT_KEY) sigcaught = SIGINT;
101 if (b==STOP_KEY) sigcaught = SIGQUIT;
105 * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
107 void edituser(CtdlIPC *ipc, int cmd)
109 char buf[SIZ];
110 char who[USERNAME_SIZE];
111 char newname[USERNAME_SIZE];
112 struct ctdluser *user = NULL;
113 int newnow = 0;
114 int r; /* IPC response code */
115 int change_name = 0;
117 strcpy(newname, "");
119 newprompt("User name: ", who, 29);
120 while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
121 scr_printf("%s\n", buf);
122 if (cmd == 25) {
123 scr_printf("Do you want to create this user? ");
124 if (yesno()) {
125 r = CtdlIPCCreateUser(ipc, who, 0, buf);
126 if (r / 100 == 2) {
127 newnow = 1;
128 continue;
130 scr_printf("%s\n", buf);
133 free(user);
134 return;
137 if (cmd == 25) {
138 val_user(ipc, user->fullname, 0); /* Display registration */
140 if (!newnow) {
141 change_name = 1;
142 while (change_name == 1) {
143 if (boolprompt("Change name", 0)) {
144 strprompt("New name", newname, USERNAME_SIZE-1);
145 r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
146 if (r / 100 != 2) {
147 scr_printf("%s\n", buf);
149 else {
150 strcpy(user->fullname, newname);
151 change_name = 0;
154 else {
155 change_name = 0;
160 if (newnow || boolprompt("Change password", 0)) {
161 strprompt("Password", user->password, -19);
164 user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
165 if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET)))
166 user->flags |= US_INTERNET;
167 else
168 user->flags &= ~US_INTERNET;
169 if (boolprompt("Ask user to register again", !(user->flags & US_REGIS)))
170 user->flags &= ~US_REGIS;
171 else
172 user->flags |= US_REGIS;
173 user->timescalled = intprompt("Times called",
174 user->timescalled, 0, INT_MAX);
175 user->posted = intprompt("Messages posted",
176 user->posted, 0, INT_MAX);
177 user->lastcall = boolprompt("Set last call to now", 0) ?
178 time(NULL) : user->lastcall;
179 user->USuserpurge = intprompt("Purge time (in days, 0 for system default",
180 user->USuserpurge, 0, INT_MAX);
183 if (cmd == 96) {
184 scr_printf("Do you want to delete this user? ");
185 if (!yesno()) {
186 free(user);
187 return;
189 user->axlevel = 0;
192 r = CtdlIPCAideSetUserParameters(ipc, user, buf);
193 if (r / 100 != 2) {
194 scr_printf("%s\n", buf);
196 free(user);
200 /* Display a prompt and flip a bit based on whether the user answers
201 * yes or no. Yes=1 and No=0, unless 'backwards' is set to a nonzero value
202 * in which case No=1 and Yes=0.
204 int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards)
206 int a;
207 int temp;
209 temp = sval;
210 color(DIM_WHITE);
211 scr_printf("%50s ", prompt);
212 color(DIM_MAGENTA);
213 scr_printf("[");
214 color(BRIGHT_MAGENTA);
216 if (backwards) {
217 scr_printf("%3s", ((temp&sbit) ? "No":"Yes"));
219 else {
220 scr_printf("%3s", ((temp&sbit) ? "Yes":"No"));
223 color(DIM_MAGENTA);
224 scr_printf("]? ");
225 color(BRIGHT_CYAN);
226 a = (temp & sbit);
227 if (a != 0) a = 1;
228 if (backwards) a = 1 - a;
229 a = yesno_d(a);
230 if (backwards) a = 1 - a;
231 color(DIM_WHITE);
232 temp = (temp|sbit);
233 if (!a) temp = (temp^sbit);
234 return(temp);
238 * modes are: 0 - .EC command, 1 - .EC for new user,
239 * 2 - toggle Xpert mode 3 - toggle floor mode
241 void enter_config(CtdlIPC *ipc, int mode)
243 char buf[SIZ];
244 struct ctdluser *user = NULL;
245 int r; /* IPC response code */
247 r = CtdlIPCGetConfig(ipc, &user, buf);
248 if (r / 100 != 2) {
249 scr_printf("%s\n", buf);
250 free(user);
251 return;
254 if (mode == 0 || mode == 1) {
256 /* Does anyone still use dialup connections with manual
257 * screen dimensions setting anymore? For now we'll keep
258 * the system's ability to set these, but remove the prompts
259 * because they're spurious for nearly everyone.
261 user->USscreenwidth = intprompt("Enter your screen width",
262 user->USscreenwidth, 20, 255);
263 user->USscreenheight = intprompt("Enter your screen height",
264 user->USscreenheight, 3, 255);
267 user->flags = set_attr(ipc, user->flags,
268 "Are you an experienced Citadel user",
269 US_EXPERT, 0);
270 if ((user->flags & US_EXPERT) == 0 && mode == 1) {
271 free(user);
272 return;
275 user->flags = set_attr(ipc, user->flags,
276 "Print last old message on New message request",
277 US_LASTOLD, 0);
279 user->flags = set_attr(ipc, user->flags,
280 "Prompt after each message",
281 US_NOPROMPT, 1);
283 if ((user->flags & US_NOPROMPT) == 0)
284 user->flags = set_attr(ipc, user->flags,
285 "Use 'disappearing' prompts",
286 US_DISAPPEAR, 0);
288 user->flags = set_attr(ipc, user->flags,
289 "Pause after each screenful of text",
290 US_PAGINATOR, 0);
292 if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR))
293 user->flags = set_attr(ipc, user->flags,
294 "<N>ext and <S>top work at paginator prompt",
295 US_PROMPTCTL, 0);
297 if (rc_floor_mode == RC_DEFAULT)
298 user->flags = set_attr(ipc, user->flags,
299 "View rooms by floor",
300 US_FLOORS, 0);
302 if (rc_ansi_color == 3)
303 user->flags = set_attr(ipc, user->flags,
304 "Enable color support",
305 US_COLOR, 0);
307 if ((user->flags & US_EXPERT) == 0)
308 formout(ipc, "unlisted");
310 user->flags = set_attr(ipc, user->flags,
311 "Be unlisted in userlog",
312 US_UNLISTED, 0);
314 if (!IsEmptyStr(editor_paths[0])) {
315 user->flags = set_attr(ipc, user->flags,
316 "Always enter messages with the full-screen editor",
317 US_EXTEDIT, 0);
322 if (mode == 2) {
323 if (user->flags & US_EXPERT) {
324 user->flags ^= US_EXPERT;
325 scr_printf("Expert mode now OFF\n");
326 } else {
327 user->flags |= US_EXPERT;
328 scr_printf("Expert mode now ON\n");
332 if (mode == 3) {
333 if (user->flags & US_FLOORS) {
334 user->flags ^= US_FLOORS;
335 scr_printf("Floor mode now OFF\n");
336 } else {
337 user->flags |= US_FLOORS;
338 scr_printf("Floor mode now ON\n");
342 r = CtdlIPCSetConfig(ipc, user, buf);
343 if (r / 100 != 2) scr_printf("%s\n", buf);
344 userflags = user->flags;
345 free(user);
349 * getstring() - get a line of text from a file
350 * ignores lines beginning with "#"
352 int getstring(FILE *fp, char *string)
354 int a,c;
355 do {
356 strcpy(string,"");
357 a=0;
358 do {
359 c=getc(fp);
360 if (c<0) {
361 string[a]=0;
362 return(-1);
364 string[a++]=c;
365 } while(c!=10);
366 string[a-1]=0;
367 } while(string[0]=='#');
368 return(strlen(string));
372 /* Searches for patn in search string */
373 int pattern(char *search, char *patn) {
374 int a,b,len;
376 len = strlen(patn);
377 for (a=0; !IsEmptyStr(&search[a]); ++a) {
378 b=strncasecmp(&search[a],patn,len);
379 if (b==0) return(b);
381 return(-1);
385 void strproc(char *string)
387 int a;
389 if (IsEmptyStr(string)) return;
391 /* Convert non-printable characters to blanks */
392 for (a=0; !IsEmptyStr(&string[a]); ++a) {
393 if (string[a]<32) string[a]=32;
394 if (string[a]>126) string[a]=32;
397 /* Remove leading and trailing blanks */
398 while(string[0]<33) strcpy(string,&string[1]);
399 while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
401 /* Remove double blanks */
402 for (a=0; a<strlen(string); ++a) {
403 if ((string[a]==32)&&(string[a+1]==32)) {
404 strcpy(&string[a],&string[a+1]);
405 a=0;
409 /* remove characters which would interfere with the network */
410 for (a=0; a<strlen(string); ++a) {
411 if (string[a]=='!') strcpy(&string[a],&string[a+1]);
412 if (string[a]=='@') strcpy(&string[a],&string[a+1]);
413 if (string[a]=='_') strcpy(&string[a],&string[a+1]);
414 if (string[a]==',') strcpy(&string[a],&string[a+1]);
415 if (string[a]=='%') strcpy(&string[a],&string[a+1]);
416 if (string[a]=='|') strcpy(&string[a],&string[a+1]);
422 #ifndef HAVE_STRERROR
424 * replacement strerror() for systems that don't have it
426 char *strerror(int e)
428 static char buf[128];
430 snprintf(buf, sizeof buf, "errno = %d",e);
431 return(buf);
433 #endif
436 void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
438 static char dots[] =
439 "**************************************************";
440 char dots_printed[51];
441 char fmt[42];
442 unsigned long a;
444 if (curr >= cmax) {
445 sln_printf("\r%79s\r","");
446 status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
447 room_name, secure, 0);
448 } else {
449 /* a will be range 0-50 rather than 0-100 */
450 a=(curr * 50) / cmax;
451 sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
452 strncpy(dots_printed, dots, a);
453 dots_printed[a] = 0;
454 sln_printf(fmt, dots_printed, "",
455 curr * 100 / cmax, curr, cmax);
456 sln_flush();
462 * NOT the same locate_host() in locate_host.c. This one just does a
463 * 'who am i' to try to discover where the user is...
465 void locate_host(CtdlIPC* ipc, char *hbuf)
467 #ifndef HAVE_UTMP_H
468 char buf[SIZ];
469 FILE *who;
470 int a,b;
472 who = (FILE *)popen("who am i","r");
473 if (who==NULL) {
474 strcpy(hbuf, ipc->ServInfo.fqdn);
475 return;
477 fgets(buf,sizeof buf,who);
478 pclose(who);
480 b = 0;
481 for (a=0; !IsEmptyStr(&buf[a]); ++a) {
482 if ((buf[a]=='(')||(buf[a]==')')) ++b;
484 if (b<2) {
485 strcpy(hbuf, ipc->ServInfo.fqdn);
486 return;
489 for (a=0; a<strlen(buf); ++a) {
490 if (buf[a]=='(') {
491 strcpy(buf,&buf[a+1]);
494 for (a=0; a<strlen(buf); ++a) {
495 if (buf[a]==')') buf[a] = 0;
498 if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
499 else strncpy(hbuf,buf,24);
500 #else
501 char *tty = ttyname(0);
502 #ifdef HAVE_GETUTXLINE
503 struct utmpx ut, *put;
504 #else
505 struct utmp ut, *put;
506 #endif
508 if (tty == NULL) {
509 fail:
510 safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
511 return;
514 if (strncmp(tty, "/dev/", 5))
515 goto fail;
517 safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
519 #ifdef HAVE_GETUTXLINE /* Solaris uses this */
520 if ((put = getutxline(&ut)) == NULL)
521 #else
522 if ((put = getutline(&ut)) == NULL)
523 #endif
524 goto fail;
526 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
527 if (put->ut_type == USER_PROCESS) {
528 #endif
529 #if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
530 if (*put->ut_host)
531 safestrncpy(hbuf, put->ut_host, 24);
532 else
533 #endif
534 safestrncpy(hbuf, put->ut_line, 24);
535 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
537 else goto fail;
538 #endif
539 #endif /* HAVE_UTMP_H */
543 * miscellaneous server commands (testing, etc.)
545 void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
546 char buf[SIZ];
548 CtdlIPC_chat_send(ipc, cmd);
549 CtdlIPC_chat_recv(ipc, buf);
550 scr_printf("%s\n",buf);
551 if (buf[0]=='1') {
552 set_keepalives(KA_HALF);
553 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
554 scr_printf("%s\n",buf);
556 set_keepalives(KA_YES);
557 return;
559 if (buf[0]=='4') {
560 do {
561 newprompt("> ",buf,255);
562 CtdlIPC_chat_send(ipc, buf);
563 } while(strcmp(buf,"000"));
564 return;
570 * compute the checksum of a file
572 int file_checksum(char *filename)
574 int cksum = 0;
575 int ch;
576 FILE *fp;
578 fp = fopen(filename,"r");
579 if (fp == NULL) return(0);
581 /* yes, this algorithm may allow cksum to overflow, but that's ok
582 * as long as it overflows consistently, which it will.
584 while (ch=getc(fp), ch>=0) {
585 cksum = (cksum + ch);
588 fclose(fp);
589 return(cksum);
593 * nuke a directory and its contents
595 int nukedir(char *dirname)
597 DIR *dp;
598 struct dirent *d;
599 char filename[SIZ];
601 dp = opendir(dirname);
602 if (dp == NULL) {
603 return(errno);
606 while (d = readdir(dp), d != NULL) {
607 snprintf(filename, sizeof filename, "%s/%s",
608 dirname, d->d_name);
609 unlink(filename);
612 closedir(dp);
613 return(rmdir(dirname));