X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Flistsub%2Fserv_listsub.c;h=72249ed537d6b2813a4efe96ee1c8e4bd20e1884;hb=51b18018ff923284d76a36cbd421d62abf6afcf4;hp=615bcc5b59685ed6ae7f9af456714d819b4d4b4e;hpb=84a0666848ccbea920f0bd398baf149c1a12ea74;p=citadel.git diff --git a/citadel/modules/listsub/serv_listsub.c b/citadel/modules/listsub/serv_listsub.c index 615bcc5b5..72249ed53 100644 --- a/citadel/modules/listsub/serv_listsub.c +++ b/citadel/modules/listsub/serv_listsub.c @@ -1,12 +1,17 @@ -/* - * $Id$ - * - * This module handles self-service subscription/unsubscription to mail lists. - * - * Copyright (C) 2002-2005 by Art Cancro and others. - * This code is released under the terms of the GNU General Public License. - * - */ +// +// 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 @@ -19,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 @@ -39,528 +34,115 @@ #include "citserver.h" #include "support.h" #include "config.h" -#include "room_ops.h" #include "user_ops.h" -#include "policy.h" #include "database.h" #include "msgbase.h" #include "internet_addressing.h" #include "clientsocket.h" -#include "file_ops.h" - -#ifndef HAVE_SNPRINTF -#include "snprintf.h" -#endif - - - #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; - - /* 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)) - ); - - /* Convert it to base64 so it looks cool */ - CtdlEncodeBase64(buf, sourcebuf, strlen(sourcebuf), 0); -} +enum { // one of these gets passed to do_subscribe_or_unsubscribe() so it knows what we asked for + UNSUBSCRIBE, + SUBSCRIBE +}; /* - * 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(char *room, char *email, char *subtype, char *webpage) { - struct ctdlroom qrbuf; - FILE *ncfp; - char filename[256]; - char token[256]; - char confirmation_request[2048]; - char buf[512]; - char urlroom[ROOMNAMELEN]; - char scancmd[64]; - char scanemail[256]; - int found_sub = 0; - - if (getroom(&qrbuf, room) != 0) { - cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, 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); - - assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir); - - /* - * Make sure the requested address isn't already subscribed - */ - begin_critical_section(S_NETCONFIGS); - ncfp = fopen(filename, "r"); - if (ncfp != NULL) { - while (fgets(buf, sizeof buf, ncfp) != NULL) { - buf[strlen(buf)-1] = 0; - extract_token(scancmd, buf, 0, '|', sizeof scancmd); - extract_token(scanemail, buf, 1, '|', sizeof scanemail); - if ((!strcasecmp(scancmd, "listrecp")) - || (!strcasecmp(scancmd, "digestrecp"))) { - if (!strcasecmp(scanemail, email)) { - ++found_sub; - } - } - } - fclose(ncfp); - } - end_critical_section(S_NETCONFIGS); +void do_subscribe_or_unsubscribe(int action, char *emailaddr, char *url) { - if (found_sub != 0) { - cprintf("%d '%s' is already subscribed to '%s'.\n", - ERROR + ALREADY_EXISTS, - email, qrbuf.QRname); - return; - } + char *netconfig, *newnetconfig; + int config_lines, i; + char buf[1024]; - /* - * Now add it to the file - */ + // Update this room's netconfig with the updated lastsent begin_critical_section(S_NETCONFIGS); - ncfp = fopen(filename, "a"); - if (ncfp != NULL) { - fprintf(ncfp, "subpending|%s|%s|%s|%ld|%s\n", - email, - subtype, - token, - time(NULL), - webpage - ); - fclose(ncfp); - } - end_critical_section(S_NETCONFIGS); - - /* Generate and send the confirmation request */ - - urlesc(urlroom, qrbuf.QRname); - - snprintf(confirmation_request, sizeof confirmation_request, - - "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" - "\n" - "Someone (probably you) has submitted a request to subscribe\n" - "<%s> to the %s mailing list.

\n" - "Please click here to confirm this request:
\n" - "" - "%s?room=%s&token=%s&cmd=confirm

