Back to index

citadel  8.12
citserver.c
Go to the documentation of this file.
00001 /* 
00002  * Main source module for the Citadel server
00003  *
00004  * Copyright (c) 1987-2011 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019  */
00020 
00021 #include "sysdep.h"
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <stdio.h>
00025 #include <fcntl.h>
00026 #include <signal.h>
00027 #include <sys/types.h>
00028 #include <sys/stat.h>
00029 
00030 #if TIME_WITH_SYS_TIME
00031 # include <sys/time.h>
00032 # include <time.h>
00033 #else
00034 # if HAVE_SYS_TIME_H
00035 #  include <sys/time.h>
00036 # else
00037 #  include <time.h>
00038 # endif
00039 #endif
00040 
00041 #if HAVE_BACKTRACE
00042 #include <execinfo.h>
00043 #endif
00044 
00045 #include <ctype.h>
00046 #include <string.h>
00047 #include <dirent.h>
00048 #include <errno.h>
00049 #include <limits.h>
00050 #include <netdb.h>
00051 #include <sys/types.h>
00052 #include <sys/socket.h>
00053 #include <netinet/in.h>
00054 #include <arpa/inet.h>
00055 #include <libcitadel.h>
00056 #include "citadel.h"
00057 #include "server.h"
00058 #include "sysdep_decls.h"
00059 #include "threads.h"
00060 #include "citserver.h"
00061 #include "config.h"
00062 #include "database.h"
00063 #include "housekeeping.h"
00064 #include "user_ops.h"
00065 #include "msgbase.h"
00066 #include "support.h"
00067 #include "locate_host.h"
00068 #include "room_ops.h"
00069 #include "file_ops.h"
00070 #include "control.h"
00071 #include "euidindex.h"
00072 #include "context.h"
00073 #include "svn_revision.h"
00074 
00075 #ifndef HAVE_SNPRINTF
00076 #include "snprintf.h"
00077 #endif
00078 
00079 #include "ctdl_module.h"
00080 
00081 char *unique_session_numbers;
00082 int ScheduledShutdown = 0;
00083 time_t server_startup_time;
00084 int panic_fd;
00085 int openid_level_supported = 0;
00086 
00087 /*
00088  * print the actual stack frame.
00089  */
00090 void cit_backtrace(void)
00091 {
00092 #ifdef HAVE_BACKTRACE
00093        void *stack_frames[50];
00094        size_t size, i;
00095        char **strings;
00096 
00097 
00098        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00099        strings = backtrace_symbols(stack_frames, size);
00100        for (i = 0; i < size; i++) {
00101               if (strings != NULL)
00102                      syslog(LOG_ALERT, "%s\n", strings[i]);
00103               else
00104                      syslog(LOG_ALERT, "%p\n", stack_frames[i]);
00105        }
00106        free(strings);
00107 #endif
00108 }
00109 
00110 void cit_oneline_backtrace(void)
00111 {
00112 #ifdef HAVE_BACKTRACE
00113        void *stack_frames[50];
00114        size_t size, i;
00115        char **strings;
00116        StrBuf *Buf;
00117 
00118        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00119        strings = backtrace_symbols(stack_frames, size);
00120        if (size > 0)
00121        {
00122               Buf = NewStrBuf();
00123               for (i = 1; i < size; i++) {
00124                      if (strings != NULL)
00125                             StrBufAppendPrintf(Buf, "%s : ", strings[i]);
00126                      else
00127                             StrBufAppendPrintf(Buf, "%p : ", stack_frames[i]);
00128               }
00129               free(strings);
00130               syslog(LOG_ALERT, "%s\n", ChrPtr(Buf));
00131               FreeStrBuf(&Buf);
00132        }
00133 #endif
00134 }
00135 
00136 /*
00137  * print the actual stack frame.
00138  */
00139 void cit_panic_backtrace(int SigNum)
00140 {
00141 #ifdef HAVE_BACKTRACE
00142        void *stack_frames[10];
00143        size_t size, i;
00144        char **strings;
00145 
00146        printf("caught signal 11\n");
00147        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00148        strings = backtrace_symbols(stack_frames, size);
00149        for (i = 0; i < size; i++) {
00150               if (strings != NULL)
00151                      syslog(LOG_ALERT, "%s\n", strings[i]);
00152               else
00153                      syslog(LOG_ALERT, "%p\n", stack_frames[i]);
00154        }
00155        free(strings);
00156 #endif
00157        exit(-1);
00158 }
00159 
00160 /*
00161  * Various things that need to be initialized at startup
00162  */
00163 void master_startup(void) {
00164        struct timeval tv;
00165        unsigned int seed;
00166        FILE *urandom;
00167        struct ctdlroom qrbuf;
00168        int rv;
00169        
00170        syslog(LOG_DEBUG, "master_startup() started\n");
00171        time(&server_startup_time);
00172        get_config();
00173 
00174        syslog(LOG_INFO, "Opening databases\n");
00175        open_databases();
00176        check_ref_counts();
00177 
00178        syslog(LOG_INFO, "Creating base rooms (if necessary)\n");
00179        CtdlCreateRoom(config.c_baseroom,  0, "", 0, 1, 0, VIEW_BBS);
00180        CtdlCreateRoom(AIDEROOM,           3, "", 0, 1, 0, VIEW_BBS);
00181        CtdlCreateRoom(SYSCONFIGROOM,             3, "", 0, 1, 0, VIEW_BBS);
00182        CtdlCreateRoom(config.c_twitroom,  0, "", 0, 1, 0, VIEW_BBS);
00183 
00184        /* The "Local System Configuration" room doesn't need to be visible */
00185         if (CtdlGetRoomLock(&qrbuf, SYSCONFIGROOM) == 0) {
00186                 qrbuf.QRflags2 |= QR2_SYSTEM;
00187                 CtdlPutRoomLock(&qrbuf);
00188         }
00189 
00190        /* Aide needs to be public postable, else we're not RFC conformant. */
00191         if (CtdlGetRoomLock(&qrbuf, AIDEROOM) == 0) {
00192                 qrbuf.QRflags2 |= QR2_SMTP_PUBLIC;
00193                 CtdlPutRoomLock(&qrbuf);
00194         }
00195 
00196        syslog(LOG_INFO, "Seeding the pseudo-random number generator...\n");
00197        urandom = fopen("/dev/urandom", "r");
00198        if (urandom != NULL) {
00199               rv = fread(&seed, sizeof seed, 1, urandom);
00200               if (rv == -1)
00201                      syslog(LOG_EMERG, "failed to read random seed: %s\n", 
00202                             strerror(errno));
00203               fclose(urandom);
00204        }
00205        else {
00206               gettimeofday(&tv, NULL);
00207               seed = tv.tv_usec;
00208        }
00209        srand(seed);
00210        srandom(seed);
00211 
00212        put_config();
00213 
00214        syslog(LOG_DEBUG, "master_startup() finished\n");
00215 }
00216 
00217 
00218 /*
00219  * Cleanup routine to be called when the server is shutting down.
00220  */
00221 void master_cleanup(int exitcode) {
00222        struct CleanupFunctionHook *fcn;
00223        static int already_cleaning_up = 0;
00224 
00225        if (already_cleaning_up) while(1) usleep(1000);
00226        already_cleaning_up = 1;
00227 
00228        /* Run any cleanup routines registered by loadable modules */
00229        for (fcn = CleanupHookTable; fcn != NULL; fcn = fcn->next) {
00230               (*fcn->h_function_pointer)();
00231        }
00232 
00233        /* Close the AdjRefCount queue file */
00234        AdjRefCount(-1, 0);
00235 
00236        /* Do system-dependent stuff */
00237        sysdep_master_cleanup();
00238        
00239        /* Close databases */
00240        syslog(LOG_INFO, "Closing databases\n");
00241        close_databases();
00242 
00243        /* If the operator requested a halt but not an exit, halt here. */
00244        if (shutdown_and_halt) {
00245               syslog(LOG_NOTICE, "citserver: Halting server without exiting.\n");
00246               fflush(stdout); fflush(stderr);
00247               while(1) {
00248                      sleep(32767);
00249               }
00250        }
00251        
00252        release_control();
00253 
00254        /* Now go away. */
00255        syslog(LOG_NOTICE, "citserver: Exiting with status %d\n", exitcode);
00256        fflush(stdout); fflush(stderr);
00257        
00258        if (restart_server != 0)
00259               exit(1);
00260        if ((running_as_daemon != 0) && ((exitcode == 0) ))
00261               exitcode = CTDLEXIT_SHUTDOWN;
00262        exit(exitcode);
00263 }
00264 
00265 
00266 
00267 /*
00268  * cmd_info()  -  tell the client about this server
00269  */
00270 void cmd_info(char *cmdbuf) {
00271        cprintf("%d Server info:\n", LISTING_FOLLOWS);
00272        cprintf("%d\n", CC->cs_pid);
00273        cprintf("%s\n", config.c_nodename);
00274        cprintf("%s\n", config.c_humannode);
00275        cprintf("%s\n", config.c_fqdn);
00276        cprintf("%s\n", CITADEL);
00277        cprintf("%d\n", REV_LEVEL);
00278        cprintf("%s\n", config.c_site_location);
00279        cprintf("%s\n", config.c_sysadm);
00280        cprintf("%d\n", SERVER_TYPE);
00281        cprintf("%s\n", config.c_moreprompt);
00282        cprintf("1\n");      /* 1 = yes, this system supports floors */
00283        cprintf("1\n"); /* 1 = we support the extended paging options */
00284        cprintf("\n");       /* nonce no longer supported */
00285        cprintf("1\n"); /* 1 = yes, this system supports the QNOP command */
00286 
00287 #ifdef HAVE_LDAP
00288        cprintf("1\n"); /* 1 = yes, this server is LDAP-enabled */
00289 #else
00290        cprintf("0\n"); /* 1 = no, this server is not LDAP-enabled */
00291 #endif
00292 
00293        if (config.c_auth_mode == AUTHMODE_NATIVE) {
00294               cprintf("%d\n", config.c_disable_newu);
00295        }
00296        else {
00297               cprintf("1\n");      /* "create new user" does not work with non-native auth modes */
00298        }
00299 
00300        cprintf("%s\n", config.c_default_cal_zone);
00301 
00302        /* thread load averages -- temporarily disabled during refactoring of this code */
00303        cprintf("0\n");             /* load average */
00304        cprintf("0\n");             /* worker average */
00305        cprintf("0\n");             /* thread count */
00306 
00307        cprintf("1\n");             /* yes, Sieve mail filtering is supported */
00308        cprintf("%d\n", config.c_enable_fulltext);
00309        cprintf("%s\n", svn_revision());
00310 
00311        if (config.c_auth_mode == AUTHMODE_NATIVE) {
00312               cprintf("%d\n", openid_level_supported); /* OpenID is enabled when using native auth */
00313        }
00314        else {
00315               cprintf("0\n");      /* OpenID is disabled when using non-native auth */
00316        }
00317 
00318        cprintf("%d\n", config.c_guest_logins);
00319        
00320        cprintf("000\n");
00321 }
00322 
00323 
00324 /*
00325  * returns an asterisk if there are any instant messages waiting,
00326  * space otherwise.
00327  */
00328 char CtdlCheckExpress(void) {
00329        if (CC->FirstExpressMessage == NULL) {
00330               return(' ');
00331        }
00332        else {
00333               return('*');
00334        }
00335 }
00336 
00337 void cmd_time(char *argbuf)
00338 {
00339    time_t tv;
00340    struct tm tmp;
00341    
00342    tv = time(NULL);
00343    localtime_r(&tv, &tmp);
00344    
00345    /* timezone and daylight global variables are not portable. */
00346 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
00347    cprintf("%d %ld|%ld|%d|%ld\n", CIT_OK, (long)tv, tmp.tm_gmtoff, tmp.tm_isdst, server_startup_time);
00348 #else
00349    cprintf("%d %ld|%ld|%d|%ld\n", CIT_OK, (long)tv, timezone, tmp.tm_isdst, server_startup_time);
00350 #endif
00351 }
00352 
00353 
00354 /*
00355  * Check originating host against the public_clients file.  This determines
00356  * whether the client is allowed to change the hostname for this session
00357  * (for example, to show the location of the user rather than the location
00358  * of the client).
00359  */
00360 int is_public_client(void)
00361 {
00362        char buf[1024];
00363        char addrbuf[1024];
00364        FILE *fp;
00365        int i;
00366        char *public_clientspos;
00367        char *public_clientsend;
00368        char *paddr = NULL;
00369        struct stat statbuf;
00370        static time_t pc_timestamp = 0;
00371        static char public_clients[SIZ];
00372        static char public_clients_file[SIZ];
00373 
00374 #define LOCALHOSTSTR "127.0.0.1"
00375 
00376        snprintf(public_clients_file, sizeof public_clients_file, "%s/public_clients", ctdl_etc_dir);
00377 
00378        /*
00379         * Check the time stamp on the public_clients file.  If it's been
00380         * updated since the last time we were here (or if this is the first
00381         * time we've been through the loop), read its contents and learn
00382         * the IP addresses of the listed hosts.
00383         */
00384        if (stat(public_clients_file, &statbuf) != 0) {
00385               /* No public_clients file exists, so bail out */
00386               syslog(LOG_WARNING, "Warning: '%s' does not exist\n", 
00387                             public_clients_file);
00388               return(0);
00389        }
00390 
00391        if (statbuf.st_mtime > pc_timestamp) {
00392               begin_critical_section(S_PUBLIC_CLIENTS);
00393               syslog(LOG_INFO, "Loading %s\n", public_clients_file);
00394 
00395               public_clientspos = &public_clients[0];
00396               public_clientsend = public_clientspos + SIZ;
00397               safestrncpy(public_clientspos, LOCALHOSTSTR, sizeof public_clients);
00398               public_clientspos += sizeof(LOCALHOSTSTR) - 1;
00399               
00400               if (hostname_to_dotted_quad(addrbuf, config.c_fqdn) == 0) {
00401                      *(public_clientspos++) = '|';
00402                      paddr = &addrbuf[0];
00403                      while (!IsEmptyStr (paddr) && 
00404                             (public_clientspos < public_clientsend))
00405                             *(public_clientspos++) = *(paddr++);
00406               }
00407 
00408               fp = fopen(public_clients_file, "r");
00409               if (fp != NULL) 
00410                      while ((fgets(buf, sizeof buf, fp)!=NULL) &&
00411                             (public_clientspos < public_clientsend)){
00412                             char *ptr;
00413                             ptr = buf;
00414                             while (!IsEmptyStr(ptr)) {
00415                                    if (*ptr == '#') {
00416                                           *ptr = 0;
00417                                           break;
00418                                    }
00419                             else ptr++;
00420                             }
00421                             ptr--;
00422                             while (ptr>buf && isspace(*ptr)) {
00423                                    *(ptr--) = 0;
00424                             }
00425                             if (hostname_to_dotted_quad(addrbuf, buf) == 0) {
00426                                    *(public_clientspos++) = '|';
00427                                    paddr = addrbuf;
00428                                    while (!IsEmptyStr(paddr) && 
00429                                           (public_clientspos < public_clientsend)){
00430                                           *(public_clientspos++) = *(paddr++);
00431                                    }
00432                             }
00433                      }
00434               fclose(fp);
00435               pc_timestamp = time(NULL);
00436               end_critical_section(S_PUBLIC_CLIENTS);
00437        }
00438 
00439        syslog(LOG_DEBUG, "Checking whether %s is a local or public client\n",
00440               CC->cs_addr);
00441        for (i=0; i<num_parms(public_clients); ++i) {
00442               extract_token(addrbuf, public_clients, i, '|', sizeof addrbuf);
00443               if (!strcasecmp(CC->cs_addr, addrbuf)) {
00444                      syslog(LOG_DEBUG, "... yes its local.\n");
00445                      return(1);
00446               }
00447        }
00448 
00449        /* No hits.  This is not a public client. */
00450        syslog(LOG_DEBUG, "... no it isn't.\n");
00451        return(0);
00452 }
00453 
00454 
00455 /*
00456  * the client is identifying itself to the server
00457  */
00458 void cmd_iden(char *argbuf)
00459 {
00460        int dev_code;
00461        int cli_code;
00462        int rev_level;
00463        char desc[128];
00464        char from_host[128];
00465 
00466        if (num_parms(argbuf)<4) {
00467               cprintf("%d usage error\n", ERROR + ILLEGAL_VALUE);
00468               return;
00469        }
00470 
00471        dev_code = extract_int(argbuf,0);
00472        cli_code = extract_int(argbuf,1);
00473        rev_level = extract_int(argbuf,2);
00474        extract_token(desc, argbuf, 3, '|', sizeof desc);
00475 
00476        safestrncpy(from_host, config.c_fqdn, sizeof from_host);
00477        from_host[sizeof from_host - 1] = 0;
00478        if (num_parms(argbuf)>=5) extract_token(from_host, argbuf, 4, '|', sizeof from_host);
00479 
00480        CC->cs_clientdev = dev_code;
00481        CC->cs_clienttyp = cli_code;
00482        CC->cs_clientver = rev_level;
00483        safestrncpy(CC->cs_clientname, desc, sizeof CC->cs_clientname);
00484        CC->cs_clientname[31] = 0;
00485 
00486        /* For local sockets and public clients, trust the hostname supplied by the client */
00487        if ( (CC->is_local_socket) || (is_public_client()) ) {
00488               safestrncpy(CC->cs_host, from_host, sizeof CC->cs_host);
00489               CC->cs_host[sizeof CC->cs_host - 1] = 0;
00490               CC->cs_addr[0] = 0;
00491        }
00492 
00493        syslog(LOG_NOTICE, "Client %d/%d/%01d.%02d (%s) from %s\n",
00494               dev_code,
00495               cli_code,
00496               (rev_level / 100),
00497               (rev_level % 100),
00498               desc,
00499               CC->cs_host
00500        );
00501        cprintf("%d Ok\n",CIT_OK);
00502 }
00503 
00504 typedef const char *ccharp;
00505 /*
00506  * display system messages or help
00507  */
00508 void cmd_mesg(char *mname)
00509 {
00510        FILE *mfp;
00511        char targ[256];
00512        char buf[256];
00513        char buf2[256];
00514        char *dirs[2];
00515        DIR *dp;
00516        struct dirent *d;
00517 
00518        extract_token(buf, mname, 0, '|', sizeof buf);
00519 
00520        dirs[0] = strdup(ctdl_message_dir);
00521        dirs[1] = strdup(ctdl_hlp_dir);
00522 
00523        snprintf(buf2, sizeof buf2, "%s.%d.%d",
00524               buf, CC->cs_clientdev, CC->cs_clienttyp);
00525 
00526        /* If the client requested "?" then produce a listing */
00527        if (!strcmp(buf, "?")) {
00528               cprintf("%d %s\n", LISTING_FOLLOWS, buf);
00529               dp = opendir(dirs[1]);
00530               if (dp != NULL) {
00531                      while (d = readdir(dp), d != NULL) {
00532                             if (d->d_name[0] != '.') {
00533                                    cprintf(" %s\n", d->d_name);
00534                             }
00535                      }
00536                      closedir(dp);
00537               }
00538               cprintf("000\n");
00539               free(dirs[0]);
00540               free(dirs[1]);
00541               return;
00542        }
00543 
00544        /* Otherwise, look for the requested file by name. */
00545        else {
00546               mesg_locate(targ, sizeof targ, buf2, 2, (const ccharp*)dirs);
00547               if (IsEmptyStr(targ)) {
00548                      snprintf(buf2, sizeof buf2, "%s.%d",
00549                                                  buf, CC->cs_clientdev);
00550                      mesg_locate(targ, sizeof targ, buf2, 2,
00551                                 (const ccharp*)dirs);
00552                      if (IsEmptyStr(targ)) {
00553                             mesg_locate(targ, sizeof targ, buf, 2,
00554                                        (const ccharp*)dirs);
00555                      }      
00556               }
00557        }
00558 
00559        free(dirs[0]);
00560        free(dirs[1]);
00561 
00562        if (IsEmptyStr(targ)) {
00563               cprintf("%d '%s' not found.  (Searching in %s and %s)\n",
00564                      ERROR + FILE_NOT_FOUND,
00565                      mname,
00566                      ctdl_message_dir,
00567                      ctdl_hlp_dir
00568               );
00569               return;
00570        }
00571 
00572        mfp = fopen(targ, "r");
00573        if (mfp==NULL) {
00574               cprintf("%d Cannot open '%s': %s\n",
00575                      ERROR + INTERNAL_ERROR, targ, strerror(errno));
00576               return;
00577        }
00578        cprintf("%d %s\n", LISTING_FOLLOWS,buf);
00579 
00580        while (fgets(buf, (sizeof buf - 1), mfp) != NULL) {
00581               buf[strlen(buf)-1] = 0;
00582               do_help_subst(buf);
00583               cprintf("%s\n",buf);
00584        }
00585 
00586        fclose(mfp);
00587        cprintf("000\n");
00588 }
00589 
00590 
00591 /*
00592  * enter system messages or help
00593  */
00594 void cmd_emsg(char *mname)
00595 {
00596        FILE *mfp;
00597        char targ[256];
00598        char buf[256];
00599        char *dirs[2];
00600        int a;
00601 
00602        unbuffer_output();
00603 
00604        if (CtdlAccessCheck(ac_aide)) return;
00605 
00606        extract_token(buf, mname, 0, '|', sizeof buf);
00607        for (a=0; !IsEmptyStr(&buf[a]); ++a) {           /* security measure */
00608               if (buf[a] == '/') buf[a] = '.';
00609        }
00610 
00611        dirs[0] = strdup(ctdl_message_dir);
00612        dirs[1] = strdup(ctdl_hlp_dir);
00613 
00614        mesg_locate(targ, sizeof targ, buf, 2, (const ccharp*)dirs);
00615        free(dirs[0]);
00616        free(dirs[1]);
00617 
00618        if (IsEmptyStr(targ)) {
00619               snprintf(targ, sizeof targ, 
00620                              "%s/%s",
00621                              ctdl_hlp_dir, buf);
00622        }
00623 
00624        mfp = fopen(targ,"w");
00625        if (mfp==NULL) {
00626               cprintf("%d Cannot open '%s': %s\n",
00627                      ERROR + INTERNAL_ERROR, targ, strerror(errno));
00628               return;
00629        }
00630        cprintf("%d %s\n", SEND_LISTING, targ);
00631 
00632        while (client_getln(buf, sizeof buf) >=0 && strcmp(buf, "000")) {
00633               fprintf(mfp, "%s\n", buf);
00634        }
00635 
00636        fclose(mfp);
00637 }
00638 
00639 
00640 /* Don't show the names of private rooms unless the viewing
00641  * user also knows the rooms.
00642  */
00643 void GenerateRoomDisplay(char *real_room,
00644                      CitContext *viewed,
00645                      CitContext *viewer) {
00646 
00647        int ra;
00648 
00649        strcpy(real_room, viewed->room.QRname);
00650        if (viewed->room.QRflags & QR_MAILBOX) {
00651               strcpy(real_room, &real_room[11]);
00652        }
00653        if (viewed->room.QRflags & QR_PRIVATE) {
00654               CtdlRoomAccess(&viewed->room, &viewer->user, &ra, NULL);
00655               if ( (ra & UA_KNOWN) == 0) {
00656                      strcpy(real_room, " ");
00657               }
00658        }
00659 
00660        if (viewed->cs_flags & CS_CHAT) {
00661               while (strlen(real_room) < 14) {
00662                      strcat(real_room, " ");
00663               }
00664               strcpy(&real_room[14], "<chat>");
00665        }
00666 
00667 }
00668 
00669 /*
00670  * Convenience function.
00671  */
00672 int CtdlAccessCheck(int required_level) {
00673 
00674        if (CC->internal_pgm) return(0);
00675        if (required_level >= ac_internal) {
00676               cprintf("%d This is not a user-level command.\n",
00677                      ERROR + HIGHER_ACCESS_REQUIRED);
00678               return(-1);
00679        }
00680 
00681        if ((required_level >= ac_logged_in_or_guest) && (CC->logged_in == 0) && (!config.c_guest_logins)) {
00682               cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
00683               return(-1);
00684        }
00685 
00686        if ((required_level >= ac_logged_in) && (CC->logged_in == 0)) {
00687               cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
00688               return(-1);
00689        }
00690 
00691        if (CC->user.axlevel >= AxAideU) return(0);
00692        if (required_level >= ac_aide) {
00693               cprintf("%d This command requires Aide access.\n",
00694                      ERROR + HIGHER_ACCESS_REQUIRED);
00695               return(-1);
00696        }
00697 
00698        if (is_room_aide()) return(0);
00699        if (required_level >= ac_room_aide) {
00700               cprintf("%d This command requires Aide or Room Aide access.\n",
00701                      ERROR + HIGHER_ACCESS_REQUIRED);
00702               return(-1);
00703        }
00704 
00705        /* shhh ... succeed quietly */
00706        return(0);
00707 }
00708 
00709 
00710 
00711 /*
00712  * Terminate another running session
00713  */
00714 void cmd_term(char *cmdbuf)
00715 {
00716        int session_num;
00717        int terminated = 0;
00718 
00719        session_num = extract_int(cmdbuf, 0);
00720 
00721        terminated = CtdlTerminateOtherSession(session_num);
00722 
00723        if (terminated < 0) {
00724               cprintf("%d You can't kill your own session.\n", ERROR + ILLEGAL_VALUE);
00725               return;
00726        }
00727 
00728        if (terminated & TERM_FOUND) {
00729               if (terminated == TERM_KILLED) {
00730                      cprintf("%d Session terminated.\n", CIT_OK);
00731               }
00732               else {
00733                      cprintf("%d You are not allowed to do that.\n",
00734                             ERROR + HIGHER_ACCESS_REQUIRED);
00735               }
00736        }
00737        else {
00738               cprintf("%d No such session.\n", ERROR + ILLEGAL_VALUE);
00739        }
00740 }
00741 
00742 
00743 /* 
00744  * get the paginator prompt
00745  */
00746 void cmd_more(char *argbuf) {
00747        cprintf("%d %s\n", CIT_OK, config.c_moreprompt);
00748 }
00749 
00750 
00751 /*
00752  * echo 
00753  */
00754 void cmd_echo(char *etext)
00755 {
00756        cprintf("%d %s\n", CIT_OK, etext);
00757 }
00758 
00759 
00760 
00761 /*
00762  * Shut down the server
00763  */
00764 void cmd_down(char *argbuf) {
00765        char *Reply ="%d Shutting down server.  Goodbye.\n";
00766 
00767        if (CtdlAccessCheck(ac_aide)) return;
00768 
00769        if (!IsEmptyStr(argbuf))
00770        {
00771               int state = CIT_OK;
00772               restart_server = extract_int(argbuf, 0);
00773               
00774               if (restart_server > 0)
00775               {
00776                      Reply = "%d citserver will now shut down and automatically restart.\n";
00777               }
00778               if ((restart_server > 0) && !running_as_daemon)
00779               {
00780                      syslog(LOG_ERR, "The user requested restart, but not running as daemon! Geronimooooooo!\n");
00781                      Reply = "%d Warning: citserver is not running in daemon mode and is therefore unlikely to restart automatically.\n";
00782                      state = ERROR;
00783               }
00784               cprintf(Reply, state);
00785        }
00786        else
00787        {
00788               cprintf(Reply, CIT_OK + SERVER_SHUTTING_DOWN); 
00789        }
00790        CC->kill_me = KILLME_SERVER_SHUTTING_DOWN;
00791        server_shutting_down = 1;
00792 }
00793 
00794 
00795 /*
00796  * Halt the server without exiting the server process.
00797  */
00798 void cmd_halt(char *argbuf) {
00799 
00800        if (CtdlAccessCheck(ac_aide)) return;
00801 
00802        cprintf("%d Halting server.  Goodbye.\n", CIT_OK);
00803        server_shutting_down = 1;
00804        shutdown_and_halt = 1;
00805 }
00806 
00807 
00808 /*
00809  * Schedule or cancel a server shutdown
00810  */
00811 void cmd_scdn(char *argbuf)
00812 {
00813        int new_state;
00814        int state = CIT_OK;
00815        char *Reply = "%d %d\n";
00816 
00817        if (CtdlAccessCheck(ac_aide)) return;
00818 
00819        new_state = extract_int(argbuf, 0);
00820        if ((new_state == 2) || (new_state == 3))
00821        {
00822               restart_server = 1;
00823               if (!running_as_daemon)
00824               {
00825                      syslog(LOG_ERR, "The user requested restart, but not running as deamon! Geronimooooooo!\n");
00826                      Reply = "%d %d Warning, not running in deamon mode. maybe we will come up again, but don't lean on it.\n";
00827                      state = ERROR;
00828               }
00829 
00830               restart_server = extract_int(argbuf, 0);
00831               new_state -= 2;
00832        }
00833        if ((new_state == 0) || (new_state == 1)) {
00834               ScheduledShutdown = new_state;
00835        }
00836        cprintf(Reply, state, ScheduledShutdown);
00837 }
00838 
00839 
00840 /*
00841  * Set or unset asynchronous protocol mode
00842  */
00843 void cmd_asyn(char *argbuf)
00844 {
00845        int new_state;
00846 
00847        new_state = extract_int(argbuf, 0);
00848        if ((new_state == 0) || (new_state == 1)) {
00849               CC->is_async = new_state;
00850        }
00851        cprintf("%d %d\n", CIT_OK, CC->is_async);
00852 }
00853 
00854 
00855 
00856 /*
00857  * Back-end function for starting a session
00858  */
00859 void begin_session(CitContext *con)
00860 {
00861        /* 
00862         * Initialize some variables specific to our context.
00863         */
00864        con->logged_in = 0;
00865        con->internal_pgm = 0;
00866        con->download_fp = NULL;
00867        con->upload_fp = NULL;
00868        con->cached_msglist = NULL;
00869        con->cached_num_msgs = 0;
00870        con->FirstExpressMessage = NULL;
00871        time(&con->lastcmd);
00872        time(&con->lastidle);
00873        strcpy(con->lastcmdname, "    ");
00874        strcpy(con->cs_clientname, "(unknown)");
00875        strcpy(con->curr_user, NLI);
00876        *con->net_node = '\0';
00877        *con->fake_username = '\0';
00878        *con->fake_hostname = '\0';
00879        *con->fake_roomname = '\0';
00880        *con->cs_clientinfo = '\0';
00881        safestrncpy(con->cs_host, config.c_fqdn, sizeof con->cs_host);
00882        safestrncpy(con->cs_addr, "", sizeof con->cs_addr);
00883        con->cs_UDSclientUID = -1;
00884        con->cs_host[sizeof con->cs_host - 1] = 0;
00885        if (!CC->is_local_socket) {
00886               locate_host(con->cs_host, sizeof con->cs_host,
00887                      con->cs_addr, sizeof con->cs_addr,
00888                      con->client_socket
00889               );
00890        }
00891        else {
00892               con->cs_host[0] = 0;
00893               con->cs_addr[0] = 0;
00894 #ifdef HAVE_STRUCT_UCRED
00895               {
00896                      /* as http://www.wsinnovations.com/softeng/articles/uds.html told us... */
00897                      struct ucred credentials;
00898                      socklen_t ucred_length = sizeof(struct ucred);
00899                      
00900                      /*fill in the user data structure */
00901                      if(getsockopt(con->client_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length)) {
00902                             syslog(LOG_NOTICE, "could obtain credentials from unix domain socket");
00903                             
00904                      }
00905                      else {        
00906                             /* the process ID of the process on the other side of the socket */
00907                             /* credentials.pid; */
00908                             
00909                             /* the effective UID of the process on the other side of the socket  */
00910                             con->cs_UDSclientUID = credentials.uid;
00911                             
00912                             /* the effective primary GID of the process on the other side of the socket */
00913                             /* credentials.gid; */
00914                             
00915                             /* To get supplemental groups, we will have to look them up in our account
00916                                database, after a reverse lookup on the UID to get the account name.
00917                                We can take this opportunity to check to see if this is a legit account.
00918                             */
00919                             snprintf(con->cs_clientinfo, sizeof(con->cs_clientinfo),
00920                                     "PID: "F_PID_T"; UID: "F_UID_T"; GID: "F_XPID_T" ", 
00921                                     credentials.pid,
00922                                     credentials.uid,
00923                                     credentials.gid);
00924                      }
00925               }
00926 #endif
00927        }
00928        con->cs_flags = 0;
00929        con->upload_type = UPL_FILE;
00930        con->dl_is_net = 0;
00931 
00932        con->nologin = 0;
00933        if (((config.c_maxsessions > 0)&&(num_sessions > config.c_maxsessions)) || CtdlWantSingleUser()) {
00934               con->nologin = 1;
00935        }
00936 
00937        if (!CC->is_local_socket) {
00938               syslog(LOG_NOTICE, "Session (%s) started from %s (%s).\n", con->ServiceName, con->cs_host, con->cs_addr);
00939        }
00940        else {
00941               syslog(LOG_NOTICE, "Session (%s) started via local socket UID:%d.\n", con->ServiceName, con->cs_UDSclientUID);
00942        }
00943 
00944        /* Run any session startup routines registered by loadable modules */
00945        PerformSessionHooks(EVT_START);
00946 }
00947 
00948 
00949 void citproto_begin_session() {
00950        if (CC->nologin==1) {
00951               cprintf("%d %s: Too many users are already online (maximum is %d)\n",
00952                      ERROR + MAX_SESSIONS_EXCEEDED,
00953                      config.c_nodename, config.c_maxsessions
00954               );
00955               CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED;
00956        }
00957        else {
00958               cprintf("%d %s Citadel server ready.\n", CIT_OK, config.c_nodename);
00959               CC->can_receive_im = 1;
00960        }
00961 }
00962 
00963 
00964 void citproto_begin_admin_session() {
00965        CC->internal_pgm = 1;
00966        cprintf("%d %s Citadel server ADMIN CONNECTION ready.\n", CIT_OK, config.c_nodename);
00967 }
00968 
00969 
00970 void cmd_noop(char *argbuf)
00971 {
00972        cprintf("%d%cok\n", CIT_OK, CtdlCheckExpress() );
00973 }
00974 
00975 
00976 void cmd_qnop(char *argbuf)
00977 {
00978        /* do nothing, this command returns no response */
00979 }
00980 
00981 
00982 void cmd_quit(char *argbuf)
00983 {
00984        cprintf("%d Goodbye.\n", CIT_OK);
00985        CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
00986 }
00987 
00988 
00989 void cmd_lout(char *argbuf)
00990 {
00991        if (CC->logged_in) 
00992               CtdlUserLogout();
00993        cprintf("%d logged out.\n", CIT_OK);
00994 }
00995 
00996 
00997 /*
00998  * This loop recognizes all server commands.
00999  */
01000 void do_command_loop(void) {
01001        char cmdbuf[SIZ];
01002        
01003        time(&CC->lastcmd);
01004        memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
01005        if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
01006               syslog(LOG_ERR, "Citadel client disconnected: ending session.\n");
01007               CC->kill_me = KILLME_CLIENT_DISCONNECTED;
01008               return;
01009        }
01010 
01011        /* Log the server command, but don't show passwords... */
01012        if ( (strncasecmp(cmdbuf, "PASS", 4)) && (strncasecmp(cmdbuf, "SETP", 4)) ) {
01013               syslog(LOG_INFO, "[%d][%s(%ld)] %s",
01014                      CC->cs_pid, CC->curr_user, CC->user.usernum, cmdbuf
01015               );
01016        }
01017        else {
01018               syslog(LOG_INFO, "[%d][%s(%ld)] <password command hidden from log>",
01019                      CC->cs_pid, CC->curr_user, CC->user.usernum
01020               );
01021        }
01022 
01023        buffer_output();
01024 
01025        /*
01026         * Let other clients see the last command we executed, and
01027         * update the idle time, but not NOOP, QNOP, PEXP, GEXP, RWHO, or TIME.
01028         */
01029        if ( (strncasecmp(cmdbuf, "NOOP", 4))
01030           && (strncasecmp(cmdbuf, "QNOP", 4))
01031           && (strncasecmp(cmdbuf, "PEXP", 4))
01032           && (strncasecmp(cmdbuf, "GEXP", 4))
01033           && (strncasecmp(cmdbuf, "RWHO", 4))
01034           && (strncasecmp(cmdbuf, "TIME", 4)) ) {
01035               strcpy(CC->lastcmdname, "    ");
01036               safestrncpy(CC->lastcmdname, cmdbuf, sizeof(CC->lastcmdname));
01037               time(&CC->lastidle);
01038        }
01039        
01040        if ((strncasecmp(cmdbuf, "ENT0", 4))
01041           && (strncasecmp(cmdbuf, "MESG", 4))
01042           && (strncasecmp(cmdbuf, "MSGS", 4)))
01043        {
01044           CC->cs_flags &= ~CS_POSTING;
01045        }
01046                  
01047        if (!DLoader_Exec_Cmd(cmdbuf)) {
01048               cprintf("%d Unrecognized or unsupported command.\n", ERROR + CMD_NOT_SUPPORTED);
01049        }      
01050 
01051        unbuffer_output();
01052 
01053        /* Run any after-each-command routines registered by modules */
01054        PerformSessionHooks(EVT_CMD);
01055 }
01056 
01057 
01058 /*
01059  * This loop performs all asynchronous functions.
01060  */
01061 void do_async_loop(void) {
01062        PerformSessionHooks(EVT_ASYNC);
01063 }
01064 
01065 
01066 /*****************************************************************************/
01067 /*                      MODULE INITIALIZATION STUFF                          */
01068 /*****************************************************************************/
01069 
01070 CTDL_MODULE_INIT(citserver)
01071 {
01072        if (!threading) {
01073               CtdlRegisterProtoHook(cmd_noop, "NOOP", "no operation");
01074               CtdlRegisterProtoHook(cmd_qnop, "QNOP", "no operation with no response");
01075               CtdlRegisterProtoHook(cmd_quit, "QUIT", "log out and disconnect from server");
01076               CtdlRegisterProtoHook(cmd_lout, "LOUT", "log out but do not disconnect from server");
01077               CtdlRegisterProtoHook(cmd_asyn, "ASYN", "enable asynchronous server responses");
01078               CtdlRegisterProtoHook(cmd_info, "INFO", "fetch server capabilities and configuration");
01079               CtdlRegisterProtoHook(cmd_mesg, "MESG", "fetch system banners");
01080               CtdlRegisterProtoHook(cmd_emsg, "EMSG", "submit system banners");
01081               CtdlRegisterProtoHook(cmd_echo, "ECHO", "echo text back to the client");
01082               CtdlRegisterProtoHook(cmd_more, "MORE", "fetch the paginator prompt");
01083               CtdlRegisterProtoHook(cmd_iden, "IDEN", "identify the client software and location");
01084               CtdlRegisterProtoHook(cmd_term, "TERM", "terminate another running session");
01085               CtdlRegisterProtoHook(cmd_down, "DOWN", "perform a server shutdown");
01086               CtdlRegisterProtoHook(cmd_halt, "HALT", "halt the server without exiting the server process");
01087               CtdlRegisterProtoHook(cmd_scdn, "SCDN", "schedule or cancel a server shutdown");
01088               CtdlRegisterProtoHook(cmd_time, "TIME", "fetch the date and time from the server");
01089        }
01090         /* return our id for the Log */
01091        return "citserver";
01092 }