46ca3ecae79581c1e6bad91b77c4aa0dfa136abe
[citadel.git] / citadel / citserver.c
1 /* 
2  * $Id$
3  *
4  * Main source module for the Citadel server
5  *
6  */
7
8 #ifdef DLL_EXPORT
9 #define IN_LIBCIT
10 #endif
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <signal.h>
18
19 #if TIME_WITH_SYS_TIME
20 # include <sys/time.h>
21 # include <time.h>
22 #else
23 # if HAVE_SYS_TIME_H
24 #  include <sys/time.h>
25 # else
26 #  include <time.h>
27 # endif
28 #endif
29
30 #include <ctype.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <syslog.h>
35 /* #include <dlfcn.h> */
36 #include <netdb.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include "citadel.h"
42 #include "server.h"
43 #include "dynloader.h"
44 #include "sysdep_decls.h"
45 #include "citserver.h"
46 #include "config.h"
47 #include "database.h"
48 #include "housekeeping.h"
49 #include "user_ops.h"
50 #include "logging.h"
51 #include "msgbase.h"
52 #include "support.h"
53 #include "locate_host.h"
54 #include "room_ops.h"
55 #include "file_ops.h"
56 #include "policy.h"
57 #include "control.h"
58 #include "tools.h"
59
60 #ifndef HAVE_SNPRINTF
61 #include "snprintf.h"
62 #endif
63
64 struct CitContext *ContextList = NULL;
65 char *unique_session_numbers;
66 int ScheduledShutdown = 0;
67 int do_defrag = 0;
68
69 /* These are commented out.  Why do we need them?  They're defined in time.h
70  * anyway, and declaring them again here makes FreeBSD barf on it.
71 extern long int timezone;
72 extern int daylight;
73  */
74
75 /*
76  * Various things that need to be initialized at startup
77  */
78 void master_startup(void) {
79         struct timeval tv;
80         
81         lprintf(9, "master_startup() started\n");
82         lprintf(7, "Opening databases\n");
83         open_databases();
84
85         if (do_defrag) {
86                 defrag_databases();
87         }
88
89         check_ref_counts();
90
91         lprintf(7, "Creating base rooms (if necessary)\n");
92         create_room(BASEROOM,           0, "", 0, 1);
93         create_room(AIDEROOM,           3, "", 0, 1);
94         create_room(SYSCONFIGROOM,      3, "", 0, 1);
95         create_room(config.c_twitroom,  0, "", 0, 1);
96
97         lprintf(7, "Seeding the pseudo-random number generator...\n");
98         gettimeofday(&tv, NULL);
99         srand(tv.tv_usec);
100         lprintf(9, "master_startup() finished\n");
101 }
102
103
104
105 /*
106  * Cleanup routine to be called when the server is shutting down.
107  * WARNING: It's no longer safe to call this function to force a shutdown.
108  * Instead, set time_to_die = 1.
109  */
110 void master_cleanup(void) {
111         struct CleanupFunctionHook *fcn;
112
113         /* Run any cleanup routines registered by loadable modules */
114         for (fcn = CleanupHookTable; fcn != NULL; fcn = fcn->next) {
115                 (*fcn->h_function_pointer)();
116         }
117
118         /* Close databases */
119         lprintf(7, "Closing databases\n");
120         close_databases();
121
122         /* Do system-dependent stuff */
123         sysdep_master_cleanup();
124
125         /* Now go away. */
126         lprintf(3, "citserver: exiting.\n");
127         fflush(stdout); fflush(stderr);
128         exit(0);
129 }
130
131
132 /*
133  * Free any per-session data allocated by modules or whatever
134  */
135 void deallocate_user_data(struct CitContext *con)
136 {
137         struct CtdlSessData *ptr;
138
139         begin_critical_section(S_SESSION_TABLE);
140         while (con->FirstSessData != NULL) {
141                 lprintf(9, "Deallocating user data symbol %ld\n",
142                         con->FirstSessData->sym_id);
143                 if (con->FirstSessData->sym_data != NULL)
144                         phree(con->FirstSessData->sym_data);
145                 ptr = con->FirstSessData->next;
146                 phree(con->FirstSessData);
147                 con->FirstSessData = ptr;
148         }
149         end_critical_section(S_SESSION_TABLE);
150 }
151
152
153
154
155 /*
156  * Terminate a session and remove its context data structure.
157  */
158 void RemoveContext (struct CitContext *con)
159 {
160         struct CitContext *ptr = NULL;
161         struct CitContext *ToFree = NULL;
162
163         lprintf(9, "RemoveContext() called\n");
164         if (con==NULL) {
165                 lprintf(5, "WARNING: RemoveContext() called with NULL!\n");
166                 return;
167                 }
168
169         /* Remove the context from the global context list.  This needs
170          * to get done FIRST to avoid concurrency problems.  It is *vitally*
171          * important to keep num_sessions accurate!!
172          */
173         lprintf(7, "Removing context for session %d\n", con->cs_pid);
174         begin_critical_section(S_SESSION_TABLE);
175         if (ContextList == con) {
176                 ToFree = ContextList;
177                 ContextList = ContextList->next;
178                 --num_sessions;
179                 }
180         else {
181                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
182                         if (ptr->next == con) {
183                                 ToFree = ptr->next;
184                                 ptr->next = ptr->next->next;
185                                 --num_sessions;
186                                 }
187                         }
188                 }
189         end_critical_section(S_SESSION_TABLE);
190
191         if (ToFree == NULL) {
192                 lprintf(9, "RemoveContext() found nothing to remove\n");
193                 return;
194         }
195
196         /* Run any cleanup routines registered by loadable modules.
197          * Note 1: This must occur *before* deallocate_user_data() because the
198          *         cleanup functions might touch dynamic session data.
199          * Note 2: We have to "become_session()" because the cleanup functions
200          *         might make references to "CC" assuming it's the right one.
201          */
202         become_session(con);
203         PerformSessionHooks(EVT_STOP);
204         become_session(NULL);
205
206         /* Now handle all of the administrivia. */
207         lprintf(7, "Calling logout(%d)\n", con->cs_pid);
208         logout(con);
209
210         rec_log(CL_TERMINATE, con->curr_user);
211         unlink(con->temp);
212         lprintf(3, "[%3d] Session ended.\n", con->cs_pid);
213         
214
215         syslog(LOG_NOTICE,"session %d: ended", con->cs_pid);
216         
217         /* Deallocate any user-data attached to this session */
218         deallocate_user_data(con);
219
220         /* If the client is still connected, blow 'em away. */
221         lprintf(7, "Closing socket %d\n", con->client_socket);
222         close(con->client_socket);
223
224         /* This is where we used to check for scheduled shutdowns. */
225
226         /* Free up the memory used by this context */
227         phree(con);
228
229         lprintf(7, "Done with RemoveContext()\n");
230 }
231
232
233
234 /*
235  * Get a dynamic symbol number for per-session user data.
236  * This API call should be made only ONCE per symbol per citserver run.
237  */
238 int CtdlGetDynamicSymbol() 
239 {
240         static unsigned int next_symbol = SYM_MAX;
241         return ++next_symbol;
242 }
243
244
245
246 /*
247  * Return a pointer to some generic per-session user data.
248  * (This function returns NULL if the requested symbol is not allocated.)
249  *
250  * NOTE: we use critical sections for allocating and de-allocating these,
251  *       but not for locating one.
252  */
253 void *CtdlGetUserData(unsigned long requested_sym) 
254 {
255         struct CtdlSessData *ptr;
256
257         for (ptr = CC->FirstSessData; ptr != NULL; ptr = ptr->next)
258                 if (ptr->sym_id == requested_sym)
259                         return(ptr->sym_data);
260
261         lprintf(2, "ERROR! CtdlGetUserData(%ld) symbol not allocated\n",
262                 requested_sym);
263         return NULL;
264 }
265
266
267 /*
268  * Allocate some generic per-session user data.
269  */
270 void CtdlAllocUserData(unsigned long requested_sym, size_t num_bytes)
271 {
272         struct CtdlSessData *ptr;
273
274         lprintf(9, "CtdlAllocUserData(%ld) called\n", requested_sym);
275
276         /* Fail silently if the symbol is already registered. */
277         for (ptr = CC->FirstSessData; ptr != NULL; ptr = ptr->next)  {
278                 if (ptr->sym_id == requested_sym) {
279                         return;
280                 }
281         }
282
283         /* Grab us some memory!  Dem's good eatin' !!  */
284         ptr = mallok(sizeof(struct CtdlSessData));
285         ptr->sym_id = requested_sym;
286         ptr->sym_data = mallok(num_bytes);
287         memset(ptr->sym_data, 0, num_bytes);
288
289         begin_critical_section(S_SESSION_TABLE);
290         ptr->next = CC->FirstSessData;
291         CC->FirstSessData = ptr;
292         end_critical_section(S_SESSION_TABLE);
293
294         lprintf(9, "CtdlAllocUserData(%ld) finished\n", requested_sym);
295 }
296
297
298 /* 
299  * Change the size of a buffer allocated with CtdlAllocUserData()
300  */
301 void CtdlReallocUserData(unsigned long requested_sym, size_t num_bytes)
302 {
303         struct CtdlSessData *ptr;
304
305         for (ptr = CC->FirstSessData; ptr != NULL; ptr = ptr->next)  {
306                 if (ptr->sym_id == requested_sym) {
307                         ptr->sym_data = reallok(ptr->sym_data, num_bytes);
308                         return;
309                 }
310         }
311
312         lprintf(2, "CtdlReallocUserData() ERROR: symbol %ld not found!\n",
313                 requested_sym);
314 }
315
316
317
318
319
320
321 /*
322  * cmd_info()  -  tell the client about this server
323  */
324 void cmd_info(void) {
325         cprintf("%d Server info:\n", LISTING_FOLLOWS);
326         cprintf("%d\n", CC->cs_pid);
327         cprintf("%s\n", config.c_nodename);
328         cprintf("%s\n", config.c_humannode);
329         cprintf("%s\n", config.c_fqdn);
330         cprintf("%s\n", CITADEL);
331         cprintf("%d\n", REV_LEVEL);
332         cprintf("%s\n", config.c_bbs_city);
333         cprintf("%s\n", config.c_sysadm);
334         cprintf("%d\n", SERVER_TYPE);
335         cprintf("%s\n", config.c_moreprompt);
336         cprintf("1\n"); /* 1 = yes, this system supports floors */
337         cprintf("1\n"); /* 1 = we support the extended paging options */
338         cprintf("%s\n", CC->cs_nonce);
339         cprintf("000\n");
340 }
341
342
343 /*
344  * returns an asterisk if there are any express messages waiting,
345  * space otherwise.
346  */
347 char CtdlCheckExpress(void) {
348         if (CC->FirstExpressMessage == NULL) {
349                 return(' ');
350                 }
351         else {
352                 return('*');
353                 }
354         }
355
356 void cmd_time(void)
357 {
358    time_t tv;
359    
360    tv = time(NULL);
361    localtime(&tv);
362    
363    cprintf("%d %ld|%ld|%d\n", OK, (long)tv, timezone, daylight);
364 }
365
366 /*
367  * Check whether two hostnames match.
368  * "Realname" should be an actual name of a client that is trying to connect;
369  * "testname" should be the value we are comparing it with. The idea is that we
370  * want to compare with both the abbreviated and fully-qualified versions of
371  * "testname;" some people define "localhost" as "localhost.foo.com," etc.
372  */
373 static int hostnames_match(const char *realname, const char *testname) {
374         struct hostent *he;
375         int retval = 0;
376
377         if (!strcasecmp(realname, testname))
378                 return 1;
379
380 #ifdef HAVE_NONREENTRANT_NETDB
381         begin_critical_section(S_NETDB);
382 #endif
383
384         if ((he = gethostbyname(testname)) != NULL)
385                 if (!strcasecmp(realname, he->h_name))
386                         retval = 1;
387
388 #ifdef HAVE_NONREENTRANT_NETDB
389         end_critical_section(S_NETDB);
390 #endif
391
392         return retval;
393         }
394
395 /*
396  * check a hostname against the public_clients file
397  */
398 int is_public_client(char *where)
399 {
400         char buf[SIZ];
401         FILE *fp;
402
403         lprintf(9, "Checking whether %s is a public client\n", where);
404
405         if (hostnames_match(where, "localhost")) return(1);
406         if (hostnames_match(where, config.c_fqdn)) return(1);
407
408         fp = fopen("public_clients","r");
409         if (fp == NULL) return(0);
410
411         while (fgets(buf, sizeof buf, fp)!=NULL) {
412                 while (isspace((buf[strlen(buf)-1]))) 
413                         buf[strlen(buf)-1] = 0;
414                 if (hostnames_match(where,buf)) {
415                         fclose(fp);
416                         return(1);
417                         }
418                 }
419
420         fclose(fp);
421         return(0);
422         }
423
424
425 /*
426  * the client is identifying itself to the server
427  */
428 void cmd_iden(char *argbuf)
429 {
430         int dev_code;
431         int cli_code;
432         int rev_level;
433         char desc[SIZ];
434         char from_host[SIZ];
435         struct in_addr addr;
436         int do_lookup = 0;
437
438         if (num_parms(argbuf)<4) {
439                 cprintf("%d usage error\n",ERROR);
440                 return;
441         }
442
443         dev_code = extract_int(argbuf,0);
444         cli_code = extract_int(argbuf,1);
445         rev_level = extract_int(argbuf,2);
446         extract(desc,argbuf,3);
447
448         safestrncpy(from_host, config.c_fqdn, sizeof from_host);
449         from_host[sizeof from_host - 1] = 0;
450         if (num_parms(argbuf)>=5) extract(from_host,argbuf,4);
451
452         CC->cs_clientdev = dev_code;
453         CC->cs_clienttyp = cli_code;
454         CC->cs_clientver = rev_level;
455         safestrncpy(CC->cs_clientname, desc, sizeof CC->cs_clientname);
456         CC->cs_clientname[31] = 0;
457
458         if (strlen(from_host) > 0) {
459                 if (CC->is_local_socket) do_lookup = 1;
460                 else if (is_public_client(CC->cs_host)) do_lookup = 1;
461         }
462
463         if (do_lookup) {
464                 lprintf(9, "Looking up hostname '%s'\n", from_host);
465                 if ((addr.s_addr = inet_addr(from_host)) != -1) {
466                         locate_host(CC->cs_host, sizeof CC->cs_host, &addr);
467                 }
468                 else {
469                         safestrncpy(CC->cs_host, from_host, sizeof CC->cs_host);
470                         CC->cs_host[sizeof CC->cs_host - 1] = 0;
471                 }
472         }
473
474         lprintf(7, "client %d/%d/%01d.%02d (%s)\n",
475                 dev_code,
476                 cli_code,
477                 (rev_level / 100),
478                 (rev_level % 100),
479                 desc);
480
481         syslog(LOG_NOTICE,"session %d: client %d/%d/%01d.%02d (%s) from %s\n",
482                 CC->cs_pid,
483                 dev_code,
484                 cli_code,
485                 (rev_level / 100),
486                 (rev_level % 100),
487                 desc,
488                 CC->cs_host);
489         cprintf("%d Ok\n",OK);
490 }
491
492
493 /*
494  * display system messages or help
495  */
496 void cmd_mesg(char *mname)
497 {
498         FILE *mfp;
499         char targ[SIZ];
500         char buf[SIZ];
501         char *dirs[2];
502
503         extract(buf,mname,0);
504
505
506         dirs[0]=mallok(64);
507         dirs[1]=mallok(64);
508         strcpy(dirs[0],"messages");
509         strcpy(dirs[1],"help");
510         mesg_locate(targ,sizeof targ,buf,2,(const char **)dirs);
511         phree(dirs[0]);
512         phree(dirs[1]);
513
514
515         if (strlen(targ)==0) {
516                 cprintf("%d '%s' not found.\n",ERROR,mname);
517                 return;
518                 }
519
520         mfp = fopen(targ,"r");
521         if (mfp==NULL) {
522                 cprintf("%d Cannot open '%s': %s\n",
523                         ERROR,targ,strerror(errno));
524                 return;
525                 }
526         cprintf("%d %s\n",LISTING_FOLLOWS,buf);
527
528         while (fgets(buf, (SIZ-1), mfp)!=NULL) {
529                 buf[strlen(buf)-1] = 0;
530                 do_help_subst(buf);
531                 cprintf("%s\n",buf);
532                 }
533
534         fclose(mfp);
535         cprintf("000\n");
536         }
537
538
539 /*
540  * enter system messages or help
541  */
542 void cmd_emsg(char *mname)
543 {
544         FILE *mfp;
545         char targ[SIZ];
546         char buf[SIZ];
547         char *dirs[2];
548         int a;
549
550         if (CtdlAccessCheck(ac_aide)) return;
551
552         extract(buf,mname,0);
553         for (a=0; a<strlen(buf); ++a) {         /* security measure */
554                 if (buf[a] == '/') buf[a] = '.';
555                 }
556
557         dirs[0]=mallok(64);
558         dirs[1]=mallok(64);
559         strcpy(dirs[0],"messages");
560         strcpy(dirs[1],"help");
561         mesg_locate(targ,sizeof targ,buf,2,(const char**)dirs);
562         phree(dirs[0]);
563         phree(dirs[1]);
564
565         if (strlen(targ)==0) {
566                 snprintf(targ, sizeof targ, "./help/%s", buf);
567                 }
568
569         mfp = fopen(targ,"w");
570         if (mfp==NULL) {
571                 cprintf("%d Cannot open '%s': %s\n",
572                         ERROR,targ,strerror(errno));
573                 return;
574                 }
575         cprintf("%d %s\n", SEND_LISTING, targ);
576
577         while (client_gets(buf), strcmp(buf, "000")) {
578                 fprintf(mfp, "%s\n", buf);
579                 }
580
581         fclose(mfp);
582         }
583
584
585 /* Don't show the names of private rooms unless the viewing
586  * user also knows the rooms.
587  */
588 void GenerateRoomDisplay(char *real_room,
589                         struct CitContext *viewed,
590                         struct CitContext *viewer) {
591
592         strcpy(real_room, viewed->quickroom.QRname);
593         if (viewed->quickroom.QRflags & QR_MAILBOX) {
594                 strcpy(real_room, &real_room[11]);
595         }
596         if (viewed->quickroom.QRflags & QR_PRIVATE) {
597                 if ( (CtdlRoomAccess(&viewed->quickroom, &viewer->usersupp)
598                    & UA_KNOWN) == 0) {
599                         strcpy(real_room, "<private room>");
600                 }
601         }
602
603         if (viewed->cs_flags & CS_CHAT) {
604                 while (strlen(real_room) < 14)
605                         strcat(real_room, " ");
606
607                 strcpy(&real_room[14], "<chat>");
608         }
609
610 }
611
612 /*
613  * Convenience function.
614  */
615 int CtdlAccessCheck(int required_level) {
616
617         if (CC->internal_pgm) return(0);
618         if (required_level >= ac_internal) {
619                 cprintf("%d This is not a user-level command.\n",
620                         ERROR+HIGHER_ACCESS_REQUIRED);
621                 return(-1);
622         }
623
624         if (CC->usersupp.axlevel >= 6) return(0);
625         if (required_level >= ac_aide) {
626                 cprintf("%d This command requires Aide access.\n",
627                         ERROR+HIGHER_ACCESS_REQUIRED);
628                 return(-1);
629         }
630
631         if (is_room_aide()) return(0);
632         if (required_level >= ac_room_aide) {
633                 cprintf("%d This command requires Aide or Room Aide access.\n",
634                         ERROR + HIGHER_ACCESS_REQUIRED);
635                 return(-1);
636         }
637
638         if (CC->logged_in) return(0);
639         if (required_level >= ac_logged_in) {
640                 cprintf("%d Not logged in.\n", ERROR+NOT_LOGGED_IN);
641                 return(-1);
642         }
643
644         /* shhh ... succeed quietly */
645         return(0);
646 }
647
648
649
650 /*
651  * Terminate another running session
652  */
653 void cmd_term(char *cmdbuf)
654 {
655         int session_num;
656         struct CitContext *ccptr;
657         int found_it = 0;
658         int allowed = 0;
659
660         session_num = extract_int(cmdbuf, 0);
661         if (session_num == CC->cs_pid) {
662                 cprintf("%d You can't kill your own session.\n", ERROR);
663                 return;
664         }
665
666         lprintf(9, "Locating session to kill\n");
667         begin_critical_section(S_SESSION_TABLE);
668         for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
669                 if (session_num == ccptr->cs_pid) {
670                         found_it = 1;
671                         if ((ccptr->usersupp.usernum == CC->usersupp.usernum)
672                            || (CC->usersupp.axlevel >= 6)) {
673                                 allowed = 1;
674                                 ccptr->kill_me = 1;
675                         }
676                         else {
677                                 allowed = 0;
678                         }
679                 }
680         }
681         end_critical_section(S_SESSION_TABLE);
682
683         if (found_it) {
684                 if (allowed) {
685                         cprintf("%d Session terminated.\n", OK);
686                 }
687                 else {
688                         cprintf("%d You are not allowed to do that.\n",
689                                 ERROR + HIGHER_ACCESS_REQUIRED);
690                 }
691         }
692         else {
693                 cprintf("%d No such session.\n", ERROR);
694         }
695 }
696
697
698
699
700
701 /* 
702  * get the paginator prompt
703  */
704 void cmd_more(void) {
705         cprintf("%d %s\n",OK,config.c_moreprompt);
706         }
707
708 /*
709  * echo 
710  */
711 void cmd_echo(char *etext)
712 {
713         cprintf("%d %s\n",OK,etext);
714         }
715
716
717
718 /* 
719  * identify as internal program
720  */
721 void cmd_ipgm(char *argbuf)
722 {
723         int secret;
724
725         secret = extract_int(argbuf, 0);
726         if (secret == config.c_ipgm_secret) {
727                 CC->internal_pgm = 1;
728                 strcpy(CC->curr_user, "<internal program>");
729                 CC->cs_flags = CC->cs_flags|CS_STEALTH;
730                 cprintf("%d Authenticated as an internal program.\n",OK);
731                 }
732         else {
733                 cprintf("%d Authentication failed.\n",ERROR);
734                 lprintf(3, "Warning: ipgm authentication failed.\n");
735                 }
736         }
737
738
739 /*
740  * Shut down the server
741  */
742 void cmd_down(void) {
743
744         if (CtdlAccessCheck(ac_aide)) return;
745
746         cprintf("%d Shutting down server.  Goodbye.\n", OK);
747         time_to_die = 1;
748         }
749
750 /*
751  * Schedule or cancel a server shutdown
752  */
753 void cmd_scdn(char *argbuf)
754 {
755         int new_state;
756
757         if (CtdlAccessCheck(ac_aide)) return;
758
759         new_state = extract_int(argbuf, 0);
760         if ((new_state == 0) || (new_state == 1)) {
761                 ScheduledShutdown = new_state;
762                 }
763         cprintf("%d %d\n", OK, ScheduledShutdown);
764 }
765
766
767 /*
768  * Set or unset asynchronous protocol mode
769  */
770 void cmd_asyn(char *argbuf)
771 {
772         int new_state;
773
774         new_state = extract_int(argbuf, 0);
775         if ((new_state == 0) || (new_state == 1)) {
776                 CC->is_async = new_state;
777         }
778         cprintf("%d %d\n", OK, CC->is_async);
779 }
780
781
782 /*
783  * Generate a "nonce" for APOP-style authentication.
784  *
785  * RFC 1725 et al specify a PID to be placed in front of the nonce.
786  * Quoth BTX: That would be stupid.
787  */
788 void generate_nonce(struct CitContext *con) {
789         struct timeval tv;
790
791         memset(con->cs_nonce, NONCE_SIZE, 0);
792         gettimeofday(&tv, NULL);
793         memset(con->cs_nonce, NONCE_SIZE, 0);
794         snprintf(con->cs_nonce, NONCE_SIZE, "<%d%ld@%s>",
795                 rand(), (long)tv.tv_usec, config.c_fqdn);
796 }
797
798
799
800
801 /*
802  * Back-end function for starting a session
803  */
804 void begin_session(struct CitContext *con)
805 {
806         int len;
807         struct sockaddr_in sin;
808
809         /* 
810          * Initialize some variables specific to our context.
811          */
812         con->logged_in = 0;
813         con->internal_pgm = 0;
814         con->download_fp = NULL;
815         con->upload_fp = NULL;
816         con->FirstExpressMessage = NULL;
817         time(&con->lastcmd);
818         time(&con->lastidle);
819         strcpy(con->lastcmdname, "    ");
820         strcpy(con->cs_clientname, "(unknown)");
821         strcpy(con->curr_user, NLI);
822         strcpy(con->net_node,"");
823         strcpy(con->fake_username, "");
824         strcpy(con->fake_postname, "");
825         strcpy(con->fake_hostname, "");
826         strcpy(con->fake_roomname, "");
827         generate_nonce(con);
828         snprintf(con->temp, sizeof con->temp, tmpnam(NULL));
829         safestrncpy(con->cs_host, config.c_fqdn, sizeof con->cs_host);
830         con->cs_host[sizeof con->cs_host - 1] = 0;
831         len = sizeof sin;
832         if (!CC->is_local_socket) {
833                 if (!getpeername(con->client_socket,
834                    (struct sockaddr *) &sin, &len))
835                         locate_host(con->cs_host, sizeof con->cs_host, &sin.sin_addr);
836         }
837         else {
838                 strcpy(con->cs_host, "");
839         }
840         con->cs_flags = 0;
841         con->upload_type = UPL_FILE;
842         con->dl_is_net = 0;
843         con->FirstSessData = NULL;
844
845         con->nologin = 0;
846         if ((config.c_maxsessions > 0)&&(num_sessions > config.c_maxsessions))
847                 con->nologin = 1;
848
849         lprintf(3, "Session started.\n");
850
851         /* Run any session startup routines registered by loadable modules */
852         PerformSessionHooks(EVT_START);
853
854         rec_log(CL_CONNECT, "");
855 }
856
857
858 void citproto_begin_session() {
859         if (CC->nologin==1) {
860                 cprintf("%d %s: Too many users are already online "
861                         "(maximum is %d)\n",
862                         ERROR+MAX_SESSIONS_EXCEEDED,
863                         config.c_nodename, config.c_maxsessions);
864                 }
865         else {
866                 cprintf("%d %s Citadel/UX server ready.\n",
867                         OK, config.c_nodename);
868                 }
869 }
870
871
872
873
874 /*
875  * This loop recognizes all server commands.
876  */
877 void do_command_loop(void) {
878         char cmdbuf[SIZ];
879
880         time(&CC->lastcmd);
881         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
882         if (client_gets(cmdbuf) < 1) {
883                 lprintf(3, "Client socket is broken.  Ending session.\n");
884                 CC->kill_me = 1;
885                 return;
886         }
887         lprintf(5, "Citadel: %s\n", cmdbuf);
888
889         /*
890          * Let other clients see the last command we executed, and
891          * update the idle time, but not NOOP, PEXP, or GEXP.
892          */
893         if ( (strncasecmp(cmdbuf, "NOOP", 4))
894            && (strncasecmp(cmdbuf, "PEXP", 4))
895            && (strncasecmp(cmdbuf, "GEXP", 4)) ) {
896                 strcpy(CC->lastcmdname, "    ");
897                 safestrncpy(CC->lastcmdname, cmdbuf, 
898                         sizeof(CC->lastcmdname) );
899                 time(&CC->lastidle);
900                 }
901                 
902         if ((strncasecmp(cmdbuf, "ENT0", 4))
903            && (strncasecmp(cmdbuf, "MESG", 4))
904            && (strncasecmp(cmdbuf, "MSGS", 4)))
905         {
906            CC->cs_flags &= ~CS_POSTING;
907         }
908                    
909         if (!strncasecmp(cmdbuf,"NOOP",4)) {
910                 cprintf("%d%cok\n",OK,CtdlCheckExpress());
911                 }
912
913         else if (!strncasecmp(cmdbuf,"QUIT",4)) {
914                 cprintf("%d Goodbye.\n",OK);
915                 CC->kill_me = 1;
916                 }
917
918         else if (!strncasecmp(cmdbuf,"ASYN",4)) {
919                 cmd_asyn(&cmdbuf[5]);
920                 }
921
922         else if (!strncasecmp(cmdbuf,"LOUT",4)) {
923                 if (CC->logged_in) logout(CC);
924                 cprintf("%d logged out.\n",OK);
925                 }
926
927         else if (!strncasecmp(cmdbuf,"USER",4)) {
928                 cmd_user(&cmdbuf[5]);
929                 }
930
931         else if (!strncasecmp(cmdbuf,"PASS",4)) {
932                 cmd_pass(&cmdbuf[5]);
933                 }
934
935         else if (!strncasecmp(cmdbuf,"NEWU",4)) {
936                 cmd_newu(&cmdbuf[5]);
937                 }
938
939         else if (!strncasecmp(cmdbuf,"CREU",4)) {
940                 cmd_creu(&cmdbuf[5]);
941                 }
942
943         else if (!strncasecmp(cmdbuf,"SETP",4)) {
944                 cmd_setp(&cmdbuf[5]);
945                 }
946
947         else if (!strncasecmp(cmdbuf,"LRMS",4)) {
948                 cmd_lrms(&cmdbuf[5]);
949                 }
950
951         else if (!strncasecmp(cmdbuf,"LKRA",4)) {
952                 cmd_lkra(&cmdbuf[5]);
953                 }
954
955         else if (!strncasecmp(cmdbuf,"LKRN",4)) {
956                 cmd_lkrn(&cmdbuf[5]);
957                 }
958
959         else if (!strncasecmp(cmdbuf,"LKRO",4)) {
960                 cmd_lkro(&cmdbuf[5]);
961                 }
962
963         else if (!strncasecmp(cmdbuf,"LZRM",4)) {
964                 cmd_lzrm(&cmdbuf[5]);
965                 }
966
967         else if (!strncasecmp(cmdbuf,"GETU",4)) {
968                 cmd_getu();
969                 }
970
971         else if (!strncasecmp(cmdbuf,"SETU",4)) {
972                 cmd_setu(&cmdbuf[5]);
973                 }
974
975         else if (!strncasecmp(cmdbuf,"GOTO",4)) {
976                 cmd_goto(&cmdbuf[5]);
977                 }
978
979         else if (!strncasecmp(cmdbuf,"MSGS",4)) {
980                 cmd_msgs(&cmdbuf[5]);
981                 }
982
983         else if (!strncasecmp(cmdbuf,"WHOK",4)) {
984                 cmd_whok();
985                 }
986
987         else if (!strncasecmp(cmdbuf,"RDIR",4)) {
988                 cmd_rdir();
989                 }
990
991         else if (!strncasecmp(cmdbuf,"MSG0",4)) {
992                 cmd_msg0(&cmdbuf[5]);
993                 }
994
995         else if (!strncasecmp(cmdbuf,"MSG2",4)) {
996                 cmd_msg2(&cmdbuf[5]);
997                 }
998
999         else if (!strncasecmp(cmdbuf,"MSG3",4)) {
1000                 cmd_msg3(&cmdbuf[5]);
1001                 }
1002
1003         else if (!strncasecmp(cmdbuf,"MSG4",4)) {
1004                 cmd_msg4(&cmdbuf[5]);
1005                 }
1006
1007         else if (!strncasecmp(cmdbuf,"OPNA",4)) {
1008                 cmd_opna(&cmdbuf[5]);
1009                 }
1010
1011         else if (!strncasecmp(cmdbuf,"INFO",4)) {
1012                 cmd_info();
1013                 }
1014
1015         else if (!strncasecmp(cmdbuf,"SLRP",4)) {
1016                 cmd_slrp(&cmdbuf[5]);
1017                 }
1018
1019         else if (!strncasecmp(cmdbuf,"INVT",4)) {
1020                 cmd_invt_kick(&cmdbuf[5],1);
1021                 }
1022
1023         else if (!strncasecmp(cmdbuf,"KICK",4)) {
1024                 cmd_invt_kick(&cmdbuf[5],0);
1025                 }
1026
1027         else if (!strncasecmp(cmdbuf,"GETR",4)) {
1028                 cmd_getr();
1029                 }
1030
1031         else if (!strncasecmp(cmdbuf,"SETR",4)) {
1032                 cmd_setr(&cmdbuf[5]);
1033                 }
1034
1035         else if (!strncasecmp(cmdbuf,"GETA",4)) {
1036                 cmd_geta();
1037                 }
1038
1039         else if (!strncasecmp(cmdbuf,"SETA",4)) {
1040                 cmd_seta(&cmdbuf[5]);
1041                 }
1042
1043         else if (!strncasecmp(cmdbuf,"ENT0",4)) {
1044                 cmd_ent0(&cmdbuf[5]);
1045                 }
1046
1047         else if (!strncasecmp(cmdbuf,"RINF",4)) {
1048                 cmd_rinf();
1049                 }
1050
1051         else if (!strncasecmp(cmdbuf,"DELE",4)) {
1052                 cmd_dele(&cmdbuf[5]);
1053                 }
1054
1055         else if (!strncasecmp(cmdbuf,"KILL",4)) {
1056                 cmd_kill(&cmdbuf[5]);
1057                 }
1058
1059         else if (!strncasecmp(cmdbuf,"CRE8",4)) {
1060                 cmd_cre8(&cmdbuf[5]);
1061                 }
1062
1063         else if (!strncasecmp(cmdbuf,"MOVE",4)) {
1064                 cmd_move(&cmdbuf[5]);
1065                 }
1066
1067         else if (!strncasecmp(cmdbuf,"FORG",4)) {
1068                 cmd_forg();
1069                 }
1070
1071         else if (!strncasecmp(cmdbuf,"MESG",4)) {
1072                 cmd_mesg(&cmdbuf[5]);
1073                 }
1074
1075         else if (!strncasecmp(cmdbuf,"EMSG",4)) {
1076                 cmd_emsg(&cmdbuf[5]);
1077                 }
1078
1079         else if (!strncasecmp(cmdbuf,"GNUR",4)) {
1080                 cmd_gnur();
1081                 }
1082
1083         else if (!strncasecmp(cmdbuf,"VALI",4)) {
1084                 cmd_vali(&cmdbuf[5]);
1085                 }
1086
1087         else if (!strncasecmp(cmdbuf,"EINF",4)) {
1088                 cmd_einf(&cmdbuf[5]);
1089                 }
1090
1091         else if (!strncasecmp(cmdbuf,"LIST",4)) {
1092                 cmd_list();
1093                 }
1094
1095         else if (!strncasecmp(cmdbuf,"CHEK",4)) {
1096                 cmd_chek();
1097                 }
1098
1099         else if (!strncasecmp(cmdbuf,"DELF",4)) {
1100                 cmd_delf(&cmdbuf[5]);
1101                 }
1102
1103         else if (!strncasecmp(cmdbuf,"MOVF",4)) {
1104                 cmd_movf(&cmdbuf[5]);
1105                 }
1106
1107         else if (!strncasecmp(cmdbuf,"NETF",4)) {
1108                 cmd_netf(&cmdbuf[5]);
1109                 }
1110
1111         else if (!strncasecmp(cmdbuf,"OPEN",4)) {
1112                 cmd_open(&cmdbuf[5]);
1113                 }
1114
1115         else if (!strncasecmp(cmdbuf,"CLOS",4)) {
1116                 cmd_clos();
1117                 }
1118
1119         else if (!strncasecmp(cmdbuf,"UOPN",4)) {
1120                 cmd_uopn(&cmdbuf[5]);
1121                 }
1122
1123         else if (!strncasecmp(cmdbuf,"UCLS",4)) {
1124                 cmd_ucls(&cmdbuf[5]);
1125                 }
1126
1127         else if (!strncasecmp(cmdbuf,"READ",4)) {
1128                 cmd_read(&cmdbuf[5]);
1129                 }
1130
1131         else if (!strncasecmp(cmdbuf,"WRIT",4)) {
1132                 cmd_writ(&cmdbuf[5]);
1133                 }
1134
1135         else if (!strncasecmp(cmdbuf,"QUSR",4)) {
1136                 cmd_qusr(&cmdbuf[5]);
1137                 }
1138
1139         else if (!strncasecmp(cmdbuf,"ECHO",4)) {
1140                 cmd_echo(&cmdbuf[5]);
1141                 }
1142
1143         else if (!strncasecmp(cmdbuf,"OIMG",4)) {
1144                 cmd_oimg(&cmdbuf[5]);
1145                 }
1146
1147         else if (!strncasecmp(cmdbuf,"MORE",4)) {
1148                 cmd_more();
1149                 }
1150
1151         else if (!strncasecmp(cmdbuf,"NDOP",4)) {
1152                 cmd_ndop(&cmdbuf[5]);
1153                 }
1154
1155         else if (!strncasecmp(cmdbuf,"NUOP",4)) {
1156                 cmd_nuop(&cmdbuf[5]);
1157                 }
1158
1159         else if (!strncasecmp(cmdbuf,"LFLR",4)) {
1160                 cmd_lflr();
1161                 }
1162
1163         else if (!strncasecmp(cmdbuf,"CFLR",4)) {
1164                 cmd_cflr(&cmdbuf[5]);
1165                 }
1166
1167         else if (!strncasecmp(cmdbuf,"KFLR",4)) {
1168                 cmd_kflr(&cmdbuf[5]);
1169                 }
1170
1171         else if (!strncasecmp(cmdbuf,"EFLR",4)) {
1172                 cmd_eflr(&cmdbuf[5]);
1173                 }
1174
1175         else if (!strncasecmp(cmdbuf,"IDEN",4)) {
1176                 cmd_iden(&cmdbuf[5]);
1177                 }
1178
1179         else if (!strncasecmp(cmdbuf,"IPGM",4)) {
1180                 cmd_ipgm(&cmdbuf[5]);
1181                 }
1182
1183         else if (!strncasecmp(cmdbuf,"TERM",4)) {
1184                 cmd_term(&cmdbuf[5]);
1185                 }
1186
1187         else if (!strncasecmp(cmdbuf,"DOWN",4)) {
1188                 cmd_down();
1189                 }
1190
1191         else if (!strncasecmp(cmdbuf,"SCDN",4)) {
1192                 cmd_scdn(&cmdbuf[5]);
1193                 }
1194
1195         else if (!strncasecmp(cmdbuf, "UIMG", 4)) {
1196                 cmd_uimg(&cmdbuf[5]);
1197                 }
1198
1199         else if (!strncasecmp(cmdbuf, "TIME", 4)) {
1200                 cmd_time();
1201                 }
1202
1203         else if (!strncasecmp(cmdbuf, "AGUP", 4)) {
1204                 cmd_agup(&cmdbuf[5]);
1205                 }
1206
1207         else if (!strncasecmp(cmdbuf, "ASUP", 4)) {
1208                 cmd_asup(&cmdbuf[5]);
1209                 }
1210
1211         else if (!strncasecmp(cmdbuf, "GPEX", 4)) {
1212                 cmd_gpex(&cmdbuf[5]);
1213                 }
1214
1215         else if (!strncasecmp(cmdbuf, "SPEX", 4)) {
1216                 cmd_spex(&cmdbuf[5]);
1217                 }
1218
1219         else if (!strncasecmp(cmdbuf, "CONF", 4)) {
1220                 cmd_conf(&cmdbuf[5]);
1221                 }
1222
1223         else if (!strncasecmp(cmdbuf, "SEEN", 4)) {
1224                 cmd_seen(&cmdbuf[5]);
1225                 }
1226
1227 #ifdef DEBUG_MEMORY_LEAKS
1228         else if (!strncasecmp(cmdbuf, "LEAK", 4)) {
1229                 dump_tracked();
1230                 }
1231 #endif
1232
1233         else if (!DLoader_Exec_Cmd(cmdbuf))
1234                 {
1235                    cprintf("%d Unrecognized or unsupported command.\n",
1236                             ERROR);
1237                 }
1238
1239         /* Run any after-each-command outines registered by modules */
1240         PerformSessionHooks(EVT_CMD);
1241 }