DRY the code to generate listsub/unsub confirmations.
We're going to need this to do One Click Unsubscribe, which we have to add
because the godless commies at Google will use any excuse to put non-Google
senders on their spam list.
Add the List-Unsubscribe-Post header
// 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);
+ syslog(LOG_INFO, "citserver: seeding the pseudo-random number generator");
+ srand(time(NULL) + getpid() + clock());
}
struct passwd *pw;
gid_t gid;
- syslog(LOG_DEBUG, "master_startup() started");
+ syslog(LOG_DEBUG, "citserver: master_startup() started");
time(&server_startup_time);
- syslog(LOG_INFO, "Checking directory access");
+ syslog(LOG_INFO, "citserver: checking directory access");
if ((pw = getpwuid(ctdluid)) == NULL) {
gid = getgid();
}
syslog(LOG_DEBUG, "citserver: ctdl_key_dir is %s", ctdl_key_dir);
syslog(LOG_DEBUG, "citserver: ctdl_run_dir is %s", ctdl_run_dir);
- syslog(LOG_INFO, "Opening databases");
+ syslog(LOG_INFO, "citserver: opening databases");
cdb_init_backends();
cdb_open_databases();
// Load site-specific configuration
seed_random_number_generator(); // must be done before config system
- syslog(LOG_INFO, "Initializing configuration system");
+ syslog(LOG_INFO, "citserver: initializing configuration system");
initialize_config_system();
validate_config();
migrate_legacy_control_record();
// Check floor reference counts
check_ref_counts();
- syslog(LOG_INFO, "Creating base rooms (if necessary)");
+ syslog(LOG_INFO, "citserver: 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);
CtdlPutRoomLock(&qrbuf);
}
- syslog(LOG_DEBUG, "master_startup() finished");
+ syslog(LOG_DEBUG, "citserver: master_startup() finished");
}
int CtdlHostAlias(char *fqdn);
char *harvest_collected_addresses(struct CtdlMessage *msg);
int is_email_subscribed_to_list(char *email, char *room_name);
+void generate_one_click_url(char *target_buf, char *base_url, char *action, char *roomname, char *emailaddr);
// Values that can be returned by CtdlHostAlias()
enum {
// This module delivers messages to mailing lists.
//
-// Copyright (c) 2002-2023 by the citadel.org team
+// Copyright (c) 2002-2024 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.
}
// return our module name for the log
- return "listsub";
+ return "listdeliver";
}
}
+// Generate a pre-authorized subscribe/unsubscribe URL for a particular email address for a particular room.
+// This can be used as the second part of a double-opt-in or double-opt-out process.
+// It can also be used to generate a "one click unsubscribe" link.
+void generate_one_click_url(char *target_buf, char *base_url, char *action, char *roomname, char *emailaddr) {
+
+ // We need a URL-safe representation of the room name
+ char encoded_roomname[ROOMNAMELEN+10];
+ urlesc(encoded_roomname, sizeof(encoded_roomname), roomname);
+
+ // The confirmation token pre-authorizes the generated URL. It is hashed by the host key so it can't be guessed.
+ char confirmation_token[128];
+ generate_confirmation_token(confirmation_token, sizeof confirmation_token, roomname, emailaddr);
+
+ // Write to the buffer
+ snprintf(target_buf, SIZ, "%s?cmd=%s&email=%s&room=%s&token=%s",
+ base_url,
+ action,
+ emailaddr,
+ encoded_roomname,
+ confirmation_token
+ );
+}
+
+
// This generates an email with a link the user clicks to confirm a list subscription.
void send_subscribe_confirmation_email(char *roomname, char *emailaddr, char *url, char *confirmation_token) {
- // We need a URL-safe representation of the room name
- char urlroom[ROOMNAMELEN+10];
- urlesc(urlroom, sizeof(urlroom), roomname);
+
+ char confirm_subscribe_url[SIZ];
+ generate_one_click_url(confirm_subscribe_url, url, "confirm_subscribe", roomname, emailaddr);
char from_address[1024];
snprintf(from_address, sizeof from_address, "noreply@%s", CtdlGetConfigStr("c_fqdn"));
"<%s> to the <%s> mailing list.\n"
"\n"
"Please go here to confirm this request:\n"
- "%s?cmd=confirm_subscribe&email=%s&room=%s&token=%s\n"
+ "%s\n"
"\n"
"If this request has been submitted in error and you do not\n"
"wish to receive the <%s> mailing list, simply do nothing,\n"
"--__ctdlmultipart__\n"
"Content-type: text/html\n"
"\n"
- "<html><body><p>Someone (probably you) has submitted a request to subscribe "
- "<strong>%s</strong> to the <strong>%s</strong> mailing list.</p>"
- "<p>Please go here to confirm this request:</p>"
- "<p><a href=\"%s?cmd=confirm_subscribe&email=%s&room=%s&token=%s\">"
- "%s?cmd=confirm_subscribe&email=%s&room=%s&token=%s</a></p>"
- "<p>If this request has been submitted in error and you do not "
- "wish to receive the <strong>%s<strong> mailing list, simply do nothing, "
- "and you will not receive any further mailings.</p>"
+ "<html><body><p>Someone (probably you) has submitted a request to subscribe\n"
+ "<strong>%s</strong> to the <strong>%s</strong> mailing list.</p>\n"
+ "<p>Please go here to confirm this request:</p>\n"
+ "<p><a href=\"%s\">%s</a></p>\n"
+ "<p>If this request has been submitted in error and you do not\n"
+ "wish to receive the <strong>%s</strong> mailing list, simply do nothing,\n"
+ "and you will not receive any further mailings.</p>\n"
"</body></html>\n"
"\n"
"--__ctdlmultipart__--\n"
,
- emailaddr, roomname,
- url, emailaddr, urlroom, confirmation_token,
- roomname
- ,
- emailaddr, roomname,
- url, emailaddr, urlroom, confirmation_token,
- url, emailaddr, urlroom, confirmation_token,
- roomname
+ emailaddr, roomname, confirm_subscribe_url, roomname,
+ emailaddr, roomname, confirm_subscribe_url, confirm_subscribe_url, roomname
);
quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list subscription");
// This generates an email with a link the user clicks to confirm a list unsubscription.
void send_unsubscribe_confirmation_email(char *roomname, char *emailaddr, char *url, char *confirmation_token) {
- // We need a URL-safe representation of the room name
- char urlroom[ROOMNAMELEN+10];
- urlesc(urlroom, sizeof(urlroom), roomname);
+
+ char confirm_unsubscribe_url[SIZ];
+ generate_one_click_url(confirm_unsubscribe_url, url, "confirm_unsubscribe", roomname, emailaddr);
char from_address[1024];
snprintf(from_address, sizeof from_address, "noreply@%s", CtdlGetConfigStr("c_fqdn"));
"<%s> from the <%s> mailing list.\n"
"\n"
"Please go here to confirm this request:\n"
- "%s?cmd=confirm_unsubscribe&email=%s&room=%s&token=%s\n"
+ "%s\n"
"\n"
"If this request has been submitted in error and you still\n"
"wish to receive the <%s> mailing list, simply do nothing,\n"
"--__ctdlmultipart__\n"
"Content-type: text/html\n"
"\n"
- "<html><body><p>Someone (probably you) has submitted a request to unsubscribe "
- "<strong>%s</strong> from the <strong>%s</strong> mailing list.</p>"
- "<p>Please go here to confirm this request:</p>"
- "<p><a href=\"%s?cmd=confirm_unsubscribe&email=%s&room=%s&token=%s\">"
- "%s?cmd=confirm_unsubscribe&email=%s&room=%s&token=%s</a></p>"
- "<p>If this request has been submitted in error and you still "
- "wish to receive the <strong>%s<strong> mailing list, simply do nothing, "
- "and you will remain subscribed.</p>"
+ "<html><body><p>Someone (probably you) has submitted a request to unsubscribe\n"
+ "<strong>%s</strong> from the <strong>%s</strong> mailing list.</p>\n"
+ "<p>Please go here to confirm this request:</p>\n"
+ "<p><a href=\"%s\">%s</a></p>\n"
+ "<p>If this request has been submitted in error and you still\n"
+ "wish to receive the <strong>%s</strong> mailing list, simply do nothing,\n"
+ "and you will remain subscribed.</p>\n"
"</body></html>\n"
"\n"
"--__ctdlmultipart__--\n"
,
- emailaddr, roomname,
- url, emailaddr, urlroom, confirmation_token,
- roomname
- ,
- emailaddr, roomname,
- url, emailaddr, urlroom, confirmation_token,
- url, emailaddr, urlroom, confirmation_token,
- roomname
+ emailaddr, roomname, confirm_unsubscribe_url, roomname,
+ emailaddr, roomname, confirm_unsubscribe_url, confirm_unsubscribe_url, roomname
);
quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list unsubscription");
//
// This is the new, exciting, clever version that makes libcurl do all the work :)
//
-// Copyright (c) 1997-2023 by the citadel.org team
+// Copyright (c) 1997-2024 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.
process_rfc822_addr(recp, user, node, name); // split recipient address into username, hostname, displayname
num_mx = getmx(mxes, node);
if (num_mx < 1) {
- return (421);
+ return(421);
}
CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
+
+ // If we have a source room, it's probably a mailing list message; generate an unsubscribe header
if (!IsEmptyStr(source_room)) {
- // If we have a source room, it's probably a mailing list message; generate an unsubscribe header
- char esc_room[ROOMNAMELEN*2];
- char esc_email[1024];
- urlesc(esc_room, sizeof esc_room, source_room);
- urlesc(esc_email, sizeof esc_email, recp);
- cprintf("List-Unsubscribe: <http://%s/listsub?cmd=unsubscribe&room=%s&email=%s>\r\n",
- CtdlGetConfigStr("c_fqdn"),
- esc_room,
- esc_email
- );
+ char base_url[SIZ];
+ char unsubscribe_url[SIZ];
+ snprintf(base_url, sizeof base_url, "https://%s/listsub", CtdlGetConfigStr("c_fqdn"));
+ generate_one_click_url(unsubscribe_url, base_url, "unsubscribe", source_room, recp);
+ cprintf("List-Unsubscribe: %s\r\n", unsubscribe_url);
+ cprintf("List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n"); // RFC 8058
+
}
+
CtdlOutputMsg(msgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, &fromaddr, NULL);
s.TheMessage = CC->redirect_buffer;
- s.bytes_total = StrLength(CC->redirect_buffer);
- s.bytes_sent = 0;
CC->redirect_buffer = NULL;
+ s.bytes_total = StrLength(s.TheMessage);
+ s.bytes_sent = 0;
response_code = 421;
- // keep trying MXes until one works or we run out
+
+ // Future enhancement: if we implement DKIM signing, this is where it must happen.
+
+ // Keep trying MXes until one works or we run out.
for (i = 0; ((i < num_mx) && ((response_code / 100) == 4)); ++i) {
response_code = 421; // default 421 makes non-protocol errors transient
s.bytes_sent = 0; // rewind our buffer in case we try multiple MXes
msg = CtdlFetchMessage(qmsgnum, 1);
if (msg == NULL) {
- syslog(LOG_WARNING, "smtpclient: %ld does not exist", qmsgnum);
+ syslog(LOG_WARNING, "smtpclient: msg#%ld does not exist", qmsgnum);
return;
}
}
}
else {
- syslog(LOG_DEBUG, "smtpclient: %ld retry time not reached", qmsgnum);
+ syslog(LOG_DEBUG, "smtpclient: msg#%ld retry time not reached", qmsgnum);
}
if (bounceto != NULL) {
}
if (mode == MT_RFC822) {
- // Construct a fun message id
- cprintf("Message-ID: <%s", mid);
- if (strchr(mid, '@')==NULL) {
- cprintf("@%s", snode);
- }
- cprintf(">%s", nl);
+ // Make the message ID RFC2822 compliant
+ cprintf("Message-ID: <%s%s%s>%s", // put it in angle brackets
+ mid,
+ (strchr(mid, '@') ? "" : "@"), // if there is no domain part,
+ (strchr(mid, '@') ? "" : snode), // tack on ours.
+ nl
+ );
if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONONLY)) {
cprintf("From: \"----\" <x@x.org>%s", nl);
}
if (error_count > 0) {
- syslog(LOG_ERR, "msgbase: encountered %d errors storing message %ld", error_count, msgid);
+ syslog(LOG_ERR, "msgbase: encountered %ld errors storing message %ld", error_count, msgid);
}
// Free the memory we used for the serialized message
throw new Error(`${response.status} ${response.statusText}`);
}
})
- //.then(str => new window.DOMParser().parseFromString(str, "text/xml"))
- .then(str => document.getElementById("ctdl-main").innerHTML = escapeHTML(str))
+ //.then(str => document.getElementById("ctdl-main").innerHTML = escapeHTML(str))
+ .then(str => new window.DOMParser().parseFromString(str, "text/xml"))
+ .then(xmlcal => {
+ document.getElementById("ctdl-main").innerHTML = "calendar items:<br>";
+ let root = xmlcal.documentElement;
+ let children = root.childNodes;
+ for (let i=0; i<children.length; ++i) {
+ let child = children[i];
+ if (child.nodeType == Node.ELEMENT_NODE) {
+ var getetag_e = child.getElementsByTagName("DAV:href")[0];
+ var getetag_s = getetag_e.textContent;
+ document.getElementById("ctdl-main").innerHTML += getetag_s + "<br>";
+ }
+ }
+
+ })
.catch(error => {
console.log(error);
document.getElementById("ctdl-main").innerHTML = `<div class="ctdl-fatal-error">${error}</div>`;
# Remove any vestiges of pre-6.05 build environments
rm -f .libs modules *.so *.lo *.la 2>/dev/null
+grep '^#define CLIENT_VERSION' webcit.h | sed 's/[^0-9]*//g' >package-version.txt
+
echo ... running aclocal ...
aclocal
echo ... mk_module_init.sh ...
./scripts/mk_module_init.sh
-
echo
echo This script has been tested with autoconf 2.53 and
echo automake 1.5. Other versions may work, but I recommend the latest
echo with the same prefix.
echo
-grep '^#define CLIENT_VERSION' webcit.h | sed 's/[^0-9]*//g' >package-version.txt
dnl Process this file with autoconf to produce a configure script.
dnl $Id$
-AC_INIT([WebCit],[m4_esyscmd_s(grep CLIENT_VERSION webcit.h | sed 's/[^0-9]*//g')],[http://uncensored.citadel.org])
+AC_INIT([WebCit],[m4_esyscmd_s(cat package-version.txt)],[http://uncensored.citadel.org])
AC_SUBST(PROG_SUBDIRS)
AC_DEFINE(PROG_SUBDIRS, [], [Program dirs])