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