/***************************************************************************** * * $Id$ * Purpose ...............: Post echomail message. * ***************************************************************************** * Copyright (C) 1997-2001 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 * 1971 BV IJmuiden * the Netherlands * * This file is part of MBSE BBS. * * This BBS is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * MBSE BBS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MBSE BBS; see the file COPYING. If not, write to the Free * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *****************************************************************************/ #include "../lib/libs.h" #include "../lib/structs.h" #include "../lib/records.h" #include "../lib/common.h" #include "../lib/clcomm.h" #include "../lib/msg.h" #include "../lib/msgtext.h" #include "../lib/dbcfg.h" #include "../lib/dbnode.h" #include "../lib/dbmsgs.h" #include "../lib/dbdupe.h" #include "../lib/dbuser.h" #include "../lib/dbftn.h" #include "ftn2rfc.h" #include "postecho.h" #include "storeecho.h" #include "addpkt.h" #include "rollover.h" /* * External declarations */ extern int do_quiet; /* No tty output */ extern int do_unsec; /* Toss unsecure */ extern int check_dupe; /* Check dupes */ extern time_t t_start; /* Reference time */ extern int echo_in; /* Echomail processed */ extern int echo_out; /* Echomail forwarded */ extern int echo_bad; /* Bad echomail */ #define MAXPATH 73 #define MAXSEEN 70 void tidy_qualify(qualify **); void fill_qualify(qualify **, fidoaddr, int, int); void dlog_qualify(qualify **, char *); int EchoOut(fidoaddr, char *, char *, char *, FILE *, int, int, time_t); /* * Add echomail mesage to the queue. */ int EchoOut(fidoaddr aka, char *toname, char *fromname, char *subj, FILE *fp, int flags, int cost, time_t date) { char *buf; FILE *qp; faddr *From, *To; int rc; if ((qp = OpenPkt(msgs.Aka, aka, (char *)"qqq")) == NULL) { WriteError("EchoOut(): OpenPkt failed"); return 1; } From = fido2faddr(msgs.Aka); To = fido2faddr(aka); rc = AddMsgHdr(qp, From, To, flags, cost, date, toname, fromname, subj); tidy_faddr(To); tidy_faddr(From); if (rc) { WriteError("EchoOut(): AddMsgHdr failed"); return 1; } rewind(fp); buf = calloc(2048, sizeof(char)); while ((fgets(buf, 2048, fp)) != NULL) { Striplf(buf); fprintf(qp, "%s\r", buf); } free(buf); putc(0, qp); fsync(fileno(qp)); fclose(qp); return 0; } void tidy_qualify(qualify **qal) { qualify *tmp, *old; for (tmp = *qal; tmp; tmp = old) { old = tmp->next; free(tmp); } *qal = NULL; } void fill_qualify(qualify **qal, fidoaddr aka, int orig, int insb) { qualify *tmp; tmp = (qualify *)malloc(sizeof(qualify)); tmp->next = *qal; tmp->aka = aka; tmp->inseenby = insb; tmp->send = ((!insb) && (!orig)); tmp->orig = orig; *qal = tmp; } void dlog_qualify(qualify **qal, char *msg) { qualify *tmpl; for (tmpl = *qal; tmpl; tmpl = tmpl->next) { Syslog('m', "%s InSB=%s Snd=%s Org=%s", aka2str(tmpl->aka), tmpl->inseenby ? "True":"False", tmpl->send ? "True":"False", tmpl->orig ? "True":"False"); } } /* * Post echomail message, forward if needed. * pkt_from, from, to, subj, orig, mdate, flags, cost, file * The msgs record must be in memory. * * 1 - Cannot open message base. * 4 - Rejected echomail message. * * For echomail, the crc32 is calculated over the ^AREA kludge, subject, * message date, origin line, message id. */ int postecho(faddr *p_from, faddr *f, faddr *t, char *orig, char *subj, time_t mdate, int flags, int cost, FILE *fp, int tonews) { char *buf, *msgid = NULL, *reply = NULL, *p, *q, sbe[16]; int First = TRUE, rc = 0, i, kludges = TRUE; int dupe = FALSE, bad = TRUE, seenlen, oldnet; faddr *Faddr; unsigned long crc; sysconnect Link; fa_list *sbl = NULL, *ptl = NULL, *tmpl; qualify *qal = NULL, *tmpq; FILE *nfp, *qp; // Syslog('M', "Entering postecho, area %s %s", msgs.Tag, msgs.Name); // Syslog('M', "p_from: %s", ascfnode(p_from, 0xff)); // Syslog('M', "from : %s", ascfnode(f, 0xff)); // Syslog('M', "to : %s", ascfnode(t, 0xff)); // Syslog('M', "subj : %s", printable(subj, 0)); // Syslog('M', "origin: %s", orig); // Syslog('M', "date : %s", rfcdate(mdate)); // Syslog('M', "flags : %08x", flags); // Syslog('M', "cost : %d", cost); // Syslog('M', "tonews: %s", tonews ? "True":"False"); memset(&Link, 0, sizeof(Link)); crc = 0xffffffff; echo_in++; /* * p_from is set for tossed echomail, it is NULL for local posted echomail and gated news. */ if (p_from) { while (GetMsgSystem(&Link, First)) { First = FALSE; if ((p_from->zone == Link.aka.zone) && (p_from->net == Link.aka.net) && (p_from->node == Link.aka.node)) { bad = FALSE; break; } } if (bad && (msgs.UnSecure || do_unsec)) { bad = FALSE; memset(&Link, 0, sizeof(Link)); } if (bad) { Syslog('+', "Node %s not connected to area %s", ascfnode(p_from, 0x1f), msgs.Tag); echo_bad++; return 4; } if (Link.cutoff && !bad) { Syslog('+', "Echomail from %s in %s refused, cutoff", ascfnode(p_from, 0x1f), msgs.Tag); bad = TRUE; echo_bad++; return 4; } if (!Link.receivefrom && !bad) { Syslog('+', "Echomail from %s in %s refused, read only", ascfnode(p_from, 0x1f), msgs.Tag); bad = TRUE; echo_bad++; return 4; } } else { /* * Fake the zone entry to be our own zone, this prevents * zonegate behaviour. It's also not a bad message yet. */ Link.aka.zone = msgs.Aka.zone; bad = FALSE; } if (CFG.toss_old && ((t_start - mdate) / 86400) > CFG.toss_old) { Syslog('+', "Rejecting msg: too old, %s", rfcdate(mdate)); bad = TRUE; echo_bad++; return 4; } /* * Read the message for kludges we need. */ buf = calloc(2048, sizeof(char)); First = TRUE; rewind(fp); while ((fgets(buf, 2048, fp)) != NULL) { Striplf(buf); // Syslogp('M', printable(buf, 0)); if (First && (!strncmp(buf, "AREA:", 5))) { crc = upd_crc32(buf, crc, strlen(buf)); First = FALSE; } if (!strncmp(buf, "\001MSGID: ", 8)) msgid = xstrcpy(buf + 8); if (!strncmp(buf, "\001REPLY: ", 8)) reply = xstrcpy(buf + 8); if (!strncmp(buf, "SEEN-BY:", 8)) { if (Link.aka.zone == msgs.Aka.zone) { p = xstrcpy(buf + 9); fill_list(&sbl, p, NULL); free(p); } else Syslog('m', "Strip zone SB lines"); } if (!strncmp(buf, "\001PATH:", 6)) { p = xstrcpy(buf + 7); fill_path(&ptl, p); free(p); } } /* end of checking kludges */ /* * Further dupe checking. */ crc = upd_crc32(subj, crc, strlen(subj)); if (orig == NULL) Syslog('!', "No origin line found"); else crc = upd_crc32(orig, crc, strlen(orig)); crc = upd_crc32((char *)&mdate, crc, sizeof(mdate)); if (msgid != NULL) { crc = upd_crc32(msgid, crc, strlen(msgid)); } else { if (check_dupe) { /* * If a MSGID is missing it is possible that dupes from some offline * readers slip through because these readers use the same date for * each message. In this case the message text is included in the * dupecheck. Redy Rodriguez. */ rewind(fp); while ((fgets(buf, 2048, fp)) != NULL) { Striplf(buf); if (strncmp(buf, "---", 3) == 0) break; if ((strncmp(buf, "\001", 1) != 0 ) && (strncmp(buf,"AREA:",5) != 0 )) crc = upd_crc32(buf, crc , strlen(buf)); } } } if (check_dupe) dupe = CheckDupe(crc, D_ECHOMAIL, CFG.toss_dupes); else dupe = FALSE; if (!dupe && !msgs.UnSecure && !do_unsec) { /* * Check if the message is for us. Don't check point address, * echomail messages don't have point destination set. */ if ((msgs.Aka.zone != t->zone) || (msgs.Aka.net != t->net) || (msgs.Aka.node != t->node)) { bad = TRUE; /* * If we are a hub or host and have all our echomail * connected to the hub/host aka, echomail from points * under a nodenumber aka isn't accepted. The match * must be further tested. */ if ((msgs.Aka.zone == t->zone) && (msgs.Aka.net == t->net)) { for (i = 0; i < 40; i++) { if ((CFG.akavalid[i]) && (CFG.aka[i].zone == t->zone) && (CFG.aka[i].net == t->net) && (CFG.aka[i].node == t->node)) bad = FALSE; /* Undo the result */ } } } if (bad) { echo_bad++; WriteError("Msg in %s not for us (%s) but for %s", msgs.Tag, aka2str(msgs.Aka), ascfnode(t,0x1f)); free(buf); if (msgid) free(msgid); if (reply) free(reply); return 4; } } /* * The echomail message is accepted for post/forward/gate */ if (!dupe) { if (msgs.Aka.zone != Link.aka.zone) { /* * If it is a zonegated echomailmessage the SEEN-BY lines * are stripped off including that of the other zone's * gate. Add the gate's aka to the SEEN-BY */ Syslog('m', "Gated echomail, clean SB"); tidy_falist(&sbl); sprintf(sbe, "%u/%u", Link.aka.net, Link.aka.node); Syslog('m', "Add gate SB %s", sbe); fill_list(&sbl, sbe, NULL); } /* * Add more aka's to SEENBY if in the same zone as our system. * When ready filter dupe's, there is at least one. */ for (i = 0; i < 40; i++) { if (CFG.akavalid[i] && (msgs.Aka.zone == CFG.aka[i].zone) && !((msgs.Aka.net == CFG.aka[i].net) && (msgs.Aka.node == CFG.aka[i].node))) { sprintf(sbe, "%u/%u", CFG.aka[i].net, CFG.aka[i].node); fill_list(&sbl, sbe, NULL); } } uniq_list(&sbl); } /* * Add our system to the path for later export. */ sprintf(sbe, "%u/%u", msgs.Aka.net, msgs.Aka.node); fill_path(&ptl, sbe); uniq_list(&ptl); /* remove possible duplicate own aka */ /* * Build a list of qualified systems to receive this message. * Complete the SEEN-BY lines. */ First = TRUE; while (GetMsgSystem(&Link, First)) { First = FALSE; if ((Link.aka.zone) && (Link.sendto) && (!Link.pause) && (!Link.cutoff)) { Faddr = fido2faddr(Link.aka); if (p_from == NULL) { fill_qualify(&qal, Link.aka, FALSE, in_list(Faddr, &sbl, FALSE)); } else { fill_qualify(&qal, Link.aka, ((p_from->zone == Link.aka.zone) && (p_from->net == Link.aka.net) && (p_from->node == Link.aka.node) && (p_from->point == Link.aka.point)), in_list(Faddr, &sbl, FALSE)); } tidy_faddr(Faddr); } } /* * Add SEEN-BY for nodes qualified to receive this message. * When ready, filter the dupes and sort the SEEN-BY entries. */ for (tmpq = qal; tmpq; tmpq = tmpq->next) { if (tmpq->send) { sprintf(sbe, "%u/%u", tmpq->aka.net, tmpq->aka.node); fill_list(&sbl, sbe, NULL); } } uniq_list(&sbl); sort_list(&sbl); /* * Create a new tmpfile with a copy of the message * without original PATH and SEENBY lines, add the * new PATH and SEENBY lines. */ rewind(fp); if ((nfp = tmpfile()) == NULL) WriteError("$Unable to open tmpfile"); while ((fgets(buf, 2048, fp)) != NULL) { Striplf(buf); fprintf(nfp, "%s", buf); /* * Don't write SEEN-BY and PATH lines */ if (strncmp(buf, " * Origin:", 10) == 0) break; fprintf(nfp, "\n"); } /* * Now add new SEEN-BY and PATH lines */ seenlen = MAXSEEN + 1; /* * Ensure that it will not match for the first entry. */ oldnet = sbl->addr->net - 1; for (tmpl = sbl; tmpl; tmpl = tmpl->next) { if (tmpl->addr->net == oldnet) sprintf(sbe, " %u", tmpl->addr->node); else sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node); oldnet = tmpl->addr->net; seenlen += strlen(sbe); if (seenlen > MAXSEEN) { seenlen = 0; fprintf(nfp, "\nSEEN-BY:"); sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node); seenlen = strlen(sbe); } fprintf(nfp, "%s", sbe); } seenlen = MAXPATH + 1; /* * Ensure it will not match for the first entry */ oldnet = ptl->addr->net - 1; for (tmpl = ptl; tmpl; tmpl = tmpl->next) { if (tmpl->addr->net == oldnet) sprintf(sbe, " %u", tmpl->addr->node); else sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node); oldnet = tmpl->addr->net; seenlen += strlen(sbe); if (seenlen > MAXPATH) { seenlen = 0; fprintf(nfp, "\n\001PATH:"); sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node); seenlen = strlen(sbe); } fprintf(nfp, "%s", sbe); } fprintf(nfp, "\n"); fflush(nfp); rewind(nfp); /* * Import this echomail, even if it's bad or a dupe. */ if ((rc = storeecho(f, t, mdate, flags, subj, msgid, reply, bad, dupe, nfp)) || bad || dupe) { /* * Store failed or it was bad or a dupe. Only log failed store. */ if (rc) WriteError("Store echomail in JAM base failed"); tidy_falist(&sbl); tidy_falist(&ptl); tidy_qualify(&qal); free(buf); if (msgid) free(msgid); if (reply) free(reply); return rc; } /* * Forward to other links */ for (tmpq = qal; tmpq; tmpq = tmpq->next) { if (tmpq->send) { if (SearchNode(tmpq->aka)) { StatAdd(&nodes.MailSent, 1L); UpdateNode(); SearchNode(tmpq->aka); } echo_out++; if (EchoOut(tmpq->aka, t->name, f->name, subj, nfp, flags, cost, mdate)) WriteError("Forward echomail to %s failed", aka2str(tmpq->aka)); } } /* * Gate to newsserver */ if (strlen(msgs.Newsgroup) && tonews) { rewind(nfp); qp = tmpfile(); while ((fgets(buf, 2048, nfp)) != NULL) { Striplf(buf); if (kludges && (buf[0] != '\001') && strncmp(buf, "AREA:", 5)) { kludges = FALSE; q = xstrcpy(Msg.From); for (i = 0; i < strlen(q); i++) if (q[i] == ' ') q[i] = '_'; fprintf(qp, "From: %s@%s\n", q, ascinode(f, 0x1f)); fprintf(qp, "Subject: %s\n", Msg.Subject); fprintf(qp, "To: %s\n", Msg.To); // FIXME: hier nog X-...-To: ?? fprintf(qp, "\n"); } fprintf(qp, "%s\n", buf); } rewind(qp); ftn2rfc(f, t, subj, orig, mdate, flags, qp); fclose(qp); } fclose(nfp); /* * Free memory used by SEEN-BY, ^APATH and Qualified lines. */ tidy_falist(&sbl); tidy_falist(&ptl); tidy_qualify(&qal); if (rc < 0) rc =-rc; free(buf); if (msgid) free(msgid); if (reply) free(reply); return rc; }