-/*
- * This module handles self-service subscription/unsubscription to mail lists.
- *
- * Copyright (c) 2002-2012 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+//
+// This module handles self-service subscription/unsubscription to mail lists.
+//
+// Copyright (c) 2002-2021 by the citadel.org team
+//
+// This program is open source software. It runs great on the
+// Linux operating system (and probably elsewhere). You can use,
+// copy, and run it under the terms of the GNU General Public
+// License version 3. Richard Stallman is an asshole communist.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
#include "sysdep.h"
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
+#include <time.h>
#include <sys/wait.h>
#include <string.h>
#include <limits.h>
#include "msgbase.h"
#include "internet_addressing.h"
#include "clientsocket.h"
-#include "file_ops.h"
#include "ctdl_module.h"
+
+enum { // one of these gets passed to do_subscribe_or_unsubscribe() so it knows what we asked for
+ UNSUBSCRIBE,
+ SUBSCRIBE
+};
+
+
/*
- * Generate a randomizationalisticized token to use for authentication of
- * a subscribe or unsubscribe request.
+ * This generates an email with a link the user clicks to confirm a list subscription.
*/
-void listsub_generate_token(char *buf) {
- char sourcebuf[SIZ];
- static int seq = 0;
-
- /* Theo, please sit down and shut up. This key doesn't have to be
- * tinfoil-hat secure, it just needs to be reasonably unguessable
- * and unique.
- */
- sprintf(sourcebuf, "%lx",
- (long) (++seq + getpid() + time(NULL))
+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 from_address[1024];
+ snprintf(from_address, sizeof from_address, "noreply@%s", CtdlGetConfigStr("c_fqdn"));
+
+ char emailtext[SIZ];
+ snprintf(emailtext, sizeof emailtext,
+ "MIME-Version: 1.0\n"
+ "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
+ "\n"
+ "This is a multipart message in MIME format.\n"
+ "\n"
+ "--__ctdlmultipart__\n"
+ "Content-type: text/plain\n"
+ "\n"
+ "Someone (probably you) has submitted a request to subscribe\n"
+ "<%s> to the <%s> mailing list.\n"
+ "\n"
+ "Please go here to confirm this request:\n"
+ "%s?room=%s&token=%s&cmd=confirm\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"
+ "and you will not receive any further mailings.\n"
+ "\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?room=%s&token=%s&cmd=confirm\">"
+ "%s?room=%s&token=%s&cmd=confirm</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>"
+ "</body></html>\n"
+ "\n"
+ "--__ctdlmultipart__--\n"
+ ,
+ emailaddr, roomname,
+ url, urlroom, confirmation_token,
+ roomname
+ ,
+ emailaddr, roomname,
+ url, urlroom, confirmation_token,
+ url, urlroom, confirmation_token,
+ roomname
);
- /* Convert it to base64 so it looks cool */
- CtdlEncodeBase64(buf, sourcebuf, strlen(sourcebuf), 0);
+ quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list subscription");
}
-const RoomNetCfg ActiveSubscribers[] = {listrecp, digestrecp};
-int CountThisSubscriber(OneRoomNetCfg *OneRNCfg, StrBuf *email)
-{
- RoomNetCfgLine *Line;
- int found_sub = 0;
- int i;
+/*
+ * 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 from_address[1024];
+ snprintf(from_address, sizeof from_address, "noreply@%s", CtdlGetConfigStr("c_fqdn"));
+
+ char emailtext[SIZ];
+ snprintf(emailtext, sizeof emailtext,
+ "MIME-Version: 1.0\n"
+ "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
+ "\n"
+ "This is a multipart message in MIME format.\n"
+ "\n"
+ "--__ctdlmultipart__\n"
+ "Content-type: text/plain\n"
+ "\n"
+ "Someone (probably you) has submitted a request to unsubscribe\n"
+ "<%s> from the <%s> mailing list.\n"
+ "\n"
+ "Please go here to confirm this request:\n"
+ "%s?room=%s&token=%s&cmd=confirm\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"
+ "and you will remain subscribed.\n"
+ "\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?room=%s&token=%s&cmd=confirm\">"
+ "%s?room=%s&token=%s&cmd=confirm</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>"
+ "</body></html>\n"
+ "\n"
+ "--__ctdlmultipart__--\n"
+ ,
+ emailaddr, roomname,
+ url, urlroom, confirmation_token,
+ roomname
+ ,
+ emailaddr, roomname,
+ url, urlroom, confirmation_token,
+ url, urlroom, confirmation_token,
+ roomname
+ );
- for (i = 0; i < 2; i++)
- {
- Line = OneRNCfg->NetConfigs[ActiveSubscribers[i]];
- while (Line != NULL)
- {
- if (!strcmp(ChrPtr(email),
- ChrPtr(Line->Value[0])))
- {
- ++found_sub;
- break;
- }
- Line = Line->next;
- }
- }
- return found_sub;
+ quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list unsubscription");
}
+
/*
- * Enter a subscription request
+ * "Subscribe" and "Unsubscribe" operations are so similar that they share a function.
+ * The actual subscription doesn't take place here -- we just send out the confirmation request
+ * and record the address and confirmation token.
*/
-void do_subscribe(StrBuf **room, StrBuf **email, StrBuf **subtype, StrBuf **webpage) {
- struct ctdlroom qrbuf;
- char token[256];
- char *pcf_req;
- StrBuf *cf_req;
- StrBuf *UrlRoom;
- int found_sub = 0;
- const char *RoomMailAddress;
- OneRoomNetCfg *OneRNCfg;
- RoomNetCfgLine *Line;
- const char *EmailSender = NULL;
- long RoomMailAddressLen;
-
- if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
- cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
- return;
- }
+void do_subscribe_or_unsubscribe(int action, char *emailaddr, char *url) {
- if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
- cprintf("%d '%s' "
- "does not accept subscribe/unsubscribe requests.\n",
- ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
- return;
- }
+ int i;
+ char buf[1024];
+ char confirmation_token[40];
- /*
- * Make sure the requested address isn't already subscribed
- */
+ // Update this room's netconfig with the updated lastsent
begin_critical_section(S_NETCONFIGS);
-
- RoomMailAddress = qrbuf.QRname;
- OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
- if (OneRNCfg!=NULL) {
- found_sub = CountThisSubscriber(OneRNCfg, *email);
- if (StrLength(OneRNCfg->Sender) > 0) {
- EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
- }
- }
-
- if (found_sub != 0) {
- cprintf("%d '%s' is already subscribed to '%s'.\n",
- ERROR + ALREADY_EXISTS,
- ChrPtr(*email),
- RoomMailAddress);
-
- end_critical_section(S_NETCONFIGS);
- return;
+ char *oldnetconfig = LoadRoomNetConfigFile(CC->room.QRnumber);
+ if (!oldnetconfig) {
+ oldnetconfig = strdup("");
}
- /*
- * Now add it to the config
- */
-
- RoomMailAddressLen = strlen(RoomMailAddress);
- listsub_generate_token(token);
- Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
- memset(Line, 0, sizeof(RoomNetCfgLine));
+ // The new netconfig begins with an empty buffer...
+ char *newnetconfig = malloc(strlen(oldnetconfig) + 1024);
+ newnetconfig[0] = 0;
- Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 5);
-
- Line->Value[0] = NewStrBufDup(*email);
- Line->Value[1] = *subtype; *subtype = NULL;
- Line->Value[2] = NewStrBufPlain(token, -1);
- Line->Value[3] = NewStrBufPlain(NULL, 10);
- StrBufPrintf(Line->Value[3], "%ld", time(NULL));
- Line->Value[4] = *webpage; *webpage = NULL;
- Line->nValues = 5;
-
- AddRoomCfgLine(OneRNCfg, &qrbuf, subpending, Line);
-
- /* Generate and send the confirmation request */
- UrlRoom = NewStrBuf();
- StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
-
- cf_req = NewStrBufPlain(NULL, 2048);
- StrBufAppendBufPlain(
- cf_req,
- HKEY("MIME-Version: 1.0\n"
- "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
- "\n"
- "This is a multipart message in MIME format.\n"
- "\n"
- "--__ctdlmultipart__\n"
- "Content-type: text/plain\n"
- "\n"
- "Someone (probably you) has submitted a request to subscribe\n"
- "<"), 0);
- StrBufAppendBuf(cf_req, Line->Value[0], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("> to the '"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list.\n"
- "\n"
- "Please go here to confirm this request:\n"
- " "), 0);
- StrBufAppendBuf(cf_req, Line->Value[4], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[2], 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("&cmd=confirm \n"
- "\n"
- "If this request has been submitted in error and you do not\n"
- "wish to receive the '"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list, simply do nothing,\n"
- "and you will not receive any further mailings.\n"
- "\n"
- "--__ctdlmultipart__\n"
- "Content-type: text/html\n"
- "\n"
- "<HTML><BODY>\n"
- "Someone (probably you) has submitted a request to subscribe\n"
- "<"), 0);
- StrBufAppendBuf(cf_req, Line->Value[0], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY( "> to the <B>"), 0);
-
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("'</B> mailing list.<BR><BR>\n"
- "Please click here to confirm this request:<BR>\n"
- "<A HREF=\""), 0);
- StrBufAppendBuf(cf_req, Line->Value[4], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[2], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
- StrBufAppendBuf(cf_req, Line->Value[4], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[2], 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("&cmd=confirm</A><BR><BR>\n"
- "If this request has been submitted in error and you do not\n"
- "wish to receive the '"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list, simply do nothing,\n"
- "and you will not receive any further mailings.\n"
- "</BODY></HTML>\n"
- "\n"
- "--__ctdlmultipart__--\n"), 0);
+ // And then we...
+ int is_already_subscribed = 0;
+ int config_lines = num_tokens(oldnetconfig, '\n');
+ for (i=0; i<config_lines; ++i) {
+ extract_token(buf, oldnetconfig, i, '\n', sizeof buf);
+ int keep_this_line =1; // set to nonzero if we are discarding a line
- end_critical_section(S_NETCONFIGS);
-
- pcf_req = SmashStrBuf(&cf_req);
- quickie_message( /* This delivers the message */
- "Citadel",
- EmailSender,
- ChrPtr(*email),
- NULL,
- pcf_req,
- FMT_RFC822,
- "Please confirm your list subscription"
- );
- free(pcf_req);
- cprintf("%d Subscription entered; confirmation request sent\n", CIT_OK);
-
- FreeStrBuf(&UrlRoom);
-}
+ if (IsEmptyStr(buf)) {
+ keep_this_line = 0;
+ }
+ char buf_directive[1024];
+ char buf_email[1024];
+ extract_token(buf_directive, buf, 0, '|', sizeof buf_directive);
+ extract_token(buf_email, buf, 1, '|', sizeof buf_email);
-/*
- * Enter an unsubscription request
- */
-void do_unsubscribe(StrBuf **room, StrBuf **email, StrBuf **webpage) {
- struct ctdlroom qrbuf;
- const char *EmailSender = NULL;
- char token[256];
- char *pcf_req;
- StrBuf *cf_req;
- StrBuf *UrlRoom;
- int found_sub = 0;
- const char *RoomMailAddress;
- OneRoomNetCfg *OneRNCfg;
- RoomNetCfgLine *Line;
- long RoomMailAddressLen;
-
- if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
- cprintf("%d There is no list called '%s'\n",
- ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
- return;
- }
+ if ( ( (!strcasecmp(buf_directive, "listrecp")) || (!strcasecmp(buf_directive, "digestrecp")) )
+ && (!strcasecmp(buf_email, emailaddr))
+ ) {
+ is_already_subscribed = 1;
+ }
- if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
- cprintf("%d '%s' "
- "does not accept subscribe/unsubscribe requests.\n",
- ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
- return;
+ if ( (!strcasecmp(buf_directive, "subpending")) || (!strcasecmp(buf_directive, "unsubpending")) ) {
+ time_t pendingtime = extract_long(buf, 3);
+ if ((time(NULL) - pendingtime) > 259200) {
+ syslog(LOG_DEBUG, "%s %s is %ld seconds old - deleting it", buf_email, buf_directive, time(NULL) - pendingtime);
+ keep_this_line = 0;
+ }
+ }
+
+ if (keep_this_line) {
+ sprintf(&newnetconfig[strlen(newnetconfig)], "%s\n", buf);
+ }
}
- listsub_generate_token(token);
-
- /*
- * Make sure there's actually a subscription there to remove
- */
- begin_critical_section(S_NETCONFIGS);
- RoomMailAddress = qrbuf.QRname;
- OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
- if (OneRNCfg!=NULL) {
- found_sub = CountThisSubscriber(OneRNCfg, *email);
- if (StrLength(OneRNCfg->Sender) > 0)
- EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
+ // Do we need to send out a confirmation email?
+ if ((action == SUBSCRIBE) && (!is_already_subscribed)) {
+ generate_uuid(confirmation_token);
+ sprintf(&newnetconfig[strlen(newnetconfig)], "subpending|%s|%s|%ld|%s", emailaddr, confirmation_token, time(NULL), url);
+ send_subscribe_confirmation_email(CC->room.QRname, emailaddr, url, confirmation_token);
}
-
- if (found_sub == 0) {
- cprintf("%d '%s' is not subscribed to '%s'.\n",
- ERROR + NO_SUCH_USER,
- ChrPtr(*email),
- qrbuf.QRname);
-
- end_critical_section(S_NETCONFIGS);
- return;
+ if ((action == UNSUBSCRIBE) && (is_already_subscribed)) {
+ generate_uuid(confirmation_token);
+ sprintf(&newnetconfig[strlen(newnetconfig)], "unsubpending|%s|%s|%ld|%s", emailaddr, confirmation_token, time(NULL), url);
+ send_unsubscribe_confirmation_email(CC->room.QRname, emailaddr, url, confirmation_token);
}
-
- /*
- * Ok, now enter the unsubscribe-pending entry.
- */
- RoomMailAddressLen = strlen(RoomMailAddress);
- listsub_generate_token(token);
- Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
- memset(Line, 0, sizeof(RoomNetCfgLine));
-
- Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 4);
-
- Line->Value[0] = NewStrBufDup(*email);
- Line->Value[1] = NewStrBufPlain(token, -1);
- Line->Value[2] = NewStrBufPlain(NULL, 10);
- StrBufPrintf(Line->Value[2], "%ld", time(NULL));
- Line->Value[3] = *webpage; *webpage = NULL;
- Line->nValues = 4;
-
- AddRoomCfgLine(OneRNCfg, &qrbuf, unsubpending, Line);
-
- /* Generate and send the confirmation request */
- UrlRoom = NewStrBuf();
- StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
-
- cf_req = NewStrBufPlain(NULL, 2048);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("MIME-Version: 1.0\n"
- "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
- "\n"
- "This is a multipart message in MIME format.\n"
- "\n"
- "--__ctdlmultipart__\n"
- "Content-type: text/plain\n"
- "\n"
- "Someone (probably you) has submitted a request to unsubscribe\n"
- "<"), 0);
- StrBufAppendBuf(cf_req, Line->Value[0], 0);
-
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("> from the '"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list.\n"
- "\n"
- "Please go here to confirm this request:\n "), 0);
- StrBufAppendBuf(cf_req, Line->Value[3], 0);
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[1], 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("&cmd=confirm \n"
- "\n"
- "If this request has been submitted in error and you do not\n"
- "wish to unsubscribe from the '"), 0);
-
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list, simply do nothing,\n"
- "and the request will not be processed.\n"
- "\n"
- "--__ctdlmultipart__\n"
- "Content-type: text/html\n"
- "\n"
- "<HTML><BODY>\n"
- "Someone (probably you) has submitted a request to unsubscribe\n"
- "<"), 0);
- StrBufAppendBuf(cf_req, Line->Value[0], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("> from the <B>"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("</B> mailing list.<BR><BR>\n"
- "Please click here to confirm this request:<BR>\n"
- "<A HREF=\""), 0);
- StrBufAppendBuf(cf_req, Line->Value[3], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[1], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
- StrBufAppendBuf(cf_req, Line->Value[3], 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
- StrBufAppendBuf(cf_req, UrlRoom, 0);
-
- StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
- StrBufAppendBuf(cf_req, Line->Value[1], 0);
-
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("&cmd=confirm</A><BR><BR>\n"
- "If this request has been submitted in error and you do not\n"
- "wish to unsubscribe from the '"), 0);
- StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
-
- StrBufAppendBufPlain(
- cf_req,
- HKEY("' mailing list, simply do nothing,\n"
- "and the request will not be processed.\n"
- "</BODY></HTML>\n"
- "\n"
- "--__ctdlmultipart__--\n"), 0);
+ // Write the new netconfig back to disk
+ SaveRoomNetConfigFile(CC->room.QRnumber, newnetconfig);
end_critical_section(S_NETCONFIGS);
+ free(newnetconfig); // this was the new netconfig, free it because we're done with it
+ free(oldnetconfig); // this was the old netconfig, free it even if we didn't do anything
- pcf_req = SmashStrBuf(&cf_req);
- quickie_message( /* This delivers the message */
- "Citadel",
- EmailSender,
- ChrPtr(*email),
- NULL,
- pcf_req,
- FMT_RFC822,
- "Please confirm your unsubscribe request"
- );
-
- free(pcf_req);
- FreeStrBuf(&UrlRoom);
- cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK);
+ // Tell the client what happened.
+ if ((action == SUBSCRIBE) && (is_already_subscribed)) {
+ cprintf("%d This email address is already subscribed.\n", ERROR + ALREADY_EXISTS);
+ }
+ else if ((action == SUBSCRIBE) && (!is_already_subscribed)) {
+ cprintf("%d Subscription was requested, and a confirmation email was sent.\n", CIT_OK);
+ }
+ else if ((action == UNSUBSCRIBE) && (!is_already_subscribed)) {
+ cprintf("%d This email address is not subscribed.\n", ERROR + NO_SUCH_USER);
+ }
+ else if ((action == UNSUBSCRIBE) && (is_already_subscribed)) {
+ cprintf("%d Unsubscription was requested, and a confirmation email was sent.\n", CIT_OK);
+ }
+ else {
+ cprintf("%d Nothing happens.\n", ERROR);
+ }
}
-const RoomNetCfg ConfirmSubscribers[] = {subpending, unsubpending};
-
/*
- * Confirm a subscribe/unsubscribe request.
+ * Confirm a list subscription or unsubscription
*/
-void do_confirm(StrBuf **room, StrBuf **token) {
- struct ctdlroom qrbuf;
- OneRoomNetCfg *OneRNCfg;
- RoomNetCfgLine *Line;
- RoomNetCfgLine *ConfirmLine = NULL;
- RoomNetCfgLine *RemoveLine = NULL;
- RoomNetCfgLine **PrevLine;
- int success = 0;
- RoomNetCfg ConfirmType;
- const char *errmsg = "";
+void do_confirm(char *token) {
+ int yes_subscribe = 0; // Set to 1 if the confirmation to subscribe is validated.
+ int yes_unsubscribe = 0; // Set to 1 if the confirmation to unsubscribe is validated.
int i;
-
- if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
- cprintf("%d There is no list called '%s'\n",
- ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
+ char buf[1024];
+ int config_lines = 0;
+ char pending_directive[128];
+ char pending_email[256];
+ char pending_token[128];
+
+ // We will have to do this in two passes. The first pass checks to see if we have a confirmation request matching the token.
+ char *oldnetconfig = LoadRoomNetConfigFile(CC->room.QRnumber);
+ if (!oldnetconfig) {
+ cprintf("%d There are no pending requests.\n", ERROR + NO_SUCH_USER);
return;
}
- if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
- cprintf("%d '%s' "
- "does not accept subscribe/unsubscribe requests.\n",
- ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
- return;
- }
-
-
- if (StrLength(*token) == 0) {
- cprintf("%d empty token.\n", ERROR + ILLEGAL_VALUE);
- return;
- }
- /*
- * Now start scanning this room's netconfig file for the
- * specified token.
- */
- begin_critical_section(S_NETCONFIGS);
- OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
-
- ConfirmType = maxRoomNetCfg;
- if (OneRNCfg==NULL)
- {
- errmsg = "no networking config found";
- }
- else for (i = 0; i < 2; i++)
- {
- int offset;
-
- if (ConfirmSubscribers[i] == subpending)
- offset = 2;
- else
- offset = 1;
- PrevLine = &OneRNCfg->NetConfigs[ConfirmSubscribers[i]];
- Line = *PrevLine;
- while (Line != NULL)
- {
- if (!strcasecmp(ChrPtr(*token),
- ChrPtr(Line->Value[offset])))
- {
- ConfirmLine = Line;
- *PrevLine = Line->next; /* Remove it from the list */
- ConfirmType = ConfirmSubscribers[i];
- ConfirmLine->next = NULL;
-
- i += 100;
- break;
+ config_lines = num_tokens(oldnetconfig, '\n');
+ for (i=0; i<config_lines; ++i) {
+ extract_token(buf, oldnetconfig, i, '\n', sizeof buf);
+ extract_token(pending_directive, buf, 0, '|', sizeof pending_directive);
+ extract_token(pending_email, buf, 1, '|', sizeof pending_email);
+ extract_token(pending_token, buf, 2, '|', sizeof pending_token);
+ if (!strcasecmp(pending_token, token)) {
+ if (!strcasecmp(pending_directive, "subpending")) {
+ yes_subscribe = 1;
+ }
+ else if (!strcasecmp(pending_directive, "unsubpending")) {
+ yes_unsubscribe = 1;
}
- PrevLine = &(*PrevLine)->next;
- Line = Line->next;
- }
- if (ConfirmType == maxRoomNetCfg)
- {
- errmsg = "No active un/subscribe request found";
}
}
+ free(oldnetconfig);
- if (ConfirmType == subpending)
- {
- if (CountThisSubscriber(OneRNCfg, ConfirmLine->Value[0]) == 0)
- {
- if (!strcasecmp(ChrPtr(ConfirmLine->Value[2]),
- ("digest")))
- {
- ConfirmType = digestrecp;
- }
- else /* "list" */
- {
- ConfirmType = listrecp;
- }
-
- syslog(LOG_NOTICE,
- "Mailing list: %s subscribed to %s with token %s\n",
- ChrPtr(ConfirmLine->Value[0]),
- qrbuf.QRname,
- ChrPtr(*token));
-
- FreeStrBuf(&ConfirmLine->Value[1]);
- FreeStrBuf(&ConfirmLine->Value[2]);
- FreeStrBuf(&ConfirmLine->Value[3]);
- FreeStrBuf(&ConfirmLine->Value[4]);
- ConfirmLine->nValues = 1;
-
- AddRoomCfgLine(OneRNCfg, &qrbuf, ConfirmType, ConfirmLine);
- success = 1;
- }
- else
- {
- /* whipe duplicate subscribe entry... */
- OneRNCfg->changed = 1;
- SaveChangedConfigs();
- errmsg = "already subscribed";
- }
+ // We didn't find a pending subscribe or unsubscribe request with the supplied token.
+ if ((!yes_subscribe) && (!yes_unsubscribe)) {
+ cprintf("%d The request you are trying to confirm was not found.\n", ERROR + NO_SUCH_USER);
+ return;
}
- else if (ConfirmType == unsubpending)
- {
- for (i = 0; i < 2; i++)
- {
- PrevLine = &OneRNCfg->NetConfigs[ActiveSubscribers[i]];
- Line = *PrevLine;
- while (Line != NULL)
- {
- if (!strcasecmp(ChrPtr(ConfirmLine->Value[0]),
- ChrPtr(Line->Value[0])))
- {
- success = 1;
- RemoveLine = Line;
- *PrevLine = Line->next; /* Remove it from the list */
- RemoveLine->next = NULL;
- if (RemoveLine != NULL)
- DeleteGenericCfgLine(NULL/*TODO*/, &RemoveLine);
- Line = *PrevLine;
- continue;
- }
- PrevLine = &(*PrevLine)->next;
- Line = Line->next;
- }
- }
+ // The second pass performs the now confirmed operation.
+ // We will have to do this in two passes. The first pass checks to see if we have a confirmation request matching the token.
+ oldnetconfig = LoadRoomNetConfigFile(CC->room.QRnumber);
+ if (!oldnetconfig) {
+ oldnetconfig = strdup("");
+ }
- if (success)
- {
- syslog(LOG_NOTICE,
- "Mailing list: %s unsubscribed to %s with token %s\n",
- ChrPtr(ConfirmLine->Value[0]),
- qrbuf.QRname,
- ChrPtr(*token));
- }
- else
- {
- errmsg = "no subscriber found for this unsubscription request";
+ // The new netconfig begins with an empty buffer...
+ begin_critical_section(S_NETCONFIGS);
+ char *newnetconfig = malloc(strlen(oldnetconfig) + 1024);
+ newnetconfig[0] = 0;
+
+ config_lines = num_tokens(oldnetconfig, '\n');
+ for (i=0; i<config_lines; ++i) {
+ char buf_email[256];
+ extract_token(buf, oldnetconfig, i, '\n', sizeof buf);
+ extract_token(buf_email, buf, 1, '|', sizeof pending_email);
+ if (strcasecmp(buf_email, pending_email)) {
+ sprintf(&newnetconfig[strlen(newnetconfig)], "%s\n", buf); // only keep lines that do not reference this subscriber
}
- DeleteGenericCfgLine(NULL/*TODO*/, &ConfirmLine);
- OneRNCfg->changed = 1;
- SaveChangedConfigs();
}
- end_critical_section(S_NETCONFIGS);
-
- /*
- * Did we do anything useful today?
- */
- if (success) {
- cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success);
- }
- else {
- syslog(LOG_NOTICE, "failed processing (un)subscribe request: %s",
- errmsg);
- cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE);
+ // We have now removed all lines containing the subscriber's email address. This deletes any pending requests.
+ // If this was an unsubscribe operation, they're now gone from the list.
+ // But if this was a subscribe operation, we now need to add them.
+ if (yes_subscribe) {
+ sprintf(&newnetconfig[strlen(newnetconfig)], "listrecp|%s\n", pending_email);
}
+ // FIXME write it back to disk
+ SaveRoomNetConfigFile(CC->room.QRnumber, newnetconfig);
+ end_critical_section(S_NETCONFIGS);
+ free(oldnetconfig);
+ free(newnetconfig);
+ cprintf("%d The pending request was confirmed.\n", CIT_OK);
}
-
/*
* process subscribe/unsubscribe requests and confirmations
*/
-void cmd_subs(char *cmdbuf)
-{
- const char *Pos = NULL;
- StrBuf *Segments[20];
- int i=1;
-
- memset(Segments, 0, sizeof(StrBuf*) * 20);
- Segments[0] = NewStrBufPlain(cmdbuf, -1);
- while ((Pos != StrBufNOTNULL) && (i < 20))
- {
- Segments[i] = NewStrBufPlain(NULL, StrLength(Segments[0]));
- StrBufExtract_NextToken(Segments[i], Segments[0], &Pos, '|');
- i++;
+void cmd_lsub(char *cmdbuf) {
+ char cmd[20];
+ char roomname[ROOMNAMELEN];
+ char emailaddr[1024];
+ char options[256];
+ char url[1024];
+ char token[128];
+
+ extract_token(cmd, cmdbuf, 0, '|', sizeof cmd); // token 0 is the sub-command being sent
+ extract_token(roomname, cmdbuf, 1, '|', sizeof roomname); // token 1 is always a room name
+
+ // First confirm that the caller is referencing a room that actually exists.
+ if (CtdlGetRoom(&CC->room, roomname) != 0) {
+ cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, roomname);
+ return;
}
- if (!strcasecmp(ChrPtr(Segments[1]), "subscribe")) {
- if ( (strcasecmp(ChrPtr(Segments[4]), "list"))
- && (strcasecmp(ChrPtr(Segments[4]), "digest")) ) {
- cprintf("%d Invalid subscription type '%s'\n",
- ERROR + ILLEGAL_VALUE, ChrPtr(Segments[4]));
- }
- else {
- do_subscribe(&Segments[2], &Segments[3], &Segments[4], &Segments[5]);
- }
+ if ((CC->room.QRflags2 & QR2_SELFLIST) == 0) {
+ cprintf("%d '%s' does not accept subscribe/unsubscribe requests.\n", ERROR + ROOM_NOT_FOUND, roomname);
+ return;
}
- else if (!strcasecmp(ChrPtr(Segments[1]), "unsubscribe")) {
- do_unsubscribe(&Segments[2], &Segments[3], &Segments[4]);
+
+ // Room confirmed, now parse the command.
+
+ if (!strcasecmp(cmd, "subscribe")) {
+ extract_token(emailaddr, cmdbuf, 2, '|', sizeof emailaddr); // token 2 is the subscriber's email address
+ extract_token(url, cmdbuf, 3, '|', sizeof url); // token 3 is the URL at which we subscribed
+ do_subscribe_or_unsubscribe(SUBSCRIBE, emailaddr, url);
}
- else if (!strcasecmp(ChrPtr(Segments[1]), "confirm")) {
- do_confirm(&Segments[2], &Segments[3]);
+
+ else if (!strcasecmp(cmd, "unsubscribe")) {
+ extract_token(emailaddr, cmdbuf, 2, '|', sizeof emailaddr); // token 2 is the subscriber's email address
+ extract_token(url, cmdbuf, 3, '|', sizeof url); // token 3 is the URL at which we subscribed
+ do_subscribe_or_unsubscribe(UNSUBSCRIBE, emailaddr, url);
}
- else {
- cprintf("%d Invalid command\n", ERROR + ILLEGAL_VALUE);
+
+ else if (!strcasecmp(cmd, "confirm")) {
+ extract_token(token, cmdbuf, 2, '|', sizeof token); // token 2 is the confirmation token
+ do_confirm(token);
}
- for (; i>=0; i--)
- {
- FreeStrBuf(&Segments[i]);
+ else { // sorry man, I can't deal with that
+ cprintf("%d Invalid command '%s'\n", ERROR + ILLEGAL_VALUE, cmd);
}
}
{
if (!threading)
{
- CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe");
+ CtdlRegisterProtoHook(cmd_lsub, "LSUB", "List subscribe/unsubscribe");
}
/* return our module name for the log */