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