658fbbc062657e2cbaef2c66cad632a161d9b0dc
[citadel.git] / citadel / modules / listsub / serv_listsub.c
1 /*
2  * This module handles self-service subscription/unsubscription to mail lists.
3  *
4  * Copyright (c) 2002-2016 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 3.
8  *  
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "sysdep.h"
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <ctype.h>
21 #include <signal.h>
22 #include <pwd.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <dirent.h>
26 #if TIME_WITH_SYS_TIME
27 # include <sys/time.h>
28 # include <time.h>
29 #else
30 # if HAVE_SYS_TIME_H
31 #  include <sys/time.h>
32 # else
33 #  include <time.h>
34 # endif
35 #endif
36
37 #include <sys/wait.h>
38 #include <string.h>
39 #include <limits.h>
40 #include <libcitadel.h>
41 #include "citadel.h"
42 #include "server.h"
43 #include "citserver.h"
44 #include "support.h"
45 #include "config.h"
46 #include "user_ops.h"
47 #include "database.h"
48 #include "msgbase.h"
49 #include "internet_addressing.h"
50 #include "clientsocket.h"
51 #include "ctdl_module.h"
52
53 /*
54  * Generate a randomizationalisticized token to use for authentication of
55  * a subscribe or unsubscribe request.
56  */
57 void listsub_generate_token(char *buf) {
58         char sourcebuf[SIZ];
59         static int seq = 0;
60         size_t len;
61
62         /* Theo, please sit down and shut up.  This key doesn't have to be
63          * tinfoil-hat secure, it just needs to be reasonably unguessable
64          * and unique.
65          */
66         len = sprintf(sourcebuf, "%lx",
67                 (long) (++seq + getpid() + time(NULL))
68         );
69
70         /* Convert it to base64 so it looks cool */     
71         len = CtdlEncodeBase64(buf, sourcebuf, len, 0);
72         if (buf[len - 1] == '\n') {
73                 buf[len - 1] = '\0';
74         }
75 }
76
77 const RoomNetCfg ActiveSubscribers[] = {listrecp, digestrecp};
78
79 int CountThisSubscriber(OneRoomNetCfg *OneRNCfg, StrBuf *email)
80 {
81         RoomNetCfgLine *Line;
82         int found_sub = 0;
83         int i;
84
85         for (i = 0; i < 2; i++)
86         {
87                 Line = OneRNCfg->NetConfigs[ActiveSubscribers[i]];
88                 while (Line != NULL)
89                 {
90                         if (!strcmp(ChrPtr(email),
91                                     ChrPtr(Line->Value[0])))
92                         {
93                                 ++found_sub;
94                                 break;                                  
95                         }
96                         Line = Line->next;
97                 }
98         }
99         return found_sub;
100 }
101
102 /*
103  * Enter a subscription request
104  */
105 void do_subscribe(StrBuf **room, StrBuf **email, StrBuf **subtype, StrBuf **webpage) {
106         struct ctdlroom qrbuf;
107         char token[256];
108         char *pcf_req;
109         StrBuf *cf_req;
110         StrBuf *UrlRoom;
111         int found_sub = 0;
112         const char *RoomMailAddress;
113         OneRoomNetCfg *OneRNCfg;
114         RoomNetCfgLine *Line;
115         const char *EmailSender = NULL;
116         long RoomMailAddressLen;
117
118         if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
119                 cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
120                 return;
121         }
122
123         if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
124                 cprintf("%d '%s' "
125                         "does not accept subscribe/unsubscribe requests.\n",
126                         ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
127                 return;
128         }
129
130         /* 
131          * Make sure the requested address isn't already subscribed
132          */
133         begin_critical_section(S_NETCONFIGS);
134
135         RoomMailAddress = qrbuf.QRname;
136         OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
137         if (OneRNCfg != NULL) {
138                 found_sub = CountThisSubscriber(OneRNCfg, *email);
139                 if (StrLength(OneRNCfg->Sender) > 0) {
140                        EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
141                 }
142         }
143
144         if (found_sub != 0) {
145                 cprintf("%d '%s' is already subscribed to '%s'.\n",
146                         ERROR + ALREADY_EXISTS,
147                         ChrPtr(*email),
148                         RoomMailAddress);
149
150                 FreeRoomNetworkStruct(&OneRNCfg);
151                 end_critical_section(S_NETCONFIGS);
152                 return;
153         }
154
155         /*
156          * Now add it to the config
157          */     
158         
159         RoomMailAddressLen = strlen(RoomMailAddress);
160         listsub_generate_token(token);
161         Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
162         memset(Line, 0, sizeof(RoomNetCfgLine));
163
164         Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 5);
165         
166         Line->Value[0] = NewStrBufDup(*email);
167         Line->Value[1] = *subtype; *subtype = NULL;
168         Line->Value[2] = NewStrBufPlain(token, -1);
169         Line->Value[3] = NewStrBufPlain(NULL, 10);
170         StrBufPrintf(Line->Value[3], "%ld", time(NULL));
171         Line->Value[4] = *webpage; *webpage = NULL;
172         Line->nValues = 5;
173
174         AddRoomCfgLine(OneRNCfg, &qrbuf, subpending, Line);
175
176         /* Generate and send the confirmation request */
177         UrlRoom = NewStrBuf();
178         StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
179
180         cf_req = NewStrBufPlain(NULL, 2048);
181         StrBufAppendBufPlain(
182                 cf_req,
183                 HKEY("MIME-Version: 1.0\n"
184                      "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
185                      "\n"
186                      "This is a multipart message in MIME format.\n"
187                      "\n"
188                      "--__ctdlmultipart__\n"
189                      "Content-type: text/plain\n"
190                      "\n"
191                      "Someone (probably you) has submitted a request to subscribe\n"
192                      "<"), 0);
193         StrBufAppendBuf(cf_req, Line->Value[0], 0);
194
195         StrBufAppendBufPlain(cf_req, HKEY("> to the '"), 0);
196         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
197
198         StrBufAppendBufPlain(
199                 cf_req,
200                 HKEY("' mailing list.\n"
201                      "\n"
202                      "Please go here to confirm this request:\n"
203                      "  "), 0);
204         StrBufAppendBuf(cf_req, Line->Value[4], 0);
205
206         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
207         StrBufAppendBuf(cf_req, UrlRoom, 0);
208
209         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
210         StrBufAppendBuf(cf_req, Line->Value[2], 0);
211
212         StrBufAppendBufPlain(
213                 cf_req,
214                 HKEY("&cmd=confirm  \n"
215                      "\n"
216                      "If this request has been submitted in error and you do not\n"
217                      "wish to receive the '"), 0);
218         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
219
220         StrBufAppendBufPlain(
221                 cf_req,
222                 HKEY("' mailing list, simply do nothing,\n"
223                      "and you will not receive any further mailings.\n"
224                      "\n"
225                      "--__ctdlmultipart__\n"
226                      "Content-type: text/html\n"
227                      "\n"
228                      "<HTML><BODY>\n"
229                      "Someone (probably you) has submitted a request to subscribe\n"
230                      "&lt;"), 0);
231         StrBufAppendBuf(cf_req, Line->Value[0], 0);
232
233         StrBufAppendBufPlain(cf_req, HKEY( "&gt; to the <B>"), 0);
234
235         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
236
237         StrBufAppendBufPlain(
238                 cf_req,
239                 HKEY("'</B> mailing list.<BR><BR>\n"
240                      "Please click here to confirm this request:<BR>\n"
241                      "<A HREF=\""), 0);
242         StrBufAppendBuf(cf_req, Line->Value[4], 0);
243
244         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
245         StrBufAppendBuf(cf_req, UrlRoom, 0);
246
247         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
248         StrBufAppendBuf(cf_req, Line->Value[2], 0);
249
250         StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
251         StrBufAppendBuf(cf_req, Line->Value[4], 0);
252
253         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
254         StrBufAppendBuf(cf_req, UrlRoom, 0);
255
256         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
257         StrBufAppendBuf(cf_req, Line->Value[2], 0);
258
259         StrBufAppendBufPlain(
260                 cf_req,
261                 HKEY("&cmd=confirm</A><BR><BR>\n"
262                      "If this request has been submitted in error and you do not\n"
263                      "wish to receive the '"), 0);
264         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
265         
266         StrBufAppendBufPlain(
267                 cf_req,
268                 HKEY("' mailing list, simply do nothing,\n"
269                      "and you will not receive any further mailings.\n"
270                      "</BODY></HTML>\n"
271                      "\n"
272                      "--__ctdlmultipart__--\n"), 0);
273
274         SaveRoomNetConfigFile(OneRNCfg, qrbuf.QRnumber);
275         FreeRoomNetworkStruct(&OneRNCfg);
276         end_critical_section(S_NETCONFIGS);
277
278         pcf_req = SmashStrBuf(&cf_req);
279         quickie_message(        /* This delivers the message */
280                 "Citadel",
281                 EmailSender,
282                 ChrPtr(*email),
283                 NULL,
284                 pcf_req,
285                 FMT_RFC822,
286                 "Please confirm your list subscription"
287                 );
288         free(pcf_req);
289         cprintf("%d Subscription entered; confirmation request sent\n", CIT_OK);
290
291         FreeStrBuf(&UrlRoom);
292 }
293
294
295 /*
296  * Enter an unsubscription request
297  */
298 void do_unsubscribe(StrBuf **room, StrBuf **email, StrBuf **webpage) {
299         struct ctdlroom qrbuf;
300         const char *EmailSender = NULL;
301         char token[256];
302         char *pcf_req;
303         StrBuf *cf_req;
304         StrBuf *UrlRoom;
305         int found_sub = 0;
306         const char *RoomMailAddress;
307         OneRoomNetCfg *OneRNCfg;
308         RoomNetCfgLine *Line;
309         long RoomMailAddressLen;
310
311         if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
312                 cprintf("%d There is no list called '%s'\n",
313                         ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
314                 return;
315         }
316
317         if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
318                 cprintf("%d '%s' "
319                         "does not accept subscribe/unsubscribe requests.\n",
320                         ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
321                 return;
322         }
323
324         listsub_generate_token(token);
325
326         /* 
327          * Make sure there's actually a subscription there to remove
328          */
329         begin_critical_section(S_NETCONFIGS);
330         RoomMailAddress = qrbuf.QRname;
331         OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
332         if (OneRNCfg!=NULL) {
333                 found_sub = CountThisSubscriber(OneRNCfg, *email);
334                 if (StrLength(OneRNCfg->Sender) > 0)
335                         EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
336         }
337
338         if (found_sub == 0) {
339                 cprintf("%d '%s' is not subscribed to '%s'.\n", ERROR + NO_SUCH_USER, ChrPtr(*email), qrbuf.QRname);
340                 FreeRoomNetworkStruct(&OneRNCfg);
341                 end_critical_section(S_NETCONFIGS);
342                 return;
343         }
344         
345         /* 
346          * Ok, now enter the unsubscribe-pending entry.
347          */
348         RoomMailAddressLen = strlen(RoomMailAddress);
349         listsub_generate_token(token);
350         Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
351         memset(Line, 0, sizeof(RoomNetCfgLine));
352
353         Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 4);
354         
355         Line->Value[0] = NewStrBufDup(*email);
356         Line->Value[1] = NewStrBufPlain(token, -1);
357         Line->Value[2] = NewStrBufPlain(NULL, 10);
358         StrBufPrintf(Line->Value[2], "%ld", time(NULL));
359         Line->Value[3] = *webpage; *webpage = NULL;
360         Line->nValues = 4;
361
362         AddRoomCfgLine(OneRNCfg, &qrbuf, unsubpending, Line);
363
364         /* Generate and send the confirmation request */
365         UrlRoom = NewStrBuf();
366         StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
367
368         cf_req = NewStrBufPlain(NULL, 2048);
369
370         StrBufAppendBufPlain(
371                 cf_req,
372                 HKEY("MIME-Version: 1.0\n"
373                      "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
374                      "\n"
375                      "This is a multipart message in MIME format.\n"
376                      "\n"
377                      "--__ctdlmultipart__\n"
378                      "Content-type: text/plain\n"
379                      "\n"
380                      "Someone (probably you) has submitted a request to unsubscribe\n"
381                      "<"), 0);
382         StrBufAppendBuf(cf_req, Line->Value[0], 0);
383
384
385         StrBufAppendBufPlain(
386                 cf_req,
387                 HKEY("> from the '"), 0);
388         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
389
390         StrBufAppendBufPlain(
391                 cf_req,
392                 HKEY("' mailing list.\n"
393                      "\n"
394                      "Please go here to confirm this request:\n  "), 0);
395         StrBufAppendBuf(cf_req, Line->Value[3], 0);
396         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
397         StrBufAppendBuf(cf_req, UrlRoom, 0);
398         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
399         StrBufAppendBuf(cf_req, Line->Value[1], 0);
400
401         StrBufAppendBufPlain(
402                 cf_req,
403                 HKEY("&cmd=confirm  \n"
404                      "\n"
405                      "If this request has been submitted in error and you do not\n"
406                      "wish to unsubscribe from the '"), 0);
407
408         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
409
410         StrBufAppendBufPlain(
411                 cf_req,
412                 HKEY("' mailing list, simply do nothing,\n"
413                      "and the request will not be processed.\n"
414                      "\n"
415                      "--__ctdlmultipart__\n"
416                      "Content-type: text/html\n"
417                      "\n"
418                      "<HTML><BODY>\n"
419                      "Someone (probably you) has submitted a request to unsubscribe\n"
420                      "&lt;"), 0);
421         StrBufAppendBuf(cf_req, Line->Value[0], 0);
422
423         StrBufAppendBufPlain(cf_req, HKEY("&gt; from the <B>"), 0);
424         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
425
426         StrBufAppendBufPlain(
427                 cf_req,
428                 HKEY("</B> mailing list.<BR><BR>\n"
429                      "Please click here to confirm this request:<BR>\n"
430                      "<A HREF=\""), 0);
431         StrBufAppendBuf(cf_req, Line->Value[3], 0);
432
433         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
434         StrBufAppendBuf(cf_req, UrlRoom, 0);
435
436         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
437         StrBufAppendBuf(cf_req, Line->Value[1], 0);
438
439         StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
440         StrBufAppendBuf(cf_req, Line->Value[3], 0);
441
442         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
443         StrBufAppendBuf(cf_req, UrlRoom, 0);
444
445         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
446         StrBufAppendBuf(cf_req, Line->Value[1], 0);
447
448
449         StrBufAppendBufPlain(
450                 cf_req,
451                 HKEY("&cmd=confirm</A><BR><BR>\n"
452                      "If this request has been submitted in error and you do not\n"
453                      "wish to unsubscribe from the '"), 0);
454         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
455
456         StrBufAppendBufPlain(
457                 cf_req,
458                 HKEY("' mailing list, simply do nothing,\n"
459                      "and the request will not be processed.\n"
460                      "</BODY></HTML>\n"
461                      "\n"
462                      "--__ctdlmultipart__--\n"), 0);
463
464         SaveRoomNetConfigFile(OneRNCfg, qrbuf.QRnumber);
465         FreeRoomNetworkStruct(&OneRNCfg);
466         end_critical_section(S_NETCONFIGS);
467
468         pcf_req = SmashStrBuf(&cf_req);
469         quickie_message(        /* This delivers the message */
470                 "Citadel",
471                 EmailSender,
472                 ChrPtr(*email),
473                 NULL,
474                 pcf_req,
475                 FMT_RFC822,
476                 "Please confirm your unsubscribe request"
477         );
478
479         free(pcf_req);
480         FreeStrBuf(&UrlRoom);
481         cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK);
482 }
483
484
485 const RoomNetCfg ConfirmSubscribers[] = {subpending, unsubpending};
486
487 /*
488  * Confirm a subscribe/unsubscribe request.
489  */
490 void do_confirm(StrBuf **room, StrBuf **token) {
491         struct ctdlroom qrbuf;
492         OneRoomNetCfg *OneRNCfg;
493         RoomNetCfgLine *Line;
494         RoomNetCfgLine *ConfirmLine = NULL;
495         RoomNetCfgLine *RemoveLine = NULL;
496         RoomNetCfgLine **PrevLine;
497         int success = 0;
498         RoomNetCfg ConfirmType;
499         const char *errmsg = "";
500         int i;
501         
502         if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
503                 cprintf("%d There is no list called '%s'\n",
504                         ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
505                 return;
506         }
507
508         if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
509                 cprintf("%d '%s' "
510                         "does not accept subscribe/unsubscribe requests.\n",
511                         ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
512                 return;
513         }
514
515
516         if (StrLength(*token) == 0) {
517                 cprintf("%d empty token.\n", ERROR + ILLEGAL_VALUE);
518                 return;
519         }
520         /*
521          * Now start scanning this room's netconfig file for the
522          * specified token.
523          */
524         begin_critical_section(S_NETCONFIGS);
525         OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
526
527         ConfirmType = maxRoomNetCfg;
528         if (OneRNCfg==NULL)
529         {
530                 errmsg = "no networking config found";
531         }
532         else for (i = 0; i < 2; i++)
533         {
534                 int offset;
535
536                 if (ConfirmSubscribers[i] == subpending)
537                         offset = 2;
538                 else
539                         offset = 1;
540                 PrevLine = &OneRNCfg->NetConfigs[ConfirmSubscribers[i]];
541                 Line = *PrevLine;
542                 while (Line != NULL)
543                 {
544                         if (!strcasecmp(ChrPtr(*token),
545                                         ChrPtr(Line->Value[offset])))
546                         {
547                                 ConfirmLine = Line;
548                                 *PrevLine = Line->next; /* Remove it from the list */
549                                 ConfirmType = ConfirmSubscribers[i];
550                                 ConfirmLine->next = NULL;
551
552                                 i += 100; 
553                                 break;
554
555                         }
556                         PrevLine = &(*PrevLine)->next;
557                         Line = Line->next;
558                 }
559                 if (ConfirmType == maxRoomNetCfg)
560                 {
561                         errmsg = "No active un/subscribe request found";
562                 }
563         }
564
565         if (ConfirmType == subpending)
566         {
567                 if (CountThisSubscriber(OneRNCfg, ConfirmLine->Value[0]) == 0)
568                 {
569                         if (!strcasecmp(ChrPtr(ConfirmLine->Value[2]), 
570                                         ("digest")))
571                         {
572                                 ConfirmType = digestrecp;
573                         }
574                         else /* "list" */
575                         {
576                                 ConfirmType = listrecp;
577                         }
578
579                         syslog(LOG_NOTICE, 
580                                "Mailing list: %s subscribed to %s with token %s\n", 
581                                ChrPtr(ConfirmLine->Value[0]), 
582                                qrbuf.QRname,
583                                ChrPtr(*token));
584
585                         FreeStrBuf(&ConfirmLine->Value[1]);
586                         FreeStrBuf(&ConfirmLine->Value[2]);
587                         FreeStrBuf(&ConfirmLine->Value[3]);
588                         FreeStrBuf(&ConfirmLine->Value[4]);
589                         ConfirmLine->nValues = 1;
590                 
591                         AddRoomCfgLine(OneRNCfg, &qrbuf, ConfirmType, ConfirmLine);
592                         success = 1;
593                 }
594                 else
595                 {
596                         /* whipe duplicate subscribe entry... */
597                         OneRNCfg->changed = 1;
598                         errmsg = "already subscribed";
599                 }
600         }
601         else if (ConfirmType == unsubpending)
602         {
603
604                 for (i = 0; i < 2; i++)
605                 {
606                         PrevLine = &OneRNCfg->NetConfigs[ActiveSubscribers[i]];
607                         Line = *PrevLine;
608                         while (Line != NULL)
609                         {
610                                 if (!strcasecmp(ChrPtr(ConfirmLine->Value[0]),
611                                                 ChrPtr(Line->Value[0])))
612                                 {
613                                         success = 1;
614                                         RemoveLine = Line;
615                                         *PrevLine = Line->next; /* Remove it from the list */
616                                         RemoveLine->next = NULL;
617                                         if (RemoveLine != NULL) 
618                                                 DeleteGenericCfgLine(NULL/*TODO*/, &RemoveLine);
619                                         Line = *PrevLine;
620                                         continue;
621                                 }
622                                 PrevLine = &(*PrevLine)->next;
623                                 Line = Line->next;
624                         }
625                 }
626
627                 if (success) 
628                 {
629                         syslog(LOG_NOTICE, 
630                                "Mailing list: %s unsubscribed to %s with token %s\n", 
631                                ChrPtr(ConfirmLine->Value[0]), 
632                                qrbuf.QRname,
633                                ChrPtr(*token));
634                 }
635                 else
636                 {
637                         errmsg = "no subscriber found for this unsubscription request";
638                 }
639                 DeleteGenericCfgLine(NULL/*TODO*/, &ConfirmLine);
640                 OneRNCfg->changed = 1;
641         }
642
643         SaveRoomNetConfigFile(OneRNCfg, qrbuf.QRnumber);
644         FreeRoomNetworkStruct(&OneRNCfg);
645         end_critical_section(S_NETCONFIGS);
646         
647         /*
648          * Did we do anything useful today?
649          */
650         if (success) {
651                 cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success);
652         }
653         else {
654                 syslog(LOG_NOTICE, "failed processing (un)subscribe request: %s",
655                        errmsg);
656                 cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE);
657         }
658
659 }
660
661
662
663 /* 
664  * process subscribe/unsubscribe requests and confirmations
665  */
666 void cmd_subs(char *cmdbuf)
667 {
668         const char *Pos = NULL;
669         StrBuf *Segments[20];
670         int i=1;
671
672         memset(Segments, 0, sizeof(StrBuf*) * 20);
673         Segments[0] = NewStrBufPlain(cmdbuf, -1);
674         while ((Pos != StrBufNOTNULL) && (i < 20))
675         {
676                 Segments[i] = NewStrBufPlain(NULL, StrLength(Segments[0]));
677                 StrBufExtract_NextToken(Segments[i], Segments[0], &Pos, '|');
678                 i++;
679         }
680
681         if (!strcasecmp(ChrPtr(Segments[1]), "subscribe")) {
682                 if ( (strcasecmp(ChrPtr(Segments[4]), "list"))
683                    && (strcasecmp(ChrPtr(Segments[4]), "digest")) ) {
684                         cprintf("%d Invalid subscription type '%s'\n",
685                                 ERROR + ILLEGAL_VALUE, ChrPtr(Segments[4]));
686                 }
687                 else {
688                         do_subscribe(&Segments[2], &Segments[3], &Segments[4], &Segments[5]);
689                 }
690         }
691         else if (!strcasecmp(ChrPtr(Segments[1]), "unsubscribe")) {
692                 do_unsubscribe(&Segments[2], &Segments[3], &Segments[4]);
693         }
694         else if (!strcasecmp(ChrPtr(Segments[1]), "confirm")) {
695                 do_confirm(&Segments[2], &Segments[3]);
696         }
697         else {
698                 cprintf("%d Invalid command\n", ERROR + ILLEGAL_VALUE);
699         }
700
701         for (; i>=0; i--)
702         {
703                 FreeStrBuf(&Segments[i]);
704         }
705 }
706
707
708 /*
709  * Module entry point
710  */
711 CTDL_MODULE_INIT(listsub)
712 {
713         if (!threading)
714         {
715                 CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe");
716         }
717         
718         /* return our module name for the log */
719         return "listsub";
720 }