X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Flistsub%2Fserv_listsub.c;h=0c8335a50fd515faee319a58d72447297ef30a8c;hb=60e1890d866f912f94b604f3af05bcd421109469;hp=073ddafaee01c7cd8b0e771968db1fd76df6a8e7;hpb=6473885628e33ec5633bb05caa92391aa34cff03;p=citadel.git diff --git a/citadel/modules/listsub/serv_listsub.c b/citadel/modules/listsub/serv_listsub.c index 073ddafae..0c8335a50 100644 --- a/citadel/modules/listsub/serv_listsub.c +++ b/citadel/modules/listsub/serv_listsub.c @@ -1,7 +1,6 @@ -// // This module handles self-service subscription/unsubscription to mail lists. // -// Copyright (c) 2002-2021 by the citadel.org team +// Copyright (c) 2002-2022 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, @@ -28,6 +27,7 @@ #include #include #include +#include #include #include "citadel.h" #include "server.h" @@ -48,9 +48,26 @@ enum { // one of these gets passed to do_subscribe_or_unsubscribe() so it kno }; -/* - * This generates an email with a link the user clicks to confirm a list subscription. - */ +// The confirmation token will be generated by combining the room name and email address with the host key, +// and then generating an encrypted hash of that string. The encrypted hash is included as part of the +// confirmation link. +void generate_confirmation_token(char *token_buf, size_t token_buf_len, char *roomname, char *emailaddr) { + char string_to_hash[1024]; + struct crypt_data cd; + char *ptr; + + snprintf(string_to_hash, sizeof string_to_hash, "%s|%s|%s", roomname, emailaddr, CtdlGetConfigStr("host_key")); + memset(&cd, 0, sizeof cd); + + strncpy(token_buf, crypt_r(string_to_hash, "$1$ctdl", &cd), token_buf_len); + + for (ptr=token_buf; *ptr; ++ptr) { + if (!isalnum((char)*ptr)) *ptr='X'; + } +} + + +// 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]; @@ -73,7 +90,7 @@ void send_subscribe_confirmation_email(char *roomname, char *emailaddr, char *ur "<%s> to the <%s> mailing list.\n" "\n" "Please go here to confirm this request:\n" - "%s?room=%s&token=%s&cmd=confirm\n" + "%s?cmd=confirm_subscribe&email=%s&room=%s&token=%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" @@ -85,8 +102,8 @@ void send_subscribe_confirmation_email(char *roomname, char *emailaddr, char *ur "

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

" + "

" + "%s?cmd=confirm_subscribe&email=%s&room=%s&token=%s

" "

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.

" @@ -95,22 +112,21 @@ void send_subscribe_confirmation_email(char *roomname, char *emailaddr, char *ur "--__ctdlmultipart__--\n" , emailaddr, roomname, - url, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, roomname , emailaddr, roomname, - url, urlroom, confirmation_token, - url, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, roomname ); quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list subscription"); + cprintf("%d confirmation email sent\n", CIT_OK); } -/* - * This generates an email with a link the user clicks to confirm a list unsubscription. - */ +// 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]; @@ -133,7 +149,7 @@ void send_unsubscribe_confirmation_email(char *roomname, char *emailaddr, char * "<%s> from the <%s> mailing list.\n" "\n" "Please go here to confirm this request:\n" - "%s?room=%s&token=%s&cmd=confirm\n" + "%s?cmd=confirm_unsubscribe&email=%s&room=%s&token=%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" @@ -145,8 +161,8 @@ void send_unsubscribe_confirmation_email(char *roomname, char *emailaddr, char * "

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

" + "

" + "%s?cmd=confirm_unsubscribe&email=%s&room=%s&token=%s

" "

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.

