1. WebCit must now run on the same host as Citadel Server, which is how everyone runs it anyway.
2. WebCit now uses the SSL key and Certificate from the Citadel Server directory.
LOCALEDIR=@LOCALEDIR@
WWWDIR=@WWWDIR@
ETCDIR=@ETCDIR@
-HEADERS=calendar.h dav.h messages.h modules_init.h paramhandling.h preferences.h roomops.h subst.h sysdep.h tcp_sockets.h utils.h webcit.h webserver.h
+HEADERS=calendar.h dav.h messages.h modules_init.h paramhandling.h preferences.h roomops.h subst.h sysdep.h sockets.h utils.h webcit.h webserver.h
# End of configuration section
webcit: webserver.o context_loop.o ical_dezonify.o \
cookie_conversion.o locate_host.o summary.o \
- webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o marchlist.o \
+ webcit.o auth.o sockets.o mainmenu.o serv_func.o who.o marchlist.o \
roomops.o roomlist.o roomtokens.o roomviews.o \
blogview_renderer.o msg_renderers.o jsonview_renderer.o mailview_renderer.o bbsview_renderer.o \
messages.o paging.o sysmsgs.o \
echo LD: webcit
$(CC) $(LDFLAGS) -o webcit $(LIBOBJS) \
webserver.o context_loop.o cookie_conversion.o marchlist.o \
- webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o listsub.o \
+ webcit.o auth.o sockets.o mainmenu.o serv_func.o who.o listsub.o \
roomops.o roomlist.o roomtokens.o roomviews.o \
messages.o msg_renderers.o paging.o sysmsgs.o \
blogview_renderer.o jsonview_renderer.o mailview_renderer.o bbsview_renderer.o \
/*
* These functions handle authentication of users to a Citadel server.
*
- * Copyright (c) 1996-2012 by the citadel.org team
+ * Copyright (c) 1996-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.
);
begin_burst();
- wc_printf("Connection to Citadel server at %s:%s : %s\r\n",
- ctdlhost, ctdlport,
+ wc_printf("Connection to Citadel server in %s : %s\r\n", ctdl_dir,
(WC->connected ? "SUCCESS" : "FAIL")
);
#include "webcit.h"
#include "webserver.h"
-/* where to find the keys */
-#define CTDL_CRYPTO_DIR ctdl_key_dir
-#define CTDL_KEY_PATH file_crpt_file_key
-#define CTDL_CSR_PATH file_crpt_file_csr
-#define CTDL_CER_PATH file_crpt_file_cer
-#define SIGN_DAYS 3650 // how long our self-signed certificate should live
SSL_CTX *ssl_ctx; // Global SSL context
char *ssl_cipher_list = DEFAULT_SSL_CIPHER_LIST;
}
-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) { // We already have a key -- don't generate a new one.
- 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(file_crpt_file_key, 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);
-}
-
-
// initialize ssl engine, load certs and initialize openssl internals
void init_ssl(void) {
const SSL_METHOD *ssl_method;
- RSA *rsa=NULL;
- X509_REQ *req = NULL;
- X509 *cer = NULL;
- EVP_PKEY *pk = NULL;
- EVP_PKEY *req_pkey = NULL;
- X509_NAME *name = NULL;
- FILE *fp;
- char buf[SIZ];
- int rv = 0;
#ifndef OPENSSL_NO_EGD
if (!access("/var/run/egd-pool", F_OK)) {
return;
}
- CRYPTO_set_locking_callback(ssl_lock);
- CRYPTO_set_id_callback(id_callback);
-
- // Get our certificates in order.
- // First, create the key/cert directory if it's not there already...
- mkdir(CTDL_CRYPTO_DIR, 0700);
-
- // Before attempting to generate keys/certificates, first try
- // link to them from the Citadel server if it's on the same host.
- // We ignore any error return because it either meant that there
- // was nothing in Citadel to link from (in which case we just
- // generate new files) or the target files already exist (which
- // is not fatal either).
- if (!strcasecmp(ctdlhost, "uds")) {
- sprintf(buf, "%s/keys/citadel.key", ctdlport);
- rv = symlink(buf, CTDL_KEY_PATH);
- if (!rv) {
- syslog(LOG_DEBUG, "%s", strerror(errno));
- }
- sprintf(buf, "%s/keys/citadel.csr", ctdlport);
- rv = symlink(buf, CTDL_CSR_PATH);
- if (!rv) {
- syslog(LOG_DEBUG, "%s", strerror(errno));
- }
- sprintf(buf, "%s/keys/citadel.cer", ctdlport);
- rv = symlink(buf, CTDL_CER_PATH);
- if (!rv) {
- syslog(LOG_DEBUG, "%s", strerror(errno));
- }
- }
-
- // If we still don't have a private key, generate one.
- generate_key(CTDL_KEY_PATH);
-
- // If there is no certificate file on disk, we will be generating a self-signed certificate
- // in the next step. Therefore, if we have neither a CSR nor a certificate, generate
- // the CSR in this step so that the next step may commence.
- if ( (access(CTDL_CER_PATH, R_OK) != 0) && (access(CTDL_CSR_PATH, R_OK) != 0) ) {
- syslog(LOG_INFO, "Generating a certificate signing request.");
-
- // Read our key from the file. No, we don't just keep this
- // in memory from the above key-generation function, because
- // there is the possibility that the key was already on disk
- // and we didn't just generate it now.
- fp = fopen(CTDL_KEY_PATH, "r");
- if (fp) {
- rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
- fclose(fp);
- }
-
- if (rsa) {
- // Create a public key from the private key
- if (pk=EVP_PKEY_new(), pk != NULL) {
- EVP_PKEY_assign_RSA(pk, rsa);
- if (req = X509_REQ_new(), req != NULL) {
- const char *env;
- // Set the public key
- X509_REQ_set_pubkey(req, pk);
- X509_REQ_set_version(req, 0L);
-
- name = X509_REQ_get_subject_name(req);
-
- // Tell it who we are
- env = getenv("O");
- if (env == NULL) {
- env = "Organization name";
- }
-
- X509_NAME_add_entry_by_txt(
- name, "O",
- MBSTRING_ASC,
- (unsigned char*)env,
- -1, -1, 0
- );
-
- env = getenv("OU");
- if (env == NULL) {
- env = "Citadel server";
- }
-
- X509_NAME_add_entry_by_txt(
- name, "OU",
- MBSTRING_ASC,
- (unsigned char*)env,
- -1, -1, 0
- );
-
- env = getenv("CN");
- if (env == NULL)
- env = "*";
-
- X509_NAME_add_entry_by_txt(
- name, "CN",
- MBSTRING_ASC,
- (unsigned char*)env,
- -1, -1, 0
- );
-
- X509_REQ_set_subject_name(req, name);
-
- // Sign the CSR
- if (!X509_REQ_sign(req, pk, EVP_md5())) {
- syslog(LOG_WARNING, "X509_REQ_sign(): error");
- }
- else {
- // Write it to disk.
- fp = fopen(CTDL_CSR_PATH, "w");
- if (fp != NULL) {
- chmod(CTDL_CSR_PATH, 0600);
- PEM_write_X509_REQ(fp, req);
- fclose(fp);
- }
- else {
- syslog(LOG_WARNING, "Cannot write key: %s", CTDL_CSR_PATH);
- ShutDownWebcit();
- exit(0);
- }
- }
-
- X509_REQ_free(req);
- }
- }
- RSA_free(rsa);
- }
- else {
- syslog(LOG_WARNING, "Unable to read private key.");
- }
- }
-
- // Generate a self-signed certificate if we don't have one.
- if (access(CTDL_CER_PATH, R_OK) != 0) {
- syslog(LOG_INFO, "Generating a self-signed certificate.\n");
- // Same deal as before: always read the key from disk because
- // it may or may not have just been generated.
- fp = fopen(CTDL_KEY_PATH, "r");
- if (fp) {
- rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
- fclose(fp);
- }
+ // Now try to bind to the key and certificate.
+ // Note that we use SSL_CTX_use_certificate_chain_file() which allows
+ // the certificate file to contain intermediate certificates.
- // This also holds true for the CSR.
- req = NULL;
- cer = NULL;
- pk = NULL;
- if (rsa) {
- if (pk=EVP_PKEY_new(), pk != NULL) {
- EVP_PKEY_assign_RSA(pk, rsa);
- }
- fp = fopen(CTDL_CSR_PATH, "r");
- if (fp) {
- req = PEM_read_X509_REQ(fp, NULL, NULL, NULL);
- fclose(fp);
- }
+ char *key_file[PATH_MAX];
+ char *cert_file[PATH_MAX];
+ snprintf(key_file, sizeof key_file, "%s/keys/citadel.key", ctdl_dir);
+ snprintf(cert_file, sizeof key_file, "%s/keys/citadel.cer", ctdl_dir);
- if (req) {
- if (cer = X509_new(), cer != NULL) {
-
- ASN1_INTEGER_set(X509_get_serialNumber(cer), 0);
- X509_set_issuer_name(cer, X509_REQ_get_subject_name(req));
- X509_set_subject_name(cer, X509_REQ_get_subject_name(req));
- X509_gmtime_adj(X509_get_notBefore(cer), 0);
- X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS);
-
- req_pkey = X509_REQ_get_pubkey(req);
- X509_set_pubkey(cer, req_pkey);
- EVP_PKEY_free(req_pkey);
-
- // Sign the cert
- if (!X509_sign(cer, pk, EVP_md5())) {
- syslog(LOG_WARNING, "X509_sign(): error");
- }
- else {
- // Write it to disk.
- fp = fopen(CTDL_CER_PATH, "w");
- if (fp != NULL) {
- chmod(CTDL_CER_PATH, 0600);
- PEM_write_X509(fp, cer);
- fclose(fp);
- }
- else {
- syslog(LOG_WARNING, "Cannot write key: %s", CTDL_CER_PATH);
- ShutDownWebcit();
- exit(0);
- }
- }
- X509_free(cer);
- }
- }
- RSA_free(rsa);
- }
- }
+ SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file);
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM);
- // Now try to bind to the key and certificate.
- // Note that we use SSL_CTX_use_certificate_chain_file() which allows
- // the certificate file to contain intermediate certificates.
- SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH);
- SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM);
if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
syslog(LOG_WARNING, "crypto: cannot install certificate: %s", ERR_reason_error_string(ERR_get_error()));
}
errval = SSL_get_error(newssl, retval);
ssl_error_reason = ERR_reason_error_string(ERR_get_error());
if (ssl_error_reason == NULL) {
- syslog(LOG_WARNING, "SSL_accept failed: errval=%ld, retval=%d %s", errval, retval, strerror(errval));
+ syslog(LOG_WARNING, "first SSL_accept failed: errval=%ld, retval=%d %s", errval, retval, strerror(errval));
}
else {
- syslog(LOG_WARNING, "SSL_accept failed: %s", ssl_error_reason);
+ syslog(LOG_WARNING, "first SSL_accept failed: %s", ssl_error_reason);
}
sleeeeeeeeeep(1);
retval = SSL_accept(newssl);
errval = SSL_get_error(newssl, retval);
ssl_error_reason = ERR_reason_error_string(ERR_get_error());
if (ssl_error_reason == NULL) {
- syslog(LOG_WARNING, "SSL_accept failed: errval=%ld, retval=%d (%s)", errval, retval, strerror(errval));
+ syslog(LOG_WARNING, "second SSL_accept failed: errval=%ld, retval=%d (%s)", errval, retval, strerror(errval));
}
else {
- syslog(LOG_WARNING, "SSL_accept failed: %s", ssl_error_reason);
+ syslog(LOG_WARNING, "second SSL_accept failed: %s", ssl_error_reason);
}
SSL_free(newssl);
newssl = NULL;
#include "webcit.h"
#include "webserver.h"
-int is_uds = 0;
-char serv_sock_name[PATH_MAX] = "";
-
HashList *EmbeddableMimes = NULL;
StrBuf *EmbeddableMimeStrs = NULL;
-
void SetInlinMimeRenderers(void) {
StrBuf *Buf;
int GetConnected (void) {
StrBuf *Buf;
- wcsession *WCC = WC;
-
- if (WCC->ReadBuf == NULL)
- WCC->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
- if (is_uds) /* unix domain socket */
- WCC->serv_sock = uds_connectsock(serv_sock_name);
- else /* tcp socket */
- WCC->serv_sock = tcp_connectsock(ctdlhost, ctdlport);
+
+ TRACE;
+ syslog(LOG_DEBUG, "GetConnected() has been called, and ctdl_dir is \"%s\"", ctdl_dir);
+
+ if (WC->ReadBuf == NULL) {
+ WC->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
+ }
+
+ static char serv_sock_name[PATH_MAX] = "";
+ if (IsEmptyStr(serv_sock_name)) {
+ snprintf(serv_sock_name, sizeof serv_sock_name, "%s/citadel.socket", ctdl_dir);
+ }
+ WC->serv_sock = connect_to_citadel(serv_sock_name);
- if (WCC->serv_sock < 0) {
- WCC->connected = 0;
- FreeStrBuf(&WCC->ReadBuf);
+ if (WC->serv_sock < 0) {
+ WC->connected = 0;
+ FreeStrBuf(&WC->ReadBuf);
return 1;
}
else {
long Status;
int short_status;
Buf = NewStrBuf();
- WCC->connected = 1;
+ WC->connected = 1;
StrBuf_ServGetln(Buf); /* get the server greeting */
short_status = GetServerStatus(Buf, &Status);
FreeStrBuf(&Buf);
* unless we are following X-Forwarded-For: headers
* and such a header has already turned up something.
*/
- if ( (!follow_xff) || (StrLength(WCC->Hdr->HR.browser_host) == 0) ) {
- if (WCC->Hdr->HR.browser_host == NULL) {
- WCC->Hdr->HR.browser_host = NewStrBuf();
- Put(WCC->Hdr->HTTPHeaders, HKEY("FreeMeWithTheOtherHeaders"),
- WCC->Hdr->HR.browser_host, HFreeStrBuf);
+ if ( (!follow_xff) || (StrLength(WC->Hdr->HR.browser_host) == 0) ) {
+ if (WC->Hdr->HR.browser_host == NULL) {
+ WC->Hdr->HR.browser_host = NewStrBuf();
+ Put(WC->Hdr->HTTPHeaders, HKEY("FreeMeWithTheOtherHeaders"),
+ WC->Hdr->HR.browser_host, HFreeStrBuf);
}
- locate_host(WCC->Hdr->HR.browser_host, WCC->Hdr->http_sock);
+ locate_host(WC->Hdr->HR.browser_host, WC->Hdr->http_sock);
}
- if (WCC->serv_info == NULL) {
- WCC->serv_info = get_serv_info(WCC->Hdr->HR.browser_host, WCC->Hdr->HR.user_agent);
+ if (WC->serv_info == NULL) {
+ WC->serv_info = get_serv_info(WC->Hdr->HR.browser_host, WC->Hdr->HR.user_agent);
}
- if (WCC->serv_info == NULL){
+ if (WC->serv_info == NULL){
begin_burst();
wc_printf(_("Received unexpected answer from Citadel server; bailing out."));
hprintf("HTTP/1.1 502 Bad Gateway\r\n");
end_webcit_session();
return 1;
}
- if (WCC->serv_info->serv_rev_level < MINIMUM_CIT_VERSION) {
+ if (WC->serv_info->serv_rev_level < MINIMUM_CIT_VERSION) {
begin_burst();
wc_printf(_("You are connected to a Citadel "
"server running Citadel %d.%02d. \n"
"In order to run this version of WebCit "
"you must also have Citadel %d.%02d or"
" newer.\n\n\n"),
- WCC->serv_info->serv_rev_level,
+ WC->serv_info->serv_rev_level,
0,
MINIMUM_CIT_VERSION,
0
* the returned pointer.
*/
int read_server_text(StrBuf *Buf, long *nLines) {
- wcsession *WCC = WC;
StrBuf *ReadBuf;
long nRead;
long nTotal = 0;
nlines = 0;
ReadBuf = NewStrBuf();
- while ((WCC->serv_sock!=-1) &&
+ while ((WC->serv_sock!=-1) &&
(nRead = StrBuf_ServGetln(ReadBuf), (nRead >= 0) &&
((nRead != 3)||(strcmp(ChrPtr(ReadBuf), "000") != 0))))
{
}
void tmplput_serv_admin(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
- StrBufAppendTemplate(Target, TP, WCC->serv_info->serv_sysadm, 0);
+ StrBufAppendTemplate(Target, TP, WC->serv_info->serv_sysadm, 0);
}
void tmplput_serv_nodename(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
- StrBufAppendTemplate(Target, TP, WCC->serv_info->serv_nodename, 0);
+ StrBufAppendTemplate(Target, TP, WC->serv_info->serv_nodename, 0);
}
void tmplput_serv_humannode(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
- StrBufAppendTemplate(Target, TP, WCC->serv_info->serv_humannode, 0);
+ StrBufAppendTemplate(Target, TP, WC->serv_info->serv_humannode, 0);
}
void tmplput_serv_fqdn(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
- StrBufAppendTemplate(Target, TP, WCC->serv_info->serv_fqdn, 0);
+ StrBufAppendTemplate(Target, TP, WC->serv_info->serv_fqdn, 0);
}
void tmplput_serv_software(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
- StrBufAppendTemplate(Target, TP, WCC->serv_info->serv_software, 0);
+ StrBufAppendTemplate(Target, TP, WC->serv_info->serv_software, 0);
}
void tmplput_serv_rev_level(StrBuf *Target, WCTemplputParams *TP) {
StrBufAppendPrintf(Target, "%d", WC->serv_info->serv_rev_level);
}
int conditional_serv_newuser_disabled(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return 0;
- return WCC->serv_info->serv_newuser_disabled != 0;
+ return WC->serv_info->serv_newuser_disabled != 0;
}
int conditional_serv_supports_guest(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return 0;
- return WCC->serv_info->serv_supports_guest != 0;
+ return WC->serv_info->serv_supports_guest != 0;
}
int conditional_serv_supports_openid(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return 0;
- return WCC->serv_info->serv_supports_openid != 0;
+ return WC->serv_info->serv_supports_openid != 0;
}
int conditional_serv_fulltext_enabled(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return 0;
- return WCC->serv_info->serv_fulltext_enabled != 0;
+ return WC->serv_info->serv_fulltext_enabled != 0;
}
int conditional_serv_ldap_enabled(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return 0;
- return WCC->serv_info->serv_supports_ldap != 0;
+ return WC->serv_info->serv_supports_ldap != 0;
}
void tmplput_serv_bbs_city(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if (WCC->serv_info == NULL)
+ if (WC->serv_info == NULL)
return;
StrBufAppendTemplate(Target, TP, WC->serv_info->serv_bbs_city, 0);
}
}
void tmplput_site_prefix(StrBuf *Target, WCTemplputParams *TP) {
- wcsession *WCC = WC;
- if ((WCC != NULL) && (WCC->Hdr->HostHeader != NULL)) {
- StrBufAppendTemplate(Target, TP, WCC->Hdr->HostHeader, 0);
+ if ((WC != NULL) && (WC->Hdr->HostHeader != NULL)) {
+ StrBufAppendTemplate(Target, TP, WC->Hdr->HostHeader, 0);
}
}
InitModule_SERVFUNC
(void)
{
- is_uds = strcasecmp(ctdlhost, "uds") == 0;
- if (is_uds)
- snprintf(serv_sock_name, PATH_MAX, "%s/citadel.socket", ctdlport);
-
RegisterConditional("COND:SERV:OPENID", 2, conditional_serv_supports_openid, CTX_NONE);
RegisterConditional("COND:SERV:NEWU", 2, conditional_serv_newuser_disabled, CTX_NONE);
RegisterConditional("COND:SERV:FULLTEXT_ENABLED", 2, conditional_serv_fulltext_enabled, CTX_NONE);
--- /dev/null
+/*
+ * 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.
+ */
+
+/*
+ * Uncomment this to log all communications with the Citadel server
+#define SERV_TRACE 1
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+
+long MaxRead = -1; /* should we do READ scattered or all at once? */
+
+/*
+ * register the timeout
+ */
+RETSIGTYPE timeout(int signum) {
+ syslog(LOG_WARNING, "Connection timed out; unable to reach citserver\n");
+ /* no exit here, since we need to server the connection unreachable thing. exit(3); */
+}
+
+
+/*
+ * Client side - connect to a unix domain socket
+ */
+int connect_to_citadel(char *sockpath) {
+ struct sockaddr_un addr;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ syslog(LOG_WARNING, "Can't create socket [%s]: %s\n", sockpath, strerror(errno));
+ return(-1);
+ }
+
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_WARNING, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
+ close(s);
+ return(-1);
+ }
+ return s;
+}
+
+
+/*
+ * input string from pipe
+ */
+int serv_getln(char *strbuf, int bufsize) {
+ int len;
+
+ *strbuf = '\0';
+ StrBuf_ServGetln(WC->MigrateReadLineBuf);
+ len = StrLength(WC->MigrateReadLineBuf);
+ if (len > bufsize)
+ len = bufsize - 1;
+ memcpy(strbuf, ChrPtr(WC->MigrateReadLineBuf), len);
+ FlushStrBuf(WC->MigrateReadLineBuf);
+ strbuf[len] = '\0';
+#ifdef SERV_TRACE
+ syslog(LOG_DEBUG, "%3d<<<%s\n", WC->serv_sock, strbuf);
+#endif
+ return len;
+}
+
+
+int StrBuf_ServGetln(StrBuf *buf) {
+ const char *ErrStr = NULL;
+ int rc;
+
+ if (!WC->connected)
+ return -1;
+
+ FlushStrBuf(buf);
+ rc = StrBufTCP_read_buffered_line_fast(buf,
+ WC->ReadBuf,
+ &WC->ReadPos,
+ &WC->serv_sock,
+ 5, 1,
+ &ErrStr);
+ if (rc < 0)
+ {
+ syslog(LOG_INFO, "StrBuf_ServGetln(): Server connection broken: %s\n",
+ (ErrStr)?ErrStr:"");
+ wc_backtrace(LOG_INFO);
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ }
+#ifdef SERV_TRACE
+ else
+ {
+ long pos = 0;
+ if (WC->ReadPos != NULL)
+ pos = WC->ReadPos - ChrPtr(WC->ReadBuf);
+ syslog(LOG_DEBUG, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
+ }
+#endif
+ return rc;
+}
+
+int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize) {
+ const char *ErrStr;
+ int rc;
+
+ rc = StrBufReadBLOBBuffered(buf,
+ WC->ReadBuf,
+ &WC->ReadPos,
+ &WC->serv_sock,
+ 1,
+ BlobSize,
+ NNN_TERM,
+ &ErrStr);
+ if (rc < 0) {
+ syslog(LOG_INFO, "StrBuf_ServGetBLOBBuffered(): Server connection broken: %s\n",
+ (ErrStr)?ErrStr:"");
+ wc_backtrace(LOG_INFO);
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ }
+#ifdef SERV_TRACE
+ else
+ syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
+#endif
+
+ return rc;
+}
+
+int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize) {
+ const char *ErrStr;
+ int rc;
+
+ WC->ReadPos = NULL;
+ rc = StrBufReadBLOB(buf, &WC->serv_sock, 1, BlobSize, &ErrStr);
+ if (rc < 0) {
+ syslog(LOG_INFO, "StrBuf_ServGetBLOB(): Server connection broken: %s\n",
+ (ErrStr)?ErrStr:"");
+ wc_backtrace(LOG_INFO);
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ }
+#ifdef SERV_TRACE
+ else
+ syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
+#endif
+
+ return rc;
+}
+
+
+void FlushReadBuf (void) {
+ long len;
+ const char *pch;
+ const char *pche;
+
+ len = StrLength(WC->ReadBuf);
+ if ((len > 0) && (WC->ReadPos != NULL) && (WC->ReadPos != StrBufNOTNULL)) {
+ pch = ChrPtr(WC->ReadBuf);
+ pche = pch + len;
+ if (WC->ReadPos != pche) {
+ syslog(LOG_ERR,
+ "ERROR: somebody didn't eat his soup! Remaing Chars: %ld [%s]\n",
+ (long)(pche - WC->ReadPos),
+ pche
+ );
+ syslog(LOG_ERR,
+ "--------------------------------------------------------------------------------\n"
+ "Whole buf: [%s]\n"
+ "--------------------------------------------------------------------------------\n",
+ pch);
+ AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
+ }
+ }
+
+ FlushStrBuf(WC->ReadBuf);
+ WC->ReadPos = NULL;
+
+
+}
+
+
+/*
+ * send binary to server
+ * buf the buffer to write to citadel server
+ * nbytes how many bytes to send to citadel server
+ */
+int serv_write(const char *buf, int nbytes) {
+ int bytes_written = 0;
+ int retval;
+
+ FlushReadBuf();
+ while (bytes_written < nbytes) {
+ retval = write(WC->serv_sock, &buf[bytes_written], nbytes - bytes_written);
+ if (retval < 1) {
+ const char *ErrStr = strerror(errno);
+ syslog(LOG_INFO, "serv_write(): Server connection broken: %s\n", (ErrStr)?ErrStr:"");
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ return 0;
+ }
+ bytes_written = bytes_written + retval;
+ }
+ return 1;
+}
+
+
+/*
+ * send line to server
+ * string the line to send to the citadel server
+ */
+int serv_puts(const char *string) {
+#ifdef SERV_TRACE
+ syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, string);
+#endif
+ FlushReadBuf();
+
+ if (!serv_write(string, strlen(string)))
+ return 0;
+ return serv_write("\n", 1);
+}
+
+/*
+ * send line to server
+ * string the line to send to the citadel server
+ */
+int serv_putbuf(const StrBuf *string) {
+#ifdef SERV_TRACE
+ syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
+#endif
+ FlushReadBuf();
+
+ if (!serv_write(ChrPtr(string), StrLength(string)))
+ return 0;
+ return serv_write("\n", 1);
+}
+
+
+/*
+ * convenience function to send stuff to the server
+ * format the formatstring
+ * ... the entities to insert into format
+ */
+int serv_printf(const char *format,...) {
+ va_list arg_ptr;
+ char buf[SIZ];
+ size_t len;
+ int rc;
+
+ FlushReadBuf();
+
+ va_start(arg_ptr, format);
+ vsnprintf(buf, sizeof buf, format, arg_ptr);
+ va_end(arg_ptr);
+
+ len = strlen(buf);
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ rc = serv_write(buf, len);
+#ifdef SERV_TRACE
+ syslog(LOG_DEBUG, ">>>%s", buf);
+#endif
+ return rc;
+}
+
+
+/*
+ * Read binary data from server into memory using a series of server READ commands.
+ * returns the read content as StrBuf
+ */
+int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf) {
+ size_t bytes_read = 0;
+ size_t this_block = 0;
+ int rc = 6;
+ int ServerRc = 6;
+
+ if (Ret == NULL) {
+ return -1;
+ }
+
+ while ((bytes_read < total_len) && (ServerRc == 6)) {
+
+ if (WC->serv_sock==-1) {
+ FlushStrBuf(Ret);
+ return -1;
+ }
+
+ serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, bytes_read, total_len-bytes_read);
+ if ( (rc = StrBuf_ServGetln(Buf) > 0) && (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) {
+ if (rc < 0)
+ return rc;
+ StrBufCutLeft(Buf, 4);
+ this_block = StrTol(Buf);
+ rc = StrBuf_ServGetBLOBBuffered(Ret, this_block);
+ if (rc < 0) {
+ syslog(LOG_INFO, "Server connection broken during download\n");
+ wc_backtrace(LOG_INFO);
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ return rc;
+ }
+ bytes_read += rc;
+ }
+ }
+
+ return StrLength(Ret);
+}
+
+
+int client_write(StrBuf *ThisBuf) {
+ const char *ptr, *eptr;
+ long count;
+ ssize_t res = 0;
+ fd_set wset;
+ int fdflags;
+
+ ptr = ChrPtr(ThisBuf);
+ count = StrLength(ThisBuf);
+ eptr = ptr + count;
+
+ fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
+
+ while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
+ if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
+ FD_ZERO(&wset);
+ FD_SET(WC->Hdr->http_sock, &wset);
+ if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
+ syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ if ((WC->Hdr->http_sock == -1) || ((res = write(WC->Hdr->http_sock, ptr, count)), (res == -1))) {
+ syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
+ wc_backtrace(LOG_INFO);
+ return -1;
+ }
+ count -= res;
+ ptr += res;
+ }
+ return 0;
+}
+
+
+int read_serv_chunk( StrBuf *Buf, size_t total_len, size_t *bytes_read) {
+ int rc;
+ int ServerRc;
+
+ serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, *bytes_read, total_len-(*bytes_read));
+ if ( (rc = StrBuf_ServGetln(Buf) > 0) && (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) {
+ size_t this_block = 0;
+
+ if (rc < 0)
+ return rc;
+
+ StrBufCutLeft(Buf, 4);
+ this_block = StrTol(Buf);
+ rc = StrBuf_ServGetBLOBBuffered(WC->WBuf, this_block);
+ if (rc < 0) {
+ syslog(LOG_INFO, "Server connection broken during download\n");
+ wc_backtrace(LOG_INFO);
+ if (WC->serv_sock > 0) close(WC->serv_sock);
+ WC->serv_sock = (-1);
+ WC->connected = 0;
+ WC->logged_in = 0;
+ return rc;
+ }
+ *bytes_read += rc;
+ }
+ return 6;
+}
+
+static inline int send_http(StrBuf *Buf) {
+#ifdef HAVE_OPENSSL
+ if (is_https)
+ return client_write_ssl(Buf);
+ else
+#endif
+ return client_write(Buf);
+}
+/*
+ * Read binary data from server into memory using a series of server READ commands.
+ * returns the read content as StrBuf
+ */
+void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime) {
+ int ServerRc = 6;
+ size_t bytes_read = 0;
+ int first = 1;
+ int client_con_state = 0;
+ int chunked = 0;
+ int is_gzip = 0;
+ const char *Err = NULL;
+ StrBuf *BufHeader = NULL;
+ StrBuf *Buf;
+ StrBuf *pBuf = NULL;
+ vStreamT *SC = NULL;
+ IOBuffer ReadBuffer;
+ IOBuffer WriteBuffer;
+
+
+ Buf = NewStrBuf();
+
+ if (WC->Hdr->HaveRange) {
+ WC->Hdr->HaveRange++;
+ WC->Hdr->TotalBytes = total_len;
+ /* open range? or beyound file border? correct the numbers. */
+ if ((WC->Hdr->RangeTil == -1) || (WC->Hdr->RangeTil>= total_len))
+ WC->Hdr->RangeTil = total_len - 1;
+ bytes_read = WC->Hdr->RangeStart;
+ total_len = WC->Hdr->RangeTil;
+ }
+ else
+ chunked = total_len > SIZ * 10; /* TODO: disallow for HTTP / 1.0 */
+
+ if (chunked) {
+ BufHeader = NewStrBuf();
+ }
+
+ if ((detect_mime != 0) && (bytes_read != 0)) {
+ /* need to read first chunk to detect mime, though the client doesn't care */
+ size_t bytes_read = 0;
+ const char *CT;
+
+ ServerRc = read_serv_chunk(
+ Buf,
+ total_len,
+ &bytes_read);
+
+ if (ServerRc != 6)
+ {
+ FreeStrBuf(&BufHeader);
+ FreeStrBuf(&Buf);
+ return;
+ }
+ CT = GuessMimeType(SKEY(WC->WBuf));
+ FlushStrBuf(WC->WBuf);
+ StrBufPlain(MimeType, CT, -1);
+ CheckGZipCompressionAllowed(SKEY(MimeType));
+ detect_mime = 0;
+ FreeStrBuf(&Buf);
+ }
+
+ memset(&WriteBuffer, 0, sizeof(IOBuffer));
+ if (chunked && !DisableGzip && WC->Hdr->HR.gzip_ok) {
+ is_gzip = 1;
+ SC = StrBufNewStreamContext (eZLibEncode, &Err);
+ if (SC == NULL) {
+ syslog(LOG_ERR, "Error while initializing stream context: %s", Err);
+ FreeStrBuf(&Buf);
+ return;
+ }
+
+ memset(&ReadBuffer, 0, sizeof(IOBuffer));
+ ReadBuffer.Buf = WC->WBuf;
+
+ WriteBuffer.Buf = NewStrBufPlain(NULL, SIZ*2);;
+ pBuf = WriteBuffer.Buf;
+ }
+ else {
+ pBuf = WC->WBuf;
+ }
+
+ if (!detect_mime) {
+ http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
+
+ if (send_http(WC->HBuf) < 0) {
+ FreeStrBuf(&Buf);
+ FreeStrBuf(&WriteBuffer.Buf);
+ FreeStrBuf(&BufHeader);
+ if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
+ syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
+ }
+ return;
+ }
+ }
+
+ while ((bytes_read < total_len) && (ServerRc == 6) && (client_con_state == 0)) {
+
+ if (WC->serv_sock==-1) {
+ FlushStrBuf(WC->WBuf);
+ FreeStrBuf(&Buf);
+ FreeStrBuf(&WriteBuffer.Buf);
+ FreeStrBuf(&BufHeader);
+ StrBufDestroyStreamContext(eZLibEncode, &SC, &Err);
+ if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
+ syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
+ }
+ return;
+ }
+
+ ServerRc = read_serv_chunk(
+ Buf,
+ total_len,
+ &bytes_read);
+ if (ServerRc != 6)
+ break;
+
+ if (detect_mime) {
+ const char *CT;
+ detect_mime = 0;
+
+ CT = GuessMimeType(SKEY(WC->WBuf));
+ StrBufPlain(MimeType, CT, -1);
+ if (is_gzip) {
+ CheckGZipCompressionAllowed(SKEY(MimeType));
+ is_gzip = WC->Hdr->HR.gzip_ok;
+ }
+ http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
+
+ client_con_state = send_http(WC->HBuf);
+ }
+
+ if (is_gzip) {
+ int done = (bytes_read == total_len);
+ while ((IOBufferStrLength(&ReadBuffer) > 0) && (client_con_state == 0)) {
+ int rc;
+
+ do {
+ rc = StrBufStreamTranscode(eZLibEncode, &WriteBuffer, &ReadBuffer, NULL, -1, SC, done, &Err);
+
+ if (StrLength (pBuf) > 0) {
+ StrBufPrintf(BufHeader, "%s%x\r\n",
+ (first)?"":"\r\n",
+ StrLength (pBuf));
+ first = 0;
+ client_con_state = send_http(BufHeader);
+ if (client_con_state == 0) {
+ client_con_state = send_http(pBuf);
+ }
+ FlushStrBuf(pBuf);
+ }
+ } while ((rc == 1) && (StrLength(pBuf) > 0));
+ }
+ FlushStrBuf(WC->WBuf);
+ }
+ else {
+ if ((chunked) && (client_con_state == 0)) {
+ StrBufPrintf(BufHeader, "%s%x\r\n",
+ (first)?"":"\r\n",
+ StrLength (pBuf));
+ first = 0;
+ client_con_state = send_http(BufHeader);
+ }
+
+ if (client_con_state == 0)
+ client_con_state = send_http(pBuf);
+
+ FlushStrBuf(pBuf);
+ }
+ }
+
+ if (SC && StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
+ syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
+ }
+ FreeStrBuf(&WriteBuffer.Buf);
+ if ((chunked) && (client_con_state == 0)) {
+ StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n"));
+ if (send_http(BufHeader) < 0) {
+ FreeStrBuf(&Buf);
+ FreeStrBuf(&BufHeader);
+ return;
+ }
+ }
+ FreeStrBuf(&BufHeader);
+ FreeStrBuf(&Buf);
+}
+
+int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) {
+ const char *Error;
+#ifdef HAVE_OPENSSL
+ const char *pch, *pchs;
+ int rlen, len, retval = 0;
+
+ if (is_https) {
+ int ntries = 0;
+ if (StrLength(Hdr->ReadBuf) > 0) {
+ pchs = ChrPtr(Hdr->ReadBuf);
+ pch = strchr(pchs, '\n');
+ if (pch != NULL) {
+ rlen = 0;
+ len = pch - pchs;
+ if (len > 0 && (*(pch - 1) == '\r') )
+ rlen ++;
+ StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
+ StrBufCutLeft(Hdr->ReadBuf, len + 1);
+ return len - rlen;
+ }
+ }
+
+ while (retval == 0) {
+ pch = NULL;
+ pchs = ChrPtr(Hdr->ReadBuf);
+ if (*pchs != '\0')
+ pch = strchr(pchs, '\n');
+ if (pch == NULL) {
+ retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
+ pchs = ChrPtr(Hdr->ReadBuf);
+ pch = strchr(pchs, '\n');
+ if (pch == NULL)
+ retval = 0;
+ }
+ if (retval == 0) {
+ sleeeeeeeeeep(1);
+ ntries ++;
+ }
+ if (ntries > 10)
+ return 0;
+ }
+ if ((retval > 0) && (pch != NULL)) {
+ rlen = 0;
+ len = pch - pchs;
+ if (len > 0 && (*(pch - 1) == '\r') )
+ rlen ++;
+ StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
+ StrBufCutLeft(Hdr->ReadBuf, len + 1);
+ return len - rlen;
+
+ }
+ else
+ return -1;
+ }
+ else
+#endif
+ return StrBufTCP_read_buffered_line_fast(Target,
+ Hdr->ReadBuf,
+ &Hdr->Pos,
+ &Hdr->http_sock,
+ 5,
+ 1,
+ &Error);
+}
+
+
+/*
+ * This is a generic function to set up a master socket for listening on
+ * a TCP port. The server shuts down if the bind fails. (IPv4/IPv6 version)
+ *
+ * ip_addr IP address to bind
+ * port_number port number to bind
+ * queue_len number of incoming connections to allow in the queue
+ */
+int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len) {
+ const char *ipv4broadcast = "0.0.0.0";
+ int IsDefault = 0;
+ struct protoent *p;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin4;
+ int s, i, b;
+ int ip_version = 6;
+
+retry:
+ memset(&sin6, 0, sizeof(sin6));
+ memset(&sin4, 0, sizeof(sin4));
+ sin6.sin6_family = AF_INET6;
+ sin4.sin_family = AF_INET;
+
+ if ( (ip_addr == NULL) /* any IPv6 */
+ || (IsEmptyStr(ip_addr))
+ || (!strcmp(ip_addr, "*"))
+ ) {
+ IsDefault = 1;
+ ip_version = 6;
+ sin6.sin6_addr = in6addr_any;
+ }
+ else if (!strcmp(ip_addr, "0.0.0.0")) /* any IPv4 */
+ {
+ ip_version = 4;
+ sin4.sin_addr.s_addr = INADDR_ANY;
+ }
+ else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) /* specific IPv4 */
+ {
+ ip_version = 4;
+ if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
+ syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
+ return (-WC_EXIT_BIND);
+ }
+ }
+ else /* specific IPv6 */
+ {
+ ip_version = 6;
+ if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
+ syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
+ return (-WC_EXIT_BIND);
+ }
+ }
+
+ if (port_number == 0) {
+ syslog(LOG_WARNING, "Cannot start: no port number specified.\n");
+ return (-WC_EXIT_BIND);
+ }
+ sin6.sin6_port = htons((u_short) port_number);
+ sin4.sin_port = htons((u_short) port_number);
+
+ p = getprotobyname("tcp");
+
+ s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
+ if (s < 0) {
+ if (IsDefault && (errno == EAFNOSUPPORT))
+ {
+ s = 0;
+ ip_addr = ipv4broadcast;
+ goto retry;
+ }
+ syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno));
+ return (-WC_EXIT_BIND);
+ }
+ /* Set some socket options that make sense. */
+ i = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
+
+ if (ip_version == 6) {
+ b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
+ }
+ else {
+ b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
+ }
+
+ if (b < 0) {
+ syslog(LOG_ERR, "Can't bind: %s\n", strerror(errno));
+ close(s);
+ return (-WC_EXIT_BIND);
+ }
+
+ if (listen(s, queue_len) < 0) {
+ syslog(LOG_ERR, "Can't listen: %s\n", strerror(errno));
+ close(s);
+ return (-WC_EXIT_BIND);
+ }
+ return (s);
+}
+
+
+/*
+ * Create a Unix domain socket and listen on it
+ * sockpath - file name of the unix domain socket
+ * queue_len - Number of incoming connections to allow in the queue
+ */
+int webcit_uds_server(char *sockpath, int queue_len) {
+ struct sockaddr_un addr;
+ int s;
+ int i;
+ int actual_queue_len;
+
+ actual_queue_len = queue_len;
+ if (actual_queue_len < 5) actual_queue_len = 5;
+
+ i = unlink(sockpath);
+ if ((i != 0) && (errno != ENOENT)) {
+ syslog(LOG_WARNING, "webcit: can't unlink %s: %s\n",
+ sockpath, strerror(errno));
+ return (-WC_EXIT_BIND);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
+ return (-WC_EXIT_BIND);
+ }
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ syslog(LOG_WARNING, "webcit: Can't bind: %s\n", strerror(errno));
+ close(s);
+ return (-WC_EXIT_BIND);
+ }
+
+ if (listen(s, actual_queue_len) < 0) {
+ syslog(LOG_WARNING, "webcit: Can't listen: %s\n", strerror(errno));
+ close(s);
+ return (-WC_EXIT_BIND);
+ }
+
+ chmod(sockpath, 0777);
+ return(s);
+}
+
+
+/*
+ * 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 client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout) {
+ const char *Error;
+ int retval = 0;
+
+#ifdef HAVE_OPENSSL
+ if (is_https) {
+ long bufremain = 0;
+ long baselen;
+
+ baselen = StrLength(Target);
+
+ if (Hdr->Pos == NULL) {
+ Hdr->Pos = ChrPtr(Hdr->ReadBuf);
+ }
+
+ if (StrLength(Hdr->ReadBuf) > 0) {
+ bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
+
+ if (bytes < bufremain)
+ bufremain = bytes;
+ StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
+ StrBufCutLeft(Hdr->ReadBuf, bufremain);
+ }
+
+ if (bytes > bufremain) {
+ while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) &&
+ (retval >= 0))
+ retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
+ if (retval >= 0) {
+ StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
+ return 1;
+ }
+ else {
+ syslog(LOG_INFO, "client_read_ssl() failed\n");
+ return -1;
+ }
+ }
+ else
+ return 1;
+ }
+#endif
+ retval = StrBufReadBLOBBuffered(Target,
+ Hdr->ReadBuf,
+ &Hdr->Pos,
+ &Hdr->http_sock,
+ 1,
+ bytes,
+ O_TERM,
+ &Error);
+ if (retval < 0) {
+ syslog(LOG_INFO, "client_read() failed: %s\n", Error);
+ wc_backtrace(LOG_DEBUG);
+ return retval;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Begin buffering HTTP output so we can transmit it all in one write operation later.
+ */
+void begin_burst(void)
+{
+ if (WC->WBuf == NULL) {
+ WC->WBuf = NewStrBufPlain(NULL, 32768);
+ }
+}
+
+
+/*
+ * Finish buffering HTTP output. [Compress using zlib and] output with a Content-Length: header.
+ */
+long end_burst(void)
+{
+ const char *ptr, *eptr;
+ long count;
+ ssize_t res = 0;
+ fd_set wset;
+ int fdflags;
+
+ if (!DisableGzip && (WC->Hdr->HR.gzip_ok))
+ {
+ if (CompressBuffer(WC->WBuf) > 0)
+ hprintf("Content-encoding: gzip\r\n");
+ else {
+ syslog(LOG_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
+ wc_backtrace(LOG_INFO);
+ }
+ }
+
+ if (WC->WFBuf != NULL) {
+ WildFireSerializePayload(WC->WFBuf, WC->HBuf, &WC->Hdr->nWildfireHeaders, NULL);
+ FreeStrBuf(&WC->WFBuf);
+ }
+
+ if (WC->Hdr->HR.prohibit_caching)
+ hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
+ hprintf("Content-length: %d\r\n\r\n", StrLength(WC->WBuf));
+
+ ptr = ChrPtr(WC->HBuf);
+ count = StrLength(WC->HBuf);
+ eptr = ptr + count;
+
+#ifdef HAVE_OPENSSL
+ if (is_https) {
+ client_write_ssl(WC->HBuf);
+ client_write_ssl(WC->WBuf);
+ return (count);
+ }
+#endif
+
+ if (WC->Hdr->http_sock == -1) {
+ return -1;
+ }
+ fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
+
+ while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
+ if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
+ FD_ZERO(&wset);
+ FD_SET(WC->Hdr->http_sock, &wset);
+ if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
+ syslog(LOG_DEBUG, "client_write: Socket select failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ if ((WC->Hdr->http_sock == -1) ||
+ (res = write(WC->Hdr->http_sock,
+ ptr,
+ count)) == -1) {
+ syslog(LOG_DEBUG, "client_write: Socket write failed (%s)\n", strerror(errno));
+ wc_backtrace(LOG_INFO);
+ return res;
+ }
+ count -= res;
+ ptr += res;
+ }
+
+ ptr = ChrPtr(WC->WBuf);
+ count = StrLength(WC->WBuf);
+ eptr = ptr + count;
+
+ while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
+ if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
+ FD_ZERO(&wset);
+ FD_SET(WC->Hdr->http_sock, &wset);
+ if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
+ syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ if ((WC->Hdr->http_sock == -1) ||
+ (res = write(WC->Hdr->http_sock,
+ ptr,
+ count)) == -1) {
+ syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
+ wc_backtrace(LOG_INFO);
+ return res;
+ }
+ count -= res;
+ ptr += res;
+ }
+
+ return StrLength(WC->WBuf);
+}
+
+
+/*
+ * lingering_close() a`la Apache. see
+ * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale
+ */
+int lingering_close(int fd)
+{
+ char buf[SIZ];
+ int i;
+ fd_set set;
+ struct timeval tv, start;
+
+ gettimeofday(&start, NULL);
+ if (fd == -1)
+ return -1;
+ shutdown(fd, 1);
+ do {
+ do {
+ gettimeofday(&tv, NULL);
+ tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
+ tv.tv_usec = start.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) {
+ tv.tv_sec--;
+ tv.tv_usec += 1000000;
+ }
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ i = select(fd + 1, &set, NULL, NULL, &tv);
+ } while (i == -1 && errno == EINTR);
+
+ if (i <= 0)
+ break;
+
+ i = read(fd, buf, sizeof buf);
+ } while (i != 0 && (i != -1 || errno == EINTR));
+
+ return close(fd);
+}
+
+void
+HttpNewModule_TCPSOCKETS
+(ParsedHttpHdrs *httpreq)
+{
+
+ httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
+}
+
+void
+HttpDetachModule_TCPSOCKETS
+(ParsedHttpHdrs *httpreq)
+{
+
+ FlushStrBuf(httpreq->ReadBuf);
+ ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
+}
+
+void
+HttpDestroyModule_TCPSOCKETS
+(ParsedHttpHdrs *httpreq)
+{
+
+ FreeStrBuf(&httpreq->ReadBuf);
+}
+
+
+void
+SessionNewModule_TCPSOCKETS
+(wcsession *sess)
+{
+ sess->CLineBuf = NewStrBuf();
+ sess->MigrateReadLineBuf = NewStrBuf();
+}
+
+void
+SessionDestroyModule_TCPSOCKETS
+(wcsession *sess)
+{
+ FreeStrBuf(&sess->CLineBuf);
+ FreeStrBuf(&sess->ReadBuf);
+ sess->connected = 0;
+ sess->ReadPos = NULL;
+ FreeStrBuf(&sess->MigrateReadLineBuf);
+ if (sess->serv_sock > 0) {
+ syslog(LOG_DEBUG, "Closing socket %d", sess->serv_sock);
+ close(sess->serv_sock);
+ }
+ sess->serv_sock = -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1996-2013 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 connect_to_citadel(char *);
+int connectsock(char *, char *);
+int serv_getln(char *strbuf, int bufsize);
+int StrBuf_ServGetln(StrBuf *buf);
+
+/*
+ * parse & check the server reply
+ *
+ * Line the line containing the server reply
+ * FullState if you need more than just the major number, this is returns it. Ignored if NULL.
+ * PutImportantMessage if you want to forward the text part of the server reply to the user, specify 1;
+ * the result will be put into the 'Important Message' framework.
+ * MajorOK in case of which major number not to put the ImportantMessage? 0 for all.
+ *
+ * returns the most significant digit of the server status
+ */
+
+int GetServerStatusMsg(StrBuf *Line, long* FullState, int PutImportantMessage, int MajorOK);
+
+/*
+ * to migrate old calls....
+ */
+#define GetServerStatus(a, b) GetServerStatusMsg(a, b, 0, 0)
+
+int serv_puts(const char *string);
+
+int serv_write(const char *buf, int nbytes);
+int serv_putbuf(const StrBuf *string);
+int serv_printf(const char *format,...)__attribute__((__format__(__printf__,1,2)));
+int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf);
+void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime);
+int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize);
+int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize);
+int read_server_text(StrBuf *Buf, long *nLines);
+
+void text_to_server(char *ptr);
+void text_to_server_qp(const StrBuf *SendMeEncoded);
+void server_to_text(void);
+int lingering_close(int fd);
extern void *housekeeping_loop(void);
extern void do_housekeeping(void);
-char ctdl_key_dir[PATH_MAX]=SSL_DIR;
-char file_crpt_file_key[PATH_MAX]="";
-char file_crpt_file_csr[PATH_MAX]="";
-char file_crpt_file_cer[PATH_MAX]="";
char file_etc_mimelist[PATH_MAX]="";
char etc_dir[PATH_MAX];
StripSlashes(static_icon_dir, 1);
StripSlashes(static_local_dir, 1);
- snprintf(file_crpt_file_key,
- sizeof file_crpt_file_key,
- "%s/citadel.key",
- ctdl_key_dir);
- snprintf(file_crpt_file_csr,
- sizeof file_crpt_file_csr,
- "%s/citadel.csr",
- ctdl_key_dir);
- snprintf(file_crpt_file_cer,
- sizeof file_crpt_file_cer,
- "%s/citadel.cer",
- ctdl_key_dir);
-
-
basedir=ETCDIR;
COMPUTE_DIRECTORY(etc_dir);
StripSlashes(etc_dir, 1);
+++ /dev/null
-/*
- * 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.
- */
-
-/*
- * Uncomment this to log all communications with the Citadel server
-#define SERV_TRACE 1
- */
-
-#include "webcit.h"
-#include "webserver.h"
-
-long MaxRead = -1; /* should we do READ scattered or all at once? */
-
-/*
- * register the timeout
- */
-RETSIGTYPE timeout(int signum)
-{
- syslog(LOG_WARNING, "Connection timed out; unable to reach citserver\n");
- /* no exit here, since we need to server the connection unreachable thing. exit(3); */
-}
-
-
-/*
- * Client side - connect to a unix domain socket
- */
-int uds_connectsock(char *sockpath)
-{
- struct sockaddr_un addr;
- int s;
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s < 0) {
- syslog(LOG_WARNING, "Can't create socket [%s]: %s\n", sockpath, strerror(errno));
- return(-1);
- }
-
- if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- syslog(LOG_WARNING, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
- close(s);
- return(-1);
- }
- return s;
-}
-
-
-/*
- * TCP client - connect to a host/port
- */
-int tcp_connectsock(char *host, char *service)
-{
- struct in6_addr serveraddr;
- struct addrinfo hints;
- struct addrinfo *res = NULL;
- struct addrinfo *ai = NULL;
- int rc = (-1);
- int s = (-1);
-
- if ((host == NULL) || IsEmptyStr(host))
- return (-1);
- if ((service == NULL) || IsEmptyStr(service))
- return (-1);
-
- syslog(LOG_DEBUG, "tcp_connectsock(%s,%s)\n", host, service);
-
- 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_DEBUG, "%s: %s\n", host, gai_strerror(rc));
- freeaddrinfo(res);
- 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) {
-
- if (ai->ai_family == AF_INET) syslog(LOG_DEBUG, "Trying IPv4\n");
- else if (ai->ai_family == AF_INET6) syslog(LOG_DEBUG, "Trying IPv6\n");
- else syslog(LOG_WARNING, "This is going to fail.\n");
-
- s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (s < 0) {
- syslog(LOG_WARNING, "socket() failed: %s\n", strerror(errno));
- freeaddrinfo(res);
- return(-1);
- }
- rc = connect(s, ai->ai_addr, ai->ai_addrlen);
- if (rc >= 0) {
- int fdflags;
- freeaddrinfo(res);
-
- fdflags = fcntl(rc, F_GETFL);
- if (fdflags < 0) {
- syslog(LOG_ERR,
- "unable to get socket %d flags! %s \n",
- rc,
- strerror(errno));
- close(rc);
- return -1;
- }
- fdflags = fdflags | O_NONBLOCK;
- if (fcntl(rc, F_SETFL, fdflags) < 0) {
- syslog(LOG_ERR,
- "unable to set socket %d nonblocking flags! %s \n",
- rc,
- strerror(errno));
- close(s);
- return -1;
- }
-
- return(s);
- }
- else {
- syslog(LOG_WARNING, "connect() failed: %s\n", strerror(errno));
- close(s);
- }
- }
- freeaddrinfo(res);
- return(-1);
-}
-
-
-/*
- * input string from pipe
- */
-int serv_getln(char *strbuf, int bufsize)
-{
- int len;
-
- *strbuf = '\0';
- StrBuf_ServGetln(WC->MigrateReadLineBuf);
- len = StrLength(WC->MigrateReadLineBuf);
- if (len > bufsize)
- len = bufsize - 1;
- memcpy(strbuf, ChrPtr(WC->MigrateReadLineBuf), len);
- FlushStrBuf(WC->MigrateReadLineBuf);
- strbuf[len] = '\0';
-#ifdef SERV_TRACE
- syslog(LOG_DEBUG, "%3d<<<%s\n", WC->serv_sock, strbuf);
-#endif
- return len;
-}
-
-
-int StrBuf_ServGetln(StrBuf *buf)
-{
- const char *ErrStr = NULL;
- int rc;
-
- if (!WC->connected)
- return -1;
-
- FlushStrBuf(buf);
- rc = StrBufTCP_read_buffered_line_fast(buf,
- WC->ReadBuf,
- &WC->ReadPos,
- &WC->serv_sock,
- 5, 1,
- &ErrStr);
- if (rc < 0)
- {
- syslog(LOG_INFO, "StrBuf_ServGetln(): Server connection broken: %s\n",
- (ErrStr)?ErrStr:"");
- wc_backtrace(LOG_INFO);
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- }
-#ifdef SERV_TRACE
- else
- {
- long pos = 0;
- if (WC->ReadPos != NULL)
- pos = WC->ReadPos - ChrPtr(WC->ReadBuf);
- syslog(LOG_DEBUG, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
- }
-#endif
- return rc;
-}
-
-int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize)
-{
- const char *ErrStr;
- int rc;
-
- rc = StrBufReadBLOBBuffered(buf,
- WC->ReadBuf,
- &WC->ReadPos,
- &WC->serv_sock,
- 1,
- BlobSize,
- NNN_TERM,
- &ErrStr);
- if (rc < 0)
- {
- syslog(LOG_INFO, "StrBuf_ServGetBLOBBuffered(): Server connection broken: %s\n",
- (ErrStr)?ErrStr:"");
- wc_backtrace(LOG_INFO);
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- }
-#ifdef SERV_TRACE
- else
- syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
-#endif
-
- return rc;
-}
-
-int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize)
-{
- const char *ErrStr;
- int rc;
-
- WC->ReadPos = NULL;
- rc = StrBufReadBLOB(buf, &WC->serv_sock, 1, BlobSize, &ErrStr);
- if (rc < 0)
- {
- syslog(LOG_INFO, "StrBuf_ServGetBLOB(): Server connection broken: %s\n",
- (ErrStr)?ErrStr:"");
- wc_backtrace(LOG_INFO);
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- }
-#ifdef SERV_TRACE
- else
- syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
-#endif
-
- return rc;
-}
-
-
-void FlushReadBuf (void)
-{
- long len;
- const char *pch;
- const char *pche;
-
- len = StrLength(WC->ReadBuf);
- if ((len > 0) &&
- (WC->ReadPos != NULL) &&
- (WC->ReadPos != StrBufNOTNULL))
-
- {
- pch = ChrPtr(WC->ReadBuf);
- pche = pch + len;
- if (WC->ReadPos != pche)
- {
- syslog(LOG_ERR,
- "ERROR: somebody didn't eat his soup! Remaing Chars: %ld [%s]\n",
- (long)(pche - WC->ReadPos),
- pche
- );
- syslog(LOG_ERR,
- "--------------------------------------------------------------------------------\n"
- "Whole buf: [%s]\n"
- "--------------------------------------------------------------------------------\n",
- pch);
- AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
- }
- }
-
- FlushStrBuf(WC->ReadBuf);
- WC->ReadPos = NULL;
-
-
-}
-
-
-/*
- * send binary to server
- * buf the buffer to write to citadel server
- * nbytes how many bytes to send to citadel server
- */
-int serv_write(const char *buf, int nbytes)
-{
- int bytes_written = 0;
- int retval;
-
- FlushReadBuf();
- while (bytes_written < nbytes) {
- retval = write(WC->serv_sock, &buf[bytes_written],
- nbytes - bytes_written);
- if (retval < 1) {
- const char *ErrStr = strerror(errno);
- syslog(LOG_INFO, "serv_write(): Server connection broken: %s\n",
- (ErrStr)?ErrStr:"");
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- return 0;
- }
- bytes_written = bytes_written + retval;
- }
- return 1;
-}
-
-
-/*
- * send line to server
- * string the line to send to the citadel server
- */
-int serv_puts(const char *string)
-{
-#ifdef SERV_TRACE
- syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, string);
-#endif
- FlushReadBuf();
-
- if (!serv_write(string, strlen(string)))
- return 0;
- return serv_write("\n", 1);
-}
-
-/*
- * send line to server
- * string the line to send to the citadel server
- */
-int serv_putbuf(const StrBuf *string)
-{
-#ifdef SERV_TRACE
- syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
-#endif
- FlushReadBuf();
-
- if (!serv_write(ChrPtr(string), StrLength(string)))
- return 0;
- return serv_write("\n", 1);
-}
-
-
-/*
- * convenience function to send stuff to the server
- * format the formatstring
- * ... the entities to insert into format
- */
-int serv_printf(const char *format,...)
-{
- va_list arg_ptr;
- char buf[SIZ];
- size_t len;
- int rc;
-
- FlushReadBuf();
-
- va_start(arg_ptr, format);
- vsnprintf(buf, sizeof buf, format, arg_ptr);
- va_end(arg_ptr);
-
- len = strlen(buf);
- buf[len++] = '\n';
- buf[len] = '\0';
- rc = serv_write(buf, len);
-#ifdef SERV_TRACE
- syslog(LOG_DEBUG, ">>>%s", buf);
-#endif
- return rc;
-}
-
-
-/*
- * Read binary data from server into memory using a series of server READ commands.
- * returns the read content as StrBuf
- */
-int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf)
-{
- size_t bytes_read = 0;
- size_t this_block = 0;
- int rc = 6;
- int ServerRc = 6;
-
- if (Ret == NULL) {
- return -1;
- }
-
- while ((bytes_read < total_len) && (ServerRc == 6)) {
-
- if (WC->serv_sock==-1) {
- FlushStrBuf(Ret);
- return -1;
- }
-
- serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, bytes_read, total_len-bytes_read);
- if ( (rc = StrBuf_ServGetln(Buf) > 0) &&
- (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) )
- {
- if (rc < 0)
- return rc;
- StrBufCutLeft(Buf, 4);
- this_block = StrTol(Buf);
- rc = StrBuf_ServGetBLOBBuffered(Ret, this_block);
- if (rc < 0) {
- syslog(LOG_INFO, "Server connection broken during download\n");
- wc_backtrace(LOG_INFO);
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- return rc;
- }
- bytes_read += rc;
- }
- }
-
- return StrLength(Ret);
-}
-
-
-int client_write(StrBuf *ThisBuf)
-{
- const char *ptr, *eptr;
- long count;
- ssize_t res = 0;
- fd_set wset;
- int fdflags;
-
- ptr = ChrPtr(ThisBuf);
- count = StrLength(ThisBuf);
- eptr = ptr + count;
-
- fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
-
- while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
- if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
- FD_ZERO(&wset);
- FD_SET(WC->Hdr->http_sock, &wset);
- if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
- syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
- return -1;
- }
- }
-
- if ((WC->Hdr->http_sock == -1) ||
- ((res = write(WC->Hdr->http_sock, ptr, count)),
- (res == -1)))
- {
- syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
- wc_backtrace(LOG_INFO);
- return -1;
- }
- count -= res;
- ptr += res;
- }
- return 0;
-}
-
-
-int
-read_serv_chunk(
-
- StrBuf *Buf,
- size_t total_len,
- size_t *bytes_read
- )
-{
- int rc;
- int ServerRc;
-
- serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, *bytes_read, total_len-(*bytes_read));
- if ( (rc = StrBuf_ServGetln(Buf) > 0) &&
- (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) )
- {
- size_t this_block = 0;
-
- if (rc < 0)
- return rc;
-
- StrBufCutLeft(Buf, 4);
- this_block = StrTol(Buf);
- rc = StrBuf_ServGetBLOBBuffered(WC->WBuf, this_block);
- if (rc < 0) {
- syslog(LOG_INFO, "Server connection broken during download\n");
- wc_backtrace(LOG_INFO);
- if (WC->serv_sock > 0) close(WC->serv_sock);
- WC->serv_sock = (-1);
- WC->connected = 0;
- WC->logged_in = 0;
- return rc;
- }
- *bytes_read += rc;
- }
- return 6;
-}
-
-static inline int send_http(StrBuf *Buf)
-{
-#ifdef HAVE_OPENSSL
- if (is_https)
- return client_write_ssl(Buf);
- else
-#endif
- return client_write(Buf);
-}
-/*
- * Read binary data from server into memory using a series of server READ commands.
- * returns the read content as StrBuf
- */
-void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime)
-{
- int ServerRc = 6;
- size_t bytes_read = 0;
- int first = 1;
- int client_con_state = 0;
- int chunked = 0;
- int is_gzip = 0;
- const char *Err = NULL;
- StrBuf *BufHeader = NULL;
- StrBuf *Buf;
- StrBuf *pBuf = NULL;
- vStreamT *SC = NULL;
- IOBuffer ReadBuffer;
- IOBuffer WriteBuffer;
-
-
- Buf = NewStrBuf();
-
- if (WC->Hdr->HaveRange)
- {
- WC->Hdr->HaveRange++;
- WC->Hdr->TotalBytes = total_len;
- /* open range? or beyound file border? correct the numbers. */
- if ((WC->Hdr->RangeTil == -1) || (WC->Hdr->RangeTil>= total_len))
- WC->Hdr->RangeTil = total_len - 1;
- bytes_read = WC->Hdr->RangeStart;
- total_len = WC->Hdr->RangeTil;
- }
- else
- chunked = total_len > SIZ * 10; /* TODO: disallow for HTTP / 1.0 */
-
- if (chunked)
- {
- BufHeader = NewStrBuf();
- }
-
- if ((detect_mime != 0) && (bytes_read != 0))
- {
- /* need to read first chunk to detect mime, though the client doesn't care */
- size_t bytes_read = 0;
- const char *CT;
-
- ServerRc = read_serv_chunk(
- Buf,
- total_len,
- &bytes_read);
-
- if (ServerRc != 6)
- {
- FreeStrBuf(&BufHeader);
- FreeStrBuf(&Buf);
- return;
- }
- CT = GuessMimeType(SKEY(WC->WBuf));
- FlushStrBuf(WC->WBuf);
- StrBufPlain(MimeType, CT, -1);
- CheckGZipCompressionAllowed(SKEY(MimeType));
- detect_mime = 0;
- FreeStrBuf(&Buf);
- }
-
- memset(&WriteBuffer, 0, sizeof(IOBuffer));
- if (chunked && !DisableGzip && WC->Hdr->HR.gzip_ok)
- {
- is_gzip = 1;
- SC = StrBufNewStreamContext (eZLibEncode, &Err);
- if (SC == NULL) {
- syslog(LOG_ERR, "Error while initializing stream context: %s", Err);
- FreeStrBuf(&Buf);
- return;
- }
-
- memset(&ReadBuffer, 0, sizeof(IOBuffer));
- ReadBuffer.Buf = WC->WBuf;
-
- WriteBuffer.Buf = NewStrBufPlain(NULL, SIZ*2);;
- pBuf = WriteBuffer.Buf;
- }
- else
- {
- pBuf = WC->WBuf;
- }
-
- if (!detect_mime)
- {
- http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
-
- if (send_http(WC->HBuf) < 0)
- {
- FreeStrBuf(&Buf);
- FreeStrBuf(&WriteBuffer.Buf);
- FreeStrBuf(&BufHeader);
- if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
- syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
- }
- return;
- }
- }
-
- while ((bytes_read < total_len) &&
- (ServerRc == 6) &&
- (client_con_state == 0))
- {
-
- if (WC->serv_sock==-1) {
- FlushStrBuf(WC->WBuf);
- FreeStrBuf(&Buf);
- FreeStrBuf(&WriteBuffer.Buf);
- FreeStrBuf(&BufHeader);
- StrBufDestroyStreamContext(eZLibEncode, &SC, &Err);
- if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
- syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
- }
- return;
- }
-
- ServerRc = read_serv_chunk(
- Buf,
- total_len,
- &bytes_read);
- if (ServerRc != 6)
- break;
-
- if (detect_mime)
- {
- const char *CT;
- detect_mime = 0;
-
- CT = GuessMimeType(SKEY(WC->WBuf));
- StrBufPlain(MimeType, CT, -1);
- if (is_gzip) {
- CheckGZipCompressionAllowed(SKEY(MimeType));
- is_gzip = WC->Hdr->HR.gzip_ok;
- }
- http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
-
- client_con_state = send_http(WC->HBuf);
- }
-
- if (is_gzip)
- {
- int done = (bytes_read == total_len);
- while ((IOBufferStrLength(&ReadBuffer) > 0) && (client_con_state == 0)) {
- int rc;
-
- do {
- rc = StrBufStreamTranscode(eZLibEncode, &WriteBuffer, &ReadBuffer, NULL, -1, SC, done, &Err);
-
- if (StrLength (pBuf) > 0) {
- StrBufPrintf(BufHeader, "%s%x\r\n",
- (first)?"":"\r\n",
- StrLength (pBuf));
- first = 0;
- client_con_state = send_http(BufHeader);
- if (client_con_state == 0) {
- client_con_state = send_http(pBuf);
- }
- FlushStrBuf(pBuf);
- }
- } while ((rc == 1) && (StrLength(pBuf) > 0));
- }
- FlushStrBuf(WC->WBuf);
- }
- else {
- if ((chunked) && (client_con_state == 0))
- {
- StrBufPrintf(BufHeader, "%s%x\r\n",
- (first)?"":"\r\n",
- StrLength (pBuf));
- first = 0;
- client_con_state = send_http(BufHeader);
- }
-
- if (client_con_state == 0)
- client_con_state = send_http(pBuf);
-
- FlushStrBuf(pBuf);
- }
- }
-
- if (SC && StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
- syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
- }
- FreeStrBuf(&WriteBuffer.Buf);
- if ((chunked) && (client_con_state == 0))
- {
- StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n"));
- if (send_http(BufHeader) < 0)
- {
- FreeStrBuf(&Buf);
- FreeStrBuf(&BufHeader);
- return;
- }
- }
- FreeStrBuf(&BufHeader);
- FreeStrBuf(&Buf);
-}
-
-int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target)
-{
- const char *Error;
-#ifdef HAVE_OPENSSL
- const char *pch, *pchs;
- int rlen, len, retval = 0;
-
- if (is_https) {
- int ntries = 0;
- if (StrLength(Hdr->ReadBuf) > 0)
- {
- pchs = ChrPtr(Hdr->ReadBuf);
- pch = strchr(pchs, '\n');
- if (pch != NULL) {
- rlen = 0;
- len = pch - pchs;
- if (len > 0 && (*(pch - 1) == '\r') )
- rlen ++;
- StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
- StrBufCutLeft(Hdr->ReadBuf, len + 1);
- return len - rlen;
- }
- }
-
- while (retval == 0) {
- pch = NULL;
- pchs = ChrPtr(Hdr->ReadBuf);
- if (*pchs != '\0')
- pch = strchr(pchs, '\n');
- if (pch == NULL) {
- retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
- pchs = ChrPtr(Hdr->ReadBuf);
- pch = strchr(pchs, '\n');
- if (pch == NULL)
- retval = 0;
- }
- if (retval == 0) {
- sleeeeeeeeeep(1);
- ntries ++;
- }
- if (ntries > 10)
- return 0;
- }
- if ((retval > 0) && (pch != NULL)) {
- rlen = 0;
- len = pch - pchs;
- if (len > 0 && (*(pch - 1) == '\r') )
- rlen ++;
- StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
- StrBufCutLeft(Hdr->ReadBuf, len + 1);
- return len - rlen;
-
- }
- else
- return -1;
- }
- else
-#endif
- return StrBufTCP_read_buffered_line_fast(Target,
- Hdr->ReadBuf,
- &Hdr->Pos,
- &Hdr->http_sock,
- 5,
- 1,
- &Error);
-}
-
-
-/*
- * This is a generic function to set up a master socket for listening on
- * a TCP port. The server shuts down if the bind fails. (IPv4/IPv6 version)
- *
- * ip_addr IP address to bind
- * port_number port number to bind
- * queue_len number of incoming connections to allow in the queue
- */
-int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len)
-{
- const char *ipv4broadcast = "0.0.0.0";
- int IsDefault = 0;
- struct protoent *p;
- struct sockaddr_in6 sin6;
- struct sockaddr_in sin4;
- int s, i, b;
- int ip_version = 6;
-
-retry:
- memset(&sin6, 0, sizeof(sin6));
- memset(&sin4, 0, sizeof(sin4));
- sin6.sin6_family = AF_INET6;
- sin4.sin_family = AF_INET;
-
- if ( (ip_addr == NULL) /* any IPv6 */
- || (IsEmptyStr(ip_addr))
- || (!strcmp(ip_addr, "*"))
- ) {
- IsDefault = 1;
- ip_version = 6;
- sin6.sin6_addr = in6addr_any;
- }
- else if (!strcmp(ip_addr, "0.0.0.0")) /* any IPv4 */
- {
- ip_version = 4;
- sin4.sin_addr.s_addr = INADDR_ANY;
- }
- else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) /* specific IPv4 */
- {
- ip_version = 4;
- if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
- syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
- return (-WC_EXIT_BIND);
- }
- }
- else /* specific IPv6 */
- {
- ip_version = 6;
- if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
- syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
- return (-WC_EXIT_BIND);
- }
- }
-
- if (port_number == 0) {
- syslog(LOG_WARNING, "Cannot start: no port number specified.\n");
- return (-WC_EXIT_BIND);
- }
- sin6.sin6_port = htons((u_short) port_number);
- sin4.sin_port = htons((u_short) port_number);
-
- p = getprotobyname("tcp");
-
- s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
- if (s < 0) {
- if (IsDefault && (errno == EAFNOSUPPORT))
- {
- s = 0;
- ip_addr = ipv4broadcast;
- goto retry;
- }
- syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno));
- return (-WC_EXIT_BIND);
- }
- /* Set some socket options that make sense. */
- i = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
-
- if (ip_version == 6) {
- b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
- }
- else {
- b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
- }
-
- if (b < 0) {
- syslog(LOG_ERR, "Can't bind: %s\n", strerror(errno));
- close(s);
- return (-WC_EXIT_BIND);
- }
-
- if (listen(s, queue_len) < 0) {
- syslog(LOG_ERR, "Can't listen: %s\n", strerror(errno));
- close(s);
- return (-WC_EXIT_BIND);
- }
- return (s);
-}
-
-
-/*
- * Create a Unix domain socket and listen on it
- * sockpath - file name of the unix domain socket
- * queue_len - Number of incoming connections to allow in the queue
- */
-int webcit_uds_server(char *sockpath, int queue_len) {
- struct sockaddr_un addr;
- int s;
- int i;
- int actual_queue_len;
-
- actual_queue_len = queue_len;
- if (actual_queue_len < 5) actual_queue_len = 5;
-
- i = unlink(sockpath);
- if ((i != 0) && (errno != ENOENT)) {
- syslog(LOG_WARNING, "webcit: can't unlink %s: %s\n",
- sockpath, strerror(errno));
- return (-WC_EXIT_BIND);
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s < 0) {
- syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
- return (-WC_EXIT_BIND);
- }
-
- if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- syslog(LOG_WARNING, "webcit: Can't bind: %s\n", strerror(errno));
- close(s);
- return (-WC_EXIT_BIND);
- }
-
- if (listen(s, actual_queue_len) < 0) {
- syslog(LOG_WARNING, "webcit: Can't listen: %s\n", strerror(errno));
- close(s);
- return (-WC_EXIT_BIND);
- }
-
- chmod(sockpath, 0777);
- return(s);
-}
-
-
-/*
- * 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 client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout) {
- const char *Error;
- int retval = 0;
-
-#ifdef HAVE_OPENSSL
- if (is_https) {
- long bufremain = 0;
- long baselen;
-
- baselen = StrLength(Target);
-
- if (Hdr->Pos == NULL) {
- Hdr->Pos = ChrPtr(Hdr->ReadBuf);
- }
-
- if (StrLength(Hdr->ReadBuf) > 0) {
- bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
-
- if (bytes < bufremain)
- bufremain = bytes;
- StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
- StrBufCutLeft(Hdr->ReadBuf, bufremain);
- }
-
- if (bytes > bufremain) {
- while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) &&
- (retval >= 0))
- retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
- if (retval >= 0) {
- StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
- return 1;
- }
- else {
- syslog(LOG_INFO, "client_read_ssl() failed\n");
- return -1;
- }
- }
- else
- return 1;
- }
-#endif
- retval = StrBufReadBLOBBuffered(Target,
- Hdr->ReadBuf,
- &Hdr->Pos,
- &Hdr->http_sock,
- 1,
- bytes,
- O_TERM,
- &Error);
- if (retval < 0) {
- syslog(LOG_INFO, "client_read() failed: %s\n", Error);
- wc_backtrace(LOG_DEBUG);
- return retval;
- }
-
- return 1;
-}
-
-
-/*
- * Begin buffering HTTP output so we can transmit it all in one write operation later.
- */
-void begin_burst(void)
-{
- if (WC->WBuf == NULL) {
- WC->WBuf = NewStrBufPlain(NULL, 32768);
- }
-}
-
-
-/*
- * Finish buffering HTTP output. [Compress using zlib and] output with a Content-Length: header.
- */
-long end_burst(void)
-{
- const char *ptr, *eptr;
- long count;
- ssize_t res = 0;
- fd_set wset;
- int fdflags;
-
- if (!DisableGzip && (WC->Hdr->HR.gzip_ok))
- {
- if (CompressBuffer(WC->WBuf) > 0)
- hprintf("Content-encoding: gzip\r\n");
- else {
- syslog(LOG_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
- wc_backtrace(LOG_INFO);
- }
- }
-
- if (WC->WFBuf != NULL) {
- WildFireSerializePayload(WC->WFBuf, WC->HBuf, &WC->Hdr->nWildfireHeaders, NULL);
- FreeStrBuf(&WC->WFBuf);
- }
-
- if (WC->Hdr->HR.prohibit_caching)
- hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
- hprintf("Content-length: %d\r\n\r\n", StrLength(WC->WBuf));
-
- ptr = ChrPtr(WC->HBuf);
- count = StrLength(WC->HBuf);
- eptr = ptr + count;
-
-#ifdef HAVE_OPENSSL
- if (is_https) {
- client_write_ssl(WC->HBuf);
- client_write_ssl(WC->WBuf);
- return (count);
- }
-#endif
-
- if (WC->Hdr->http_sock == -1) {
- return -1;
- }
- fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
-
- while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
- if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
- FD_ZERO(&wset);
- FD_SET(WC->Hdr->http_sock, &wset);
- if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
- syslog(LOG_DEBUG, "client_write: Socket select failed (%s)\n", strerror(errno));
- return -1;
- }
- }
-
- if ((WC->Hdr->http_sock == -1) ||
- (res = write(WC->Hdr->http_sock,
- ptr,
- count)) == -1) {
- syslog(LOG_DEBUG, "client_write: Socket write failed (%s)\n", strerror(errno));
- wc_backtrace(LOG_INFO);
- return res;
- }
- count -= res;
- ptr += res;
- }
-
- ptr = ChrPtr(WC->WBuf);
- count = StrLength(WC->WBuf);
- eptr = ptr + count;
-
- while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
- if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
- FD_ZERO(&wset);
- FD_SET(WC->Hdr->http_sock, &wset);
- if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
- syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
- return -1;
- }
- }
-
- if ((WC->Hdr->http_sock == -1) ||
- (res = write(WC->Hdr->http_sock,
- ptr,
- count)) == -1) {
- syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
- wc_backtrace(LOG_INFO);
- return res;
- }
- count -= res;
- ptr += res;
- }
-
- return StrLength(WC->WBuf);
-}
-
-
-/*
- * lingering_close() a`la Apache. see
- * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale
- */
-int lingering_close(int fd)
-{
- char buf[SIZ];
- int i;
- fd_set set;
- struct timeval tv, start;
-
- gettimeofday(&start, NULL);
- if (fd == -1)
- return -1;
- shutdown(fd, 1);
- do {
- do {
- gettimeofday(&tv, NULL);
- tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
- tv.tv_usec = start.tv_usec - tv.tv_usec;
- if (tv.tv_usec < 0) {
- tv.tv_sec--;
- tv.tv_usec += 1000000;
- }
- FD_ZERO(&set);
- FD_SET(fd, &set);
- i = select(fd + 1, &set, NULL, NULL, &tv);
- } while (i == -1 && errno == EINTR);
-
- if (i <= 0)
- break;
-
- i = read(fd, buf, sizeof buf);
- } while (i != 0 && (i != -1 || errno == EINTR));
-
- return close(fd);
-}
-
-void
-HttpNewModule_TCPSOCKETS
-(ParsedHttpHdrs *httpreq)
-{
-
- httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
-}
-
-void
-HttpDetachModule_TCPSOCKETS
-(ParsedHttpHdrs *httpreq)
-{
-
- FlushStrBuf(httpreq->ReadBuf);
- ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
-}
-
-void
-HttpDestroyModule_TCPSOCKETS
-(ParsedHttpHdrs *httpreq)
-{
-
- FreeStrBuf(&httpreq->ReadBuf);
-}
-
-
-void
-SessionNewModule_TCPSOCKETS
-(wcsession *sess)
-{
- sess->CLineBuf = NewStrBuf();
- sess->MigrateReadLineBuf = NewStrBuf();
-}
-
-void
-SessionDestroyModule_TCPSOCKETS
-(wcsession *sess)
-{
- FreeStrBuf(&sess->CLineBuf);
- FreeStrBuf(&sess->ReadBuf);
- sess->connected = 0;
- sess->ReadPos = NULL;
- FreeStrBuf(&sess->MigrateReadLineBuf);
- if (sess->serv_sock > 0) {
- syslog(LOG_DEBUG, "Closing socket %d", sess->serv_sock);
- close(sess->serv_sock);
- }
- sess->serv_sock = -1;
-}
+++ /dev/null
-/*
- * Copyright (c) 1996-2013 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 uds_connectsock(char *);
-int tcp_connectsock(char *, char *);
-int serv_getln(char *strbuf, int bufsize);
-int StrBuf_ServGetln(StrBuf *buf);
-
-/*
- * parse & check the server reply
- *
- * Line the line containing the server reply
- * FullState if you need more than just the major number, this is returns it. Ignored if NULL.
- * PutImportantMessage if you want to forward the text part of the server reply to the user, specify 1;
- * the result will be put into the 'Important Message' framework.
- * MajorOK in case of which major number not to put the ImportantMessage? 0 for all.
- *
- * returns the most significant digit of the server status
- */
-
-int GetServerStatusMsg(StrBuf *Line, long* FullState, int PutImportantMessage, int MajorOK);
-
-/*
- * to migrate old calls....
- */
-#define GetServerStatus(a, b) GetServerStatusMsg(a, b, 0, 0)
-
-int serv_puts(const char *string);
-
-int serv_write(const char *buf, int nbytes);
-int serv_putbuf(const StrBuf *string);
-int serv_printf(const char *format,...)__attribute__((__format__(__printf__,1,2)));
-int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf);
-void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime);
-int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize);
-int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize);
-int read_server_text(StrBuf *Buf, long *nLines);
-
-void text_to_server(char *ptr);
-void text_to_server_qp(const StrBuf *SendMeEncoded);
-void server_to_text(void);
-int lingering_close(int fd);
#include "roomops.h"
#include "preferences.h"
-#include "tcp_sockets.h"
+#include "sockets.h"
#include "utils.h"
#ifdef HAVE_OPENSSL
/* Work around RedHat's b0rken OpenSSL includes */
#undef memcpy
#endif
-#define SLEEPING 180 /* TCP connection timeout */
-#define WEBCIT_TIMEOUT 900 /* WebCit session timeout */
-#define PORT_NUM 80 /* port number to listen on */
+#define SLEEPING 180 /* TCP connection timeout */
+#define WEBCIT_TIMEOUT 900 /* WebCit session timeout */
+#define PORT_NUM 80 /* port number to listen on */
#define DEVELOPER_ID 0
#define CLIENT_ID 4
#define CLIENT_VERSION 941 /* This version of WebCit */
-#define MINIMUM_CIT_VERSION 931 /* Minimum required version of Citadel server */
-#define LIBCITADEL_MIN 931 /* Minimum required version of libcitadel */
-#define DEFAULT_HOST "localhost" /* Default Citadel server */
-#define DEFAULT_PORT "504"
-#define TARGET "webcit01" /* Window target for inline URL's */
-#define HOUSEKEEPING 15 /* Housekeeping frequency */
+#define MINIMUM_CIT_VERSION 931 /* Minimum required version of Citadel server */
+#define LIBCITADEL_MIN 931 /* Minimum required version of libcitadel */
+#define DEFAULT_CTDLDIR "/usr/local/citadel" /* Default Citadel server directory */
+#define TARGET "webcit01" /* Window target for inline URL's */
+#define HOUSEKEEPING 15 /* Housekeeping frequency */
#define MAX_WORKER_THREADS 250
-#define LISTEN_QUEUE_LENGTH 100 /* listen() backlog queue */
+#define LISTEN_QUEUE_LENGTH 100 /* listen() backlog queue */
#define USERCONFIGROOM "My Citadel Config"
#define DEFAULT_MAXMSGS 20
#ifdef HAVE_OPENSSL
#define THREADSSL ((SSL *)pthread_getspecific(ThreadSSL))
extern pthread_key_t ThreadSSL;
-extern char ctdl_key_dir[PATH_MAX];
-extern char file_crpt_file_key[PATH_MAX];
-extern char file_crpt_file_csr[PATH_MAX];
-extern char file_crpt_file_cer[PATH_MAX];
void init_ssl(void);
void endtls(void);
extern int is_https;
extern int follow_xff;
extern char *server_cookie;
-extern char *ctdlhost, *ctdlport;
extern char *axdefs[];
extern int num_threads_existing;
extern int num_threads_executing;
HashList *GetValidDomainNames(StrBuf *Target, WCTemplputParams *TP);
void output_error_pic(const char *ErrMsg1, const char *ErrMsg2);
void jsonMessageListHdr(void);
+extern char *ctdl_dir; /* Directory where Citadel Server is running */
-// Copyright (c) 1996-2021 by the citadel.org team
+// Copyright (c) 1996-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.
extern void worker_entry(void);
extern void drop_root(uid_t UID);
-char socket_dir[PATH_MAX]; /* where to talk to our citadel server */
-char *server_cookie = NULL; /* our Cookie connection to the client */
-int http_port = PORT_NUM; /* Port to listen on */
-char *ctdlhost = DEFAULT_HOST; /* Host name or IP address of Citadel server */
-char *ctdlport = DEFAULT_PORT; /* Port number of Citadel server */
-int running_as_daemon = 0; /* should we deamonize on startup? */
+char socket_dir[PATH_MAX]; /* where to talk to our citadel server */
+char *server_cookie = NULL; /* our Cookie connection to the client */
+int http_port = PORT_NUM; /* Port to listen on */
+int running_as_daemon = 0; /* should we deamonize on startup? */
+char *ctdl_dir = DEFAULT_CTDLDIR; /* Directory where Citadel Server is running */
/* #define DBG_PRINNT_HOOKS_AT_START */
#ifdef DBG_PRINNT_HOOKS_AT_START
else {
safestrncpy(relhome, relhome, sizeof relhome);
}
- /* free(hdir); TODO: SHOULD WE DO THIS? */
home=1;
break;
case 'd':
#ifdef HAVE_OPENSSL
"[-s] [-S cipher_suites]"
#endif
- "[remotehost [remoteport]]\n");
+ "[citadel_server_directory]\n");
return 1;
}
LOG_DAEMON
);
- if (optind < argc) {
- ctdlhost = argv[optind];
- if (++optind < argc)
- ctdlport = argv[optind];
+ while (optind < argc) {
+ ctdl_dir = strdup(argv[optind]);
+ ++optind;
}
/* daemonize, if we were asked to */
/* Tell 'em who's in da house */
syslog(LOG_NOTICE, "%s", PACKAGE_STRING);
- syslog(LOG_NOTICE, "Copyright (C) 1996-2020 by the citadel.org team");
+ syslog(LOG_NOTICE, "Copyright (C) 1996-2022 by the citadel.org team");
syslog(LOG_NOTICE, " ");
syslog(LOG_NOTICE, "This program is open source software: you can redistribute it and/or");
syslog(LOG_NOTICE, "modify it under the terms of the GNU General Public License, version 3.");
pthread_mutex_init(&SessionListMutex, NULL);
- /*
- * Start up the housekeeping thread
- */
+ // Start up the housekeeping thread
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&SessThread, &attr, (void *(*)(void *)) housekeeping_loop, NULL);
- /*
- * If this is an HTTPS server, fire up SSL
- */
+ // If this is an HTTPS server, fire up SSL
#ifdef HAVE_OPENSSL
if (is_https) {
init_ssl();
#endif
drop_root(UID);
- /* Become a worker thread. More worker threads will be spawned as they are needed. */
+ // Become a worker thread. More worker threads will be spawned as they are needed.
worker_entry();
ShutDownLibCitadel();
return 0;