\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" - "\n" - "--__ctdlmultipart__--\n", - - email, qrbuf.QRname, - webpage, urlroom, token, - qrbuf.QRname, - - email, qrbuf.QRname, - webpage, urlroom, token, - webpage, urlroom, token, - qrbuf.QRname - ); - - quickie_message( /* This delivers the message */ - "Citadel", - NULL, - email, - NULL, - confirmation_request, - FMT_RFC822, - "Please confirm your list subscription" - ); - - cprintf("%d Subscription entered; confirmation request sent\n", CIT_OK); -} - - -/* - * Enter an unsubscription request - */ -void do_unsubscribe(char *room, char *email, char *webpage) { - struct ctdlroom qrbuf; - FILE *ncfp; - char filename[256]; - char token[256]; - char buf[512]; - char confirmation_request[2048]; - char urlroom[ROOMNAMELEN]; - char scancmd[256]; - char scanemail[256]; - int found_sub = 0; - - if (getroom(&qrbuf, room) != 0) { - cprintf("%d There is no list called '%s'\n", - ERROR + ROOM_NOT_FOUND, room); - return; + netconfig = LoadRoomNetConfigFile(CC->room.QRnumber); + if (!netconfig) { + netconfig = strdup(""); } - 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); - - assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir); + // The new netconfig begins with the new lastsent directive + newnetconfig = malloc(strlen(netconfig) + 1024); +#if 0 + FIXME SYNTAX ERROR #$%$&%$^#%$ sprintf(newnetconfig, "lastsent|%ld\n", ld.msgnum); +#endif - /* - * Make sure there's actually a subscription there to remove - */ - begin_critical_section(S_NETCONFIGS); - ncfp = fopen(filename, "r"); - if (ncfp != NULL) { - while (fgets(buf, sizeof buf, ncfp) != NULL) { - buf[strlen(buf)-1] = 0; - extract_token(scancmd, buf, 0, '|', sizeof scancmd); - extract_token(scanemail, buf, 1, '|', sizeof scanemail); - if ((!strcasecmp(scancmd, "listrecp")) - || (!strcasecmp(scancmd, "digestrecp"))) { - if (!strcasecmp(scanemail, email)) { - ++found_sub; - } - } + // And then we append all of the old netconfig, minus the old lastsent. Also omit blank lines. + config_lines = num_tokens(netconfig, '\n'); + for (i=0; iroom.QRnumber, newnetconfig); end_critical_section(S_NETCONFIGS); + free(newnetconfig); // this was the new netconfig, free it because we're done with it + free(netconfig); // this was the old netconfig, free it even if we didn't do anything - /* Generate and send the confirmation request */ - - urlesc(urlroom, qrbuf.QRname); - - snprintf(confirmation_request, sizeof confirmation_request, - - "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 do not\n" - "wish to unsubscribe from the '%s' 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" - "<%s> from the %s mailing list.

\n" - "Please click here to confirm this request:
\n" - "" - "%s?room=%s&token=%s&cmd=confirm

