summaryrefslogtreecommitdiffstats
path: root/innbbsd
diff options
context:
space:
mode:
Diffstat (limited to 'innbbsd')
-rw-r--r--innbbsd/COPYRIGHT.nocem11
-rw-r--r--innbbsd/Makefile4
-rw-r--r--innbbsd/bbslib.c1
-rw-r--r--innbbsd/bbslink.c4
-rw-r--r--innbbsd/innbbsd.c2
-rw-r--r--innbbsd/nocem.c629
-rw-r--r--innbbsd/nocem.h61
-rw-r--r--innbbsd/receive_article.c2
8 files changed, 711 insertions, 3 deletions
diff --git a/innbbsd/COPYRIGHT.nocem b/innbbsd/COPYRIGHT.nocem
new file mode 100644
index 00000000..fdd43b20
--- /dev/null
+++ b/innbbsd/COPYRIGHT.nocem
@@ -0,0 +1,11 @@
+# Author: Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw>
+# Start Date: Thu Feb 25 1999 +0800
+# Project: INNBBSD - NoCeM
+# File: nocem.c nocem.h
+#
+# Copyright: Copyright (c) 2000 by Yen-Ming Lee
+#
+# Permission to use, copy, modify, and distribute this
+# software for any purpose with or without fee is hereby
+# granted, provided that the above copyright notice and this
+# permission notice appear in all copies.
diff --git a/innbbsd/Makefile b/innbbsd/Makefile
index 1ac29ad7..69208a38 100644
--- a/innbbsd/Makefile
+++ b/innbbsd/Makefile
@@ -50,12 +50,12 @@ CFLAGS+= -c -I. -I$(BBS_SRC)/include -I$(BBS_SRC)/mbbsd -D$(BBS_DEP) \
OBJS = inndchannel.o innbbsd.o connectsock.o rfc931.o \
daemon.o file.o pmain.o his.o dbz.o \
closeonexec.o dbztool.o inntobbs.o receive_article.o \
- echobbslib.o str_decode.o $(BBS_REC)
+ echobbslib.o str_decode.o $(BBS_REC) nocem.o
# $(BBS_REC)
SRCS = inndchannel.c innbbsd.c connectsock.c rfc931.c \
daemon.c file.c pmain.c parsdate.y his.c dbz.c \
closeonexec.c dbztool.c inntobbs.c bbslib.c receive_article.c \
- port.c str_decode.c
+ port.c str_decode.c nocem.c
MOBJS = makedbz.o bbslib.o file.o dbz.o closeonexec.o
HOBJS = mkhistory.o bbslib.o file.o his.o dbz.o port.o closeonexec.o \
diff --git a/innbbsd/bbslib.c b/innbbsd/bbslib.c
index 9436216e..ad171bf6 100644
--- a/innbbsd/bbslib.c
+++ b/innbbsd/bbslib.c
@@ -244,6 +244,7 @@ initial_bbs(outgoing)
}
if (NONENEWSFEEDS == 0)
readnffile(INNDHOME);
+ readNCMfile(INNDHOME);
if (LOCALNODELIST == 0) {
if (readnlfile(INNDHOME, outgoing) != 0)
return 0;
diff --git a/innbbsd/bbslink.c b/innbbsd/bbslink.c
index 51bdb3a7..e9ebf143 100644
--- a/innbbsd/bbslink.c
+++ b/innbbsd/bbslink.c
@@ -1782,3 +1782,7 @@ main(argc, argv)
}
return 0;
}
+
+readNCMfile()
+{
+}
diff --git a/innbbsd/innbbsd.c b/innbbsd/innbbsd.c
index a302c398..f782e8da 100644
--- a/innbbsd/innbbsd.c
+++ b/innbbsd/innbbsd.c
@@ -717,6 +717,8 @@ CMDihave(client)
}
} else if ((char *)strstr(path1, path2) != NULL) {
bbslog(":Warn: Loop back article: %s!%s\n", MYBBSID, HEADER[PATH_H]);
+ } else if (strstr(SUBJECT, "@@") && strstr(BODY, "NCM") && strstr(BODY, "PGP")) {
+ rel = receive_nocem();
} else {
rel = receive_article();
}
diff --git a/innbbsd/nocem.c b/innbbsd/nocem.c
new file mode 100644
index 00000000..a57834ed
--- /dev/null
+++ b/innbbsd/nocem.c
@@ -0,0 +1,629 @@
+/*
+ * NoCeM-INNBBSD Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw> NCMparse(),
+ * NCMverify(), NCMcancel(): return 0 success, otherwise fail;
+ */
+
+#include "nocem.h"
+#define PGP5
+#undef PGP2
+
+int ncmdebug = 0;
+
+char NCMVER[8];
+char ISSUER[STRLEN];
+char TYPE[8];
+char ACTION[8];
+char NCMID[STRLEN];
+char COUNT[8];
+char THRESHOLD[STRLEN];
+char KEYID[16];
+char SPAMMID_NOW[STRLEN];
+char SPAMMID[MAXSPAMMID][STRLEN];
+
+int NNTP = -1;
+FILE *NNTPrfp = NULL;
+FILE *NNTPwfp = NULL;
+char NNTPbuffer[1024];
+int num_spammid = 0;
+char errmsg[1024] = "nothing";
+
+/* ------------------------------------------------------------------ */
+/* NCM initial and maintain */
+/* ------------------------------------------------------------------ */
+
+static int
+ncm_bytypecmp(a, b)
+ ncmperm_t **a, **b;
+{
+ return strcasecmp((*a)->type, (*b)->type);
+}
+
+static int
+ncmcmp(a, b)
+ ncmperm_t *a, *b;
+{
+ return strcasecmp(a->issuer, b->issuer);
+}
+
+#if 0
+ncmperm_t *
+search_issuer(issuer)
+ char *issuer;
+{
+ ncmperm_t ncmt, *find;
+ ncmt.issuer = "*";
+ find = (ncmperm_t *) bsearch((char *) &ncmt, NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ if (find)
+ return find;
+ ncmt.issuer = issuer;
+ find = (ncmperm_t *) bsearch((char *) &ncmt, NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ return find;
+}
+#else
+ncmperm_t *
+search_issuer(issuer)
+ char *issuer;
+{
+ ncmperm_t ncmt, *find;
+ int i;
+ for (i = 0; i < NCMCOUNT; i++)
+ {
+ find = &NCMPERM[i];
+ if (strstr(find->issuer, "*"))
+ return find;
+ if (strstr(issuer, find->issuer))
+ return find;
+ }
+ return NULL;
+}
+#endif
+
+ncmperm_t *
+search_issuer_type(issuer, type)
+ char *issuer, *type;
+{
+ ncmperm_t ncmt, *find;
+ int i;
+ for (i = 0; i < NCMCOUNT; i++)
+ {
+ find = &NCMPERM[i];
+ if ((!strcmp(find->issuer, "*") || strstr(issuer, find->issuer)) &&
+ (!strcmp(find->type, "*") || !strcasecmp(find->type, type)))
+ return find;
+ }
+ return NULL;
+}
+
+int
+readNCMfile(inndhome)
+ char *inndhome;
+{
+ FILE *fp;
+ char buff[LINELEN];
+ struct stat st;
+ int i, count;
+ char *ptr, *ncmpermptr;
+
+ sprintf(buff, "%s/ncmperm.bbs", inndhome);
+ fp = fopen(buff, "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "read fail %s", buff);
+ return -1;
+ }
+ if (fstat(fileno(fp), &st) != 0)
+ {
+ fprintf(stderr, "stat fail %s", buff);
+ return -1;
+ }
+ if (NCMPERM_BUF == NULL)
+ {
+ NCMPERM_BUF = (char *) mymalloc(st.st_size + 1);
+ }
+ else
+ {
+ NCMPERM_BUF = (char *) myrealloc(NCMPERM_BUF, st.st_size + 1);
+ }
+ i = 0, count = 0;
+ while (fgets(buff, sizeof buff, fp) != NULL)
+ {
+ if (buff[0] == '#')
+ continue;
+ if (buff[0] == '\n')
+ continue;
+ strcpy(NCMPERM_BUF + i, buff);
+ i += strlen(buff);
+ count++;
+ }
+ fclose(fp);
+ if (NCMPERM == NULL)
+ {
+ NCMPERM = (ncmperm_t *) mymalloc(sizeof(ncmperm_t) * (count + 1));
+ NCMPERM_BYTYPE = (ncmperm_t **) mymalloc(sizeof(ncmperm_t *) * (count + 1));
+ }
+ else
+ {
+ NCMPERM = (ncmperm_t *) myrealloc(NCMPERM, sizeof(ncmperm_t) * (count + 1));
+ NCMPERM_BYTYPE = (ncmperm_t **) myrealloc(NCMPERM_BYTYPE, sizeof(ncmperm_t *) * (count + 1));
+ }
+ NCMCOUNT = 0;
+ for (ptr = NCMPERM_BUF; (ncmpermptr = (char *) strchr(ptr, '\n')) != NULL; ptr = ncmpermptr + 1, NCMCOUNT++)
+ {
+ char *nptr;
+ *ncmpermptr = '\0';
+ NCMPERM[NCMCOUNT].issuer = "";
+ NCMPERM[NCMCOUNT].type = "";
+ NCMPERM[NCMCOUNT].perm = 0;
+ NCMPERM_BYTYPE[NCMCOUNT] = NCMPERM + NCMCOUNT;
+ for (nptr = ptr; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].issuer = nptr;
+ for (nptr++; *nptr && !(*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ *nptr = '\0';
+ for (nptr++; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].type = nptr;
+ for (nptr++; *nptr && !(*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ *nptr = '\0';
+ for (nptr++; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].perm = (strstr(nptr, "y") || strstr(nptr, "Y"));
+ for (nptr++; *nptr && !strchr("\r\n", *nptr);)
+ nptr++;
+ /* if (*nptr == '\0') continue; */
+ *nptr = '\0';
+ }
+ qsort(NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ qsort(NCMPERM_BYTYPE, NCMCOUNT, sizeof(ncmperm_t *), ncm_bytypecmp);
+#if 0
+ NCMregister();
+#endif
+ return 0;
+}
+
+NCMupdate(char *issuer, char *type)
+{
+ FILE *fp;
+ char buff[LINELEN];
+
+ sprintf(buff, "%s/ncmperm.bbs", INNDHOME);
+ if (!isfile(buff))
+ {
+ if ((fp = fopen(buff, "w")) == NULL)
+ {
+ fprintf(stderr, "write fail %s", buff);
+ return -1;
+ }
+ fprintf(fp, "# This is ncmperm.bbs, it's auto-generated by program for first time\n");
+ fprintf(fp, "# The columns *MUST* be separated by [TAB]\n");
+ fprintf(fp, "# If you wanna accept someone's NoCeM notice, change his perm from \'no\' to \'yes\'\n");
+ fprintf(fp, "# put \"*\" in Issuer column means to match all\n");
+ fprintf(fp, "# Any questions ? please e-mail %s\n", LeeymEMAIL);
+ fprintf(fp, "# Issuer\t\tType\tPerm\n");
+ fflush(fp);
+ fclose(fp);
+ bbslog("NCMupdate create %s\n", buff);
+ }
+ if ((fp = fopen(buff, "a")) == NULL)
+ {
+ fprintf(stderr, "attach fail %s", buff);
+ return -1;
+ }
+ fprintf(fp, "%s\t\t%s\tno\n", issuer, type);
+ fflush(fp);
+ fclose(fp);
+ bbslog("NCMupdate add Issuer: %s , Type: %s\n", ISSUER, TYPE);
+ sleep(1);
+ if (readNCMfile(INNDHOME) == -1)
+ bbslog("fail to readNCMfile\n");
+}
+
+int tcpcommand(char *fmt, ...)
+{
+ va_list ap;
+ char *ptr;
+ va_start(ap, fmt);
+ vfprintf(NNTPwfp, fmt, ap);
+ va_end(ap);
+ fprintf(NNTPwfp, "\r\n");
+ fflush(NNTPwfp);
+
+ fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp);
+ ptr = strchr(NNTPbuffer, '\r');
+ if (ptr)
+ *ptr = '\0';
+ ptr = strchr(NNTPbuffer, '\n');
+ if (ptr)
+ *ptr = '\0';
+ return atoi(NNTPbuffer);
+}
+
+int
+NCMregister()
+{
+ int status;
+ time_t now = time(NULL);
+ char hbuf[80];
+
+ gethostname(hbuf, 80);
+
+ if (!strcmp(hbuf, LeeymBBS))
+ return 1;
+
+ if ((NNTP = inetclient(LeeymBBS, "7777", "tcp")) < 0)
+ {
+ bbslog("NCMregister :Err: server %s %s error: cant connect\n", LeeymBBS, "7777");
+ return 0;
+ }
+
+ if (!(NNTPrfp = fdopen(NNTP, "r")) || !(NNTPwfp = fdopen(NNTP, "w")))
+ {
+ bbslog("NCMregister :Err: fdopen failed\n");
+ return 0;
+ }
+
+ fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp);
+ if (atoi(NNTPbuffer) != 200)
+ {
+ bbslog("NCMregister :Err: server error: %s", NNTPbuffer);
+ return 0;
+ }
+ status = tcpcommand("ADDHIST <%d-%s> NCMregister/%s/%s",
+ now, hbuf, VERSION, NCMINNBBSVER);
+ status = tcpcommand("QUIT");
+ fclose(NNTPwfp);
+ fclose(NNTPrfp);
+ close(NNTP);
+ return 1;
+}
+
+/* ------------------------------------------------------------------ */
+/* PGP verify */
+/* ------------------------------------------------------------------ */
+
+#ifdef PGP5
+int
+run_pgp(char *cmd, FILE ** in, FILE ** out)
+{
+ int pin[2], pout[2], child_pid;
+ char PGPPATH[80];
+
+ sprintf(PGPPATH, "%s/.pgp", BBSHOME);
+ setenv("PGPPATH", PGPPATH, 1);
+
+ *in = *out = NULL;
+
+ pipe(pin);
+ pipe(pout);
+
+ if (!(child_pid = fork()))
+ {
+ /* We're the child. */
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(127);
+ }
+ /* Only get here if we're the parent. */
+ close(pout[1]);
+ *out = fdopen(pout[0], "r");
+
+ close(pin[0]);
+ *in = fdopen(pin[1], "w");
+
+ return (child_pid);
+}
+
+int
+verify_buffer(char *buf, char *passphrase)
+{
+ FILE *pgpin, *pgpout;
+ char tmpbuf[1024] = " ";
+ int ans = NOPGP;
+
+ setenv("PGPPASSFD", "0", 1);
+ run_pgp("/usr/local/bin/pgpv -f +batchmode=1 +OutputInformationFD=1",
+ &pgpin, &pgpout);
+ if (pgpin && pgpout)
+ {
+ fprintf(pgpin, "%s\n", passphrase); /* Send the passphrase in, first */
+ bzero(passphrase, strlen(passphrase)); /* Burn the passphrase */
+ fprintf(pgpin, "%s", buf);
+ fclose(pgpin);
+
+ *buf = '\0';
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ while (!feof(pgpout))
+ {
+ strcat(buf, tmpbuf);
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ }
+
+ wait(NULL);
+
+ fclose(pgpout);
+ }
+
+ if (strstr(buf, "BAD signature made"))
+ {
+ strcpy(errmsg, "BAD signature");
+ ans = PGPBAD;
+ }
+ else if (strstr(buf, "Good signature made"))
+ {
+ strcpy(errmsg, "Good signature");
+ ans = PGPGOOD;
+ }
+ else if (strcpy(tmpbuf, strstr(buf, "Signature by unknown keyid:")))
+ {
+ sprintf(errmsg, "%s ", strtok(tmpbuf, "\r\n"));
+ strcpy(KEYID, strrchr(tmpbuf, ' ') + 1);
+ ans = PGPUN;
+ }
+
+ unsetenv("PGPPASSFD");
+ return ans;
+}
+
+int
+NCMverify()
+{
+ int ans;
+ char passphrase[80] = "Haha, I am Leeym..";
+ ans = verify_buffer(BODY, passphrase);
+ return ans;
+}
+#endif
+/* ------------------------------------------------------------------ */
+/* parse NoCeM Notice Headers/Body */
+/* ------------------------------------------------------------------ */
+
+int
+readNCMheader(char *line)
+{
+ if (!strncasecmp(line, "Version", strlen("Version")))
+ {
+ strcpy(NCMVER, line + strlen("Version") + 2);
+ if (!strstr(NCMVER, "0.9"))
+ {
+ sprintf(errmsg, "unknown version: %s", NCMVER);
+ return P_FAIL;
+ }
+ }
+ else if (!strncasecmp(line, "Issuer", strlen("Issuer")))
+ {
+ strcpy(ISSUER, line + strlen("Issuer") + 2);
+ FROM = ISSUER;
+ }
+ else if (!strncasecmp(line, "Type", strlen("Type")))
+ {
+ strcpy(TYPE, line + strlen("Type") + 2);
+ }
+ else if (!strncasecmp(line, "Action", strlen("Action")))
+ {
+ strcpy(ACTION, line + strlen("Action") + 2);
+ if (!strstr(ACTION, "hide"))
+ {
+ sprintf(errmsg, "unsupported action: %s", ACTION);
+ return P_FAIL;
+ }
+ }
+ else if (!strncasecmp(line, "Notice-ID", strlen("Notice-ID")))
+ {
+ strcpy(NCMID, line + strlen("Notice-ID") + 2);
+ }
+ else if (!strncasecmp(line, "Count", strlen("Count")))
+ {
+ strcpy(COUNT, line + strlen("Count") + 2);
+ }
+ else if (!strncasecmp(line, "Threshold", strlen("Threshold")))
+ {
+ strcpy(THRESHOLD, line + strlen("Threshold") + 2);
+ }
+
+ return P_OKAY;
+}
+
+int
+readNCMbody(char *line)
+{
+ char buf[LINELEN], *group;
+ strcpy(buf, line);
+
+ if (!strstr(buf, "\t"))
+ return P_FAIL;
+
+ group = strrchr(line, '\t') + 1;
+
+ if (buf[0] == '<' && strstr(buf, ">"))
+ {
+ strtok(buf, "\t");
+ strcpy(SPAMMID_NOW, buf);
+ }
+
+ if (num_spammid && !strcmp(SPAMMID[num_spammid - 1], SPAMMID_NOW))
+ return 0;
+
+ if (search_group(group))
+ strcpy(SPAMMID[num_spammid++], SPAMMID_NOW);
+}
+
+int
+NCMparse()
+{
+ char *fptr, *ptr;
+ int type = TEXT;
+
+ if (!(fptr = strstr(BODY, "-----BEGIN PGP SIGNED MESSAGE-----")))
+ {
+ strcpy(errmsg, "notice isn't signed");
+ return P_FAIL;
+ }
+
+ for (ptr = strchr(fptr, '\n'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\n'))
+ {
+ int ch = *ptr;
+ int ch2 = *(ptr - 1);
+
+ *ptr = '\0';
+ if (*(ptr - 1) == '\r')
+ *(ptr - 1) = '\0';
+
+ if (num_spammid > MAXSPAMMID)
+ return P_OKAY;
+
+ if (ncmdebug >= 2)
+ bbslog("NCMparse: %s\n", fptr);
+
+ if (!strncmp(fptr, "@@", 2))
+ {
+ if (strstr(fptr, "BEGIN NCM HEADERS"))
+ type = NCMHDR;
+ else if (strstr(fptr, "BEGIN NCM BODY"))
+ {
+ if (NCMVER && ISSUER && TYPE && ACTION && COUNT && NCMID)
+ {
+ ncmperm_t *ncmt;
+ ncmt = (ncmperm_t *) search_issuer_type(ISSUER, TYPE);
+ if (ncmt == NULL)
+ {
+ NCMupdate(ISSUER, TYPE);
+ sprintf(errmsg, "unknown issuer: %s, %s", ISSUER, MSGID);
+ return P_UNKNOWN;
+ }
+ if (ncmt->perm == NULL)
+ {
+ sprintf(errmsg, "disallow issuer: %s, %s", ISSUER, MSGID);
+ return P_DISALLOW;
+ }
+ }
+ else
+ {
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+ }
+ type = NCMBDY;
+ }
+ else if (strstr(fptr, "END NCM BODY"))
+ {
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ break;
+ }
+ else
+ {
+ strcpy(errmsg, "NCM Notice syntax not correct");
+ return P_FAIL;
+ }
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ continue;
+ }
+
+ if (type == NCMHDR && readNCMheader(fptr) == P_FAIL)
+ return P_FAIL;
+ else if (type == NCMBDY)
+ readNCMbody(fptr);
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ }
+ if (NCMVER && ISSUER && TYPE && ACTION && COUNT && NCMID)
+ return P_OKAY;
+ else
+ {
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+ }
+
+ strcpy(errmsg, "I don't know..");
+ return P_FAIL;
+}
+
+int
+NCMcancel()
+{
+ int i, rel, num_ok, num_fail;
+ for (i = rel = num_ok = num_fail = 0; i < num_spammid; i++)
+ {
+ rel = cancel_article_front(SPAMMID[i]);
+ if (rel)
+ num_fail++;
+ else
+ num_ok++;
+ }
+ bbslog("NCMcancel %s %s, count:%d spam:%d, ok:%d fail:%d\n",
+ ISSUER, MSGID, atoi(COUNT), num_spammid, num_ok, num_fail);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* NoCeM-innbbsd */
+/* ------------------------------------------------------------------ */
+
+initial_nocem()
+{
+ bzero(SPAMMID[0], strlen(SPAMMID[0]) * num_spammid);
+ num_spammid = 0;
+ bzero(SPAMMID_NOW, strlen(SPAMMID_NOW));
+}
+
+int
+receive_nocem()
+{
+ int rel;
+ ncmperm_t *ncmt;
+
+ if (ncmdebug)
+ bbslog("NCM: receive %s\n", MSGID);
+
+ initial_nocem();
+
+ rel = NCMparse();
+
+ if (rel != P_OKAY)
+ {
+ if (rel != P_DISALLOW)
+ bbslog("NCMparse %s\n", errmsg);
+ return 0;
+ }
+
+ if (!num_spammid)
+ {
+ bbslog("NCMparse: nothing to cancel\n");
+ return 0;
+ }
+#ifdef PGP5
+ if (ncmdebug)
+ bbslog("NCM: verifying PGP sign\n");
+
+ rel = NCMverify();
+
+ if (rel != PGPGOOD)
+ {
+ bbslog("NCMverify %s, %s, %s\n", errmsg, MSGID, ISSUER);
+ return 0;
+ }
+#endif
+
+ if (ncmdebug)
+ bbslog("NCM: canceling spam in NoCeM Notice\n");
+ return NCMcancel();
+}
diff --git a/innbbsd/nocem.h b/innbbsd/nocem.h
new file mode 100644
index 00000000..40f3271d
--- /dev/null
+++ b/innbbsd/nocem.h
@@ -0,0 +1,61 @@
+/*
+ NoCeM-INNBBSD
+ Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw>
+*/
+
+#ifndef NOCEM_H
+#define NOCEM_H
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+#include "inntobbs.h"
+
+#include <stdarg.h> /* for va_start() problem */
+
+typedef struct ncmperm_t
+{
+ char *issuer;
+ char *type;
+ int perm;
+} ncmperm_t;
+
+ncmperm_t *NCMPERM=NULL, **NCMPERM_BYTYPE=NULL;
+static char *NCMPERM_BUF;
+int NCMCOUNT = 0;
+
+#define TEXT 0
+#define NCMHDR 1
+#define NCMBDY 2
+
+#define NOPGP -1
+#define PGPGOOD 0
+#define PGPBAD 1
+#define PGPUN 2
+
+#define P_OKAY 0
+#define P_FAIL -1
+#define P_UNKNOWN -2
+#define P_DISALLOW -3
+
+#define STRLEN 80
+#define MAXSPAMMID 10000
+#define LINELEN 512
+
+#define LeeymBBS "bbs.civil.ncku.edu.tw"
+#define LeeymEMAIL "leeym@cae.ce.ntu.edu.tw"
+#define NCMINNBBSVER "NoCeM-INNBBSD-0.71"
+
+#undef DONT_REGISTER
+
+extern char NCMVER[];
+extern char ISSUER[];
+extern char TYPE[];
+extern char ACTION[];
+extern char NCMID[];
+extern char COUNT[];
+extern char THRESHOLD[];
+extern char KEYID[];
+extern char SPAMMID_NOW[];
+extern char SPAMMID[][];
+
+#endif /* NOCEM_H */
diff --git a/innbbsd/receive_article.c b/innbbsd/receive_article.c
index c1d8c274..dbd542ae 100644
--- a/innbbsd/receive_article.c
+++ b/innbbsd/receive_article.c
@@ -561,7 +561,7 @@ cancel_article_front(msgid)
}
}
fclose(fp);
- if (strcmp(xfrom0, xfrom)) {
+ if (strcmp(xfrom0, xfrom) && !search_issuer(FROM)) {
bbslog("Invalid cancel %s, path: %s!%s, [`%s` != `%s`]\n",
FROM, MYBBSID, PATH, xfrom0, xfrom);
return 0;