From: Art Cancro Date: Sat, 4 Jun 2022 23:11:58 +0000 (-0400) Subject: This is an omnibus commit which moves the Citadel Server from crusty old GNU Autotool... X-Git-Tag: v952~14 X-Git-Url: https://code.citadel.org/?a=commitdiff_plain;h=f6fcf350671e3661f8f22696eb35133014ab6a14;p=citadel.git This is an omnibus commit which moves the Citadel Server from crusty old GNU Autotools to the shiny new conf-IG-ure system. WARNING BROKEN BUILD ALERT: at the moment it only compiles the server and lacks 'clean' and 'install' targets. Subsequent commits will resolve these but don't try to build production from this yet. --- diff --git a/citadel/.gitignore b/citadel/.gitignore index 7f78ac606..26f283b08 100644 --- a/citadel/.gitignore +++ b/citadel/.gitignore @@ -2,14 +2,8 @@ *.d *.gcno *.gcda -Make_modules -Make_sources -Makefile -Makefile.in locale -aclocal.m4 aidepost -base64 build-arch-stamp build-indep-stamp chkpw @@ -17,26 +11,15 @@ chkpwd citadel citmail citserver -config.log -config.status -configure -configure-stamp ctdlmigrate getmail -modules_init.c -modules_init.h -modules_upgrade.c msgform panic.log sendcommand setup svn_revision.c -sysdep.h -sysdep.h.in userlist -whobbs gmon.out -autom4te.cache citadel.config citadel.control citadel.pid diff --git a/citadel/COPYING b/citadel/COPYING index b9a6cc30f..97eb78723 100644 --- a/citadel/COPYING +++ b/citadel/COPYING @@ -6,23 +6,13 @@ Copyright: (C) 1987-2022 Citadel development team; GPL V3 software" -- and that our favorite operating system is called "Linux" -- NOT "GNU/Linux". -* In addition, as a special exception, it is declared without exception - that Richard Stallman is a communist, is an asshole, and should - shut up and go away because he contributes nothing useful to the - open source community. - -* In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - You must obey the General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. +* We reject and oppose Richard M. Stallman because he contributes + nothing but trouble to the open source community. + +* As a special exception, the copyright holders of the Citadel + system grant permission to link Citadel with non-GPL-compatible + libraries, including OpenSSL, in the sole case that such linking + is done to build the Citadel system itself. GENERAL PUBLIC LICENSE diff --git a/citadel/Makefile b/citadel/Makefile new file mode 100644 index 000000000..f1439fad9 --- /dev/null +++ b/citadel/Makefile @@ -0,0 +1,27 @@ +# Makefile for Citadel Server +# Copyright (c) 1987-2022 by Art Cancro and the citadel.org team +# +# This is the new and improved version that does not use the GNU Autotools, +# because it is The Current Year and we aren't trying to build for weird +# obscure systems anymore. +# +# This program is open source software. Use, duplication, and/or +# disclosure are subject to the GNU General Purpose License version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + + +# config.mk is generated by ./configure +include config.mk + +citserver: server/*.c server/modules/*/*.c config.mk + cc ${CFLAGS} \ + server/*.c server/modules/*/*.c \ + ${LDFLAGS} -lresolv -lcitadel -lpthread -lz -lical -lldap -lcrypt -lexpat -lcurl -ldb \ + -o citserver + +config.mk: configure + ./configure diff --git a/citadel/citadel.h b/citadel/citadel.h deleted file mode 100644 index 5e80d6548..000000000 --- a/citadel/citadel.h +++ /dev/null @@ -1,187 +0,0 @@ -// Main Citadel header file -// -// Copyright (c) 1987-2021 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -/* system customizations are in sysconfig.h */ - -#ifndef CITADEL_H -#define CITADEL_H -/* #include uncomment if using dmalloc */ - -#include "sysdep.h" -#include -#include "sysconfig.h" -#include "typesize.h" -#include "ipcdef.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Text description of this software - * (We used to define this ourselves, but why bother when the build tools do it for us?) - */ -#define CITADEL PACKAGE_STRING - -#define REV_LEVEL 951 // This version -#define REV_MIN 591 // Oldest compatible database -#define EXPORT_REV_MIN 931 // Oldest compatible export files -#define LIBCITADEL_MIN 931 // Minimum required version of libcitadel -#define SERVER_TYPE 0 // zero for stock Citadel; other developers please obtain SERVER_TYPE codes for your implementations - -#ifdef LIBCITADEL_VERSION_NUMBER -#if LIBCITADEL_VERSION_NUMBER < LIBCITADEL_MIN -#error libcitadel is too old. Please upgrade it before continuing. -#endif -#endif - -/* - * This is the user name and password for the default administrator account - * that is created when Citadel Server is started with an empty database. - */ -#define DEFAULT_ADMIN_USERNAME "admin" -#define DEFAULT_ADMIN_PASSWORD "citadel" - -/* Various length constants */ - -#define ROOMNAMELEN 128 /* The size of a roomname string */ -#define USERNAME_SIZE 64 /* The size of a username string */ -#define MAX_EDITORS 5 /* number of external editors supported ; must be at least 1 */ - -/* - * Message expiration policy stuff - */ -typedef struct ExpirePolicy ExpirePolicy; -struct ExpirePolicy { - int expire_mode; - int expire_value; -}; - -#define EXPIRE_NEXTLEVEL 0 // Inherit expiration policy -#define EXPIRE_MANUAL 1 // Don't expire messages at all -#define EXPIRE_NUMMSGS 2 // Keep only latest n messages -#define EXPIRE_AGE 3 // Expire messages after n days - - -/* - * User records. - */ -typedef struct ctdluser ctdluser; -struct ctdluser { // User record - int version; // Citadel version which created this record - uid_t uid; // Associate with a unix account? - char password[32]; // password - unsigned flags; // See US_ flags below - long timescalled; // Total number of logins - long posted; // Number of messages ever submitted - cit_uint8_t axlevel; // Access level - long usernum; // User number (never recycled) - time_t lastcall; // Date/time of most recent login - int USuserpurge; // Purge time (in days) for user - char fullname[64]; // Display name (primary identifier) - long msgnum_bio; // msgnum of user's profile (bio) - long msgnum_pic; // msgnum of user's avatar (photo) - char emailaddrs[512]; // Internet email addresses - long msgnum_inboxrules; // msgnum of user's inbox filtering rules - long lastproc_inboxrules; // msgnum of last message filtered -}; - - -/* Bits which may appear in MMflags. - */ -#define MM_VALID 4 // New users need validating - -/* - * Room records. - */ -typedef struct ctdlroom ctdlroom; -struct ctdlroom { - char QRname[ROOMNAMELEN]; // Name of room - char QRpasswd[10]; // Only valid if it's a private rm - long QRroomaide; // User number of room aide - long QRhighest; // Highest message NUMBER in room - time_t QRgen; // Generation number of room - unsigned QRflags; // See flag values below - char QRdirname[15]; // Directory name, if applicable - long msgnum_info; // msgnum of room banner (info file) - char QRfloor; // Which floor this room is on - time_t QRmtime; // Date/time of last post - struct ExpirePolicy QRep; // Message expiration policy - long QRnumber; // Globally unique room number - char QRorder; // Sort key for room listing order - unsigned QRflags2; // Additional flags - int QRdefaultview; // How to display the contents - long msgnum_pic; // msgnum of room picture or icon -}; - -/* Private rooms are always flagged with QR_PRIVATE. If neither QR_PASSWORDED - * or QR_GUESSNAME is set, then it is invitation-only. Passworded rooms are - * flagged with both QR_PRIVATE and QR_PASSWORDED while guess-name rooms are - * flagged with both QR_PRIVATE and QR_GUESSNAME. NEVER set all three flags. - */ - -/* - * Miscellaneous - */ -#define MES_NORMAL 65 // Normal message -#define MES_ANONONLY 66 // "****" header -#define MES_ANONOPT 67 // "Anonymous" header - -/****************************************************************************/ - -/* - * Floor record. The floor number is implicit in its location in the file. - */ -typedef struct floor floor; -struct floor { - unsigned short f_flags; // flags - char f_name[256]; // name of floor - int f_ref_count; // reference count - struct ExpirePolicy f_ep; // default expiration policy -}; - -#define F_INUSE 1 // floor is in use - - -/* - * Values used internally for function call returns, etc. - */ -#define NEWREGISTER 0 // new user to register -#define REREGISTER 1 // existing user reregistering - -/* number of items which may be handled by the CONF command */ -#define NUM_CONFIGS 71 - -#define TRACE syslog(LOG_DEBUG, "\033[7m Checkpoint: %s : %d \033[0m", __FILE__, __LINE__) - -#ifndef LONG_MAX -#define LONG_MAX 2147483647L -#endif - -/* - * Authentication modes - */ -#define AUTHMODE_NATIVE 0 // Native (self-contained or "black box") -#define AUTHMODE_HOST 1 // Authenticate against the host OS user database -#define AUTHMODE_LDAP 2 // Authenticate using LDAP server with RFC 2307 schema -#define AUTHMODE_LDAP_AD 3 // Authenticate using LDAP server with Active Directory schema - -#ifdef __cplusplus -} -#endif - -#if __GNUC__ >= 8 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-truncation" -#endif - -#endif /* CITADEL_H */ diff --git a/citadel/citadel_dirs.c b/citadel/citadel_dirs.c deleted file mode 100644 index 056047718..000000000 --- a/citadel/citadel_dirs.c +++ /dev/null @@ -1,59 +0,0 @@ -// citadel_dirs.c : calculate pathnames for various files used in the Citadel system -// -// Copyright (c) 1987-2021 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "citadel_dirs.h" - -// Generate an associated file name for a room -size_t assoc_file_name(char *buf, size_t n, struct ctdlroom *qrbuf, const char *prefix) { - return snprintf(buf, n, "%s%ld", prefix, qrbuf->QRnumber); -} - - -int create_dir(char *which, long ACCESS, long UID, long GID) { - int rv; - rv = mkdir(which, ACCESS); - if ((rv == -1) && (errno != EEXIST)) { - syslog(LOG_ERR, - "failed to create directory %s: %s", - which, - strerror(errno)); - return rv; - } - rv = chmod(which, ACCESS); - if (rv == -1) { - syslog(LOG_ERR, "failed to set permissions for directory %s: %s", which, strerror(errno)); - return rv; - } - rv = chown(which, UID, GID); - if (rv == -1) { - syslog(LOG_ERR, "failed to set owner for directory %s: %s", which, strerror(errno)); - return rv; - } - return rv; -} - - -int create_run_directories(long UID, long GID) { - int rv = 0; - rv += create_dir(ctdl_message_dir , S_IRUSR|S_IWUSR|S_IXUSR, UID, -1); - rv += create_dir(ctdl_file_dir , S_IRUSR|S_IWUSR|S_IXUSR, UID, -1); - rv += create_dir(ctdl_key_dir , S_IRUSR|S_IWUSR|S_IXUSR, UID, -1); - rv += create_dir(ctdl_run_dir , S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, UID, GID); - return rv; -} diff --git a/citadel/citadel_dirs.h b/citadel/citadel_dirs.h deleted file mode 100644 index ad422bf05..000000000 --- a/citadel/citadel_dirs.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef __CITADEL_DIRS_H -#define __CITADEL_DIRS_H - -#include - -/* Fixed directory names (some of these are obsolete and used only for migration) */ -#define ctdl_home_directory "." -#define ctdl_db_dir "data" -#define ctdl_file_dir "files" -#define ctdl_shared_dir "." -#define ctdl_image_dir "images" -#define ctdl_info_dir "info" -#define ctdl_key_dir "keys" -#define ctdl_message_dir "messages" -#define ctdl_usrpic_dir "userpics" -#define ctdl_autoetc_dir "." -#define ctdl_run_dir "." -#define ctdl_netcfg_dir "netconfigs" -#define ctdl_bbsbase_dir "." -#define ctdl_sbin_dir "." -#define ctdl_bin_dir "." -#define ctdl_utilbin_dir "." - -/* Fixed file names (some of these are obsolete and used only for migration) */ -#define file_citadel_config "citadel.config" -#define file_lmtp_socket "lmtp.socket" -#define file_lmtp_unfiltered_socket "lmtp-unfiltered.socket" -#define file_arcq "refcount_adjustments.dat" -#define file_citadel_socket "citadel.socket" -#define file_citadel_admin_socket "citadel-admin.socket" -#define file_pid_file "/var/run/citserver.pid" -#define file_pid_paniclog "panic.log" -#define file_crpt_file_key "keys/citadel.key" -#define file_crpt_file_cer "keys/citadel.cer" -#define file_chkpwd "chkpwd" -#define file_guesstimezone "guesstimezone.sh" - - -/* externs */ -extern int create_run_directories(long UID, long GUID); -extern size_t assoc_file_name(char *buf, size_t n, struct ctdlroom *qrbuf, const char *prefix); -extern FILE *create_digest_file(struct ctdlroom *room, int forceCreate); -extern void remove_digest_file(struct ctdlroom *room); - -#endif /* __CITADEL_DIRS_H */ diff --git a/citadel/citadel_ldap.h b/citadel/citadel_ldap.h deleted file mode 100644 index 926fbde56..000000000 --- a/citadel/citadel_ldap.h +++ /dev/null @@ -1,17 +0,0 @@ -// Configuration for LDAP authentication. Most of this stuff gets pulled out of our site config file. -// -// Copyright (c) 1987-2017 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -int CtdlTryUserLDAP(char *username, char *found_dn, int found_dn_size, char *fullname, int fullname_size, uid_t *found_uid); -int CtdlTryPasswordLDAP(char *user_dn, const char *password); -int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v); -int extract_email_addresses_from_ldap(char *ldap_dn, char *emailaddrs); -void CtdlSynchronizeUsersFromLDAP(void); diff --git a/citadel/citserver.c b/citadel/citserver.c deleted file mode 100644 index 2d829f1dd..000000000 --- a/citadel/citserver.c +++ /dev/null @@ -1,198 +0,0 @@ -// Main source module for the Citadel server -// -// Copyright (c) 1987-2021 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include -#include -#include -#include -#include "sysdep.h" -#include -#include - -#include "ctdl_module.h" -#include "housekeeping.h" -#include "locate_host.h" -#include "citserver.h" -#include "user_ops.h" -#include "control.h" -#include "config.h" - -char *unique_session_numbers; -int ScheduledShutdown = 0; -time_t server_startup_time; -int panic_fd; -int openid_level_supported = 0; - - -// We need pseudo-random numbers for a few things. Seed generously. -void seed_random_number_generator(void) { - FILE *urandom; - struct timeval tv; - unsigned int seed; - - syslog(LOG_INFO, "Seeding the pseudo-random number generator..."); - urandom = fopen("/dev/urandom", "r"); - if (urandom != NULL) { - if (fread(&seed, sizeof seed, 1, urandom) == -1) { - syslog(LOG_ERR, "citserver: failed to read random seed: %m"); - } - fclose(urandom); - } - else { - gettimeofday(&tv, NULL); - seed = tv.tv_usec; - } - srand(seed); - srandom(seed); -} - - -// Various things that need to be initialized at startup -void master_startup(void) { - struct ctdlroom qrbuf; - struct passwd *pw; - gid_t gid; - - syslog(LOG_DEBUG, "master_startup() started"); - time(&server_startup_time); - - syslog(LOG_INFO, "Checking directory access"); - if ((pw = getpwuid(ctdluid)) == NULL) { - gid = getgid(); - } - else { - gid = pw->pw_gid; - } - - if (create_run_directories(CTDLUID, gid) != 0) { - syslog(LOG_ERR, "citserver: failed to access and create directories"); - exit(1); - } - syslog(LOG_INFO, "Opening databases"); - open_databases(); - - // Load site-specific configuration - seed_random_number_generator(); // must be done before config system - syslog(LOG_INFO, "Initializing configuration system"); - initialize_config_system(); - validate_config(); - migrate_legacy_control_record(); - - // If we have an existing database that is older than version 928, reindex the user records. - // Unfortunately we cannot do this in serv_upgrade.c because it needs to happen VERY early during startup. - int existing_db = CtdlGetConfigInt("MM_hosted_upgrade_level"); - if ( (existing_db > 0) && (existing_db < 928) ) { - ForEachUser(reindex_user_928, NULL); - } - - // Check floor reference counts - check_ref_counts(); - - syslog(LOG_INFO, "Creating base rooms (if necessary)"); - CtdlCreateRoom(CtdlGetConfigStr("c_baseroom"), 0, "", 0, 1, 0, VIEW_BBS); - CtdlCreateRoom(AIDEROOM, 3, "", 0, 1, 0, VIEW_BBS); - CtdlCreateRoom(SYSCONFIGROOM, 3, "", 0, 1, 0, VIEW_BBS); - CtdlCreateRoom(CtdlGetConfigStr("c_twitroom"), 0, "", 0, 1, 0, VIEW_BBS); - - // The "Local System Configuration" room doesn't need to be visible - if (CtdlGetRoomLock(&qrbuf, SYSCONFIGROOM) == 0) { - qrbuf.QRflags2 |= QR2_SYSTEM; - CtdlPutRoomLock(&qrbuf); - } - - // Aide needs to be public postable, else we're not RFC conformant. - if (CtdlGetRoomLock(&qrbuf, AIDEROOM) == 0) { - qrbuf.QRflags2 |= QR2_SMTP_PUBLIC; - CtdlPutRoomLock(&qrbuf); - } - - syslog(LOG_DEBUG, "master_startup() finished"); -} - - -// Cleanup routine to be called when the server is shutting down. Returns the needed exit code. -int master_cleanup(int exitcode) { - static int already_cleaning_up = 0; - - if (already_cleaning_up) { - while (1) { - usleep(1000000); - } - } - already_cleaning_up = 1; - - // Do system-dependent stuff - sysdep_master_cleanup(); - - // Close the configuration system - shutdown_config_system(); - - // Close databases - syslog(LOG_INFO, "citserver: closing databases"); - close_databases(); - - // If the operator requested a halt but not an exit, halt here. - if (shutdown_and_halt) { - syslog(LOG_ERR, "citserver: Halting server without exiting."); - fflush(stdout); - fflush(stderr); - while (1) { - sleep(32767); - } - } - - // Now go away. - syslog(LOG_ERR, "citserver: Exiting with status %d", exitcode); - fflush(stdout); - fflush(stderr); - - if (restart_server != 0) { - exitcode = 1; - } - else if ((running_as_daemon != 0) && ((exitcode == 0))) { - exitcode = CTDLEXIT_SHUTDOWN; - } - return (exitcode); -} - - -// returns an asterisk if there are any instant messages waiting, space otherwise. -char CtdlCheckExpress(void) { - if (CC->FirstExpressMessage == NULL) { - return (' '); - } - else { - return ('*'); - } -} - - -void citproto_begin_session() { - if (CC->nologin == 1) { - cprintf("%d Too many users are already online (maximum is %d)\n", - ERROR + MAX_SESSIONS_EXCEEDED, CtdlGetConfigInt("c_maxsessions") - ); - CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED; - } - else { - cprintf("%d %s Citadel server ready.\n", CIT_OK, CtdlGetConfigStr("c_fqdn")); - CC->can_receive_im = 1; - } -} - - -void citproto_begin_admin_session() { - CC->internal_pgm = 1; - cprintf("%d %s Citadel server ADMIN CONNECTION ready.\n", CIT_OK, CtdlGetConfigStr("c_fqdn")); -} - - -// This loop performs all asynchronous functions. -void do_async_loop(void) { - PerformSessionHooks(EVT_ASYNC); -} diff --git a/citadel/citserver.h b/citadel/citserver.h deleted file mode 100644 index effe9557a..000000000 --- a/citadel/citserver.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 1987-2019 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -/* I fucking hate const and want it to die. */ -#pragma GCC diagnostic ignored "-Wcast-qual" - -#include "serv_extensions.h" -#include "context.h" -#include "ctdl_module.h" - -/* Simple linked list structures ... used in a bunch of different places. */ -typedef struct RoomProcList RoomProcList; -struct RoomProcList { - struct RoomProcList *next; - char name[ROOMNAMELEN]; - char lcname[ROOMNAMELEN]; - long namelen; - long lastsent; - long key; - long QRNum; -}; -struct UserProcList { - struct UserProcList *next; - char user[USERNAME_SIZE]; -}; - -#define CTDLUSERIP (IsEmptyStr(CC->cs_addr) ? CC->cs_clientinfo: CC->cs_addr) - -void master_startup (void); -int master_cleanup (int exitcode); -void set_wtmpsupp (char *newtext); -void set_wtmpsupp_to_current_room(void); -void do_command_loop(void); -void do_async_loop(void); -void begin_session(struct CitContext *con); -void citproto_begin_session(void); -void citproto_begin_admin_session(void); -void help_subst (char *strbuf, char *source, char *dest); -char CtdlCheckExpress(void); - -extern int panic_fd; -extern time_t server_startup_time; -extern int openid_level_supported; diff --git a/citadel/clientsocket.c b/citadel/clientsocket.c deleted file mode 100644 index a88c614d9..000000000 --- a/citadel/clientsocket.c +++ /dev/null @@ -1,270 +0,0 @@ -// This module handles client-side sockets opened by the Citadel server (for -// the client side of Internet protocols, etc.) It does _not_ handle client -// sockets for the Citadel client; for that you must look in ipc_c_tcp.c -// (which, uncoincidentally, bears a striking similarity to this file). -// -// Copyright (c) 1987-2017 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif -#include -#include "ctdl_module.h" -#include "clientsocket.h" - -int sock_connect(char *host, char *service) { - struct in6_addr serveraddr; - struct addrinfo hints; - struct addrinfo *res = NULL; - struct addrinfo *ai = NULL; - int rc = (-1); - int sock = (-1); - - if ((host == NULL) || IsEmptyStr(host)) - return (-1); - if ((service == NULL) || IsEmptyStr(service)) - return (-1); - - memset(&hints, 0x00, sizeof(hints)); - hints.ai_flags = AI_NUMERICSERV; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - // Handle numeric IPv4 and IPv6 addresses - rc = inet_pton(AF_INET, host, &serveraddr); - if (rc == 1) { // dotted quad - hints.ai_family = AF_INET; - hints.ai_flags |= AI_NUMERICHOST; - } - else { - rc = inet_pton(AF_INET6, host, &serveraddr); - if (rc == 1) { // IPv6 address - hints.ai_family = AF_INET6; - hints.ai_flags |= AI_NUMERICHOST; - } - } - - // Begin the connection process - rc = getaddrinfo(host, service, &hints, &res); - if (rc != 0) { - syslog(LOG_ERR, "%s: %s", host, gai_strerror(rc)); - return(-1); - } - - // Try all available addresses until we connect to one or until we run out. - for (ai = res; ai != NULL; ai = ai->ai_next) { - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) { - syslog(LOG_ERR, "%s: %m", host); - freeaddrinfo(res); - return(-1); - } - rc = connect(sock, ai->ai_addr, ai->ai_addrlen); - if (rc >= 0) { - freeaddrinfo(res); - return(sock); - } - else { - syslog(LOG_ERR, "%s: %m", host); - close(sock); - } - } - freeaddrinfo(res); - return(-1); -} - - -// Read data from the client socket. -// -// sock socket fd to read from -// buf buffer to read into -// bytes number of bytes to read -// timeout Number of seconds to wait before timing out -// -// Possible return values: -// 1 Requested number of bytes has been read. -// 0 Request timed out. -// -1 Connection is broken, or other error. -int socket_read_blob(int *Socket, StrBuf *Target, int bytes, int timeout) { - const char *Error; - int retval = 0; - - retval = StrBufReadBLOBBuffered(Target, CC->SBuf.Buf, &CC->SBuf.ReadWritePointer, Socket, 1, bytes, O_TERM, &Error); - if (retval < 0) { - syslog(LOG_ERR, "clientsocket: socket_read_blob() failed: %s", Error); - } - return retval; -} - - -int CtdlSockGetLine(int *sock, StrBuf *Target, int nSec) { - const char *Error; - int rc; - - FlushStrBuf(Target); - rc = StrBufTCP_read_buffered_line_fast(Target, - CC->SBuf.Buf, - &CC->SBuf.ReadWritePointer, - sock, nSec, 1, &Error); - if ((rc < 0) && (Error != NULL)) { - syslog(LOG_ERR, "clientsocket: CtdlSockGetLine() failed: %s", Error); - } - return rc; -} - - -// client_getln() ... Get a LF-terminated line of text from the client. -int sock_getln(int *sock, char *buf, int bufsize) { - int i, retval; - const char *pCh; - - FlushStrBuf(CC->sMigrateBuf); - retval = CtdlSockGetLine(sock, CC->sMigrateBuf, 5); - - i = StrLength(CC->sMigrateBuf); - pCh = ChrPtr(CC->sMigrateBuf); - - memcpy(buf, pCh, i + 1); - - FlushStrBuf(CC->sMigrateBuf); - if (retval < 0) { - safestrncpy(&buf[i], "000", bufsize - i); - i += 3; - } - return i; -} - - -// sock_write() - send binary to server. -// Returns the number of bytes written, or -1 for error. -int sock_write(int *sock, const char *buf, int nbytes) { - return sock_write_timeout(sock, buf, nbytes, 50); -} - - -int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout) { - int nSuccessLess = 0; - int bytes_written = 0; - int retval; - fd_set rfds; - int fdflags; - int IsNonBlock; - struct timeval tv; - int selectresolution = 100; - - fdflags = fcntl(*sock, F_GETFL); - IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; - - while ((nSuccessLess < timeout) && (*sock != -1) && (bytes_written < nbytes)) { - if (IsNonBlock) { - tv.tv_sec = selectresolution; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*sock, &rfds); - if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) { - close (*sock); - *sock = -1; - return -1; - } - } - if (IsNonBlock && ! FD_ISSET(*sock, &rfds)) { - nSuccessLess ++; - continue; - } - retval = write(*sock, &buf[bytes_written], - nbytes - bytes_written); - if (retval < 1) { - sock_close(*sock); - *sock = -1; - return (-1); - } - bytes_written = bytes_written + retval; - if (IsNonBlock && (bytes_written == nbytes)){ - tv.tv_sec = selectresolution; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*sock, &rfds); - if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) { - close (*sock); - *sock = -1; - return -1; - } - } - } - return (bytes_written); -} - - -// client_getln() ... Get a LF-terminated line of text from the client. -int sock_getln_err(int *sock, char *buf, int bufsize, int *rc, int nSec) { - int i, retval; - const char *pCh; - - FlushStrBuf(CC->sMigrateBuf); - *rc = retval = CtdlSockGetLine(sock, CC->sMigrateBuf, nSec); - - i = StrLength(CC->sMigrateBuf); - pCh = ChrPtr(CC->sMigrateBuf); - - memcpy(buf, pCh, i + 1); - - FlushStrBuf(CC->sMigrateBuf); - if (retval < 0) { - safestrncpy(&buf[i], "000", bufsize - i); - i += 3; - } - return i; -} - - -// Multiline version of sock_gets() ... this is a convenience function for -// client side protocol implementations. It only returns the first line of -// a multiline response, discarding the rest. -int ml_sock_gets(int *sock, char *buf, int nSec) { - int rc = 0; - char bigbuf[1024]; - int g; - - g = sock_getln_err(sock, buf, SIZ, &rc, nSec); - if (rc < 0) - return rc; - if (g < 4) - return (g); - if (buf[3] != '-') - return (g); - - do { - g = sock_getln_err(sock, bigbuf, SIZ, &rc, nSec); - if (rc < 0) - return rc; - if (g < 0) - return (g); - } while ((g >= 4) && (bigbuf[3] == '-')); - - return (strlen(buf)); -} - - -// sock_puts() - send line to server - implemented in terms of serv_write() -// Returns the number of bytes written, or -1 for error. -int sock_puts(int *sock, char *buf) { - int i, j; - - i = sock_write(sock, buf, strlen(buf)); - if (i < 0) - return (i); - j = sock_write(sock, "\n", 1); - if (j < 0) - return (j); - return (i + j); -} diff --git a/citadel/clientsocket.h b/citadel/clientsocket.h deleted file mode 100644 index cc275e1a5..000000000 --- a/citadel/clientsocket.h +++ /dev/null @@ -1,32 +0,0 @@ -// Header file for TCP client socket library -// -// Copyright (c) 1987-2012 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -int sock_connect(char *host, char *service); -int sock_write(int *sock, const char *buf, int nbytes); -int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout); -int ml_sock_gets(int *sock, char *buf, int nSec); -int sock_getln(int *sock, char *buf, int bufsize); -int CtdlSockGetLine(int *sock, StrBuf *Target, int nSec); -int sock_puts(int *sock, char *buf); -int socket_read_blob(int *Socket, StrBuf * Target, int bytes, int timeout); - - -/* - * This looks dumb, but it's being done for future portability - */ -#define sock_close(sock) close(sock) -#define sock_shutdown(sock, how) shutdown(sock, how) - -/* - * Default timeout for client sessions - */ -#define CLIENT_TIMEOUT 600 diff --git a/citadel/config.c b/citadel/config.c deleted file mode 100644 index 86077a0c6..000000000 --- a/citadel/config.c +++ /dev/null @@ -1,485 +0,0 @@ -// Read and write the system configuration database -// -// Copyright (c) 1987-2022 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "config.h" -#include "ctdl_module.h" - -long config_msgnum = 0; -HashList *ctdlconfig = NULL; // new configuration - - -void config_warn_if_port_unset(char *key, int default_port) { - int p = CtdlGetConfigInt(key); - if ((p < -1) || (p == 0) || (p > UINT16_MAX)) { - syslog(LOG_ERR, "config: setting %s is not -1 (disabled) or a valid TCP port - setting to default %d", key, default_port); - CtdlSetConfigInt(key, default_port); - } -} - - -void config_warn_if_empty(char *key) { - if (IsEmptyStr(CtdlGetConfigStr(key))) { - syslog(LOG_ERR, "config: setting %s is empty, but must not - check your config!", key); - } -} - - -void validate_config(void) { - - // these shouldn't be empty - config_warn_if_empty("c_fqdn"); - config_warn_if_empty("c_baseroom"); - config_warn_if_empty("c_aideroom"); - config_warn_if_empty("c_twitroom"); - config_warn_if_empty("c_nodename"); - - // Sanity check for port bindings - config_warn_if_port_unset("c_smtp_port", 25); - config_warn_if_port_unset("c_pop3_port", 110); - config_warn_if_port_unset("c_imap_port", 143); - config_warn_if_port_unset("c_msa_port", 587); - config_warn_if_port_unset("c_port_number", 504); - config_warn_if_port_unset("c_smtps_port", 465); - config_warn_if_port_unset("c_pop3s_port", 995); - config_warn_if_port_unset("c_imaps_port", 993); - config_warn_if_port_unset("c_pftcpdict_port", -1); - config_warn_if_port_unset("c_xmpp_c2s_port", 5222); - config_warn_if_port_unset("c_xmpp_s2s_port", 5269); - config_warn_if_port_unset("c_nntp_port", 119); - config_warn_if_port_unset("c_nntps_port", 563); - - if (getpwuid(ctdluid) == NULL) { - syslog(LOG_ERR, "config: uid (%d) does not exist ... citserver will run as root", ctdluid); - } -} - - -// The "host key" is a 100-byte random string that is used to do some persistent hashing. -// It must remain consistent throughout the lifetime of this Citadel installation -void generate_host_key(void) { - syslog(LOG_INFO, "config: generating a host key"); - int len = 0; - char host_key[128]; - - while (len < 100) { - do { - host_key[len] = random() % 0x7F; - } while (!isalnum(host_key[len])); - host_key[++len] = 0; - } - CtdlSetConfigStr("host_key", host_key); -} - - -// Put some sane default values into our configuration. Some will be overridden when we run setup. -void brand_new_installation_set_defaults(void) { - - struct utsname my_utsname; - struct hostent *he; - char detected_hostname[256]; - - // Determine our host name, in case we need to use it as a default - uname(&my_utsname); - - // set some sample/default values in place of blanks... - extract_token(detected_hostname, my_utsname.nodename, 0, '.', sizeof detected_hostname); - CtdlSetConfigStr("c_nodename", detected_hostname); - - if ((he = gethostbyname(my_utsname.nodename)) != NULL) { - CtdlSetConfigStr("c_fqdn", he->h_name); - } - else { - CtdlSetConfigStr("c_fqdn", my_utsname.nodename); - } - - CtdlSetConfigStr("c_humannode", "Citadel Server"); - CtdlSetConfigInt("c_initax", 4); - CtdlSetConfigStr("c_moreprompt", ""); - CtdlSetConfigStr("c_twitroom", "Trashcan"); - CtdlSetConfigStr("c_baseroom", BASEROOM); - CtdlSetConfigStr("c_aideroom", "Aide"); - CtdlSetConfigInt("c_sleeping", 900); - - if (CtdlGetConfigInt("c_createax") == 0) { - CtdlSetConfigInt("c_createax", 3); - } - - // Default port numbers for various services - CtdlSetConfigInt("c_port_number", 504); - CtdlSetConfigInt("c_smtp_port", 25); - CtdlSetConfigInt("c_pop3_port", 110); - CtdlSetConfigInt("c_imap_port", 143); - CtdlSetConfigInt("c_msa_port", 587); - CtdlSetConfigInt("c_smtps_port", 465); - CtdlSetConfigInt("c_pop3s_port", 995); - CtdlSetConfigInt("c_imaps_port", 993); - CtdlSetConfigInt("c_pftcpdict_port", -1); - CtdlSetConfigInt("c_xmpp_c2s_port", 5222); - CtdlSetConfigInt("c_xmpp_s2s_port", 5269); - CtdlSetConfigInt("c_nntp_port", 119); - CtdlSetConfigInt("c_nntps_port", 563); - - // Prevent the "new installation, set defaults" behavior from occurring again - CtdlSetConfigLong("c_config_created_or_migrated", (long)time(NULL)); -} - - -// Migrate a supplied legacy configuration to the new in-db format. -// No individual site should ever have to do this more than once. -void migrate_legacy_config(struct legacy_config *lconfig) { - CtdlSetConfigStr( "c_nodename" , lconfig->c_nodename ); - CtdlSetConfigStr( "c_fqdn" , lconfig->c_fqdn ); - CtdlSetConfigStr( "c_humannode" , lconfig->c_humannode ); - CtdlSetConfigInt( "c_creataide" , lconfig->c_creataide ); - CtdlSetConfigInt( "c_sleeping" , lconfig->c_sleeping ); - CtdlSetConfigInt( "c_initax" , lconfig->c_initax ); - CtdlSetConfigInt( "c_regiscall" , lconfig->c_regiscall ); - CtdlSetConfigInt( "c_twitdetect" , lconfig->c_twitdetect ); - CtdlSetConfigStr( "c_twitroom" , lconfig->c_twitroom ); - CtdlSetConfigStr( "c_moreprompt" , lconfig->c_moreprompt ); - CtdlSetConfigInt( "c_restrict" , lconfig->c_restrict ); - CtdlSetConfigStr( "c_site_location" , lconfig->c_site_location ); - CtdlSetConfigStr( "c_sysadm" , lconfig->c_sysadm ); - CtdlSetConfigInt( "c_maxsessions" , lconfig->c_maxsessions ); - CtdlSetConfigStr( "c_ip_addr" , lconfig->c_ip_addr ); - CtdlSetConfigInt( "c_port_number" , lconfig->c_port_number ); - CtdlSetConfigInt( "c_ep_mode" , lconfig->c_ep.expire_mode ); - CtdlSetConfigInt( "c_ep_value" , lconfig->c_ep.expire_value ); - CtdlSetConfigInt( "c_userpurge" , lconfig->c_userpurge ); - CtdlSetConfigInt( "c_roompurge" , lconfig->c_roompurge ); - CtdlSetConfigStr( "c_logpages" , lconfig->c_logpages ); - CtdlSetConfigInt( "c_createax" , lconfig->c_createax ); - CtdlSetConfigLong( "c_maxmsglen" , lconfig->c_maxmsglen ); - CtdlSetConfigInt( "c_min_workers" , lconfig->c_min_workers ); - CtdlSetConfigInt( "c_max_workers" , lconfig->c_max_workers ); - CtdlSetConfigInt( "c_pop3_port" , lconfig->c_pop3_port ); - CtdlSetConfigInt( "c_smtp_port" , lconfig->c_smtp_port ); - CtdlSetConfigInt( "c_rfc822_strict_from" , lconfig->c_rfc822_strict_from ); - CtdlSetConfigInt( "c_aide_zap" , lconfig->c_aide_zap ); - CtdlSetConfigInt( "c_imap_port" , lconfig->c_imap_port ); - CtdlSetConfigLong( "c_net_freq" , lconfig->c_net_freq ); - CtdlSetConfigInt( "c_disable_newu" , lconfig->c_disable_newu ); - CtdlSetConfigInt( "c_enable_fulltext" , lconfig->c_enable_fulltext ); - CtdlSetConfigStr( "c_baseroom" , lconfig->c_baseroom ); - CtdlSetConfigStr( "c_aideroom" , lconfig->c_aideroom ); - CtdlSetConfigInt( "c_purge_hour" , lconfig->c_purge_hour ); - CtdlSetConfigInt( "c_mbxep_mode" , lconfig->c_mbxep.expire_mode ); - CtdlSetConfigInt( "c_mbxep_value" , lconfig->c_mbxep.expire_value ); - CtdlSetConfigStr( "c_ldap_host" , lconfig->c_ldap_host ); - CtdlSetConfigInt( "c_ldap_port" , lconfig->c_ldap_port ); - CtdlSetConfigStr( "c_ldap_base_dn" , lconfig->c_ldap_base_dn ); - CtdlSetConfigStr( "c_ldap_bind_dn" , lconfig->c_ldap_bind_dn ); - CtdlSetConfigStr( "c_ldap_bind_pw" , lconfig->c_ldap_bind_pw ); - CtdlSetConfigInt( "c_msa_port" , lconfig->c_msa_port ); - CtdlSetConfigInt( "c_imaps_port" , lconfig->c_imaps_port ); - CtdlSetConfigInt( "c_pop3s_port" , lconfig->c_pop3s_port ); - CtdlSetConfigInt( "c_smtps_port" , lconfig->c_smtps_port ); - CtdlSetConfigInt( "c_auto_cull" , lconfig->c_auto_cull ); - CtdlSetConfigInt( "c_allow_spoofing" , lconfig->c_allow_spoofing ); - CtdlSetConfigInt( "c_journal_email" , lconfig->c_journal_email ); - CtdlSetConfigInt( "c_journal_pubmsgs" , lconfig->c_journal_pubmsgs ); - CtdlSetConfigStr( "c_journal_dest" , lconfig->c_journal_dest ); - CtdlSetConfigStr( "c_default_cal_zone" , lconfig->c_default_cal_zone ); - CtdlSetConfigInt( "c_pftcpdict_port" , lconfig->c_pftcpdict_port ); - CtdlSetConfigInt( "c_auth_mode" , lconfig->c_auth_mode ); - CtdlSetConfigInt( "c_rbl_at_greeting" , lconfig->c_rbl_at_greeting ); - CtdlSetConfigStr( "c_pager_program" , lconfig->c_pager_program ); - CtdlSetConfigInt( "c_imap_keep_from" , lconfig->c_imap_keep_from ); - CtdlSetConfigInt( "c_xmpp_c2s_port" , lconfig->c_xmpp_c2s_port ); - CtdlSetConfigInt( "c_xmpp_s2s_port" , lconfig->c_xmpp_s2s_port ); - CtdlSetConfigLong( "c_pop3_fetch" , lconfig->c_pop3_fetch ); - CtdlSetConfigLong( "c_pop3_fastest" , lconfig->c_pop3_fastest ); - CtdlSetConfigInt( "c_spam_flag_only" , lconfig->c_spam_flag_only ); - CtdlSetConfigInt( "c_guest_logins" , lconfig->c_guest_logins ); - CtdlSetConfigInt( "c_nntp_port" , lconfig->c_nntp_port ); - CtdlSetConfigInt( "c_nntps_port" , lconfig->c_nntps_port ); -} - - -// Called during the initialization of Citadel server. -// It verifies the system's integrity and reads citadel.config into memory. -void initialize_config_system(void) { - FILE *cfp; - int rv; - struct legacy_config lconfig; // legacy configuration - ctdlconfig = NewHash(1, NULL); // set up the real config system - - // Ensure that we are linked to the correct version of libcitadel - if (libcitadel_version_number() < LIBCITADEL_VERSION_NUMBER) { - fprintf(stderr, "You are running libcitadel version %d\n", libcitadel_version_number()); - fprintf(stderr, "citserver was compiled against version %d\n", LIBCITADEL_VERSION_NUMBER); - exit(CTDLEXIT_LIBCITADEL); - } - - memset(&lconfig, 0, sizeof(struct legacy_config)); - cfp = fopen(file_citadel_config, "rb"); - if (cfp != NULL) { - if (CtdlGetConfigLong("c_config_created_or_migrated") > 0) { - fprintf(stderr, "Citadel Server found BOTH legacy and new configurations present.\n"); - fprintf(stderr, "Exiting to prevent data corruption.\n"); - exit(CTDLEXIT_CONFIG); - } - rv = fread((char *) &lconfig, sizeof(struct legacy_config), 1, cfp); - if (rv != 1) { - fprintf(stderr, - "Warning: Found a legacy config file %s has unexpected size. \n", - file_citadel_config - ); - } - - migrate_legacy_config(&lconfig); - - fclose(cfp); - if (unlink(file_citadel_config) != 0) { - fprintf(stderr, "Unable to remove legacy config file %s after migrating it.\n", file_citadel_config); - fprintf(stderr, "Exiting to prevent data corruption.\n"); - exit(CTDLEXIT_CONFIG); - } - - // Prevent migration/initialization from happening again. - CtdlSetConfigLong("c_config_created_or_migrated", (long)time(NULL)); - - } - - // New installation? Set up configuration - if (CtdlGetConfigLong("c_config_created_or_migrated") <= 0) { - brand_new_installation_set_defaults(); - } - - // Default maximum message length is 10 megabytes. This is site - // configurable. Also check to make sure the limit has not been - // set below 8192 bytes. - if (CtdlGetConfigLong("c_maxmsglen") <= 0) CtdlSetConfigLong("c_maxmsglen", 10485760); - if (CtdlGetConfigLong("c_maxmsglen") < 8192) CtdlSetConfigLong("c_maxmsglen", 8192); - - // Default lower and upper limits on number of worker threads - if (CtdlGetConfigInt("c_min_workers") < 5) CtdlSetConfigInt("c_min_workers", 5); // min - if (CtdlGetConfigInt("c_max_workers") == 0) CtdlSetConfigInt("c_max_workers", 256); // default max - if (CtdlGetConfigInt("c_max_workers") < CtdlGetConfigInt("c_min_workers")) { - CtdlSetConfigInt("c_max_workers", CtdlGetConfigInt("c_min_workers")); // max >= min - } - - // Networking more than once every five minutes just isn't sane - if (CtdlGetConfigLong("c_net_freq") == 0) CtdlSetConfigLong("c_net_freq", 3600); // once per hour default - if (CtdlGetConfigLong("c_net_freq") < 300) CtdlSetConfigLong("c_net_freq", 300); // minimum 5 minutes - - // Same goes for POP3 - if (CtdlGetConfigLong("c_pop3_fetch") == 0) CtdlSetConfigLong("c_pop3_fetch", 3600); // once per hour default - if (CtdlGetConfigLong("c_pop3_fetch") < 300) CtdlSetConfigLong("c_pop3_fetch", 300); // 5 minutes min - if (CtdlGetConfigLong("c_pop3_fastest") == 0) CtdlSetConfigLong("c_pop3_fastest", 3600); // once per hour default - if (CtdlGetConfigLong("c_pop3_fastest") < 300) CtdlSetConfigLong("c_pop3_fastest", 300); // 5 minutes min - - // LDAP sync frequency - if (CtdlGetConfigLong("c_ldap_sync_freq") == 0) CtdlSetConfigLong("c_ldap_sync_freq", 300); // every 5 minutes default - - // "create new user" only works with native authentication mode - if (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_NATIVE) { - CtdlSetConfigInt("c_disable_newu", 1); - } - - // If we do not have a host key, generate one now, and save it. - if (IsEmptyStr(CtdlGetConfigStr("host_key"))) { - generate_host_key(); - } -} - - -// Called when Citadel server is shutting down. -// Clears out the config hash table. -void shutdown_config_system(void) { - DeleteHash(&ctdlconfig); -} - - -// Set a system config value. Simple key/value here. -void CtdlSetConfigStr(char *key, char *value) { - int key_len = strlen(key); - int value_len = strlen(value); - - // Save it in memory - Put(ctdlconfig, key, key_len, strdup(value), NULL); - - // Also write it to the config database - - int dbv_size = key_len + value_len + 2; - char *dbv = malloc(dbv_size); - strcpy(dbv, key); - strcpy(&dbv[key_len + 1], value); - cdb_store(CDB_CONFIG, key, key_len, dbv, dbv_size); - free(dbv); -} - - -// Set a numeric system config value (long integer) -void CtdlSetConfigLong(char *key, long value) { - char longstr[256]; - sprintf(longstr, "%ld", value); - CtdlSetConfigStr(key, longstr); -} - - -// Set a numeric system config value (integer) -void CtdlSetConfigInt(char *key, int value) { - char intstr[256]; - sprintf(intstr, "%d", value); - CtdlSetConfigStr(key, intstr); -} - - -// Delete a system config value. -void CtdlDelConfig(char *key) { - int key_len = strlen(key); - - if (IsEmptyStr(key)) return; - - // Delete from the database. - cdb_delete(CDB_CONFIG, key, key_len); - - // Delete from the in-memory cache - HashPos *Pos = GetNewHashPos(ctdlconfig, 1); - if (GetHashPosFromKey(ctdlconfig, key, key_len, Pos)) { - DeleteEntryFromHash(ctdlconfig, Pos); - } - DeleteHashPos(&Pos); - - assert(Pos == NULL); // no memory leaks allowed -} - - -// Fetch a system config value. Caller does *not* own the returned value and may not alter it. -char *CtdlGetConfigStr(char *key) { - char *value = NULL; - struct cdbdata *cdb; - int key_len = strlen(key); - - if (IsEmptyStr(key)) return(NULL); - - // First look in memory - if (GetHash(ctdlconfig, key, key_len, (void *)&value)) { - return value; - } - - // Then look in the database. - cdb = cdb_fetch(CDB_CONFIG, key, key_len); - if (cdb == NULL) { // nope, not there either. - return(NULL); - } - - // Got it. Save it in memory for the next fetch. - value = strdup(cdb->ptr + key_len + 1); // The key was stored there too; skip past it - cdb_free(cdb); - Put(ctdlconfig, key, key_len, value, NULL); - return value; -} - - -// Fetch a system config value - integer -int CtdlGetConfigInt(char *key) { - char *s = CtdlGetConfigStr(key); - if (s) return atoi(s); - return 0; -} - - -// Fetch a system config value - long integer -long CtdlGetConfigLong(char *key) { - char *s = CtdlGetConfigStr(key); - if (s) return atol(s); - return 0; -} - - -void CtdlGetSysConfigBackend(long msgnum, void *userdata) { - config_msgnum = msgnum; -} - - -// This is for fetching longer configuration sets which are stored in the message base. -char *CtdlGetSysConfig(char *sysconfname) { - char hold_rm[ROOMNAMELEN]; - long msgnum = -1; - char *conf; - struct CtdlMessage *msg; - char buf[SIZ]; - - strcpy(hold_rm, CC->room.QRname); - if (CtdlGetRoom(&CC->room, SYSCONFIGROOM) != 0) { - CtdlGetRoom(&CC->room, hold_rm); - return NULL; - } - - // The new way: hunt for the message number in the config database - msgnum = CtdlGetConfigLong(sysconfname); - - // Legacy format: hunt through the local system configuration room for a message with a matching MIME type - if (msgnum <= 0) { - begin_critical_section(S_CONFIG); - config_msgnum = -1; - CtdlForEachMessage(MSGS_LAST, 1, NULL, sysconfname, NULL, CtdlGetSysConfigBackend, NULL); - msgnum = config_msgnum; - end_critical_section(S_CONFIG); - if (msgnum > 0) { - CtdlSetConfigLong(sysconfname, msgnum); // store it the new way so we don't have to do this again - } - } - - if (msgnum <= 0) { - conf = NULL; - } - else { - msg = CtdlFetchMessage(msgnum, 1); - if (msg != NULL) { - conf = strdup(msg->cm_fields[eMesageText]); - CM_Free(msg); - } - else { - conf = NULL; - } - } - - CtdlGetRoom(&CC->room, hold_rm); - - if (conf != NULL) { // Strip the MIME headers, leaving only the content - do { - extract_token(buf, conf, 0, '\n', sizeof buf); - strcpy(conf, &conf[strlen(buf)+1]); - } while ( (!IsEmptyStr(conf)) && (!IsEmptyStr(buf)) ); - } - - return(conf); -} - - -// This is for storing longer configuration sets which are stored in the message base. -void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) { - long old_msgnum = -1; - long new_msgnum = -1; - - // Search for the previous copy of this config item, so we can delete it - old_msgnum = CtdlGetConfigLong(sysconfname); - - // Go ahead and save it, and write the new msgnum to the config database so we can find it again - new_msgnum = CtdlWriteObject(SYSCONFIGROOM, sysconfname, sysconfdata, (strlen(sysconfdata)+1), NULL, 0, 0); - if (new_msgnum > 0) { - CtdlSetConfigLong(sysconfname, new_msgnum); - - // Now delete the old copy - if (old_msgnum > 0) { - CtdlDeleteMessages(SYSCONFIGROOM, &old_msgnum, 1, ""); - } - } -} diff --git a/citadel/config.guess b/citadel/config.guess deleted file mode 100644 index f7727026b..000000000 --- a/citadel/config.guess +++ /dev/null @@ -1,1701 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2021 Free Software Foundation, Inc. - -timestamp='2021-01-01' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. -# -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess -# -# Please send patches to . - - -me=$(echo "$0" | sed -e 's,.*/,,') - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2021 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -tmp= -# shellcheck disable=SC2172 -trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 - -set_cc_for_build() { - # prevent multiple calls if $tmp is already set - test "$tmp" && return 0 - : "${TMPDIR=/tmp}" - # shellcheck disable=SC2039 - { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } - dummy=$tmp/dummy - case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in - ,,) echo "int x;" > "$dummy.c" - for driver in cc gcc c89 c99 ; do - if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$driver" - break - fi - done - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; - esac -} - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if test -f /.attbin/uname ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown -UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown -UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown -UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown - -case "$UNAME_SYSTEM" in -Linux|GNU|GNU/*) - LIBC=unknown - - set_cc_for_build - cat <<-EOF > "$dummy.c" - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #elif defined(__GLIBC__) - LIBC=gnu - #else - #include - /* First heuristic to detect musl libc. */ - #ifdef __DEFINED_va_list - LIBC=musl - #endif - #endif - EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" - - # Second heuristic to detect musl libc. - if [ "$LIBC" = unknown ] && - command -v ldd >/dev/null && - ldd --version 2>&1 | grep -q ^musl; then - LIBC=musl - fi - - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - if [ "$LIBC" = unknown ]; then - LIBC=gnu - fi - ;; -esac - -# Note: order is significant - the case branches are not exclusive. - -case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ - "/sbin/$sysctl" 2>/dev/null || \ - "/usr/sbin/$sysctl" 2>/dev/null || \ - echo unknown)) - case "$UNAME_MACHINE_ARCH" in - aarch64eb) machine=aarch64_be-unknown ;; - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - earmv*) - arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') - endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') - machine="${arch}${endian}"-unknown - ;; - *) machine="$UNAME_MACHINE_ARCH"-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently (or will in the future) and ABI. - case "$UNAME_MACHINE_ARCH" in - earm*) - os=netbsdelf - ;; - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # Determine ABI tags. - case "$UNAME_MACHINE_ARCH" in - earm*) - expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "$UNAME_VERSION" in - Debian*) - release='-gnu' - ;; - *) - release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi-}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') - echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') - echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" - exit ;; - *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') - echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" - exit ;; - *:MidnightBSD:*:*) - echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" - exit ;; - *:ekkoBSD:*:*) - echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" - exit ;; - *:SolidBSD:*:*) - echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" - exit ;; - *:OS108:*:*) - echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:MirBSD:*:*) - echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:Sortix:*:*) - echo "$UNAME_MACHINE"-unknown-sortix - exit ;; - *:Twizzler:*:*) - echo "$UNAME_MACHINE"-unknown-twizzler - exit ;; - *:Redox:*:*) - echo "$UNAME_MACHINE"-unknown-redox - exit ;; - mips:OSF1:*.*) - echo mips-dec-osf1 - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') - ;; - *5.*) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1) - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE=alpha ;; - "EV4.5 (21064)") - UNAME_MACHINE=alpha ;; - "LCA4 (21066/21068)") - UNAME_MACHINE=alpha ;; - "EV5 (21164)") - UNAME_MACHINE=alphaev5 ;; - "EV5.6 (21164A)") - UNAME_MACHINE=alphaev56 ;; - "EV5.6 (21164PC)") - UNAME_MACHINE=alphapca56 ;; - "EV5.7 (21164PC)") - UNAME_MACHINE=alphapca57 ;; - "EV6 (21264)") - UNAME_MACHINE=alphaev6 ;; - "EV6.7 (21264A)") - UNAME_MACHINE=alphaev67 ;; - "EV6.8CB (21264C)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8AL (21264B)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8CX (21264D)") - UNAME_MACHINE=alphaev68 ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE=alphaev69 ;; - "EV7 (21364)") - UNAME_MACHINE=alphaev7 ;; - "EV7.9 (21364A)") - UNAME_MACHINE=alphaev79 ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)" - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix"$UNAME_RELEASE" - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "$( (/bin/universe) 2>/dev/null)" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case $(/usr/bin/uname -p) in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux"$UNAME_RELEASE" - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - set_cc_for_build - SUN_ARCH=i386 - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH=x86_64 - fi - fi - echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - sun4*:SunOS:*:*) - case "$(/usr/bin/arch -k)" in - Series*|S4*) - UNAME_RELEASE=$(uname -v) - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos"$UNAME_RELEASE" - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) - test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "$(/bin/arch)" in - sun3) - echo m68k-sun-sunos"$UNAME_RELEASE" - ;; - sun4) - echo sparc-sun-sunos"$UNAME_RELEASE" - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos"$UNAME_RELEASE" - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint"$UNAME_RELEASE" - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint"$UNAME_RELEASE" - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint"$UNAME_RELEASE" - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten"$UNAME_RELEASE" - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten"$UNAME_RELEASE" - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix"$UNAME_RELEASE" - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix"$UNAME_RELEASE" - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix"$UNAME_RELEASE" - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && - dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && - SYSTEM_NAME=$("$dummy" "$dummyarg") && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos"$UNAME_RELEASE" - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=$(/usr/bin/uname -p) - if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 - then - if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ - test "$TARGET_BINARY_INTERFACE"x = x - then - echo m88k-dg-dgux"$UNAME_RELEASE" - else - echo m88k-dg-dguxbcs"$UNAME_RELEASE" - fi - else - echo i586-dg-dgux"$UNAME_RELEASE" - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if test -x /usr/bin/oslevel ; then - IBM_REV=$(/usr/bin/oslevel) - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }') - if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if test -x /usr/bin/lslpp ; then - IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$IBM_ARCH"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') - case "$UNAME_MACHINE" in - 9000/31?) HP_ARCH=m68000 ;; - 9000/[34]??) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if test -x /usr/bin/getconf; then - sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) - sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) - case "$sc_cpu_version" in - 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 - 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "$sc_kernel_bits" in - 32) HP_ARCH=hppa2.0n ;; - 64) HP_ARCH=hppa2.0w ;; - '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 - esac ;; - esac - fi - if test "$HP_ARCH" = ""; then - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if test "$HP_ARCH" = hppa2.0w - then - set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH=hppa2.0w - else - HP_ARCH=hppa64 - fi - fi - echo "$HP_ARCH"-hp-hpux"$HPUX_REV" - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') - echo ia64-hp-hpux"$HPUX_REV" - exit ;; - 3050*:HI-UX:*:*) - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if test -x /usr/sbin/sysversion ; then - echo "$UNAME_MACHINE"-unknown-osf1mk - else - echo "$UNAME_MACHINE"-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi"$UNAME_RELEASE" - exit ;; - *:BSD/OS:*:*) - echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" - exit ;; - arm:FreeBSD:*:*) - UNAME_PROCESSOR=$(uname -p) - set_cc_for_build - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi - else - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf - fi - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=$(/usr/bin/uname -p) - case "$UNAME_PROCESSOR" in - amd64) - UNAME_PROCESSOR=x86_64 ;; - i386) - UNAME_PROCESSOR=i586 ;; - esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" - exit ;; - i*:CYGWIN*:*) - echo "$UNAME_MACHINE"-pc-cygwin - exit ;; - *:MINGW64*:*) - echo "$UNAME_MACHINE"-pc-mingw64 - exit ;; - *:MINGW*:*) - echo "$UNAME_MACHINE"-pc-mingw32 - exit ;; - *:MSYS*:*) - echo "$UNAME_MACHINE"-pc-msys - exit ;; - i*:PW*:*) - echo "$UNAME_MACHINE"-pc-pw32 - exit ;; - *:Interix*:*) - case "$UNAME_MACHINE" in - x86) - echo i586-pc-interix"$UNAME_RELEASE" - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix"$UNAME_RELEASE" - exit ;; - IA64) - echo ia64-unknown-interix"$UNAME_RELEASE" - exit ;; - esac ;; - i*:UWIN*:*) - echo "$UNAME_MACHINE"-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-pc-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - *:GNU:*:*) - # the GNU system - echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC" - exit ;; - *:Minix:*:*) - echo "$UNAME_MACHINE"-unknown-minix - exit ;; - aarch64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - alpha:Linux:*:*) - case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arm*:Linux:*:*) - set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi - else - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - cris:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - crisv32:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - e2k:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - frv:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - hexagon:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; - ia64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - k1om:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m32r*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m68*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - set_cc_for_build - IS_GLIBC=0 - test x"${LIBC}" = xgnu && IS_GLIBC=1 - sed 's/^ //' << EOF > "$dummy.c" - #undef CPU - #undef mips - #undef mipsel - #undef mips64 - #undef mips64el - #if ${IS_GLIBC} && defined(_ABI64) - LIBCABI=gnuabi64 - #else - #if ${IS_GLIBC} && defined(_ABIN32) - LIBCABI=gnuabin32 - #else - LIBCABI=${LIBC} - #endif - #endif - - #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 - CPU=mipsisa64r6 - #else - #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 - CPU=mipsisa32r6 - #else - #if defined(__mips64) - CPU=mips64 - #else - CPU=mips - #endif - #endif - #endif - - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - MIPS_ENDIAN=el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - MIPS_ENDIAN= - #else - MIPS_ENDIAN= - #endif - #endif -EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" - test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } - ;; - mips64el:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-"$LIBC" - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-"$LIBC" - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-"$LIBC" - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in - PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; - PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; - *) echo hppa-unknown-linux-"$LIBC" ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-"$LIBC" - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-"$LIBC" - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-"$LIBC" - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-"$LIBC" - exit ;; - riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" - exit ;; - sh64*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sh*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - tile*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - vax:Linux:*:*) - echo "$UNAME_MACHINE"-dec-linux-"$LIBC" - exit ;; - x86_64:Linux:*:*) - set_cc_for_build - LIBCABI=$LIBC - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - LIBCABI="$LIBC"x32 - fi - fi - echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" - exit ;; - xtensa*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo "$UNAME_MACHINE"-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo "$UNAME_MACHINE"-unknown-stop - exit ;; - i*86:atheos:*:*) - echo "$UNAME_MACHINE"-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo "$UNAME_MACHINE"-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos"$UNAME_RELEASE" - exit ;; - i*86:*DOS:*:*) - echo "$UNAME_MACHINE"-pc-msdosdjgpp - exit ;; - i*86:*:4.*:*) - UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case $(/bin/uname -X | grep "^Machine") in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=$(sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configure will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos"$UNAME_RELEASE" - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos"$UNAME_RELEASE" - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv"$UNAME_RELEASE" - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=$( (uname -p) 2>/dev/null) - echo "$UNAME_MACHINE"-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo "$UNAME_MACHINE"-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux"$UNAME_RELEASE" - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if test -d /usr/nec; then - echo mips-nec-sysv"$UNAME_RELEASE" - else - echo mips-unknown-sysv"$UNAME_RELEASE" - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux"$UNAME_RELEASE" - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux"$UNAME_RELEASE" - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux"$UNAME_RELEASE" - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux"$UNAME_RELEASE" - exit ;; - SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux"$UNAME_RELEASE" - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody"$UNAME_RELEASE" - exit ;; - *:Rhapsody:*:*) - echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" - exit ;; - arm64:Darwin:*:*) - echo aarch64-apple-darwin"$UNAME_RELEASE" - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=$(uname -p) - case $UNAME_PROCESSOR in - unknown) UNAME_PROCESSOR=powerpc ;; - esac - if command -v xcode-select > /dev/null 2> /dev/null && \ - ! xcode-select --print-path > /dev/null 2> /dev/null ; then - # Avoid executing cc if there is no toolchain installed as - # cc will be a stub that puts up a graphical alert - # prompting the user to install developer tools. - CC_FOR_BUILD=no_compiler_found - else - set_cc_for_build - fi - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # uname -m returns i386 or x86_64 - UNAME_PROCESSOR=$UNAME_MACHINE - fi - echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=$(uname -p) - if test "$UNAME_PROCESSOR" = x86; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-*:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSR-*:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSV-*:NONSTOP_KERNEL:*:*) - echo nsv-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSX-*:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk"$UNAME_RELEASE" - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - # shellcheck disable=SC2154 - if test "$cputype" = 386; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo "$UNAME_MACHINE"-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux"$UNAME_RELEASE" - exit ;; - *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=$( (uname -p) 2>/dev/null) - case "$UNAME_MACHINE" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" - exit ;; - i*86:rdos:*:*) - echo "$UNAME_MACHINE"-pc-rdos - exit ;; - i*86:AROS:*:*) - echo "$UNAME_MACHINE"-pc-aros - exit ;; - x86_64:VMkernel:*:*) - echo "$UNAME_MACHINE"-unknown-esx - exit ;; - amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; - *:Unleashed:*:*) - echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" - exit ;; -esac - -# No uname command or uname output not recognized. -set_cc_for_build -cat > "$dummy.c" < -#include -#endif -#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) -#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) -#include -#if defined(_SIZE_T_) || defined(SIGLOST) -#include -#endif -#endif -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null); - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); -#endif - -#if defined (vax) -#if !defined (ultrix) -#include -#if defined (BSD) -#if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -#else -#if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -#else - printf ("vax-dec-bsd\n"); exit (0); -#endif -#endif -#else - printf ("vax-dec-bsd\n"); exit (0); -#endif -#else -#if defined(_SIZE_T_) || defined(SIGLOST) - struct utsname un; - uname (&un); - printf ("vax-dec-ultrix%s\n", un.release); exit (0); -#else - printf ("vax-dec-ultrix\n"); exit (0); -#endif -#endif -#endif -#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) -#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) -#if defined(_SIZE_T_) || defined(SIGLOST) - struct utsname *un; - uname (&un); - printf ("mips-dec-ultrix%s\n", un.release); exit (0); -#else - printf ("mips-dec-ultrix\n"); exit (0); -#endif -#endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. -test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } - -echo "$0: unable to guess system type" >&2 - -case "$UNAME_MACHINE:$UNAME_SYSTEM" in - mips:Linux | mips64:Linux) - # If we got here on MIPS GNU/Linux, output extra information. - cat >&2 <&2 <&2 </dev/null || echo unknown) -uname -r = $( (uname -r) 2>/dev/null || echo unknown) -uname -s = $( (uname -s) 2>/dev/null || echo unknown) -uname -v = $( (uname -v) 2>/dev/null || echo unknown) - -/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null) -/bin/uname -X = $( (/bin/uname -X) 2>/dev/null) - -hostinfo = $( (hostinfo) 2>/dev/null) -/bin/universe = $( (/bin/universe) 2>/dev/null) -/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) -/bin/arch = $( (/bin/arch) 2>/dev/null) -/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) -/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) - -UNAME_MACHINE = "$UNAME_MACHINE" -UNAME_RELEASE = "$UNAME_RELEASE" -UNAME_SYSTEM = "$UNAME_SYSTEM" -UNAME_VERSION = "$UNAME_VERSION" -EOF -fi - -exit 1 - -# Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/citadel/config.h b/citadel/config.h deleted file mode 100644 index 23f144a48..000000000 --- a/citadel/config.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 1987-2016 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -#include "serv_extensions.h" -#include "citadel_dirs.h" - -// This is the format of the legacy config file. Do not attempt to do anything with it other -// than migrate it into the new format. Seriously -- DO NOT CHANGE IT. The only purpose of this -// struct is to represent the OLD configuration format. -struct legacy_config { - char c_nodename[16]; // short name of this node on a Citadel network - char c_fqdn[64]; // this site's fully qualified domain name - char c_humannode[21]; // human-readable site name - char c_niu_7[16]; - uid_t c_niu_6; - char c_creataide; // 1 = creating a room auto-grants room aide privileges - int c_sleeping; // watchdog timer (seconds) - char c_initax; // initial access level for new users - char c_regiscall; // after c_regiscall logins user will be asked to register - char c_twitdetect; // automatically move messages from problem users to trashcan - char c_twitroom[ROOMNAMELEN]; // name of trashcan - char c_moreprompt[80]; // paginator prompt - char c_restrict; // require per-user permission to send Internet mail - long c_niu_1; - char c_site_location[32]; // geographic location of this Citadel site - char c_sysadm[26]; // name of system administrator - char c_niu_2[15]; - int c_niu_3; - int c_maxsessions; // maximum number of concurrent sessions allowed - char c_ip_addr[20]; // bind address for listening sockets - int c_port_number; // port number for Citadel protocol (usually 504) - int c_niu_4; - struct ExpirePolicy c_ep; // default expire policy for the entire site - int c_userpurge; // user purge time (in days) - int c_roompurge; // room purge time (in days) - char c_logpages[ROOMNAMELEN]; - char c_createax; - long c_maxmsglen; - int c_min_workers; - int c_max_workers; - int c_pop3_port; - int c_smtp_port; - int c_rfc822_strict_from; - int c_aide_zap; - int c_imap_port; - time_t c_net_freq; - char c_disable_newu; - char c_enable_fulltext; - char c_baseroom[ROOMNAMELEN]; - char c_aideroom[ROOMNAMELEN]; - int c_purge_hour; - struct ExpirePolicy c_mbxep; - char c_ldap_host[128]; - int c_ldap_port; - char c_ldap_base_dn[256]; - char c_ldap_bind_dn[256]; - char c_ldap_bind_pw[256]; - int c_msa_port; - int c_imaps_port; - int c_pop3s_port; - int c_smtps_port; - char c_auto_cull; - char c_niu_5; - char c_allow_spoofing; - char c_journal_email; - char c_journal_pubmsgs; - char c_journal_dest[128]; - char c_default_cal_zone[128]; - int c_pftcpdict_port; - int c_niu_9; - int c_auth_mode; - char c_niu_8[256]; - int c_niu_10; - char c_niu_11[256]; - char c_niu_12[256]; - char c_rbl_at_greeting; - char c_master_user[32]; - char c_master_pass[32]; - char c_pager_program[256]; - char c_imap_keep_from; - int c_xmpp_c2s_port; - int c_xmpp_s2s_port; - time_t c_pop3_fetch; - time_t c_pop3_fastest; - int c_spam_flag_only; - int c_guest_logins; - int c_nntp_port; - int c_nntps_port; -}; - - -void initialize_config_system(void); -void shutdown_config_system(void); -void put_config(void); -void CtdlSetConfigStr(char *, char *); -char *CtdlGetConfigStr(char *); -int CtdlGetConfigInt(char *); -long CtdlGetConfigLong(char *); -void CtdlSetConfigInt(char *key, int value); -void CtdlSetConfigLong(char *key, long value); -void CtdlDelConfig(char *key); - -char *CtdlGetSysConfig(char *sysconfname); -void CtdlPutSysConfig(char *sysconfname, char *sysconfdata); -void validate_config(void); -void netcfg_keyname(char *, long); diff --git a/citadel/config.sub b/citadel/config.sub deleted file mode 100644 index b0f849234..000000000 --- a/citadel/config.sub +++ /dev/null @@ -1,1855 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2021 Free Software Foundation, Inc. - -timestamp='2021-01-07' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=$(echo "$0" | sed -e 's,.*/,,') - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS - -Canonicalize a configuration name. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2021 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo "$1" - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Split fields of configuration type -# shellcheck disable=SC2162 -IFS="-" read field1 field2 field3 field4 <&2 - exit 1 - ;; - *-*-*-*) - basic_machine=$field1-$field2 - basic_os=$field3-$field4 - ;; - *-*-*) - # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two - # parts - maybe_os=$field2-$field3 - case $maybe_os in - nto-qnx* | linux-* | uclinux-uclibc* \ - | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ - | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ - | storm-chaos* | os2-emx* | rtmk-nova*) - basic_machine=$field1 - basic_os=$maybe_os - ;; - android-linux) - basic_machine=$field1-unknown - basic_os=linux-android - ;; - *) - basic_machine=$field1-$field2 - basic_os=$field3 - ;; - esac - ;; - *-*) - # A lone config we happen to match not fitting any pattern - case $field1-$field2 in - decstation-3100) - basic_machine=mips-dec - basic_os= - ;; - *-*) - # Second component is usually, but not always the OS - case $field2 in - # Prevent following clause from handling this valid os - sun*os*) - basic_machine=$field1 - basic_os=$field2 - ;; - # Manufacturers - dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ - | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ - | unicom* | ibm* | next | hp | isi* | apollo | altos* \ - | convergent* | ncr* | news | 32* | 3600* | 3100* \ - | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ - | ultra | tti* | harris | dolphin | highlevel | gould \ - | cbm | ns | masscomp | apple | axis | knuth | cray \ - | microblaze* | sim | cisco \ - | oki | wec | wrs | winbond) - basic_machine=$field1-$field2 - basic_os= - ;; - *) - basic_machine=$field1 - basic_os=$field2 - ;; - esac - ;; - esac - ;; - *) - # Convert single-component short-hands not valid as part of - # multi-component configurations. - case $field1 in - 386bsd) - basic_machine=i386-pc - basic_os=bsd - ;; - a29khif) - basic_machine=a29k-amd - basic_os=udi - ;; - adobe68k) - basic_machine=m68010-adobe - basic_os=scout - ;; - alliant) - basic_machine=fx80-alliant - basic_os= - ;; - altos | altos3068) - basic_machine=m68k-altos - basic_os= - ;; - am29k) - basic_machine=a29k-none - basic_os=bsd - ;; - amdahl) - basic_machine=580-amdahl - basic_os=sysv - ;; - amiga) - basic_machine=m68k-unknown - basic_os= - ;; - amigaos | amigados) - basic_machine=m68k-unknown - basic_os=amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - basic_os=sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - basic_os=sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - basic_os=bsd - ;; - aros) - basic_machine=i386-pc - basic_os=aros - ;; - aux) - basic_machine=m68k-apple - basic_os=aux - ;; - balance) - basic_machine=ns32k-sequent - basic_os=dynix - ;; - blackfin) - basic_machine=bfin-unknown - basic_os=linux - ;; - cegcc) - basic_machine=arm-unknown - basic_os=cegcc - ;; - convex-c1) - basic_machine=c1-convex - basic_os=bsd - ;; - convex-c2) - basic_machine=c2-convex - basic_os=bsd - ;; - convex-c32) - basic_machine=c32-convex - basic_os=bsd - ;; - convex-c34) - basic_machine=c34-convex - basic_os=bsd - ;; - convex-c38) - basic_machine=c38-convex - basic_os=bsd - ;; - cray) - basic_machine=j90-cray - basic_os=unicos - ;; - crds | unos) - basic_machine=m68k-crds - basic_os= - ;; - da30) - basic_machine=m68k-da30 - basic_os= - ;; - decstation | pmax | pmin | dec3100 | decstatn) - basic_machine=mips-dec - basic_os= - ;; - delta88) - basic_machine=m88k-motorola - basic_os=sysv3 - ;; - dicos) - basic_machine=i686-pc - basic_os=dicos - ;; - djgpp) - basic_machine=i586-pc - basic_os=msdosdjgpp - ;; - ebmon29k) - basic_machine=a29k-amd - basic_os=ebmon - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - basic_os=ose - ;; - gmicro) - basic_machine=tron-gmicro - basic_os=sysv - ;; - go32) - basic_machine=i386-pc - basic_os=go32 - ;; - h8300hms) - basic_machine=h8300-hitachi - basic_os=hms - ;; - h8300xray) - basic_machine=h8300-hitachi - basic_os=xray - ;; - h8500hms) - basic_machine=h8500-hitachi - basic_os=hms - ;; - harris) - basic_machine=m88k-harris - basic_os=sysv3 - ;; - hp300 | hp300hpux) - basic_machine=m68k-hp - basic_os=hpux - ;; - hp300bsd) - basic_machine=m68k-hp - basic_os=bsd - ;; - hppaosf) - basic_machine=hppa1.1-hp - basic_os=osf - ;; - hppro) - basic_machine=hppa1.1-hp - basic_os=proelf - ;; - i386mach) - basic_machine=i386-mach - basic_os=mach - ;; - isi68 | isi) - basic_machine=m68k-isi - basic_os=sysv - ;; - m68knommu) - basic_machine=m68k-unknown - basic_os=linux - ;; - magnum | m3230) - basic_machine=mips-mips - basic_os=sysv - ;; - merlin) - basic_machine=ns32k-utek - basic_os=sysv - ;; - mingw64) - basic_machine=x86_64-pc - basic_os=mingw64 - ;; - mingw32) - basic_machine=i686-pc - basic_os=mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - basic_os=mingw32ce - ;; - monitor) - basic_machine=m68k-rom68k - basic_os=coff - ;; - morphos) - basic_machine=powerpc-unknown - basic_os=morphos - ;; - moxiebox) - basic_machine=moxie-unknown - basic_os=moxiebox - ;; - msdos) - basic_machine=i386-pc - basic_os=msdos - ;; - msys) - basic_machine=i686-pc - basic_os=msys - ;; - mvs) - basic_machine=i370-ibm - basic_os=mvs - ;; - nacl) - basic_machine=le32-unknown - basic_os=nacl - ;; - ncr3000) - basic_machine=i486-ncr - basic_os=sysv4 - ;; - netbsd386) - basic_machine=i386-pc - basic_os=netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - basic_os=linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - basic_os=newsos - ;; - news1000) - basic_machine=m68030-sony - basic_os=newsos - ;; - necv70) - basic_machine=v70-nec - basic_os=sysv - ;; - nh3000) - basic_machine=m68k-harris - basic_os=cxux - ;; - nh[45]000) - basic_machine=m88k-harris - basic_os=cxux - ;; - nindy960) - basic_machine=i960-intel - basic_os=nindy - ;; - mon960) - basic_machine=i960-intel - basic_os=mon960 - ;; - nonstopux) - basic_machine=mips-compaq - basic_os=nonstopux - ;; - os400) - basic_machine=powerpc-ibm - basic_os=os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - basic_os=ose - ;; - os68k) - basic_machine=m68k-none - basic_os=os68k - ;; - paragon) - basic_machine=i860-intel - basic_os=osf - ;; - parisc) - basic_machine=hppa-unknown - basic_os=linux - ;; - psp) - basic_machine=mipsallegrexel-sony - basic_os=psp - ;; - pw32) - basic_machine=i586-unknown - basic_os=pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - basic_os=rdos - ;; - rdos32) - basic_machine=i386-pc - basic_os=rdos - ;; - rom68k) - basic_machine=m68k-rom68k - basic_os=coff - ;; - sa29200) - basic_machine=a29k-amd - basic_os=udi - ;; - sei) - basic_machine=mips-sei - basic_os=seiux - ;; - sequent) - basic_machine=i386-sequent - basic_os= - ;; - sps7) - basic_machine=m68k-bull - basic_os=sysv2 - ;; - st2000) - basic_machine=m68k-tandem - basic_os= - ;; - stratus) - basic_machine=i860-stratus - basic_os=sysv4 - ;; - sun2) - basic_machine=m68000-sun - basic_os= - ;; - sun2os3) - basic_machine=m68000-sun - basic_os=sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - basic_os=sunos4 - ;; - sun3) - basic_machine=m68k-sun - basic_os= - ;; - sun3os3) - basic_machine=m68k-sun - basic_os=sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - basic_os=sunos4 - ;; - sun4) - basic_machine=sparc-sun - basic_os= - ;; - sun4os3) - basic_machine=sparc-sun - basic_os=sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - basic_os=sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - basic_os=solaris2 - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - basic_os= - ;; - sv1) - basic_machine=sv1-cray - basic_os=unicos - ;; - symmetry) - basic_machine=i386-sequent - basic_os=dynix - ;; - t3e) - basic_machine=alphaev5-cray - basic_os=unicos - ;; - t90) - basic_machine=t90-cray - basic_os=unicos - ;; - toad1) - basic_machine=pdp10-xkl - basic_os=tops20 - ;; - tpf) - basic_machine=s390x-ibm - basic_os=tpf - ;; - udi29k) - basic_machine=a29k-amd - basic_os=udi - ;; - ultra3) - basic_machine=a29k-nyu - basic_os=sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - basic_os=none - ;; - vaxv) - basic_machine=vax-dec - basic_os=sysv - ;; - vms) - basic_machine=vax-dec - basic_os=vms - ;; - vsta) - basic_machine=i386-pc - basic_os=vsta - ;; - vxworks960) - basic_machine=i960-wrs - basic_os=vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - basic_os=vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - basic_os=vxworks - ;; - xbox) - basic_machine=i686-pc - basic_os=mingw32 - ;; - ymp) - basic_machine=ymp-cray - basic_os=unicos - ;; - *) - basic_machine=$1 - basic_os= - ;; - esac - ;; -esac - -# Decode 1-component or ad-hoc basic machines -case $basic_machine in - # Here we handle the default manufacturer of certain CPU types. It is in - # some cases the only manufacturer, in others, it is the most popular. - w89k) - cpu=hppa1.1 - vendor=winbond - ;; - op50n) - cpu=hppa1.1 - vendor=oki - ;; - op60c) - cpu=hppa1.1 - vendor=oki - ;; - ibm*) - cpu=i370 - vendor=ibm - ;; - orion105) - cpu=clipper - vendor=highlevel - ;; - mac | mpw | mac-mpw) - cpu=m68k - vendor=apple - ;; - pmac | pmac-mpw) - cpu=powerpc - vendor=apple - ;; - - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - cpu=m68000 - vendor=att - ;; - 3b*) - cpu=we32k - vendor=att - ;; - bluegene*) - cpu=powerpc - vendor=ibm - basic_os=cnk - ;; - decsystem10* | dec10*) - cpu=pdp10 - vendor=dec - basic_os=tops10 - ;; - decsystem20* | dec20*) - cpu=pdp10 - vendor=dec - basic_os=tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - cpu=m68k - vendor=motorola - ;; - dpx2*) - cpu=m68k - vendor=bull - basic_os=sysv3 - ;; - encore | umax | mmax) - cpu=ns32k - vendor=encore - ;; - elxsi) - cpu=elxsi - vendor=elxsi - basic_os=${basic_os:-bsd} - ;; - fx2800) - cpu=i860 - vendor=alliant - ;; - genix) - cpu=ns32k - vendor=ns - ;; - h3050r* | hiux*) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - cpu=m68000 - vendor=hp - ;; - hp9k3[2-9][0-9]) - cpu=m68k - vendor=hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - i*86v32) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv32 - ;; - i*86v4*) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv4 - ;; - i*86v) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv - ;; - i*86sol2) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=solaris2 - ;; - j90 | j90-cray) - cpu=j90 - vendor=cray - basic_os=${basic_os:-unicos} - ;; - iris | iris4d) - cpu=mips - vendor=sgi - case $basic_os in - irix*) - ;; - *) - basic_os=irix4 - ;; - esac - ;; - miniframe) - cpu=m68000 - vendor=convergent - ;; - *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) - cpu=m68k - vendor=atari - basic_os=mint - ;; - news-3600 | risc-news) - cpu=mips - vendor=sony - basic_os=newsos - ;; - next | m*-next) - cpu=m68k - vendor=next - case $basic_os in - openstep*) - ;; - nextstep*) - ;; - ns2*) - basic_os=nextstep2 - ;; - *) - basic_os=nextstep3 - ;; - esac - ;; - np1) - cpu=np1 - vendor=gould - ;; - op50n-* | op60c-*) - cpu=hppa1.1 - vendor=oki - basic_os=proelf - ;; - pa-hitachi) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - pbd) - cpu=sparc - vendor=tti - ;; - pbb) - cpu=m68k - vendor=tti - ;; - pc532) - cpu=ns32k - vendor=pc532 - ;; - pn) - cpu=pn - vendor=gould - ;; - power) - cpu=power - vendor=ibm - ;; - ps2) - cpu=i386 - vendor=ibm - ;; - rm[46]00) - cpu=mips - vendor=siemens - ;; - rtpc | rtpc-*) - cpu=romp - vendor=ibm - ;; - sde) - cpu=mipsisa32 - vendor=sde - basic_os=${basic_os:-elf} - ;; - simso-wrs) - cpu=sparclite - vendor=wrs - basic_os=vxworks - ;; - tower | tower-32) - cpu=m68k - vendor=ncr - ;; - vpp*|vx|vx-*) - cpu=f301 - vendor=fujitsu - ;; - w65) - cpu=w65 - vendor=wdc - ;; - w89k-*) - cpu=hppa1.1 - vendor=winbond - basic_os=proelf - ;; - none) - cpu=none - vendor=none - ;; - leon|leon[3-9]) - cpu=sparc - vendor=$basic_machine - ;; - leon-*|leon[3-9]-*) - cpu=sparc - vendor=$(echo "$basic_machine" | sed 's/-.*//') - ;; - - *-*) - # shellcheck disable=SC2162 - IFS="-" read cpu vendor <&2 - exit 1 - ;; - esac - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $vendor in - digital*) - vendor=dec - ;; - commodore*) - vendor=cbm - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if test x$basic_os != x -then - -# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just -# set os. -case $basic_os in - gnu/linux*) - kernel=linux - os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') - ;; - os2-emx) - kernel=os2 - os=$(echo $basic_os | sed -e 's|os2-emx|emx|') - ;; - nto-qnx*) - kernel=nto - os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') - ;; - *-*) - # shellcheck disable=SC2162 - IFS="-" read kernel os <&2 - exit 1 - ;; -esac - -# As a final step for OS-related things, validate the OS-kernel combination -# (given a valid OS), if there is a kernel. -case $kernel-$os in - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) - ;; - uclinux-uclibc* ) - ;; - -dietlibc* | -newlib* | -musl* | -uclibc* ) - # These are just libc implementations, not actual OSes, and thus - # require a kernel. - echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 - exit 1 - ;; - kfreebsd*-gnu* | kopensolaris*-gnu*) - ;; - nto-qnx*) - ;; - os2-emx) - ;; - *-eabi* | *-gnueabi*) - ;; - -*) - # Blank kernel with real OS is always fine. - ;; - *-*) - echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 - exit 1 - ;; -esac - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -case $vendor in - unknown) - case $cpu-$os in - *-riscix*) - vendor=acorn - ;; - *-sunos*) - vendor=sun - ;; - *-cnk* | *-aix*) - vendor=ibm - ;; - *-beos*) - vendor=be - ;; - *-hpux*) - vendor=hp - ;; - *-mpeix*) - vendor=hp - ;; - *-hiux*) - vendor=hitachi - ;; - *-unos*) - vendor=crds - ;; - *-dgux*) - vendor=dg - ;; - *-luna*) - vendor=omron - ;; - *-genix*) - vendor=ns - ;; - *-clix*) - vendor=intergraph - ;; - *-mvs* | *-opened*) - vendor=ibm - ;; - *-os400*) - vendor=ibm - ;; - s390-* | s390x-*) - vendor=ibm - ;; - *-ptx*) - vendor=sequent - ;; - *-tpf*) - vendor=ibm - ;; - *-vxsim* | *-vxworks* | *-windiss*) - vendor=wrs - ;; - *-aux*) - vendor=apple - ;; - *-hms*) - vendor=hitachi - ;; - *-mpw* | *-macos*) - vendor=apple - ;; - *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) - vendor=atari - ;; - *-vos*) - vendor=stratus - ;; - esac - ;; -esac - -echo "$cpu-$vendor-${kernel:+$kernel-}$os" -exit - -# Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/citadel/configure b/citadel/configure new file mode 100755 index 000000000..4f2304d72 --- /dev/null +++ b/citadel/configure @@ -0,0 +1,103 @@ +#!/bin/sh + +# CONFIGURE SCRIPT FOR CITADEL SERVER +# This file is part of "conf-IG-ure" +# Copyright (C) 2016-2022 by Art Cancro +# +# This program is open source software. Use, duplication, and/or +# disclosure are subject to the GNU General Purpose License version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +echo +echo +echo Running the configure script to create config.mk +echo + +# Parse the command line arguments +for x in $* +do + a=$1 + k=`echo $a | awk -F= ' { print $1 } '` + v=`echo $a | awk -F= ' { print $2 } '` + + case $k in + --prefix) + PREFIX=$v + ;; + --bindir) + BINDIR=$v + ;; + --ctdldir) + CTDLDIR=$v + ;; + --with-ssl) + SSL=yes + ;; + --without-ssl) + SSL=no + ;; + *) + echo $0 : unknown option $k + echo + echo Valid options are: + echo ' --prefix=PREFIX Install files in PREFIX [/usr/local]' + echo ' --bindir=DIR Install executables in DIR [PREFIX/bin]' + echo ' --ctdldir=DIR Look for Citadel server in DIR [/usr/local/citadel]' + echo ' --with-ssl Force build with OpenSSL support [normally autodetected]' + echo ' --without-ssl Force build without OpenSSL support [normally autodetected]' + exit 1 + ;; + esac + shift +done + +# Set any missing values (FIXME remove the ultra-fatal -W options when finished converting from autotools) + +[ "$PREFIX" = "" ] && PREFIX=/usr/local/citadel +[ "$BINDIR" = "" ] && BINDIR=${PREFIX} +[ "$CTDLDIR" = "" ] && CTDLDIR=${PREFIX} +[ "$CFLAGS" = "" ] && CFLAGS='-ggdb -Werror -Wfatal-errors -Wno-discarded-qualifiers' +[ "$LDFLAGS" = "" ] && LDFLAGS='' + +# Test for OpenSSL +[ "$SSL" != "yes" ] && [ "$SSL" != "no" ] && { + echo Testing for OpenSSL... + tempfile=`tempfile 2>/dev/null` || tempfile=/tmp/configure.$$ + tempcc=${tempfile}.c + cat >$tempcc < +int main(int argc, char **argv) { + SSL_load_error_strings(); + exit(0); +} +! + SSL='no'; + cc $tempcc -lssl -lcrypto -o $tempfile && $tempfile && SSL='yes' + rm -f $tempfile 2>/dev/null + rm -f $tempcc 2>/dev/null +} +echo SSL: $SSL +[ "$SSL" = "yes" ] && { + CFLAGS=${CFLAGS}' -DHAVE_OPENSSL' + LDFLAGS=${LDFLAGS}' -lssl -lcrypto -lz' +} + +# FIXME do a real build id here +CFLAGS=${CFLAGS}' -DBUILD_ID=\"unknown\"' + +# Output the config.mk + +( + echo "CFLAGS := ${CFLAGS}" + echo "LDFLAGS := ${LDFLAGS}" + echo "PREFIX := ${PREFIX}" + echo "BINDIR := ${BINDIR}" + echo "CTDLDIR := ${CTDLDIR}" +) >config.mk + +cat config.mk +echo diff --git a/citadel/context.c b/citadel/context.c deleted file mode 100644 index bca317d23..000000000 --- a/citadel/context.c +++ /dev/null @@ -1,686 +0,0 @@ -// -// Citadel context management stuff. -// Here's where we (hopefully) have all the code that manipulates contexts. -// -// Copyright (c) 1987-2020 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include "ctdl_module.h" -#include "serv_extensions.h" -#include "citserver.h" -#include "user_ops.h" -#include "locate_host.h" -#include "context.h" -#include "control.h" -#include "config.h" - -pthread_key_t MyConKey; /* TSD key for MyContext() */ -CitContext masterCC; -CitContext *ContextList = NULL; -time_t last_purge = 0; /* Last dead session purge */ -int num_sessions = 0; /* Current number of sessions */ -int next_pid = 0; - -/* Flag for single user mode */ -static int want_single_user = 0; - -/* Try to go single user */ - -int CtdlTrySingleUser(void) { - int can_do = 0; - - begin_critical_section(S_SINGLE_USER); - if (want_single_user) - can_do = 0; - else - { - can_do = 1; - want_single_user = 1; - } - end_critical_section(S_SINGLE_USER); - return can_do; -} - - -void CtdlEndSingleUser(void) { - begin_critical_section(S_SINGLE_USER); - want_single_user = 0; - end_critical_section(S_SINGLE_USER); -} - - -int CtdlWantSingleUser(void) { - return want_single_user; -} - - -int CtdlIsSingleUser(void) { - if (want_single_user) { - /* check for only one context here */ - if (num_sessions == 1) - return 1; - } - return 0; -} - - -/* - * Locate a context by its session number and terminate it if the user is able. - * User can NOT terminate their current session. - * User CAN terminate any other session that has them logged in. - * Aide CAN terminate any session except the current one. - */ -int CtdlTerminateOtherSession (int session_num) { - int ret = 0; - CitContext *ccptr; - int aide; - - if (session_num == CC->cs_pid) return TERM_NOTALLOWED; - - aide = ( (CC->user.axlevel >= AxAideU) || (CC->internal_pgm) ) ; - - syslog(LOG_DEBUG, "context: locating session to kill"); - begin_critical_section(S_SESSION_TABLE); - for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) { - if (session_num == ccptr->cs_pid) { - ret |= TERM_FOUND; - if ((ccptr->user.usernum == CC->user.usernum) || aide) { - ret |= TERM_ALLOWED; - } - break; - } - } - - if (((ret & TERM_FOUND) != 0) && ((ret & TERM_ALLOWED) != 0)) - { - if (ccptr->user.usernum == CC->user.usernum) - ccptr->kill_me = KILLME_ADMIN_TERMINATE; - else - ccptr->kill_me = KILLME_IDLE; - end_critical_section(S_SESSION_TABLE); - } - else - end_critical_section(S_SESSION_TABLE); - - return ret; -} - - -/* - * Check to see if the user who we just sent mail to is logged in. If yes, - * bump the 'new mail' counter for their session. That enables them to - * receive a new mail notification without having to hit the database. - */ -void BumpNewMailCounter(long which_user) { - CtdlBumpNewMailCounter(which_user); -} - - -void CtdlBumpNewMailCounter(long which_user) { - CitContext *ptr; - - begin_critical_section(S_SESSION_TABLE); - - for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { - if (ptr->user.usernum == which_user) { - ptr->newmail += 1; - } - } - - end_critical_section(S_SESSION_TABLE); -} - - -/* - * Check to see if a user is currently logged in - * Take care with what you do as a result of this test. - * The user may not have been logged in when this function was called BUT - * because of threading the user might be logged in before you test the result. - */ -int CtdlIsUserLoggedIn(char *user_name) { - CitContext *cptr; - int ret = 0; - - begin_critical_section (S_SESSION_TABLE); - for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { - if (!strcasecmp(cptr->user.fullname, user_name)) { - ret = 1; - break; - } - } - end_critical_section(S_SESSION_TABLE); - return ret; -} - - -/* - * Check to see if a user is currently logged in. - * Basically same as CtdlIsUserLoggedIn() but uses the user number instead. - * Take care with what you do as a result of this test. - * The user may not have been logged in when this function was called BUT - * because of threading the user might be logged in before you test the result. - */ -int CtdlIsUserLoggedInByNum (long usernum) { - CitContext *cptr; - int ret = 0; - - begin_critical_section(S_SESSION_TABLE); - for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { - if (cptr->user.usernum == usernum) { - ret = 1; - } - } - end_critical_section(S_SESSION_TABLE); - return ret; -} - - -/* - * Return a pointer to the CitContext structure bound to the thread which - * called this function. If there's no such binding (for example, if it's - * called by the housekeeper thread) then a generic 'master' CC is returned. - * - * This function is used *VERY* frequently and must be kept small. - */ -CitContext *MyContext(void) { - register CitContext *c; - return ((c = (CitContext *) pthread_getspecific(MyConKey), c == NULL) ? &masterCC : c); -} - - -/* - * Terminate idle sessions. This function pounds through the session table - * comparing the current time to each session's time-of-last-command. If an - * idle session is found it is terminated, then the search restarts at the - * beginning because the pointer to our place in the list becomes invalid. - */ -void terminate_idle_sessions(void) { - CitContext *ccptr; - time_t now; - int killed = 0; - int longrunners = 0; - - now = time(NULL); - begin_critical_section(S_SESSION_TABLE); - for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) { - if ( - (ccptr != CC) - && (CtdlGetConfigLong("c_sleeping") > 0) - && (now - (ccptr->lastcmd) > CtdlGetConfigLong("c_sleeping")) - ) { - if (!ccptr->dont_term) { - ccptr->kill_me = KILLME_IDLE; - ++killed; - } - else { - ++longrunners; - } - } - } - end_critical_section(S_SESSION_TABLE); - if (killed > 0) { - syslog(LOG_INFO, "context: scheduled %d idle sessions for termination", killed); - } - if (longrunners > 0) { - syslog(LOG_INFO, "context: did not terminate %d protected idle sessions", longrunners); - } -} - - -/* - * During shutdown, close the sockets of any sessions still connected. - */ -void terminate_all_sessions(void) { - CitContext *ccptr; - int killed = 0; - - begin_critical_section(S_SESSION_TABLE); - for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) { - if (ccptr->client_socket != -1) - { - syslog(LOG_INFO, "context: terminate_all_sessions() is murdering %s CC[%d]", ccptr->curr_user, ccptr->cs_pid); - close(ccptr->client_socket); - ccptr->client_socket = -1; - killed++; - } - } - end_critical_section(S_SESSION_TABLE); - if (killed > 0) { - syslog(LOG_INFO, "context: flushed %d stuck sessions", killed); - } -} - - -/* - * Terminate a session. - */ -void RemoveContext (CitContext *con) { - const char *c; - if (con == NULL) { - syslog(LOG_ERR, "context: RemoveContext() called with NULL, this should not happen"); - return; - } - c = con->ServiceName; - if (c == NULL) { - c = "WTF?"; - } - syslog(LOG_DEBUG, "context: RemoveContext(%s) session %d", c, con->cs_pid); - - /* Run any cleanup routines registered by loadable modules. - * Note: We have to "become_session()" because the cleanup functions - * might make references to "CC" assuming it's the right one. - */ - become_session(con); - CtdlUserLogout(); - PerformSessionHooks(EVT_STOP); - client_close(); /* If the client is still connected, blow 'em away. */ - become_session(NULL); - - syslog(LOG_INFO, "context: [%3d]SRV[%s] Session ended.", con->cs_pid, c); - - /* - * If the client is still connected, blow 'em away. - * if the socket is 0 or -1, its already gone or was never there. - */ - if (con->client_socket > 0) { - syslog(LOG_INFO, "context: closing socket %d", con->client_socket); - close(con->client_socket); - } - - /* If using AUTHMODE_LDAP, free the DN */ - if (con->ldap_dn) { - free(con->ldap_dn); - con->ldap_dn = NULL; - } - FreeStrBuf(&con->StatusMessage); - FreeStrBuf(&con->MigrateBuf); - FreeStrBuf(&con->RecvBuf.Buf); - if (con->cached_msglist) { - free(con->cached_msglist); - } - - syslog(LOG_DEBUG, "context: done with RemoveContext()"); -} - - -/* - * Initialize a new context and place it in the list. The session number - * used to be the PID (which is why it's called cs_pid), but that was when we - * had one process per session. Now we just assign them sequentially, starting - * at 1 (don't change it to 0 because masterCC uses 0). - */ -CitContext *CreateNewContext(void) { - CitContext *me; - - me = (CitContext *) malloc(sizeof(CitContext)); - if (me == NULL) { - syslog(LOG_ERR, "citserver: malloc() failed: %m"); - return NULL; - } - memset(me, 0, sizeof(CitContext)); - - /* Give the context a name. Hopefully makes it easier to track */ - strcpy (me->user.fullname, "SYS_notauth"); - - /* The new context will be created already in the CON_EXECUTING state - * in order to prevent another thread from grabbing it while it's - * being set up. - */ - me->state = CON_EXECUTING; - /* - * Generate a unique session number and insert this context into - * the list. - */ - me->MigrateBuf = NewStrBuf(); - - me->RecvBuf.Buf = NewStrBuf(); - - me->lastcmd = time(NULL); /* set lastcmd to now to prevent idle timer infanticide TODO: if we have a valid IO, use that to set the timer. */ - - - begin_critical_section(S_SESSION_TABLE); - me->cs_pid = ++next_pid; - me->prev = NULL; - me->next = ContextList; - ContextList = me; - if (me->next != NULL) { - me->next->prev = me; - } - ++num_sessions; - end_critical_section(S_SESSION_TABLE); - return (me); -} - - -/* - * Initialize a new context and place it in the list. The session number - * used to be the PID (which is why it's called cs_pid), but that was when we - * had one process per session. Now we just assign them sequentially, starting - * at 1 (don't change it to 0 because masterCC uses 0). - */ -CitContext *CloneContext(CitContext *CloneMe) { - CitContext *me; - - me = (CitContext *) malloc(sizeof(CitContext)); - if (me == NULL) { - syslog(LOG_ERR, "citserver: malloc() failed: %m"); - return NULL; - } - memcpy(me, CloneMe, sizeof(CitContext)); - - memset(&me->RecvBuf, 0, sizeof(IOBuffer)); - memset(&me->SendBuf, 0, sizeof(IOBuffer)); - memset(&me->SBuf, 0, sizeof(IOBuffer)); - me->MigrateBuf = NULL; - me->sMigrateBuf = NULL; - me->redirect_buffer = NULL; -#ifdef HAVE_OPENSSL - me->ssl = NULL; -#endif - - me->download_fp = NULL; - me->upload_fp = NULL; - /// TODO: what about the room/user? - me->ma = NULL; - me->openid_data = NULL; - me->ldap_dn = NULL; - me->session_specific_data = NULL; - - me->CIT_ICAL = NULL; - - me->cached_msglist = NULL; - me->download_fp = NULL; - me->upload_fp = NULL; - me->client_socket = 0; - - me->MigrateBuf = NewStrBuf(); - me->RecvBuf.Buf = NewStrBuf(); - - begin_critical_section(S_SESSION_TABLE); - { - me->cs_pid = ++next_pid; - me->prev = NULL; - me->next = ContextList; - me->lastcmd = time(NULL); /* set lastcmd to now to prevent idle timer infanticide */ - ContextList = me; - if (me->next != NULL) { - me->next->prev = me; - } - ++num_sessions; - } - end_critical_section(S_SESSION_TABLE); - return (me); -} - - -/* - * Return an array containing a copy of the context list. - * This allows worker threads to perform "for each context" operations without - * having to lock and traverse the live list. - */ -CitContext *CtdlGetContextArray(int *count) { - int nContexts, i; - CitContext *nptr, *cptr; - - nContexts = num_sessions; - nptr = malloc(sizeof(CitContext) * nContexts); - if (!nptr) { - *count = 0; - return NULL; - } - begin_critical_section(S_SESSION_TABLE); - for (cptr = ContextList, i=0; cptr != NULL && i < nContexts; cptr = cptr->next, i++) { - memcpy(&nptr[i], cptr, sizeof (CitContext)); - } - end_critical_section (S_SESSION_TABLE); - - *count = i; - return nptr; -} - - -/* - * Back-end function for starting a session - */ -void begin_session(CitContext *con) { - /* - * Initialize some variables specific to our context. - */ - con->logged_in = 0; - con->internal_pgm = 0; - con->download_fp = NULL; - con->upload_fp = NULL; - con->cached_msglist = NULL; - con->cached_num_msgs = 0; - con->FirstExpressMessage = NULL; - time(&con->lastcmd); - time(&con->lastidle); - strcpy(con->lastcmdname, " "); - strcpy(con->cs_clientname, "(unknown)"); - strcpy(con->curr_user, NLI); - *con->cs_clientinfo = '\0'; - safestrncpy(con->cs_host, CtdlGetConfigStr("c_fqdn"), sizeof con->cs_host); - safestrncpy(con->cs_addr, "", sizeof con->cs_addr); - con->cs_UDSclientUID = -1; - con->cs_host[sizeof con->cs_host - 1] = 0; - if (!CC->is_local_client) { - locate_host(con->cs_host, sizeof con->cs_host, - con->cs_addr, sizeof con->cs_addr, - con->client_socket - ); - } - else { - con->cs_host[0] = 0; - con->cs_addr[0] = 0; -#ifdef HAVE_STRUCT_UCRED - { - /* as http://www.wsinnovations.com/softeng/articles/uds.html told us... */ - struct ucred credentials; - socklen_t ucred_length = sizeof(struct ucred); - - /*fill in the user data structure */ - if(getsockopt(con->client_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length)) { - syslog(LOG_ERR, "context: could obtain credentials from unix domain socket"); - - } - else { - /* the process ID of the process on the other side of the socket */ - /* credentials.pid; */ - - /* the effective UID of the process on the other side of the socket */ - con->cs_UDSclientUID = credentials.uid; - - /* the effective primary GID of the process on the other side of the socket */ - /* credentials.gid; */ - - /* To get supplemental groups, we will have to look them up in our account - database, after a reverse lookup on the UID to get the account name. - We can take this opportunity to check to see if this is a legit account. - */ - snprintf(con->cs_clientinfo, sizeof(con->cs_clientinfo), - "PID: "F_PID_T"; UID: "F_UID_T"; GID: "F_XPID_T" ", - credentials.pid, - credentials.uid, - credentials.gid); - } - } -#endif - } - con->cs_flags = 0; - - con->nologin = 0; - if (((CtdlGetConfigInt("c_maxsessions") > 0)&&(num_sessions > CtdlGetConfigInt("c_maxsessions"))) || CtdlWantSingleUser()) { - con->nologin = 1; - } - - syslog(LOG_INFO, "context: session (%s) started from %s (%s) uid=%d", - con->ServiceName, con->cs_host, con->cs_addr, con->cs_UDSclientUID - ); - - /* Run any session startup routines registered by loadable modules */ - PerformSessionHooks(EVT_START); -} - - -/* - * This function fills in a context and its user field correctly - * Then creates/loads that user - */ -void CtdlFillSystemContext(CitContext *context, char *name) { - char sysname[SIZ]; - long len; - - memset(context, 0, sizeof(CitContext)); - context->internal_pgm = 1; - context->cs_pid = 0; - strcpy (sysname, "SYS_"); - strcat (sysname, name); - len = strlen(sysname); - memcpy(context->curr_user, sysname, len + 1); - context->client_socket = (-1); - context->state = CON_SYS; - context->ServiceName = name; - - /* internal_create_user has the side effect of loading the user regardless of whether they - * already existed or needed to be created - */ - internal_create_user(sysname, &(context->user), -1) ; - - /* Check to see if the system user needs upgrading */ - if (context->user.usernum == 0) { /* old system user with number 0, upgrade it */ - context->user.usernum = get_new_user_number(); - syslog(LOG_INFO, "context: upgrading system user \"%s\" from user number 0 to user number %ld", context->user.fullname, context->user.usernum); - /* add user to the database */ - CtdlPutUser(&(context->user)); - cdb_store(CDB_USERSBYNUMBER, &(context->user.usernum), sizeof(long), context->user.fullname, strlen(context->user.fullname)+1); - } -} - - -/* - * Cleanup any contexts that are left lying around - */ -void context_cleanup(void) { - CitContext *ptr = NULL; - CitContext *rem = NULL; - - /* - * Clean up the contexts. - * There are no threads so no critical_section stuff is needed. - */ - ptr = ContextList; - - /* We need to update the ContextList because some modules may want to itterate it - * Question is should we NULL it before iterating here or should we just keep updating it - * as we remove items? - * - * Answer is to NULL it first to prevent modules from doing any actions on the list at all - */ - ContextList=NULL; - while (ptr != NULL){ - /* Remove the session from the active list */ - rem = ptr->next; - --num_sessions; - - syslog(LOG_DEBUG, "context: context_cleanup() purging session %d", ptr->cs_pid); - RemoveContext(ptr); - free (ptr); - ptr = rem; - } -} - - -/* - * Purge all sessions which have the 'kill_me' flag set. - * This function has code to prevent it from running more than once every - * few seconds, because running it after every single unbind would waste a lot - * of CPU time and keep the context list locked too much. To force it to run - * anyway, set "force" to nonzero. - */ -void dead_session_purge(int force) { - CitContext *ptr, *ptr2; /* general-purpose utility pointer */ - CitContext *rem = NULL; /* list of sessions to be destroyed */ - - if (force == 0) { - if ( (time(NULL) - last_purge) < 5 ) { - return; /* Too soon, go away */ - } - } - time(&last_purge); - - if (try_critical_section(S_SESSION_TABLE)) - return; - - ptr = ContextList; - while (ptr) { - ptr2 = ptr; - ptr = ptr->next; - - if ( (ptr2->state == CON_IDLE) && (ptr2->kill_me) ) { - /* Remove the session from the active list */ - if (ptr2->prev) { - ptr2->prev->next = ptr2->next; - } - else { - ContextList = ptr2->next; - } - if (ptr2->next) { - ptr2->next->prev = ptr2->prev; - } - - --num_sessions; - /* And put it on our to-be-destroyed list */ - ptr2->next = rem; - rem = ptr2; - } - } - end_critical_section(S_SESSION_TABLE); - - /* Now that we no longer have the session list locked, we can take - * our time and destroy any sessions on the to-be-killed list, which - * is allocated privately on this thread's stack. - */ - while (rem != NULL) { - syslog(LOG_DEBUG, "context: dead_session_purge() purging session %d, reason=%d", rem->cs_pid, rem->kill_me); - RemoveContext(rem); - ptr = rem; - rem = rem->next; - free(ptr); - } -} - - -/* - * masterCC is the context we use when not attached to a session. This - * function initializes it. - */ -void InitializeMasterCC(void) { - memset(&masterCC, 0, sizeof(struct CitContext)); - masterCC.internal_pgm = 1; - masterCC.cs_pid = 0; -} - - -/* - * Set the "async waiting" flag for a session, if applicable - */ -void set_async_waiting(struct CitContext *ccptr) { - syslog(LOG_DEBUG, "context: setting async_waiting flag for session %d", ccptr->cs_pid); - if (ccptr->is_async) { - ccptr->async_waiting++; - if (ccptr->state == CON_IDLE) { - ccptr->state = CON_READY; - } - } -} - - -CTDL_MODULE_INIT(session) -{ - if (!threading) { - } - return "session"; -} diff --git a/citadel/context.h b/citadel/context.h deleted file mode 100644 index e76d7acb0..000000000 --- a/citadel/context.h +++ /dev/null @@ -1,179 +0,0 @@ - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include "sysdep.h" -#include "server.h" -#include "sysdep_decls.h" -#include "threads.h" - - -/* - * Values for CitContext.state - * - * A session that is doing nothing is in CON_IDLE state. When activity - * is detected on the socket, it goes to CON_READY, indicating that it - * needs to have a worker thread bound to it. When a thread binds to - * the session, it goes to CON_EXECUTING and does its thing. When the - * transaction is finished, the thread sets it back to CON_IDLE and lets - * it go. - */ -typedef enum __CCState { - CON_IDLE, /* This context is doing nothing */ - CON_GREETING, /* This context needs to output its greeting */ - CON_STARTING, /* This context is outputting its greeting */ - CON_READY, /* This context needs attention */ - CON_EXECUTING, /* This context is bound to a thread */ - CON_SYS /* This is a system context and mustn't be purged */ -} CCState; - -#ifndef __CIT_CONTEXT__ -#define __CIT_CONTEXT__ -typedef struct CitContext CitContext; -#endif - -/* - * This structure keeps track of all information relating to a running - * session on the server. We keep one of these for each session. - */ -struct CitContext { - CitContext *prev; /* Link to previous session in list */ - CitContext *next; /* Link to next session in the list */ - - int cs_pid; /* session ID */ - int dont_term; /* for special activities like artv so we don't get killed */ - double created; /* time of birth */ - time_t lastcmd; /* time of last command executed */ - time_t lastidle; /* For computing idle time */ - CCState state; /* thread state (see CON_ values below) */ - int kill_me; /* Set to nonzero to flag for termination */ - - IOBuffer SendBuf, /* Our write Buffer */ - RecvBuf, /* Our block buffered read buffer */ - SBuf; /* Our block buffered read buffer for clients */ - - StrBuf *MigrateBuf; /* Our block buffered read buffer */ - StrBuf *sMigrateBuf; /* Our block buffered read buffer */ - - int client_socket; - int is_local_client; /* set to 1 if client is running on the same host */ - /* Redirect this session's output to a memory buffer? */ - StrBuf *redirect_buffer; /* the buffer */ - StrBuf *StatusMessage; -#ifdef HAVE_OPENSSL - SSL *ssl; - int redirect_ssl; -#endif - - char curr_user[USERNAME_SIZE]; /* name of current user */ - int logged_in; /* logged in? */ - int internal_pgm; /* authenticated as internal program? */ - int nologin; /* not allowed to log in */ - int curr_view; /* The view type for the current user/room */ - - time_t previous_login; /* Date/time of previous login */ - char lastcmdname[5]; /* name of last command executed */ - unsigned cs_flags; /* miscellaneous flags */ - int is_async; /* Nonzero if client accepts async msgs */ - int async_waiting; /* Nonzero if there are async msgs waiting */ - int input_waiting; /* Nonzero if there is client input waiting */ - int can_receive_im; /* Session is capable of receiving instant messages */ - - /* Client information */ - int cs_clientdev; /* client developer ID */ - int cs_clienttyp; /* client type code */ - int cs_clientver; /* client version number */ - char cs_clientinfo[256];/* if its a unix domain socket, some info for logging. */ - uid_t cs_UDSclientUID; /* the uid of the client when talking via UDS */ - char cs_clientname[32]; /* name of client software */ - char cs_host[64]; /* host logged in from */ - char cs_addr[64]; /* address logged in from */ - - /* The Internet type of thing */ - char cs_principal_id[256]; /* User principal identity for XMPP, ActivityPub, etc. */ - char cs_inet_email[128]; /* Return address of outbound Internet mail */ - char cs_inet_other_emails[1024]; /* User's other valid Internet email addresses */ - char cs_inet_fn[128]; /* Friendly-name of outbound Internet mail */ - - FILE *download_fp; /* Fields relating to file transfer */ - size_t download_fp_total; - char download_desired_section[128]; - FILE *upload_fp; - char upl_file[256]; - char upl_path[PATH_MAX]; - char upl_comment[256]; - char upl_filedir[PATH_MAX]; - char upl_mimetype[64]; - - struct ctdluser user; /* Database record buffers */ - struct ctdlroom room; - - /* A linked list of all instant messages sent to us. */ - struct ExpressMessage *FirstExpressMessage; - int disable_exp; /* Set to 1 to disable incoming pages */ - int newmail; /* Other sessions increment this */ - - /* Preferred MIME formats */ - char preferred_formats[256]; - int msg4_dont_decode; - - /* Dynamically allocated session data */ - void *session_specific_data; /* Used by individual protocol modules */ - struct cit_ical *CIT_ICAL; /* calendaring data */ - struct ma_info *ma; /* multipart/alternative data */ - const char *ServiceName; /* readable purpose of this session */ - long tcp_port; - void *openid_data; /* Data stored by the OpenID module */ - char *ldap_dn; /* DN of user when using AUTHMODE_LDAP */ - - void (*h_command_function) (void) ; /* service command function */ - void (*h_async_function) (void) ; /* do async msgs function */ - void (*h_greeting_function) (void) ; /* greeting function for session startup */ - - long *cached_msglist; /* results of the previous CtdlForEachMessage() */ - int cached_num_msgs; - - char vcard_updated_by_ldap; /* !0 iff ldap changed the vcard, treat as aide update */ -}; - -#define CC MyContext() - -extern pthread_key_t MyConKey; /* TSD key for MyContext() */ -extern int num_sessions; -extern CitContext masterCC; -extern CitContext *ContextList; - -CitContext *MyContext (void); -void RemoveContext (struct CitContext *); -CitContext *CreateNewContext (void); -void context_cleanup(void); -void kill_session (int session_to_kill); -void InitializeMasterCC(void); -void dead_session_purge(int force); -void set_async_waiting(struct CitContext *ccptr); - -CitContext *CloneContext(CitContext *CloneMe); - -/* forcibly close and flush fd's on shutdown */ -void terminate_all_sessions(void); - -/* Deprecated, user CtdlBumpNewMailCounter() instead */ -void BumpNewMailCounter(long) __attribute__ ((deprecated)); - -void terminate_idle_sessions(void); -int CtdlTerminateOtherSession (int session_num); -/* bits returned by CtdlTerminateOtherSession */ -#define TERM_FOUND 0x01 -#define TERM_ALLOWED 0x02 -#define TERM_KILLED 0x03 -#define TERM_NOTALLOWED -1 - -/* - * Bind a thread to a context. (It's inline merely to speed things up.) - */ -static INLINE void become_session(CitContext *which_con) { - pthread_setspecific(MyConKey, (void *)which_con ); -} - -#endif /* CONTEXT_H */ diff --git a/citadel/control.c b/citadel/control.c deleted file mode 100644 index 645904267..000000000 --- a/citadel/control.c +++ /dev/null @@ -1,810 +0,0 @@ -// -// This module handles states which are global to the entire server. -// -// Copyright (c) 1987-2021 by the citadel.org team -// -// This program is open source software. Use, duplication, or disclosure -// is subject to the terms of the GNU General Public License, version 3. -// The program is distributed without any warranty, expressed or implied. - -#include -#include -#include - -#include "ctdl_module.h" -#include "config.h" -#include "citserver.h" -#include "user_ops.h" - -long control_highest_user = 0; - -/* - * This is the legacy "control record" format for the message base. If found - * on disk, its contents will be migrated into the system configuration. Never - * change this. - */ -struct legacy_ctrl_format { - long MMhighest; /* highest message number in file */ - unsigned MMflags; /* Global system flags */ - long MMnextuser; /* highest user number on system */ - long MMnextroom; /* highest room number on system */ - int MM_hosted_upgrade_level; /* Server-hosted upgrade level */ - int MM_fulltext_wordbreaker; /* ID of wordbreaker in use */ - long MMfulltext; /* highest message number indexed */ - int MMdbversion; /* Version of Berkeley DB used on previous server run */ -}; - - -/* - * data that gets passed back and forth between control_find_highest() and its caller - */ -struct cfh { - long highest_roomnum_found; - long highest_msgnum_found; -}; - - -/* - * Callback to get highest room number when rebuilding message base metadata - * - * sanity_diag_mode (can be set by -s flag at startup) may be: - * 0 = attempt to fix inconsistencies - * 1 = show inconsistencies but don't repair them, exit after complete - * 2 = show inconsistencies but don't repair them, continue execution - */ -void control_find_highest(struct ctdlroom *qrbuf, void *data) { - struct cfh *cfh = (struct cfh *)data; - struct cdbdata *cdbfr; - long *msglist; - int num_msgs=0; - int c; - - if (qrbuf->QRnumber > cfh->highest_roomnum_found) { - cfh->highest_roomnum_found = qrbuf->QRnumber; - } - - /* Load the message list */ - cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long)); - if (cdbfr != NULL) { - msglist = (long *) cdbfr->ptr; - num_msgs = cdbfr->len / sizeof(long); - } - else { - return; /* No messages at all? No further action. */ - } - - if (num_msgs > 0) { - for (c=0; c cfh->highest_msgnum_found) { - cfh->highest_msgnum_found = msglist[c]; - } - } - } - - cdb_free(cdbfr); -} - - -/* - * Callback to get highest user number. - */ -void control_find_user(char *username, void *out_data) { - struct ctdluser EachUser; - - if (CtdlGetUser(&EachUser, username) != 0) { - return; - } - - if (EachUser.usernum > CtdlGetConfigLong("MMnextuser")) { - syslog(LOG_DEBUG, "control: fixing MMnextuser %ld > %ld , found in %s", - EachUser.usernum, CtdlGetConfigLong("MMnextuser"), EachUser.fullname - ); - if (!sanity_diag_mode) { - CtdlSetConfigLong("MMnextuser", EachUser.usernum); - } - } -} - - -/* - * If we have a legacy format control record on disk, import it. - */ -void migrate_legacy_control_record(void) { - FILE *fp = NULL; - struct legacy_ctrl_format c; - memset(&c, 0, sizeof(c)); - - fp = fopen("citadel.control", "rb+"); - if (fp != NULL) { - syslog(LOG_INFO, "control: legacy format record found -- importing to db"); - fread(&c, sizeof(struct legacy_ctrl_format), 1, fp); - - CtdlSetConfigLong( "MMhighest", c.MMhighest); - CtdlSetConfigInt( "MMflags", c.MMflags); - CtdlSetConfigLong( "MMnextuser", c.MMnextuser); - CtdlSetConfigLong( "MMnextroom", c.MMnextroom); - CtdlSetConfigInt( "MM_hosted_upgrade_level", c.MM_hosted_upgrade_level); - CtdlSetConfigInt( "MM_fulltext_wordbreaker", c.MM_fulltext_wordbreaker); - CtdlSetConfigLong( "MMfulltext", c.MMfulltext); - - fclose(fp); - if (unlink("citadel.control") != 0) { - fprintf(stderr, "Unable to remove legacy control record after migrating it.\n"); - fprintf(stderr, "Exiting to prevent data corruption.\n"); - exit(CTDLEXIT_CONFIG); - } - } -} - - -/* - * check_control - check the control record has sensible values for message, user and room numbers - */ -void check_control(void) { - - syslog(LOG_INFO, "control: sanity checking the recorded highest message and room numbers"); - struct cfh cfh; - memset(&cfh, 0, sizeof(struct cfh)); - CtdlForEachRoom(control_find_highest, &cfh); - - if (cfh.highest_roomnum_found > CtdlGetConfigLong("MMnextroom")) { - syslog(LOG_DEBUG, "control: fixing MMnextroom %ld > %ld", cfh.highest_roomnum_found, CtdlGetConfigLong("MMnextroom")); - if (!sanity_diag_mode) { - CtdlSetConfigLong("MMnextroom", cfh.highest_roomnum_found); - } - } - - if (cfh.highest_msgnum_found > CtdlGetConfigLong("MMhighest")) { - syslog(LOG_DEBUG, "control: fixing MMhighest %ld > %ld", cfh.highest_msgnum_found, CtdlGetConfigLong("MMhighest")); - if (!sanity_diag_mode) { - CtdlSetConfigLong("MMhighest", cfh.highest_msgnum_found); - } - } - - syslog(LOG_INFO, "control: sanity checking the recorded highest user number"); - ForEachUser(control_find_user, NULL); - - syslog(LOG_INFO, "control: sanity checks complete"); - - if (sanity_diag_mode == 1) { - syslog(LOG_INFO, "control: sanity check diagnostic mode is active - exiting now"); - abort(); - } -} - - -/* - * get_new_message_number() - Obtain a new, unique ID to be used for a message. - */ -long get_new_message_number(void) -{ - long retval = 0L; - begin_critical_section(S_CONTROL); - retval = CtdlGetConfigLong("MMhighest"); - ++retval; - CtdlSetConfigLong("MMhighest", retval); - end_critical_section(S_CONTROL); - return(retval); -} - - -/* - * CtdlGetCurrentMessageNumber() - Obtain the current highest message number in the system - * This provides a quick way to initialise a variable that might be used to indicate - * messages that should not be processed. For example, an inbox rules script will use this - * to record determine that messages older than this should not be processed. - * - * (Why is this function here? Can't we just go straight to the config variable it fetches?) - */ -long CtdlGetCurrentMessageNumber(void) -{ - return CtdlGetConfigLong("MMhighest"); -} - - -/* - * get_new_user_number() - Obtain a new, unique ID to be used for a user. - */ -long get_new_user_number(void) -{ - long retval = 0L; - begin_critical_section(S_CONTROL); - retval = CtdlGetConfigLong("MMnextuser"); - ++retval; - CtdlSetConfigLong("MMnextuser", retval); - end_critical_section(S_CONTROL); - return(retval); -} - - -/* - * get_new_room_number() - Obtain a new, unique ID to be used for a room. - */ -long get_new_room_number(void) -{ - long retval = 0L; - begin_critical_section(S_CONTROL); - retval = CtdlGetConfigLong("MMnextroom"); - ++retval; - CtdlSetConfigLong("MMnextroom", retval); - end_critical_section(S_CONTROL); - return(retval); -} - - -/* - * Helper function for cmd_conf() to handle boolean values - */ -int confbool(char *v) -{ - if (IsEmptyStr(v)) return(0); - if (atoi(v) != 0) return(1); - return(0); -} - - -/* - * Get or set global configuration options - */ -void cmd_conf(char *argbuf) -{ - char cmd[16]; - char buf[1024]; - int a, i; - long ii; - char *confptr; - char confname[128]; - - if (CtdlAccessCheck(ac_aide)) return; - - extract_token(cmd, argbuf, 0, '|', sizeof cmd); - - // CONF GET - retrieve system configuration in legacy format - // This is deprecated; please do not add fields or change their order. - if (!strcasecmp(cmd, "GET")) { - cprintf("%d Configuration...\n", LISTING_FOLLOWS); - cprintf("%s\n", CtdlGetConfigStr("c_nodename")); - cprintf("%s\n", CtdlGetConfigStr("c_fqdn")); - cprintf("%s\n", CtdlGetConfigStr("c_humannode")); - cprintf("xxx\n"); /* placeholder -- field no longer in use */ - cprintf("%d\n", CtdlGetConfigInt("c_creataide")); - cprintf("%d\n", CtdlGetConfigInt("c_sleeping")); - cprintf("%d\n", CtdlGetConfigInt("c_initax")); - cprintf("%d\n", CtdlGetConfigInt("c_regiscall")); - cprintf("%d\n", CtdlGetConfigInt("c_twitdetect")); - cprintf("%s\n", CtdlGetConfigStr("c_twitroom")); - cprintf("%s\n", CtdlGetConfigStr("c_moreprompt")); - cprintf("%d\n", CtdlGetConfigInt("c_restrict")); - cprintf("%s\n", CtdlGetConfigStr("c_site_location")); - cprintf("%s\n", CtdlGetConfigStr("c_sysadm")); - cprintf("%d\n", CtdlGetConfigInt("c_maxsessions")); - cprintf("xxx\n"); /* placeholder -- field no longer in use */ - cprintf("%d\n", CtdlGetConfigInt("c_userpurge")); - cprintf("%d\n", CtdlGetConfigInt("c_roompurge")); - cprintf("%s\n", CtdlGetConfigStr("c_logpages")); - cprintf("%d\n", CtdlGetConfigInt("c_createax")); - cprintf("%ld\n", CtdlGetConfigLong("c_maxmsglen")); - cprintf("%d\n", CtdlGetConfigInt("c_min_workers")); - cprintf("%d\n", CtdlGetConfigInt("c_max_workers")); - cprintf("%d\n", CtdlGetConfigInt("c_pop3_port")); - cprintf("%d\n", CtdlGetConfigInt("c_smtp_port")); - cprintf("%d\n", CtdlGetConfigInt("c_rfc822_strict_from")); - cprintf("%d\n", CtdlGetConfigInt("c_aide_zap")); - cprintf("%d\n", CtdlGetConfigInt("c_imap_port")); - cprintf("%ld\n", CtdlGetConfigLong("c_net_freq")); - cprintf("%d\n", CtdlGetConfigInt("c_disable_newu")); - cprintf("1\n"); /* niu */ - cprintf("%d\n", CtdlGetConfigInt("c_purge_hour")); - cprintf("%s\n", CtdlGetConfigStr("c_ldap_host")); - cprintf("%d\n", CtdlGetConfigInt("c_ldap_port")); - cprintf("%s\n", CtdlGetConfigStr("c_ldap_base_dn")); - cprintf("%s\n", CtdlGetConfigStr("c_ldap_bind_dn")); - cprintf("%s\n", CtdlGetConfigStr("c_ldap_bind_pw")); - cprintf("%s\n", CtdlGetConfigStr("c_ip_addr")); - cprintf("%d\n", CtdlGetConfigInt("c_msa_port")); - cprintf("%d\n", CtdlGetConfigInt("c_imaps_port")); - cprintf("%d\n", CtdlGetConfigInt("c_pop3s_port")); - cprintf("%d\n", CtdlGetConfigInt("c_smtps_port")); - cprintf("%d\n", CtdlGetConfigInt("c_enable_fulltext")); - cprintf("%d\n", CtdlGetConfigInt("c_auto_cull")); - cprintf("1\n"); - cprintf("%d\n", CtdlGetConfigInt("c_allow_spoofing")); - cprintf("%d\n", CtdlGetConfigInt("c_journal_email")); - cprintf("%d\n", CtdlGetConfigInt("c_journal_pubmsgs")); - cprintf("%s\n", CtdlGetConfigStr("c_journal_dest")); - cprintf("%s\n", CtdlGetConfigStr("c_default_cal_zone")); - cprintf("%d\n", CtdlGetConfigInt("c_pftcpdict_port")); - cprintf("0\n"); - cprintf("%d\n", CtdlGetConfigInt("c_auth_mode")); - cprintf("\n"); - cprintf("\n"); - cprintf("\n"); - cprintf("\n"); - cprintf("%d\n", CtdlGetConfigInt("c_rbl_at_greeting")); - cprintf("\n"); - cprintf("\n"); - cprintf("%s\n", CtdlGetConfigStr("c_pager_program")); - cprintf("%d\n", CtdlGetConfigInt("c_imap_keep_from")); - cprintf("%d\n", CtdlGetConfigInt("c_xmpp_c2s_port")); - cprintf("%d\n", CtdlGetConfigInt("c_xmpp_s2s_port")); - cprintf("%ld\n", CtdlGetConfigLong("c_pop3_fetch")); - cprintf("%ld\n", CtdlGetConfigLong("c_pop3_fastest")); - cprintf("%d\n", CtdlGetConfigInt("c_spam_flag_only")); - cprintf("%d\n", CtdlGetConfigInt("c_guest_logins")); - cprintf("%d\n", CtdlGetConfigInt("c_port_number")); - cprintf("%d\n", ctdluid); - cprintf("%d\n", CtdlGetConfigInt("c_nntp_port")); - cprintf("%d\n", CtdlGetConfigInt("c_nntps_port")); - cprintf("000\n"); - } - - // CONF SET - set system configuration in legacy format - // This is deprecated; please do not add fields or change their order. - else if (!strcasecmp(cmd, "SET")) { - unbuffer_output(); - cprintf("%d Send configuration...\n", SEND_LISTING); - a = 0; - while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) { - switch (a) { - case 0: - CtdlSetConfigStr("c_nodename", buf); - break; - case 1: - CtdlSetConfigStr("c_fqdn", buf); - break; - case 2: - CtdlSetConfigStr("c_humannode", buf); - break; - case 3: - /* placeholder -- field no longer in use */ - break; - case 4: - CtdlSetConfigInt("c_creataide", confbool(buf)); - break; - case 5: - CtdlSetConfigInt("c_sleeping", atoi(buf)); - break; - case 6: - i = atoi(buf); - if (i < 1) i = 1; - if (i > 6) i = 6; - CtdlSetConfigInt("c_initax", i); - break; - case 7: - CtdlSetConfigInt("c_regiscall", confbool(buf)); - break; - case 8: - CtdlSetConfigInt("c_twitdetect", confbool(buf)); - break; - case 9: - CtdlSetConfigStr("c_twitroom", buf); - break; - case 10: - CtdlSetConfigStr("c_moreprompt", buf); - break; - case 11: - CtdlSetConfigInt("c_restrict", confbool(buf)); - break; - case 12: - CtdlSetConfigStr("c_site_location", buf); - break; - case 13: - CtdlSetConfigStr("c_sysadm", buf); - break; - case 14: - i = atoi(buf); - if (i < 0) i = 0; - CtdlSetConfigInt("c_maxsessions", i); - break; - case 15: - /* placeholder -- field no longer in use */ - break; - case 16: - CtdlSetConfigInt("c_userpurge", atoi(buf)); - break; - case 17: - CtdlSetConfigInt("c_roompurge", atoi(buf)); - break; - case 18: - CtdlSetConfigStr("c_logpages", buf); - break; - case 19: - i = atoi(buf); - if (i < 1) i = 1; - if (i > 6) i = 6; - CtdlSetConfigInt("c_createax", i); - break; - case 20: - ii = atol(buf); - if (ii >= 8192) { - CtdlSetConfigLong("c_maxmsglen", ii); - } - break; - case 21: - i = atoi(buf); - if (i >= 3) { // minimum value - CtdlSetConfigInt("c_min_workers", i); - } - break; - case 22: - i = atoi(buf); - if (i >= CtdlGetConfigInt("c_min_workers")) { // max must be >= min - CtdlSetConfigInt("c_max_workers", i); - } - break; - case 23: - CtdlSetConfigInt("c_pop3_port", atoi(buf)); - break; - case 24: - CtdlSetConfigInt("c_smtp_port", atoi(buf)); - break; - case 25: - CtdlSetConfigInt("c_rfc822_strict_from", atoi(buf)); - break; - case 26: - CtdlSetConfigInt("c_aide_zap", confbool(buf)); - break; - case 27: - CtdlSetConfigInt("c_imap_port", atoi(buf)); - break; - case 28: - CtdlSetConfigLong("c_net_freq", atol(buf)); - break; - case 29: - CtdlSetConfigInt("c_disable_newu", confbool(buf)); - break; - case 30: - /* niu */ - break; - case 31: - i = atoi(buf); - if ((i >= 0) && (i <= 23)) { - CtdlSetConfigInt("c_purge_hour", i); - } - break; - case 32: - CtdlSetConfigStr("c_ldap_host", buf); - break; - case 33: - CtdlSetConfigInt("c_ldap_port", atoi(buf)); - break; - case 34: - CtdlSetConfigStr("c_ldap_base_dn", buf); - break; - case 35: - CtdlSetConfigStr("c_ldap_bind_dn", buf); - break; - case 36: - CtdlSetConfigStr("c_ldap_bind_pw", buf); - break; - case 37: - CtdlSetConfigStr("c_ip_addr", buf); - break; - case 38: - CtdlSetConfigInt("c_msa_port", atoi(buf)); - break; - case 39: - CtdlSetConfigInt("c_imaps_port", atoi(buf)); - break; - case 40: - CtdlSetConfigInt("c_pop3s_port", atoi(buf)); - break; - case 41: - CtdlSetConfigInt("c_smtps_port", atoi(buf)); - break; - case 42: - CtdlSetConfigInt("c_enable_fulltext", confbool(buf)); - break; - case 43: - CtdlSetConfigInt("c_auto_cull", confbool(buf)); - break; - case 44: - /* niu */ - break; - case 45: - CtdlSetConfigInt("c_allow_spoofing", confbool(buf)); - break; - case 46: - CtdlSetConfigInt("c_journal_email", confbool(buf)); - break; - case 47: - CtdlSetConfigInt("c_journal_pubmsgs", confbool(buf)); - break; - case 48: - CtdlSetConfigStr("c_journal_dest", buf); - break; - case 49: - CtdlSetConfigStr("c_default_cal_zone", buf); - break; - case 50: - CtdlSetConfigInt("c_pftcpdict_port", atoi(buf)); - break; - case 51: - /* niu */ - break; - case 52: - CtdlSetConfigInt("c_auth_mode", atoi(buf)); - break; - case 53: - /* niu */ - break; - case 54: - /* niu */ - break; - case 55: - /* niu */ - break; - case 56: - /* niu */ - break; - case 57: - CtdlSetConfigInt("c_rbl_at_greeting", confbool(buf)); - break; - case 58: - /* niu */ - break; - case 59: - /* niu */ - break; - case 60: - CtdlSetConfigStr("c_pager_program", buf); - break; - case 61: - CtdlSetConfigInt("c_imap_keep_from", confbool(buf)); - break; - case 62: - CtdlSetConfigInt("c_xmpp_c2s_port", atoi(buf)); - break; - case 63: - CtdlSetConfigInt("c_xmpp_s2s_port", atoi(buf)); - break; - case 64: - CtdlSetConfigLong("c_pop3_fetch", atol(buf)); - break; - case 65: - CtdlSetConfigLong("c_pop3_fastest", atol(buf)); - break; - case 66: - CtdlSetConfigInt("c_spam_flag_only", confbool(buf)); - break; - case 67: - CtdlSetConfigInt("c_guest_logins", confbool(buf)); - break; - case 68: - CtdlSetConfigInt("c_port_number", atoi(buf)); - break; - case 69: - /* niu */ - break; - case 70: - CtdlSetConfigInt("c_nntp_port", atoi(buf)); - break; - case 71: - CtdlSetConfigInt("c_nntps_port", atoi(buf)); - break; - } - ++a; - } - snprintf(buf, sizeof buf, - "The global system configuration has been edited by %s.\n", - (CC->logged_in ? CC->curr_user : "an administrator") - ); - CtdlAideMessage(buf, "Citadel Configuration Manager Message"); - - if (!IsEmptyStr(CtdlGetConfigStr("c_logpages"))) - CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS); - - /* If full text indexing has been disabled, invalidate the - * index so it doesn't try to use it later. - */ - if (CtdlGetConfigInt("c_enable_fulltext") == 0) { - CtdlSetConfigInt("MM_fulltext_wordbreaker", 0); - } - } - - // CONF GETSYS - retrieve arbitrary system configuration stanzas stored in the message base - else if (!strcasecmp(cmd, "GETSYS")) { - extract_token(confname, argbuf, 1, '|', sizeof confname); - confptr = CtdlGetSysConfig(confname); - if (confptr != NULL) { - long len; - - len = strlen(confptr); - cprintf("%d %s\n", LISTING_FOLLOWS, confname); - client_write(confptr, len); - if ((len > 0) && (confptr[len - 1] != 10)) - client_write("\n", 1); - cprintf("000\n"); - free(confptr); - } else { - cprintf("%d No such configuration.\n", - ERROR + ILLEGAL_VALUE); - } - } - - // CONF PUTSYS - store arbitrary system configuration stanzas in the message base - else if (!strcasecmp(cmd, "PUTSYS")) { - extract_token(confname, argbuf, 1, '|', sizeof confname); - unbuffer_output(); - cprintf("%d %s\n", SEND_LISTING, confname); - confptr = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0); - CtdlPutSysConfig(confname, confptr); - free(confptr); - } - - // CONF GETVAL - retrieve configuration variables from the database - // CONF LOADVAL - same thing but can handle variables bigger than 1 KB (deprecated and probably safe to remove) - else if ( (!strcasecmp(cmd, "GETVAL")) || (!strcasecmp(cmd, "LOADVAL")) ) { - extract_token(confname, argbuf, 1, '|', sizeof confname); - char *v = CtdlGetConfigStr(confname); - if ( (v) && (!strcasecmp(cmd, "GETVAL")) ) { - cprintf("%d %s|\n", CIT_OK, v); - } - else if ( (v) && (!strcasecmp(cmd, "LOADVAL")) ) { - cprintf("%d %d\n", BINARY_FOLLOWS, (int)strlen(v)); - client_write(v, strlen(v)); - } - else { - cprintf("%d |\n", ERROR); - } - } - - // CONF PUTVAL - store configuration variables in the database - else if (!strcasecmp(cmd, "PUTVAL")) { - if (num_tokens(argbuf, '|') < 3) { - cprintf("%d name and value required\n", ERROR); - } - else { - extract_token(confname, argbuf, 1, '|', sizeof confname); - extract_token(buf, argbuf, 2, '|', sizeof buf); - CtdlSetConfigStr(confname, buf); - cprintf("%d setting '%s' to '%s'\n", CIT_OK, confname, buf); - } - } - - // CONF STOREVAL - store configuration variables in the database bigger than 1 KB (deprecated and probably safe to remove) - else if (!strcasecmp(cmd, "STOREVAL")) { - if (num_tokens(argbuf, '|') < 3) { - cprintf("%d name and length required\n", ERROR); - } - else { - extract_token(confname, argbuf, 1, '|', sizeof confname); - int bytes = extract_int(argbuf, 2); - char *valbuf = malloc(bytes + 1); - cprintf("%d %d\n", SEND_BINARY, bytes); - client_read(valbuf, bytes); - valbuf[bytes+1] = 0; - CtdlSetConfigStr(confname, valbuf); - free(valbuf); - } - } - - // CONF LISTVAL - list configuration variables in the database and their values - else if (!strcasecmp(cmd, "LISTVAL")) { - struct cdbdata *cdbcfg; - int keylen = 0; - char *key = NULL; - char *value = NULL; - - cprintf("%d all configuration variables\n", LISTING_FOLLOWS); - cdb_rewind(CDB_CONFIG); - while (cdbcfg = cdb_next_item(CDB_CONFIG), cdbcfg != NULL) { - if (cdbcfg->len < 1020) { - keylen = strlen(cdbcfg->ptr); - key = cdbcfg->ptr; - value = cdbcfg->ptr + keylen + 1; - cprintf("%s|%s\n", key, value); - } - cdb_free(cdbcfg); - } - cprintf("000\n"); - } - - else { - cprintf("%d Illegal option(s) specified.\n", ERROR + ILLEGAL_VALUE); - } -} - - -typedef struct __ConfType { - ConstStr Name; - long Type; -}ConfType; - -ConfType CfgNames[] = { - { {HKEY("localhost") }, 0}, - { {HKEY("directory") }, 0}, - { {HKEY("smarthost") }, 2}, - { {HKEY("fallbackhost") }, 2}, - { {HKEY("rbl") }, 3}, - { {HKEY("spamassassin") }, 3}, - { {HKEY("masqdomain") }, 1}, - { {HKEY("clamav") }, 3}, - { {HKEY("notify") }, 3}, - { {NULL, 0}, 0} -}; - -HashList *CfgNameHash = NULL; -void cmd_gvdn(char *argbuf) -{ - const ConfType *pCfg; - char *confptr; - long min = atol(argbuf); - const char *Pos = NULL; - const char *PPos = NULL; - const char *HKey; - long HKLen; - StrBuf *Line; - StrBuf *Config; - StrBuf *Cfg; - StrBuf *CfgToken; - HashList *List; - HashPos *It; - void *vptr; - - List = NewHash(1, NULL); - Cfg = NewStrBufPlain(CtdlGetConfigStr("c_fqdn"), -1); - Put(List, SKEY(Cfg), Cfg, HFreeStrBuf); - Cfg = NULL; - - confptr = CtdlGetSysConfig(INTERNETCFG); - Config = NewStrBufPlain(confptr, -1); - free(confptr); - - Line = NewStrBufPlain(NULL, StrLength(Config)); - CfgToken = NewStrBufPlain(NULL, StrLength(Config)); - while (StrBufSipLine(Line, Config, &Pos)) - { - if (Cfg == NULL) - Cfg = NewStrBufPlain(NULL, StrLength(Line)); - PPos = NULL; - StrBufExtract_NextToken(Cfg, Line, &PPos, '|'); - StrBufExtract_NextToken(CfgToken, Line, &PPos, '|'); - if (GetHash(CfgNameHash, SKEY(CfgToken), &vptr) && - (vptr != NULL)) - { - pCfg = (ConfType *) vptr; - if (pCfg->Type <= min) - { - Put(List, SKEY(Cfg), Cfg, HFreeStrBuf); - Cfg = NULL; - } - } - } - - cprintf("%d Valid Domains\n", LISTING_FOLLOWS); - It = GetNewHashPos(List, 1); - while (GetNextHashPos(List, It, &HKLen, &HKey, &vptr)) - { - cputbuf(vptr); - cprintf("\n"); - } - cprintf("000\n"); - - DeleteHashPos(&It); - DeleteHash(&List); - FreeStrBuf(&Cfg); - FreeStrBuf(&Line); - FreeStrBuf(&CfgToken); - FreeStrBuf(&Config); -} - -/*****************************************************************************/ -/* MODULE INITIALIZATION STUFF */ -/*****************************************************************************/ - -CTDL_MODULE_INIT(control) -{ - if (!threading) { - int i; - - CfgNameHash = NewHash(1, NULL); - for (i = 0; CfgNames[i].Name.Key != NULL; i++) - Put(CfgNameHash, CKEY(CfgNames[i].Name), &CfgNames[i], reference_free_handler); - - CtdlRegisterProtoHook(cmd_gvdn, "GVDN", "get valid domain names"); - CtdlRegisterProtoHook(cmd_conf, "CONF", "get/set system configuration"); - } - /* return our id for the Log */ - return "control"; -} diff --git a/citadel/control.h b/citadel/control.h deleted file mode 100644 index 649d3fd85..000000000 --- a/citadel/control.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 1987-2015 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -void get_control (void); -void put_control (void); -void check_control(void); -long int get_new_message_number (void); -long int get_new_user_number (void); -long int get_new_room_number (void); -void migrate_legacy_control_record(void); diff --git a/citadel/ctdl_module.h b/citadel/ctdl_module.h deleted file mode 100644 index 0ddfc6970..000000000 --- a/citadel/ctdl_module.h +++ /dev/null @@ -1,326 +0,0 @@ - -#ifndef CTDL_MODULE_H -#define CTDL_MODULE_H - -#include "sysdep.h" - -#ifdef HAVE_GC -#define GC_THREADS -#define GC_REDIRECT_TO_LOCAL -#include -#else -#define GC_MALLOC malloc -#define GC_MALLOC_ATOMIC malloc -#define GC_FREE free -#define GC_REALLOC realloc -#endif - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_STRINGS_H -#include -#endif -#include - -#include - -#include "server.h" -#include "sysdep_decls.h" -#include "msgbase.h" -#include "threads.h" -#include "citadel_dirs.h" -#include "context.h" - -/* - * define macros for module init stuff - */ - -#define CTDL_MODULE_INIT(module_name) char *ctdl_module_##module_name##_init (int threading) - -#define CTDL_INIT_CALL(module_name) ctdl_module_##module_name##_init (threading) - -#define CTDL_MODULE_UPGRADE(module_name) char *ctdl_module_##module_name##_upgrade (void) - -#define CTDL_UPGRADE_CALL(module_name) ctdl_module_##module_name##_upgrade () - -#define CtdlAideMessage(TEXT, SUBJECT) \ - quickie_message( \ - "Citadel", \ - NULL, \ - NULL, \ - AIDEROOM, \ - TEXT, \ - FMT_CITADEL, \ - SUBJECT) - -/* - * Hook functions available to modules. - */ -/* Priorities for */ -#define PRIO_QUEUE 500 -#define PRIO_AGGR 1000 -#define PRIO_SEND 1500 -#define PRIO_CLEANUP 2000 -/* Priorities for EVT_HOUSE */ -#define PRIO_HOUSE 3000 -/* Priorities for EVT_LOGIN */ -#define PRIO_CREATE 10000 -/* Priorities for EVT_LOGOUT */ -#define PRIO_LOGOUT 15000 -/* Priorities for EVT_LOGIN */ -#define PRIO_LOGIN 20000 -/* Priorities for EVT_START */ -#define PRIO_START 25000 -/* Priorities for EVT_STOP */ -#define PRIO_STOP 30000 -/* Priorities for EVT_ASYNC */ -#define PRIO_ASYNC 35000 -/* Priorities for EVT_SHUTDOWN */ -#define PRIO_SHUTDOWN 40000 -/* Priorities for EVT_UNSTEALTH */ -#define PRIO_UNSTEALTH 45000 -/* Priorities for EVT_STEALTH */ -#define PRIO_STEALTH 50000 - - -void CtdlRegisterSessionHook(void (*fcn_ptr)(void), int EventType, int Priority); -void CtdlUnregisterSessionHook(void (*fcn_ptr)(void), int EventType); -void CtdlShutdownServiceHooks(void); - -void CtdlRegisterUserHook(void (*fcn_ptr)(struct ctdluser *), int EventType); -void CtdlUnregisterUserHook(void (*fcn_ptr)(struct ctdluser *), int EventType); - -void CtdlRegisterXmsgHook(int (*fcn_ptr)(char *, char *, char *, char *), int order); -void CtdlUnregisterXmsgHook(int (*fcn_ptr)(char *, char *, char *, char *), int order); - -void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *, struct recptypes *), int EventType); -void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *, struct recptypes *), int EventType); - -void CtdlRegisterRoomHook(int (*fcn_ptr)(struct ctdlroom *) ); -void CtdlUnregisterRoomHook(int (*fnc_ptr)(struct ctdlroom *) ); - -void CtdlRegisterDeleteHook(void (*handler)(char *, long) ); -void CtdlUnregisterDeleteHook(void (*handler)(char *, long) ); - -void CtdlRegisterCleanupHook(void (*fcn_ptr)(void)); -void CtdlUnregisterCleanupHook(void (*fcn_ptr)(void)); - -void CtdlRegisterEVCleanupHook(void (*fcn_ptr)(void)); -void CtdlUnregisterEVCleanupHook(void (*fcn_ptr)(void)); - -void CtdlRegisterProtoHook(void (*handler)(char *), char *cmd, char *desc); - -void CtdlRegisterServiceHook(int tcp_port, - char *sockpath, - void (*h_greeting_function) (void), - void (*h_command_function) (void), - void (*h_async_function) (void), - const char *ServiceName -); -void CtdlUnregisterServiceHook(int tcp_port, - char *sockpath, - void (*h_greeting_function) (void), - void (*h_command_function) (void), - void (*h_async_function) (void) -); - -void CtdlRegisterFixedOutputHook(char *content_type, void (*output_function) (char *supplied_data, int len)); -void CtdlUnRegisterFixedOutputHook(char *content_type); -void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc) (void *arg)); -void CtdlRegisterSearchFuncHook(void (*fcn_ptr)(int *, long **, const char *), char *name); - -/* - * if you say a) (which may take a while) - * don't forget to say b) - */ -void CtdlDisableHouseKeeping(void); -void CtdlEnableHouseKeeping(void); - -/* TODODRW: This needs to be changed into a hook type interface - * for now we have this horrible hack - */ -void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response); - -/* return the current context list as an array and do it in a safe manner - * The returned data is a copy so only reading is useful - * The number of contexts is returned in count. - * Beware, this does not copy any of the data pointed to by the context. - * This means that you can not rely on things like the redirect buffer being valid. - * You must free the returned pointer when done. - */ -struct CitContext *CtdlGetContextArray (int *count); -void CtdlFillSystemContext(struct CitContext *context, char *name); -int CtdlTrySingleUser(void); -void CtdlEndSingleUser(void); -int CtdlWantSingleUser(void); -int CtdlIsSingleUser(void); - - -int CtdlIsUserLoggedIn (char *user_name); -int CtdlIsUserLoggedInByNum (long usernum); -void CtdlBumpNewMailCounter(long which_user); - - -/* - * CtdlGetCurrentMessageNumber() - Obtain the current highest message number in the system - * This provides a quick way to initialise a variable that might be used to indicate - * messages that should not be processed. For example, a new inbox script will use this - * to record determine that messages older than this should not be processed. - * This function is defined in control.c - */ -long CtdlGetCurrentMessageNumber(void); - - - -/* - * Expose various room operation functions from room_ops.c to the modules API - */ - -unsigned CtdlCreateRoom(char *new_room_name, - int new_room_type, - char *new_room_pass, - int new_room_floor, - int really_create, - int avoid_access, - int new_room_view); -int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name); -int CtdlGetRoomLock(struct ctdlroom *qrbuf, const char *room_name); -int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr); -void CtdlRoomAccess(struct ctdlroom *roombuf, struct ctdluser *userbuf, int *result, int *view); -void CtdlPutRoomLock(struct ctdlroom *qrbuf); -typedef void (*ForEachRoomCallBack)(struct ctdlroom *EachRoom, void *out_data); -void CtdlForEachRoom(ForEachRoomCallBack CB, void *in_data); -char *LoadRoomNetConfigFile(long roomnum); -void SaveRoomNetConfigFile(long roomnum, const char *raw_netconfig); -void CtdlDeleteRoom(struct ctdlroom *qrbuf); -int CtdlRenameRoom(char *old_name, char *new_name, int new_floor); -void CtdlUserGoto (char *where, int display_result, int transiently, int *msgs, int *new, long *oldest, long *newest); -struct floor *CtdlGetCachedFloor(int floor_num); -void CtdlScheduleRoomForDeletion(struct ctdlroom *qrbuf); -void CtdlGetFloor (struct floor *flbuf, int floor_num); -void CtdlPutFloor (struct floor *flbuf, int floor_num); -void CtdlPutFloorLock(struct floor *flbuf, int floor_num); -int CtdlGetFloorByName(const char *floor_name); -int CtdlGetFloorByNameLock(const char *floor_name); -int CtdlGetAvailableFloor(void); -int CtdlIsNonEditable(struct ctdlroom *qrbuf); -void CtdlPutRoom(struct ctdlroom *); - -/* - * Possible return values for CtdlRenameRoom() - */ -enum { - crr_ok, /* success */ - crr_room_not_found, /* room not found */ - crr_already_exists, /* new name already exists */ - crr_noneditable, /* cannot edit this room */ - crr_invalid_floor, /* target floor does not exist */ - crr_access_denied /* not allowed to edit this room */ -}; - - - -/* - * API declarations from citserver.h - */ -int CtdlAccessCheck(int); -/* 'required access level' values which may be passed to CtdlAccessCheck() - */ -enum { - ac_none, - ac_logged_in_or_guest, - ac_logged_in, - ac_room_aide, - ac_aide, - ac_internal, -}; - - - -/* - * API declarations from serv_extensions.h - */ -void CtdlModuleDoSearch(int *num_msgs, long **search_msgs, const char *search_string, const char *func_name); - -#define NODENAME CtdlGetConfigStr("c_nodename") -#define FQDN CtdlGetConfigStr("c_fqdn") -#define CTDLUID ctdluid -#define CREATAIDE CtdlGetConfigInt("c_creataide") -#define REGISCALL CtdlGetConfigInt("c_regiscall") -#define TWITDETECT CtdlGetConfigInt("c_twitdetect") -#define TWITROOM CtdlGetConfigStr("c_twitroom") -#define RESTRICT_INTERNET CtdlGetConfigInt("c_restrict") - -#define CtdlREGISTERRoomCfgType(a, p, uniq, nSegs, s, d) RegisterRoomCfgType(#a, sizeof(#a) - 1, a, p, uniq, nSegs, s, d); - - - -/* - * Expose API calls from user_ops.c - */ -int CtdlGetUser(struct ctdluser *usbuf, char *name); -int CtdlGetUserLen(struct ctdluser *usbuf, const char *name, long len); -int CtdlGetUserLock(struct ctdluser *usbuf, char *name); -void CtdlPutUser(struct ctdluser *usbuf); -void CtdlPutUserLock(struct ctdluser *usbuf); -int CtdlLockGetCurrentUser(void); -void CtdlPutCurrentUserLock(void); -int CtdlGetUserByNumber(struct ctdluser *usbuf, long number); -void CtdlGetRelationship(visit *vbuf, struct ctdluser *rel_user, struct ctdlroom *rel_room); -void CtdlSetRelationship(visit *newvisit, struct ctdluser *rel_user, struct ctdlroom *rel_room); -void CtdlMailboxName(char *buf, size_t n, const struct ctdluser *who, const char *prefix); -int CtdlLoginExistingUser(const char *username); - -/* - * Values which may be returned by CtdlLoginExistingUser() - */ -enum { - pass_ok, - pass_already_logged_in, - pass_no_user, - pass_internal_error, - pass_wrong_password -}; - -int CtdlTryPassword(const char *password, long len); -/* - * Values which may be returned by CtdlTryPassword() - */ -enum { - login_ok, - login_already_logged_in, - login_too_many_users, - login_not_found -}; - -void CtdlUserLogout(void); - -/* - * Expose API calls from msgbase.c - */ - - -/* - * Expose API calls from euidindex.c - */ -long CtdlLocateMessageByEuid(char *euid, struct ctdlroom *qrbuf); - - -/* - * Expose API calls from modules/openid/serv_openid_rp.c in order to turn it into a generic external authentication driver - */ -int attach_extauth(struct ctdluser *who, StrBuf *claimed_id); - -#endif /* CTDL_MODULE_H */ diff --git a/citadel/database.c b/citadel/database.c deleted file mode 100644 index 45b5d2734..000000000 --- a/citadel/database.c +++ /dev/null @@ -1,812 +0,0 @@ -// This is a data store backend for the Citadel server which uses Berkeley DB. -// -// Copyright (c) 1987-2021 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -/***************************************************************************** - Tunable configuration parameters for the Berkeley DB back end - *****************************************************************************/ - -/* Citadel will checkpoint the db at the end of every session, but only if - * the specified number of kilobytes has been written, or if the specified - * number of minutes has passed, since the last checkpoint. - */ -#define MAX_CHECKPOINT_KBYTES 256 -#define MAX_CHECKPOINT_MINUTES 15 - -/*****************************************************************************/ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include - -#if DB_VERSION_MAJOR < 5 -#error Citadel requires Berkeley DB v5.0 or newer. Please upgrade. -#endif - -#include -#include "ctdl_module.h" -#include "control.h" -#include "citserver.h" -#include "config.h" - -static DB *dbp[MAXCDB]; /* One DB handle for each Citadel database */ -static DB_ENV *dbenv; /* The DB environment (global) */ - - -void cdb_abort(void) { - syslog(LOG_DEBUG, "db: citserver is stopping in order to prevent data loss. uid=%d gid=%d euid=%d egid=%d", - getuid(), getgid(), geteuid(), getegid() - ); - exit(CTDLEXIT_DB); -} - - -/* Verbose logging callback */ -void cdb_verbose_log(const DB_ENV * dbenv, const char *msg) { - if (!IsEmptyStr(msg)) { - syslog(LOG_DEBUG, "db: %s", msg); - } -} - - -/* Verbose logging callback */ -void cdb_verbose_err(const DB_ENV * dbenv, const char *errpfx, const char *msg) { - syslog(LOG_ERR, "db: %s", msg); -} - - -/* wrapper for txn_abort() that logs/aborts on error */ -static void txabort(DB_TXN *tid) { - int ret; - - ret = tid->abort(tid); - - if (ret) { - syslog(LOG_ERR, "db: txn_abort: %s", db_strerror(ret)); - cdb_abort(); - } -} - - -/* wrapper for txn_commit() that logs/aborts on error */ -static void txcommit(DB_TXN *tid) { - int ret; - - ret = tid->commit(tid, 0); - - if (ret) { - syslog(LOG_ERR, "db: txn_commit: %s", db_strerror(ret)); - cdb_abort(); - } -} - - -/* wrapper for txn_begin() that logs/aborts on error */ -static void txbegin(DB_TXN **tid) { - int ret; - - ret = dbenv->txn_begin(dbenv, NULL, tid, 0); - - if (ret) { - syslog(LOG_ERR, "db: txn_begin: %s", db_strerror(ret)); - cdb_abort(); - } -} - - -/* panic callback */ -static void dbpanic(DB_ENV * env, int errval) { - syslog(LOG_ERR, "db: PANIC: %s", db_strerror(errval)); -} - - -static void cclose(DBC * cursor) { - int ret; - - if ((ret = cursor->c_close(cursor))) { - syslog(LOG_ERR, "db: c_close: %s", db_strerror(ret)); - cdb_abort(); - } -} - - -static void bailIfCursor(DBC ** cursors, const char *msg) { - int i; - - for (i = 0; i < MAXCDB; i++) - if (cursors[i] != NULL) { - syslog(LOG_ERR, "db: cursor still in progress on cdb %02x: %s", i, msg); - cdb_abort(); - } -} - - -void cdb_check_handles(void) { - bailIfCursor(TSD->cursors, "in check_handles"); - - if (TSD->tid != NULL) { - syslog(LOG_ERR, "db: transaction still in progress!"); - cdb_abort(); - } -} - - -/* - * Request a checkpoint of the database. Called once per minute by the thread manager. - */ -void cdb_checkpoint(void) { - int ret; - - syslog(LOG_DEBUG, "db: -- checkpoint --"); - ret = dbenv->txn_checkpoint(dbenv, MAX_CHECKPOINT_KBYTES, MAX_CHECKPOINT_MINUTES, 0); - - if (ret != 0) { - syslog(LOG_ERR, "db: cdb_checkpoint() txn_checkpoint: %s", db_strerror(ret)); - cdb_abort(); - } - - /* After a successful checkpoint, we can cull the unused logs */ - if (CtdlGetConfigInt("c_auto_cull")) { - ret = dbenv->log_set_config(dbenv, DB_LOG_AUTO_REMOVE, 1); - } - else { - ret = dbenv->log_set_config(dbenv, DB_LOG_AUTO_REMOVE, 0); - } -} - - -/* - * Open the various databases we'll be using. Any database which - * does not exist should be created. Note that we don't need a - * critical section here, because there aren't any active threads - * manipulating the database yet. - */ -void open_databases(void) { - int ret; - int i; - char dbfilename[32]; - u_int32_t flags = 0; - int dbversion_major, dbversion_minor, dbversion_patch; - - syslog(LOG_DEBUG, "db: open_databases() starting"); - syslog(LOG_DEBUG, "db: Compiled libdb: %s", DB_VERSION_STRING); - syslog(LOG_DEBUG, "db: Linked libdb: %s", db_version(&dbversion_major, &dbversion_minor, &dbversion_patch)); - syslog(LOG_DEBUG, "db: Linked zlib: %s", zlibVersion()); - - /* - * Silently try to create the database subdirectory. If it's already there, no problem. - */ - if ((mkdir(ctdl_db_dir, 0700) != 0) && (errno != EEXIST)) { - syslog(LOG_ERR, "db: unable to create database directory [%s]: %m", ctdl_db_dir); - } - if (chmod(ctdl_db_dir, 0700) != 0) { - syslog(LOG_ERR, "db: unable to set database directory permissions [%s]: %m", ctdl_db_dir); - } - if (chown(ctdl_db_dir, CTDLUID, (-1)) != 0) { - syslog(LOG_ERR, "db: unable to set the owner for [%s]: %m", ctdl_db_dir); - } - syslog(LOG_DEBUG, "db: Setting up DB environment"); - // db_env_set_func_yield((int (*)(u_long, u_long))sched_yield); - ret = db_env_create(&dbenv, 0); - if (ret) { - syslog(LOG_ERR, "db: db_env_create: %s", db_strerror(ret)); - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - dbenv->set_errpfx(dbenv, "citserver"); - dbenv->set_paniccall(dbenv, dbpanic); - dbenv->set_errcall(dbenv, cdb_verbose_err); - dbenv->set_errpfx(dbenv, "ctdl"); - dbenv->set_msgcall(dbenv, cdb_verbose_log); - dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK, 1); - dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1); - - /* - * We want to specify the shared memory buffer pool cachesize, - * but everything else is the default. - */ - ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0); - if (ret) { - syslog(LOG_ERR, "db: set_cachesize: %s", db_strerror(ret)); - dbenv->close(dbenv, 0); - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - - if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT))) { - syslog(LOG_ERR, "db: set_lk_detect: %s", db_strerror(ret)); - dbenv->close(dbenv, 0); - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - - flags = DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_INIT_TXN | DB_INIT_LOCK | DB_THREAD | DB_INIT_LOG; - syslog(LOG_DEBUG, "db: dbenv->open(dbenv, %s, %d, 0)", ctdl_db_dir, flags); - ret = dbenv->open(dbenv, ctdl_db_dir, flags, 0); // try opening the database cleanly - if (ret == DB_RUNRECOVERY) { - syslog(LOG_ERR, "db: dbenv->open: %s", db_strerror(ret)); - syslog(LOG_ERR, "db: attempting recovery..."); - flags |= DB_RECOVER; - ret = dbenv->open(dbenv, ctdl_db_dir, flags, 0); // try recovery - } - if (ret == DB_RUNRECOVERY) { - syslog(LOG_ERR, "db: dbenv->open: %s", db_strerror(ret)); - syslog(LOG_ERR, "db: attempting catastrophic recovery..."); - flags &= ~DB_RECOVER; - flags |= DB_RECOVER_FATAL; - ret = dbenv->open(dbenv, ctdl_db_dir, flags, 0); // try catastrophic recovery - } - if (ret) { - syslog(LOG_ERR, "db: dbenv->open: %s", db_strerror(ret)); - dbenv->close(dbenv, 0); - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - - syslog(LOG_INFO, "db: mounting databases"); - for (i = 0; i < MAXCDB; ++i) { - ret = db_create(&dbp[i], dbenv, 0); // Create a database handle - if (ret) { - syslog(LOG_ERR, "db: db_create: %s", db_strerror(ret)); - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - - snprintf(dbfilename, sizeof dbfilename, "cdb.%02x", i); // table names by number - ret = dbp[i]->open(dbp[i], NULL, dbfilename, NULL, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT | DB_THREAD, 0600); - if (ret) { - syslog(LOG_ERR, "db: db_open[%02x]: %s", i, db_strerror(ret)); - if (ret == ENOMEM) { - syslog(LOG_ERR, "db: You may need to tune your database; please read http://www.citadel.org/doku.php?id=faq:troubleshooting:out_of_lock_entries for more information."); - } - syslog(LOG_ERR, "db: exit code %d", ret); - exit(CTDLEXIT_DB); - } - } -} - - -/* - * Make sure we own all the files, because in a few milliseconds we're going to drop root privs. - */ -void cdb_chmod_data(void) { - DIR *dp; - struct dirent *d; - char filename[PATH_MAX]; - - dp = opendir(ctdl_db_dir); - if (dp != NULL) { - while (d = readdir(dp), d != NULL) { - if (d->d_name[0] != '.') { - snprintf(filename, sizeof filename, "%s/%s", ctdl_db_dir, d->d_name); - syslog(LOG_DEBUG, "db: chmod(%s, 0600) returned %d", filename, chmod(filename, 0600)); - syslog(LOG_DEBUG, "db: chown(%s, CTDLUID, -1) returned %d", - filename, chown(filename, CTDLUID, (-1)) - ); - } - } - closedir(dp); - } -} - - -/* - * Close all of the db database files we've opened. This can be done - * in a loop, since it's just a bunch of closes. - */ -void close_databases(void) { - int i; - int ret; - - syslog(LOG_INFO, "db: performing final checkpoint"); - if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, 0))) { - syslog(LOG_ERR, "db: txn_checkpoint: %s", db_strerror(ret)); - } - - syslog(LOG_INFO, "db: flushing the database logs"); - if ((ret = dbenv->log_flush(dbenv, NULL))) { - syslog(LOG_ERR, "db: log_flush: %s", db_strerror(ret)); - } - - /* close the tables */ - syslog(LOG_INFO, "db: closing databases"); - for (i = 0; i < MAXCDB; ++i) { - syslog(LOG_INFO, "db: closing database %02x", i); - ret = dbp[i]->close(dbp[i], 0); - if (ret) { - syslog(LOG_ERR, "db: db_close: %s", db_strerror(ret)); - } - - } - - // This seemed nifty at the time but did anyone really look at it? - // #ifdef DB_STAT_ALL - // /* print some statistics... */ - // dbenv->lock_stat_print(dbenv, DB_STAT_ALL); - // #endif - - /* Close the handle. */ - ret = dbenv->close(dbenv, 0); - if (ret) { - syslog(LOG_ERR, "db: DBENV->close: %s", db_strerror(ret)); - } -} - - -/* - * Decompress a database item if it was compressed on disk - */ -void cdb_decompress_if_necessary(struct cdbdata *cdb) { - static int magic = COMPRESS_MAGIC; - - if ((cdb == NULL) || (cdb->ptr == NULL) || (cdb->len < sizeof(magic)) || (memcmp(cdb->ptr, &magic, sizeof(magic)))) { - return; - } - - /* At this point we know we're looking at a compressed item. */ - - struct CtdlCompressHeader zheader; - char *uncompressed_data; - char *compressed_data; - uLongf destLen, sourceLen; - size_t cplen; - - memset(&zheader, 0, sizeof(struct CtdlCompressHeader)); - cplen = sizeof(struct CtdlCompressHeader); - if (sizeof(struct CtdlCompressHeader) > cdb->len) { - cplen = cdb->len; - } - memcpy(&zheader, cdb->ptr, cplen); - - compressed_data = cdb->ptr; - compressed_data += sizeof(struct CtdlCompressHeader); - - sourceLen = (uLongf) zheader.compressed_len; - destLen = (uLongf) zheader.uncompressed_len; - uncompressed_data = malloc(zheader.uncompressed_len); - - if (uncompress((Bytef *) uncompressed_data, - (uLongf *) & destLen, (const Bytef *) compressed_data, (uLong) sourceLen) != Z_OK) { - syslog(LOG_ERR, "db: uncompress() error"); - cdb_abort(); - } - - free(cdb->ptr); - cdb->len = (size_t) destLen; - cdb->ptr = uncompressed_data; -} - - -/* - * Store a piece of data. Returns 0 if the operation was successful. If a - * key already exists it should be overwritten. - */ -int cdb_store(int cdb, const void *ckey, int ckeylen, void *cdata, int cdatalen) { - - DBT dkey, ddata; - DB_TXN *tid = NULL; - int ret = 0; - struct CtdlCompressHeader zheader; - char *compressed_data = NULL; - int compressing = 0; - size_t buffer_len = 0; - uLongf destLen = 0; - - memset(&dkey, 0, sizeof(DBT)); - memset(&ddata, 0, sizeof(DBT)); - dkey.size = ckeylen; - dkey.data = (void *) ckey; - ddata.size = cdatalen; - ddata.data = cdata; - - /* Only compress Visit and UseTable records. Everything else is uncompressed. */ - if ((cdb == CDB_VISIT) || (cdb == CDB_USETABLE)) { - compressing = 1; - zheader.magic = COMPRESS_MAGIC; - zheader.uncompressed_len = cdatalen; - buffer_len = ((cdatalen * 101) / 100) + 100 + sizeof(struct CtdlCompressHeader); - destLen = (uLongf) buffer_len; - compressed_data = malloc(buffer_len); - if (compress2((Bytef *) (compressed_data + sizeof(struct CtdlCompressHeader)), - &destLen, (Bytef *) cdata, (uLongf) cdatalen, 1) != Z_OK) { - syslog(LOG_ERR, "db: compress2() error"); - cdb_abort(); - } - zheader.compressed_len = (size_t) destLen; - memcpy(compressed_data, &zheader, sizeof(struct CtdlCompressHeader)); - ddata.size = (size_t) (sizeof(struct CtdlCompressHeader) + zheader.compressed_len); - ddata.data = compressed_data; - } - - if (TSD->tid != NULL) { - ret = dbp[cdb]->put(dbp[cdb], // db - TSD->tid, // transaction ID - &dkey, // key - &ddata, // data - 0 // flags - ); - if (ret) { - syslog(LOG_ERR, "db: cdb_store(%d): %s", cdb, db_strerror(ret)); - cdb_abort(); - } - if (compressing) { - free(compressed_data); - } - return ret; - } else { - bailIfCursor(TSD->cursors, "attempt to write during r/o cursor"); - - retry: - txbegin(&tid); - - if ((ret = dbp[cdb]->put(dbp[cdb], // db - tid, // transaction ID - &dkey, // key - &ddata, // data - 0))) { // flags - if (ret == DB_LOCK_DEADLOCK) { - txabort(tid); - goto retry; - } else { - syslog(LOG_ERR, "db: cdb_store(%d): %s", cdb, db_strerror(ret)); - cdb_abort(); - } - } else { - txcommit(tid); - if (compressing) { - free(compressed_data); - } - return ret; - } - } - return ret; -} - - -/* - * Delete a piece of data. Returns 0 if the operation was successful. - */ -int cdb_delete(int cdb, void *key, int keylen) { - DBT dkey; - DB_TXN *tid; - int ret; - - memset(&dkey, 0, sizeof dkey); - dkey.size = keylen; - dkey.data = key; - - if (TSD->tid != NULL) { - ret = dbp[cdb]->del(dbp[cdb], TSD->tid, &dkey, 0); - if (ret) { - syslog(LOG_ERR, "db: cdb_delete(%d): %s", cdb, db_strerror(ret)); - if (ret != DB_NOTFOUND) { - cdb_abort(); - } - } - } else { - bailIfCursor(TSD->cursors, "attempt to delete during r/o cursor"); - - retry: - txbegin(&tid); - - if ((ret = dbp[cdb]->del(dbp[cdb], tid, &dkey, 0)) && ret != DB_NOTFOUND) { - if (ret == DB_LOCK_DEADLOCK) { - txabort(tid); - goto retry; - } else { - syslog(LOG_ERR, "db: cdb_delete(%d): %s", cdb, db_strerror(ret)); - cdb_abort(); - } - } else { - txcommit(tid); - } - } - return ret; -} - - -static DBC *localcursor(int cdb) { - int ret; - DBC *curs; - - if (TSD->cursors[cdb] == NULL) { - ret = dbp[cdb]->cursor(dbp[cdb], TSD->tid, &curs, 0); - } - else { - ret = TSD->cursors[cdb]->c_dup(TSD->cursors[cdb], &curs, DB_POSITION); - } - - if (ret) { - syslog(LOG_ERR, "db: localcursor: %s", db_strerror(ret)); - cdb_abort(); - } - - return curs; -} - - -/* - * Fetch a piece of data. If not found, returns NULL. Otherwise, it returns - * a struct cdbdata which it is the caller's responsibility to free later on - * using the cdb_free() routine. - */ -struct cdbdata *cdb_fetch(int cdb, const void *key, int keylen) { - - if (keylen == 0) { // key length zero is impossible - return(NULL); - } - - struct cdbdata *tempcdb; - DBT dkey, dret; - int ret; - - memset(&dkey, 0, sizeof(DBT)); - dkey.size = keylen; - dkey.data = (void *) key; - - if (TSD->tid != NULL) { - memset(&dret, 0, sizeof(DBT)); - dret.flags = DB_DBT_MALLOC; - ret = dbp[cdb]->get(dbp[cdb], TSD->tid, &dkey, &dret, 0); - } - else { - DBC *curs; - - do { - memset(&dret, 0, sizeof(DBT)); - dret.flags = DB_DBT_MALLOC; - curs = localcursor(cdb); - ret = curs->c_get(curs, &dkey, &dret, DB_SET); - cclose(curs); - } while (ret == DB_LOCK_DEADLOCK); - } - - if ((ret != 0) && (ret != DB_NOTFOUND)) { - syslog(LOG_ERR, "db: cdb_fetch(%d): %s", cdb, db_strerror(ret)); - cdb_abort(); - } - - if (ret != 0) { - return NULL; - } - - tempcdb = (struct cdbdata *) malloc(sizeof(struct cdbdata)); - if (tempcdb == NULL) { - syslog(LOG_ERR, "db: cdb_fetch() cannot allocate memory for tempcdb: %m"); - cdb_abort(); - return NULL; /* make it easier for static analysis... */ - } - else { - tempcdb->len = dret.size; - tempcdb->ptr = dret.data; - cdb_decompress_if_necessary(tempcdb); - return (tempcdb); - } -} - - -/* - * Free a cdbdata item. - * - * Note that we only free the 'ptr' portion if it is not NULL. This allows - * other code to assume ownership of that memory simply by storing the - * pointer elsewhere and then setting 'ptr' to NULL. cdb_free() will then - * avoid freeing it. - */ -void cdb_free(struct cdbdata *cdb) { - if (cdb->ptr) { - free(cdb->ptr); - } - free(cdb); -} - - -void cdb_close_cursor(int cdb) { - if (TSD->cursors[cdb] != NULL) { - cclose(TSD->cursors[cdb]); - } - - TSD->cursors[cdb] = NULL; -} - - -/* - * Prepare for a sequential search of an entire database. - * (There is guaranteed to be no more than one traversal in - * progress per thread at any given time.) - */ -void cdb_rewind(int cdb) { - int ret = 0; - - if (TSD->cursors[cdb] != NULL) { - syslog(LOG_ERR, "db: cdb_rewind: must close cursor on database %d before reopening", cdb); - cdb_abort(); - /* cclose(TSD->cursors[cdb]); */ - } - - /* - * Now initialize the cursor - */ - ret = dbp[cdb]->cursor(dbp[cdb], TSD->tid, &TSD->cursors[cdb], 0); - if (ret) { - syslog(LOG_ERR, "db: cdb_rewind: db_cursor: %s", db_strerror(ret)); - cdb_abort(); - } -} - - -/* - * Fetch the next item in a sequential search. Returns a pointer to a - * cdbdata structure, or NULL if we've hit the end. - */ -struct cdbdata *cdb_next_item(int cdb) { - DBT key, data; - struct cdbdata *cdbret; - int ret = 0; - - /* Initialize the key/data pair so the flags aren't set. */ - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - data.flags = DB_DBT_MALLOC; - - ret = TSD->cursors[cdb]->c_get(TSD->cursors[cdb], &key, &data, DB_NEXT); - - if (ret) { - if (ret != DB_NOTFOUND) { - syslog(LOG_ERR, "db: cdb_next_item(%d): %s", cdb, db_strerror(ret)); - cdb_abort(); - } - cdb_close_cursor(cdb); - return NULL; /* presumably, end of file */ - } - - cdbret = (struct cdbdata *) malloc(sizeof(struct cdbdata)); - cdbret->len = data.size; - cdbret->ptr = data.data; - cdb_decompress_if_necessary(cdbret); - - return (cdbret); -} - - -/* - * Transaction-based stuff. I'm writing this as I bake cookies... - */ -void cdb_begin_transaction(void) { - bailIfCursor(TSD->cursors, "can't begin transaction during r/o cursor"); - - if (TSD->tid != NULL) { - syslog(LOG_ERR, "db: cdb_begin_transaction: ERROR: nested transaction"); - cdb_abort(); - } - - txbegin(&TSD->tid); -} - - -void cdb_end_transaction(void) { - int i; - - for (i = 0; i < MAXCDB; i++) { - if (TSD->cursors[i] != NULL) { - syslog(LOG_WARNING, "db: cdb_end_transaction: WARNING: cursor %d still open at transaction end", i); - cclose(TSD->cursors[i]); - TSD->cursors[i] = NULL; - } - } - - if (TSD->tid == NULL) { - syslog(LOG_ERR, "db: cdb_end_transaction: ERROR: txcommit(NULL) !!"); - cdb_abort(); - } - else { - txcommit(TSD->tid); - } - - TSD->tid = NULL; -} - - -/* - * Truncate (delete every record) - */ -void cdb_trunc(int cdb) { - /* DB_TXN *tid; */ - int ret; - u_int32_t count; - - if (TSD->tid != NULL) { - syslog(LOG_ERR, "db: cdb_trunc must not be called in a transaction."); - cdb_abort(); - } - else { - bailIfCursor(TSD->cursors, "attempt to write during r/o cursor"); - - retry: - /* txbegin(&tid); */ - - if ((ret = dbp[cdb]->truncate(dbp[cdb], /* db */ - NULL, /* transaction ID */ - &count, /* #rows deleted */ - 0))) { /* flags */ - if (ret == DB_LOCK_DEADLOCK) { - /* txabort(tid); */ - goto retry; - } else { - syslog(LOG_ERR, "db: cdb_truncate(%d): %s", cdb, db_strerror(ret)); - if (ret == ENOMEM) { - syslog(LOG_ERR, "db: You may need to tune your database; please read http://www.citadel.org/doku.php?id=faq:troubleshooting:out_of_lock_entries for more information."); - } - exit(CTDLEXIT_DB); - } - } - else { - /* txcommit(tid); */ - } - } -} - - -// compact (defragment) the database, possibly returning space back to the underlying filesystem -void cdb_compact(void) { - int ret; - int i; - - syslog(LOG_DEBUG, "db: cdb_compact() started"); - for (i = 0; i < MAXCDB; i++) { - syslog(LOG_DEBUG, "db: compacting database %d", i); - ret = dbp[i]->compact(dbp[i], NULL, NULL, NULL, NULL, DB_FREE_SPACE, NULL); - if (ret) { - syslog(LOG_ERR, "db: compact: %s", db_strerror(ret)); - } - } - syslog(LOG_DEBUG, "db: cdb_compact() finished"); -} - - -// Has an item already been seen (is it in the CDB_USETABLE) ? -// Returns 0 if it hasn't, 1 if it has -// In either case, writes the item to the database for next time. -int CheckIfAlreadySeen(StrBuf *guid) { - int found = 0; - struct UseTable ut; - struct cdbdata *cdbut; - - syslog(LOG_DEBUG, "db: CheckIfAlreadySeen(%s)", ChrPtr(guid)); - cdbut = cdb_fetch(CDB_USETABLE, SKEY(guid)); - if (cdbut != NULL) { - found = 1; - cdb_free(cdbut); - } - - /* (Re)write the record, to update the timestamp. Zeroing it out makes it compress better. */ - memset(&ut, 0, sizeof(struct UseTable)); - memcpy(ut.ut_msgid, SKEY(guid)); - ut.ut_timestamp = time(NULL); - cdb_store(CDB_USETABLE, SKEY(guid), &ut, sizeof(struct UseTable)); - return (found); -} - - -CTDL_MODULE_INIT(database) -{ - if (!threading) { - // nothing to do here - } - - /* return our module id for the log */ - return "database"; -} diff --git a/citadel/database.h b/citadel/database.h deleted file mode 100644 index b9b76b715..000000000 --- a/citadel/database.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 1987-2017 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef DATABASE_H -#define DATABASE_H - - -void open_databases (void); -void close_databases (void); -int cdb_store (int cdb, const void *key, int keylen, void *data, int datalen); -int cdb_delete (int cdb, void *key, int keylen); -struct cdbdata *cdb_fetch (int cdb, const void *key, int keylen); -void cdb_free (struct cdbdata *cdb); -void cdb_rewind (int cdb); -struct cdbdata *cdb_next_item (int cdb); -void cdb_close_cursor(int cdb); -void cdb_begin_transaction(void); -void cdb_end_transaction(void); -void cdb_allocate_tsd(void); -void cdb_free_tsd(void); -void cdb_check_handles(void); -void cdb_trunc(int cdb); -void *checkpoint_thread(void *arg); -void cdb_chmod_data(void); -void cdb_checkpoint(void); -void check_handles(void *arg); -void cdb_cull_logs(void); -void cdb_compact(void); - - -/* - * Database records beginning with this magic number are assumed to - * be compressed. In the event that a database record actually begins with - * this magic number, we *must* compress it whether we want to or not, - * because the fetch function will try to uncompress it anyway. - * - * (No need to #ifdef this stuff; it compiles ok even if zlib is not present - * and doesn't declare anything so it won't bloat the code) - */ -#define COMPRESS_MAGIC 0xc0ffeeee - -struct CtdlCompressHeader { - int magic; - size_t uncompressed_len; - size_t compressed_len; -}; - -int CheckIfAlreadySeen(StrBuf *guid); - -#endif /* DATABASE_H */ - diff --git a/citadel/default_timezone.c b/citadel/default_timezone.c deleted file mode 100644 index 6ce5c107e..000000000 --- a/citadel/default_timezone.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 1987-2018 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "sysdep_decls.h" -#include "support.h" -#include "config.h" -#include "default_timezone.h" -#include "ctdl_module.h" - - -/* - * Figure out which time zone needs to be used for timestamps that are - * not UTC and do not have a time zone specified. - */ -icaltimezone *get_default_icaltimezone(void) { - - icaltimezone *zone = NULL; - char *default_zone_name = CtdlGetConfigStr("c_default_cal_zone"); - - if (!zone) { - zone = icaltimezone_get_builtin_timezone(default_zone_name); - } - if (!zone) { - syslog(LOG_ERR, "ical: Unable to load '%s' time zone. Defaulting to UTC.", default_zone_name); - zone = icaltimezone_get_utc_timezone(); - } - if (!zone) { - syslog(LOG_ERR, "ical: unable to load UTC time zone!"); - } - - return zone; -} diff --git a/citadel/default_timezone.h b/citadel/default_timezone.h deleted file mode 100644 index 869c70ded..000000000 --- a/citadel/default_timezone.h +++ /dev/null @@ -1 +0,0 @@ -icaltimezone *get_default_icaltimezone(void); diff --git a/citadel/domain.c b/citadel/domain.c deleted file mode 100644 index 79461f55a..000000000 --- a/citadel/domain.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * DNS lookup for SMTP sender - * - * Copyright (c) 1987-2021 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "sysdep.h" -#include -#include -#ifdef HAVE_RESOLV_H -#include -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include -#endif -#ifdef __FreeBSD__ -#include -#endif -#include -#endif -#include -#include "sysdep_decls.h" -#include "citadel.h" -#include "domain.h" -#include "internet_addressing.h" - - -/* - * get_hosts() checks the Internet configuration for various types of - * entries and returns them in the same format as getmx() does -- fill the - * buffer with a delimited list of hosts and return the number of hosts. - * - * This is used to fetch MX smarthosts, SpamAssassin hosts, etc. - */ -int get_hosts(char *mxbuf, char *rectype) { - int config_lines; - int i; - char buf[256]; - char host[256], type[256]; - int total_smarthosts = 0; - - if (inetcfg == NULL) return(0); - strcpy(mxbuf, ""); - - config_lines = num_tokens(inetcfg, '\n'); - for (i=0; ipref; - pref2 = ((const struct mx *)mx2)->pref; - - if (pref1 > pref2) { - return(1); - } - else if (pref1 < pref2) { - return(0); - } - else { - return(rand() % 2); - } -} - - -/* - * getmx() - * - * Return one or more MX's for a mail destination. - * - * Upon success, it fills 'mxbuf' with one or more MX hosts, separated by - * vertical bar characters, and returns the number of hosts as its return - * value. If no MX's are found, it returns 0. - * - */ -int getmx(char *mxbuf, char *dest) { - -#ifdef HAVE_RESOLV_H - union { - u_char bytes[1024]; - HEADER header; - } answer; -#endif - - int ret; - unsigned char *startptr, *endptr, *ptr; - char expanded_buf[1024]; - unsigned short pref, type; - int n = 0; - int qdcount; - Array *mxrecords = NULL; - struct mx mx; - - // If we're configured to send all mail to a smart-host, then our job here is really easy -- just return those. - n = get_hosts(mxbuf, "smarthost"); - if (n > 0) { - return(n); - } - - mxrecords = array_new(sizeof(struct mx)); - - // No smart-host? Look up the best MX for a site. Make a call to the resolver library. - ret = res_query(dest, C_IN, T_MX, (unsigned char *)answer.bytes, sizeof(answer)); - - if (ret < 0) { - mx.pref = 0; - strcpy(mx.host, dest); - array_append(mxrecords, &mx); - } - else { - if (ret > sizeof(answer)) { // If we had to truncate, shrink the number to avoid fireworks - ret = sizeof(answer); - } - - startptr = &answer.bytes[0]; // start and end of buffer - endptr = &answer.bytes[ret]; - ptr = startptr + HFIXEDSZ; // advance past header - - for (qdcount = ntohs(answer.header.qdcount); qdcount--; ptr += ret + QFIXEDSZ) { - if ((ret = dn_skipname(ptr, endptr)) < 0) { - syslog(LOG_DEBUG, "domain: dn_skipname error"); - return(0); - } - } - - while(1) { - memset(expanded_buf, 0, sizeof(expanded_buf)); - ret = dn_expand(startptr, endptr, ptr, expanded_buf, sizeof(expanded_buf)); - if (ret < 0) break; - ptr += ret; - - GETSHORT(type, ptr); - ptr += INT16SZ + INT32SZ; - GETSHORT(n, ptr); - - if (type != T_MX) { - ptr += n; - } - - else { - GETSHORT(pref, ptr); - ret = dn_expand(startptr, endptr, ptr, expanded_buf, sizeof(expanded_buf)); - ptr += ret; - - mx.pref = pref; - strcpy(mx.host, expanded_buf); - array_append(mxrecords, &mx); - } - } - } - - // Sort the MX records by preference - if (array_len(mxrecords) > 1) { - array_sort(mxrecords, mx_compare_pref); - } - - int num_mxrecs = array_len(mxrecords); - strcpy(mxbuf, ""); - for (n=0; nhost); - strcat(mxbuf, "|"); - } - array_free(mxrecords); - - // Append any fallback smart hosts we have configured. - num_mxrecs += get_hosts(&mxbuf[strlen(mxbuf)], "fallbackhost"); - return(num_mxrecs); -} diff --git a/citadel/domain.h b/citadel/domain.h deleted file mode 100644 index ba367593d..000000000 --- a/citadel/domain.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 1987-2012 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -struct mx { - int pref; - char host[1024]; -}; - -int getmx(char *mxbuf, char *dest); -int get_hosts(char *mxbuf, char *rectype); - - -/* HP/UX has old include files...these are from arpa/nameser.h */ - -#include "typesize.h" - -#ifndef HFIXEDSZ -#define HFIXEDSZ 12 /* I hope! */ -#endif -#ifndef INT16SZ -#define INT16SZ sizeof(cit_int16_t) -#endif -#ifndef INT32SZ -#define INT32SZ sizeof(cit_int32_t) -#endif diff --git a/citadel/euidindex.c b/citadel/euidindex.c deleted file mode 100644 index 59d67deb9..000000000 --- a/citadel/euidindex.c +++ /dev/null @@ -1,214 +0,0 @@ -// Index messages by EUID per room. -// -// Copyright (c) 1987-2022 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License, version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -#include "sysdep.h" -#include -#include - -#include "citserver.h" -#include "room_ops.h" - -// The structure of an euidindex record *key* is: -// -// |----room_number----|----------EUID-------------| -// (sizeof long) (actual length of euid) -// -// -// The structure of an euidindex record *value* is: -// -// |-----msg_number----|----room_number----|----------EUID-------------| -// (sizeof long) (sizeof long) (actual length of euid) - -// Return nonzero if the supplied room is one which should have -// an EUID index. -int DoesThisRoomNeedEuidIndexing(struct ctdlroom *qrbuf) { - - switch(qrbuf->QRdefaultview) { - case VIEW_BBS: return(0); - case VIEW_MAILBOX: return(0); - case VIEW_ADDRESSBOOK: return(1); - case VIEW_DRAFTS: return(0); - case VIEW_CALENDAR: return(1); - case VIEW_TASKS: return(1); - case VIEW_NOTES: return(1); - case VIEW_WIKI: return(1); - case VIEW_BLOG: return(1); - } - - return(0); -} - - -// Locate a message in a given room with a given euid, and return -// its message number. -long locate_message_by_euid(char *euid, struct ctdlroom *qrbuf) { - return CtdlLocateMessageByEuid (euid, qrbuf); -} - - -long CtdlLocateMessageByEuid(char *euid, struct ctdlroom *qrbuf) { - char *key; - int key_len; - struct cdbdata *cdb_euid; - long msgnum = (-1L); - - syslog(LOG_DEBUG, "euidindex: searching for EUID <%s> in <%s>", euid, qrbuf->QRname); - - key_len = strlen(euid) + sizeof(long) + 1; - key = malloc(key_len); - memcpy(key, &qrbuf->QRnumber, sizeof(long)); - strcpy(&key[sizeof(long)], euid); - - cdb_euid = cdb_fetch(CDB_EUIDINDEX, key, key_len); - free(key); - - if (cdb_euid == NULL) { - msgnum = (-1L); - } - else { - // The first (sizeof long) of the record is what we're looking for. Throw away the rest. - memcpy(&msgnum, cdb_euid->ptr, sizeof(long)); - cdb_free(cdb_euid); - } - syslog(LOG_DEBUG, "euidindex: returning msgnum = %ld", msgnum); - return(msgnum); -} - - -// Store the euid index for a message, which has presumably just been -// stored in this room by the caller. -void index_message_by_euid(char *euid, struct ctdlroom *qrbuf, long msgnum) { - char *key; - int key_len; - char *data; - int data_len; - - syslog(LOG_DEBUG, "euidindex: indexing message #%ld <%s> in <%s>", msgnum, euid, qrbuf->QRname); - - key_len = strlen(euid) + sizeof(long) + 1; - key = malloc(key_len); - memcpy(key, &qrbuf->QRnumber, sizeof(long)); - strcpy(&key[sizeof(long)], euid); - - data_len = sizeof(long) + key_len; - data = malloc(data_len); - - memcpy(data, &msgnum, sizeof(long)); - memcpy(&data[sizeof(long)], key, key_len); - - cdb_store(CDB_EUIDINDEX, key, key_len, data, data_len); - free(key); - free(data); -} - - -// Called by rebuild_euid_index_for_room() to index one message. -void rebuild_euid_index_for_msg(long msgnum, void *userdata) { - struct CtdlMessage *msg = NULL; - - msg = CtdlFetchMessage(msgnum, 0); - if (msg == NULL) return; - if (!CM_IsEmpty(msg, eExclusiveID)) { - index_message_by_euid(msg->cm_fields[eExclusiveID], &CC->room, msgnum); - } - CM_Free(msg); -} - - -void rebuild_euid_index_for_room(struct ctdlroom *qrbuf, void *data) { - static struct RoomProcList *rplist = NULL; - struct RoomProcList *ptr; - struct ctdlroom qr; - - // Lazy programming here. Call this function as a CtdlForEachRoom backend - // in order to queue up the room names, or call it with a null room - // to make it do the processing. - if (qrbuf != NULL) { - ptr = (struct RoomProcList *) - malloc(sizeof (struct RoomProcList)); - if (ptr == NULL) return; - - safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name); - ptr->next = rplist; - rplist = ptr; - return; - } - - while (rplist != NULL) { - if (CtdlGetRoom(&qr, rplist->name) == 0) { - if (DoesThisRoomNeedEuidIndexing(&qr)) { - syslog(LOG_DEBUG, - "euidindex: rebuilding EUID index for <%s>", - rplist->name); - CtdlUserGoto(rplist->name, 0, 0, NULL, NULL, NULL, NULL); - CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, rebuild_euid_index_for_msg, NULL); - } - } - ptr = rplist; - rplist = rplist->next; - free(ptr); - } -} - - -// Globally rebuild the EUID indices in every room. -void rebuild_euid_index(void) { - cdb_trunc(CDB_EUIDINDEX); // delete the old indices - CtdlForEachRoom(rebuild_euid_index_for_room, NULL); // enumerate room names - rebuild_euid_index_for_room(NULL, NULL); // and index them -} - - -// Server command to fetch a message number given an euid. -void cmd_euid(char *cmdbuf) { - char euid[256]; - long msgnum; - struct cdbdata *cdbfr; - long *msglist = NULL; - int num_msgs = 0; - int i; - - if (CtdlAccessCheck(ac_logged_in_or_guest)) return; - - extract_token(euid, cmdbuf, 0, '|', sizeof euid); - msgnum = CtdlLocateMessageByEuid(euid, &CC->room); - if (msgnum <= 0L) { - cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND); - return; - } - - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); - if (cdbfr != NULL) { - num_msgs = cdbfr->len / sizeof(long); - msglist = (long *) cdbfr->ptr; - for (i = 0; i < num_msgs; ++i) { - if (msglist[i] == msgnum) { - cdb_free(cdbfr); - cprintf("%d %ld\n", CIT_OK, msgnum); - return; - } - } - cdb_free(cdbfr); - } - - cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND); -} - - -CTDL_MODULE_INIT(euidindex) -{ - if (!threading) { - CtdlRegisterProtoHook(cmd_euid, "EUID", "Perform operations on Extended IDs for messages"); - } - /* return our Subversion id for the Log */ - return "euidindex"; -} diff --git a/citadel/euidindex.h b/citadel/euidindex.h deleted file mode 100644 index e30f79fd9..000000000 --- a/citadel/euidindex.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Index messages by EUID per room. - */ - -int DoesThisRoomNeedEuidIndexing(struct ctdlroom *qrbuf); -/* locate_message_by_euid is deprecated. Use CtdlLocateMessageByEuid instead */ -long locate_message_by_euid(char *euid, struct ctdlroom *qrbuf) __attribute__ ((deprecated)); -void index_message_by_euid(char *euid, struct ctdlroom *qrbuf, long msgnum); -void rebuild_euid_index(void); diff --git a/citadel/genstamp.c b/citadel/genstamp.c deleted file mode 100644 index e8d5dd86b..000000000 --- a/citadel/genstamp.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Function to generate RFC822-compliant textual time/date stamp - */ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include "genstamp.h" - -static char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static char *weekdays[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -/* - * Supplied with a unix timestamp, generate an RFC822-compliant textual - * time and date stamp. - */ -long datestring(char *buf, size_t n, time_t xtime, int which_format) { - struct tm t; - - long offset; - char offsign; - - localtime_r(&xtime, &t); - - /* Convert "seconds west of GMT" to "hours/minutes offset" */ -#ifdef HAVE_STRUCT_TM_TM_GMTOFF - offset = t.tm_gmtoff; -#else - offset = timezone; -#endif - if (offset > 0) { - offsign = '+'; - } - else { - offset = 0L - offset; - offsign = '-'; - } - offset = ( (offset / 3600) * 100 ) + ( offset % 60 ); - - switch(which_format) { - - case DATESTRING_RFC822: - return snprintf( - buf, n, - "%s, %02d %s %04d %02d:%02d:%02d %c%04ld", - weekdays[t.tm_wday], - t.tm_mday, - months[t.tm_mon], - t.tm_year + 1900, - t.tm_hour, - t.tm_min, - t.tm_sec, - offsign, offset - ); - break; - - case DATESTRING_IMAP: - return snprintf( - buf, n, - "%02d-%s-%04d %02d:%02d:%02d %c%04ld", - t.tm_mday, - months[t.tm_mon], - t.tm_year + 1900, - t.tm_hour, - t.tm_min, - t.tm_sec, - offsign, offset - ); - break; - - } - return 0; -} diff --git a/citadel/genstamp.h b/citadel/genstamp.h deleted file mode 100644 index ffe0d9c01..000000000 --- a/citadel/genstamp.h +++ /dev/null @@ -1,7 +0,0 @@ - -long datestring(char *buf, size_t n, time_t xtime, int which_format); - -enum { - DATESTRING_RFC822, - DATESTRING_IMAP -}; diff --git a/citadel/housekeeping.c b/citadel/housekeeping.c deleted file mode 100644 index 54cfe11b8..000000000 --- a/citadel/housekeeping.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * This file contains miscellaneous housekeeping tasks. - * - * Copyright (c) 1987-2021 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include - -#include "ctdl_module.h" -#include "serv_extensions.h" -#include "room_ops.h" -#include "internet_addressing.h" -#include "config.h" -#include "journaling.h" -#include "citadel_ldap.h" - -void check_sched_shutdown(void) { - if ((ScheduledShutdown == 1) && (ContextList == NULL)) { - syslog(LOG_NOTICE, "housekeeping: scheduled shutdown initiating"); - server_shutting_down = 1; - } -} - - -/* - * Check (and fix) floor reference counts. This doesn't need to be done - * very often, since the counts should remain correct during normal operation. - */ -void check_ref_counts_backend(struct ctdlroom *qrbuf, void *data) { - - int *new_refcounts; - - new_refcounts = (int *) data; - - ++new_refcounts[(int)qrbuf->QRfloor]; -} - - -void check_ref_counts(void) { - struct floor flbuf; - int a; - - int new_refcounts[MAXFLOORS]; - - syslog(LOG_DEBUG, "housekeeping: checking floor reference counts"); - for (a=0; a 0) { - flbuf.f_flags = flbuf.f_flags | QR_INUSE; - } - else { - flbuf.f_flags = flbuf.f_flags & ~QR_INUSE; - } - lputfloor(&flbuf, a); - syslog(LOG_DEBUG, "housekeeping: floor %d has %d rooms", a, new_refcounts[a]); - } -} - - -/* - * Provide hints as to whether we have any memory leaks - */ -void keep_an_eye_on_memory_usage(void) { - static void *original_brk = NULL; - if (!original_brk) original_brk = sbrk(0); // Remember the original program break so we can test for leaks - syslog(LOG_DEBUG, "original_brk=%lx, current_brk=%lx, addl=%ld", (long)original_brk, (long)sbrk(0), (long)(sbrk(0)-original_brk)); // FIXME not so noisy please -} - - -/* - * This is the housekeeping loop. Worker threads come through here after - * processing client requests but before jumping back into the pool. We - * only allow housekeeping to execute once per minute, and we only allow one - * instance to run at a time. - */ -static int housekeeping_in_progress = 0; -static int housekeeping_disabled = 0; -static time_t last_timer = 0L; - -void do_housekeeping(void) { - int do_housekeeping_now = 0; - int do_perminute_housekeeping_now = 0; - time_t now; - - if (housekeeping_disabled) { - return; - } - - /* - * We do it this way instead of wrapping the whole loop in an - * S_HOUSEKEEPING critical section because it eliminates the need to - * potentially have multiple concurrent mutexes in progress. - */ - begin_critical_section(S_HOUSEKEEPING); - if (housekeeping_in_progress == 0) { - do_housekeeping_now = 1; - housekeeping_in_progress = 1; - } - end_critical_section(S_HOUSEKEEPING); - - now = time(NULL); - if ( (do_housekeeping_now == 0) && (!CtdlIsSingleUser()) ) { - if ( (now - last_timer) > (time_t)300 ) { - syslog(LOG_WARNING, - "housekeeping: WARNING: housekeeping loop has not run for %ld minutes. Is something stuck?", - ((now - last_timer) / 60) - ); - } - return; - } - - /* - * Ok, at this point we've made the decision to run the housekeeping - * loop. Everything below this point is real work. - */ - - if ( (now - last_timer) > (time_t)60 ) { - do_perminute_housekeeping_now = 1; - last_timer = time(NULL); - } - - /* First, do the "as often as needed" stuff... */ - JournalRunQueue(); - PerformSessionHooks(EVT_HOUSE); - - /* Then, do the "once per minute" stuff... */ - if (do_perminute_housekeeping_now) { - cdb_check_handles(); - PerformSessionHooks(EVT_TIMER); // Run all registered TIMER hooks - - // LDAP sync isn't in a module so we can put it here - static time_t last_ldap_sync = 0L; - if ( (now - last_ldap_sync) > (time_t)CtdlGetConfigLong("c_ldap_sync_freq") ) { - CtdlSynchronizeUsersFromLDAP(); - last_ldap_sync = time(NULL); - } - - keep_an_eye_on_memory_usage(); - } - - /* - * All done. - */ - begin_critical_section(S_HOUSEKEEPING); - housekeeping_in_progress = 0; - end_critical_section(S_HOUSEKEEPING); -} - - -void CtdlDisableHouseKeeping(void) { - syslog(LOG_INFO, "housekeeping: trying to disable"); - while ( (!housekeeping_disabled) && (!server_shutting_down) && (!housekeeping_in_progress) ) { - - if (housekeeping_in_progress) { - sleep(1); - } - else { - begin_critical_section(S_HOUSEKEEPING); - if (!housekeeping_in_progress) { - housekeeping_disabled = 1; - } - end_critical_section(S_HOUSEKEEPING); - } - } - syslog(LOG_INFO, "housekeeping: disabled now"); -} - - -void CtdlEnableHouseKeeping(void) { - begin_critical_section(S_HOUSEKEEPING); - housekeeping_in_progress = 0; - end_critical_section(S_HOUSEKEEPING); -} diff --git a/citadel/housekeeping.h b/citadel/housekeeping.h deleted file mode 100644 index 90ee12a2a..000000000 --- a/citadel/housekeeping.h +++ /dev/null @@ -1,3 +0,0 @@ -void check_sched_shutdown(void); -void check_ref_counts(void); -void do_housekeeping(void); diff --git a/citadel/internet_addressing.c b/citadel/internet_addressing.c deleted file mode 100644 index e67a9dc12..000000000 --- a/citadel/internet_addressing.c +++ /dev/null @@ -1,1721 +0,0 @@ -// This file contains functions which handle the mapping of Internet addresses -// to users on the Citadel system. -// -// Copyright (c) 1987-2022 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "sysdep_decls.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "msgbase.h" -#include "internet_addressing.h" -#include "user_ops.h" -#include "room_ops.h" -#include "parsedate.h" -#include "database.h" -#include "ctdl_module.h" -#ifdef HAVE_ICONV -#include - -// This is the non-define version in case it is needed for debugging -#if 0 -inline void FindNextEnd (char *bptr, char *end) -{ - /* Find the next ?Q? */ - end = strchr(bptr + 2, '?'); - if (end == NULL) return NULL; - if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && - (*(end + 2) == '?')) { - /* skip on to the end of the cluster, the next ?= */ - end = strstr(end + 3, "?="); - } - else - /* sort of half valid encoding, try to find an end. */ - end = strstr(bptr, "?="); -} -#endif - -#define FindNextEnd(bptr, end) { \ - end = strchr(bptr + 2, '?'); \ - if (end != NULL) { \ - if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && (*(end + 2) == '?')) { \ - end = strstr(end + 3, "?="); \ - } else end = strstr(bptr, "?="); \ - } \ -} - -// Handle subjects with RFC2047 encoding such as: -// =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= -void utf8ify_rfc822_string(char *buf) { - char *start, *end, *next, *nextend, *ptr; - char newbuf[1024]; - char charset[128]; - char encoding[16]; - char istr[1024]; - iconv_t ic = (iconv_t)(-1) ; - char *ibuf; // Buffer of characters to be converted - char *obuf; // Buffer for converted characters - size_t ibuflen; // Length of input buffer - size_t obuflen; // Length of output buffer - char *isav; // Saved pointer to input buffer - char *osav; // Saved pointer to output buffer - int passes = 0; - int i, len, delta; - int illegal_non_rfc2047_encoding = 0; - - // Sometimes, badly formed messages contain strings which were simply - // written out directly in some foreign character set instead of - // using RFC2047 encoding. This is illegal but we will attempt to - // handle it anyway by converting from a user-specified default - // charset to UTF-8 if we see any nonprintable characters. - len = strlen(buf); - for (i=0; i 126)) { - illegal_non_rfc2047_encoding = 1; - i = len; // take a shortcut, it won't be more than one. - } - } - if (illegal_non_rfc2047_encoding) { - const char *default_header_charset = "iso-8859-1"; - if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) { - ctdl_iconv_open("UTF-8", default_header_charset, &ic); - if (ic != (iconv_t)(-1) ) { - ibuf = malloc(1024); - isav = ibuf; - safestrncpy(ibuf, buf, 1024); - ibuflen = strlen(ibuf); - obuflen = 1024; - obuf = (char *) malloc(obuflen); - osav = obuf; - iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); - osav[1024-obuflen] = 0; - strcpy(buf, osav); - free(osav); - iconv_close(ic); - free(isav); - } - } - } - - // pre evaluate the first pair - nextend = end = NULL; - len = strlen(buf); - start = strstr(buf, "=?"); - if (start != NULL) - FindNextEnd (start, end); - - while ((start != NULL) && (end != NULL)) { - next = strstr(end, "=?"); - if (next != NULL) - FindNextEnd(next, nextend); - if (nextend == NULL) - next = NULL; - - // did we find two partitions - if ((next != NULL) && ((next - end) > 2)) { - ptr = end + 2; - while ((ptr < next) && - (isspace(*ptr) || - (*ptr == '\r') || - (*ptr == '\n') || - (*ptr == '\t'))) - ptr ++; - // did we find a gab just filled with blanks? - if (ptr == next) { - memmove(end + 2, next, len - (next - start)); - - // now terminate the gab at the end - delta = (next - end) - 2; - len -= delta; - buf[len] = '\0'; - - // move next to its new location. - next -= delta; - nextend -= delta; - } - } - // our next-pair is our new first pair now. - start = next; - end = nextend; - } - - // Now we handle foreign character sets properly encoded in RFC2047 format. - start = strstr(buf, "=?"); - FindNextEnd((start != NULL)? start : buf, end); - while (start != NULL && end != NULL && end > start) { - extract_token(charset, start, 1, '?', sizeof charset); - extract_token(encoding, start, 2, '?', sizeof encoding); - extract_token(istr, start, 3, '?', sizeof istr); - - ibuf = malloc(1024); - isav = ibuf; - if (!strcasecmp(encoding, "B")) { // base64 - ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr)); - } - else if (!strcasecmp(encoding, "Q")) { // quoted-printable - size_t len; - unsigned long pos; - - len = strlen(istr); - pos = 0; - while (pos < len) { - if (istr[pos] == '_') istr[pos] = ' '; - pos++; - } - ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len); - } - else { - strcpy(ibuf, istr); // unknown encoding - ibuflen = strlen(istr); - } - - ctdl_iconv_open("UTF-8", charset, &ic); - if (ic != (iconv_t)(-1) ) { - obuflen = 1024; - obuf = (char *) malloc(obuflen); - osav = obuf; - iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); - osav[1024-obuflen] = 0; - - end = start; - end++; - strcpy(start, ""); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - strcpy(end, &end[1]); - - snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end); - strcpy(buf, newbuf); - free(osav); - iconv_close(ic); - } - else { - end = start; - end++; - strcpy(start, ""); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - strcpy(end, &end[1]); - - snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end); - strcpy(buf, newbuf); - } - - free(isav); - - // Since spammers will go to all sorts of absurd lengths to get their - // messages through, there are LOTS of corrupt headers out there. - // So, prevent a really badly formed RFC2047 header from throwing - // this function into an infinite loop. - ++passes; - if (passes > 20) return; - - start = strstr(buf, "=?"); - FindNextEnd((start != NULL)? start : buf, end); - } - -} -#else -inline void utf8ify_rfc822_string(char *a){}; - -#endif - - -char *inetcfg = NULL; - -// Return nonzero if the supplied name is an alias for this host. -int CtdlHostAlias(char *fqdn) { - int config_lines; - int i; - char buf[256]; - char host[256], type[256]; - int found = 0; - - if (fqdn == NULL) return(hostalias_nomatch); - if (IsEmptyStr(fqdn)) return(hostalias_nomatch); - if (!strcasecmp(fqdn, "localhost")) return(hostalias_localhost); - if (!strcasecmp(fqdn, CtdlGetConfigStr("c_fqdn"))) return(hostalias_localhost); - if (!strcasecmp(fqdn, CtdlGetConfigStr("c_nodename"))) return(hostalias_localhost); - if (inetcfg == NULL) return(hostalias_nomatch); - - config_lines = num_tokens(inetcfg, '\n'); - for (i=0; inum_local == 0) { - free_recipients(recp); - return(0); - } - - for (i=0; inum_local; ++i) { - extract_token(addr, recp->recp_local, i, '|', addr_buf_len); - if (!strcasecmp(addr, CC->user.fullname)) { - free_recipients(recp); - return(1); - } - } - - free_recipients(recp); - return(0); -} - - -// If the last item in a list of recipients was truncated to a partial address, -// remove it completely in order to avoid choking library functions. -void sanitize_truncated_recipient(char *str) { - if (!str) return; - if (num_tokens(str, ',') < 2) return; - - int len = strlen(str); - if (len < 900) return; - if (len > 998) str[998] = 0; - - char *cptr = strrchr(str, ','); - if (!cptr) return; - - char *lptr = strchr(cptr, '<'); - char *rptr = strchr(cptr, '>'); - - if ( (lptr) && (rptr) && (rptr > lptr) ) return; - - *cptr = 0; -} - - -// This function is self explanatory. -// (What can I say, I'm in a weird mood today...) -void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name) { - char *ptr; - if (!name) return; - - for (ptr=name; *ptr; ++ptr) { - while ( (isspace(*ptr)) && (*(ptr+1)=='@') ) { - strcpy(ptr, ptr+1); - if (ptr > name) --ptr; - } - while ( (*ptr=='@') && (*(ptr+1)!=0) && (isspace(*(ptr+1))) ) { - strcpy(ptr+1, ptr+2); - } - } -} - - -// values that can be returned by expand_aliases() -enum { - EA_ERROR, // Can't send message due to bad address - EA_MULTIPLE, // Alias expanded into multiple recipients -- run me again! - EA_LOCAL, // Local message, do no network processing - EA_INTERNET, // Convert msg and send as Internet mail - EA_SKIP // This recipient has been invalidated -- skip it! -}; - - -// Process alias and routing info for email addresses -int expand_aliases(char *name, char *aliases) { - int a; - char aaa[SIZ]; - int at = 0; - - if (aliases) { - int num_aliases = num_tokens(aliases, '\n'); - for (a=0; a to <%s>", name, bar); - strcpy(name, bar); - } - } - } - if (strchr(name, ',')) { - return(EA_MULTIPLE); - } - } - - char original_name[256]; // Now go for the regular aliases - safestrncpy(original_name, name, sizeof original_name); - - // should these checks still be here, or maybe move them to split_recps() ? - striplt(name); - remove_any_whitespace_to_the_left_or_right_of_at_symbol(name); - stripallbut(name, '<', '>'); - - // Hit the email address directory - if (CtdlDirectoryLookup(aaa, name, sizeof aaa) == 0) { - strcpy(name, aaa); - } - - if (strcasecmp(original_name, name)) { - syslog(LOG_INFO, "internet_addressing: directory alias <%s> to <%s>", original_name, name); - } - - // Change "user @ xxx" to "user" if xxx is an alias for this host - for (a=0; name[a] != '\0'; ++a) { - if (name[a] == '@') { - if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) { - name[a] = 0; - syslog(LOG_DEBUG, "internet_addressing: host is local, recipient is <%s>", name); - break; - } - } - } - - // Is this a local or remote recipient? - at = haschar(name, '@'); - if (at == 0) { - return(EA_LOCAL); // no @'s = local address - } - else if (at == 1) { - return(EA_INTERNET); // one @ = internet address - } - else { - return(EA_ERROR); // more than one @ = badly formed address - } -} - - -// Return a supplied list of email addresses as an array, removing superfluous information and syntax. -// If an existing Array is supplied as "append_to" it will do so; otherwise a new Array is allocated. -Array *split_recps(char *addresses, Array *append_to) { - - if (IsEmptyStr(addresses)) { // nothing supplied, nothing returned - return(NULL); - } - - // Copy the supplied address list into our own memory space, because we are going to modify it. - char *a = strdup(addresses); - if (a == NULL) { - syslog(LOG_ERR, "internet_addressing: malloc() failed: %m"); - return(NULL); - } - - // Strip out anything in double quotes - char *l = NULL; - char *r = NULL; - do { - l = strchr(a, '\"'); - r = strrchr(a, '\"'); - if (r > l) { - strcpy(l, r+1); - } - } while (r > l); - - // Transform all qualifying delimiters to commas - char *t; - for (t=a; t[0]; ++t) { - if ((t[0]==';') || (t[0]=='|')) { - t[0]=','; - } - } - - // Tokenize the recipients into an array. No single recipient should be larger than 256 bytes. - Array *recipients_array = NULL; - if (append_to) { - recipients_array = append_to; // Append to an existing array of recipients - } - else { - recipients_array = array_new(256); // This is a new array of recipients - } - - int num_addresses = num_tokens(a, ','); - int i; - for (i=0; i'); // if angle brackets are present, keep only what is inside them - if (!IsEmptyStr(this_address)) { - array_append(recipients_array, this_address); - } - } - - free(a); // We don't need this buffer anymore. - return(recipients_array); // Return the completed array to the caller. -} - - -// Validate recipients, count delivery types and errors, and handle aliasing -// -// Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses -// were specified, or the number of addresses found invalid. -// -// Caller needs to free the result using free_recipients() -// -struct recptypes *validate_recipients(char *supplied_recipients, const char *RemoteIdentifier, int Flags) { - struct recptypes *ret; - char *recipients = NULL; - char append[SIZ]; - long len; - int mailtype; - int invalid; - struct ctdluser tempUS; - struct ctdlroom original_room; - int err = 0; - char errmsg[SIZ]; - char *org_recp; - char this_recp[256]; - - ret = (struct recptypes *) malloc(sizeof(struct recptypes)); // Initialize - if (ret == NULL) return(NULL); - memset(ret, 0, sizeof(struct recptypes)); // set all values to null/zero - - if (supplied_recipients == NULL) { - recipients = strdup(""); - } - else { - recipients = strdup(supplied_recipients); - } - - len = strlen(recipients) + 1024; // allocate memory - ret->errormsg = malloc(len); - ret->recp_local = malloc(len); - ret->recp_internet = malloc(len); - ret->recp_room = malloc(len); - ret->display_recp = malloc(len); - ret->recp_orgroom = malloc(len); - - ret->errormsg[0] = 0; - ret->recp_local[0] = 0; - ret->recp_internet[0] = 0; - ret->recp_room[0] = 0; - ret->recp_orgroom[0] = 0; - ret->display_recp[0] = 0; - ret->recptypes_magic = RECPTYPES_MAGIC; - - Array *recp_array = split_recps(supplied_recipients, NULL); - - char *aliases = CtdlGetSysConfig(GLOBAL_ALIASES); // First hit the Global Alias Table - - int r; - for (r=0; (recp_array && r", r, mailtype, this_recp); - invalid = 0; - errmsg[0] = 0; - switch(mailtype) { - case EA_LOCAL: // There are several types of "local" recipients. - - // Old BBS conventions require mail to "sysop" to go somewhere. Send it to the admin room. - if (!strcasecmp(this_recp, "sysop")) { - ++ret->num_room; - strcpy(this_recp, CtdlGetConfigStr("c_aideroom")); - if (!IsEmptyStr(ret->recp_room)) { - strcat(ret->recp_room, "|"); - } - strcat(ret->recp_room, this_recp); - } - - // This handles rooms which can receive posts via email. - else if (!strncasecmp(this_recp, "room_", 5)) { - original_room = CC->room; // Remember where we parked - - char mail_to_room[ROOMNAMELEN]; - char *m; - strncpy(mail_to_room, &this_recp[5], sizeof mail_to_room); - for (m = mail_to_room; *m; ++m) { - if (m[0] == '_') m[0]=' '; - } - if (!CtdlGetRoom(&CC->room, mail_to_room)) { // Find the room they asked for - - err = CtdlDoIHavePermissionToPostInThisRoom( // check for write permissions to room - errmsg, - sizeof errmsg, - RemoteIdentifier, - Flags, - 0 // 0 means "this is not a reply" - ); - if (err) { - ++ret->num_error; - invalid = 1; - } - else { - ++ret->num_room; - if (!IsEmptyStr(ret->recp_room)) { - strcat(ret->recp_room, "|"); - } - strcat(ret->recp_room, CC->room.QRname); - - if (!IsEmptyStr(ret->recp_orgroom)) { - strcat(ret->recp_orgroom, "|"); - } - strcat(ret->recp_orgroom, this_recp); - - } - } - else { // no such room exists - ++ret->num_error; - invalid = 1; - } - - // Restore this session's original room location. - CC->room = original_room; - - } - - // This handles the most common case, which is mail to a user's inbox. - else if (CtdlGetUser(&tempUS, this_recp) == 0) { - ++ret->num_local; - strcpy(this_recp, tempUS.fullname); - if (!IsEmptyStr(ret->recp_local)) { - strcat(ret->recp_local, "|"); - } - strcat(ret->recp_local, this_recp); - } - - // No match for this recipient - else { - ++ret->num_error; - invalid = 1; - } - break; - case EA_INTERNET: - // Yes, you're reading this correctly: if the target domain points back to the local system, - // the address is invalid. That's because if the address were valid, we would have - // already translated it to a local address by now. - if (IsDirectory(this_recp, 0)) { - ++ret->num_error; - invalid = 1; - } - else { - ++ret->num_internet; - if (!IsEmptyStr(ret->recp_internet)) { - strcat(ret->recp_internet, "|"); - } - strcat(ret->recp_internet, this_recp); - } - break; - case EA_MULTIPLE: - case EA_SKIP: - // no action required, anything in this slot has already been processed elsewhere - break; - case EA_ERROR: - ++ret->num_error; - invalid = 1; - break; - } - if (invalid) { - if (IsEmptyStr(errmsg)) { - snprintf(append, sizeof append, "Invalid recipient: %s", this_recp); - } - else { - snprintf(append, sizeof append, "%s", errmsg); - } - if ( (strlen(ret->errormsg) + strlen(append) + 3) < SIZ) { - if (!IsEmptyStr(ret->errormsg)) { - strcat(ret->errormsg, "; "); - } - strcat(ret->errormsg, append); - } - } - else { - if (IsEmptyStr(ret->display_recp)) { - strcpy(append, this_recp); - } - else { - snprintf(append, sizeof append, ", %s", this_recp); - } - if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) { - strcat(ret->display_recp, append); - } - } - } - - if (aliases != NULL) { // ok, we're done with the global alias list now - free(aliases); - } - - if ( (ret->num_local + ret->num_internet + ret->num_room + ret->num_error) == 0) { - ret->num_error = (-1); - strcpy(ret->errormsg, "No recipients specified."); - } - - syslog(LOG_DEBUG, "internet_addressing: validate_recipients() = %d local, %d room, %d SMTP, %d error", - ret->num_local, ret->num_room, ret->num_internet, ret->num_error - ); - - free(recipients); - if (recp_array) { - array_free(recp_array); - } - - return(ret); -} - - -// Destructor for recptypes -void free_recipients(struct recptypes *valid) { - - if (valid == NULL) { - return; - } - - if (valid->recptypes_magic != RECPTYPES_MAGIC) { - syslog(LOG_ERR, "internet_addressing: attempt to call free_recipients() on some other data type!"); - abort(); - } - - if (valid->errormsg != NULL) free(valid->errormsg); - if (valid->recp_local != NULL) free(valid->recp_local); - if (valid->recp_internet != NULL) free(valid->recp_internet); - if (valid->recp_room != NULL) free(valid->recp_room); - if (valid->recp_orgroom != NULL) free(valid->recp_orgroom); - if (valid->display_recp != NULL) free(valid->display_recp); - if (valid->bounce_to != NULL) free(valid->bounce_to); - if (valid->envelope_from != NULL) free(valid->envelope_from); - if (valid->sending_room != NULL) free(valid->sending_room); - free(valid); -} - - -char *qp_encode_email_addrs(char *source) { - char *user, *node, *name; - const char headerStr[] = "=?UTF-8?Q?"; - char *Encoded; - char *EncodedName; - char *nPtr; - int need_to_encode = 0; - long SourceLen; - long EncodedMaxLen; - long nColons = 0; - long *AddrPtr; - long *AddrUtf8; - long nAddrPtrMax = 50; - long nmax; - int InQuotes = 0; - int i, n; - - if (source == NULL) return source; - if (IsEmptyStr(source)) return source; - syslog(LOG_DEBUG, "internet_addressing: qp_encode_email_addrs <%s>", source); - - AddrPtr = malloc (sizeof (long) * nAddrPtrMax); - AddrUtf8 = malloc (sizeof (long) * nAddrPtrMax); - memset(AddrUtf8, 0, sizeof (long) * nAddrPtrMax); - *AddrPtr = 0; - i = 0; - while (!IsEmptyStr (&source[i])) { - if (nColons >= nAddrPtrMax){ - long *ptr; - - ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2); - memcpy (ptr, AddrPtr, sizeof (long) * nAddrPtrMax); - free (AddrPtr), AddrPtr = ptr; - - ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2); - memset(&ptr[nAddrPtrMax], 0, sizeof (long) * nAddrPtrMax); - - memcpy (ptr, AddrUtf8, sizeof (long) * nAddrPtrMax); - free (AddrUtf8), AddrUtf8 = ptr; - nAddrPtrMax *= 2; - } - if (((unsigned char) source[i] < 32) || ((unsigned char) source[i] > 126)) { - need_to_encode = 1; - AddrUtf8[nColons] = 1; - } - if (source[i] == '"') { - InQuotes = !InQuotes; - } - if (!InQuotes && source[i] == ',') { - AddrPtr[nColons] = i; - nColons++; - } - i++; - } - if (need_to_encode == 0) { - free(AddrPtr); - free(AddrUtf8); - return source; - } - - SourceLen = i; - EncodedMaxLen = nColons * (sizeof(headerStr) + 3) + SourceLen * 3; - Encoded = (char*) malloc (EncodedMaxLen); - - for (i = 0; i < nColons; i++) { - source[AddrPtr[i]++] = '\0'; - } - // TODO: if libidn, this might get larger - user = malloc(SourceLen + 1); - node = malloc(SourceLen + 1); - name = malloc(SourceLen + 1); - - nPtr = Encoded; - *nPtr = '\0'; - for (i = 0; i < nColons && nPtr != NULL; i++) { - nmax = EncodedMaxLen - (nPtr - Encoded); - if (AddrUtf8[i]) { - process_rfc822_addr(&source[AddrPtr[i]], user, node, name); - // TODO: libIDN here ! - if (IsEmptyStr(name)) { - n = snprintf(nPtr, nmax, (i==0)?"%s@%s" : ",%s@%s", user, node); - } - else { - EncodedName = rfc2047encode(name, strlen(name)); - n = snprintf(nPtr, nmax, (i==0)?"%s <%s@%s>" : ",%s <%s@%s>", EncodedName, user, node); - free(EncodedName); - } - } - else { - n = snprintf(nPtr, nmax, (i==0)?"%s" : ",%s", &source[AddrPtr[i]]); - } - if (n > 0 ) - nPtr += n; - else { - char *ptr, *nnPtr; - ptr = (char*) malloc(EncodedMaxLen * 2); - memcpy(ptr, Encoded, EncodedMaxLen); - nnPtr = ptr + (nPtr - Encoded), nPtr = nnPtr; - free(Encoded), Encoded = ptr; - EncodedMaxLen *= 2; - i--; // do it once more with properly lengthened buffer - } - } - for (i = 0; i < nColons; i++) - source[--AddrPtr[i]] = ','; - - free(user); - free(node); - free(name); - free(AddrUtf8); - free(AddrPtr); - return Encoded; -} - - -// Unfold a multi-line field into a single line, removing multi-whitespaces -void unfold_rfc822_field(char **field, char **FieldEnd) -{ - int quote = 0; - char *pField = *field; - char *sField; - char *pFieldEnd = *FieldEnd; - - while (isspace(*pField)) - pField++; - // remove leading/trailing whitespace - ; - - while (isspace(*pFieldEnd)) - pFieldEnd --; - - *FieldEnd = pFieldEnd; - // convert non-space whitespace to spaces, and remove double blanks - for (sField = *field = pField; - sField < pFieldEnd; - pField++, sField++) - { - if ((*sField=='\r') || (*sField=='\n')) - { - int offset = 1; - while ( ( (*(sField + offset) == '\r') || (*(sField + offset) == '\n' )) && (sField + offset < pFieldEnd) ) { - offset ++; - } - sField += offset; - *pField = *sField; - } - else { - if (*sField=='\"') quote = 1 - quote; - if (!quote) { - if (isspace(*sField)) { - *pField = ' '; - pField++; - sField++; - - while ((sField < pFieldEnd) && - isspace(*sField)) - sField++; - *pField = *sField; - } - else *pField = *sField; - } - else *pField = *sField; - } - } - *pField = '\0'; - *FieldEnd = pField - 1; -} - - -// Split an RFC822-style address into userid, host, and full name -// -// Note: This still handles obsolete address syntaxes such as user%node@node and ...node!user -// We should probably remove that. -void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) { - int a; - - strcpy(user, ""); - strcpy(node, CtdlGetConfigStr("c_fqdn")); - strcpy(name, ""); - - if (rfc822 == NULL) return; - - // extract full name - first, it's From minus - strcpy(name, rfc822); - stripout(name, '<', '>'); - - // strip anything to the left of a bang - while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0)) - strcpy(name, &name[1]); - - // and anything to the right of a @ or % - for (a = 0; name[a] != '\0'; ++a) { - if (name[a] == '@') { - name[a] = 0; - break; - } - if (name[a] == '%') { - name[a] = 0; - break; - } - } - - // but if there are parentheses, that changes the rules... - if ((haschar(rfc822, '(') == 1) && (haschar(rfc822, ')') == 1)) { - strcpy(name, rfc822); - stripallbut(name, '(', ')'); - } - - // but if there are a set of quotes, that supersedes everything - if (haschar(rfc822, 34) == 2) { - strcpy(name, rfc822); - while ((!IsEmptyStr(name)) && (name[0] != 34)) { - strcpy(&name[0], &name[1]); - } - strcpy(&name[0], &name[1]); - for (a = 0; name[a] != '\0'; ++a) - if (name[a] == 34) { - name[a] = 0; - break; - } - } - // extract user id - strcpy(user, rfc822); - - // first get rid of anything in parens - stripout(user, '(', ')'); - - // if there's a set of angle brackets, strip it down to that - if ((haschar(user, '<') == 1) && (haschar(user, '>') == 1)) { - stripallbut(user, '<', '>'); - } - - // strip anything to the left of a bang - while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0)) - strcpy(user, &user[1]); - - // and anything to the right of a @ or % - for (a = 0; user[a] != '\0'; ++a) { - if (user[a] == '@') { - user[a] = 0; - break; - } - if (user[a] == '%') { - user[a] = 0; - break; - } - } - - - // extract node name - strcpy(node, rfc822); - - // first get rid of anything in parens - stripout(node, '(', ')'); - - // if there's a set of angle brackets, strip it down to that - if ((haschar(node, '<') == 1) && (haschar(node, '>') == 1)) { - stripallbut(node, '<', '>'); - } - - // If no node specified, tack ours on instead - if ( - (haschar(node, '@')==0) - && (haschar(node, '%')==0) - && (haschar(node, '!')==0) - ) { - strcpy(node, CtdlGetConfigStr("c_nodename")); - } - else { - - // strip anything to the left of a @ - while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0)) - strcpy(node, &node[1]); - - // strip anything to the left of a % - while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0)) - strcpy(node, &node[1]); - - // reduce multiple system bang paths to node!user - while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1)) - strcpy(node, &node[1]); - - // now get rid of the user portion of a node!user string - for (a = 0; node[a] != '\0'; ++a) - if (node[a] == '!') { - node[a] = 0; - break; - } - } - - // strip leading and trailing spaces in all strings - striplt(user); - striplt(node); - striplt(name); - - // If we processed a string that had the address in angle brackets - // but no name outside the brackets, we now have an empty name. In - // this case, use the user portion of the address as the name. - if ((IsEmptyStr(name)) && (!IsEmptyStr(user))) { - strcpy(name, user); - } -} - - -// convert_field() is a helper function for convert_internet_message(). -// Given start/end positions for an rfc822 field, it converts it to a Citadel -// field if it wants to, and unfolds it if necessary. -// -// Returns 1 if the field was converted and inserted into the Citadel message -// structure, implying that the source field should be removed from the -// message text. -int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) { - char *key, *value, *valueend; - long len; - const char *pos; - int i; - const char *colonpos = NULL; - int processed = 0; - char user[1024]; - char node[1024]; - char name[1024]; - char addr[1024]; - time_t parsed_date; - long valuelen; - - for (pos = end; pos >= beg; pos--) { - if (*pos == ':') colonpos = pos; - } - - if (colonpos == NULL) return(0); /* no colon? not a valid header line */ - - len = end - beg; - key = malloc(len + 2); - memcpy(key, beg, len + 1); - key[len] = '\0'; - valueend = key + len; - * ( key + (colonpos - beg) ) = '\0'; - value = &key[(colonpos - beg) + 1]; - // printf("Header: [%s]\nValue: [%s]\n", key, value); - unfold_rfc822_field(&value, &valueend); - valuelen = valueend - value + 1; - // printf("UnfoldedValue: [%s]\n", value); - - // Here's the big rfc822-to-citadel loop. - - // Date/time is converted into a unix timestamp. If the conversion - // fails, we replace it with the time the message arrived locally. - if (!strcasecmp(key, "Date")) { - parsed_date = parsedate(value); - if (parsed_date < 0L) parsed_date = time(NULL); - - if (CM_IsEmpty(msg, eTimestamp)) - CM_SetFieldLONG(msg, eTimestamp, parsed_date); - processed = 1; - } - - else if (!strcasecmp(key, "From")) { - process_rfc822_addr(value, user, node, name); - syslog(LOG_DEBUG, "internet_addressing: converted to <%s@%s> (%s)", user, node, name); - snprintf(addr, sizeof(addr), "%s@%s", user, node); - if (CM_IsEmpty(msg, eAuthor) && !IsEmptyStr(name)) { - CM_SetField(msg, eAuthor, name, -1); - } - if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr)) { - CM_SetField(msg, erFc822Addr, addr, -1); - } - processed = 1; - } - - else if (!strcasecmp(key, "Subject")) { - if (CM_IsEmpty(msg, eMsgSubject)) - CM_SetField(msg, eMsgSubject, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "List-ID")) { - if (CM_IsEmpty(msg, eListID)) - CM_SetField(msg, eListID, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "To")) { - if (CM_IsEmpty(msg, eRecipient)) - CM_SetField(msg, eRecipient, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "CC")) { - if (CM_IsEmpty(msg, eCarbonCopY)) - CM_SetField(msg, eCarbonCopY, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "Message-ID")) { - if (!CM_IsEmpty(msg, emessageId)) { - syslog(LOG_WARNING, "internet_addressing: duplicate message id"); - } - else { - char *pValue; - long pValueLen; - - pValue = value; - pValueLen = valuelen; - // Strip angle brackets - while (haschar(pValue, '<') > 0) { - pValue ++; - pValueLen --; - } - - for (i = 0; i <= pValueLen; ++i) - if (pValue[i] == '>') { - pValueLen = i; - break; - } - - CM_SetField(msg, emessageId, pValue, pValueLen); - } - - processed = 1; - } - - else if (!strcasecmp(key, "Return-Path")) { - if (CM_IsEmpty(msg, eMessagePath)) - CM_SetField(msg, eMessagePath, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "Envelope-To")) { - if (CM_IsEmpty(msg, eenVelopeTo)) - CM_SetField(msg, eenVelopeTo, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "References")) { - CM_SetField(msg, eWeferences, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "Reply-To")) { - CM_SetField(msg, eReplyTo, value, valuelen); - processed = 1; - } - - else if (!strcasecmp(key, "In-reply-to")) { - if (CM_IsEmpty(msg, eWeferences)) // References: supersedes In-reply-to: - CM_SetField(msg, eWeferences, value, valuelen); - processed = 1; - } - - - - // Clean up and move on. - free(key); // Don't free 'value', it's actually the same buffer - return processed; -} - - -// Convert RFC822 references format (References) to Citadel references format (Weferences) -void convert_references_to_wefewences(char *str) { - int bracket_nesting = 0; - char *ptr = str; - char *moveptr = NULL; - char ch; - - while(*ptr) { - ch = *ptr; - if (ch == '>') { - --bracket_nesting; - if (bracket_nesting < 0) bracket_nesting = 0; - } - if ((ch == '>') && (bracket_nesting == 0) && (*(ptr+1)) && (ptr>str) ) { - *ptr = '|'; - ++ptr; - } - else if (bracket_nesting > 0) { - ++ptr; - } - else { - moveptr = ptr; - while (*moveptr) { - *moveptr = *(moveptr+1); - ++moveptr; - } - } - if (ch == '<') ++bracket_nesting; - } - -} - - -// Convert an RFC822 message (headers + body) to a CtdlMessage structure. -// NOTE: the supplied buffer becomes part of the CtdlMessage structure, and -// will be deallocated when CM_Free() is called. Therefore, the -// supplied buffer should be DEREFERENCED. It should not be freed or used -// again. -struct CtdlMessage *convert_internet_message(char *rfc822) { - StrBuf *RFCBuf = NewStrBufPlain(rfc822, -1); - free (rfc822); - return convert_internet_message_buf(&RFCBuf); -} - - -struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822) -{ - struct CtdlMessage *msg; - const char *pos, *beg, *end, *totalend; - int done, alldone = 0; - int converted; - StrBuf *OtherHeaders; - - msg = malloc(sizeof(struct CtdlMessage)); - if (msg == NULL) return msg; - - memset(msg, 0, sizeof(struct CtdlMessage)); - msg->cm_magic = CTDLMESSAGE_MAGIC; // self check - msg->cm_anon_type = 0; // never anonymous - msg->cm_format_type = FMT_RFC822; // internet message - - pos = ChrPtr(*rfc822); - totalend = pos + StrLength(*rfc822); - done = 0; - OtherHeaders = NewStrBufPlain(NULL, StrLength(*rfc822)); - - while (!alldone) { - - /* Locate beginning and end of field, keeping in mind that - * some fields might be multiline - */ - end = beg = pos; - - while ((end < totalend) && - (end == beg) && - (done == 0) ) - { - - if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) ) - { - end = pos; - } - - /* done with headers? */ - if ((*pos=='\n') && - ( (*(pos+1)=='\n') || - (*(pos+1)=='\r')) ) - { - alldone = 1; - } - - if (pos >= (totalend - 1) ) - { - end = pos; - done = 1; - } - - ++pos; - - } - - /* At this point we have a field. Are we interested in it? */ - converted = convert_field(msg, beg, end); - - /* Strip the field out of the RFC822 header if we used it */ - if (!converted) { - StrBufAppendBufPlain(OtherHeaders, beg, end - beg, 0); - StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); - } - - /* If we've hit the end of the message, bail out */ - if (pos >= totalend) - alldone = 1; - } - StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); - if (pos < totalend) - StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0); - FreeStrBuf(rfc822); - CM_SetAsFieldSB(msg, eMesageText, &OtherHeaders); - - /* Follow-up sanity checks... */ - - /* If there's no timestamp on this message, set it to now. */ - if (CM_IsEmpty(msg, eTimestamp)) { - CM_SetFieldLONG(msg, eTimestamp, time(NULL)); - } - - /* If a W (references, or rather, Wefewences) field is present, we - * have to convert it from RFC822 format to Citadel format. - */ - if (!CM_IsEmpty(msg, eWeferences)) { - /// todo: API! - convert_references_to_wefewences(msg->cm_fields[eWeferences]); - } - - return msg; -} - - -/* - * Look for a particular header field in an RFC822 message text. If the - * requested field is found, it is unfolded (if necessary) and returned to - * the caller. The field name is stripped out, leaving only its contents. - * The caller is responsible for freeing the returned buffer. If the requested - * field is not present, or anything else goes wrong, it returns NULL. - */ -char *rfc822_fetch_field(const char *rfc822, const char *fieldname) { - char *fieldbuf = NULL; - const char *end_of_headers; - const char *field_start; - const char *ptr; - char *cont; - char fieldhdr[SIZ]; - - /* Should never happen, but sometimes we get stupid */ - if (rfc822 == NULL) return(NULL); - if (fieldname == NULL) return(NULL); - - snprintf(fieldhdr, sizeof fieldhdr, "%s:", fieldname); - - /* Locate the end of the headers, so we don't run past that point */ - end_of_headers = cbmstrcasestr(rfc822, "\n\r\n"); - if (end_of_headers == NULL) { - end_of_headers = cbmstrcasestr(rfc822, "\n\n"); - } - if (end_of_headers == NULL) return (NULL); - - field_start = cbmstrcasestr(rfc822, fieldhdr); - if (field_start == NULL) return(NULL); - if (field_start > end_of_headers) return(NULL); - - fieldbuf = malloc(SIZ); - strcpy(fieldbuf, ""); - - ptr = field_start; - ptr = cmemreadline(ptr, fieldbuf, SIZ-strlen(fieldbuf) ); - while ( (isspace(ptr[0])) && (ptr < end_of_headers) ) { - strcat(fieldbuf, " "); - cont = &fieldbuf[strlen(fieldbuf)]; - ptr = cmemreadline(ptr, cont, SIZ-strlen(fieldbuf) ); - striplt(cont); - } - - strcpy(fieldbuf, &fieldbuf[strlen(fieldhdr)]); - striplt(fieldbuf); - - return(fieldbuf); -} - - -/***************************************************************************** - * DIRECTORY MANAGEMENT FUNCTIONS * - *****************************************************************************/ - -/* - * Generate the index key for an Internet e-mail address to be looked up - * in the database. - */ -void directory_key(char *key, char *addr) { - int i; - int keylen = 0; - - for (i=0; !IsEmptyStr(&addr[i]); ++i) { - if (!isspace(addr[i])) { - key[keylen++] = tolower(addr[i]); - } - } - key[keylen++] = 0; - - syslog(LOG_DEBUG, "internet_addressing: directory key is <%s>", key); -} - - -/* - * Return nonzero if the supplied address is in one of "our" domains - */ -int IsDirectory(char *addr, int allow_masq_domains) { - char domain[256]; - int h; - - extract_token(domain, addr, 1, '@', sizeof domain); - striplt(domain); - - h = CtdlHostAlias(domain); - - if ( (h == hostalias_masq) && allow_masq_domains) - return(1); - - if (h == hostalias_localhost) { - return(1); - } - else { - return(0); - } -} - - -/* - * Add an Internet e-mail address to the directory for a user - */ -int CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr) { - char key[SIZ]; - - if (IsDirectory(internet_addr, 0) == 0) { - return 0; - } - syslog(LOG_DEBUG, "internet_addressing: create directory entry: %s --> %s", internet_addr, citadel_addr); - directory_key(key, internet_addr); - cdb_store(CDB_DIRECTORY, key, strlen(key), citadel_addr, strlen(citadel_addr)+1 ); - return 1; -} - - -/* - * Delete an Internet e-mail address from the directory. - * - * (NOTE: we don't actually use or need the citadel_addr variable; it's merely - * here because the callback API expects to be able to send it.) - */ -int CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr) { - char key[SIZ]; - - syslog(LOG_DEBUG, "internet_addressing: delete directory entry: %s --> %s", internet_addr, citadel_addr); - directory_key(key, internet_addr); - return cdb_delete(CDB_DIRECTORY, key, strlen(key) ) == 0; -} - - -/* - * Look up an Internet e-mail address in the directory. - * On success: returns 0, and Citadel address stored in 'target' - * On failure: returns nonzero - */ -int CtdlDirectoryLookup(char *target, char *internet_addr, size_t targbuflen) { - struct cdbdata *cdbrec; - char key[SIZ]; - - /* Dump it in there unchanged, just for kicks */ - if (target != NULL) { - safestrncpy(target, internet_addr, targbuflen); - } - - /* Only do lookups for addresses with hostnames in them */ - if (num_tokens(internet_addr, '@') != 2) return(-1); - - /* Only do lookups for domains in the directory */ - if (IsDirectory(internet_addr, 0) == 0) return(-1); - - directory_key(key, internet_addr); - cdbrec = cdb_fetch(CDB_DIRECTORY, key, strlen(key) ); - if (cdbrec != NULL) { - if (target != NULL) { - safestrncpy(target, cdbrec->ptr, targbuflen); - } - cdb_free(cdbrec); - return(0); - } - - return(-1); -} - - -/* - * Harvest any email addresses that someone might want to have in their - * "collected addresses" book. - */ -char *harvest_collected_addresses(struct CtdlMessage *msg) { - char *coll = NULL; - char addr[256]; - char user[256], node[256], name[256]; - int is_harvestable; - int i, j, h; - eMsgField field = 0; - - if (msg == NULL) return(NULL); - - is_harvestable = 1; - strcpy(addr, ""); - if (!CM_IsEmpty(msg, eAuthor)) { - strcat(addr, msg->cm_fields[eAuthor]); - } - if (!CM_IsEmpty(msg, erFc822Addr)) { - strcat(addr, " <"); - strcat(addr, msg->cm_fields[erFc822Addr]); - strcat(addr, ">"); - if (IsDirectory(msg->cm_fields[erFc822Addr], 0)) { - is_harvestable = 0; - } - } - - if (is_harvestable) { - coll = strdup(addr); - } - else { - coll = strdup(""); - } - - if (coll == NULL) return(NULL); - - /* Scan both the R (To) and Y (CC) fields */ - for (i = 0; i < 2; ++i) { - if (i == 0) field = eRecipient; - if (i == 1) field = eCarbonCopY; - - if (!CM_IsEmpty(msg, field)) { - for (j=0; jcm_fields[field], ','); ++j) { - extract_token(addr, msg->cm_fields[field], j, ',', sizeof addr); - if (strstr(addr, "=?") != NULL) - utf8ify_rfc822_string(addr); - process_rfc822_addr(addr, user, node, name); - h = CtdlHostAlias(node); - if (h != hostalias_localhost) { - coll = realloc(coll, strlen(coll) + strlen(addr) + 4); - if (coll == NULL) return(NULL); - if (!IsEmptyStr(coll)) { - strcat(coll, ","); - } - striplt(addr); - strcat(coll, addr); - } - } - } - } - - if (IsEmptyStr(coll)) { - free(coll); - return(NULL); - } - return(coll); -} - - -/* - * Helper function for CtdlRebuildDirectoryIndex() - */ -void CtdlRebuildDirectoryIndex_backend(char *username, void *data) { - - int j = 0; - struct ctdluser usbuf; - - if (CtdlGetUser(&usbuf, username) != 0) { - return; - } - - if ( (!IsEmptyStr(usbuf.fullname)) && (!IsEmptyStr(usbuf.emailaddrs)) ) { - for (j=0; j to <%s>", usbuf.fullname, new_emailaddrs); - - // Delete all of the existing directory index records for the user (easier this way) - for (i=0; ifullname, CtdlGetConfigStr("c_fqdn")); - for (j=0; ((synthetic_email_addr[j] != '\0')&&(synthetic_email_addr[j] != '@')); j++) { - synthetic_email_addr[j] = tolower(synthetic_email_addr[j]); - if (!isalnum(synthetic_email_addr[j])) { - synthetic_email_addr[j] = '_'; - } - } - } - else if (i == 1) { - // then try 'ctdl' followed by the user number - snprintf(synthetic_email_addr, sizeof synthetic_email_addr, "ctdl%08lx@%s", user->usernum, CtdlGetConfigStr("c_fqdn")); - } - else if (i > 1) { - // oof. just keep trying other numbers until we find one - snprintf(synthetic_email_addr, sizeof synthetic_email_addr, "ctdl%08x@%s", i, CtdlGetConfigStr("c_fqdn")); - } - u = CtdlDirectoryLookup(NULL, synthetic_email_addr, 0); - syslog(LOG_DEBUG, "user_ops: address <%s> lookup returned <%d>", synthetic_email_addr, u); - } - - CtdlSetEmailAddressesForUser(user->fullname, synthetic_email_addr); - strncpy(CC->user.emailaddrs, synthetic_email_addr, sizeof(user->emailaddrs)); - syslog(LOG_DEBUG, "user_ops: auto-generated email address <%s> for <%s>", synthetic_email_addr, user->fullname); -} - - -// Determine whether the supplied email address is subscribed to the supplied room's mailing list service. -int is_email_subscribed_to_list(char *email, char *room_name) { - struct ctdlroom room; - long roomnum; - char *roomnetconfig; - int found_it = 0; - - if (CtdlGetRoom(&room, room_name)) { - return(0); // room not found, so definitely not subscribed - } - - // If this room has the QR2_SMTP_PUBLIC flag set, anyone may email a post to this room, even non-subscribers. - if (room.QRflags2 & QR2_SMTP_PUBLIC) { - return(1); - } - - roomnum = room.QRnumber; - roomnetconfig = LoadRoomNetConfigFile(roomnum); - if (roomnetconfig == NULL) { - return(0); - } - - // We're going to do a very sloppy match here and simply search for the specified email address - // anywhere in the room's netconfig. If you don't like this, fix it yourself. - if (bmstrcasestr(roomnetconfig, email)) { - found_it = 1; - } - else { - found_it = 0; - } - - free(roomnetconfig); - return(found_it); -} diff --git a/citadel/internet_addressing.h b/citadel/internet_addressing.h deleted file mode 100644 index 443033561..000000000 --- a/citadel/internet_addressing.h +++ /dev/null @@ -1,35 +0,0 @@ - -#include "server.h" -#include "ctdl_module.h" - -struct recptypes *validate_recipients(char *recipients, const char *RemoteIdentifier, int Flags); -void free_recipients(struct recptypes *); -void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name); -char *rfc822_fetch_field(const char *rfc822, const char *fieldname); -void sanitize_truncated_recipient(char *str); -char *qp_encode_email_addrs(char *source); -int alias (char *name); -int IsDirectory(char *addr, int allow_masq_domains); -void CtdlRebuildDirectoryIndex(void); -int CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr); -int CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr); -int CtdlDirectoryLookup(char *target, char *internet_addr, size_t targbuflen); -void CtdlSetEmailAddressesForUser(char *requested_user, char *new_emailaddrs); -void AutoGenerateEmailAddressForUser(struct ctdluser *user); -struct CtdlMessage *convert_internet_message(char *rfc822); -struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822); -int CtdlIsMe(char *addr, int addr_buf_len); -int CtdlHostAlias(char *fqdn); -char *harvest_collected_addresses(struct CtdlMessage *msg); -int is_email_subscribed_to_list(char *email, char *room_name); - -/* - * Values that can be returned by CtdlHostAlias() - */ -enum { - hostalias_nomatch, - hostalias_localhost, - hostalias_masq -}; - -extern char *inetcfg; diff --git a/citadel/ipcdef.h b/citadel/ipcdef.h deleted file mode 100644 index 23897e455..000000000 --- a/citadel/ipcdef.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#define LISTING_FOLLOWS 100 -#define CIT_OK 200 -#define MORE_DATA 300 -#define SEND_LISTING 400 -#define ERROR 500 -#define BINARY_FOLLOWS 600 -#define SEND_BINARY 700 -#define START_CHAT_MODE 800 - -#define INTERNAL_ERROR 10 -#define TOO_BIG 11 -#define ILLEGAL_VALUE 12 -#define NOT_LOGGED_IN 20 -#define CMD_NOT_SUPPORTED 30 -#define SERVER_SHUTTING_DOWN 31 -#define PASSWORD_REQUIRED 40 -#define ALREADY_LOGGED_IN 41 -#define USERNAME_REQUIRED 42 -#define HIGHER_ACCESS_REQUIRED 50 -#define MAX_SESSIONS_EXCEEDED 51 -#define RESOURCE_BUSY 52 -#define RESOURCE_NOT_OPEN 53 -#define NOT_HERE 60 -#define INVALID_FLOOR_OPERATION 61 -#define NO_SUCH_USER 70 -#define FILE_NOT_FOUND 71 -#define ROOM_NOT_FOUND 72 -#define NO_SUCH_SYSTEM 73 -#define ALREADY_EXISTS 74 -#define MESSAGE_NOT_FOUND 75 - -#define ASYNC_MSG 900 -#define ASYNC_GEXP 02 - -#define QR_PERMANENT 1 /* Room does not purge */ -#define QR_INUSE 2 /* Set if in use, clear if avail */ -#define QR_PRIVATE 4 /* Set for any type of private room */ -#define QR_PASSWORDED 8 /* Set if there's a password too */ -#define QR_GUESSNAME 16 /* Set if it's a guessname room */ -#define QR_DIRECTORY 32 /* Directory room */ -#define QR_UPLOAD 64 /* Allowed to upload */ -#define QR_DOWNLOAD 128 /* Allowed to download */ -#define QR_VISDIR 256 /* Visible directory */ -#define QR_ANONONLY 512 /* Anonymous-Only room */ -#define QR_ANONOPT 1024 /* Anonymous-Option room */ -#define QR_NETWORK 2048 /* Shared network room */ -#define QR_PREFONLY 4096 /* Preferred status needed to enter */ -#define QR_READONLY 8192 /* Aide status required to post */ -#define QR_MAILBOX 16384 /* Set if this is a private mailbox */ - -#define QR2_SYSTEM 1 /* System room; hide by default */ -#define QR2_SELFLIST 2 /* Self-service mailing list mgmt */ -#define QR2_COLLABDEL 4 /* Anyone who can post can delete */ -#define QR2_SUBJECTREQ 8 /* Subject strongly recommended */ -#define QR2_SMTP_PUBLIC 16 /* Listservice Subscribers may post */ - -#define US_NEEDVALID 1 /* User needs to be validated */ -#define US_EXTEDIT 2 /* Always use external editor */ -#define US_PERM 4 /* Permanent user */ -#define US_LASTOLD 16 /* Print last old message with new */ -#define US_EXPERT 32 /* Experienced user */ -#define US_UNLISTED 64 /* Unlisted userlog entry */ -#define US_NOPROMPT 128 /* Don't prompt after each message */ -#define US_PROMPTCTL 256 /* ext & top work at prompt */ -#define US_DISAPPEAR 512 /* Use "disappearing msg prompts" */ -#define US_REGIS 1024 /* Registered user */ -#define US_PAGINATOR 2048 /* Pause after each screen of text */ -#define US_INTERNET 4096 /* Internet mail privileges */ -#define US_FLOORS 8192 /* User wants to see floors */ -#define US_COLOR 16384 /* User wants ANSI color support */ -#define US_USER_SET (US_LASTOLD | US_EXPERT | US_UNLISTED | \ - US_NOPROMPT | US_DISAPPEAR | US_PAGINATOR | \ - US_FLOORS | US_COLOR | US_PROMPTCTL | US_EXTEDIT) - -#define UA_KNOWN 2 /* Room appears in a 'known rooms' list */ -#define UA_GOTOALLOWED 4 /* User may goto this room if specified by exact name */ -#define UA_HASNEWMSGS 8 /* Unread messages exist in this room */ -#define UA_ZAPPED 16 /* User has forgotten (zapped) this room */ -#define UA_POSTALLOWED 32 /* User may post top-level messages here */ -#define UA_ADMINALLOWED 64 /* Aide or Room Aide rights exist here */ -#define UA_DELETEALLOWED 128 /* User is allowed to delete messages from this room */ -#define UA_REPLYALLOWED 256 /* User is allowed to reply to existing messages here */ - -#ifdef __cplusplus -} -#endif diff --git a/citadel/journaling.c b/citadel/journaling.c deleted file mode 100644 index 939ec3b7d..000000000 --- a/citadel/journaling.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Message journaling functions. - * - * Copyright (c) 1987-2020 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include "ctdl_module.h" -#include "citserver.h" -#include "config.h" -#include "user_ops.h" -#include "serv_vcard.h" /* Needed for vcard_getuser and extract_inet_email_addrs */ -#include "internet_addressing.h" -#include "journaling.h" - -struct jnlq *jnlq = NULL; /* journal queue */ - -/* - * Hand off a copy of a message to be journalized. - */ -void JournalBackgroundSubmit(struct CtdlMessage *msg, - StrBuf *saved_rfc822_version, - struct recptypes *recps) { - - struct jnlq *jptr = NULL; - - /* Avoid double journaling! */ - if (!CM_IsEmpty(msg, eJournal)) { - FreeStrBuf(&saved_rfc822_version); - return; - } - - jptr = (struct jnlq *)malloc(sizeof(struct jnlq)); - if (jptr == NULL) { - FreeStrBuf(&saved_rfc822_version); - return; - } - memset(jptr, 0, sizeof(struct jnlq)); - if (recps != NULL) memcpy(&jptr->recps, recps, sizeof(struct recptypes)); - if (!CM_IsEmpty(msg, eAuthor)) jptr->from = strdup(msg->cm_fields[eAuthor]); - if (!CM_IsEmpty(msg, erFc822Addr)) jptr->rfca = strdup(msg->cm_fields[erFc822Addr]); - if (!CM_IsEmpty(msg, eMsgSubject)) jptr->subj = strdup(msg->cm_fields[eMsgSubject]); - if (!CM_IsEmpty(msg, emessageId)) jptr->msgn = strdup(msg->cm_fields[emessageId]); - jptr->rfc822 = SmashStrBuf(&saved_rfc822_version); - - /* Add to the queue */ - begin_critical_section(S_JOURNAL_QUEUE); - jptr->next = jnlq; - jnlq = jptr; - end_critical_section(S_JOURNAL_QUEUE); -} - - -/* - * Convert a local user name to an internet email address for the journal - * FIXME - grab the user's Internet email address from the user record, not from vCard !!!! - */ -void local_to_inetemail(char *inetemail, char *localuser, size_t inetemail_len) { - struct ctdluser us; - struct vCard *v; - - strcpy(inetemail, ""); - if (CtdlGetUser(&us, localuser) != 0) { - return; - } - - v = vcard_get_user(&us); - if (v == NULL) { - return; - } - - extract_inet_email_addrs(inetemail, inetemail_len, NULL, 0, v, 1); - vcard_free(v); -} - - -/* - * Called by JournalRunQueue() to send an individual message. - */ -void JournalRunQueueMsg(struct jnlq *jmsg) { - - struct CtdlMessage *journal_msg = NULL; - struct recptypes *journal_recps = NULL; - StrBuf *message_text = NULL; - char mime_boundary[256]; - long mblen; - long rfc822len; - char recipient[256]; - char inetemail[256]; - static int seq = 0; - int i; - - if (jmsg == NULL) - return; - journal_recps = validate_recipients(CtdlGetConfigStr("c_journal_dest"), NULL, 0); - if (journal_recps != NULL) { - - if ( (journal_recps->num_local > 0) - || (journal_recps->num_internet > 0) - || (journal_recps->num_room > 0) - ) { - - /* - * Construct journal message. - * Note that we are transferring ownership of some of the memory here. - */ - journal_msg = malloc(sizeof(struct CtdlMessage)); - memset(journal_msg, 0, sizeof(struct CtdlMessage)); - journal_msg->cm_magic = CTDLMESSAGE_MAGIC; - journal_msg->cm_anon_type = MES_NORMAL; - journal_msg->cm_format_type = FMT_RFC822; - CM_SetField(journal_msg, eJournal, HKEY("is journal")); - - if (!IsEmptyStr(jmsg->from)) { - CM_SetField(journal_msg, eAuthor, jmsg->from, -1); - } - - if (!IsEmptyStr(jmsg->rfca)) { - CM_SetField(journal_msg, erFc822Addr, jmsg->rfca, -1); - } - - if (!IsEmptyStr(jmsg->subj)) { - CM_SetField(journal_msg, eMsgSubject, jmsg->subj, -1); - } - - mblen = snprintf(mime_boundary, sizeof(mime_boundary), - "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq); - - if (!IsEmptyStr(jmsg->rfc822)) { - rfc822len = strlen(jmsg->rfc822); - } - else { - rfc822len = 0; - } - - message_text = NewStrBufPlain(NULL, rfc822len + sizeof(struct recptypes) + 1024); - - /* - * Here is where we begin to compose the journalized message. - * (The "ExJournalReport" header is consumed by some email retention services which assume the journaling agent is Exchange.) - */ - StrBufAppendBufPlain( - message_text, - HKEY("Content-type: multipart/mixed; boundary=\""), - 0 - ); - - StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); - - StrBufAppendBufPlain( - message_text, - HKEY("\"\r\n" - "Content-Identifier: ExJournalReport\r\n" - "MIME-Version: 1.0\r\n" - "\n" - "--"), - 0 - ); - - StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); - - StrBufAppendBufPlain( - message_text, - HKEY("\r\n" - "Content-type: text/plain\r\n" - "\r\n" - "Sender: "), 0); - - if (CM_IsEmpty(journal_msg, eAuthor)) - StrBufAppendBufPlain( - message_text, - journal_msg->cm_fields[eAuthor], -1, 0); - else - StrBufAppendBufPlain( - message_text, - HKEY("(null)"), 0); - - if (!CM_IsEmpty(journal_msg, erFc822Addr)) { - StrBufAppendPrintf(message_text, " <%s>", - journal_msg->cm_fields[erFc822Addr]); - } - - StrBufAppendBufPlain(message_text, HKEY("\r\nMessage-ID: <"), 0); - StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0); - StrBufAppendBufPlain(message_text, HKEY(">\r\nRecipients:\r\n"), 0); - - if (jmsg->recps.num_local > 0) { - for (i=0; irecps.num_local; ++i) { - extract_token(recipient, jmsg->recps.recp_local, i, '|', sizeof recipient); - local_to_inetemail(inetemail, recipient, sizeof inetemail); - StrBufAppendPrintf(message_text, " %s <%s>\r\n", recipient, inetemail); - } - } - - if (jmsg->recps.num_internet > 0) { - for (i=0; irecps.num_internet; ++i) { - extract_token(recipient, jmsg->recps.recp_internet, i, '|', sizeof recipient); - StrBufAppendPrintf(message_text, " %s\r\n", recipient); - } - } - - StrBufAppendBufPlain(message_text, HKEY("\r\n" "--"), 0); - StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); - StrBufAppendBufPlain(message_text, HKEY("\r\nContent-type: message/rfc822\r\n\r\n"), 0); - StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0); - StrBufAppendBufPlain(message_text, HKEY("--"), 0); - StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); - StrBufAppendBufPlain(message_text, HKEY("--\r\n"), 0); - - CM_SetAsFieldSB(journal_msg, eMesageText, &message_text); - free(jmsg->rfc822); - free(jmsg->msgn); - jmsg->rfc822 = NULL; - jmsg->msgn = NULL; - - /* Submit journal message */ - CtdlSubmitMsg(journal_msg, journal_recps, ""); - CM_Free(journal_msg); - } - - free_recipients(journal_recps); - } - - /* We are responsible for freeing this memory. */ - free(jmsg); -} - - -/* - * Run the queue. - */ -void JournalRunQueue(void) { - struct jnlq *jptr = NULL; - - while (jnlq != NULL) { - begin_critical_section(S_JOURNAL_QUEUE); - if (jnlq != NULL) { - jptr = jnlq; - jnlq = jnlq->next; - } - end_critical_section(S_JOURNAL_QUEUE); - JournalRunQueueMsg(jptr); - } -} - - diff --git a/citadel/journaling.h b/citadel/journaling.h deleted file mode 100644 index 4a66781ae..000000000 --- a/citadel/journaling.h +++ /dev/null @@ -1,16 +0,0 @@ -struct jnlq { - struct jnlq *next; - struct recptypes recps; - char *from; - char *node; - char *rfca; - char *subj; - char *msgn; - char *rfc822; -}; - -void JournalBackgroundSubmit(struct CtdlMessage *msg, - StrBuf *saved_rfc822_version, - struct recptypes *recps); -void JournalRunQueueMsg(struct jnlq *jmsg); -void JournalRunQueue(void); diff --git a/citadel/ldap.c b/citadel/ldap.c deleted file mode 100644 index 5c54953c1..000000000 --- a/citadel/ldap.c +++ /dev/null @@ -1,631 +0,0 @@ -// These functions implement the portions of AUTHMODE_LDAP and AUTHMODE_LDAP_AD which -// actually speak to the LDAP server. -// -// Copyright (c) 2011-2022 by the citadel.org development team. -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License, version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// ldapsearch -D uid=admin,cn=users,cn=compat,dc=demo1,dc=freeipa,dc=org -w Secret123 -h ipa.demo1.freeipa.org - -int ctdl_require_ldap_version = 3; - -#define _GNU_SOURCE // Needed to suppress warning about vasprintf() when running on Linux/Linux -#include -#include -#include "citserver.h" -#include "citadel_ldap.h" -#include "ctdl_module.h" -#include "user_ops.h" -#include "internet_addressing.h" -#include "config.h" -#include - - -// Utility function, supply a search result and get back the fullname (display name, common name, etc) from the first result -// -// POSIX schema: the display name will be found in "cn" (common name) -// Active Directory: the display name will be found in "displayName" -// -void derive_fullname_from_ldap_result(char *fullname, int fullname_size, LDAP *ldserver, LDAPMessage *search_result) { - struct berval **values; - - if (fullname == NULL) return; - if (search_result == NULL) return; - if (ldserver == NULL) return; - - if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { - values = ldap_get_values_len(ldserver, search_result, "displayName"); - if (values) { - if (ldap_count_values_len(values) > 0) { - safestrncpy(fullname, values[0]->bv_val, fullname_size); - syslog(LOG_DEBUG, "ldap: displayName = %s", fullname); - } - ldap_value_free_len(values); - } - } - else { - values = ldap_get_values_len(ldserver, search_result, "cn"); - if (values) { - if (ldap_count_values_len(values) > 0) { - safestrncpy(fullname, values[0]->bv_val, fullname_size); - syslog(LOG_DEBUG, "ldap: cn = %s", fullname); - } - ldap_value_free_len(values); - } - } -} - - -// Utility function, supply a search result and get back the uid from the first result -// -// POSIX schema: numeric user id will be in the "uidNumber" attribute -// Active Directory: we make a uid hashed from "objectGUID" -// -uid_t derive_uid_from_ldap(LDAP *ldserver, LDAPMessage *entry) { - struct berval **values; - uid_t uid = (-1); - - if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { - values = ldap_get_values_len(ldserver, entry, "objectGUID"); - if (values) { - if (ldap_count_values_len(values) > 0) { - uid = abs(HashLittle(values[0]->bv_val, values[0]->bv_len)); - } - ldap_value_free_len(values); - } - } - else { - values = ldap_get_values_len(ldserver, entry, "uidNumber"); - if (values) { - if (ldap_count_values_len(values) > 0) { - uid = atoi(values[0]->bv_val); - } - ldap_value_free_len(values); - } - } - - syslog(LOG_DEBUG, "ldap: uid = %d", uid); - return(uid); -} - - -// Wrapper function for ldap_initialize() that consistently fills in the correct fields -int ctdl_ldap_initialize(LDAP **ld) { - - char server_url[256]; - int ret; - - snprintf(server_url, sizeof server_url, "ldap://%s:%d", CtdlGetConfigStr("c_ldap_host"), CtdlGetConfigInt("c_ldap_port")); - ret = ldap_initialize(ld, server_url); - if (ret != LDAP_SUCCESS) { - syslog(LOG_ERR, "ldap: could not connect to %s : %m", server_url); - *ld = NULL; - return(errno); - } - - return(ret); -} - - -// Bind to the LDAP server and return a working handle -LDAP *ctdl_ldap_bind(void) { - LDAP *ldserver = NULL; - int i; - - if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) { - return(NULL); - } - - ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); - ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); - - striplt(CtdlGetConfigStr("c_ldap_bind_dn")); - striplt(CtdlGetConfigStr("c_ldap_bind_pw")); - i = ldap_simple_bind_s(ldserver, - (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_dn")) ? CtdlGetConfigStr("c_ldap_bind_dn") : NULL), - (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_pw")) ? CtdlGetConfigStr("c_ldap_bind_pw") : NULL) - ); - if (i != LDAP_SUCCESS) { - syslog(LOG_ERR, "ldap: Cannot bind: %s (%d)", ldap_err2string(i), i); - return(NULL); - } - - return(ldserver); -} - - -// Look up a user in the directory to see if this is an account that can be authenticated -// -// POSIX schema: Search all "inetOrgPerson" objects with "uid" set to the supplied username -// Active Directory: Look for an account with "sAMAccountName" set to the supplied username -// -int CtdlTryUserLDAP(char *username, char *found_dn, int found_dn_size, char *fullname, int fullname_size, uid_t *uid) { - LDAP *ldserver = NULL; - LDAPMessage *search_result = NULL; - LDAPMessage *entry = NULL; - char searchstring[1024]; - struct timeval tv; - char *user_dn = NULL; - - ldserver = ctdl_ldap_bind(); - if (!ldserver) return(-1); - - if (fullname) safestrncpy(fullname, username, fullname_size); - tv.tv_sec = 10; - tv.tv_usec = 0; - - if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { - snprintf(searchstring, sizeof(searchstring), "(sAMAccountName=%s)", username); - } - else { - snprintf(searchstring, sizeof(searchstring), "(&(objectclass=inetOrgPerson)(uid=%s))", username); - } - - syslog(LOG_DEBUG, "ldap: search: %s", searchstring); - syslog(LOG_DEBUG, "ldap: search results: %s", ldap_err2string(ldap_search_ext_s( - ldserver, // ld - CtdlGetConfigStr("c_ldap_base_dn"), // base - LDAP_SCOPE_SUBTREE, // scope - searchstring, // filter - NULL, // attrs (all attributes) - 0, // attrsonly (attrs + values) - NULL, // serverctrls (none) - NULL, // clientctrls (none) - &tv, // timeout - 1, // sizelimit (1 result max) - &search_result // put the result here - ))); - - // Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when - // the search succeeds. Instead, we check to see whether search_result is still NULL. - if (search_result == NULL) { - syslog(LOG_DEBUG, "ldap: zero search results were returned"); - ldap_unbind(ldserver); - return(2); - } - - // At this point we've got at least one result from our query. If there are multiple - // results, we still only look at the first one. - entry = ldap_first_entry(ldserver, search_result); - if (entry) { - - user_dn = ldap_get_dn(ldserver, entry); - if (user_dn) { - syslog(LOG_DEBUG, "ldap: dn = %s", user_dn); - } - - derive_fullname_from_ldap_result(fullname, fullname_size, ldserver, search_result); - *uid = derive_uid_from_ldap(ldserver, search_result); - } - - // free the results - ldap_msgfree(search_result); - - // unbind so we can go back in as the authenticating user - ldap_unbind(ldserver); - - if (!user_dn) { - syslog(LOG_DEBUG, "ldap: No such user was found."); - return(4); - } - - if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size); - ldap_memfree(user_dn); - return(0); -} - - -// This is an extension of CtdlTryPassword() which gets called when using LDAP authentication. -int CtdlTryPasswordLDAP(char *user_dn, const char *password) { - LDAP *ldserver = NULL; - int i = (-1); - - if (IsEmptyStr(password)) { - syslog(LOG_DEBUG, "ldap: empty passwords are not permitted"); - return(1); - } - - syslog(LOG_DEBUG, "ldap: trying to bind as %s", user_dn); - i = ctdl_ldap_initialize(&ldserver); - if (i == LDAP_SUCCESS) { - ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); - i = ldap_simple_bind_s(ldserver, user_dn, password); - if (i == LDAP_SUCCESS) { - syslog(LOG_DEBUG, "ldap: bind succeeded"); - } - else { - syslog(LOG_DEBUG, "ldap: Cannot bind: %s (%d)", ldap_err2string(i), i); - } - ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); - ldap_unbind(ldserver); - } - - if (i == LDAP_SUCCESS) { - return(0); - } - - return(1); -} - - -// set multiple properties -// returns nonzero only if property changed. -int vcard_set_props_iff_different(struct vCard *v, char *propname, int numvals, char **vals) { - int i; - char *oldval = ""; - for (i=0; ibv_val,l[0]->bv_val,st[0]->bv_val,postalCode[0]->bv_val,c[0]->bv_val); - if (telephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;work","%s",telephoneNumber[0]->bv_val); - if (facsimileTelephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;fax","%s",facsimileTelephoneNumber[0]->bv_val); - if (mobile) changed_something |= vcard_set_one_prop_iff_different(v,"tel;cell","%s",mobile[0]->bv_val); - if (homePhone) changed_something |= vcard_set_one_prop_iff_different(v,"tel;home","%s",homePhone[0]->bv_val); - if (givenName && sn) { - if (initials) { - changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s;%s",sn[0]->bv_val,givenName[0]->bv_val,initials[0]->bv_val); - } - else { - changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s",sn[0]->bv_val,givenName[0]->bv_val); - } - } - - // FIXME we need a new way to do this. - //if (mail) { - //changed_something |= vcard_set_props_iff_different(v,"email;internet",ldap_count_values_len(mail),mail); - //} - - if (uuid) changed_something |= vcard_set_one_prop_iff_different(v,"X-uuid","%s",uuid[0]->bv_val); - if (o) changed_something |= vcard_set_one_prop_iff_different(v,"org","%s",o[0]->bv_val); - if (cn) changed_something |= vcard_set_one_prop_iff_different(v,"fn","%s",cn[0]->bv_val); - if (title) changed_something |= vcard_set_one_prop_iff_different(v,"title","%s",title[0]->bv_val); - - if (givenName) ldap_value_free_len(givenName); - if (initials) ldap_value_free_len(initials); - if (sn) ldap_value_free_len(sn); - if (cn) ldap_value_free_len(cn); - if (o) ldap_value_free_len(o); - if (street) ldap_value_free_len(street); - if (l) ldap_value_free_len(l); - if (st) ldap_value_free_len(st); - if (postalCode) ldap_value_free_len(postalCode); - if (telephoneNumber) ldap_value_free_len(telephoneNumber); - if (mobile) ldap_value_free_len(mobile); - if (homePhone) ldap_value_free_len(homePhone); - if (facsimileTelephoneNumber) ldap_value_free_len(facsimileTelephoneNumber); - if (mail) ldap_value_free_len(mail); - if (uid) ldap_value_free_len(uid); - if (homeDirectory) ldap_value_free_len(homeDirectory); - if (uidNumber) ldap_value_free_len(uidNumber); - if (loginShell) ldap_value_free_len(loginShell); - if (gidNumber) ldap_value_free_len(gidNumber); - if (c) ldap_value_free_len(c); - if (title) ldap_value_free_len(title); - if (uuid) ldap_value_free_len(uuid); - } - // free the results - ldap_msgfree(search_result); - - // unbind so we can go back in as the authenticating user - ldap_unbind(ldserver); - return(changed_something); // tell the caller whether we made any changes -} - - -// Extract a user's Internet email addresses from LDAP. -// Returns zero if we got a valid set of addresses; nonzero for error. -int extract_email_addresses_from_ldap(char *ldap_dn, char *emailaddrs) { - LDAP *ldserver = NULL; - struct timeval tv; - LDAPMessage *search_result = NULL; - LDAPMessage *entry = NULL; - struct berval **mail; - char *attrs[] = { "*","+",NULL }; - - if (!ldap_dn) return(1); - if (!emailaddrs) return(1); - - ldserver = ctdl_ldap_bind(); - if (!ldserver) return(-1); - - tv.tv_sec = 10; - tv.tv_usec = 0; - - syslog(LOG_DEBUG, "ldap: search: %s", ldap_dn); - syslog(LOG_DEBUG, "ldap: search results: %s", ldap_err2string(ldap_search_ext_s( - ldserver, // ld - ldap_dn, // base - LDAP_SCOPE_SUBTREE, // scope - NULL, // filter - attrs, // attrs (all attributes) - 0, // attrsonly (attrs + values) - NULL, // serverctrls (none) - NULL, // clientctrls (none) - &tv, // timeout - 1, // sizelimit (1 result max) - &search_result // res - ))); - - // Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when - // the search succeeds. Instead, we check to see whether search_result is still NULL. - if (search_result == NULL) { - syslog(LOG_DEBUG, "ldap: zero search results were returned"); - ldap_unbind(ldserver); - return(4); - } - - // At this point we've got at least one result from our query. - // If there are multiple results, we still only look at the first one. - emailaddrs[0] = 0; // clear out any previous results - entry = ldap_first_entry(ldserver, search_result); - if (entry) { - syslog(LOG_DEBUG, "ldap: search got user details"); - mail = ldap_get_values_len(ldserver, search_result, "mail"); - if (mail) { - int q; - for (q=0; qbv_val, 0)) { - if ((strlen(emailaddrs) + mail[q]->bv_len + 2) > 512) { - syslog(LOG_ERR, "ldap: can't fit all email addresses into user record"); - } - else { - if (!IsEmptyStr(emailaddrs)) { - strcat(emailaddrs, "|"); - } - strcat(emailaddrs, mail[q]->bv_val); - } - } - } - } - } - - // free the results - ldap_msgfree(search_result); - - // unbind so we can go back in as the authenticating user - ldap_unbind(ldserver); - return(0); -} - - -// Remember that a particular user exists in the Citadel database. -// As we scan the LDAP tree we will remove users from this list when we find them. -// At the end of the scan, any users remaining in this list are stale and should be deleted. -void ldap_note_user_in_citadel(char *username, void *data) { - return; -} - - -// Scan LDAP for users and populate Citadel's user database with everyone -// -// POSIX schema: All objects of class "inetOrgPerson" -// Active Directory: Objects that are class "user" and class "person" but NOT class "computer" -// -void CtdlSynchronizeUsersFromLDAP(void) { - LDAP *ldserver = NULL; - LDAPMessage *search_result = NULL; - LDAPMessage *entry = NULL; - char *user_dn = NULL; - char searchstring[1024]; - struct timeval tv; - - if ((CtdlGetConfigInt("c_auth_mode") != AUTHMODE_LDAP) && (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_LDAP_AD)) { - return; // If this site is not running LDAP, stop here. - } - - syslog(LOG_INFO, "ldap: synchronizing Citadel user database from LDAP"); - - // first, scan the existing Citadel user list - // ForEachUser(ldap_note_user_in_citadel, NULL); // FIXME finish this - - ldserver = ctdl_ldap_bind(); - if (!ldserver) return; - - tv.tv_sec = 10; - tv.tv_usec = 0; - - if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { - snprintf(searchstring, sizeof(searchstring), "(&(objectClass=user)(objectClass=person)(!(objectClass=computer)))"); - } - else { - snprintf(searchstring, sizeof(searchstring), "(objectClass=inetOrgPerson)"); - } - - syslog(LOG_DEBUG, "ldap: search: %s", searchstring); - syslog(LOG_DEBUG, "ldap: search results: %s", ldap_err2string(ldap_search_ext_s( - ldserver, // ld - CtdlGetConfigStr("c_ldap_base_dn"), // base - LDAP_SCOPE_SUBTREE, // scope - searchstring, // filter - NULL, // attrs (all attributes) - 0, // attrsonly (attrs + values) - NULL, // serverctrls (none) - NULL, // clientctrls (none) - &tv, // timeout - INT_MAX, // sizelimit (max) - &search_result // put the result here - ))); - - // Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when - // the search succeeds. Instead, we check to see whether search_result is still NULL. - if (search_result == NULL) { - syslog(LOG_DEBUG, "ldap: zero search results were returned"); - ldap_unbind(ldserver); - return; - } - - syslog(LOG_DEBUG, "ldap: %d entries returned", ldap_count_entries(ldserver, search_result)); - for (entry=ldap_first_entry(ldserver, search_result); entry!=NULL; entry=ldap_next_entry(ldserver, entry)) { - user_dn = ldap_get_dn(ldserver, entry); - if (user_dn) { - syslog(LOG_DEBUG, "ldap: found %s", user_dn); - - int fullname_size = 256; - char fullname[256] = { 0 } ; - uid_t uid = (-1); - char new_emailaddrs[512] = { 0 } ; - - uid = derive_uid_from_ldap(ldserver, entry); - derive_fullname_from_ldap_result(fullname, fullname_size, ldserver, entry); - syslog(LOG_DEBUG, "ldap: display name: <%s> , uid = <%d>", fullname, uid); - - // now create or update the user - int found_user; - struct ctdluser usbuf; - - found_user = getuserbyuid(&usbuf, uid); - if (found_user != 0) { - create_user(fullname, CREATE_USER_DO_NOT_BECOME_USER, uid); - found_user = getuserbyuid(&usbuf, uid); - strcpy(fullname, usbuf.fullname); - } - - if (found_user == 0) { // user record exists - // now update the account email addresses if necessary - if (CtdlGetConfigInt("c_ldap_sync_email_addrs") > 0) { - if (extract_email_addresses_from_ldap(user_dn, new_emailaddrs) == 0) { - if (strcmp(usbuf.emailaddrs, new_emailaddrs)) { // update only if changed - CtdlSetEmailAddressesForUser(usbuf.fullname, new_emailaddrs); - } - } - } - } - ldap_memfree(user_dn); - } - } - - // free the results - ldap_msgfree(search_result); - - // unbind so we can go back in as the authenticating user - ldap_unbind(ldserver); -} diff --git a/citadel/locate_host.c b/citadel/locate_host.c deleted file mode 100644 index 64879eefb..000000000 --- a/citadel/locate_host.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Functions which handle hostname/address lookups and resolution - * - * Copyright (c) 1987-2019 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "context.h" -#ifdef HAVE_RESOLV_H -#include -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include -#endif -#include -#endif - -#include "domain.h" -#include "locate_host.h" - -/* START: some missing macros on OpenBSD 3.9 */ -#ifndef NS_CMPRSFLGS -#define NS_CMPRSFLGS 0xc0 -#endif -#if !defined(NS_MAXCDNAME) && defined (MAXCDNAME) -#define NS_MAXCDNAME MAXCDNAME -#endif -#if !defined(NS_INT16SZ) && defined(INT16SZ) -#define NS_INT16SZ INT16SZ -#define NS_INT32SZ INT32SZ -#endif -#ifndef NS_GET16 -# define NS_GET16 GETSHORT -#endif -/* END: some missing macros on OpenBSD 3.9 */ - - -/* - * Given an open client socket, return the host name and IP address at the other end. - * (IPv4 and IPv6 compatible) - */ -void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket) -{ - struct sockaddr_in6 clientaddr; - unsigned int addrlen = sizeof(clientaddr); - - tbuf[0] = 0; - abuf[0] = 0; - - getpeername(client_socket, (struct sockaddr *)&clientaddr, &addrlen); - getnameinfo((struct sockaddr *)&clientaddr, addrlen, tbuf, n, NULL, 0, 0); - getnameinfo((struct sockaddr *)&clientaddr, addrlen, abuf, na, NULL, 0, NI_NUMERICHOST); - - /* Convert IPv6-mapped IPv4 addresses back to traditional dotted quad. - * - * Other code here, such as the RBL check, will expect IPv4 addresses to be represented - * as dotted-quad, even if they come in over a hybrid IPv6/IPv4 socket. - */ - if ( (strlen(abuf) > 7) && (!strncasecmp(abuf, "::ffff:", 7)) ) { - if (!strcmp(abuf, tbuf)) strcpy(tbuf, &tbuf[7]); - strcpy(abuf, &abuf[7]); - } -} - - -/* - * RBL check written by Edward S. Marshall [http://rblcheck.sourceforge.net] - */ -#define RESULT_SIZE 4096 /* What is the longest result text we support? */ -int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { - int a, b, c; - char *result = NULL; - u_char fixedans[ PACKETSZ ]; - u_char *answer; - int need_to_free_answer = 0; - const u_char *cp; - u_char *rp; - const u_char *cend; - const u_char *rend; - int len; - char *p = NULL; - static int res_initted = 0; - - if (!res_initted) { /* only have to do this once */ - res_init(); - res_initted = 1; - } - - /* Make our DNS query. */ - answer = fixedans; - if (server_shutting_down) { - if (txtbuf != NULL) { - snprintf(txtbuf, txtbufsize, "System shutting down"); - } - return (1); - } - len = res_query(domain, C_IN, T_A, answer, PACKETSZ); - - /* Was there a problem? If so, the domain doesn't exist. */ - if (len == -1) { - if (txtbuf != NULL) { - strcpy(txtbuf, ""); - } - return(0); - } - - if (len > PACKETSZ) { - answer = malloc(len); - need_to_free_answer = 1; - len = res_query(domain, C_IN, T_A, answer, len); - if( len == -1 ) { - if (txtbuf != NULL) { - snprintf(txtbuf, txtbufsize, "Message rejected due to known spammer source IP address"); - } - if (need_to_free_answer) free(answer); - return(1); - } - } - if (server_shutting_down) { - if (txtbuf != NULL) { - snprintf(txtbuf, txtbufsize, "System shutting down"); - } - if (need_to_free_answer) free(answer); - return (1); - } - - result = (char *) malloc(RESULT_SIZE); - result[0] = '\0'; - - /* Make another DNS query for textual data; this shouldn't - * be a performance hit, since it'll now be cached at the - * nameserver we're using. - */ - len = res_query(domain, C_IN, T_TXT, answer, PACKETSZ); - if (server_shutting_down) { - if (txtbuf != NULL) { - snprintf(txtbuf, txtbufsize, "System shutting down"); - } - if (need_to_free_answer) free(answer); - free(result); - return (1); - } - - /* Just in case there's no TXT record... */ - if (len ==(-1)) { - if (txtbuf != NULL) { - snprintf(txtbuf, txtbufsize, "Message rejected due to known spammer source IP address"); - } - if (need_to_free_answer) free(answer); - free(result); - return(1); - } - - /* Skip the header and the address we queried. */ - cp = answer + sizeof( HEADER ); - while( *cp != '\0' ) { - a = *cp++; - while( a-- ) - cp++; - } - - /* This seems to be a bit of magic data that we need to - * skip. I wish there were good online documentation - * for programming for libresolv, so I'd know what I'm - * skipping here. Anyone reading this, feel free to - * enlighten me. - */ - cp += 1 + NS_INT16SZ + NS_INT32SZ; - - /* Skip the type, class and ttl. */ - cp += (NS_INT16SZ * 2) + NS_INT32SZ; - - /* Get the length and end of the buffer. */ - NS_GET16(c, cp); - cend = cp + c; - - /* Iterate over any multiple answers we might have. In - * this context, it's unlikely, but anyway. - */ - rp = (u_char *) result; - rend = (u_char *) result + RESULT_SIZE - 1; - while (cp < cend && rp < rend) { - a = *cp++; - if (a != 0) { - for (b = a; b > 0 && cp < cend && rp < rend; b--) { - if (*cp == '\n' || *cp == '"' || *cp == '\\') { - *rp++ = '\\'; - } - *rp++ = *cp++; - } - } - } - *rp = '\0'; - if (txtbuf != NULL) { - long len; - len = snprintf(txtbuf, txtbufsize, "%s", result); - - /* Remove nonprintable characters */ - for (p = txtbuf; *p != '\0'; p++) { - if (!isprint(*p)) { - memmove (p, - p + 1, - len - (p - txtbuf) - 1); - } - } - } - if (need_to_free_answer) free(answer); - free(result); - return(1); -} - - -/* - * Check to see if the client host is on some sort of spam list (RBL) - * If spammer, returns nonzero and places reason in 'message_to_spammer' - */ -int rbl_check(char *cs_addr, char *message_to_spammer) -{ - char tbuf[256] = ""; - int suffix_pos = 0; - int rbl; - int rc; - int num_rbl; - char rbl_domains[SIZ]; - char txt_answer[1024]; - struct timeval tx_start; - struct timeval tx_finish; - - rc = 0; - strcpy(message_to_spammer, "ok"); - gettimeofday(&tx_start, NULL); /* start a stopwatch for performance timing */ - - if ((strchr(cs_addr, '.')) && (!strchr(cs_addr, ':'))) { - int a1, a2, a3, a4; - - sscanf(cs_addr, "%d.%d.%d.%d", &a1, &a2, &a3, &a4); - snprintf(tbuf, sizeof tbuf, "%d.%d.%d.%d.", a4, a3, a2, a1); - suffix_pos = strlen(tbuf); - } - else if ((!strchr(cs_addr, '.')) && (strchr(cs_addr, ':'))) { - int num_colons = 0; - int i = 0; - char workbuf[sizeof tbuf]; - char *ptr; - - /* tedious code to expand and reverse an IPv6 address */ - safestrncpy(tbuf, cs_addr, sizeof tbuf); - num_colons = haschar(tbuf, ':'); - if ((num_colons < 2) || (num_colons > 7)) - goto finish_rbl; /* badly formed address */ - - /* expand the "::" shorthand */ - while (num_colons < 7) { - ptr = strstr(tbuf, "::"); - if (!ptr) - goto finish_rbl; /* badly formed address */ - - ++ptr; - strcpy(workbuf, ptr); - strcpy(ptr, ":"); - strcat(ptr, workbuf); - ++num_colons; - } - - /* expand to 32 hex characters with no colons */ - strcpy(workbuf, tbuf); - strcpy(tbuf, "00000000000000000000000000000000"); - for (i=0; i<8; ++i) { - char tokbuf[5]; - extract_token(tokbuf, workbuf, i, ':', sizeof tokbuf); - memcpy(&tbuf[ (i*4) + (4-strlen(tokbuf)) ], tokbuf, strlen(tokbuf) ); - } - if (strlen(tbuf) != 32) { - goto finish_rbl; - } - - /* now reverse it and add dots */ - strcpy(workbuf, tbuf); - for (i=0; i<32; ++i) { - tbuf[i*2] = workbuf[31-i]; - tbuf[(i*2)+1] = '.'; - } - tbuf[64] = 0; - suffix_pos = 64; - } - else { - goto finish_rbl; /* unknown address format */ - } - - /* See if we have any RBL domains configured */ - num_rbl = get_hosts(rbl_domains, "rbl"); - if (num_rbl < 1) - { - goto finish_rbl; - } - - /* Try all configured RBL's */ - for (rbl=0; rblh_addr_list[0]; - a1 = ((*i++) & 0xff); - a2 = ((*i++) & 0xff); - a3 = ((*i++) & 0xff); - a4 = ((*i++) & 0xff); - sprintf(addr, "%d.%d.%d.%d", a1, a2, a3, a4); - return(0); -} diff --git a/citadel/locate_host.h b/citadel/locate_host.h deleted file mode 100644 index 68f113f50..000000000 --- a/citadel/locate_host.h +++ /dev/null @@ -1,4 +0,0 @@ -void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket); -int rbl_check(char *cs_addr, char *message_to_spammer); -int hostname_to_dotted_quad(char *addr, char *host); -int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize); diff --git a/citadel/missing b/citadel/missing deleted file mode 100755 index 0a7fb5a2a..000000000 --- a/citadel/missing +++ /dev/null @@ -1,283 +0,0 @@ -#! /bin/sh -# Common stub for a few missing GNU programs while installing. -# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. -# Originally by Fran,cois Pinard , 1996. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 -fi - -run=: - -# In the cases where this matters, `missing' is being run in the -# srcdir already. -if test -f configure.ac; then - configure_ac=configure.ac -else - configure_ac=configure.in -fi - -case "$1" in ---run) - # Try to run requested program, and just exit if it succeeds. - run= - shift - "$@" && exit 0 - ;; -esac - -# If it does not exist, or fails to run (possibly an outdated version), -# try to emulate it. -case "$1" in - - -h|--h|--he|--hel|--help) - echo "\ -$0 [OPTION]... PROGRAM [ARGUMENT]... - -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an -error status if there is no known handling for PROGRAM. - -Options: - -h, --help display this help and exit - -v, --version output version information and exit - --run try to run the given command, and emulate it if it fails - -Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c - help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c - makeinfo touch the output file - tar try tar, gnutar, gtar, then tar without non-portable flags - yacc create \`y.tab.[ch]', if possible, from existing .[ch]" - ;; - - -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing 0.3 - GNU automake" - ;; - - -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 - ;; - - aclocal) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from - any GNU archive site." - touch aclocal.m4 - ;; - - autoconf) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU - archive site." - touch configure - ;; - - autoheader) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them - from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` - test -z "$files" && files="config.h" - touch_files= - for f in $files; do - case "$f" in - *:*) touch_files="$touch_files "`echo "$f" | - sed -e 's/^[^:]*://' -e 's/:.*//'`;; - *) touch_files="$touch_files $f.in";; - esac - done - touch $touch_files - ;; - - automake) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. - Grab them from any GNU archive site." - find . -type f -name Makefile.am -print | - sed 's/\.am$/.in/' | - while read f; do touch "$f"; done - ;; - - bison|yacc) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package - in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." - rm -f y.tab.c y.tab.h - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.y) - SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.c - fi - SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.h - fi - ;; - esac - fi - if [ ! -f y.tab.h ]; then - echo >y.tab.h - fi - if [ ! -f y.tab.c ]; then - echo 'main() { return 0; }' >y.tab.c - fi - ;; - - lex|flex) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package - in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." - rm -f lex.yy.c - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.l) - SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" lex.yy.c - fi - ;; - esac - fi - if [ ! -f lex.yy.c ]; then - echo 'main() { return 0; }' >lex.yy.c - fi - ;; - - help2man) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." - - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` - fi - if [ -f "$file" ]; then - touch $file - else - test -z "$file" || exec >$file - echo ".ab help2man is required to generate this page" - exit 1 - fi - ;; - - makeinfo) - if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then - # We have makeinfo, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file - indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` - file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` - fi - touch $file - ;; - - tar) - shift - if test -n "$run"; then - echo 1>&2 "ERROR: \`tar' requires --run" - exit 1 - fi - - # We have already tried tar in the generic part. - # Look for gnutar/gtar before invocation to avoid ugly error - # messages. - if (gnutar --version > /dev/null 2>&1); then - gnutar ${1+"$@"} && exit 0 - fi - if (gtar --version > /dev/null 2>&1); then - gtar ${1+"$@"} && exit 0 - fi - firstarg="$1" - if shift; then - case "$firstarg" in - *o*) - firstarg=`echo "$firstarg" | sed s/o//` - tar "$firstarg" ${1+"$@"} && exit 0 - ;; - esac - case "$firstarg" in - *h*) - firstarg=`echo "$firstarg" | sed s/h//` - tar "$firstarg" ${1+"$@"} && exit 0 - ;; - esac - fi - - echo 1>&2 "\ -WARNING: I can't seem to be able to run \`tar' with the given arguments. - You may want to install GNU tar or Free paxutils, or check the - command line arguments." - exit 1 - ;; - - *) - echo 1>&2 "\ -WARNING: \`$1' is needed, and you do not seem to have it handy on your - system. You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, - it often tells you about the needed prerequirements for installing - this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." - exit 1 - ;; -esac - -exit 0 diff --git a/citadel/modules/autocompletion/.gitignore b/citadel/modules/autocompletion/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/autocompletion/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/autocompletion/serv_autocompletion.c b/citadel/modules/autocompletion/serv_autocompletion.c deleted file mode 100644 index 0e39b6435..000000000 --- a/citadel/modules/autocompletion/serv_autocompletion.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Autocompletion of email recipients, etc. - * - * Copyright (c) 1987-2020 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - - -#include "ctdl_module.h" -#include "serv_autocompletion.h" -#include "config.h" - - -/* - * Convert a structured name into a friendly name. Caller must free the - * returned pointer. - */ -char *n_to_fn(char *value) { - char *nnn = NULL; - int i; - - nnn = malloc(strlen(value) + 10); - strcpy(nnn, ""); - extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999); - strcat(nnn, " "); - extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999); - strcat(nnn, " "); - extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999); - strcat(nnn, " "); - extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999); - strcat(nnn, " "); - extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999); - strcat(nnn, " "); - for (i=0; icm_fields[eMesageText]); - CM_Free(msg); - - /* - * Try to match from a friendly name (the "fn" field). If there is - * a match, return the entry in the form of: - * Display Name - */ - value = vcard_get_prop(v, "fn", 0, 0, 0); - if (value != NULL) if (bmstrcasestr(value, search_string)) { - value2 = vcard_get_prop(v, "email", 1, 0, 0); - if (value2 == NULL) value2 = ""; - cprintf("%s <%s>\n", value, value2); - vcard_free(v); - return; - } - - /* - * Try to match from a structured name (the "n" field). If there is - * a match, return the entry in the form of: - * Display Name - */ - value = vcard_get_prop(v, "n", 0, 0, 0); - if (value != NULL) if (bmstrcasestr(value, search_string)) { - - value2 = vcard_get_prop(v, "email", 1, 0, 0); - if (value2 == NULL) value2 = ""; - nnn = n_to_fn(value); - cprintf("%s <%s>\n", nnn, value2); - free(nnn); - vcard_free(v); - return; - } - - /* - * Try a partial match on all listed email addresses. - */ - i = 0; - while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) { - if (bmstrcasestr(value, search_string)) { - if (vcard_get_prop(v, "fn", 0, 0, 0)) { - cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value); - } - else if (vcard_get_prop(v, "n", 0, 0, 0)) { - nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0)); - cprintf("%s <%s>\n", nnn, value); - free(nnn); - - } - else { - cprintf("%s\n", value); - } - vcard_free(v); - return; - } - } - - vcard_free(v); -} - - - -/* - * Attempt to autocomplete an address based on a partial... - */ -void cmd_auto(char *argbuf) { - char hold_rm[ROOMNAMELEN]; - char search_string[256]; - long *msglist = NULL; - int num_msgs = 0; - long *fts_msgs = NULL; - int fts_num_msgs = 0; - struct cdbdata *cdbfr; - int r = 0; - int i = 0; - int j = 0; - int search_match = 0; - char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM }; - - if (CtdlAccessCheck(ac_logged_in)) return; - extract_token(search_string, argbuf, 0, '|', sizeof search_string); - if (IsEmptyStr(search_string)) { - cprintf("%d You supplied an empty partial.\n", - ERROR + ILLEGAL_VALUE); - return; - } - - strcpy(hold_rm, CC->room.QRname); /* save current room */ - cprintf("%d try these:\n", LISTING_FOLLOWS); - - /* - * Gather up message pointers in rooms containing vCards - */ - for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) { - if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) { - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); - if (cdbfr != NULL) { - msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1); - memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len); - num_msgs += (cdbfr->len / sizeof(long)); - cdb_free(cdbfr); - } - } - } - - /* - * Search-reduce the results if we have the full text index available - */ - if (CtdlGetConfigInt("c_enable_fulltext")) { - CtdlModuleDoSearch(&fts_num_msgs, &fts_msgs, search_string, "fulltext"); - if (fts_msgs) { - for (i=0; i 0) for (i=0; iroom.QRname, hold_rm)) { - CtdlGetRoom(&CC->room, hold_rm); /* return to saved room */ - } - - if (msglist) { - free(msglist); - } - -} - - -CTDL_MODULE_INIT(autocompletion) -{ - if (!threading) - { - CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion"); - } - /* return our module name for the log */ - return "autocompletion"; -} diff --git a/citadel/modules/autocompletion/serv_autocompletion.h b/citadel/modules/autocompletion/serv_autocompletion.h deleted file mode 100644 index 9ef0caff7..000000000 --- a/citadel/modules/autocompletion/serv_autocompletion.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 1987-2012 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -char *serv_autocompletion_init(void); diff --git a/citadel/modules/bio/.gitignore b/citadel/modules/bio/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/bio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/bio/serv_bio.c b/citadel/modules/bio/serv_bio.c deleted file mode 100644 index e6e1dd20b..000000000 --- a/citadel/modules/bio/serv_bio.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This module implementsserver commands related to the display and - * manipulation of user "bio" files. - * - * Copyright (c) 1987-2021 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "ctdl_module.h" -#include "config.h" - -#include -#include -#include - - -/* - * Command to enter user bio (profile) in plain text. - * This is deprecated , or at least it will be when its replacement is written :) - * I want commands to get/set bio in full MIME wonderfulness. - */ -void cmd_ebio(char *cmdbuf) { - char buf[SIZ]; - - unbuffer_output(); - - if (!(CC->logged_in)) { - cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); - return; - } - - StrBuf *NewProfile = NewStrBufPlain("Content-type: text/plain; charset=UTF-8\nContent-transfer-encoding: 8bit\n\n", -1); - - cprintf("%d Transmit user profile in plain text now.\n", SEND_LISTING); - while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) { - StrBufAppendBufPlain(NewProfile, buf, -1, 0); - StrBufAppendBufPlain(NewProfile, HKEY("\n"), 0); - } - - /* we have read the new profile from the user , now save it */ - long old_msgnum = CC->user.msgnum_bio; - char userconfigroomname[ROOMNAMELEN]; - CtdlMailboxName(userconfigroomname, sizeof userconfigroomname, &CC->user, USERCONFIGROOM); - long new_msgnum = quickie_message("Citadel", NULL, NULL, userconfigroomname, ChrPtr(NewProfile), FMT_RFC822, "Profile submitted with EBIO command"); - FreeStrBuf(&NewProfile); - CtdlGetUserLock(&CC->user, CC->curr_user); - CC->user.msgnum_bio = new_msgnum; - CtdlPutUserLock(&CC->user); - if (old_msgnum > 0) { - syslog(LOG_DEBUG, "Deleting old message %ld from %s", old_msgnum, userconfigroomname); - CtdlDeleteMessages(userconfigroomname, &old_msgnum, 1, ""); - } -} - - -/* - * Command to read user bio (profile) in plain text. - * This is deprecated , or at least it will be when its replacement is written :) - * I want commands to get/set bio in full MIME wonderfulness. - */ -void cmd_rbio(char *cmdbuf) -{ - struct ctdluser ruser; - char buf[SIZ]; - - extract_token(buf, cmdbuf, 0, '|', sizeof buf); - if (CtdlGetUser(&ruser, buf) != 0) { - cprintf("%d No such user.\n",ERROR + NO_SUCH_USER); - return; - } - - cprintf("%d OK|%s|%ld|%d|%ld|%ld|%ld\n", LISTING_FOLLOWS, - ruser.fullname, ruser.usernum, ruser.axlevel, - (long)ruser.lastcall, ruser.timescalled, ruser.posted); - - struct CtdlMessage *msg = CtdlFetchMessage(ruser.msgnum_bio, 1); - if (msg != NULL) { - CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0, 0); - CM_Free(msg); - } - cprintf("000\n"); -} - - -/* - * Import function called by import_old_bio_files() for a single user - */ -void import_one_bio_file(char *username, long usernum, char *path) -{ - syslog(LOG_DEBUG, "Import legacy bio for %s, usernum=%ld, filename=%s", username, usernum, path); - - FILE *fp = fopen(path, "r"); - if (!fp) return; - - fseek(fp, 0, SEEK_END); - long data_length = ftell(fp); - - if (data_length >= 1) { - rewind(fp); - char *unencoded_data = malloc(data_length); - if (unencoded_data) { - fread(unencoded_data, data_length, 1, fp); - char *encoded_data = malloc((data_length * 2) + 100); - if (encoded_data) { - sprintf(encoded_data, "Content-type: text/plain; charset=UTF-8\nContent-transfer-encoding: base64\n\n"); - CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1); - - char userconfigroomname[ROOMNAMELEN]; - struct ctdluser usbuf; - - if (CtdlGetUser(&usbuf, username) == 0) { // no need to lock it , we are still initializing - long old_msgnum = usbuf.msgnum_bio; - CtdlMailboxName(userconfigroomname, sizeof userconfigroomname, &usbuf, USERCONFIGROOM); - long new_msgnum = quickie_message("Citadel", NULL, NULL, userconfigroomname, encoded_data, FMT_RFC822, "Profile imported from bio"); - syslog(LOG_DEBUG, "Message %ld is now the profile for %s", new_msgnum, username); - usbuf.msgnum_bio = new_msgnum; - CtdlPutUser(&usbuf); - unlink(path); // delete the old file , it's in the database now - if (old_msgnum > 0) { - syslog(LOG_DEBUG, "Deleting old message %ld from %s", old_msgnum, userconfigroomname); - CtdlDeleteMessages(userconfigroomname, &old_msgnum, 1, ""); - } - } - free(encoded_data); - } - free(unencoded_data); - } - } - fclose(fp); -} - - -/* - * Look for old-format "bio" files and import them into the message base - */ -void import_old_bio_files(void) -{ - DIR *filedir = NULL; - struct dirent *filedir_entry; - size_t d_namelen; - struct ctdluser usbuf; - long usernum = 0; - int d_type = 0; - struct stat s; - char path[PATH_MAX]; - - - syslog(LOG_DEBUG, "Importing old style bio files into the message base"); - filedir = opendir("bio"); - if (filedir == NULL) { - return; - } - while ( (filedir_entry = readdir(filedir)) , (filedir_entry != NULL)) - { -#ifdef _DIRENT_HAVE_D_NAMLEN - d_namelen = filedir_entry->d_namlen; - -#else - d_namelen = strlen(filedir_entry->d_name); -#endif - -#ifdef _DIRENT_HAVE_D_TYPE - d_type = filedir_entry->d_type; -#else - -#ifndef DT_UNKNOWN -#define DT_UNKNOWN 0 -#define DT_DIR 4 -#define DT_REG 8 -#define DT_LNK 10 - -#define IFTODT(mode) (((mode) & 0170000) >> 12) -#define DTTOIF(dirtype) ((dirtype) << 12) -#endif - d_type = DT_UNKNOWN; -#endif - if ((d_namelen == 1) && - (filedir_entry->d_name[0] == '.')) - continue; - - if ((d_namelen == 2) && - (filedir_entry->d_name[0] == '.') && - (filedir_entry->d_name[1] == '.')) - continue; - - snprintf(path, PATH_MAX, "bio/%s", filedir_entry->d_name); - if (d_type == DT_UNKNOWN) { - if (lstat(path, &s) == 0) { - d_type = IFTODT(s.st_mode); - } - } - switch (d_type) - { - case DT_DIR: - break; - case DT_LNK: - case DT_REG: - usernum = atol(filedir_entry->d_name); - if (CtdlGetUserByNumber(&usbuf, usernum) == 0) { - import_one_bio_file(usbuf.fullname, usernum, path); - } - } - } - closedir(filedir); - rmdir("bio"); -} - - - -CTDL_MODULE_INIT(bio) -{ - if (!threading) - { - import_old_bio_files(); - CtdlRegisterProtoHook(cmd_ebio, "EBIO", "Enter your bio"); - CtdlRegisterProtoHook(cmd_rbio, "RBIO", "Read a user's bio"); - } - /* return our module name for the log */ - return "bio"; -} diff --git a/citadel/modules/blog/serv_blog.c b/citadel/modules/blog/serv_blog.c deleted file mode 100644 index 98bc938d3..000000000 --- a/citadel/modules/blog/serv_blog.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Support for blog rooms - * - * Copyright (c) 1999-2011 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "control.h" -#include "user_ops.h" -#include "database.h" -#include "msgbase.h" -#include "internet_addressing.h" -#include "serv_vcard.h" -#include "citadel_ldap.h" -#include "ctdl_module.h" - -/* - * Pre-save hook for saving a message in a blog room. - * (Do we want to only do this for top-level messages?) - */ -int blog_upload_beforesave(struct CtdlMessage *msg, struct recptypes *recp) { - - /* Only run this hook for blog rooms */ - if (CC->room.QRdefaultview != VIEW_BLOG) { - return(0); - } - - /* - * If the message doesn't have an EUID, give it one. - */ - if (CM_IsEmpty(msg, eExclusiveID)) - { - char uuid[SIZ]; - generate_uuid(uuid); - CM_SetField(msg, eExclusiveID, uuid, strlen(uuid)); - } - - /* - * We also want to define a maximum length, whether we generated it or not. - */ - CM_CutFieldAt(msg, eExclusiveID, BLOG_EUIDBUF_SIZE - 1); - - /* Now allow the save to complete. */ - return(0); -} - - -CTDL_MODULE_INIT(blog) -{ - if (!threading) - { - CtdlRegisterMessageHook(blog_upload_beforesave, EVT_BEFORESAVE); - } - - /* return our module id for the Log */ - return "blog"; -} diff --git a/citadel/modules/calendar/.gitignore b/citadel/modules/calendar/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/calendar/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/calendar/serv_calendar.c b/citadel/modules/calendar/serv_calendar.c deleted file mode 100644 index 0a9090d17..000000000 --- a/citadel/modules/calendar/serv_calendar.c +++ /dev/null @@ -1,2566 +0,0 @@ -/* - * This module implements iCalendar object processing and the Calendar> - * room on a Citadel server. It handles iCalendar objects using the - * iTIP protocol. See RFCs 2445 and 2446. - * - * Copyright (c) 1987-2021 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define PRODID "-//Citadel//NONSGML Citadel Calendar//EN" - -#include "ctdl_module.h" -#include -#include "msgbase.h" -#include "internet_addressing.h" -#include "serv_calendar.h" -#include "room_ops.h" -#include "euidindex.h" -#include "default_timezone.h" -#include "config.h" - -struct ical_respond_data { - char desired_partnum[SIZ]; - icalcomponent *cal; -}; - - -/* - * Utility function to create a new VCALENDAR component with some of the - * required fields already set the way we like them. - */ -icalcomponent *icalcomponent_new_citadel_vcalendar(void) { - icalcomponent *encaps; - - encaps = icalcomponent_new_vcalendar(); - if (encaps == NULL) { - syslog(LOG_ERR, "calendar: could not allocate component"); - return NULL; - } - - /* Set the Product ID */ - icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); - - /* Set the Version Number */ - icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); - - return(encaps); -} - - -/* - * Utility function to encapsulate a subcomponent into a full VCALENDAR - */ -icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) { - icalcomponent *encaps; - - /* If we're already looking at a full VCALENDAR component, - * don't bother ... just return itself. - */ - if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) { - return subcomp; - } - - /* Encapsulate the VEVENT component into a complete VCALENDAR */ - encaps = icalcomponent_new_citadel_vcalendar(); - if (encaps == NULL) return NULL; - - /* Encapsulate the subcomponent inside */ - icalcomponent_add_component(encaps, subcomp); - - /* Return the object we just created. */ - return(encaps); -} - - -/* - * Write a calendar object into the specified user's calendar room. - * If the supplied user is NULL, this function writes the calendar object - * to the currently selected room. - */ -void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { - char *ser = NULL; - long serlen; - icalcomponent *encaps = NULL; - struct CtdlMessage *msg = NULL; - icalcomponent *tmp=NULL; - - if (cal == NULL) return; - - /* If the supplied object is a subcomponent, encapsulate it in - * a full VCALENDAR component, and save that instead. - */ - if (icalcomponent_isa(cal) != ICAL_VCALENDAR_COMPONENT) { - tmp = icalcomponent_new_clone(cal); - encaps = ical_encapsulate_subcomponent(tmp); - ical_write_to_cal(u, encaps); - icalcomponent_free(tmp); - icalcomponent_free(encaps); - return; - } - - ser = icalcomponent_as_ical_string_r(cal); - if (ser == NULL) return; - - serlen = strlen(ser); - - /* If the caller supplied a user, write to that user's default calendar room */ - if (u) { - /* This handy API function does all the work for us. */ - CtdlWriteObject(USERCALENDARROOM, /* which room */ - "text/calendar", /* MIME type */ - ser, /* data */ - serlen + 1, /* length */ - u, /* which user */ - 0, /* not binary */ - 0 /* no flags */ - ); - } - - /* If the caller did not supply a user, write to the currently selected room */ - if (!u) { - struct CitContext *CCC = CC; - StrBuf *MsgBody; - - msg = malloc(sizeof(struct CtdlMessage)); - memset(msg, 0, sizeof(struct CtdlMessage)); - msg->cm_magic = CTDLMESSAGE_MAGIC; - msg->cm_anon_type = MES_NORMAL; - msg->cm_format_type = 4; - CM_SetField(msg, eAuthor, CCC->user.fullname, strlen(CCC->user.fullname)); - CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname)); - - MsgBody = NewStrBufPlain(NULL, serlen + 100); - StrBufAppendBufPlain(MsgBody, HKEY("Content-type: text/calendar\r\n\r\n"), 0); - StrBufAppendBufPlain(MsgBody, ser, serlen, 0); - - CM_SetAsFieldSB(msg, eMesageText, &MsgBody); - - /* Now write the data */ - CtdlSubmitMsg(msg, NULL, ""); - CM_Free(msg); - } - - /* In either case, now we can free the serialized calendar object */ - free(ser); -} - - -/* - * Send a reply to a meeting invitation. - * - * 'request' is the invitation to reply to. - * 'action' is the string "accept" or "decline" or "tentative". - * - */ -void ical_send_a_reply(icalcomponent *request, char *action) { - icalcomponent *the_reply = NULL; - icalcomponent *vevent = NULL; - icalproperty *attendee = NULL; - char attendee_string[SIZ]; - icalproperty *organizer = NULL; - char organizer_string[SIZ]; - icalproperty *summary = NULL; - char summary_string[SIZ]; - icalproperty *me_attend = NULL; - struct recptypes *recp = NULL; - icalparameter *partstat = NULL; - char *serialized_reply = NULL; - char *reply_message_text = NULL; - const char *ch; - struct CtdlMessage *msg = NULL; - struct recptypes *valid = NULL; - - *organizer_string = '\0'; - strcpy(summary_string, "Calendar item"); - - if (request == NULL) { - syslog(LOG_ERR, "calendar: trying to reply to NULL event"); - return; - } - - the_reply = icalcomponent_new_clone(request); - if (the_reply == NULL) { - syslog(LOG_ERR, "calendar: cannot clone request"); - return; - } - - /* Change the method from REQUEST to REPLY */ - icalcomponent_set_method(the_reply, ICAL_METHOD_REPLY); - - vevent = icalcomponent_get_first_component(the_reply, ICAL_VEVENT_COMPONENT); - if (vevent != NULL) { - /* Hunt for attendees, removing ones that aren't us. - * (Actually, remove them all, cloning our own one so we can - * re-insert it later) - */ - while (attendee = icalcomponent_get_first_property(vevent, - ICAL_ATTENDEE_PROPERTY), (attendee != NULL) - ) { - ch = icalproperty_get_attendee(attendee); - if ((ch != NULL) && !strncasecmp(ch, "MAILTO:", 7)) { - safestrncpy(attendee_string, ch + 7, sizeof (attendee_string)); - striplt(attendee_string); - recp = validate_recipients(attendee_string, NULL, 0); - if (recp != NULL) { - if (!strcasecmp(recp->recp_local, CC->user.fullname)) { - if (me_attend) icalproperty_free(me_attend); - me_attend = icalproperty_new_clone(attendee); - } - free_recipients(recp); - } - } - - /* Remove it... */ - icalcomponent_remove_property(vevent, attendee); - icalproperty_free(attendee); - } - - /* We found our own address in the attendee list. */ - if (me_attend) { - /* Change the partstat from NEEDS-ACTION to ACCEPT or DECLINE */ - icalproperty_remove_parameter_by_kind(me_attend, ICAL_PARTSTAT_PARAMETER); - - if (!strcasecmp(action, "accept")) { - partstat = icalparameter_new_partstat(ICAL_PARTSTAT_ACCEPTED); - } - else if (!strcasecmp(action, "decline")) { - partstat = icalparameter_new_partstat(ICAL_PARTSTAT_DECLINED); - } - else if (!strcasecmp(action, "tentative")) { - partstat = icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE); - } - - if (partstat) icalproperty_add_parameter(me_attend, partstat); - - /* Now insert it back into the vevent. */ - icalcomponent_add_property(vevent, me_attend); - } - - /* Figure out who to send this thing to */ - organizer = icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY); - if (organizer != NULL) { - if (icalproperty_get_organizer(organizer)) { - strcpy(organizer_string, - icalproperty_get_organizer(organizer) ); - } - } - if (!strncasecmp(organizer_string, "MAILTO:", 7)) { - strcpy(organizer_string, &organizer_string[7]); - striplt(organizer_string); - } else { - strcpy(organizer_string, ""); - } - - /* Extract the summary string -- we'll use it as the - * message subject for the reply - */ - summary = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY); - if (summary != NULL) { - if (icalproperty_get_summary(summary)) { - strcpy(summary_string, - icalproperty_get_summary(summary) ); - } - } - } - - /* Now generate the reply message and send it out. */ - serialized_reply = icalcomponent_as_ical_string_r(the_reply); - icalcomponent_free(the_reply); /* don't need this anymore */ - if (serialized_reply == NULL) return; - - reply_message_text = malloc(strlen(serialized_reply) + SIZ); - if (reply_message_text != NULL) { - sprintf(reply_message_text, - "Content-type: text/calendar; charset=\"utf-8\"\r\n\r\n%s\r\n", - serialized_reply - ); - - msg = CtdlMakeMessage(&CC->user, - organizer_string, /* to */ - "", /* cc */ - CC->room.QRname, 0, FMT_RFC822, - "", - "", - summary_string, /* Use summary for subject */ - NULL, - reply_message_text, - NULL); - - if (msg != NULL) { - valid = validate_recipients(organizer_string, NULL, 0); - CtdlSubmitMsg(msg, valid, ""); - CM_Free(msg); - free_recipients(valid); - } - } - free(serialized_reply); -} - - -/* - * Callback function for mime parser that hunts for calendar content types - * and turns them into calendar objects. If something is found, it is placed - * in ird->cal, and the caller now owns that memory and is responsible for freeing it. - */ -void ical_locate_part(char *name, char *filename, char *partnum, char *disp, - void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - char *cbid, void *cbuserdata) { - - struct ical_respond_data *ird = NULL; - - ird = (struct ical_respond_data *) cbuserdata; - - /* desired_partnum can be set to "_HUNT_" to have it just look for - * the first part with a content type of text/calendar. Otherwise - * we have to only process the right one. - */ - if (strcasecmp(ird->desired_partnum, "_HUNT_")) { - if (strcasecmp(partnum, ird->desired_partnum)) { - return; - } - } - - if ( (strcasecmp(cbtype, "text/calendar")) - && (strcasecmp(cbtype, "application/ics")) ) { - return; - } - - if (ird->cal != NULL) { - icalcomponent_free(ird->cal); - ird->cal = NULL; - } - - ird->cal = icalcomponent_new_from_string(content); -} - - -/* - * Respond to a meeting request. - */ -void ical_respond(long msgnum, char *partnum, char *action) { - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - - if ( - (strcasecmp(action, "accept")) - && (strcasecmp(action, "decline")) - ) { - cprintf("%d Action must be 'accept' or 'decline'\n", - ERROR + ILLEGAL_VALUE - ); - return; - } - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) { - cprintf("%d Message %ld not found.\n", - ERROR + ILLEGAL_VALUE, - (long)msgnum - ); - return; - } - - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, partnum); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - - /* We're done with the incoming message, because we now have a - * calendar object in memory. - */ - CM_Free(msg); - - /* - * Here is the real meat of this function. Handle the event. - */ - if (ird.cal != NULL) { - /* Save this in the user's calendar if necessary */ - if (!strcasecmp(action, "accept")) { - ical_write_to_cal(&CC->user, ird.cal); - } - - /* Send a reply if necessary */ - if (icalcomponent_get_method(ird.cal) == ICAL_METHOD_REQUEST) { - ical_send_a_reply(ird.cal, action); - } - - /* We used to delete the invitation after handling it. - * We don't do that anymore, but here is the code that handled it: - * CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, ""); - */ - - /* Free the memory we allocated and return a response. */ - icalcomponent_free(ird.cal); - ird.cal = NULL; - cprintf("%d ok\n", CIT_OK); - return; - } - else { - cprintf("%d No calendar object found\n", ERROR + ROOM_NOT_FOUND); - return; - } - - /* should never get here */ -} - - -/* - * Figure out the UID of the calendar event being referred to in a - * REPLY object. This function is recursive. - */ -void ical_learn_uid_of_reply(char *uidbuf, icalcomponent *cal) { - icalcomponent *subcomponent; - icalproperty *p; - - /* If this object is a REPLY, then extract the UID. */ - if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) { - p = icalcomponent_get_first_property(cal, ICAL_UID_PROPERTY); - if (p != NULL) { - strcpy(uidbuf, icalproperty_get_comment(p)); - } - } - - /* Otherwise, recurse through any VEVENT subcomponents. We do NOT want the - * UID of the reply; we want the UID of the invitation being replied to. - */ - for (subcomponent = icalcomponent_get_first_component(cal, ICAL_VEVENT_COMPONENT); - subcomponent != NULL; - subcomponent = icalcomponent_get_next_component(cal, ICAL_VEVENT_COMPONENT) ) { - ical_learn_uid_of_reply(uidbuf, subcomponent); - } -} - - -/* - * ical_update_my_calendar_with_reply() refers to this callback function; when we - * locate the message containing the calendar event we're replying to, this function - * gets called. It basically just sticks the message number in a supplied buffer. - */ -void ical_hunt_for_event_to_update(long msgnum, void *data) { - long *msgnumptr; - - msgnumptr = (long *) data; - *msgnumptr = msgnum; -} - - -struct original_event_container { - icalcomponent *c; -}; - -/* - * Callback function for mime parser that hunts for calendar content types - * and turns them into calendar objects (called by ical_update_my_calendar_with_reply() - * to fetch the object being updated) - */ -void ical_locate_original_event(char *name, char *filename, char *partnum, char *disp, - void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - char *cbid, void *cbuserdata) { - - struct original_event_container *oec = NULL; - - if ( (strcasecmp(cbtype, "text/calendar")) - && (strcasecmp(cbtype, "application/ics")) ) { - return; - } - oec = (struct original_event_container *) cbuserdata; - if (oec->c != NULL) { - icalcomponent_free(oec->c); - } - oec->c = icalcomponent_new_from_string(content); -} - - -/* - * Merge updated attendee information from a REPLY into an existing event. - */ -void ical_merge_attendee_reply(icalcomponent *event, icalcomponent *reply) { - icalcomponent *c; - icalproperty *e_attendee, *r_attendee; - - /* First things first. If we're not looking at a VEVENT component, - * recurse through subcomponents until we find one. - */ - if (icalcomponent_isa(event) != ICAL_VEVENT_COMPONENT) { - for (c = icalcomponent_get_first_component(event, ICAL_VEVENT_COMPONENT); - c != NULL; - c = icalcomponent_get_next_component(event, ICAL_VEVENT_COMPONENT) ) { - ical_merge_attendee_reply(c, reply); - } - return; - } - - /* Now do the same thing with the reply. - */ - if (icalcomponent_isa(reply) != ICAL_VEVENT_COMPONENT) { - for (c = icalcomponent_get_first_component(reply, ICAL_VEVENT_COMPONENT); - c != NULL; - c = icalcomponent_get_next_component(reply, ICAL_VEVENT_COMPONENT) ) { - ical_merge_attendee_reply(event, c); - } - return; - } - - /* Clone the reply, because we're going to rip its guts out. */ - reply = icalcomponent_new_clone(reply); - - /* At this point we're looking at the correct subcomponents. - * Iterate through the attendees looking for a match. - */ -STARTOVER: - for (e_attendee = icalcomponent_get_first_property(event, ICAL_ATTENDEE_PROPERTY); - e_attendee != NULL; - e_attendee = icalcomponent_get_next_property(event, ICAL_ATTENDEE_PROPERTY)) { - - for (r_attendee = icalcomponent_get_first_property(reply, ICAL_ATTENDEE_PROPERTY); - r_attendee != NULL; - r_attendee = icalcomponent_get_next_property(reply, ICAL_ATTENDEE_PROPERTY)) { - - /* Check to see if these two attendees match... - */ - const char *e, *r; - e = icalproperty_get_attendee(e_attendee); - r = icalproperty_get_attendee(r_attendee); - - if ((e != NULL) && - (r != NULL) && - !strcasecmp(e, r)) { - /* ...and if they do, remove the attendee from the event - * and replace it with the attendee from the reply. (The - * reply's copy will have the same address, but an updated - * status.) - */ - icalcomponent_remove_property(event, e_attendee); - icalproperty_free(e_attendee); - icalcomponent_remove_property(reply, r_attendee); - icalcomponent_add_property(event, r_attendee); - - /* Since we diddled both sets of attendees, we have to start - * the iteration over again. This will not create an infinite - * loop because we removed the attendee from the reply. (That's - * why we cloned the reply, and that's what we mean by "ripping - * its guts out.") - */ - goto STARTOVER; - } - - } - } - - /* Free the *clone* of the reply. */ - icalcomponent_free(reply); -} - - -/* - * Handle an incoming RSVP (object with method==ICAL_METHOD_REPLY) for a - * calendar event. The object has already been deserialized for us; all - * we have to do here is hunt for the event in our calendar, merge in the - * updated attendee status, and save it again. - * - * This function returns 0 on success, 1 if the event was not found in the - * user's calendar, or 2 if an internal error occurred. - */ -int ical_update_my_calendar_with_reply(icalcomponent *cal) { - char uid[SIZ]; - char hold_rm[ROOMNAMELEN]; - long msgnum_being_replaced = 0; - struct CtdlMessage *msg = NULL; - struct original_event_container oec; - icalcomponent *original_event; - char *serialized_event = NULL; - char roomname[ROOMNAMELEN]; - char *message_text = NULL; - - /* Figure out just what event it is we're dealing with */ - strcpy(uid, "--==<< InVaLiD uId >>==--"); - ical_learn_uid_of_reply(uid, cal); - syslog(LOG_DEBUG, "calendar: UID of event being replied to is <%s>", uid); - - strcpy(hold_rm, CC->room.QRname); /* save current room */ - - if (CtdlGetRoom(&CC->room, USERCALENDARROOM) != 0) { - CtdlGetRoom(&CC->room, hold_rm); - syslog(LOG_ERR, "calendar: cannot get user calendar room"); - return(2); - } - - /* - * Look in the EUID index for a message with - * the Citadel EUID set to the value we're looking for. Since - * Citadel always sets the message EUID to the iCalendar UID of - * the event, this will work. - */ - msgnum_being_replaced = CtdlLocateMessageByEuid(uid, &CC->room); - - CtdlGetRoom(&CC->room, hold_rm); /* return to saved room */ - - syslog(LOG_DEBUG, "calendar: msgnum_being_replaced == %ld", msgnum_being_replaced); - if (msgnum_being_replaced == 0) { - return(1); /* no calendar event found */ - } - - /* Now we know the ID of the message containing the event being updated. - * We don't actually have to delete it; that'll get taken care of by the - * server when we save another event with the same UID. This just gives - * us the ability to load the event into memory so we can diddle the - * attendees. - */ - msg = CtdlFetchMessage(msgnum_being_replaced, 1); - if (msg == NULL) { - return(2); /* internal error */ - } - oec.c = NULL; - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_original_event, /* callback function */ - NULL, NULL, - &oec, /* user data */ - 0 - ); - CM_Free(msg); - - original_event = oec.c; - if (original_event == NULL) { - syslog(LOG_ERR, "calendar: original_component is NULL"); - return(2); - } - - /* Merge the attendee's updated status into the event */ - ical_merge_attendee_reply(original_event, cal); - - /* Serialize it */ - serialized_event = icalcomponent_as_ical_string_r(original_event); - icalcomponent_free(original_event); /* Don't need this anymore. */ - if (serialized_event == NULL) return(2); - - CtdlMailboxName(roomname, sizeof roomname, &CC->user, USERCALENDARROOM); - - message_text = malloc(strlen(serialized_event) + SIZ); - if (message_text != NULL) { - sprintf(message_text, - "Content-type: text/calendar; charset=\"utf-8\"\r\n\r\n%s\r\n", - serialized_event - ); - - msg = CtdlMakeMessage(&CC->user, - "", /* No recipient */ - "", /* No recipient */ - roomname, - 0, FMT_RFC822, - "", - "", - "", /* no subject */ - NULL, - message_text, - NULL); - - if (msg != NULL) { - CIT_ICAL->avoid_sending_invitations = 1; - CtdlSubmitMsg(msg, NULL, roomname); - CM_Free(msg); - CIT_ICAL->avoid_sending_invitations = 0; - } - } - free(serialized_event); - return(0); -} - - -/* - * Handle an incoming RSVP for an event. (This is the server subcommand part; it - * simply extracts the calendar object from the message, deserializes it, and - * passes it up to ical_update_my_calendar_with_reply() for processing. - */ -void ical_handle_rsvp(long msgnum, char *partnum, char *action) { - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - int ret; - - if ( - (strcasecmp(action, "update")) - && (strcasecmp(action, "ignore")) - ) { - cprintf("%d Action must be 'update' or 'ignore'\n", - ERROR + ILLEGAL_VALUE - ); - return; - } - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) { - cprintf("%d Message %ld not found.\n", - ERROR + ILLEGAL_VALUE, - (long)msgnum - ); - return; - } - - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, partnum); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - - /* We're done with the incoming message, because we now have a - * calendar object in memory. - */ - CM_Free(msg); - - /* - * Here is the real meat of this function. Handle the event. - */ - if (ird.cal != NULL) { - /* Update the user's calendar if necessary */ - if (!strcasecmp(action, "update")) { - ret = ical_update_my_calendar_with_reply(ird.cal); - if (ret == 0) { - cprintf("%d Your calendar has been updated with this reply.\n", - CIT_OK); - } - else if (ret == 1) { - cprintf("%d This event does not exist in your calendar.\n", - ERROR + FILE_NOT_FOUND); - } - else { - cprintf("%d An internal error occurred.\n", - ERROR + INTERNAL_ERROR); - } - } - else { - cprintf("%d This reply has been ignored.\n", CIT_OK); - } - - /* Now that we've processed this message, we don't need it - * anymore. So delete it. (Don't do this anymore.) - CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, ""); - */ - - /* Free the memory we allocated and return a response. */ - icalcomponent_free(ird.cal); - ird.cal = NULL; - return; - } - else { - cprintf("%d No calendar object found\n", ERROR + ROOM_NOT_FOUND); - return; - } - - /* should never get here */ -} - - -/* - * Search for a property in both the top level and in a VEVENT subcomponent - */ -icalproperty *ical_ctdl_get_subprop( - icalcomponent *cal, - icalproperty_kind which_prop -) { - icalproperty *p; - icalcomponent *c; - - p = icalcomponent_get_first_property(cal, which_prop); - if (p == NULL) { - c = icalcomponent_get_first_component(cal, - ICAL_VEVENT_COMPONENT); - if (c != NULL) { - p = icalcomponent_get_first_property(c, which_prop); - } - } - return p; -} - - -/* - * Check to see if two events overlap. Returns nonzero if they do. - * (This function is used in both Citadel and WebCit. If you change it in - * one place, change it in the other. Better yet, put it in a library.) - */ -int ical_ctdl_is_overlap( - struct icaltimetype t1start, - struct icaltimetype t1end, - struct icaltimetype t2start, - struct icaltimetype t2end -) { - if (icaltime_is_null_time(t1start)) return(0); - if (icaltime_is_null_time(t2start)) return(0); - - /* if either event lacks end time, assume end = start */ - if (icaltime_is_null_time(t1end)) - memcpy(&t1end, &t1start, sizeof(struct icaltimetype)); - else { - if (t1end.is_date && icaltime_compare(t1start, t1end)) { - /* - * the end date is non-inclusive so adjust it by one - * day because our test is inclusive, note that a day is - * not too much because we are talking about all day - * events - * if start = end we assume that nevertheless the whole - * day is meant - */ - icaltime_adjust(&t1end, -1, 0, 0, 0); - } - } - - if (icaltime_is_null_time(t2end)) - memcpy(&t2end, &t2start, sizeof(struct icaltimetype)); - else { - if (t2end.is_date && icaltime_compare(t2start, t2end)) { - icaltime_adjust(&t2end, -1, 0, 0, 0); - } - } - - /* First, check for all-day events */ - if (t1start.is_date || t2start.is_date) { - /* If event 1 ends before event 2 starts, we're in the clear. */ - if (icaltime_compare_date_only(t1end, t2start) < 0) return(0); - - /* If event 2 ends before event 1 starts, we're also ok. */ - if (icaltime_compare_date_only(t2end, t1start) < 0) return(0); - - return(1); - } - - /* syslog(LOG_DEBUG, "Comparing t1start %d:%d t1end %d:%d t2start %d:%d t2end %d:%d", - t1start.hour, t1start.minute, t1end.hour, t1end.minute, - t2start.hour, t2start.minute, t2end.hour, t2end.minute); - */ - - /* Now check for overlaps using date *and* time. */ - - /* If event 1 ends before event 2 starts, we're in the clear. */ - if (icaltime_compare(t1end, t2start) <= 0) return(0); - /* syslog(LOG_DEBUG, "calendar: first passed"); */ - - /* If event 2 ends before event 1 starts, we're also ok. */ - if (icaltime_compare(t2end, t1start) <= 0) return(0); - /* syslog(LOG_DEBUG, "calendar: second passed"); */ - - /* Otherwise, they overlap. */ - return(1); -} - - -/* - * Phase 6 of "hunt for conflicts" - * called by ical_conflicts_phase5() - * - * Now both the proposed and existing events have been boiled down to start and end times. - * Check for overlap and output any conflicts. - * - * Returns nonzero if a conflict was reported. This allows the caller to stop iterating. - */ -int ical_conflicts_phase6(struct icaltimetype t1start, - struct icaltimetype t1end, - struct icaltimetype t2start, - struct icaltimetype t2end, - long existing_msgnum, - char *conflict_event_uid, - char *conflict_event_summary, - char *compare_uid) -{ - int conflict_reported = 0; - - /* debugging cruft * - time_t tt; - tt = icaltime_as_timet_with_zone(t1start, t1start.zone); - syslog(LOG_DEBUG, "PROPOSED START: %s", ctime(&tt)); - tt = icaltime_as_timet_with_zone(t1end, t1end.zone); - syslog(LOG_DEBUG, " PROPOSED END: %s", ctime(&tt)); - tt = icaltime_as_timet_with_zone(t2start, t2start.zone); - syslog(LOG_DEBUG, "EXISTING START: %s", ctime(&tt)); - tt = icaltime_as_timet_with_zone(t2end, t2end.zone); - syslog(LOG_DEBUG, " EXISTING END: %s", ctime(&tt)); - * debugging cruft */ - - /* compare and output */ - - if (ical_ctdl_is_overlap(t1start, t1end, t2start, t2end)) { - cprintf("%ld||%s|%s|%d|\n", - existing_msgnum, - conflict_event_uid, - conflict_event_summary, - ( (!IsEmptyStr(compare_uid) - &&(!strcasecmp(compare_uid, - conflict_event_uid))) ? 1 : 0 - ) - ); - conflict_reported = 1; - } - - return(conflict_reported); -} - - -/* - * Phase 5 of "hunt for conflicts" - * Called by ical_conflicts_phase4() - * - * We have the proposed event boiled down to start and end times. - * Now check it against an existing event. - */ -void ical_conflicts_phase5(struct icaltimetype t1start, - struct icaltimetype t1end, - icalcomponent *existing_event, - long existing_msgnum, - char *compare_uid) -{ - char conflict_event_uid[SIZ]; - char conflict_event_summary[SIZ]; - struct icaltimetype t2start, t2end; - icalproperty *p; - - /* recur variables */ - icalproperty *rrule = NULL; - struct icalrecurrencetype recur; - icalrecur_iterator *ritr = NULL; - struct icaldurationtype dur; - int num_recur = 0; - - /* initialization */ - strcpy(conflict_event_uid, ""); - strcpy(conflict_event_summary, ""); - t2start = icaltime_null_time(); - t2end = icaltime_null_time(); - - /* existing event stuff */ - p = ical_ctdl_get_subprop(existing_event, ICAL_DTSTART_PROPERTY); - if (p == NULL) return; - if (p != NULL) t2start = icalproperty_get_dtstart(p); - if (icaltime_is_utc(t2start)) { - t2start.zone = icaltimezone_get_utc_timezone(); - } - else { - t2start.zone = icalcomponent_get_timezone(existing_event, - icalparameter_get_tzid( - icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) - ) - ); - if (!t2start.zone) { - t2start.zone = get_default_icaltimezone(); - } - } - - p = ical_ctdl_get_subprop(existing_event, ICAL_DTEND_PROPERTY); - if (p != NULL) { - t2end = icalproperty_get_dtend(p); - - if (icaltime_is_utc(t2end)) { - t2end.zone = icaltimezone_get_utc_timezone(); - } - else { - t2end.zone = icalcomponent_get_timezone(existing_event, - icalparameter_get_tzid( - icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) - ) - ); - if (!t2end.zone) { - t2end.zone = get_default_icaltimezone(); - } - } - dur = icaltime_subtract(t2end, t2start); - } - else { - memset (&dur, 0, sizeof(struct icaldurationtype)); - } - - rrule = ical_ctdl_get_subprop(existing_event, ICAL_RRULE_PROPERTY); - if (rrule) { - recur = icalproperty_get_rrule(rrule); - ritr = icalrecur_iterator_new(recur, t2start); - } - - do { - p = ical_ctdl_get_subprop(existing_event, ICAL_UID_PROPERTY); - if (p != NULL) { - strcpy(conflict_event_uid, icalproperty_get_comment(p)); - } - - p = ical_ctdl_get_subprop(existing_event, ICAL_SUMMARY_PROPERTY); - if (p != NULL) { - strcpy(conflict_event_summary, icalproperty_get_comment(p)); - } - - if (ical_conflicts_phase6(t1start, t1end, t2start, t2end, - existing_msgnum, conflict_event_uid, conflict_event_summary, compare_uid)) - { - num_recur = MAX_RECUR + 1; /* force it out of scope, no need to continue */ - } - - if (rrule) { - t2start = icalrecur_iterator_next(ritr); - if (!icaltime_is_null_time(t2end)) { - const icaltimezone *hold_zone = t2end.zone; - t2end = icaltime_add(t2start, dur); - t2end.zone = hold_zone; - } - ++num_recur; - } - - if (icaltime_compare(t2start, t1end) < 0) { - num_recur = MAX_RECUR + 1; /* force it out of scope */ - } - - } while ( (rrule) && (!icaltime_is_null_time(t2start)) && (num_recur < MAX_RECUR) ); - icalrecur_iterator_free(ritr); -} - - -/* - * Phase 4 of "hunt for conflicts" - * Called by ical_hunt_for_conflicts_backend() - * - * At this point we've got it boiled down to two icalcomponent events in memory. - * If they conflict, output something to the client. - */ -void ical_conflicts_phase4(icalcomponent *proposed_event, - icalcomponent *existing_event, - long existing_msgnum) -{ - struct icaltimetype t1start, t1end; - icalproperty *p; - char compare_uid[SIZ]; - - /* recur variables */ - icalproperty *rrule = NULL; - struct icalrecurrencetype recur; - icalrecur_iterator *ritr = NULL; - struct icaldurationtype dur; - int num_recur = 0; - - /* initialization */ - t1end = icaltime_null_time(); - *compare_uid = '\0'; - - /* proposed event stuff */ - - p = ical_ctdl_get_subprop(proposed_event, ICAL_DTSTART_PROPERTY); - if (p == NULL) - return; - else - t1start = icalproperty_get_dtstart(p); - - if (icaltime_is_utc(t1start)) { - t1start.zone = icaltimezone_get_utc_timezone(); - } - else { - t1start.zone = icalcomponent_get_timezone(proposed_event, - icalparameter_get_tzid( - icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) - ) - ); - if (!t1start.zone) { - t1start.zone = get_default_icaltimezone(); - } - } - - p = ical_ctdl_get_subprop(proposed_event, ICAL_DTEND_PROPERTY); - if (p != NULL) { - t1end = icalproperty_get_dtend(p); - - if (icaltime_is_utc(t1end)) { - t1end.zone = icaltimezone_get_utc_timezone(); - } - else { - t1end.zone = icalcomponent_get_timezone(proposed_event, - icalparameter_get_tzid( - icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) - ) - ); - if (!t1end.zone) { - t1end.zone = get_default_icaltimezone(); - } - } - - dur = icaltime_subtract(t1end, t1start); - } - else { - memset (&dur, 0, sizeof(struct icaldurationtype)); - } - - rrule = ical_ctdl_get_subprop(proposed_event, ICAL_RRULE_PROPERTY); - if (rrule) { - recur = icalproperty_get_rrule(rrule); - ritr = icalrecur_iterator_new(recur, t1start); - } - - p = ical_ctdl_get_subprop(proposed_event, ICAL_UID_PROPERTY); - if (p != NULL) { - strcpy(compare_uid, icalproperty_get_comment(p)); - } - - do { - ical_conflicts_phase5(t1start, t1end, existing_event, existing_msgnum, compare_uid); - - if (rrule) { - t1start = icalrecur_iterator_next(ritr); - if (!icaltime_is_null_time(t1end)) { - const icaltimezone *hold_zone = t1end.zone; - t1end = icaltime_add(t1start, dur); - t1end.zone = hold_zone; - } - ++num_recur; - } - - } while ( (rrule) && (!icaltime_is_null_time(t1start)) && (num_recur < MAX_RECUR) ); - icalrecur_iterator_free(ritr); -} - - -/* - * Phase 3 of "hunt for conflicts" - * Called by ical_hunt_for_conflicts() - */ -void ical_hunt_for_conflicts_backend(long msgnum, void *data) { - icalcomponent *proposed_event; - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - - proposed_event = (icalcomponent *)data; - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) return; - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, "_HUNT_"); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - CM_Free(msg); - - if (ird.cal == NULL) return; - - ical_conflicts_phase4(proposed_event, ird.cal, msgnum); - icalcomponent_free(ird.cal); -} - - -/* - * Phase 2 of "hunt for conflicts" operation. - * At this point we have a calendar object which represents the VEVENT that - * is proposed for addition to the calendar. Now hunt through the user's - * calendar room, and output zero or more existing VEVENTs which conflict - * with this one. - */ -void ical_hunt_for_conflicts(icalcomponent *cal) { - char hold_rm[ROOMNAMELEN]; - - strcpy(hold_rm, CC->room.QRname); /* save current room */ - - if (CtdlGetRoom(&CC->room, USERCALENDARROOM) != 0) { - CtdlGetRoom(&CC->room, hold_rm); - cprintf("%d You do not have a calendar.\n", ERROR + ROOM_NOT_FOUND); - return; - } - - cprintf("%d Conflicting events:\n", LISTING_FOLLOWS); - - CtdlForEachMessage(MSGS_ALL, 0, NULL, - NULL, - NULL, - ical_hunt_for_conflicts_backend, - (void *) cal - ); - - cprintf("000\n"); - CtdlGetRoom(&CC->room, hold_rm); /* return to saved room */ - -} - - -/* - * Hunt for conflicts (Phase 1 -- retrieve the object and call Phase 2) - */ -void ical_conflicts(long msgnum, char *partnum) { - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) { - cprintf("%d Message %ld not found\n", - ERROR + ILLEGAL_VALUE, - (long)msgnum - ); - return; - } - - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, partnum); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - - CM_Free(msg); - - if (ird.cal != NULL) { - ical_hunt_for_conflicts(ird.cal); - icalcomponent_free(ird.cal); - return; - } - - cprintf("%d No calendar object found\n", ERROR + ROOM_NOT_FOUND); -} - - -/* - * Look for busy time in a VEVENT and add it to the supplied VFREEBUSY. - * - * fb The VFREEBUSY component to which we are appending - * top_level_cal The top-level VCALENDAR component which contains a VEVENT to be added - */ -void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *top_level_cal) { - icalcomponent *cal; - icalproperty *p; - icalvalue *v; - struct icalperiodtype this_event_period = icalperiodtype_null_period(); - icaltimetype dtstart; - icaltimetype dtend; - - /* recur variables */ - icalproperty *rrule = NULL; - struct icalrecurrencetype recur; - icalrecur_iterator *ritr = NULL; - struct icaldurationtype dur; - int num_recur = 0; - - if (!top_level_cal) return; - - /* Find the VEVENT component containing an event */ - cal = icalcomponent_get_first_component(top_level_cal, ICAL_VEVENT_COMPONENT); - if (!cal) return; - - /* If this event is not opaque, the user isn't publishing it as - * busy time, so don't bother doing anything else. - */ - p = icalcomponent_get_first_property(cal, ICAL_TRANSP_PROPERTY); - if (p != NULL) { - v = icalproperty_get_value(p); - if (v != NULL) { - if (icalvalue_get_transp(v) != ICAL_TRANSP_OPAQUE) { - return; - } - } - } - - /* - * Now begin calculating the event start and end times. - */ - p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY); - if (!p) return; - dtstart = icalproperty_get_dtstart(p); - - if (icaltime_is_utc(dtstart)) { - dtstart.zone = icaltimezone_get_utc_timezone(); - } - else { - dtstart.zone = icalcomponent_get_timezone(top_level_cal, - icalparameter_get_tzid( - icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) - ) - ); - if (!dtstart.zone) { - dtstart.zone = get_default_icaltimezone(); - } - } - - dtend = icalcomponent_get_dtend(cal); - if (!icaltime_is_null_time(dtend)) { - dur = icaltime_subtract(dtend, dtstart); - } - else { - memset (&dur, 0, sizeof(struct icaldurationtype)); - } - - /* Is a recurrence specified? If so, get ready to process it... */ - rrule = ical_ctdl_get_subprop(cal, ICAL_RRULE_PROPERTY); - if (rrule) { - recur = icalproperty_get_rrule(rrule); - ritr = icalrecur_iterator_new(recur, dtstart); - } - - do { - /* Convert the DTSTART and DTEND properties to an icalperiod. */ - this_event_period.start = dtstart; - - if (!icaltime_is_null_time(dtend)) { - this_event_period.end = dtend; - } - - /* Convert the timestamps to UTC. It's ok to do this because we've already expanded - * recurrences and this data is never going to get used again. - */ - this_event_period.start = icaltime_convert_to_zone( - this_event_period.start, - icaltimezone_get_utc_timezone() - ); - this_event_period.end = icaltime_convert_to_zone( - this_event_period.end, - icaltimezone_get_utc_timezone() - ); - - /* Now add it. */ - icalcomponent_add_property(fb, icalproperty_new_freebusy(this_event_period)); - - /* Make sure the DTSTART property of the freebusy *list* is set to - * the DTSTART property of the *earliest event*. - */ - p = icalcomponent_get_first_property(fb, ICAL_DTSTART_PROPERTY); - if (p == NULL) { - icalcomponent_set_dtstart(fb, this_event_period.start); - } - else { - if (icaltime_compare(this_event_period.start, icalcomponent_get_dtstart(fb)) < 0) { - icalcomponent_set_dtstart(fb, this_event_period.start); - } - } - - /* Make sure the DTEND property of the freebusy *list* is set to - * the DTEND property of the *latest event*. - */ - p = icalcomponent_get_first_property(fb, ICAL_DTEND_PROPERTY); - if (p == NULL) { - icalcomponent_set_dtend(fb, this_event_period.end); - } - else { - if (icaltime_compare(this_event_period.end, icalcomponent_get_dtend(fb)) > 0) { - icalcomponent_set_dtend(fb, this_event_period.end); - } - } - - if (rrule) { - dtstart = icalrecur_iterator_next(ritr); - if (!icaltime_is_null_time(dtend)) { - dtend = icaltime_add(dtstart, dur); - dtend.zone = dtstart.zone; - } - ++num_recur; - } - - } while ( (rrule) && (!icaltime_is_null_time(dtstart)) && (num_recur < MAX_RECUR) ) ; - icalrecur_iterator_free(ritr); -} - - -/* - * Backend for ical_freebusy() - * - * This function simply loads the messages in the user's calendar room, - * which contain VEVENTs, then strips them of all non-freebusy data, and - * adds them to the supplied VCALENDAR. - * - */ -void ical_freebusy_backend(long msgnum, void *data) { - icalcomponent *fb; - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - - fb = (icalcomponent *)data; /* User-supplied data will be the VFREEBUSY component */ - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) return; - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, "_HUNT_"); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - CM_Free(msg); - - if (ird.cal) { - ical_add_to_freebusy(fb, ird.cal); /* Add VEVENT times to VFREEBUSY */ - icalcomponent_free(ird.cal); - } -} - - -/* - * Grab another user's free/busy times - */ -void ical_freebusy(char *who) { - struct ctdluser usbuf; - char calendar_room_name[ROOMNAMELEN]; - char hold_rm[ROOMNAMELEN]; - char *serialized_request = NULL; - icalcomponent *encaps = NULL; - icalcomponent *fb = NULL; - int found_user = (-1); - struct recptypes *recp = NULL; - char buf[256]; - char host[256]; - char type[256]; - int i = 0; - int config_lines = 0; - - /* First try an exact match. */ - found_user = CtdlGetUser(&usbuf, who); - - /* If not found, try it as an unqualified email address. */ - if (found_user != 0) { - strcpy(buf, who); - recp = validate_recipients(buf, NULL, 0); - syslog(LOG_DEBUG, "calendar: trying <%s>", buf); - if (recp != NULL) { - if (recp->num_local == 1) { - found_user = CtdlGetUser(&usbuf, recp->recp_local); - } - free_recipients(recp); - } - } - - /* If still not found, try it as an address qualified with the - * primary FQDN of this Citadel node. - */ - if (found_user != 0) { - snprintf(buf, sizeof buf, "%s@%s", who, CtdlGetConfigStr("c_fqdn")); - syslog(LOG_DEBUG, "calendar: trying <%s>", buf); - recp = validate_recipients(buf, NULL, 0); - if (recp != NULL) { - if (recp->num_local == 1) { - found_user = CtdlGetUser(&usbuf, recp->recp_local); - } - free_recipients(recp); - } - } - - /* Still not found? Try qualifying it with every domain we - * might have addresses in. - */ - if (found_user != 0) { - config_lines = num_tokens(inetcfg, '\n'); - for (i=0; ((i < config_lines) && (found_user != 0)); ++i) { - extract_token(buf, inetcfg, i, '\n', sizeof buf); - extract_token(host, buf, 0, '|', sizeof host); - extract_token(type, buf, 1, '|', sizeof type); - - if ( (!strcasecmp(type, "localhost")) - || (!strcasecmp(type, "directory")) ) { - snprintf(buf, sizeof buf, "%s@%s", who, host); - syslog(LOG_DEBUG, "calendar: trying <%s>", buf); - recp = validate_recipients(buf, NULL, 0); - if (recp != NULL) { - if (recp->num_local == 1) { - found_user = CtdlGetUser(&usbuf, recp->recp_local); - } - free_recipients(recp); - } - } - } - } - - if (found_user != 0) { - cprintf("%d No such user.\n", ERROR + NO_SUCH_USER); - return; - } - - CtdlMailboxName(calendar_room_name, sizeof calendar_room_name, - &usbuf, USERCALENDARROOM); - - strcpy(hold_rm, CC->room.QRname); /* save current room */ - - if (CtdlGetRoom(&CC->room, calendar_room_name) != 0) { - cprintf("%d Cannot open calendar\n", ERROR + ROOM_NOT_FOUND); - CtdlGetRoom(&CC->room, hold_rm); - return; - } - - /* Create a VFREEBUSY subcomponent */ - syslog(LOG_DEBUG, "calendar: creating VFREEBUSY component"); - fb = icalcomponent_new_vfreebusy(); - if (fb == NULL) { - cprintf("%d Internal error: cannot allocate memory.\n", ERROR + INTERNAL_ERROR); - CtdlGetRoom(&CC->room, hold_rm); - return; - } - - /* Set the method to PUBLISH */ - icalcomponent_set_method(fb, ICAL_METHOD_PUBLISH); - - /* Set the DTSTAMP to right now. */ - icalcomponent_set_dtstamp(fb, icaltime_from_timet_with_zone(time(NULL), 0, icaltimezone_get_utc_timezone())); - - /* Add the user's email address as ORGANIZER */ - sprintf(buf, "MAILTO:%s", who); - if (strchr(buf, '@') == NULL) { - strcat(buf, "@"); - strcat(buf, CtdlGetConfigStr("c_fqdn")); - } - for (i=0; buf[i]; ++i) { - if (buf[i]==' ') buf[i] = '_'; - } - icalcomponent_add_property(fb, icalproperty_new_organizer(buf)); - - /* Add busy time from events */ - syslog(LOG_DEBUG, "calendar: adding busy time from events"); - CtdlForEachMessage(MSGS_ALL, 0, NULL, NULL, NULL, ical_freebusy_backend, (void *)fb ); - - /* If values for DTSTART and DTEND are still not present, set them - * to yesterday and tomorrow as default values. - */ - if (icalcomponent_get_first_property(fb, ICAL_DTSTART_PROPERTY) == NULL) { - icalcomponent_set_dtstart(fb, icaltime_from_timet_with_zone(time(NULL)-86400L, 0, icaltimezone_get_utc_timezone())); - } - if (icalcomponent_get_first_property(fb, ICAL_DTEND_PROPERTY) == NULL) { - icalcomponent_set_dtend(fb, icaltime_from_timet_with_zone(time(NULL)+86400L, 0, icaltimezone_get_utc_timezone())); - } - - /* Put the freebusy component into the calendar component */ - syslog(LOG_DEBUG, "calendar: encapsulating"); - encaps = ical_encapsulate_subcomponent(fb); - if (encaps == NULL) { - icalcomponent_free(fb); - cprintf("%d Internal error: cannot allocate memory.\n", - ERROR + INTERNAL_ERROR); - CtdlGetRoom(&CC->room, hold_rm); - return; - } - - /* Set the method to PUBLISH */ - syslog(LOG_DEBUG, "calendar: setting method"); - icalcomponent_set_method(encaps, ICAL_METHOD_PUBLISH); - - /* Serialize it */ - syslog(LOG_DEBUG, "calendar: serializing"); - serialized_request = icalcomponent_as_ical_string_r(encaps); - icalcomponent_free(encaps); /* Don't need this anymore. */ - - cprintf("%d Free/busy for %s\n", LISTING_FOLLOWS, usbuf.fullname); - if (serialized_request != NULL) { - client_write(serialized_request, strlen(serialized_request)); - free(serialized_request); - } - cprintf("\n000\n"); - - /* Go back to the room from which we came... */ - CtdlGetRoom(&CC->room, hold_rm); -} - - -/* - * Backend for ical_getics() - * - * This is a ForEachMessage() callback function that searches the current room - * for calendar events and adds them each into one big calendar component. - */ -void ical_getics_backend(long msgnum, void *data) { - icalcomponent *encaps, *c; - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - - encaps = (icalcomponent *)data; - if (encaps == NULL) return; - - /* Look for the calendar event... */ - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) return; - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, "_HUNT_"); - mime_parser(CM_RANGE(msg, eMesageText), - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - CM_Free(msg); - - if (ird.cal == NULL) return; - - /* Here we go: put the VEVENT into the VCALENDAR. We now no longer - * are responsible for "the_request"'s memory -- it will be freed - * when we free "encaps". - */ - - /* If the top-level component is *not* a VCALENDAR, we can drop it right - * in. This will almost never happen. - */ - if (icalcomponent_isa(ird.cal) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_add_component(encaps, ird.cal); - } - /* - * In the more likely event that we're looking at a VCALENDAR with the VEVENT - * and other components encapsulated inside, we have to extract them. - */ - else { - for (c = icalcomponent_get_first_component(ird.cal, ICAL_ANY_COMPONENT); - (c != NULL); - c = icalcomponent_get_next_component(ird.cal, ICAL_ANY_COMPONENT)) { - - /* For VTIMEZONE components, suppress duplicates of the same tzid */ - - if (icalcomponent_isa(c) == ICAL_VTIMEZONE_COMPONENT) { - icalproperty *p = icalcomponent_get_first_property(c, ICAL_TZID_PROPERTY); - if (p) { - const char *tzid = icalproperty_get_tzid(p); - if (!icalcomponent_get_timezone(encaps, tzid)) { - icalcomponent_add_component(encaps, - icalcomponent_new_clone(c)); - } - } - } - - /* All other types of components can go in verbatim */ - else { - icalcomponent_add_component(encaps, icalcomponent_new_clone(c)); - } - } - icalcomponent_free(ird.cal); - } -} - - -/* - * Retrieve all of the calendar items in the current room, and output them - * as a single icalendar object. - */ -void ical_getics(void) -{ - icalcomponent *encaps = NULL; - char *ser = NULL; - - if ( (CC->room.QRdefaultview != VIEW_CALENDAR) - &&(CC->room.QRdefaultview != VIEW_TASKS) ) { - cprintf("%d Not a calendar room\n", ERROR+NOT_HERE); - return; /* Not an iCalendar-centric room */ - } - - encaps = icalcomponent_new_vcalendar(); - if (encaps == NULL) { - syslog(LOG_ERR, "calendar: could not allocate component!"); - cprintf("%d Could not allocate memory\n", ERROR+INTERNAL_ERROR); - return; - } - - cprintf("%d one big calendar\n", LISTING_FOLLOWS); - - /* Set the Product ID */ - icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); - - /* Set the Version Number */ - icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); - - /* Set the method to PUBLISH */ - icalcomponent_set_method(encaps, ICAL_METHOD_PUBLISH); - - /* Now go through the room encapsulating all calendar items. */ - CtdlForEachMessage(MSGS_ALL, 0, NULL, - NULL, - NULL, - ical_getics_backend, - (void *) encaps - ); - - ser = icalcomponent_as_ical_string_r(encaps); - icalcomponent_free(encaps); /* Don't need this anymore. */ - client_write(ser, strlen(ser)); - free(ser); - cprintf("\n000\n"); -} - - -/* - * Helper callback function for ical_putics() to discover which TZID's we need. - * Simply put the tzid name string into a hash table. After the callbacks are - * done we'll go through them and attach the ones that we have. - */ -void ical_putics_grabtzids(icalparameter *param, void *data) -{ - const char *tzid = icalparameter_get_tzid(param); - HashList *keys = (HashList *) data; - - if ( (keys) && (tzid) && (!IsEmptyStr(tzid)) ) { - Put(keys, tzid, strlen(tzid), strdup(tzid), NULL); - } -} - - -/* - * Delete all of the calendar items in the current room, and replace them - * with calendar items from a client-supplied data stream. - */ -void ical_putics(void) -{ - char *calstream = NULL; - icalcomponent *cal; - icalcomponent *c; - icalcomponent *encaps = NULL; - HashList *tzidlist = NULL; - HashPos *HashPos; - void *Value; - const char *Key; - long len; - - /* Only allow this operation if we're in a room containing a calendar or tasks view */ - if ( (CC->room.QRdefaultview != VIEW_CALENDAR) - &&(CC->room.QRdefaultview != VIEW_TASKS) ) { - cprintf("%d Not a calendar room\n", ERROR+NOT_HERE); - return; - } - - /* Only allow this operation if we have permission to overwrite the existing calendar */ - if (!CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) { - cprintf("%d Permission denied.\n", ERROR+HIGHER_ACCESS_REQUIRED); - return; - } - - cprintf("%d Transmit data now\n", SEND_LISTING); - calstream = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0); - if (calstream == NULL) { - return; - } - - cal = icalcomponent_new_from_string(calstream); - free(calstream); - - /* We got our data stream -- now do something with it. */ - - /* Delete the existing messages in the room, because we are overwriting - * the entire calendar with an entire new (or updated) calendar. - * (Careful: this opens an S_ROOMS critical section!) - */ - CtdlDeleteMessages(CC->room.QRname, NULL, 0, ""); - - /* If the top-level component is *not* a VCALENDAR, we can drop it right - * in. This will almost never happen. - */ - if (icalcomponent_isa(cal) != ICAL_VCALENDAR_COMPONENT) { - ical_write_to_cal(NULL, cal); - } - /* - * In the more likely event that we're looking at a VCALENDAR with the VEVENT - * and other components encapsulated inside, we have to extract them. - */ - else { - for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT); - (c != NULL); - c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) { - - /* Non-VTIMEZONE components each get written as individual messages. - * But we also need to attach the relevant VTIMEZONE components to them. - */ - if ( (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) - && (encaps = icalcomponent_new_vcalendar()) ) { - icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); - icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); - icalcomponent_set_method(encaps, ICAL_METHOD_PUBLISH); - - /* Attach any needed timezones here */ - tzidlist = NewHash(1, NULL); - if (tzidlist) { - icalcomponent_foreach_tzid(c, ical_putics_grabtzids, tzidlist); - } - HashPos = GetNewHashPos(tzidlist, 0); - - while (GetNextHashPos(tzidlist, HashPos, &len, &Key, &Value)) { - syslog(LOG_DEBUG, "calendar: attaching timezone '%s'", (char*) Value); - icaltimezone *t = NULL; - - /* First look for a timezone attached to the original calendar */ - t = icalcomponent_get_timezone(cal, Value); - - /* Try built-in tzdata if the right one wasn't attached */ - if (!t) { - t = icaltimezone_get_builtin_timezone(Value); - } - - /* I've got a valid timezone to attach. */ - if (t) { - icalcomponent_add_component(encaps, - icalcomponent_new_clone( - icaltimezone_get_component(t) - ) - ); - } - - } - DeleteHashPos(&HashPos); - DeleteHash(&tzidlist); - - /* Now attach the component itself (usually a VEVENT or VTODO) */ - icalcomponent_add_component(encaps, icalcomponent_new_clone(c)); - - /* Write it to the message store */ - ical_write_to_cal(NULL, encaps); - icalcomponent_free(encaps); - } - } - } - - icalcomponent_free(cal); -} - - -/* - * All Citadel calendar commands from the client come through here. - */ -void cmd_ical(char *argbuf) -{ - char subcmd[64]; - long msgnum; - char partnum[256]; - char action[256]; - char who[256]; - - extract_token(subcmd, argbuf, 0, '|', sizeof subcmd); - - /* Allow "test" and "freebusy" subcommands without logging in. */ - - if (!strcasecmp(subcmd, "test")) { - cprintf("%d This server supports calendaring\n", CIT_OK); - return; - } - - if (!strcasecmp(subcmd, "freebusy")) { - extract_token(who, argbuf, 1, '|', sizeof who); - ical_freebusy(who); - return; - } - - if (!strcasecmp(subcmd, "sgi")) { - CIT_ICAL->server_generated_invitations = (extract_int(argbuf, 1) ? 1 : 0) ; - cprintf("%d %d\n", CIT_OK, CIT_ICAL->server_generated_invitations); - return; - } - - if (CtdlAccessCheck(ac_logged_in)) return; - - if (!strcasecmp(subcmd, "respond")) { - msgnum = extract_long(argbuf, 1); - extract_token(partnum, argbuf, 2, '|', sizeof partnum); - extract_token(action, argbuf, 3, '|', sizeof action); - ical_respond(msgnum, partnum, action); - return; - } - - if (!strcasecmp(subcmd, "handle_rsvp")) { - msgnum = extract_long(argbuf, 1); - extract_token(partnum, argbuf, 2, '|', sizeof partnum); - extract_token(action, argbuf, 3, '|', sizeof action); - ical_handle_rsvp(msgnum, partnum, action); - return; - } - - if (!strcasecmp(subcmd, "conflicts")) { - msgnum = extract_long(argbuf, 1); - extract_token(partnum, argbuf, 2, '|', sizeof partnum); - ical_conflicts(msgnum, partnum); - return; - } - - if (!strcasecmp(subcmd, "getics")) { - ical_getics(); - return; - } - - if (!strcasecmp(subcmd, "putics")) { - ical_putics(); - return; - } - - cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED); -} - - -/* - * We don't know if the calendar room exists so we just create it at login - */ -void ical_CtdlCreateRoom(void) -{ - struct ctdlroom qr; - visit vbuf; - - /* Create the calendar room if it doesn't already exist */ - CtdlCreateRoom(USERCALENDARROOM, 4, "", 0, 1, 0, VIEW_CALENDAR); - - /* Set expiration policy to manual; otherwise objects will be lost! */ - if (CtdlGetRoomLock(&qr, USERCALENDARROOM)) { - syslog(LOG_ERR, "calendar: couldn't get the user calendar room"); - return; - } - qr.QRep.expire_mode = EXPIRE_MANUAL; - qr.QRdefaultview = VIEW_CALENDAR; /* 3 = calendar view */ - CtdlPutRoomLock(&qr); - - /* Set the view to a calendar view */ - CtdlGetRelationship(&vbuf, &CC->user, &qr); - vbuf.v_view = VIEW_CALENDAR; - CtdlSetRelationship(&vbuf, &CC->user, &qr); - - /* Create the tasks list room if it doesn't already exist */ - CtdlCreateRoom(USERTASKSROOM, 4, "", 0, 1, 0, VIEW_TASKS); - - /* Set expiration policy to manual; otherwise objects will be lost! */ - if (CtdlGetRoomLock(&qr, USERTASKSROOM)) { - syslog(LOG_ERR, "calendar: couldn't get the user calendar room!"); - return; - } - qr.QRep.expire_mode = EXPIRE_MANUAL; - qr.QRdefaultview = VIEW_TASKS; - CtdlPutRoomLock(&qr); - - /* Set the view to a task list view */ - CtdlGetRelationship(&vbuf, &CC->user, &qr); - vbuf.v_view = VIEW_TASKS; - CtdlSetRelationship(&vbuf, &CC->user, &qr); - - /* Create the notes room if it doesn't already exist */ - CtdlCreateRoom(USERNOTESROOM, 4, "", 0, 1, 0, VIEW_NOTES); - - /* Set expiration policy to manual; otherwise objects will be lost! */ - if (CtdlGetRoomLock(&qr, USERNOTESROOM)) { - syslog(LOG_ERR, "calendar: couldn't get the user calendar room!"); - return; - } - qr.QRep.expire_mode = EXPIRE_MANUAL; - qr.QRdefaultview = VIEW_NOTES; - CtdlPutRoomLock(&qr); - - /* Set the view to a notes view */ - CtdlGetRelationship(&vbuf, &CC->user, &qr); - vbuf.v_view = VIEW_NOTES; - CtdlSetRelationship(&vbuf, &CC->user, &qr); - - return; -} - - -/* - * ical_send_out_invitations() is called by ical_saving_vevent() when it finds a VEVENT. - * - * top_level_cal is the highest available level calendar object. - * cal is the subcomponent containing the VEVENT. - * - * Note: if you change the encapsulation code here, change it in WebCit's ical_encapsulate_subcomponent() - */ -void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal) { - icalcomponent *the_request = NULL; - char *serialized_request = NULL; - icalcomponent *encaps = NULL; - char *request_message_text = NULL; - struct CtdlMessage *msg = NULL; - struct recptypes *valid = NULL; - char attendees_string[SIZ]; - int num_attendees = 0; - char this_attendee[256]; - icalproperty *attendee = NULL; - char summary_string[SIZ]; - icalproperty *summary = NULL; - size_t reqsize; - icalproperty *p; - struct icaltimetype t; - const icaltimezone *attached_zones[5] = { NULL, NULL, NULL, NULL, NULL }; - int i; - const icaltimezone *z; - int num_zones_attached = 0; - int zone_already_attached; - icalparameter *tzidp = NULL; - const char *tzidc = NULL; - - if (cal == NULL) { - syslog(LOG_ERR, "calendar: trying to reply to NULL event?"); - return; - } - - /* If this is a VCALENDAR component, look for a VEVENT subcomponent. */ - if (icalcomponent_isa(cal) == ICAL_VCALENDAR_COMPONENT) { - ical_send_out_invitations(top_level_cal, - icalcomponent_get_first_component( - cal, ICAL_VEVENT_COMPONENT - ) - ); - return; - } - - /* Clone the event */ - the_request = icalcomponent_new_clone(cal); - if (the_request == NULL) { - syslog(LOG_ERR, "calendar: cannot clone calendar object"); - return; - } - - /* Extract the summary string -- we'll use it as the - * message subject for the request - */ - strcpy(summary_string, "Meeting request"); - summary = icalcomponent_get_first_property(the_request, ICAL_SUMMARY_PROPERTY); - if (summary != NULL) { - if (icalproperty_get_summary(summary)) { - strcpy(summary_string, - icalproperty_get_summary(summary) ); - } - } - - /* Determine who the recipients of this message are (the attendees) */ - strcpy(attendees_string, ""); - for (attendee = icalcomponent_get_first_property(the_request, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(the_request, ICAL_ATTENDEE_PROPERTY)) { - const char *ch = icalproperty_get_attendee(attendee); - if ((ch != NULL) && !strncasecmp(ch, "MAILTO:", 7)) { - safestrncpy(this_attendee, ch + 7, sizeof(this_attendee)); - - if (!CtdlIsMe(this_attendee, sizeof this_attendee)) { /* don't send an invitation to myself! */ - snprintf(&attendees_string[strlen(attendees_string)], - sizeof(attendees_string) - strlen(attendees_string), - "%s, ", - this_attendee - ); - ++num_attendees; - } - } - } - - syslog(LOG_DEBUG, "calendar: <%d> attendees: <%s>", num_attendees, attendees_string); - - /* If there are no attendees, there are no invitations to send, so... - * don't bother putting one together! Punch out, Maverick! - */ - if (num_attendees == 0) { - icalcomponent_free(the_request); - return; - } - - /* Encapsulate the VEVENT component into a complete VCALENDAR */ - encaps = icalcomponent_new_vcalendar(); - if (encaps == NULL) { - syslog(LOG_ERR, "calendar: could not allocate component!"); - icalcomponent_free(the_request); - return; - } - - /* Set the Product ID */ - icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); - - /* Set the Version Number */ - icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); - - /* Set the method to REQUEST */ - icalcomponent_set_method(encaps, ICAL_METHOD_REQUEST); - - /* Look for properties containing timezone parameters, to see if we need to attach VTIMEZONEs */ - for (p = icalcomponent_get_first_property(the_request, ICAL_ANY_PROPERTY); - p != NULL; - p = icalcomponent_get_next_property(the_request, ICAL_ANY_PROPERTY)) - { - if ( (icalproperty_isa(p) == ICAL_COMPLETED_PROPERTY) - || (icalproperty_isa(p) == ICAL_CREATED_PROPERTY) - || (icalproperty_isa(p) == ICAL_DATEMAX_PROPERTY) - || (icalproperty_isa(p) == ICAL_DATEMIN_PROPERTY) - || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY) - || (icalproperty_isa(p) == ICAL_DTSTAMP_PROPERTY) - || (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY) - || (icalproperty_isa(p) == ICAL_DUE_PROPERTY) - || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY) - || (icalproperty_isa(p) == ICAL_LASTMODIFIED_PROPERTY) - || (icalproperty_isa(p) == ICAL_MAXDATE_PROPERTY) - || (icalproperty_isa(p) == ICAL_MINDATE_PROPERTY) - || (icalproperty_isa(p) == ICAL_RECURRENCEID_PROPERTY) - ) { - t = icalproperty_get_dtstart(p); // it's safe to use dtstart for all of them - - /* Determine the tzid in order for some of the conditions below to work */ - tzidp = icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER); - if (tzidp) { - tzidc = icalparameter_get_tzid(tzidp); - } - else { - tzidc = NULL; - } - - /* First see if there's a timezone attached to the data structure itself */ - if (icaltime_is_utc(t)) { - z = icaltimezone_get_utc_timezone(); - } - else { - z = icaltime_get_timezone(t); - } - - /* If not, try to determine the tzid from the parameter using attached zones */ - if ((!z) && (tzidc)) { - z = icalcomponent_get_timezone(top_level_cal, tzidc); - } - - /* Still no good? Try our internal database */ - if ((!z) && (tzidc)) { - z = icaltimezone_get_builtin_timezone_from_tzid(tzidc); - } - - if (z) { - /* We have a valid timezone. Good. Now we need to attach it. */ - - zone_already_attached = 0; - for (i=0; i<5; ++i) { - if (z == attached_zones[i]) { - /* We've already got this one, no need to attach another. */ - ++zone_already_attached; - } - } - if ((!zone_already_attached) && (num_zones_attached < 5)) { - /* This is a new one, so attach it. */ - attached_zones[num_zones_attached++] = z; - } - - icalproperty_set_parameter(p, icalparameter_new_tzid(icaltimezone_get_tzid(z)) - ); - } - } - } - - /* Encapsulate any timezones we need */ - if (num_zones_attached > 0) for (i=0; iuser, - NULL, /* No single recipient here */ - NULL, /* No single recipient here */ - CC->room.QRname, - 0, - FMT_RFC822, - NULL, - NULL, - summary_string, /* Use summary for subject */ - NULL, - request_message_text, - NULL - ); - - if (msg != NULL) { - valid = validate_recipients(attendees_string, NULL, 0); - CtdlSubmitMsg(msg, valid, ""); - CM_Free(msg); - free_recipients(valid); - } - } - free(serialized_request); -} - - -/* - * When a calendar object is being saved, determine whether it's a VEVENT - * and the user saving it is the organizer. If so, send out invitations - * to any listed attendees. - * - * This function is recursive. The caller can simply supply the same object - * as both arguments. When it recurses it will alter the second argument - * while holding on to the top level object. This allows us to go back and - * grab things like time zones which might be attached. - * - */ -void ical_saving_vevent(icalcomponent *top_level_cal, icalcomponent *cal) { - icalcomponent *c; - icalproperty *organizer = NULL; - char organizer_string[SIZ]; - - syslog(LOG_DEBUG, "calendar: ical_saving_vevent() has been called"); - - /* Don't send out invitations unless the client wants us to. */ - if (CIT_ICAL->server_generated_invitations == 0) { - return; - } - - /* Don't send out invitations if we've been asked not to. */ - if (CIT_ICAL->avoid_sending_invitations > 0) { - return; - } - - strcpy(organizer_string, ""); - /* - * The VEVENT subcomponent is the one we're interested in. - * Send out invitations if, and only if, this user is the Organizer. - */ - if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) { - organizer = icalcomponent_get_first_property(cal, ICAL_ORGANIZER_PROPERTY); - if (organizer != NULL) { - if (icalproperty_get_organizer(organizer)) { - strcpy(organizer_string, - icalproperty_get_organizer(organizer)); - } - } - if (!strncasecmp(organizer_string, "MAILTO:", 7)) { - strcpy(organizer_string, &organizer_string[7]); - striplt(organizer_string); - /* - * If the user saving the event is listed as the - * organizer, then send out invitations. - */ - if (CtdlIsMe(organizer_string, sizeof organizer_string)) { - ical_send_out_invitations(top_level_cal, cal); - } - } - } - - /* If the component has subcomponents, recurse through them. */ - for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT); - (c != NULL); - c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) { - /* Recursively process subcomponent */ - ical_saving_vevent(top_level_cal, c); - } - -} - - -/* - * Back end for ical_obj_beforesave() - * This hunts for the UID of the calendar event (becomes Citadel msg EUID), - * the summary of the event (becomes message subject), - * and the start time (becomes message date/time). - */ -void ical_obj_beforesave_backend(char *name, char *filename, char *partnum, - char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, char *cbid, void *cbuserdata) -{ - const char* pch; - icalcomponent *cal, *nested_event, *nested_todo, *whole_cal; - icalproperty *p; - char new_uid[256] = ""; - struct CtdlMessage *msg = (struct CtdlMessage *) cbuserdata; - - if (!msg) return; - - /* We're only interested in calendar data. */ - if ( (strcasecmp(cbtype, "text/calendar")) - && (strcasecmp(cbtype, "application/ics")) ) { - return; - } - - /* Hunt for the UID and drop it in - * the "user data" pointer for the MIME parser. When - * ical_obj_beforesave() sees it there, it'll set the Exclusive msgid - * to that string. - */ - whole_cal = icalcomponent_new_from_string(content); - cal = whole_cal; - if (cal != NULL) { - if (icalcomponent_isa(cal) == ICAL_VCALENDAR_COMPONENT) { - nested_event = icalcomponent_get_first_component( - cal, ICAL_VEVENT_COMPONENT); - if (nested_event != NULL) { - cal = nested_event; - } - else { - nested_todo = icalcomponent_get_first_component( - cal, ICAL_VTODO_COMPONENT); - if (nested_todo != NULL) { - cal = nested_todo; - } - } - } - - if (cal != NULL) { - - /* Set the message EUID to the iCalendar UID */ - - p = ical_ctdl_get_subprop(cal, ICAL_UID_PROPERTY); - if (p == NULL) { - /* If there's no uid we must generate one */ - generate_uuid(new_uid); - icalcomponent_add_property(cal, icalproperty_new_uid(new_uid)); - p = ical_ctdl_get_subprop(cal, ICAL_UID_PROPERTY); - } - if (p != NULL) { - pch = icalproperty_get_comment(p); - if (!IsEmptyStr(pch)) { - CM_SetField(msg, eExclusiveID, pch, strlen(pch)); - syslog(LOG_DEBUG, "calendar: saving calendar UID <%s>", pch); - } - } - - /* Set the message subject to the iCalendar summary */ - - p = ical_ctdl_get_subprop(cal, ICAL_SUMMARY_PROPERTY); - if (p != NULL) { - pch = icalproperty_get_comment(p); - if (!IsEmptyStr(pch)) { - char *subj; - - subj = rfc2047encode(pch, strlen(pch)); - CM_SetAsField(msg, eMsgSubject, &subj, strlen(subj)); - } - } - - /* Set the message date/time to the iCalendar start time */ - - p = ical_ctdl_get_subprop(cal, ICAL_DTSTART_PROPERTY); - if (p != NULL) { - time_t idtstart; - idtstart = icaltime_as_timet(icalproperty_get_dtstart(p)); - if (idtstart > 0) { - CM_SetFieldLONG(msg, eTimestamp, idtstart); - } - } - - } - icalcomponent_free(cal); - if (whole_cal != cal) { - icalcomponent_free(whole_cal); - } - } -} - - -/* - * See if we need to prevent the object from being saved (we don't allow - * MIME types other than text/calendar in "calendar" or "tasks" rooms). - * - * If the message is being saved, we also set various message header fields - * using data found in the iCalendar object. - */ -int ical_obj_beforesave(struct CtdlMessage *msg, struct recptypes *recp) -{ - /* First determine if this is a calendar or tasks room */ - if ( (CC->room.QRdefaultview != VIEW_CALENDAR) - && (CC->room.QRdefaultview != VIEW_TASKS) - ) { - return(0); /* Not an iCalendar-centric room */ - } - - /* It must be an RFC822 message! */ - if (msg->cm_format_type != 4) { - syslog(LOG_DEBUG, "calendar: rejecting non-RFC822 message"); - return(1); /* You tried to save a non-RFC822 message! */ - } - - if (CM_IsEmpty(msg, eMesageText)) { - return(1); /* You tried to save a null message! */ - } - - /* Do all of our lovely back-end parsing */ - mime_parser(CM_RANGE(msg, eMesageText), - *ical_obj_beforesave_backend, - NULL, NULL, - (void *)msg, - 0 - ); - - return(0); -} - - -/* - * Things we need to do after saving a calendar event. - */ -void ical_obj_aftersave_backend(char *name, char *filename, char *partnum, - char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, char *cbid, void *cbuserdata) -{ - icalcomponent *cal; - - /* We're only interested in calendar items here. */ - if ( (strcasecmp(cbtype, "text/calendar")) - && (strcasecmp(cbtype, "application/ics")) ) { - return; - } - - /* Hunt for the UID and drop it in - * the "user data" pointer for the MIME parser. When - * ical_obj_beforesave() sees it there, it'll set the Exclusive msgid - * to that string. - */ - if ( (!strcasecmp(cbtype, "text/calendar")) - || (!strcasecmp(cbtype, "application/ics")) ) { - cal = icalcomponent_new_from_string(content); - if (cal != NULL) { - ical_saving_vevent(cal, cal); - icalcomponent_free(cal); - } - } -} - - -/* - * Things we need to do after saving a calendar event. - * (This will start back end tasks such as automatic generation of invitations, - * if such actions are appropriate.) - */ -int ical_obj_aftersave(struct CtdlMessage *msg, struct recptypes *recp) -{ - char roomname[ROOMNAMELEN]; - - /* - * If this isn't the Calendar> room, no further action is necessary. - */ - - /* First determine if this is our room */ - CtdlMailboxName(roomname, sizeof roomname, &CC->user, USERCALENDARROOM); - if (strcasecmp(roomname, CC->room.QRname)) { - return(0); /* Not the Calendar room -- don't do anything. */ - } - - /* It must be an RFC822 message! */ - if (msg->cm_format_type != 4) return(1); - - /* Reject null messages */ - if (CM_IsEmpty(msg, eMesageText)) return(1); - - /* Now recurse through it looking for our icalendar data */ - mime_parser(CM_RANGE(msg, eMesageText), - *ical_obj_aftersave_backend, - NULL, NULL, - NULL, - 0 - ); - - return(0); -} - - -void ical_session_startup(void) { - CIT_ICAL = malloc(sizeof(struct cit_ical)); - memset(CIT_ICAL, 0, sizeof(struct cit_ical)); -} - - -void ical_session_shutdown(void) { - free(CIT_ICAL); -} - - -/* - * Back end for ical_fixed_output() - */ -void ical_fixed_output_backend(icalcomponent *cal, int recursion_level) { - icalcomponent *c; - icalproperty *p; - char buf[256]; - const char *ch; - - p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY); - if (p != NULL) { - cprintf("%s\n", (const char *)icalproperty_get_comment(p)); - } - - p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY); - if (p != NULL) { - cprintf("%s\n", (const char *)icalproperty_get_comment(p)); - } - - p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY); - if (p != NULL) { - cprintf("%s\n", (const char *)icalproperty_get_comment(p)); - } - - /* If the component has attendees, iterate through them. */ - for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) { - ch = icalproperty_get_attendee(p); - if ((ch != NULL) && - !strncasecmp(ch, "MAILTO:", 7)) { - - /* screen name or email address */ - safestrncpy(buf, ch + 7, sizeof(buf)); - striplt(buf); - cprintf("%s ", buf); - } - cprintf("\n"); - } - - /* If the component has subcomponents, recurse through them. */ - for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT); - (c != 0); - c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) { - /* Recursively process subcomponent */ - ical_fixed_output_backend(c, recursion_level+1); - } -} - - -/* - * Function to output iCalendar data as plain text. Nobody uses MSG0 - * anymore, so really this is just so we expose the vCard data to the full - * text indexer. - */ -void ical_fixed_output(char *ptr, int len) { - icalcomponent *cal; - char *stringy_cal; - - stringy_cal = malloc(len + 1); - safestrncpy(stringy_cal, ptr, len + 1); - cal = icalcomponent_new_from_string(stringy_cal); - free(stringy_cal); - - if (cal == NULL) { - return; - } - - ical_fixed_output_backend(cal, 0); - - /* Free the memory we obtained from libical's constructor */ - icalcomponent_free(cal); -} - - -/* - * Register this module with the Citadel server. - */ -CTDL_MODULE_INIT(calendar) -{ - if (!threading) - { - - /* Tell libical to return errors instead of aborting if it gets bad data */ - -#ifdef LIBICAL_ICAL_EXPORT // cheap and sleazy way to detect libical >=2.0 - icalerror_set_errors_are_fatal(0); -#else - icalerror_errors_are_fatal = 0; -#endif - - /* Use our own application prefix in tzid's generated from system tzdata */ - icaltimezone_set_tzid_prefix("/citadel.org/"); - - /* Initialize our hook functions */ - CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); - CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); - CtdlRegisterSessionHook(ical_CtdlCreateRoom, EVT_LOGIN, PRIO_LOGIN + 1); - CtdlRegisterProtoHook(cmd_ical, "ICAL", "Citadel iCalendar commands"); - CtdlRegisterSessionHook(ical_session_startup, EVT_START, PRIO_START + 1); - CtdlRegisterSessionHook(ical_session_shutdown, EVT_STOP, PRIO_STOP + 80); - CtdlRegisterFixedOutputHook("text/calendar", ical_fixed_output); - CtdlRegisterFixedOutputHook("application/ics", ical_fixed_output); - } - - /* return our module name for the log */ - return "calendar"; -} diff --git a/citadel/modules/calendar/serv_calendar.h b/citadel/modules/calendar/serv_calendar.h deleted file mode 100644 index d7a149c53..000000000 --- a/citadel/modules/calendar/serv_calendar.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * iCalendar implementation for Citadel - * - * - * Copyright (c) 1987-2012 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * - * - */ - -/* - * "server_generated_invitations" tells the Citadel server that the - * client wants invitations to be generated and sent out by the - * server. Set to 1 to enable this functionality. - * - * "avoid_sending_invitations" is a server-internal variable. It is - * set internally during certain transactions and cleared - * automatically. - */ -struct cit_ical { - int server_generated_invitations; - int avoid_sending_invitations; -}; - -#define CIT_ICAL CC->CIT_ICAL -#define MAX_RECUR 1000 diff --git a/citadel/modules/checkpoint/.gitignore b/citadel/modules/checkpoint/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/checkpoint/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/checkpoint/serv_checkpoint.c b/citadel/modules/checkpoint/serv_checkpoint.c deleted file mode 100644 index 578622b2e..000000000 --- a/citadel/modules/checkpoint/serv_checkpoint.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * checkpointing module for the database - * - * Copyright (c) 1987-2012 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include - -#include - -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "msgbase.h" -#include "sysdep_decls.h" -#include "config.h" -#include "threads.h" - -#include "ctdl_module.h" -#include "context.h" - -CTDL_MODULE_INIT(checkpoint) -{ - if (threading) - { - CtdlRegisterSessionHook(cdb_checkpoint, EVT_TIMER, PRIO_CLEANUP + 10); - } - /* return our module name for the log */ - return "checkpoint"; -} diff --git a/citadel/modules/clamav/.gitignore b/citadel/modules/clamav/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/clamav/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/clamav/serv_virus.c b/citadel/modules/clamav/serv_virus.c deleted file mode 100644 index c5599b9de..000000000 --- a/citadel/modules/clamav/serv_virus.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * This module allows Citadel to use clamd to filter incoming messages - * arriving via SMTP. For more information on clamd, visit - * http://clamav.net (the ClamAV project is not in any way - * affiliated with the Citadel project). - * - * Copyright (c) 1987-2012 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define CLAMD_PORT "3310" - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "control.h" -#include "user_ops.h" -#include "database.h" -#include "msgbase.h" -#include "internet_addressing.h" -#include "domain.h" -#include "clientsocket.h" -#include "ctdl_module.h" - - -/* - * Connect to the clamd server and scan a message. - */ -int clamd(struct CtdlMessage *msg, struct recptypes *recp) { - int sock = (-1); - int streamsock = (-1); - char clamhosts[SIZ]; - int num_clamhosts; - char buf[SIZ]; - char hostbuf[SIZ]; - char portbuf[SIZ]; - int is_virus = 0; - int clamhost; - StrBuf *msgtext; - CitContext *CCC; - - /* See if we have any clamd hosts configured */ - num_clamhosts = get_hosts(clamhosts, "clamav"); - if (num_clamhosts < 1) { - return(0); - } - - /* Try them one by one until we get a working one */ - for (clamhost=0; clamhost\n", buf); - - /* Assuming a host:port entry */ - extract_token(hostbuf, buf, 0, ':', sizeof hostbuf); - if (extract_token(portbuf, buf, 1, ':', sizeof portbuf)==-1) - /* Didn't specify a port so we'll try the psuedo-standard 3310 */ - sock = sock_connect(hostbuf, CLAMD_PORT); - else - /* Port specified lets try connecting to it! */ - sock = sock_connect(hostbuf, portbuf); - - if (sock >= 0) syslog(LOG_DEBUG, "Connected!\n"); - } - - if (sock < 0) { - /* If the service isn't running, just pass the mail - * through. Potentially throwing away mails isn't good. - */ - return(0); - } - CCC=CC; - CCC->SBuf.Buf = NewStrBuf(); - CCC->sMigrateBuf = NewStrBuf(); - CCC->SBuf.ReadWritePointer = NULL; - - /* Command */ - syslog(LOG_DEBUG, "Transmitting STREAM command\n"); - sprintf(buf, "STREAM\r\n"); - sock_write(&sock, buf, strlen(buf)); - - syslog(LOG_DEBUG, "Waiting for PORT number\n"); - if (sock_getln(&sock, buf, sizeof buf) < 0) { - goto bail; - } - - syslog(LOG_DEBUG, "<%s\n", buf); - if (strncasecmp(buf, "PORT", 4)!=0) { - goto bail; - } - - /* Should have received a port number to connect to */ - extract_token(portbuf, buf, 1, ' ', sizeof portbuf); - - /* Attempt to establish connection to STREAM socket */ - streamsock = sock_connect(hostbuf, portbuf); - - if (streamsock < 0) { - /* If the service isn't running, just pass the mail - * through. Potentially throwing away mails isn't good. - */ - FreeStrBuf(&CCC->SBuf.Buf); - FreeStrBuf(&CCC->sMigrateBuf); - return(0); - } - else { - syslog(LOG_DEBUG, "STREAM socket connected!\n"); - } - - - /* Message */ - CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0); - msgtext = CC->redirect_buffer; - CC->redirect_buffer = NULL; - - sock_write(&streamsock, SKEY(msgtext)); - FreeStrBuf(&msgtext); - - /* Close the streamsocket connection; this tells clamd - * that we're done. - */ - if (streamsock != -1) { - close(streamsock); - } - - /* Response */ - syslog(LOG_DEBUG, "Awaiting response\n"); - if (sock_getln(&sock, buf, sizeof buf) < 0) { - goto bail; - } - syslog(LOG_DEBUG, "<%s\n", buf); - if (strncasecmp(buf, "stream: OK", 10)!=0) { - is_virus = 1; - } - - if (is_virus) { - CM_SetField(msg, eErrorMsg, HKEY("message rejected by virus filter")); - } - -bail: close(sock); - FreeStrBuf(&CCC->SBuf.Buf); - FreeStrBuf(&CCC->sMigrateBuf); - return(is_virus); -} - - -CTDL_MODULE_INIT(virus) -{ - if (!threading) - { - CtdlRegisterMessageHook(clamd, EVT_SMTPSCAN); - } - - /* return our module name for the log */ - return "virus"; -} diff --git a/citadel/modules/crypto/.gitignore b/citadel/modules/crypto/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/citadel/modules/crypto/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/citadel/modules/crypto/serv_crypto.c b/citadel/modules/crypto/serv_crypto.c deleted file mode 100644 index f78c0cd85..000000000 --- a/citadel/modules/crypto/serv_crypto.c +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright (c) 1987-2022 by the citadel.org team -// -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -#include -#include -#include -#include -#include "sysdep.h" - -#ifdef HAVE_OPENSSL -#include -#include -#include -#endif - -#include - -#ifdef HAVE_PTHREAD_H -#include -#endif - -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#include -#include -#include "server.h" -#include "serv_crypto.h" -#include "sysdep_decls.h" -#include "citadel.h" -#include "config.h" -#include "ctdl_module.h" - -#ifdef HAVE_OPENSSL - -SSL_CTX *ssl_ctx = NULL; // This SSL context is used for all sessions. -char *ssl_cipher_list = CIT_CIPHERS; - -// If a private key does not exist, generate one now. -void generate_key(char *keyfilename) { - int ret = 0; - RSA *rsa = NULL; - BIGNUM *bne = NULL; - int bits = 2048; - unsigned long e = RSA_F4; - FILE *fp; - - if (access(keyfilename, R_OK) == 0) { // Already have one. - syslog(LOG_INFO, "crypto: %s exists and is readable", keyfilename); - return; - } - - syslog(LOG_INFO, "crypto: generating RSA key pair"); - - // generate rsa key - bne = BN_new(); - ret = BN_set_word(bne,e); - if (ret != 1) { - goto free_all; - } - - rsa = RSA_new(); - ret = RSA_generate_key_ex(rsa, bits, bne, NULL); - if (ret != 1) { - goto free_all; - } - - // write the key file - fp = fopen(keyfilename, "w"); - if (fp != NULL) { - chmod(keyfilename, 0600); - if (PEM_write_RSAPrivateKey(fp, // the file - rsa, // the key - NULL, // no enc - NULL, // no passphrase - 0, // no passphrase - NULL, // no callback - NULL // no callback - ) != 1) { - syslog(LOG_ERR, "crypto: cannot write key: %s", ERR_reason_error_string(ERR_get_error())); - unlink(keyfilename); - } - fclose(fp); - } - - // free the memory we used -free_all: - RSA_free(rsa); - BN_free(bne); -} - - -// If a certificate does not exist, generate a self-signed one now. -void generate_certificate(char *keyfilename, char *certfilename) { - RSA *private_key = NULL; - EVP_PKEY *public_key = NULL; - X509_REQ *certificate_signing_request = NULL; - X509_NAME *name = NULL; - X509 *certificate = NULL; - FILE *fp; - - if (access(certfilename, R_OK) == 0) { // already have one. - syslog(LOG_INFO, "crypto: %s exists and is readable", certfilename); - return; - } - - syslog(LOG_INFO, "crypto: generating a self-signed certificate"); - - // Read in our private key. - fp = fopen(keyfilename, "r"); - if (fp) { - private_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); - fclose(fp); - } - - if (!private_key) { - syslog(LOG_ERR, "crypto: cannot read the private key"); - return; - } - - // Create a public key from the private key - public_key = EVP_PKEY_new(); - if (!public_key) { - syslog(LOG_ERR, "crypto: cannot allocate public key"); - RSA_free(private_key); - return; - } - EVP_PKEY_assign_RSA(public_key, private_key); - - // Create a certificate signing request - certificate_signing_request = X509_REQ_new(); - if (!certificate_signing_request) { - syslog(LOG_ERR, "crypto: cannot allocate certificate signing request"); - RSA_free(private_key); - EVP_PKEY_free(public_key); - return; - } - - // Assign the public key to the certificate signing request - X509_REQ_set_pubkey(certificate_signing_request, public_key); - X509_REQ_set_version(certificate_signing_request, 0L); - - // Tell it who we are - name = X509_REQ_get_subject_name(certificate_signing_request); - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned const char *)"ZZ", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned const char *)"The World", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned const char *)"My Location", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned const char *)"Generic certificate", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned const char *)"Citadel server", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned const char *)"*", -1, -1, 0); - X509_REQ_set_subject_name(certificate_signing_request, name); - - // Sign the CSR - if (!X509_REQ_sign(certificate_signing_request, public_key, EVP_md5())) { - syslog(LOG_ERR, "crypto: X509_REQ_sign(): error"); - X509_REQ_free(certificate_signing_request); - RSA_free(private_key); - EVP_PKEY_free(public_key); - return; - } - - // Generate the self-signed certificate - certificate = X509_new(); - if (!certificate) { - syslog(LOG_ERR, "crypto: cannot allocate X.509 certificate"); - X509_REQ_free(certificate_signing_request); - RSA_free(private_key); - EVP_PKEY_free(public_key); - return; - } - - ASN1_INTEGER_set(X509_get_serialNumber(certificate), 0); - X509_set_issuer_name(certificate, X509_REQ_get_subject_name(certificate_signing_request)); - X509_set_subject_name(certificate, X509_REQ_get_subject_name(certificate_signing_request)); - X509_gmtime_adj(X509_get_notBefore(certificate), 0); - X509_gmtime_adj(X509_get_notAfter(certificate), (long)60*60*24*SIGN_DAYS); - X509_set_pubkey(certificate, public_key); - X509_REQ_free(certificate_signing_request); // We're done with the CSR; free it - - // Finally, sign the certificate with our private key. - if (!X509_sign(certificate, public_key, EVP_md5())) { - syslog(LOG_ERR, "crypto: X509_sign() error"); - X509_free(certificate); - RSA_free(private_key); - EVP_PKEY_free(public_key); - return; - } - - // Write the certificate to disk - fp = fopen(certfilename, "w"); - if (fp != NULL) { - chmod(certfilename, 0600); - PEM_write_X509(fp, certificate); - fclose(fp); - } - else { - syslog(LOG_ERR, "crypto: %s: %m", certfilename); - } - - X509_free(certificate); - EVP_PKEY_free(public_key); - // do not RSA_free(private_key); because it was freed by EVP_PKEY_free() above -} - - -// Set the private key and certificate chain for the global SSL Context. -// This is called during initialization, and can be called again later if the certificate changes. -void bind_to_key_and_certificate(void) { - - SSL_CTX *old_ctx = NULL; - SSL_CTX *new_ctx = NULL; - - const SSL_METHOD *method = SSLv23_server_method(); - if (!method) { - syslog(LOG_ERR, "crypto: SSLv23_server_method() failed: %s", ERR_reason_error_string(ERR_get_error())); - return; - } - - new_ctx = SSL_CTX_new(method); - if (!new_ctx) { - syslog(LOG_ERR, "crypto: SSL_CTX_new failed: %s", ERR_reason_error_string(ERR_get_error())); - return; - } - - if (!(SSL_CTX_set_cipher_list(new_ctx, ssl_cipher_list))) { - syslog(LOG_ERR, "crypto: SSL_CTX_set_cipher_list failed: %s", ERR_reason_error_string(ERR_get_error())); - return; - } - - syslog(LOG_DEBUG, "crypto: using certificate chain %s", file_crpt_file_cer); - if (!SSL_CTX_use_certificate_chain_file(new_ctx, file_crpt_file_cer)) { - syslog(LOG_ERR, "crypto: SSL_CTX_use_certificate_chain_file failed: %s", ERR_reason_error_string(ERR_get_error())); - return; - } - - syslog(LOG_DEBUG, "crypto: using private key %s", file_crpt_file_key); - if (!SSL_CTX_use_PrivateKey_file(new_ctx, file_crpt_file_key, SSL_FILETYPE_PEM)) { - syslog(LOG_ERR, "crypto: SSL_CTX_use_PrivateKey_file failed: %s", ERR_reason_error_string(ERR_get_error())); - return; - } - - old_ctx = ssl_ctx; - ssl_ctx = new_ctx; // All future binds will use the new certificate - - if (old_ctx != NULL) { - sleep(1); // Hopefully wait until all in-progress binds to the old certificate have completed - SSL_CTX_free(old_ctx); - } -} - - -// Check the modification time of the key and certificate files. Reload if either one changed. -void update_key_and_cert_if_needed(void) { - static time_t previous_mtime = 0; - struct stat keystat; - struct stat certstat; - - if (stat(file_crpt_file_key, &keystat) != 0) { - syslog(LOG_ERR, "%s: %s", file_crpt_file_key, strerror(errno)); - return; - } - if (stat(file_crpt_file_cer, &certstat) != 0) { - syslog(LOG_ERR, "%s: %s", file_crpt_file_cer, strerror(errno)); - return; - } - - if ((keystat.st_mtime + certstat.st_mtime) != previous_mtime) { - begin_critical_section(S_OPENSSL); - bind_to_key_and_certificate(); - end_critical_section(S_OPENSSL); - previous_mtime = keystat.st_mtime + certstat.st_mtime; - } -} - - -// Initialize the TLS subsystem. -void init_ssl(void) { - - // Initialize the OpenSSL library - SSL_library_init(); - SSL_load_error_strings(); - - // Load (or generate) a key and certificate - mkdir(ctdl_key_dir, 0700); // If the keys directory does not exist, create it - generate_key(file_crpt_file_key); // If a private key does not exist, create it - generate_certificate(file_crpt_file_key, file_crpt_file_cer); // If a certificate does not exist, create it - bind_to_key_and_certificate(); // Load key and cert from disk, and bind to them. - - // Register some Citadel protocol commands for dealing with encrypted sessions - CtdlRegisterProtoHook(cmd_stls, "STLS", "Start TLS session"); - CtdlRegisterProtoHook(cmd_gtls, "GTLS", "Get TLS session status"); - CtdlRegisterSessionHook(endtls, EVT_STOP, PRIO_STOP + 10); -} - - -// client_write_ssl() Send binary data to the client encrypted. -void client_write_ssl(const char *buf, int nbytes) { - int retval; - int nremain; - char junk[1]; - - nremain = nbytes; - - while (nremain > 0) { - if (SSL_want_write(CC->ssl)) { - if ((SSL_read(CC->ssl, junk, 0)) < 1) { - syslog(LOG_DEBUG, "crypto: SSL_read in client_write: %s", ERR_reason_error_string(ERR_get_error())); - } - } - retval = - SSL_write(CC->ssl, &buf[nbytes - nremain], nremain); - if (retval < 1) { - long errval; - - errval = SSL_get_error(CC->ssl, retval); - if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) { - sleep(1); - continue; - } - syslog(LOG_DEBUG, "crypto: SSL_write got error %ld, ret %d", errval, retval); - if (retval == -1) { - syslog(LOG_DEBUG, "crypto: errno is %d", errno); - } - endtls(); - client_write(&buf[nbytes - nremain], nremain); - return; - } - nremain -= retval; - } -} - - -// read data from the encrypted layer. -int client_read_sslbuffer(StrBuf *buf, int timeout) { - char sbuf[16384]; // OpenSSL communicates in 16k blocks, so let's speak its native tongue. - int rlen; - char junk[1]; - SSL *pssl = CC->ssl; - - if (pssl == NULL) return(-1); - - while (1) { - if (SSL_want_read(pssl)) { - if ((SSL_write(pssl, junk, 0)) < 1) { - syslog(LOG_DEBUG, "crypto: SSL_write in client_read"); - } - } - rlen = SSL_read(pssl, sbuf, sizeof(sbuf)); - if (rlen < 1) { - long errval; - - errval = SSL_get_error(pssl, rlen); - if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) { - sleep(1); - continue; - } - syslog(LOG_DEBUG, "crypto: SSL_read got error %ld", errval); - endtls(); - return (-1); - } - StrBufAppendBufPlain(buf, sbuf, rlen, 0); - return rlen; - } - return (0); -} - - -int client_readline_sslbuffer(StrBuf *Line, StrBuf *IOBuf, const char **Pos, int timeout) { - const char *pos = NULL; - const char *pLF; - int len, rlen; - int nSuccessLess = 0; - const char *pch = NULL; - - if ((Line == NULL) || (Pos == NULL) || (IOBuf == NULL)) { - if (Pos != NULL) { - *Pos = NULL; - } - return -1; - } - - pos = *Pos; - if ((StrLength(IOBuf) > 0) && (pos != NULL) && (pos < ChrPtr(IOBuf) + StrLength(IOBuf))) { - pch = pos; - pch = strchr(pch, '\n'); - - if (pch == NULL) { - StrBufAppendBufPlain(Line, pos, StrLength(IOBuf) - (pos - ChrPtr(IOBuf)), 0); - FlushStrBuf(IOBuf); - *Pos = NULL; - } - else { - int n = 0; - if ((pch > ChrPtr(IOBuf)) && (*(pch - 1) == '\r')) { - n = 1; - } - StrBufAppendBufPlain(Line, pos, (pch - pos - n), 0); - - if (StrLength(IOBuf) <= (pch - ChrPtr(IOBuf) + 1)) { - FlushStrBuf(IOBuf); - *Pos = NULL; - } - else { - *Pos = pch + 1; - } - return StrLength(Line); - } - } - - pLF = NULL; - while ((nSuccessLess < timeout) && (pLF == NULL) && (CC->ssl != NULL)) { - - rlen = client_read_sslbuffer(IOBuf, timeout); - if (rlen < 1) { - return -1; - } - else if (rlen > 0) { - pLF = strchr(ChrPtr(IOBuf), '\n'); - } - } - *Pos = NULL; - if (pLF != NULL) { - pos = ChrPtr(IOBuf); - len = pLF - pos; - if (len > 0 && (*(pLF - 1) == '\r') ) - len --; - StrBufAppendBufPlain(Line, pos, len, 0); - if (pLF + 1 >= ChrPtr(IOBuf) + StrLength(IOBuf)) { - FlushStrBuf(IOBuf); - } - else { - *Pos = pLF + 1; - } - return StrLength(Line); - } - return -1; -} - - -int client_read_sslblob(StrBuf *Target, long bytes, int timeout) { - long baselen; - long RemainRead; - int retval = 0; - - baselen = StrLength(Target); - - if (StrLength(CC->RecvBuf.Buf) > 0) { - long RemainLen; - long TotalLen; - const char *pchs; - - if (CC->RecvBuf.ReadWritePointer == NULL) { - CC->RecvBuf.ReadWritePointer = ChrPtr(CC->RecvBuf.Buf); - } - pchs = ChrPtr(CC->RecvBuf.Buf); - TotalLen = StrLength(CC->RecvBuf.Buf); - RemainLen = TotalLen - (pchs - CC->RecvBuf.ReadWritePointer); - if (RemainLen > bytes) { - RemainLen = bytes; - } - if (RemainLen > 0) { - StrBufAppendBufPlain(Target, CC->RecvBuf.ReadWritePointer, RemainLen, 0); - CC->RecvBuf.ReadWritePointer += RemainLen; - } - if ((ChrPtr(CC->RecvBuf.Buf) + StrLength(CC->RecvBuf.Buf)) <= CC->RecvBuf.ReadWritePointer) { - CC->RecvBuf.ReadWritePointer = NULL; - FlushStrBuf(CC->RecvBuf.Buf); - } - } - - if (StrLength(Target) >= bytes + baselen) { - return 1; - } - - CC->RecvBuf.ReadWritePointer = NULL; - - while ((StrLength(Target) < bytes + baselen) && (retval >= 0)) { - retval = client_read_sslbuffer(CC->RecvBuf.Buf, timeout); - if (retval >= 0) { - RemainRead = bytes - (StrLength (Target) - baselen); - if (RemainRead < StrLength(CC->RecvBuf.Buf)) { - StrBufAppendBufPlain( - Target, - ChrPtr(CC->RecvBuf.Buf), - RemainRead, 0); - CC->RecvBuf.ReadWritePointer = ChrPtr(CC->RecvBuf.Buf) + RemainRead; - break; - } - StrBufAppendBuf(Target, CC->RecvBuf.Buf, 0); - FlushStrBuf(CC->RecvBuf.Buf); - } - else { - FlushStrBuf(CC->RecvBuf.Buf); - return -1; - - } - } - return 1; -} - - -// CtdlStartTLS() starts TLS encryption for the current session. It -// must be supplied with pre-generated strings for responses of "ok," "no -// support for TLS," and "error" so that we can use this in any protocol. -void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response) { - int retval, bits, alg_bits; - - if (CC->redirect_ssl) { - syslog(LOG_ERR, "crypto: attempt to begin SSL on an already encrypted connection"); - if (error_response != NULL) { - cprintf("%s", error_response); - } - return; - } - - if (!ssl_ctx) { - syslog(LOG_ERR, "crypto: SSL failed: context has not been initialized"); - if (nosup_response != NULL) { - cprintf("%s", nosup_response); - } - return; - } - - update_key_and_cert_if_needed(); // did someone update the key or cert? if so, re-bind them - - if (!(CC->ssl = SSL_new(ssl_ctx))) { - syslog(LOG_ERR, "crypto: SSL_new failed: %s", ERR_reason_error_string(ERR_get_error())); - if (error_response != NULL) { - cprintf("%s", error_response); - } - return; - } - if (!(SSL_set_fd(CC->ssl, CC->client_socket))) { - syslog(LOG_ERR, "crypto: SSL_set_fd failed: %s", ERR_reason_error_string(ERR_get_error())); - SSL_free(CC->ssl); - CC->ssl = NULL; - if (error_response != NULL) { - cprintf("%s", error_response); - } - return; - } - if (ok_response != NULL) { - cprintf("%s", ok_response); - } - retval = SSL_accept(CC->ssl); - if (retval < 1) { - // Can't notify the client of an error here; they will - // discover the problem at the SSL layer and should - // revert to unencrypted communications. - syslog(LOG_ERR, "crypto: SSL_accept failed: %s", ERR_reason_error_string(ERR_get_error())); - SSL_free(CC->ssl); - CC->ssl = NULL; - return; - } - bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits); - syslog(LOG_INFO, "crypto: TLS using %s on %s (%d of %d bits)", - SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)), - SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)), - bits, alg_bits - ); - CC->redirect_ssl = 1; -} - - -// cmd_stls() starts TLS encryption for the current session -void cmd_stls(char *params) { - char ok_response[SIZ]; - char nosup_response[SIZ]; - char error_response[SIZ]; - - unbuffer_output(); - - sprintf(ok_response, "%d Begin TLS negotiation now\n", CIT_OK); - sprintf(nosup_response, "%d TLS not supported here\n", ERROR + CMD_NOT_SUPPORTED); - sprintf(error_response, "%d TLS negotiation error\n", ERROR + INTERNAL_ERROR); - - CtdlStartTLS(ok_response, nosup_response, error_response); -} - - -// cmd_gtls() returns status info about the TLS connection -void cmd_gtls(char *params) { - int bits, alg_bits; - - if (!CC->ssl || !CC->redirect_ssl) { - cprintf("%d Session is not encrypted.\n", ERROR); - return; - } - bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits); - cprintf("%d %s|%s|%d|%d\n", CIT_OK, - SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)), - SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)), - alg_bits, bits); -} - - -// endtls() shuts down the TLS connection -void endtls(void) { - if (!CC->ssl) { - CC->redirect_ssl = 0; - return; - } - - syslog(LOG_INFO, "crypto: ending TLS on this session"); - SSL_shutdown(CC->ssl); - SSL_free(CC->ssl); - CC->ssl = NULL; - CC->redirect_ssl = 0; -} - -#endif // HAVE_OPENSSL diff --git a/citadel/modules/crypto/serv_crypto.h b/citadel/modules/crypto/serv_crypto.h deleted file mode 100644 index b5ee85d53..000000000 --- a/citadel/modules/crypto/serv_crypto.h +++ /dev/null @@ -1,23 +0,0 @@ - -/* - * Number of days for which self-signed certs are valid. - */ -#define SIGN_DAYS 1100 // Just over three years - -// Which ciphers will be offered; see https://www.openssl.org/docs/manmaster/man1/ciphers.html -#define CIT_CIPHERS "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH" - -#ifdef HAVE_OPENSSL -#define OPENSSL_NO_KRB5 /* work around redhat b0rken ssl headers */ -void init_ssl(void); -void client_write_ssl (const char *buf, int nbytes); -int client_read_sslbuffer(StrBuf *buf, int timeout); -int client_readline_sslbuffer(StrBuf *Target, StrBuf *Buffer, const char **Pos, int timeout); -int client_read_sslblob(StrBuf *Target, long want_len, int timeout); -void cmd_stls(char *params); -void cmd_gtls(char *params); -void endtls(void); -void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response); -extern SSL_CTX *ssl_ctx; - -#endif diff --git a/citadel/modules/ctdlproto/files.h b/citadel/modules/ctdlproto/files.h deleted file mode 100644 index 080c83e76..000000000 --- a/citadel/modules/ctdlproto/files.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef FILE_OPS_H -#define FILE_OPS_H - -#include "context.h" - -void OpenCmdResult (char *, const char *); -void abort_upl (CitContext *who); - - -#endif /* FILE_OPS_H */ diff --git a/citadel/modules/ctdlproto/serv_ctdlproto.c b/citadel/modules/ctdlproto/serv_ctdlproto.c deleted file mode 100644 index cf51e62c3..000000000 --- a/citadel/modules/ctdlproto/serv_ctdlproto.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Citadel protocol main dispatcher - * - * Copyright (c) 1987-2017 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include - -#include "citserver.h" -#include "ctdl_module.h" -#include "config.h" -/* - * This loop recognizes all server commands. - */ -void do_command_loop(void) { - struct CitContext *CCC = CC; - char cmdbuf[SIZ]; - - time(&CCC->lastcmd); - memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ - if (client_getln(cmdbuf, sizeof cmdbuf) < 1) { - syslog(LOG_INFO, "Citadel client disconnected: ending session."); - CCC->kill_me = KILLME_CLIENT_DISCONNECTED; - return; - } - - /* Log the server command, but don't show passwords... */ - if ( (strncasecmp(cmdbuf, "PASS", 4)) && (strncasecmp(cmdbuf, "SETP", 4)) ) { - syslog(LOG_DEBUG, "[%s(%ld)] %s", - CCC->curr_user, CCC->user.usernum, cmdbuf - ); - } - else { - syslog(LOG_DEBUG, "[%s(%ld)]