// 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");
}
}
+// 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) {