X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Flistsub%2Fserv_listsub.c;h=9cf3fb86c83f9ebf1bb94c8e66eec66771602b2c;hb=18a23ae97de67506094a26e92f60af36b0036d91;hp=ae8554bafb0ce9f065b60fcc220949126a2e0211;hpb=79bddf1aaf7c65188a812102bd91c6158ce3f0a4;p=citadel.git diff --git a/citadel/modules/listsub/serv_listsub.c b/citadel/modules/listsub/serv_listsub.c index ae8554baf..9cf3fb86c 100644 --- a/citadel/modules/listsub/serv_listsub.c +++ b/citadel/modules/listsub/serv_listsub.c @@ -1,16 +1,17 @@ -/* - * 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 @@ -23,17 +24,7 @@ #include #include #include -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - +#include #include #include #include @@ -50,655 +41,271 @@ #include "clientsocket.h" #include "ctdl_module.h" -/* - * Generate a randomizationalisticized token to use for authentication of - * a subscribe or unsubscribe request. - */ -void listsub_generate_token(char *buf) { - char sourcebuf[SIZ]; - static int seq = 0; - size_t len; - - /* 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. - */ - len = sprintf(sourcebuf, "%lx", - (long) (++seq + getpid() + time(NULL)) - ); - - /* Convert it to base64 so it looks cool */ - len = CtdlEncodeBase64(buf, sourcebuf, len, 0); - if (buf[len - 1] == '\n') { - buf[len - 1] = '\0'; - } -} -const RoomNetCfg ActiveSubscribers[] = {listrecp, digestrecp}; +enum { // one of these gets passed to do_subscribe_or_unsubscribe() so it knows what we asked for + UNSUBSCRIBE, + SUBSCRIBE +}; -int CountThisSubscriber(OneRoomNetCfg *OneRNCfg, StrBuf *email) -{ - RoomNetCfgLine *Line; - int found_sub = 0; - int i; - - 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; -} /* - * Enter a subscription request + * This generates an email with a link the user clicks to confirm a list subscription. */ -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; - } - - if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) { - cprintf("%d '%s' " - "does not accept subscribe/unsubscribe requests.\n", - ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname); - return; - } - - /* - * Make sure the requested address isn't already subscribed - */ - 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; - } - - /* - * Now add it to the config - */ - - RoomMailAddressLen = strlen(RoomMailAddress); - listsub_generate_token(token); - Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine)); - memset(Line, 0, sizeof(RoomNetCfgLine)); - - 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" - "\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" - "Please click here to confirm this request:
\n" - "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

\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" - "\n" - "--__ctdlmultipart__--\n"), 0); - - end_critical_section(S_NETCONFIGS); +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" + "

Someone (probably you) has submitted a request to subscribe " + "%s to the %s mailing list.

" + "

Please go here to confirm this request:

" + "

" + "%s?room=%s&token=%s&cmd=confirm

" + "

If this request has been submitted in error and you do not " + "wish to receive the %s mailing list, simply do nothing, " + "and you will not receive any further mailings.

" + "\n" + "\n" + "--__ctdlmultipart__--\n" + , + emailaddr, roomname, url, urlroom, confirmation_token, roomname, + emailaddr, roomname, + url, urlroom, confirmation_token, + url, urlroom, confirmation_token, + roomname + ); - 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); + quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list subscription"); } /* - * Enter an unsubscription request + * This generates an email with a link the user clicks to confirm a list unsubscription. */ -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 ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) { - cprintf("%d '%s' " - "does not accept subscribe/unsubscribe requests.\n", - ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname); - return; - } - - 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); - } - - 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; - } - - /* - * 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" - "\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" - "Please click here to confirm this request:
\n" - "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

\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" - "\n" - "--__ctdlmultipart__--\n"), 0); - - 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 unsubscribe request" +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" + "

Someone (probably you) has submitted a request to unsubscribe " + "%s from the %s mailing list.

" + "

Please go here to confirm this request:

" + "

" + "%s?room=%s&token=%s&cmd=confirm

" + "

If this request has been submitted in error and you still " + "wish to receive the %s mailing list, simply do nothing, " + "and you will remain subscribed.

" + "\n" + "\n" + "--__ctdlmultipart__--\n" + , + emailaddr, roomname, url, urlroom, confirmation_token, roomname, + emailaddr, roomname, + url, urlroom, confirmation_token, + url, urlroom, confirmation_token, + roomname ); - free(pcf_req); - FreeStrBuf(&UrlRoom); - cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK); + quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list subscription"); } -const RoomNetCfg ConfirmSubscribers[] = {subpending, unsubpending}; - /* - * Confirm a subscribe/unsubscribe 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_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_subscribe_or_unsubscribe(int action, char *emailaddr, char *url) { + int i; - - if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) { - cprintf("%d There is no list called '%s'\n", - ERROR + ROOM_NOT_FOUND, ChrPtr(*room)); - return; - } + char buf[1024]; + char confirmation_token[40]; - if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) { - cprintf("%d '%s' " - "does not accept subscribe/unsubscribe requests.\n", - ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname); - return; + // Update this room's netconfig with the updated lastsent + begin_critical_section(S_NETCONFIGS); + char *oldnetconfig = LoadRoomNetConfigFile(CC->room.QRnumber); + if (!oldnetconfig) { + oldnetconfig = strdup(""); } + // The new netconfig begins with an empty buffer... + char *newnetconfig = malloc(strlen(oldnetconfig) + 1024); + newnetconfig[0] = 0; - 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); + // And then we... + int is_already_subscribed = 0; + int config_lines = num_tokens(oldnetconfig, '\n'); + for (i=0; iNetConfigs[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; - - } - PrevLine = &(*PrevLine)->next; - Line = Line->next; + if (IsEmptyStr(buf)) { + keep_this_line = 0; } - if (ConfirmType == maxRoomNetCfg) - { - errmsg = "No active un/subscribe request found"; - } - } - if (ConfirmType == subpending) - { - if (CountThisSubscriber(OneRNCfg, ConfirmLine->Value[0]) == 0) - { - if (!strcasecmp(ChrPtr(ConfirmLine->Value[2]), - ("digest"))) - { - ConfirmType = digestrecp; - } - else /* "list" */ - { - ConfirmType = listrecp; - } + char buf_token[1024]; + char buf_email[1024]; + extract_token(buf_token, buf, 0, '|', sizeof buf_token); + extract_token(buf_email, buf, 1, '|', sizeof buf_email); - 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; + if ( ( (!strcasecmp(buf_token, "listrecp")) || (!strcasecmp(buf_token, "digestrecp")) ) + && (!strcasecmp(buf_email, emailaddr)) + ) { + is_already_subscribed = 1; } - else - { - /* whipe duplicate subscribe entry... */ - OneRNCfg->changed = 1; - // SaveChangedConfigs(); FIXME FOOFOO SAVE CONFIG HERE - errmsg = "already subscribed"; - } - } - 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; + if ( (!strcasecmp(buf_token, "subpending")) || (!strcasecmp(buf_token, "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_token, time(NULL) - pendingtime); + keep_this_line = 0; } } - - 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"; + + if (keep_this_line) { + sprintf(&newnetconfig[strlen(newnetconfig)], "%s\n", buf); } - DeleteGenericCfgLine(NULL/*TODO*/, &ConfirmLine); - OneRNCfg->changed = 1; - // SaveChangedConfigs(); FIXME FOOFOO SAVE CONFIG HERE } + // 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 ((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); + } + + // Write the new netconfig back to disk + syslog(LOG_DEBUG, "old: <\033[31m%s\033[0m>", oldnetconfig); + syslog(LOG_DEBUG, "new: <\033[32m%s\033[0m>", newnetconfig); + SaveRoomNetConfigFile(CC->room.QRnumber, newnetconfig); end_critical_section(S_NETCONFIGS); - - /* - * Did we do anything useful today? - */ - if (success) { - cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success); + 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 + + // Tell the client what happened. + if ((action == SUBSCRIBE) && (is_already_subscribed)) { + cprintf("%d This email is already subscribed.\n", ERROR + ALREADY_EXISTS); + } + else if ((action == SUBSCRIBE) && (!is_already_subscribed)) { + cprintf("%d Confirmation email sent.\n", CIT_OK); + } + else if ((action == UNSUBSCRIBE) && (!is_already_subscribed)) { + cprintf("%d This email is not subscribed.\n", ERROR + NO_SUCH_USER); + } + else if ((action == UNSUBSCRIBE) && (is_already_subscribed)) { + cprintf("%d Confirmation email sent.\n", CIT_OK); } else { - syslog(LOG_NOTICE, "failed processing (un)subscribe request: %s", - errmsg); - cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE); + cprintf("%d FIXME tell the client what we did\n", ERROR); } - } - /* * 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_subs(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(options, cmdbuf, 3, '|', sizeof options); // there are no options ... ignore this token + extract_token(url, cmdbuf, 4, '|', 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(options, cmdbuf, 3, '|', sizeof options); // there are no options ... ignore this token + extract_token(url, cmdbuf, 4, '|', 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 + cprintf("%d not implemented\n", ERROR); } - 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); } }