" @@ -155,159 +171,37 @@ void send_unsubscribe_confirmation_email(char *roomname, char *emailaddr, char * "--__ctdlmultipart__--\n" , emailaddr, roomname, - url, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, roomname , emailaddr, roomname, - url, urlroom, confirmation_token, - url, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, + url, emailaddr, urlroom, confirmation_token, roomname ); quickie_message("Citadel", from_address, emailaddr, NULL, emailtext, FMT_RFC822, "Please confirm your list unsubscription"); + cprintf("%d confirmation email sent\n", CIT_OK); } -/* - * "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_or_unsubscribe(int action, char *emailaddr, char *url) { - - int i; - char buf[1024]; - char confirmation_token[40]; - - // 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; - - // And then we... - int is_already_subscribed = 0; - int config_lines = num_tokens(oldnetconfig, '\n'); - for (i=0; i 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); - } - } - - // 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 - 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 - - // 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); - } -} - - -/* - * Confirm a list subscription or unsubscription - */ -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. +// Confirm a list subscription or unsubscription +void do_confirm(int cmd, char *roomname, char *emailaddr, char *url, char *generated_token, char *supplied_token) { int i; char buf[1024]; int config_lines = 0; - char pending_directive[128]; - char pending_email[256]; - char pending_token[128]; + char *oldnetconfig, *newnetconfig; - // 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); + // The server has generated a persistent confirmation token for the user+room combination. + // Let's see if the user has supplied the same token. + if (strcmp(generated_token, supplied_token)) { + cprintf("%d This request could not be authenticated.\n", ERROR + PASSWORD_REQUIRED); return; } - config_lines = num_tokens(oldnetconfig, '\n'); - for (i=0; iroom.QRnumber); if (!oldnetconfig) { oldnetconfig = strdup(""); @@ -315,27 +209,28 @@ void do_confirm(char *token) { // The new netconfig begins with an empty buffer... begin_critical_section(S_NETCONFIGS); - char *newnetconfig = malloc(strlen(oldnetconfig) + 1024); + newnetconfig = malloc(strlen(oldnetconfig) + 1024); newnetconfig[0] = 0; + // Load the config lines in one by one, skipping any that reference this subscriber. Also remove blank lines. config_lines = num_tokens(oldnetconfig, '\n'); for (i=0; iroom.QRnumber, newnetconfig); end_critical_section(S_NETCONFIGS); free(oldnetconfig); @@ -344,18 +239,24 @@ void do_confirm(char *token) { } -/* - * process subscribe/unsubscribe requests and confirmations - */ +// process subscribe/unsubscribe requests and confirmations void cmd_lsub(char *cmdbuf) { char cmd[20]; char roomname[ROOMNAMELEN]; char emailaddr[1024]; char url[1024]; - char token[128]; + char generated_token[128]; + char supplied_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 + 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 + extract_token(supplied_token, cmdbuf, 4, '|', sizeof supplied_token); // token 4 is the token supplied by the caller + + syslog(LOG_DEBUG, "cmd_lsub(cmd=%s, roomname=%s, emailaddr=%s, url=%s, token=%s", + cmd, roomname, emailaddr, url, supplied_token + ); // First confirm that the caller is referencing a room that actually exists. if (CtdlGetRoom(&CC->room, roomname) != 0) { @@ -369,22 +270,22 @@ void cmd_lsub(char *cmdbuf) { } // Room confirmed, now parse the command. + generate_confirmation_token(generated_token, sizeof generated_token, roomname, emailaddr); 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); + send_subscribe_confirmation_email(roomname, emailaddr, url, generated_token); } 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); + send_unsubscribe_confirmation_email(roomname, emailaddr, url, generated_token); + } + + else if (!strcasecmp(cmd, "confirm_subscribe")) { + do_confirm(SUBSCRIBE, roomname, emailaddr, url, generated_token, supplied_token); } - else if (!strcasecmp(cmd, "confirm")) { - extract_token(token, cmdbuf, 2, '|', sizeof token); // token 2 is the confirmation token - do_confirm(token); + else if (!strcasecmp(cmd, "confirm_unsubscribe")) { + do_confirm(UNSUBSCRIBE, roomname, emailaddr, url, generated_token, supplied_token); } else { // sorry man, I can't deal with that