ada4bbadd64e2323be4d29a903020104f4d87cbe
[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-2012 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                 end_critical_section(S_NETCONFIGS);
151                 return;
152         }
153
154         /*
155          * Now add it to the config
156          */     
157         
158         RoomMailAddressLen = strlen(RoomMailAddress);
159         listsub_generate_token(token);
160         Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
161         memset(Line, 0, sizeof(RoomNetCfgLine));
162
163         Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 5);
164         
165         Line->Value[0] = NewStrBufDup(*email);
166         Line->Value[1] = *subtype; *subtype = NULL;
167         Line->Value[2] = NewStrBufPlain(token, -1);
168         Line->Value[3] = NewStrBufPlain(NULL, 10);
169         StrBufPrintf(Line->Value[3], "%ld", time(NULL));
170         Line->Value[4] = *webpage; *webpage = NULL;
171         Line->nValues = 5;
172
173         AddRoomCfgLine(OneRNCfg, &qrbuf, subpending, Line);
174
175         /* Generate and send the confirmation request */
176         UrlRoom = NewStrBuf();
177         StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
178
179         cf_req = NewStrBufPlain(NULL, 2048);
180         StrBufAppendBufPlain(
181                 cf_req,
182                 HKEY("MIME-Version: 1.0\n"
183                      "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
184                      "\n"
185                      "This is a multipart message in MIME format.\n"
186                      "\n"
187                      "--__ctdlmultipart__\n"
188                      "Content-type: text/plain\n"
189                      "\n"
190                      "Someone (probably you) has submitted a request to subscribe\n"
191                      "<"), 0);
192         StrBufAppendBuf(cf_req, Line->Value[0], 0);
193
194         StrBufAppendBufPlain(cf_req, HKEY("> to the '"), 0);
195         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
196
197         StrBufAppendBufPlain(
198                 cf_req,
199                 HKEY("' mailing list.\n"
200                      "\n"
201                      "Please go here to confirm this request:\n"
202                      "  "), 0);
203         StrBufAppendBuf(cf_req, Line->Value[4], 0);
204
205         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
206         StrBufAppendBuf(cf_req, UrlRoom, 0);
207
208         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
209         StrBufAppendBuf(cf_req, Line->Value[2], 0);
210
211         StrBufAppendBufPlain(
212                 cf_req,
213                 HKEY("&cmd=confirm  \n"
214                      "\n"
215                      "If this request has been submitted in error and you do not\n"
216                      "wish to receive the '"), 0);
217         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
218
219         StrBufAppendBufPlain(
220                 cf_req,
221                 HKEY("' mailing list, simply do nothing,\n"
222                      "and you will not receive any further mailings.\n"
223                      "\n"
224                      "--__ctdlmultipart__\n"
225                      "Content-type: text/html\n"
226                      "\n"
227                      "<HTML><BODY>\n"
228                      "Someone (probably you) has submitted a request to subscribe\n"
229                      "&lt;"), 0);
230         StrBufAppendBuf(cf_req, Line->Value[0], 0);
231
232         StrBufAppendBufPlain(cf_req, HKEY( "&gt; to the <B>"), 0);
233
234         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
235
236         StrBufAppendBufPlain(
237                 cf_req,
238                 HKEY("'</B> mailing list.<BR><BR>\n"
239                      "Please click here to confirm this request:<BR>\n"
240                      "<A HREF=\""), 0);
241         StrBufAppendBuf(cf_req, Line->Value[4], 0);
242
243         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
244         StrBufAppendBuf(cf_req, UrlRoom, 0);
245
246         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
247         StrBufAppendBuf(cf_req, Line->Value[2], 0);
248
249         StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
250         StrBufAppendBuf(cf_req, Line->Value[4], 0);
251
252         StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
253         StrBufAppendBuf(cf_req, UrlRoom, 0);
254
255         StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
256         StrBufAppendBuf(cf_req, Line->Value[2], 0);
257
258         StrBufAppendBufPlain(
259                 cf_req,
260                 HKEY("&cmd=confirm</A><BR><BR>\n"
261                      "If this request has been submitted in error and you do not\n"
262                      "wish to receive the '"), 0);
263         StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
264         
265         StrBufAppendBufPlain(
266                 cf_req,
267                 HKEY("' mailing list, simply do nothing,\n"
268                      "and you will not receive any further mailings.\n"
269                      "</BODY></HTML>\n"
270                      "\n"
271                      "--__ctdlmultipart__--\n"), 0);
272
273         end_critical_section(S_NETCONFIGS);
274
275         pcf_req = SmashStrBuf(&cf_req);
276         quickie_message(        /* This delivers the message */
277                 "Citadel",
278                 EmailSender,
279                 ChrPtr(*email),
280                 NULL,
281                 pcf_req,
282                 FMT_RFC822,
283                 "Please confirm your list subscription"
284                 );
285         free(pcf_req);
286         cprintf("%d Subscription entered; confirmation request sent\n", CIT_OK);
287
288         FreeStrBuf(&UrlRoom);
289 }
290
291
292 /*
293  * Enter an unsubscription request
294  */
295 void do_unsubscribe(StrBuf **room, StrBuf **email, StrBuf **webpage) {
296         struct ctdlroom qrbuf;
297         const char *EmailSender = NULL;
298         char token[256];
299         char *pcf_req;
300         StrBuf *cf_req;
301         StrBuf *UrlRoom;
302         int found_sub = 0;
303         const char *RoomMailAddress;
304         OneRoomNetCfg *OneRNCfg;
305         RoomNetCfgLine *Line;
306         long RoomMailAddressLen;
307
308         if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
309                 cprintf("%d There is no list called '%s'\n",
310                         ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
311                 return;
312         }
313
314         if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
315                 cprintf("%d '%s' "
316                         "does not accept subscribe/unsubscribe requests.\n",
317                         ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
318                 return;
319         }
320
321         listsub_generate_token(token);
322
323         /* 
324          * Make sure there's actually a subscription there to remove
325          */
326         begin_critical_section(S_NETCONFIGS);
327         RoomMailAddress = qrbuf.QRname;
328         OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
329         if (OneRNCfg!=NULL) {
330                 found_sub = CountThisSubscriber(OneRNCfg, *email);
331                 if (StrLength(OneRNCfg->Sender) > 0)
332                         EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
333         }
334
335         if (found_sub == 0) {
336                 cprintf("%d '%s' is not subscribed to '%s'.\n",
337                         ERROR + NO_SUCH_USER,
338                         ChrPtr(*email),
339                         qrbuf.QRname);
340
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         end_critical_section(S_NETCONFIGS);
465
466         pcf_req = SmashStrBuf(&cf_req);
467         quickie_message(        /* This delivers the message */
468                 "Citadel",
469                 EmailSender,
470                 ChrPtr(*email),
471                 NULL,
472                 pcf_req,
473                 FMT_RFC822,
474                 "Please confirm your unsubscribe request"
475         );
476
477         free(pcf_req);
478         FreeStrBuf(&UrlRoom);
479         cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK);
480 }
481
482
483 const RoomNetCfg ConfirmSubscribers[] = {subpending, unsubpending};
484
485 /*
486  * Confirm a subscribe/unsubscribe request.
487  */
488 void do_confirm(StrBuf **room, StrBuf **token) {
489         struct ctdlroom qrbuf;
490         OneRoomNetCfg *OneRNCfg;
491         RoomNetCfgLine *Line;
492         RoomNetCfgLine *ConfirmLine = NULL;
493         RoomNetCfgLine *RemoveLine = NULL;
494         RoomNetCfgLine **PrevLine;
495         int success = 0;
496         RoomNetCfg ConfirmType;
497         const char *errmsg = "";
498         int i;
499         
500         if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
501                 cprintf("%d There is no list called '%s'\n",
502                         ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
503                 return;
504         }
505
506         if ((qrbuf.QRflags2 & QR2_SELFLIST) == 0) {
507                 cprintf("%d '%s' "
508                         "does not accept subscribe/unsubscribe requests.\n",
509                         ERROR + HIGHER_ACCESS_REQUIRED, qrbuf.QRname);
510                 return;
511         }
512
513
514         if (StrLength(*token) == 0) {
515                 cprintf("%d empty token.\n", ERROR + ILLEGAL_VALUE);
516                 return;
517         }
518         /*
519          * Now start scanning this room's netconfig file for the
520          * specified token.
521          */
522         begin_critical_section(S_NETCONFIGS);
523         OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
524
525         ConfirmType = maxRoomNetCfg;
526         if (OneRNCfg==NULL)
527         {
528                 errmsg = "no networking config found";
529         }
530         else for (i = 0; i < 2; i++)
531         {
532                 int offset;
533
534                 if (ConfirmSubscribers[i] == subpending)
535                         offset = 2;
536                 else
537                         offset = 1;
538                 PrevLine = &OneRNCfg->NetConfigs[ConfirmSubscribers[i]];
539                 Line = *PrevLine;
540                 while (Line != NULL)
541                 {
542                         if (!strcasecmp(ChrPtr(*token),
543                                         ChrPtr(Line->Value[offset])))
544                         {
545                                 ConfirmLine = Line;
546                                 *PrevLine = Line->next; /* Remove it from the list */
547                                 ConfirmType = ConfirmSubscribers[i];
548                                 ConfirmLine->next = NULL;
549
550                                 i += 100; 
551                                 break;
552
553                         }
554                         PrevLine = &(*PrevLine)->next;
555                         Line = Line->next;
556                 }
557                 if (ConfirmType == maxRoomNetCfg)
558                 {
559                         errmsg = "No active un/subscribe request found";
560                 }
561         }
562
563         if (ConfirmType == subpending)
564         {
565                 if (CountThisSubscriber(OneRNCfg, ConfirmLine->Value[0]) == 0)
566                 {
567                         if (!strcasecmp(ChrPtr(ConfirmLine->Value[2]), 
568                                         ("digest")))
569                         {
570                                 ConfirmType = digestrecp;
571                         }
572                         else /* "list" */
573                         {
574                                 ConfirmType = listrecp;
575                         }
576
577                         syslog(LOG_NOTICE, 
578                                "Mailing list: %s subscribed to %s with token %s\n", 
579                                ChrPtr(ConfirmLine->Value[0]), 
580                                qrbuf.QRname,
581                                ChrPtr(*token));
582
583                         FreeStrBuf(&ConfirmLine->Value[1]);
584                         FreeStrBuf(&ConfirmLine->Value[2]);
585                         FreeStrBuf(&ConfirmLine->Value[3]);
586                         FreeStrBuf(&ConfirmLine->Value[4]);
587                         ConfirmLine->nValues = 1;
588                 
589                         AddRoomCfgLine(OneRNCfg, &qrbuf, ConfirmType, ConfirmLine);
590                         success = 1;
591                 }
592                 else
593                 {
594                         /* whipe duplicate subscribe entry... */
595                         OneRNCfg->changed = 1;
596                         SaveChangedConfigs();
597                         errmsg = "already subscribed";
598                 }
599         }
600         else if (ConfirmType == unsubpending)
601         {
602
603                 for (i = 0; i < 2; i++)
604                 {
605                         PrevLine = &OneRNCfg->NetConfigs[ActiveSubscribers[i]];
606                         Line = *PrevLine;
607                         while (Line != NULL)
608                         {
609                                 if (!strcasecmp(ChrPtr(ConfirmLine->Value[0]),
610                                                 ChrPtr(Line->Value[0])))
611                                 {
612                                         success = 1;
613                                         RemoveLine = Line;
614                                         *PrevLine = Line->next; /* Remove it from the list */
615                                         RemoveLine->next = NULL;
616                                         if (RemoveLine != NULL) 
617                                                 DeleteGenericCfgLine(NULL/*TODO*/, &RemoveLine);
618                                         Line = *PrevLine;
619                                         continue;
620                                 }
621                                 PrevLine = &(*PrevLine)->next;
622                                 Line = Line->next;
623                         }
624                 }
625
626                 if (success) 
627                 {
628                         syslog(LOG_NOTICE, 
629                                "Mailing list: %s unsubscribed to %s with token %s\n", 
630                                ChrPtr(ConfirmLine->Value[0]), 
631                                qrbuf.QRname,
632                                ChrPtr(*token));
633                 }
634                 else
635                 {
636                         errmsg = "no subscriber found for this unsubscription request";
637                 }
638                 DeleteGenericCfgLine(NULL/*TODO*/, &ConfirmLine);
639                 OneRNCfg->changed = 1;
640                 SaveChangedConfigs();
641         }
642
643         end_critical_section(S_NETCONFIGS);
644         
645         /*
646          * Did we do anything useful today?
647          */
648         if (success) {
649                 cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success);
650         }
651         else {
652                 syslog(LOG_NOTICE, "failed processing (un)subscribe request: %s",
653                        errmsg);
654                 cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE);
655         }
656
657 }
658
659
660
661 /* 
662  * process subscribe/unsubscribe requests and confirmations
663  */
664 void cmd_subs(char *cmdbuf)
665 {
666         const char *Pos = NULL;
667         StrBuf *Segments[20];
668         int i=1;
669
670         memset(Segments, 0, sizeof(StrBuf*) * 20);
671         Segments[0] = NewStrBufPlain(cmdbuf, -1);
672         while ((Pos != StrBufNOTNULL) && (i < 20))
673         {
674                 Segments[i] = NewStrBufPlain(NULL, StrLength(Segments[0]));
675                 StrBufExtract_NextToken(Segments[i], Segments[0], &Pos, '|');
676                 i++;
677         }
678
679         if (!strcasecmp(ChrPtr(Segments[1]), "subscribe")) {
680                 if ( (strcasecmp(ChrPtr(Segments[4]), "list"))
681                    && (strcasecmp(ChrPtr(Segments[4]), "digest")) ) {
682                         cprintf("%d Invalid subscription type '%s'\n",
683                                 ERROR + ILLEGAL_VALUE, ChrPtr(Segments[4]));
684                 }
685                 else {
686                         do_subscribe(&Segments[2], &Segments[3], &Segments[4], &Segments[5]);
687                 }
688         }
689         else if (!strcasecmp(ChrPtr(Segments[1]), "unsubscribe")) {
690                 do_unsubscribe(&Segments[2], &Segments[3], &Segments[4]);
691         }
692         else if (!strcasecmp(ChrPtr(Segments[1]), "confirm")) {
693                 do_confirm(&Segments[2], &Segments[3]);
694         }
695         else {
696                 cprintf("%d Invalid command\n", ERROR + ILLEGAL_VALUE);
697         }
698
699         for (; i>=0; i--)
700         {
701                 FreeStrBuf(&Segments[i]);
702         }
703 }
704
705
706 /*
707  * Module entry point
708  */
709 CTDL_MODULE_INIT(listsub)
710 {
711         if (!threading)
712         {
713                 CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe");
714         }
715         
716         /* return our module name for the log */
717         return "listsub";
718 }