/*- * Copyright (c) 1996 * David Parsons. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by David Parsons * (orc@pell.chi.il.us) * 4. My name may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static char rcsid[] = "$Header: /home/orc/src/CVS/listmgr/server.c,v 1.9 1996/04/23 03:32:24 orc Exp $"; /* * server: run a mailing list. */ #include "server.h" #include "mailer.h" #include #include #include #include #include #include #include /* Macros to do error-checking arglist construction */ #define XADDARG(arg,siz) (addarg(&argc,&argv,arg,siz) == -1)\ ? critical_error("building exec list", errno)\ : 0 #define XADDSTR(arg) XADDARG(arg,-1) extern int critical_error(char *message, int errornumber); /* * header lines that are of some importance */ struct _header { char *line; int lsize; char *value; int required; #define HDR_REQUIRED 0x01 #define HDR_DISCARD 0x02 } headers[] = { { "From:", 5, (char*)0, HDR_REQUIRED }, { "Newsgroups:", 11, (char*)0, 0, }, { "References:", 11, (char*)0, 0 }, { "Subject:", 8, (char*)0, HDR_REQUIRED }, { "Path:", 5, (char*)0, 0 }, { "Date:", 5, (char*)0, 0 }, { "Message-Id:", 11, (char*)0, HDR_REQUIRED }, { "To:", 3, (char*)0, HDR_DISCARD }, { "Sender:", 7, (char*)0, HDR_DISCARD }, { "Received:", 9, (char*)0, HDR_DISCARD }, { "Approved:", 9, (char*)0, HDR_DISCARD }, }; #define NR_HEADERS (sizeof headers/sizeof headers[0]) #define FROM (headers) #define NEWSGROUPS (headers+1) #define REFERENCES (headers+2) #define SUBJECT (headers+3) #define PATH (headers+4) /* * the membership list */ GDBM_FILE users; char *pgm; /* what mailing list is this for? */ FILE *log; /* the incoming mail log */ /* * the name of the list */ char list[200]; /* * getheaders() pulls in some headers, writing them out to a spoolfile */ int getheaders(FILE *ff) { char *bfr; register i; struct _header *p; /* throw away all earlier headers */ for (i=0; ivalue) { free(p->value); p->value = 0; } } /* slurp up all the headers we can eat */ while ((bfr=getline(stdin)) && bfr[0] != 0) { fprintf(log, "%s\n", bfr); for (i=0; iline, bfr, p->lsize) == 0) { if (p->required & HDR_DISCARD) goto nextline; if (p->value) free(p->value); p->value = strdup(Trim(p->lsize + bfr)); if (p == PATH) fprintf(ff, "%s there.is.no.cabal!%s", PATH->line, Trim(PATH->lsize+bfr)); else fputs(bfr, ff); putc('\n', ff); } } nextline: free(bfr); } return bfr ? 1 : 0; } /* getheaders */ /* * complete(bits) checks to see if all the required headers have been read */ int complete(long *bits) { register i; register struct _header *p; *bits = 0; for (i=0; irequired & HDR_REQUIRED) && (p->value == (char*)0)) (*bits) |= (1<value); free(FROM->value); FROM->value = from; #ifdef LISTMGR_MACH sprintf(list, "%s@%s", pgm, LISTMGR_MACH); #else sprintf(list, "%s@", pgm); # ifdef USEHOSTINADDRESS gethostname(list+strlen(list), sizeof list-strlen(list)); strcat(list, "."); # endif getdomainname(list+strlen(list), sizeof list-strlen(list)); #endif fprintf(ff, "X-Mailer: ListManager (user-friendly object-oriented(tm) hack)\n"); fprintf(ff, "To: %s (%s mailing list)\n", list, pgm); fprintf(ff, "Errors-To: %s\n", LISTMGR_ADMIN); fprintf(ff, "Reply-To: %s\n", list); fprintf(ff, "Approved: %s\n\n", list); fputs("\n[article]\n", log); while ((c = getchar()) != EOF) { putc(c, ff); putc(c, log); } fputs("@OK-----\n", log); fclose(log); fflush(ff); rewind(ff); return ff; } /* retrieve */ /* * dopost() mails the article to the mailing list via the * SENDMAIL program, building an argument list, forking, * then calling execv(2) to do the dirty work. */ void dopost(FILE *ff) { char **argv = malloc(1); /* the argument list to send to execv */ int argc = 0; /* macros to make it easier to build the argument list */ datum key, value; /* to pick names out of the mailing list */ int pip[2]; /* IPC between server and sendmail */ register c; int rc; FILE *pipeline; pid_t pid; XADDSTR("sendmail"); XADDSTR("-i"); for (key = gdbm_firstkey(users); key.dptr; key = gdbm_nextkey(users, key)) if (strcasecmp(key.dptr, FROM->value) != 0) { value = gdbm_fetch(users, key); if (value.dptr && value.dsize > 0 && value.dptr[0] != 'Y') XADDARG(key.dptr, key.dsize); } XADDSTR((char*)0); if (pipe(pip) != 0) critical_error("creating pipe", errno); if ((pid=launch(SENDMAIL, argv, pip[0], EOF, EOF)) >= 0) { /* pipe everything to the child, then die */ if ((pipeline = fdopen(pip[1], "w")) != (FILE *)0) { rewind(ff); while ((c=getc(ff)) != EOF) putc(c, pipeline); if (ferror(pipeline) || ferror(ff)) critical_error("writing to " SENDMAIL, errno); fclose(pipeline); waitpid(pid, &rc, 0); exit(WEXITSTATUS(rc)); } else critical_error("writing to " SENDMAIL, errno); } else critical_error("launching " SENDMAIL, errno); } /* dopost */ /* * reject() returns a refusal message to the sender */ void reject(char *subject, char *fmt, ...) { char **argv = malloc(1); /* the argument list to send to execv */ int argc = 0; /* use argv/argc for XADDARG */ int pip[2]; /* IPC between server and sendmail */ int rc; FILE *pipeline; pid_t pid; va_list ptr; XADDSTR("sendmail"); XADDSTR("-i"); XADDSTR(FROM->value); XADDSTR(LISTMGR_ADMIN); XADDSTR((char*)0); if (pipe(pip) != 0) critical_error("creating pipe", errno); if ((pid=launch(SENDMAIL, argv, pip[0], EOF, EOF)) >= 0) { /* pipe everything to the child, then die */ if ((pipeline = fdopen(pip[1], "w")) != (FILE *)0) { fprintf(pipeline, "To: %s\n", FROM->value); fprintf(pipeline, "Cc: %s\n", LISTMGR_ADMIN); fprintf(pipeline, "From: %s (list manager)\n", list); fprintf(pipeline, "Subject: %s\n\n", subject); va_start(ptr, fmt); vfprintf(pipeline, fmt, ptr); va_end(ptr); fclose(pipeline); waitpid(pid, &rc, 0); exit(WEXITSTATUS(rc)); } else critical_error("writing to " SENDMAIL, errno); } } /* reject */ /* * critical_error() is the all-purpose panic and inform everyone * error routine. It writes an error report on stdout, for sendmail * to return to the sender, and it also writes a error message to * the server moderator for their (hopefully) immediate attention */ int critical_error(char *message, int errornumber) { FILE *mail = popen(SENDMAIL " " LISTMGR_ADMIN, "w"); syslog(LOG_CRIT, errornumber ? "%s %s" : "%s", message, strerror(errornumber)); if (mail) { fprintf(mail, "From: %s (list manager)\n", pgm); fprintf(mail, "Subject: IT'S THE APOCOLYPSE!\n\n"); fprintf(mail, "We lost an incoming article: %s\n", message); if (errornumber) { fprintf(mail, "\terror %d\n", errornumber); fprintf(mail, "\t %s\n", strerror(errornumber)); } fprintf(mail, "\n-- the computer\n"); pclose(mail); } reject( "Internal listmgr error!", "Your message to %s was lost by the list manager\n" "Please contact %s about it, including a copy of\n", "this message with the note\n" "\n\tWhen: %s\n" "\n-- the computer (all die. Oh the embarrassment)\n", pgm, LISTMGR_ADMIN, message); exit(1); } /* * the server, in mortal flesh */ int main(int argc, char **argv) { FILE *ff; datum key, value; setregid(getegid(),getegid()); setreuid(geteuid(),geteuid()); if ((pgm = strrchr(argv[0], '/')) != 0) pgm++; else pgm = argv[0]; openlog(pgm, 0, LOG_NEWS); #define USERS LISTMGR_DIR "/%s-users" if ((users = gdbm_open(Filename(USERS, pgm), 0, GDBM_READER, 0, 0)) == 0) { critical_error(gdbm_strerror(gdbm_errno), 0); } ff = retrieve(); key.dptr = FROM->value; strlwr(key.dptr); key.dsize = strlen(FROM->value); value = gdbm_fetch(users, key); if (value.dsize > 0 && value.dptr != 0) dopost(ff); reject( "Article rejected", "%s, you are not a member of the %s mailing list.\n" "If you wish to join this list, please ask %s\n", FROM->value, pgm, LISTMGR_REQUEST); exit(1); } /* main */