\n" - "If this request has been submitted in error and you do not\n" - "wish to unsubscribe from the '%s' mailing list, simply do nothing,\n" - "and the request will not be processed.\n" - "\n" - "\n" - "--__ctdlmultipart__--\n", - - email, qrbuf.QRname, - webpage, urlroom, token, - qrbuf.QRname, - - email, qrbuf.QRname, - webpage, urlroom, token, - webpage, urlroom, token, - qrbuf.QRname - ); +#if 0 + FIXME tell the client what we did +#endif - quickie_message( /* This delivers the message */ - "Citadel", - NULL, - email, - NULL, - confirmation_request, - FMT_RFC822, - "Please confirm your unsubscribe request" - ); - cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK); } -/* - * Confirm a subscribe/unsubscribe request. +/* + * process subscribe/unsubscribe requests and confirmations */ -void do_confirm(char *room, char *token) { - struct ctdlroom qrbuf; - FILE *ncfp; - char filename[256]; - char line_token[256]; - long line_offset; - int line_length; - char buf[512]; - char cmd[256]; - char email[256]; - char subtype[128]; - int success = 0; - char address_to_unsubscribe[256]; - char scancmd[256]; - char scanemail[256]; - char *holdbuf = NULL; - int linelen = 0; - int buflen = 0; - - strcpy(address_to_unsubscribe, ""); - - if (getroom(&qrbuf, room) != 0) { - cprintf("%d There is no list called '%s'\n", - ERROR + ROOM_NOT_FOUND, room); +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 ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) { - cprintf("%d '%s' " - "does not accept subscribe/unsubscribe requests.\n", - ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname); + if ((CC->room.QRflags2 & QR2_SELFLIST) == 0) { + cprintf("%d '%s' does not accept subscribe/unsubscribe requests.\n", ERROR + ROOM_NOT_FOUND, roomname); return; } - /* - * Now start scanning this room's netconfig file for the - * specified token. - */ - assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir); - begin_critical_section(S_NETCONFIGS); - ncfp = fopen(filename, "r+"); - if (ncfp != NULL) { - while (line_offset = ftell(ncfp), - (fgets(buf, sizeof buf, ncfp) != NULL) ) { - buf[strlen(buf)-1] = 0; - line_length = strlen(buf); - extract_token(cmd, buf, 0, '|', sizeof cmd); - if (!strcasecmp(cmd, "subpending")) { - extract_token(email, buf, 1, '|', sizeof email); - extract_token(subtype, buf, 2, '|', sizeof subtype); - extract_token(line_token, buf, 3, '|', sizeof line_token); - if (!strcasecmp(token, line_token)) { - if (!strcasecmp(subtype, "digest")) { - safestrncpy(buf, "digestrecp|", sizeof buf); - } - else { - safestrncpy(buf, "listrecp|", sizeof buf); - } - strcat(buf, email); - strcat(buf, "|"); - /* SLEAZY HACK: pad the line out so - * it's the same length as the line - * we're replacing. - */ - while (strlen(buf) < line_length) { - strcat(buf, " "); - } - fseek(ncfp, line_offset, SEEK_SET); - fprintf(ncfp, "%s\n", buf); - ++success; - } - } - if (!strcasecmp(cmd, "unsubpending")) { - extract_token(line_token, buf, 2, '|', sizeof line_token); - if (!strcasecmp(token, line_token)) { - extract_token(address_to_unsubscribe, buf, 1, '|', - sizeof address_to_unsubscribe); - } - } - } - fclose(ncfp); - } - end_critical_section(S_NETCONFIGS); + // Room confirmed, now parse the command. - /* - * If "address_to_unsubscribe" contains something, then we have to - * make another pass at the file, stripping out lines referring to - * that address. - */ - if (!IsEmptyStr(address_to_unsubscribe)) { - holdbuf = malloc(SIZ); - begin_critical_section(S_NETCONFIGS); - ncfp = fopen(filename, "r+"); - if (ncfp != NULL) { - while (line_offset = ftell(ncfp), - (fgets(buf, sizeof buf, ncfp) != NULL) ) { - buf[strlen(buf)-1]=0; - extract_token(scancmd, buf, 0, '|', sizeof scancmd); - extract_token(scanemail, buf, 1, '|', sizeof scanemail); - if ( (!strcasecmp(scancmd, "listrecp")) - && (!strcasecmp(scanemail, - address_to_unsubscribe)) ) { - ++success; - } - else if ( (!strcasecmp(scancmd, "digestrecp")) - && (!strcasecmp(scanemail, - address_to_unsubscribe)) ) { - ++success; - } - else if ( (!strcasecmp(scancmd, "subpending")) - && (!strcasecmp(scanemail, - address_to_unsubscribe)) ) { - ++success; - } - else if ( (!strcasecmp(scancmd, "unsubpending")) - && (!strcasecmp(scanemail, - address_to_unsubscribe)) ) { - ++success; - } - else { /* Not relevant, so *keep* it! */ - linelen = strlen(buf); - holdbuf = realloc(holdbuf, - (buflen + linelen + 2) ); - strcpy(&holdbuf[buflen], buf); - buflen += linelen; - strcpy(&holdbuf[buflen], "\n"); - buflen += 1; - } - } - fclose(ncfp); - } - ncfp = fopen(filename, "w"); - if (ncfp != NULL) { - fwrite(holdbuf, buflen+1, 1, ncfp); - fclose(ncfp); - } - end_critical_section(S_NETCONFIGS); - free(holdbuf); + 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); } - /* - * Did we do anything useful today? - */ - if (success) { - cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success); - lprintf(CTDL_NOTICE, - "Mailing list: %s %ssubscribed to %s with token %s\n", - email, - (!IsEmptyStr(address_to_unsubscribe)) ? "un" : "", - room, - token); - } - else { - cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE); + 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); } -} - - - -/* - * process subscribe/unsubscribe requests and confirmations - */ -void cmd_subs(char *cmdbuf) { - - char opr[256]; - char room[ROOMNAMELEN]; - char email[256]; - char subtype[256]; - char token[256]; - char webpage[256]; - - extract_token(opr, cmdbuf, 0, '|', sizeof opr); - if (!strcasecmp(opr, "subscribe")) { - extract_token(subtype, cmdbuf, 3, '|', sizeof subtype); - if ( (strcasecmp(subtype, "list")) - && (strcasecmp(subtype, "digest")) ) { - cprintf("%d Invalid subscription type '%s'\n", - ERROR + ILLEGAL_VALUE, subtype); - } - else { - extract_token(room, cmdbuf, 1, '|', sizeof room); - extract_token(email, cmdbuf, 2, '|', sizeof email); - extract_token(webpage, cmdbuf, 4, '|', sizeof webpage); - do_subscribe(room, email, subtype, webpage); - } - } - else if (!strcasecmp(opr, "unsubscribe")) { - extract_token(room, cmdbuf, 1, '|', sizeof room); - extract_token(email, cmdbuf, 2, '|', sizeof email); - extract_token(webpage, cmdbuf, 3, '|', sizeof webpage); - do_unsubscribe(room, email, webpage); + else if (!strcasecmp(cmd, "confirm")) { + extract_token(token, cmdbuf, 2, '|', sizeof token); // token 2 is the confirmation token + cprintf("%d not implemented\n", ERROR); } - else if (!strcasecmp(opr, "confirm")) { - extract_token(room, cmdbuf, 1, '|', sizeof room); - extract_token(token, cmdbuf, 2, '|', sizeof token); - do_confirm(room, token); - } - else { - cprintf("%d Invalid command\n", ERROR + ILLEGAL_VALUE); + + else { // sorry man, I can't deal with that + cprintf("%d Invalid command '%s'\n", ERROR + ILLEGAL_VALUE, cmd); } } @@ -575,6 +157,6 @@ CTDL_MODULE_INIT(listsub) CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe"); } - /* return our Subversion id for the Log */ - return "$Id$"; + /* return our module name for the log */ + return "listsub"; }