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