diff options
Diffstat (limited to 'innbbsd')
-rw-r--r-- | innbbsd/COPYRIGHT.nocem | 11 | ||||
-rw-r--r-- | innbbsd/Makefile | 4 | ||||
-rw-r--r-- | innbbsd/bbslib.c | 1 | ||||
-rw-r--r-- | innbbsd/bbslink.c | 4 | ||||
-rw-r--r-- | innbbsd/innbbsd.c | 2 | ||||
-rw-r--r-- | innbbsd/nocem.c | 629 | ||||
-rw-r--r-- | innbbsd/nocem.h | 61 | ||||
-rw-r--r-- | innbbsd/receive_article.c | 2 |
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; |