Fixed up a compiler warning
[citadel.git] / citadel / citserver.c
1 /* 
2  * $Id$
3  *
4  * Main source module for the Citadel server
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #if HAVE_BACKTRACE
30 #include <execinfo.h>
31 #endif
32
33 #include <ctype.h>
34 #include <string.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <netdb.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <libcitadel.h>
44 #include "citadel.h"
45 #include "server.h"
46 #include "sysdep_decls.h"
47 #include "threads.h"
48 #include "citserver.h"
49 #include "config.h"
50 #include "database.h"
51 #include "housekeeping.h"
52 #include "user_ops.h"
53 #include "msgbase.h"
54 #include "support.h"
55 #include "locate_host.h"
56 #include "room_ops.h"
57 #include "file_ops.h"
58 #include "policy.h"
59 #include "control.h"
60 #include "euidindex.h"
61
62 #ifndef HAVE_SNPRINTF
63 #include "snprintf.h"
64 #endif
65
66 #include "ctdl_module.h"
67
68
69 struct CitContext *ContextList = NULL;
70 struct CitContext* next_session = NULL;
71 char *unique_session_numbers;
72 int ScheduledShutdown = 0;
73 int do_defrag = 0;
74 time_t server_startup_time;
75 int panic_fd;
76
77 /**
78  * \brief print the actual stack frame.
79  */
80 void cit_backtrace(void)
81 {
82 #ifdef HAVE_BACKTRACE
83         void *stack_frames[50];
84         size_t size, i;
85         char **strings;
86
87
88         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
89         strings = backtrace_symbols(stack_frames, size);
90         for (i = 0; i < size; i++) {
91                 if (strings != NULL)
92                         lprintf(1, "%s\n", strings[i]);
93                 else
94                         lprintf(1, "%p\n", stack_frames[i]);
95         }
96         free(strings);
97 #endif
98 }
99
100 /**
101  * \brief print the actual stack frame.
102  */
103 void cit_panic_backtrace(int SigNum)
104 {
105 #ifdef HAVE_BACKTRACE
106         void *stack_frames[10];
107         size_t size, i;
108         char **strings;
109
110         printf("caught signal 11\n");
111         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
112         strings = backtrace_symbols(stack_frames, size);
113         for (i = 0; i < size; i++) {
114                 if (strings != NULL)
115                         lprintf(1, "%s\n", strings[i]);
116                 else
117                         lprintf(1, "%p\n", stack_frames[i]);
118         }
119         free(strings);
120 #endif
121         exit(-1);
122 }
123
124 /*
125  * Various things that need to be initialized at startup
126  */
127 void master_startup(void) {
128         struct timeval tv;
129         unsigned int seed;
130         FILE *urandom;
131         struct ctdlroom qrbuf;
132         
133         lprintf(CTDL_DEBUG, "master_startup() started\n");
134         time(&server_startup_time);
135
136         lprintf(CTDL_INFO, "Opening databases\n");
137         open_databases();
138
139         ctdl_thread_internal_init_tsd();
140         
141         CtdlThreadAllocTSD();
142         
143         if (do_defrag) {
144                 defrag_databases();
145         }
146
147         check_ref_counts();
148
149         lprintf(CTDL_INFO, "Creating base rooms (if necessary)\n");
150         create_room(config.c_baseroom,  0, "", 0, 1, 0, VIEW_BBS);
151         create_room(AIDEROOM,           3, "", 0, 1, 0, VIEW_BBS);
152         create_room(SYSCONFIGROOM,      3, "", 0, 1, 0, VIEW_BBS);
153         create_room(config.c_twitroom,  0, "", 0, 1, 0, VIEW_BBS);
154
155         /* The "Local System Configuration" room doesn't need to be visible */
156         if (lgetroom(&qrbuf, SYSCONFIGROOM) == 0) {
157                 qrbuf.QRflags2 |= QR2_SYSTEM;
158                 lputroom(&qrbuf);
159         }
160
161         lprintf(CTDL_INFO, "Seeding the pseudo-random number generator...\n");
162         urandom = fopen("/dev/urandom", "r");
163         if (urandom != NULL) {
164                 fread(&seed, sizeof seed, 1, urandom);
165                 fclose(urandom);
166         }
167         else {
168                 gettimeofday(&tv, NULL);
169                 seed = tv.tv_usec;
170         }
171         srandom(seed);
172
173         lprintf(CTDL_INFO, "Initializing ipgm secret\n");
174         get_config();
175         config.c_ipgm_secret = rand();
176         put_config();
177
178         lprintf(CTDL_DEBUG, "master_startup() finished\n");
179 }
180
181
182 /*
183  * Cleanup routine to be called when the server is shutting down.
184  */
185 void master_cleanup(int exitcode) {
186         struct CleanupFunctionHook *fcn;
187         static int already_cleaning_up = 0;
188
189         if (already_cleaning_up) while(1) sleep(1);
190         already_cleaning_up = 1;
191
192         /* Run any cleanup routines registered by loadable modules */
193         for (fcn = CleanupHookTable; fcn != NULL; fcn = fcn->next) {
194                 (*fcn->h_function_pointer)();
195         }
196
197         /* Close the AdjRefCount queue file */
198         AdjRefCount(-1, 0);
199
200         /* Do system-dependent stuff */
201         sysdep_master_cleanup();
202         
203         /* Close databases */
204         lprintf(CTDL_INFO, "Closing databases\n");
205         close_databases();
206
207 #ifdef DEBUG_MEMORY_LEAKS
208         dump_heap();
209 #endif
210
211         /* If the operator requested a halt but not an exit, halt here. */
212         if (shutdown_and_halt) {
213                 lprintf(CTDL_NOTICE, "citserver: Halting server without exiting.\n");
214                 fflush(stdout); fflush(stderr);
215                 while(1) {
216                         sleep(32767);
217                 }
218         }
219         
220         release_control();
221
222         /* Now go away. */
223         lprintf(CTDL_NOTICE, "citserver: Exiting with status %d\n", exitcode);
224         fflush(stdout); fflush(stderr);
225         
226         if (restart_server != 0)
227                 exit(1);
228         exit(exitcode);
229 }
230
231
232
233 /*
234  * Terminate a session.
235  */
236 void RemoveContext (struct CitContext *con)
237 {
238         if (con==NULL) {
239                 lprintf(CTDL_ERR,
240                         "WARNING: RemoveContext() called with NULL!\n");
241                 return;
242         }
243         lprintf(CTDL_DEBUG, "RemoveContext() session %d\n", con->cs_pid);
244
245         /* Run any cleanup routines registered by loadable modules.
246          * Note: We have to "become_session()" because the cleanup functions
247          *       might make references to "CC" assuming it's the right one.
248          */
249         become_session(con);
250         PerformSessionHooks(EVT_STOP);
251         become_session(NULL);
252
253         /* Now handle all of the administrivia. */
254         lprintf(CTDL_DEBUG, "Calling logout(%d)\n", con->cs_pid);
255         logout(con);
256
257         lprintf(CTDL_NOTICE, "[%3d] Session ended.\n", con->cs_pid);
258
259         /* If the client is still connected, blow 'em away. */
260         lprintf(CTDL_DEBUG, "Closing socket %d\n", con->client_socket);
261         close(con->client_socket);
262
263         lprintf(CTDL_DEBUG, "Done with RemoveContext()\n");
264 }
265
266
267
268
269
270 /*
271  * cmd_info()  -  tell the client about this server
272  */
273 void cmd_info(void) {
274         cprintf("%d Server info:\n", LISTING_FOLLOWS);
275         cprintf("%d\n", CC->cs_pid);
276         cprintf("%s\n", config.c_nodename);
277         cprintf("%s\n", config.c_humannode);
278         cprintf("%s\n", config.c_fqdn);
279         cprintf("%s\n", CITADEL);
280         cprintf("%d\n", REV_LEVEL);
281         cprintf("%s\n", config.c_site_location);
282         cprintf("%s\n", config.c_sysadm);
283         cprintf("%d\n", SERVER_TYPE);
284         cprintf("%s\n", config.c_moreprompt);
285         cprintf("1\n"); /* 1 = yes, this system supports floors */
286         cprintf("1\n"); /* 1 = we support the extended paging options */
287         cprintf("%s\n", CC->cs_nonce);
288         cprintf("1\n"); /* 1 = yes, this system supports the QNOP command */
289
290 #ifdef HAVE_LDAP
291         cprintf("1\n"); /* 1 = yes, this server is LDAP-enabled */
292 #else
293         cprintf("0\n"); /* 1 = no, this server is not LDAP-enabled */
294 #endif
295
296         if (config.c_auth_mode == 1) {
297                 cprintf("1\n"); /* "create new user" never works with host auth */
298         }
299         else {
300                 cprintf("%d\n", config.c_disable_newu); /* otherwise, site defined */
301         }
302
303         cprintf("%s\n", config.c_default_cal_zone);
304         /* Output load averages */
305         cprintf("%f\n", CtdlThreadLoadAvg);
306         cprintf("%f\n", CtdlThreadWorkerAvg);
307         cprintf("%d\n", CtdlThreadGetCount());
308         
309         cprintf("000\n");
310 }
311
312
313 /*
314  * returns an asterisk if there are any instant messages waiting,
315  * space otherwise.
316  */
317 char CtdlCheckExpress(void) {
318         if (CC->FirstExpressMessage == NULL) {
319                 return(' ');
320         }
321         else {
322                 return('*');
323         }
324 }
325
326 void cmd_time(void)
327 {
328    time_t tv;
329    struct tm tmp;
330    
331    tv = time(NULL);
332    localtime_r(&tv, &tmp);
333    
334    /* timezone and daylight global variables are not portable. */
335 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
336    cprintf("%d %ld|%ld|%d\n", CIT_OK, (long)tv, tmp.tm_gmtoff, tmp.tm_isdst);
337 #else
338    cprintf("%d %ld|%ld|%d\n", CIT_OK, (long)tv, timezone, tmp.tm_isdst);
339 #endif
340 }
341
342
343 /*
344  * Check originating host against the public_clients file.  This determines
345  * whether the client is allowed to change the hostname for this session
346  * (for example, to show the location of the user rather than the location
347  * of the client).
348  */
349 int is_public_client(void)
350 {
351         char buf[1024];
352         char addrbuf[1024];
353         FILE *fp;
354         int i;
355         struct stat statbuf;
356         static time_t pc_timestamp = 0;
357         static char public_clients[SIZ];
358         static char public_clients_file[SIZ];
359
360         snprintf(public_clients_file, 
361                          sizeof public_clients_file,
362                          "%s/public_clients",
363                          ctdl_etc_dir);
364
365         /*
366          * Check the time stamp on the public_clients file.  If it's been
367          * updated since the last time we were here (or if this is the first
368          * time we've been through the loop), read its contents and learn
369          * the IP addresses of the listed hosts.
370          */
371         if (stat(public_clients_file, &statbuf) != 0) {
372                 /* No public_clients file exists, so bail out */
373                 lprintf(CTDL_WARNING, "Warning: '%s' does not exist\n", 
374                                 public_clients_file);
375                 return(0);
376         }
377
378         if (statbuf.st_mtime > pc_timestamp) {
379                 begin_critical_section(S_PUBLIC_CLIENTS);
380                 lprintf(CTDL_INFO, "Loading %s\n", public_clients_file);
381
382                 safestrncpy(public_clients, "127.0.0.1", sizeof public_clients);
383                 if (hostname_to_dotted_quad(addrbuf, config.c_fqdn) == 0) {
384                         strcat(public_clients, "|");
385                         strcat(public_clients, addrbuf);
386                 }
387
388                 fp = fopen(public_clients_file, "r");
389                 if (fp != NULL) while (fgets(buf, sizeof buf, fp)!=NULL) {
390                         char *ptr;
391                         ptr = buf;
392                         while (!IsEmptyStr(ptr)) {
393                                 if (*ptr == '#') {
394                                         *ptr = 0;
395                                         break;
396                                 }
397                                 else ptr++;
398                         }
399                         ptr--;
400                         while (ptr>buf && isspace(*ptr)) {
401                                 *(ptr--) = 0;
402                         }
403                         if (hostname_to_dotted_quad(addrbuf, buf) == 0) {
404                                 if ((strlen(public_clients) +
405                                    strlen(addrbuf) + 2)
406                                    < sizeof(public_clients)) {
407                                         strcat(public_clients, "|");
408                                         strcat(public_clients, addrbuf);
409                                 }
410                         }
411                 }
412                 fclose(fp);
413                 pc_timestamp = time(NULL);
414                 end_critical_section(S_PUBLIC_CLIENTS);
415         }
416
417         lprintf(CTDL_DEBUG, "Checking whether %s is a local or public client\n",
418                 CC->cs_addr);
419         for (i=0; i<num_parms(public_clients); ++i) {
420                 extract_token(addrbuf, public_clients, i, '|', sizeof addrbuf);
421                 if (!strcasecmp(CC->cs_addr, addrbuf)) {
422                         lprintf(CTDL_DEBUG, "... yes it is.\n");
423                         return(1);
424                 }
425         }
426
427         /* No hits.  This is not a public client. */
428         lprintf(CTDL_DEBUG, "... no it isn't.\n");
429         return(0);
430 }
431
432
433 /*
434  * the client is identifying itself to the server
435  */
436 void cmd_iden(char *argbuf)
437 {
438         int dev_code;
439         int cli_code;
440         int rev_level;
441         char desc[128];
442         char from_host[128];
443         struct in_addr addr;
444         int do_lookup = 0;
445
446         if (num_parms(argbuf)<4) {
447                 cprintf("%d usage error\n", ERROR + ILLEGAL_VALUE);
448                 return;
449         }
450
451         dev_code = extract_int(argbuf,0);
452         cli_code = extract_int(argbuf,1);
453         rev_level = extract_int(argbuf,2);
454         extract_token(desc, argbuf, 3, '|', sizeof desc);
455
456         safestrncpy(from_host, config.c_fqdn, sizeof from_host);
457         from_host[sizeof from_host - 1] = 0;
458         if (num_parms(argbuf)>=5) extract_token(from_host, argbuf, 4, '|', sizeof from_host);
459
460         CC->cs_clientdev = dev_code;
461         CC->cs_clienttyp = cli_code;
462         CC->cs_clientver = rev_level;
463         safestrncpy(CC->cs_clientname, desc, sizeof CC->cs_clientname);
464         CC->cs_clientname[31] = 0;
465
466         if (!IsEmptyStr(from_host)) {
467                 if (CC->is_local_socket) do_lookup = 1;
468                 else if (is_public_client()) do_lookup = 1;
469         }
470
471         if (do_lookup) {
472                 lprintf(CTDL_DEBUG, "Looking up hostname '%s'\n", from_host);
473                 if ((addr.s_addr = inet_addr(from_host)) != -1) {
474                         locate_host(CC->cs_host, sizeof CC->cs_host,
475                                 CC->cs_addr, sizeof CC->cs_addr,
476                                 &addr);
477                 }
478                 else {
479                         safestrncpy(CC->cs_host, from_host, sizeof CC->cs_host);
480                         CC->cs_host[sizeof CC->cs_host - 1] = 0;
481                 }
482         }
483
484         lprintf(CTDL_NOTICE, "Client %d/%d/%01d.%02d (%s) from %s\n",
485                 dev_code,
486                 cli_code,
487                 (rev_level / 100),
488                 (rev_level % 100),
489                 desc,
490                 CC->cs_host);
491         cprintf("%d Ok\n",CIT_OK);
492 }
493
494
495 /*
496  * display system messages or help
497  */
498 void cmd_mesg(char *mname)
499 {
500         FILE *mfp;
501         char targ[256];
502         char buf[256];
503         char buf2[256];
504         char *dirs[2];
505         DIR *dp;
506         struct dirent *d;
507
508         extract_token(buf, mname, 0, '|', sizeof buf);
509
510         dirs[0] = strdup(ctdl_message_dir);
511         dirs[1] = strdup(ctdl_hlp_dir);
512
513         snprintf(buf2, sizeof buf2, "%s.%d.%d",
514                 buf, CC->cs_clientdev, CC->cs_clienttyp);
515
516         /* If the client requested "?" then produce a listing */
517         if (!strcmp(buf, "?")) {
518                 cprintf("%d %s\n", LISTING_FOLLOWS, buf);
519                 dp = opendir(dirs[1]);
520                 if (dp != NULL) {
521                         while (d = readdir(dp), d != NULL) {
522                                 if (d->d_name[0] != '.') {
523                                         cprintf(" %s\n", d->d_name);
524                                 }
525                         }
526                         closedir(dp);
527                 }
528                 cprintf("000\n");
529                 free(dirs[0]);
530                 free(dirs[1]);
531                 return;
532         }
533
534         /* Otherwise, look for the requested file by name. */
535         else {
536                 mesg_locate(targ, sizeof targ, buf2, 2, (const char **)dirs);
537                 if (IsEmptyStr(targ)) {
538                         snprintf(buf2, sizeof buf2, "%s.%d",
539                                                         buf, CC->cs_clientdev);
540                         mesg_locate(targ, sizeof targ, buf2, 2,
541                                                         (const char **)dirs);
542                         if (IsEmptyStr(targ)) {
543                                 mesg_locate(targ, sizeof targ, buf, 2,
544                                                         (const char **)dirs);
545                         }       
546                 }
547         }
548
549         free(dirs[0]);
550         free(dirs[1]);
551
552         if (IsEmptyStr(targ)) {
553                 cprintf("%d '%s' not found.  (Searching in %s and %s)\n",
554                         ERROR + FILE_NOT_FOUND,
555                         mname,
556                         ctdl_message_dir,
557                         ctdl_hlp_dir
558                 );
559                 return;
560         }
561
562         mfp = fopen(targ, "r");
563         if (mfp==NULL) {
564                 cprintf("%d Cannot open '%s': %s\n",
565                         ERROR + INTERNAL_ERROR, targ, strerror(errno));
566                 return;
567         }
568         cprintf("%d %s\n", LISTING_FOLLOWS,buf);
569
570         while (fgets(buf, (sizeof buf - 1), mfp) != NULL) {
571                 buf[strlen(buf)-1] = 0;
572                 do_help_subst(buf);
573                 cprintf("%s\n",buf);
574         }
575
576         fclose(mfp);
577         cprintf("000\n");
578 }
579
580
581 /*
582  * enter system messages or help
583  */
584 void cmd_emsg(char *mname)
585 {
586         FILE *mfp;
587         char targ[256];
588         char buf[256];
589         char *dirs[2];
590         int a;
591
592         unbuffer_output();
593
594         if (CtdlAccessCheck(ac_aide)) return;
595
596         extract_token(buf, mname, 0, '|', sizeof buf);
597         for (a=0; !IsEmptyStr(&buf[a]); ++a) {          /* security measure */
598                 if (buf[a] == '/') buf[a] = '.';
599         }
600
601         dirs[0] = strdup(ctdl_message_dir);
602         dirs[1] = strdup(ctdl_hlp_dir);
603
604         mesg_locate(targ, sizeof targ, buf, 2, (const char**)dirs);
605         free(dirs[0]);
606         free(dirs[1]);
607
608         if (IsEmptyStr(targ)) {
609                 snprintf(targ, sizeof targ, 
610                                  "%s/%s",
611                                  ctdl_hlp_dir, buf);
612         }
613
614         mfp = fopen(targ,"w");
615         if (mfp==NULL) {
616                 cprintf("%d Cannot open '%s': %s\n",
617                         ERROR + INTERNAL_ERROR, targ, strerror(errno));
618                 return;
619         }
620         cprintf("%d %s\n", SEND_LISTING, targ);
621
622         while (client_getln(buf, sizeof buf), strcmp(buf, "000")) {
623                 fprintf(mfp, "%s\n", buf);
624         }
625
626         fclose(mfp);
627 }
628
629
630 /* Don't show the names of private rooms unless the viewing
631  * user also knows the rooms.
632  */
633 void GenerateRoomDisplay(char *real_room,
634                         struct CitContext *viewed,
635                         struct CitContext *viewer) {
636
637         int ra;
638
639         strcpy(real_room, viewed->room.QRname);
640         if (viewed->room.QRflags & QR_MAILBOX) {
641                 strcpy(real_room, &real_room[11]);
642         }
643         if (viewed->room.QRflags & QR_PRIVATE) {
644                 CtdlRoomAccess(&viewed->room, &viewer->user, &ra, NULL);
645                 if ( (ra & UA_KNOWN) == 0) {
646                         strcpy(real_room, "<private room>");
647                 }
648         }
649
650         if (viewed->cs_flags & CS_CHAT) {
651                 while (strlen(real_room) < 14)
652                         strcat(real_room, " ");
653
654                 strcpy(&real_room[14], "<chat>");
655         }
656
657 }
658
659 /*
660  * Convenience function.
661  */
662 int CtdlAccessCheck(int required_level) {
663
664         if (CC->internal_pgm) return(0);
665         if (required_level >= ac_internal) {
666                 cprintf("%d This is not a user-level command.\n",
667                         ERROR + HIGHER_ACCESS_REQUIRED);
668                 return(-1);
669         }
670
671         if ((required_level >= ac_logged_in) && (CC->logged_in == 0)) {
672                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
673                 return(-1);
674         }
675
676         if (CC->user.axlevel >= 6) return(0);
677         if (required_level >= ac_aide) {
678                 cprintf("%d This command requires Aide access.\n",
679                         ERROR + HIGHER_ACCESS_REQUIRED);
680                 return(-1);
681         }
682
683         if (is_room_aide()) return(0);
684         if (required_level >= ac_room_aide) {
685                 cprintf("%d This command requires Aide or Room Aide access.\n",
686                         ERROR + HIGHER_ACCESS_REQUIRED);
687                 return(-1);
688         }
689
690         /* shhh ... succeed quietly */
691         return(0);
692 }
693
694
695
696 /*
697  * Terminate another running session
698  */
699 void cmd_term(char *cmdbuf)
700 {
701         int session_num;
702         struct CitContext *ccptr;
703         int found_it = 0;
704         int allowed = 0;
705
706         session_num = extract_int(cmdbuf, 0);
707         if (session_num == CC->cs_pid) {
708                 cprintf("%d You can't kill your own session.\n", ERROR + ILLEGAL_VALUE);
709                 return;
710         }
711
712         lprintf(CTDL_DEBUG, "Locating session to kill\n");
713         begin_critical_section(S_SESSION_TABLE);
714         for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
715                 if (session_num == ccptr->cs_pid) {
716                         found_it = 1;
717                         if ((ccptr->user.usernum == CC->user.usernum)
718                            || (CC->user.axlevel >= 6)) {
719                                 allowed = 1;
720                                 ccptr->kill_me = 1;
721                         }
722                         else {
723                                 allowed = 0;
724                         }
725                 }
726         }
727         end_critical_section(S_SESSION_TABLE);
728
729         if (found_it) {
730                 if (allowed) {
731                         cprintf("%d Session terminated.\n", CIT_OK);
732                 }
733                 else {
734                         cprintf("%d You are not allowed to do that.\n",
735                                 ERROR + HIGHER_ACCESS_REQUIRED);
736                 }
737         }
738         else {
739                 cprintf("%d No such session.\n", ERROR + ILLEGAL_VALUE);
740         }
741 }
742
743
744
745
746
747 /* 
748  * get the paginator prompt
749  */
750 void cmd_more(void) {
751         cprintf("%d %s\n", CIT_OK, config.c_moreprompt);
752 }
753
754 /*
755  * echo 
756  */
757 void cmd_echo(char *etext)
758 {
759         cprintf("%d %s\n", CIT_OK, etext);
760 }
761
762
763
764 /* 
765  * identify as internal program
766  */
767 void cmd_ipgm(char *argbuf)
768 {
769         int secret;
770
771         secret = extract_int(argbuf, 0);
772
773         /* For security reasons, we do NOT allow this command to run
774          * over the network.  Local sockets only.
775          */
776         if (!CC->is_local_socket) {
777                 sleep(5);
778                 cprintf("%d Authentication failed.\n",
779                         ERROR + PASSWORD_REQUIRED);
780         }
781         else if (secret == config.c_ipgm_secret) {
782                 CC->internal_pgm = 1;
783                 strcpy(CC->curr_user, "<internal program>");
784                 CC->cs_flags = CC->cs_flags|CS_STEALTH;
785                 cprintf("%d Authenticated as an internal program.\n", CIT_OK);
786         }
787         else {
788                 sleep(5);
789                 cprintf("%d Authentication failed.\n",
790                         ERROR + PASSWORD_REQUIRED);
791                 lprintf(CTDL_ERR, "Warning: ipgm authentication failed.\n");
792                 CC->kill_me = 1;
793         }
794
795         /* Now change the ipgm secret for the next round.
796          * (Disabled because it breaks concurrent scripts.  The fact that
797          * we no longer accept IPGM over the network should be sufficient
798          * to prevent brute-force attacks.  If you don't agree, uncomment
799          * this block.)
800         get_config();
801         config.c_ipgm_secret = rand();
802         put_config();
803         */
804 }
805
806
807 /*
808  * Shut down the server
809  */
810 void cmd_down(char *argbuf) {
811         char *Reply ="%d Shutting down server.  Goodbye.\n";
812
813         if (CtdlAccessCheck(ac_aide)) return;
814
815         if (!IsEmptyStr(argbuf))
816         {
817                 int state = CIT_OK;
818                 restart_server = extract_int(argbuf, 0);
819                 
820                 if (restart_server > 0)
821                         Reply = "%d Restarting server.  See you soon.\n";
822                 if ((restart_server > 0) && !running_as_daemon)
823                 {
824                         lprintf(CTDL_ERR, "The user requested restart, but not running as deamon! Geronimooooooo!\n");
825                         Reply = "%d Warning, not running in deamon mode. maybe we will come up again, but don't lean on it.\n";
826                         state = ERROR;
827                 }
828                 cprintf(Reply, state);
829         }
830         else
831         {
832                 cprintf(Reply, CIT_OK + SERVER_SHUTTING_DOWN);
833         }
834         CtdlThreadStopAll();
835 }
836
837 /*
838  * Halt the server without exiting the server process.
839  */
840 void cmd_halt(void) {
841
842         if (CtdlAccessCheck(ac_aide)) return;
843
844         cprintf("%d Halting server.  Goodbye.\n", CIT_OK);
845         CtdlThreadStopAll();
846         shutdown_and_halt = 1;
847 }
848
849 /*
850  * Schedule or cancel a server shutdown
851  */
852 void cmd_scdn(char *argbuf)
853 {
854         int new_state;
855         int state = CIT_OK;
856         char *Reply = "%d %d\n";
857
858         if (CtdlAccessCheck(ac_aide)) return;
859
860         new_state = extract_int(argbuf, 0);
861         if ((new_state == 2) || (new_state == 3))
862         {
863                 restart_server = 1;
864                 if (!running_as_daemon)
865                 {
866                         lprintf(CTDL_ERR, "The user requested restart, but not running as deamon! Geronimooooooo!\n");
867                         Reply = "%d %d Warning, not running in deamon mode. maybe we will come up again, but don't lean on it.\n";
868                         state = ERROR;
869                 }
870
871                 restart_server = extract_int(argbuf, 0);
872                 new_state -= 2;
873         }
874         if ((new_state == 0) || (new_state == 1)) {
875                 ScheduledShutdown = new_state;
876         }
877         cprintf(Reply, state, ScheduledShutdown);
878 }
879
880
881 /*
882  * Set or unset asynchronous protocol mode
883  */
884 void cmd_asyn(char *argbuf)
885 {
886         int new_state;
887
888         new_state = extract_int(argbuf, 0);
889         if ((new_state == 0) || (new_state == 1)) {
890                 CC->is_async = new_state;
891         }
892         cprintf("%d %d\n", CIT_OK, CC->is_async);
893 }
894
895
896 /*
897  * Generate a "nonce" for APOP-style authentication.
898  *
899  * RFC 1725 et al specify a PID to be placed in front of the nonce.
900  * Quoth BTX: That would be stupid.
901  */
902 void generate_nonce(struct CitContext *con) {
903         struct timeval tv;
904
905         memset(con->cs_nonce, NONCE_SIZE, 0);
906         gettimeofday(&tv, NULL);
907         memset(con->cs_nonce, NONCE_SIZE, 0);
908         snprintf(con->cs_nonce, NONCE_SIZE, "<%d%ld@%s>",
909                 rand(), (long)tv.tv_usec, config.c_fqdn);
910 }
911
912
913
914
915 /*
916  * Back-end function for starting a session
917  */
918 void begin_session(struct CitContext *con)
919 {
920         socklen_t len;
921         struct sockaddr_in sin;
922
923         /* 
924          * Initialize some variables specific to our context.
925          */
926         con->logged_in = 0;
927         con->internal_pgm = 0;
928         con->download_fp = NULL;
929         con->upload_fp = NULL;
930         con->FirstExpressMessage = NULL;
931         time(&con->lastcmd);
932         time(&con->lastidle);
933         strcpy(con->lastcmdname, "    ");
934         strcpy(con->cs_clientname, "(unknown)");
935         strcpy(con->curr_user, NLI);
936         *con->net_node = '\0';
937         *con->fake_username = '\0';
938         *con->fake_hostname = '\0';
939         *con->fake_roomname = '\0';
940         generate_nonce(con);
941         safestrncpy(con->cs_host, config.c_fqdn, sizeof con->cs_host);
942         safestrncpy(con->cs_addr, "", sizeof con->cs_addr);
943         con->cs_host[sizeof con->cs_host - 1] = 0;
944         len = sizeof sin;
945         if (!CC->is_local_socket) {
946                 if (!getpeername(con->client_socket, (struct sockaddr *) &sin, &len)) {
947                         locate_host(con->cs_host, sizeof con->cs_host,
948                                 con->cs_addr, sizeof con->cs_addr,
949                                 &sin.sin_addr
950                         );
951                 }
952         }
953         else {
954                 strcpy(con->cs_host, "");
955         }
956         con->cs_flags = 0;
957         con->upload_type = UPL_FILE;
958         con->dl_is_net = 0;
959
960         con->nologin = 0;
961         if ((config.c_maxsessions > 0)&&(num_sessions > config.c_maxsessions)) {
962                 con->nologin = 1;
963         }
964
965         lprintf(CTDL_NOTICE, "Session started.\n");
966
967         /* Run any session startup routines registered by loadable modules */
968         PerformSessionHooks(EVT_START);
969 }
970
971
972 void citproto_begin_session() {
973         if (CC->nologin==1) {
974                 cprintf("%d %s: Too many users are already online (maximum is %d)\n",
975                         ERROR + MAX_SESSIONS_EXCEEDED,
976                         config.c_nodename, config.c_maxsessions
977                 );
978                 CC->kill_me = 1;
979         }
980         else {
981                 cprintf("%d %s Citadel server ready.\n",
982                         CIT_OK, config.c_nodename);
983         }
984 }
985
986
987
988
989 /*
990  * This loop recognizes all server commands.
991  */
992 void do_command_loop(void) {
993         char cmdbuf[SIZ];
994         const char *old_name = NULL;
995         
996         old_name = CtdlThreadName("do_command_loop");
997         
998         time(&CC->lastcmd);
999         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
1000         if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
1001                 lprintf(CTDL_ERR, "Client disconnected: ending session.\n");
1002                 CC->kill_me = 1;
1003                 CtdlThreadName(old_name);
1004                 return;
1005         }
1006
1007         /* Log the server command, but don't show passwords... */
1008         if ( (strncasecmp(cmdbuf, "PASS", 4))
1009            && (strncasecmp(cmdbuf, "SETP", 4)) ) {
1010                 lprintf(CTDL_INFO, "%s\n", cmdbuf);
1011         }
1012         else {
1013                 lprintf(CTDL_INFO, "<password command sent>\n");
1014         }
1015
1016         buffer_output();
1017
1018         /*
1019          * Let other clients see the last command we executed, and
1020          * update the idle time, but not NOOP, QNOP, PEXP, GEXP, RWHO, or TIME.
1021          */
1022         if ( (strncasecmp(cmdbuf, "NOOP", 4))
1023            && (strncasecmp(cmdbuf, "QNOP", 4))
1024            && (strncasecmp(cmdbuf, "PEXP", 4))
1025            && (strncasecmp(cmdbuf, "GEXP", 4))
1026            && (strncasecmp(cmdbuf, "RWHO", 4))
1027            && (strncasecmp(cmdbuf, "TIME", 4)) ) {
1028                 strcpy(CC->lastcmdname, "    ");
1029                 safestrncpy(CC->lastcmdname, cmdbuf, sizeof(CC->lastcmdname));
1030                 time(&CC->lastidle);
1031         }
1032         
1033         CtdlThreadName(cmdbuf);
1034                 
1035         if ((strncasecmp(cmdbuf, "ENT0", 4))
1036            && (strncasecmp(cmdbuf, "MESG", 4))
1037            && (strncasecmp(cmdbuf, "MSGS", 4)))
1038         {
1039            CC->cs_flags &= ~CS_POSTING;
1040         }
1041                    
1042         if (!strncasecmp(cmdbuf, "NOOP", 4)) {
1043                 cprintf("%d%cok\n", CIT_OK, CtdlCheckExpress() );
1044         }
1045         
1046         else if (!strncasecmp(cmdbuf, "QNOP", 4)) {
1047                 /* do nothing, this command returns no response */
1048         }
1049
1050         else if (!strncasecmp(cmdbuf,"QUIT",4)) {
1051                 cprintf("%d Goodbye.\n", CIT_OK);
1052                 CC->kill_me = 1;
1053         }
1054
1055         else if (!strncasecmp(cmdbuf,"ASYN",4)) {
1056                 cmd_asyn(&cmdbuf[5]);
1057         }
1058
1059         else if (!strncasecmp(cmdbuf,"LOUT",4)) {
1060                 if (CC->logged_in) logout(CC);
1061                 cprintf("%d logged out.\n", CIT_OK);
1062         }
1063
1064         else if (!strncasecmp(cmdbuf,"USER",4)) {
1065                 cmd_user(&cmdbuf[5]);
1066         }
1067
1068         else if (!strncasecmp(cmdbuf,"PASS",4)) {
1069                 cmd_pass(&cmdbuf[5]);
1070         }
1071
1072         else if (!strncasecmp(cmdbuf,"NEWU",4)) {
1073                 cmd_newu(&cmdbuf[5]);
1074         }
1075
1076         else if (!strncasecmp(cmdbuf,"CREU",4)) {
1077                 cmd_creu(&cmdbuf[5]);
1078         }
1079
1080         else if (!strncasecmp(cmdbuf,"SETP",4)) {
1081                 cmd_setp(&cmdbuf[5]);
1082         }
1083
1084         else if (!strncasecmp(cmdbuf,"LRMS",4)) {
1085                 cmd_lrms(&cmdbuf[5]);
1086         }
1087
1088         else if (!strncasecmp(cmdbuf,"LKRA",4)) {
1089                 cmd_lkra(&cmdbuf[5]);
1090         }
1091
1092         else if (!strncasecmp(cmdbuf,"LKRN",4)) {
1093                 cmd_lkrn(&cmdbuf[5]);
1094         }
1095
1096         else if (!strncasecmp(cmdbuf,"LKRO",4)) {
1097                 cmd_lkro(&cmdbuf[5]);
1098         }
1099
1100         else if (!strncasecmp(cmdbuf,"LZRM",4)) {
1101                 cmd_lzrm(&cmdbuf[5]);
1102         }
1103
1104         else if (!strncasecmp(cmdbuf,"LPRM",4)) {
1105                 cmd_lprm(&cmdbuf[5]);
1106         }
1107
1108         else if (!strncasecmp(cmdbuf,"GETU",4)) {
1109                 cmd_getu();
1110         }
1111
1112         else if (!strncasecmp(cmdbuf,"SETU",4)) {
1113                 cmd_setu(&cmdbuf[5]);
1114         }
1115
1116         else if (!strncasecmp(cmdbuf,"GOTO",4)) {
1117                 cmd_goto(&cmdbuf[5]);
1118         }
1119
1120         else if (!strncasecmp(cmdbuf,"MSGS",4)) {
1121                 cmd_msgs(&cmdbuf[5]);
1122         }
1123
1124         else if (!strncasecmp(cmdbuf,"WHOK",4)) {
1125                 cmd_whok();
1126         }
1127
1128         else if (!strncasecmp(cmdbuf,"RDIR",4)) {
1129                 cmd_rdir();
1130         }
1131
1132         else if (!strncasecmp(cmdbuf,"EUID",4)) {
1133                 cmd_euid(&cmdbuf[5]);
1134         }
1135
1136         else if (!strncasecmp(cmdbuf,"MSG0",4)) {
1137                 cmd_msg0(&cmdbuf[5]);
1138         }
1139
1140         else if (!strncasecmp(cmdbuf,"MSG2",4)) {
1141                 cmd_msg2(&cmdbuf[5]);
1142         }
1143
1144         else if (!strncasecmp(cmdbuf,"MSG3",4)) {
1145                 cmd_msg3(&cmdbuf[5]);
1146         }
1147
1148         else if (!strncasecmp(cmdbuf,"MSG4",4)) {
1149                 cmd_msg4(&cmdbuf[5]);
1150         }
1151
1152         else if (!strncasecmp(cmdbuf,"MSGP",4)) {
1153                 cmd_msgp(&cmdbuf[5]);
1154         }
1155
1156         else if (!strncasecmp(cmdbuf,"OPNA",4)) {
1157                 cmd_opna(&cmdbuf[5]);
1158         }
1159
1160         else if (!strncasecmp(cmdbuf,"DLAT",4)) {
1161                 cmd_dlat(&cmdbuf[5]);
1162         }
1163
1164         else if (!strncasecmp(cmdbuf,"INFO",4)) {
1165                 cmd_info();
1166         }
1167
1168         else if (!strncasecmp(cmdbuf,"SLRP",4)) {
1169                 cmd_slrp(&cmdbuf[5]);
1170         }
1171
1172         else if (!strncasecmp(cmdbuf,"INVT",4)) {
1173                 cmd_invt_kick(&cmdbuf[5],1);
1174         }
1175
1176         else if (!strncasecmp(cmdbuf,"KICK",4)) {
1177                 cmd_invt_kick(&cmdbuf[5],0);
1178         }
1179
1180         else if (!strncasecmp(cmdbuf,"GETR",4)) {
1181                 cmd_getr();
1182         }
1183
1184         else if (!strncasecmp(cmdbuf,"SETR",4)) {
1185                 cmd_setr(&cmdbuf[5]);
1186         }
1187
1188         else if (!strncasecmp(cmdbuf,"GETA",4)) {
1189                 cmd_geta();
1190         }
1191
1192         else if (!strncasecmp(cmdbuf,"SETA",4)) {
1193                 cmd_seta(&cmdbuf[5]);
1194         }
1195
1196         else if (!strncasecmp(cmdbuf,"ENT0",4)) {
1197                 cmd_ent0(&cmdbuf[5]);
1198         }
1199
1200         else if (!strncasecmp(cmdbuf,"RINF",4)) {
1201                 cmd_rinf();
1202         }
1203
1204         else if (!strncasecmp(cmdbuf,"DELE",4)) {
1205                 cmd_dele(&cmdbuf[5]);
1206         }
1207
1208         else if (!strncasecmp(cmdbuf,"KILL",4)) {
1209                 cmd_kill(&cmdbuf[5]);
1210         }
1211
1212         else if (!strncasecmp(cmdbuf,"CRE8",4)) {
1213                 cmd_cre8(&cmdbuf[5]);
1214         }
1215
1216         else if (!strncasecmp(cmdbuf,"MOVE",4)) {
1217                 cmd_move(&cmdbuf[5]);
1218         }
1219
1220         else if (!strncasecmp(cmdbuf,"FORG",4)) {
1221                 cmd_forg();
1222         }
1223
1224         else if (!strncasecmp(cmdbuf,"MESG",4)) {
1225                 cmd_mesg(&cmdbuf[5]);
1226         }
1227
1228         else if (!strncasecmp(cmdbuf,"EMSG",4)) {
1229                 cmd_emsg(&cmdbuf[5]);
1230         }
1231
1232         else if (!strncasecmp(cmdbuf,"GNUR",4)) {
1233                 cmd_gnur();
1234         }
1235
1236         else if (!strncasecmp(cmdbuf,"VALI",4)) {
1237                 cmd_vali(&cmdbuf[5]);
1238         }
1239
1240         else if (!strncasecmp(cmdbuf,"EINF",4)) {
1241                 cmd_einf(&cmdbuf[5]);
1242         }
1243
1244         else if (!strncasecmp(cmdbuf,"LIST",4)) {
1245                 cmd_list(&cmdbuf[5]);
1246         }
1247
1248         else if (!strncasecmp(cmdbuf,"CHEK",4)) {
1249                 cmd_chek();
1250         }
1251
1252         else if (!strncasecmp(cmdbuf,"DELF",4)) {
1253                 cmd_delf(&cmdbuf[5]);
1254         }
1255
1256         else if (!strncasecmp(cmdbuf,"MOVF",4)) {
1257                 cmd_movf(&cmdbuf[5]);
1258         }
1259
1260         else if (!strncasecmp(cmdbuf,"NETF",4)) {
1261                 cmd_netf(&cmdbuf[5]);
1262         }
1263
1264         else if (!strncasecmp(cmdbuf,"OPEN",4)) {
1265                 cmd_open(&cmdbuf[5]);
1266         }
1267
1268         else if (!strncasecmp(cmdbuf,"CLOS",4)) {
1269                 cmd_clos();
1270         }
1271
1272         else if (!strncasecmp(cmdbuf,"UOPN",4)) {
1273                 cmd_uopn(&cmdbuf[5]);
1274         }
1275
1276         else if (!strncasecmp(cmdbuf,"UCLS",4)) {
1277                 cmd_ucls(&cmdbuf[5]);
1278         }
1279
1280         else if (!strncasecmp(cmdbuf,"READ",4)) {
1281                 cmd_read(&cmdbuf[5]);
1282         }
1283
1284         else if (!strncasecmp(cmdbuf,"WRIT",4)) {
1285                 cmd_writ(&cmdbuf[5]);
1286         }
1287
1288         else if (!strncasecmp(cmdbuf,"QUSR",4)) {
1289                 cmd_qusr(&cmdbuf[5]);
1290         }
1291
1292         else if (!strncasecmp(cmdbuf,"ECHO",4)) {
1293                 cmd_echo(&cmdbuf[5]);
1294         }
1295
1296         else if (!strncasecmp(cmdbuf,"OIMG",4)) {
1297                 cmd_oimg(&cmdbuf[5]);
1298         }
1299
1300         else if (!strncasecmp(cmdbuf,"MORE",4)) {
1301                 cmd_more();
1302         }
1303
1304         else if (!strncasecmp(cmdbuf,"NDOP",4)) {
1305                 cmd_ndop(&cmdbuf[5]);
1306         }
1307
1308         else if (!strncasecmp(cmdbuf,"NUOP",4)) {
1309                 cmd_nuop(&cmdbuf[5]);
1310         }
1311
1312         else if (!strncasecmp(cmdbuf,"LFLR",4)) {
1313                 cmd_lflr();
1314         }
1315
1316         else if (!strncasecmp(cmdbuf,"CFLR",4)) {
1317                 cmd_cflr(&cmdbuf[5]);
1318         }
1319
1320         else if (!strncasecmp(cmdbuf,"KFLR",4)) {
1321                 cmd_kflr(&cmdbuf[5]);
1322         }
1323
1324         else if (!strncasecmp(cmdbuf,"EFLR",4)) {
1325                 cmd_eflr(&cmdbuf[5]);
1326         }
1327
1328         else if (!strncasecmp(cmdbuf,"IDEN",4)) {
1329                 cmd_iden(&cmdbuf[5]);
1330         }
1331
1332         else if (!strncasecmp(cmdbuf,"IPGM",4)) {
1333                 cmd_ipgm(&cmdbuf[5]);
1334         }
1335
1336         else if (!strncasecmp(cmdbuf,"TERM",4)) {
1337                 cmd_term(&cmdbuf[5]);
1338         }
1339
1340         else if (!strncasecmp(cmdbuf,"DOWN",4)) {
1341                 cmd_down(&cmdbuf[5]);
1342         }
1343
1344         else if (!strncasecmp(cmdbuf,"HALT",4)) {
1345                 cmd_halt();
1346         }
1347
1348         else if (!strncasecmp(cmdbuf,"SCDN",4)) {
1349                 cmd_scdn(&cmdbuf[5]);
1350         }
1351
1352         else if (!strncasecmp(cmdbuf, "UIMG", 4)) {
1353                 cmd_uimg(&cmdbuf[5]);
1354         }
1355
1356         else if (!strncasecmp(cmdbuf, "TIME", 4)) {
1357                 cmd_time();
1358         }
1359
1360         else if (!strncasecmp(cmdbuf, "AGUP", 4)) {
1361                 cmd_agup(&cmdbuf[5]);
1362         }
1363
1364         else if (!strncasecmp(cmdbuf, "ASUP", 4)) {
1365                 cmd_asup(&cmdbuf[5]);
1366         }
1367
1368         else if (!strncasecmp(cmdbuf, "GPEX", 4)) {
1369                 cmd_gpex(&cmdbuf[5]);
1370         }
1371
1372         else if (!strncasecmp(cmdbuf, "SPEX", 4)) {
1373                 cmd_spex(&cmdbuf[5]);
1374         }
1375
1376         else if (!strncasecmp(cmdbuf, "CONF", 4)) {
1377                 cmd_conf(&cmdbuf[5]);
1378         }
1379
1380         else if (!strncasecmp(cmdbuf, "SEEN", 4)) {
1381                 cmd_seen(&cmdbuf[5]);
1382         }
1383
1384         else if (!strncasecmp(cmdbuf, "GTSN", 4)) {
1385                 cmd_gtsn(&cmdbuf[5]);
1386         }
1387
1388         else if (!strncasecmp(cmdbuf, "VIEW", 4)) {
1389                 cmd_view(&cmdbuf[5]);
1390         }
1391
1392         else if (!strncasecmp(cmdbuf, "ISME", 4)) {
1393                 cmd_isme(&cmdbuf[5]);
1394         }
1395
1396         else if (!DLoader_Exec_Cmd(cmdbuf)) {
1397                 cprintf("%d Unrecognized or unsupported command.\n",
1398                         ERROR + CMD_NOT_SUPPORTED);
1399                }
1400
1401         unbuffer_output();
1402
1403         /* Run any after-each-command routines registered by modules */
1404         PerformSessionHooks(EVT_CMD);
1405         CtdlThreadName(old_name);
1406 }
1407
1408
1409 /*
1410  * This loop performs all asynchronous functions.
1411  */
1412 void do_async_loop(void) {
1413         PerformSessionHooks(EVT_ASYNC);
1414 }