: This is a shar archive. Extract with sh, not csh. echo x - export-listserv/ADDHELP cat > export-listserv/ADDHELP << '!Funky!Stuff!' ADD listname ADD address listname adds the given address to or from the list specified. Mail is sent to the address given to confirm the add operation. We strongly recommend that you use your campus registered mailname when subscribing (i.e., use the second form of the command which includes a specification of the address). If you omit the 'address' the command will assume the mailbox that is in the From: line of the message. Note that SUBSCRIBE is a synonym for ADD. !Funky!Stuff! echo x - export-listserv/BADADDR cat > export-listserv/BADADDR << '!Funky!Stuff!' Your file request cannot be completed. !Funky!Stuff! echo x - export-listserv/COMMANDS cat > export-listserv/COMMANDS << '!Funky!Stuff!' add ADDHELP subscribe ADDHELP sub ADDHELP delete DELHELP del DELHELP unsubscribe DELHELP unsub DELHELP delete-all D-ALLHELP del-all D-ALLHELP unsubscribe-all D-ALLHELP unsub-all D-ALLHELP list LISTHELP index INDEXHELP longindex INDEXHELP help HELP faq FAQHELP info INFOHELP get INFOHELP !Funky!Stuff! echo x - export-listserv/DELHELP cat > export-listserv/DELHELP << '!Funky!Stuff!' DELETE listname DELETE address listname deletes the given address to or from the list specified. Mail is sent to the address given to confirm the delete operation. We strongly recommend that you use your campus registered mailname when subscribing(i.e., use the second form of the command which includes a specification of the address). If you omit the 'address' the command will assume the mailbox that is in the From: line of the message. Note that UNSUBSCRIBE is a synonym for DELETE. !Funky!Stuff! echo x - export-listserv/D_ALLHELP cat > export-listserv/D_ALLHELP << '!Funky!Stuff!' DELETE-ALL UNSUBSCRIBE-ALL DELETE-ALL address UNSUBSCRIBE-ALL address unsubscribes given address from all mailing lists. Mail is sent the address given to confirm the deletions. If you omit the 'address' the command will assume the mailbox that is in the From: line of the message. !Funky!Stuff! echo x - export-listserv/FAQHELP cat > export-listserv/FAQHELP << '!Funky!Stuff!' FAQ FAQ listname sends a list of "Frequently Asked Questions" for the appropriate mailing list. The command "FAQ" by itself sends an index of available FAQ's. !Funky!Stuff! echo x - export-listserv/HELP cat > export-listserv/HELP << '!Funky!Stuff!' You can subscribe or unsubscribe to any of the various campus mailing lists and the local redistributions of global mailing lists by sending email to "listserv@ucsd". The commands understood by the listserv program are: HELP lists this file. This is also sent whenever a message to listserv is received from which no valid command could be parsed. HELP groupname lists a brief description of the group requested. INDEX lists all the groups available for subscription. LONGINDEX lists all the groups and their descriptions. ADD listname DELETE listname ADD address listname DELETE address listname adds or deletes the given address to or from the list specified. Mail is sent to the address given to confirm the add or delete operation. We strongly recommend that you use your campus registered mailname when subscribing (i.e., use the second form of the command which includes a specification of the address). If you omit the 'address' the command will assume the mailbox that is in the From: line of the message. Note that SUBSCRIBE is a synonym for ADD; UNSUBSCRIBE for DELETE. DELETE-ALL UNSUBSCRIBE-ALL DELETE-ALL address UNSUBSCRIBE-ALL address unsubscribes given address from all mailing lists. Mail is sent the address given to confirm the deletions. If you omit the 'address' the command will assume the mailbox that is in the From: line of the message. LIST LIST address lists all mailing lists to which the given address is subscribed. If you omit the 'address' the command will assume the mailbox is in the from line. FAQ FAQ listname sends a list of "Frequently Asked Questions" for the appropriate mailing list. The command "FAQ" by itself sends an index of available FAQ's. INFO INFO filename1 filename2 ... filenameN GET filename1 filename2 ... filenameN sends a copy of the appropriate information sheet associated with each file name. The command "INFO" by itself will send an index of available info sheets. A command must be the first word on each line in the message. Lines which do not start with a command word are ignored. If no commands were found in the entire message, this help file will be returned to the user. A single message may contain multiple commands; a separate response will be sent for each. Please note that is IS possible to add or delete someone else's subscription to a mailing list. This facility is provided so that subscribers may alter their own subscriptions from a new or different computer account. There is therefore some potential for abuse; we have chosen to limit this by mailing a confirmation notification of any addition or deletion to the address added or deleted including a copy of the message which requested the operation. At least you can find out who's doing it to you. Examples: add sunusers add ewombat foodlovers delete wombat@cyberpunk.ucsd.edu connectionists help eggbeaters Note that although you would mail submissions to a campus mailing list by addressing mail to e.g., sunusers@ucsd.edu, in a subscription request you specify the name of the list simply (without the @ucsd part) as in the first example above. !Funky!Stuff! echo x - export-listserv/INDEXHELP cat > export-listserv/INDEXHELP << '!Funky!Stuff!' INDEX lists all the groups available for subscription. LONGINDEX lists all the groups and their descriptions. !Funky!Stuff! echo x - export-listserv/INFOHELP cat > export-listserv/INFOHELP << '!Funky!Stuff!' INFO INFO filename1 filename2 ... filenameN GET filename1 filename2 ... filenameN sends a copy of the appropriate information sheet associated with each file name. The command "INFO" by itself will send an index of available info sheets. !Funky!Stuff! echo x - export-listserv/LISTHELP cat > export-listserv/LISTHELP << '!Funky!Stuff!' LIST LIST address lists all mailing lists to which the given address is subscribed. If you omit the 'address' the command will assume the mailbox is in the From: line of the message. !Funky!Stuff! echo x - export-listserv/Makefile cat > export-listserv/Makefile << '!Funky!Stuff!' CFLAGS = -g all: listserv clean:: rm -f core listserv *.o install: all install -c -m 2711 -g mail listserv /usr/mail/bin listserv: main.o commands.o subscribe.o str.o listsearch.o info.o faq.o cc -O -Bstatic -o listserv main.o commands.o subscribe.o str.o listsearch.o info.o faq.o main.o: main.c listserv.h cc -c $(CFLAGS) main.c commands.o: commands.c listserv.h cc -c $(CFLAGS) commands.c subscribe.o: subscribe.c listserv.h cc -c $(CFLAGS) subscribe.c str.o: str.c listserv.h cc -c $(CFLAGS) str.c listsearch.o: listsearch.c listserv.h cc -c $(CFLAGS) listsearch.c info.o: info.c listserv.h cc -c $(CFLAGS) info.c faq.o: faq.c listserv.h cc -c $(CFLAGS) faq.c !Funky!Stuff! echo x - export-listserv/README cat > export-listserv/README << '!Funky!Stuff!' To install LISTSERV on your machine: ----------------------------------- 1. Edit the listserv.h file This file contains definitions for the directories in which mailing lists are stored, as well as the location of help files, control files, FAQs, and .info and .pub files. Set these to locally sensible directories. Their definitions are: HELPFILE - This is the helpfile automatically sent to users who send unparsable input to LISTSERV SERVDIR - This is the directory in which mailing lists and their associated .pub and .info files reside. LOGFILE - This is the file where, when logging is enabled, all requests sent to LISTSERV are logged. COMMANDFILE - This file is a sort of 'alias' file for obtaining help. It describes which specific helpfile is mailed to users requesting help for a particular command; for example, HELP ADD and HELP SUB should probably return the same helpfile, ADDHELP. LISTSERVADDR - The address to which people mail to talk to the LISTSERV program. LISTSERVMANAGER - The address of a human being at your local site who maintains the LISTSERV program. DOMAIN - This is, in general, your Internet domain; in regards to LISTSERV's behaviour, it also serves to define 'local' and 'nonlocal' for the purposes of .pub and .info files. That is, the presence of a .info file will allow subscription to a mailing list from anyone within this local domain; the presence of a .pub file will allow subscription by people outside of this domain. INFODIR - The directory in which information files are maintained. You should create a file an index file detailing the contents of the info directory. This file is sent people when listserv receives the command "info" without any arguments. An example index file has been included with this distribution. INFOFILE - The index file for listserv information files. 2. Edit the Makefile. Ensure that install is installing the listserv program in a place sensible for your system, or alter the location. 3. Run 'make install'. 4. Enter an alias for listserv in your /usr/lib/aliases file. Our local aliases look like this, for a copy of listserv stored in /usr/mail/listserv: listserv:"|/usr/mail/listserv" listserve:listserv listserver:listserv infoserver:listserv 5. If you run into questions or complications, please contact listserv-manager@ucsd.edu !Funky!Stuff! echo x - export-listserv/commands.c cat > export-listserv/commands.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/commands.c,v 1.4 92/09/14 11:03:42 andy Exp Locker: andy $"; extern FILE *msg; extern FILE *mailer; sendhelp(from,request) char *from, *request; { printf("called sendhelp with %s %s\n", from, request); callmailer("", from, request); mailcat(HELPFILE,""); pclose(mailer); return; } listhelp(from,grp,request,outsider) char *from,*grp,*request; int outsider; { FILE *commands; int i; char tmp[128], hlp[128], cmd[128]; printf("called listhelp with %s %s %s\n", from,grp,request); callmailer("", from, request); strcpy(tmp, COMMANDFILE); commands = fopen(tmp, "r"); if( commands == NULL) { perror(tmp); exit(1); } while(i = fscanf(commands, "%s %s",cmd, hlp) > 0) if (!strcasecmp(cmd, grp)) { sprintf(tmp, "%s/%s", SERVDIR, hlp); mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); fclose(commands); return; } fclose(commands); if (outsider) sprintf(tmp,"%s/%s.pub", SERVDIR, grp); else sprintf(tmp,"%s/%s.info", SERVDIR, grp); if (access(tmp,R_OK) != 0) { fprintf(mailer,"The mailing list \"%s\" could not be found.\n", grp); fprintf(mailer,"You may use the INDEX command to get a listing\n"); fprintf(mailer,"of available mailing lists.\n"); fflush(mailer); pclose(mailer); return; } fprintf(mailer,"Mailing list \"%s\":\n", grp); mailcat(tmp,"\t"); pclose(mailer); return; } sendindex(from,request,l,outsider) char *from,*request; int l,outsider; { FILE *ls; char tmp[128]; char buf[128]; int i; printf("called sendindex with %s %s %d %d\n", from,request,l,outsider); if (outsider) sprintf(tmp,"cd %s; ls *.pub | sed -e 's/.pub//", SERVDIR); else sprintf(tmp,"cd %s; ls *.info | sed -e 's/.info//", SERVDIR); ls = popen(tmp,"r"); if (ls == NULL) { perror(tmp); exit(1); } callmailer("", from, request); fprintf(mailer,"Index of mailing lists\n"); /* read the result of the ls */ while (fgets(tmp, sizeof(tmp), ls)) { while (tmp[(i=strlen(tmp)-1)] == '\n') tmp[i] = '\0'; fprintf(mailer, "%s\n", tmp); /* if he wanted the long listing, cat the .info file */ if (l) { if (outsider) sprintf(buf, "%s/%s.pub", SERVDIR, tmp); else sprintf(buf, "%s/%s.info", SERVDIR, tmp); mailcat(buf,"\t"); fprintf(mailer, "\n"); } } pclose(ls); pclose(mailer); return; } !Funky!Stuff! echo x - export-listserv/faq.c cat > export-listserv/faq.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/faq.c,v 1.1 92/09/14 11:03:46 andy Exp Locker: andy $"; extern FILE *mailer; sendfaq(from, command, outsider) char *from, *command; int outsider; { int i; char grp[256], tmp[512]; printf("called sendfaq with %s %s %s\n", from, command, outsider); i = sscanf(command, "%s%s", tmp, grp); if(i != 2) { callmailer("", from, command); sprintf(tmp, "%s/FAQHELP", SERVDIR); mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); return; } sprintf(tmp, "%s/%s.pub", SERVDIR, grp); callmailer("", from, command); if(access(tmp,R_OK) == 0) { sprintf(tmp, "%s/%s.faq", SERVDIR, grp); if(access(tmp, R_OK) == 0) { mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); return; } else { fprintf(mailer, "There is no FAQ available for"); fprintf(mailer, " mailing list \"%s\".\n", grp); fflush(mailer); pclose(mailer); return; } } else if(outsider) { fprintf(mailer,"The mailing list \"%s\" could not be found.\n", grp); fprintf(mailer,"You may use the INDEX command to get a listing\n"); fprintf(mailer,"of available mailing lists.\n"); fflush(mailer); pclose(mailer); return; } else { sprintf(tmp, "%s/%s.faq", SERVDIR, grp); if(access(tmp, R_OK) == 0) { mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); return; } else { fprintf(mailer, "There is no FAQ available for"); fprintf(mailer, " mailing list \"%s\".\n", grp); fflush(mailer); pclose(mailer); return; } } } faqindex(from,request) char *from,*request; { FILE *ls; char tmp[128]; char buf[128]; int i; printf("called faqindex with %s %s\n", from,request); sprintf(tmp,"cd %s; ls *.faq | sed -e 's/.faq//", SERVDIR); ls = popen(tmp,"r"); if (ls == NULL) { perror(tmp); exit(1); } callmailer("", from, request); fprintf(mailer,"Index of Frequently Asked Questions\n"); /* read the result of the ls */ while (fgets(tmp, sizeof(tmp), ls)) { while (tmp[(i=strlen(tmp)-1)] == '\n') tmp[i] = '\0'; fprintf(mailer, "%s\n", tmp); } pclose(ls); pclose(mailer); return; } !Funky!Stuff! echo x - export-listserv/info.c cat > export-listserv/info.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/info.c,v 1.1 92/09/14 11:03:52 andy Exp Locker: andy $"; extern FILE *msg; extern FILE *mailer; infoindex(from,request) char *from, *request; { printf("called sendhelp with %s %s\n", from, request); callmailer("", from, request); mailcat(INFOFILE,""); pclose(mailer); return; } sendinfo(from, command) char *from, *command; { int i; char *p; char doc[256], tmp[512], buf[BUFSIZ]; printf("called sendfaq with %s %s\n", from, command); /* Grab filenames trailing after word "info" */ strcpy(buf, command); while(p = index(buf, ' ')) { /* Get rid of extra blanks and return if only trailing whitespace remains */ while(*(++p) == ' ') ; if(!strlen(p)) return; strcpy(buf, p); sscanf(buf, "%s", doc); if (index(doc, '*')) { callmailer("", from, command); sprintf(tmp, "%s/INFOHELP", SERVDIR); mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); continue; } sprintf(tmp, "%s/%s", INFODIR, doc); callmailer("", from, command); if(access(tmp,R_OK) == 0) { mailcat(tmp, "\t"); fflush(mailer); pclose(mailer); } else { fprintf(mailer, "File %s does not exist.", doc); fflush(mailer); pclose(mailer); } } } !Funky!Stuff! echo x - export-listserv/listsearch.c cat > export-listserv/listsearch.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/listsearch.c,v 1.3 92/02/24 12:54:25 andy Exp Locker: andy $"; extern FILE *msg; extern FILE *mailer; listsearch(from,command) char *from,*command; { FILE *list; FILE *listtmp; FILE *subslist; DIR *listdir; struct dirent *entry; struct stat statbuf; char *s, *p; char *template = "/tmp/listservXXXXXX"; char adr[256]; char tmp[512]; char request[256]; char buf[BUFSIZ]; int i; printf("listsearch %s %s \n", from, command); i = sscanf(command,"%s%s", request, adr); if ((i < 1) || (i > 2)) sendhelp(from, command); if (i == 1) strcpy(adr, from); cleanup(adr,&i); listdir = opendir(SERVDIR); if (listdir == NULL) { perror(SERVDIR); exit(1); } i = mkstemp(template); if ( i == -1) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[5] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } else { subslist = fdopen(i, "w+"); if (subslist == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[5] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } } while (entry = readdir(listdir)) { if ((index(entry->d_name, '.')) || (isupper(entry->d_name[0]))) continue; sprintf(tmp, "%s/%s", SERVDIR, entry->d_name); if (stat(tmp,&statbuf)) { perror(tmp); exit(1); } /*If it's a directory, go to next entry*/ else if ((statbuf.st_mode & S_IFMT) == S_IFDIR) continue; /*Skip the restricted access ones*/ if (access(tmp,R_OK) != 0) { callmailer(LISTSERVMANAGER, from, ""); p = rindex(tmp, '/'); fprintf(mailer,"Unable to access %s\n", p+1); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); continue; } list = fopen(tmp, "r"); if (list == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[6] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } flock(fileno(list), LOCK_EX); while(fgets(buf, sizeof(buf), list)) { buf[strlen(buf)-1] = '\0'; if (!strcasecmp(buf, adr)) { fputs(entry->d_name, subslist); fputs("\n", subslist); } } fflush(list); flock(fileno(list), LOCK_UN); fclose(list); } closedir(listdir); if (!strcasecmp(request, "delete-all") || !strcasecmp(request, "unsubscribe-all") || !strcasecmp(request, "unsub-all") || !strcasecmp(request, "del-all")) { rewind(subslist); while(fgets(tmp, sizeof(tmp), subslist)) { strcpy(buf, SERVDIR); strcat(buf, "/"); strcat(buf, tmp); buf[strlen(buf) -1] = '\0'; list = fopen(buf, "r"); if (list == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer, "Error[7] processing request. Please try later.\n"); fprintf(mailer, ">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } flock(fileno(list), LOCK_EX); strcat(buf, ".tmp"); listtmp = fopen(buf, "w"); if (listtmp == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[8] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } /* copy the list, omitting the one address */ while (fgets(tmp, sizeof(tmp), list)) { tmp[strlen(tmp)-1] = '\0'; if (strcasecmp(tmp, adr)) { fputs(tmp, listtmp); fputs("\n", listtmp); } } fflush(listtmp); fclose(listtmp); /* replace the old list with the shortened one */ strcpy(tmp, buf); s = rindex(buf, '.'); *s = '\0'; unlink(buf); rename(tmp, buf); /* put updated one in place */ flock(fileno(list), LOCK_UN); /* release lock */ } } #ifndef DEBUG if (strcmp(from, adr)) { callmailer(adr, from, command); fprintf(mailer,"Per request by %s\n", from); } else { callmailer("", from, command); fprintf(mailer,"Per your request\n"); } #else callmailer("", from, command); fprintf(mailer,"Per your request\n"); #endif fprintf(mailer,"\t\"%s\"\n", command); rewind(subslist); if ((i = getc(subslist)) == EOF) fprintf(mailer, "'%s' is not subscribed to any mailing lists.\n", adr); else { if (strcasecmp(request, "delete-all") && strcasecmp(request, "unsubscribe-all") && strcasecmp(request, "unsub-all") && strcasecmp(request, "del-all")) fprintf(mailer,"'%s' is subscribed to the following mailing lists:\n", adr); else fprintf(mailer,"'%s' was DELETED from the following mailing lists:\n", adr); mailcat(template, "\t"); } fflush(mailer); pclose(mailer); unlink(template); fflush(subslist); fclose(subslist); } !Funky!Stuff! echo x - export-listserv/listserv.h cat > export-listserv/listserv.h << '!Funky!Stuff!' #include #include #include #include #include #include #include #include #include #define MAILER "/usr/lib/sendmail" #define HELPFILE "/usr/mail/listserver/HELP" #define INFOFILE "/usr/mail/infoserver/INDEX" #define SERVDIR "/usr/mail/listserver" #define LOGFILE "/var/log/listserv" #define COMMANDFILE "/usr/mail/listserver/COMMANDS" #define AFLAGS (O_APPEND | O_CREAT | O_RDWR) #define AMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) #define LISTSERVADDR "Listserv@FOOBAR.EDU" #define LISTSERVMANAGER "listserv-manager@foobar.edu" #define INFODIR "/usr/mail/infoserver" #define DOMAIN "foobar.edu" !Funky!Stuff! echo x - export-listserv/listserv.l cat > export-listserv/listserv.l << '!Funky!Stuff!' .\" @(#)listserv.l 1.03 92/03/03 SMI; UCSD Edition .TH LISTSERV L "03 March 1992" .SH NAME listserv \- manage mailing lists via sendmail interface .SH SYNOPSIS .B /usr/mail/listserv .SH DESCRIPTION .LP .B listserv is a mail server which allows users to easily list the available mailing lists on a machine, and add or delete themselves from lists, through electronic mail. A user communicates with .B listserv by mailing it a file containing .B listserv commands, one to each line. The general syntax for mail to a listserver is .IB mail .IB listserv@host.domain .LP The .B listserv commands available on the UCSD .B listserv program are as follows. Several commands may be included in a single mail message, but must be on separate lines in the message. .TP 15 .B help Returns the contents of the HELPFILE, including instructions on the use of all .B listserv commands. .TP \fBhelp \fIcommand\fR Returns the help file for the indicated .B listserv command. .TP \fBhelp \fIlistname\fR Returns the .info file for the indicated .I listname. .TP .B index Gives a list of which mailing lists are available on the machine on which the listserver resides. .TP .B longindex Returns a list of all mailing lists available, including information contained in the .info and .pub files for those lists. .TP \fBadd \fIlistname\fR Adds the user sending the message to the indicated listname with the address found in the From: line in the mail message. .TP \fBadd \fIaddress listname\fR Adds the user indicated in .I address to the indicated listname. .TP \fBdelete \fIlistname\fR Attempts to unsubscribe the user sending the message from the indicated listname, using the address found in the From: line in the mail message. .TP \fBdelete \fIaddress listname\fR Attempts to unsubscribe the user indicated in .I address from the indicated listname. .TP .B list Provides a list of the mailing lists to which the user whose address is found in the From: line of the mail message is subscribed. .TP \fBlist \fIaddress\fR Provides a list of the mailing lists to which the user whose address is .I address is subscribed. .TP \fBfaq \fIlistname\fR If available, returns a list of Frequently Asked Questions about the indicated .I listname and their answers. .TP .B info Returns a list of all information sheets and listserv sendable files available. .TP \fBinfo \fIfilename\fR Returns the indicated file or info sheet. .SH FILES .IP /usr/mail/maillists/*.info 30 Short descriptions of locally-subscribable mailing lists. These files must exist for a mailing list to be accessable through .B listserv. .IP /usr/mail/maillists/*.pub 30 Short descriptions of publicly-subscribable mailing lists. These files must exist for a mailing list to be accessable through .B listserv from machines outside the local domain. .IP /usr/mail/maillists/*.intro 30 Optional; provides an introductory posting which, if it exists, is automatically sent to new users when they subscribe. .IP /usr/mail/maillists/*.faq 30 An optional FAQ (Frequently Asked Questions) posting which, if it exists, is sent to users when they submit the .B faq .BI listname command to .B listserv for a particular mailing list. .SH "SEE ALSO" .BR mail (1), .BR sendmail (8). .SH BUGS .LP Not a bug, but if the user's home machine's sendmail.cf file has changed since she subscribed to a list, she may be unable to unsubscribe using the .B delete .BI listname syntax. If she does not know how her From: line appeared previously, it may be necessary for her to contact a human to unsubscribe from the mailing list. .SH NOTES .LP Please contact .B listserv-manager@ucsd.edu for questions or updates to this program. !Funky!Stuff! echo x - export-listserv/main.c cat > export-listserv/main.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /nfs/ucsd/local/src/mail/listserv/RCS/main.c,v 1.9 93/02/25 16:04:13 andy Exp Locker: andy $"; FILE *mailer; FILE *logfile; main(argc,argv) int argc; char **argv; { char from[256]; char buf[BUFSIZ]; char grp[64]; char dat[64]; char *p; int i; int gotcommand = 0; int outsider = 0; int inheader; int log; int garbage = 0; /* gotta have some default if the From: line is missing */ strcpy(from, "ListServ"); inheader = 1; while (fgets(buf,BUFSIZ,stdin) != NULL) { /* drop trailing newline */ while (buf[(i=strlen(buf)-1)] == '\n') buf[i] = '\0'; /* drop trailing blanks */ p = buf + (strlen(buf) - 1); while (p && *p && p >= buf && isspace(*p)) *p-- = '\0'; /* deblank beginning of line */ p = buf; while (p && *p && isspace(*p)) p++; if (p != buf) strcpy(buf, p); if (strlen(buf) == 0) /* blank line ends header */ { inheader = 0; continue; } /* get the From: line so we can return the answer Note that we DON'T check that we're in the header when picking up the From: line; that's so that we'll take the last from line we encounter which makes it handle forwarded messages correctly. */ if (strncasecmp(buf,"From: ",6) == 0) { strcpy(from, &buf[6]); cleanup(from, &outsider); printf("outsider = %d\n", outsider); continue; } if (strncasecmp(buf, "From: listserv", 14) == 0 || strncasecmp(buf, "subject: re: your listserve request", 35) == 0) { printf("loop detected, flushing mail\n"); exit(0); } /* don't look for commands in the header */ if (inheader) continue; /* avoid mail loops - ignore requests from ourself! */ if (!strncasecmp(from, "listserv", 8)) { printf("I refuse to talk to myself.\n"); exit(0); } /* Throw away bounced mail */ if (strstr(from, "daemon") != NULL) { printf("We don't do business with your type.\n"); exit(0); } /* force to lower case and scan for commands */ p = buf; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } /* log the request */ log = open(LOGFILE, AFLAGS, AMODE); if (log == -1) perror(LOGFILE); else { logfile = fdopen(log, "a"); if (logfile != NULL) { long clock = time(0); strcpy(dat,ctime(&clock)); /* drop trailing newline */ while (dat[(i=strlen(dat)-1)] == '\n') dat[i] = '\0'; printf("logfile: %s|%s|%s\n", dat, from, buf); fprintf(logfile,"%s|%s|%s\n", dat, from, buf); fflush(logfile); close(log); } else perror(LOGFILE); } if (!strncasecmp(buf, "list ",5) || !strncasecmp(buf, "delete-all ",11) || !strncasecmp(buf, "del-all ",8) || !strncasecmp(buf, "unsubscribe-all ",16) || !strncasecmp(buf, "unsub-all ",10) || !strcasecmp(buf, "list") || !strcasecmp(buf, "delete-all") || !strcasecmp(buf, "del-all") || !strcasecmp(buf, "unsubscribe-all") || !strcasecmp(buf, "unsub-all")) { gotcommand++; listsearch(from, buf); continue; } if (!strncasecmp(buf,"add ", 4) || !strncasecmp(buf,"subscribe ", 10) || !strncasecmp(buf,"sub ",4)) { gotcommand++; subscription(from,buf,1,outsider); continue; } if (!strncasecmp(buf,"delete ", 7) || !strncasecmp(buf,"del ",4) || !strncasecmp(buf,"unsubscribe ", 12) || !strncasecmp(buf,"unsub ",6)) { gotcommand++; subscription(from,buf,0,outsider); continue; } if (!strcasecmp(buf,"index") || !strcasecmp(buf,"longindex")) { gotcommand++; sendindex(from,buf,!strcasecmp(buf,"longindex"), outsider); continue; } if (!strncasecmp(buf, "faq ", 4)) { gotcommand++; sendfaq(from, buf, outsider); continue; } if (!strcasecmp(buf, "faq")) { gotcommand++; faqindex(from, buf); continue; } if (!strcasecmp(buf, "info")) { gotcommand++; infoindex(from, buf); continue; } if (!strncasecmp(buf, "info ", 5) || !strncasecmp(buf, "get ",4)) { gotcommand++; sendinfo(from, buf); continue; } if (!strcasecmp(buf,"help")) { gotcommand++; sendhelp(from,buf); continue; } if (!strncasecmp(buf,"help ",5)) { gotcommand++; sscanf(buf,"%*s%s", grp); listhelp(from,grp,buf,outsider); continue; } /* Flush message if > 5 lines of garbage */ if (++garbage > 5) { printf("Garbled message, flushing...\n"); exit(0); } } if (gotcommand == 0) sendhelp(from, "indecipherable"); exit(0); } callmailer(redirect, toaddr, request) char *redirect, *toaddr, *request; { char cbuf[BUFSIZ]; printf("callmailer \"%s\",\"%s\",\"%s\"\n", redirect, toaddr, request); strcpy(cbuf, MAILER); strcat(cbuf, " -f"); strcat(cbuf, LISTSERVMANAGER); strcat(cbuf, " -F\"Mailing List Processor\""); strcat(cbuf, " -t -oi"); strcat(cbuf, " "); strcat(cbuf, redirect); mailer = popen(cbuf,"w"); printf("mailer popen '%s'\n", cbuf); if (mailer == NULL) { perror(cbuf); exit(1); } fprintf(mailer,"From: "); fprintf(mailer, LISTSERVADDR); fprintf(mailer, " (Mailing List Processor)\n"); fprintf(mailer,"To: %s\n", toaddr); fprintf(mailer,"Subject: Re: your LISTSERV request \"%s\"\n", request); fprintf(mailer,"\n"); } mailcat(mfname,prefix) char *mfname, *prefix; { FILE *mf; char buf[BUFSIZ]; printf("mailcat \"%s\", \"%s\"\n", mfname, prefix); mf = fopen(mfname,"r"); if (mf == NULL) { perror(mfname); fprintf(mailer,"This should have been the contents of\n"); fprintf(mailer,"file '%s' but it's missing!\n",mfname); return; } /* copy the message file into the mailer */ while (fgets(buf,BUFSIZ,mf) != NULL) { fputs(prefix,mailer); fputs(buf,mailer); } fclose(mf); return; } cleanup(from, outsider) char *from; int *outsider; { char *p, *q; printf("cleanup \"%s\"\n", from); while (p = index(from,'~')) *p = 'X'; while (p = index(from,'|')) *p = 'X'; /* elide stuff in parenthesis */ if (p = index(from,'(')) { if ( (q=rindex(from,')')) == NULL) { /* zap the from line; it's invalid */ *p = '\0'; return; } strcpy(p, q+1); } if (p = index(from,'<')) { if ( (q=index(from,'>')) == NULL) { /* zap the from line; it's invalid */ *p = '\0'; return; } *q = '\0'; strcpy(from, p+1); } p = from; /* drop trailing blanks */ p = from + (strlen(from) - 1); while (p && *p && p >= from && isspace(*p)) *p-- = '\0'; /* 'outsider' indicates whether it's mail from a local user or not. 'subscribe' uses that to determine whether to acknowledge the existence of the list. (local users may subscribe to anything with a .info file; others may only subscribe if there is a .pub file) */ if (index(from, '!') != NULL) /* uucp sites are foreign */ { *outsider = 1; return; } if ((p = index(from, '@')) == NULL) /* on-machine user */ { *outsider = 0; return; } if (strcasecmp(p+1, DOMAIN) == 0) { *outsider = 0; return; } if ((p = index(p, '.')) == NULL) /* in-domain user */ { *outsider = 0; return; } if (strncasecmp(p+1, DOMAIN,8) == 0) { *outsider = 0; return; } *outsider = 1; return; } !Funky!Stuff! echo x - export-listserv/str.c cat > export-listserv/str.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/str.c,v 1.3 92/02/24 12:55:07 andy Exp Locker: andy $"; strcasecmp(a,b) char *a, *b; { char *p; p = a; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } p = b; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } return(strcmp(a,b)); } strncasecmp(a,b,n) char *a, *b; int n; { char *p; p = a; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } p = b; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } return(strncmp(a,b,n)); } char *strstr(a,b) char *a, *b; { char *p; p = a; while(p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } p = b; while (p && *p) { if (isupper(*p)) *p = tolower(*p); p++; } p = a; while (p && *p) { if (!strncmp(p, b, strlen(b))) return p; p++; } p = 0; return p; } !Funky!Stuff! echo x - export-listserv/subscribe.c cat > export-listserv/subscribe.c << '!Funky!Stuff!' #include "listserv.h" static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/subscribe.c,v 1.4 92/09/14 11:04:20 andy Exp Locker: andy $"; extern FILE *msg; extern FILE *mailer; subscription(from,command,add,outsider) char *from,*command; int add,outsider; { FILE *list; FILE *listtmp; char grp[256]; char adr[256]; char tmp[256]; char buf[BUFSIZ]; int del = 0; int i, l; int aliasok; printf("subscription %s %s %d\n", from, command, add); i = sscanf(command,"%s%s%s", tmp, adr, grp); if (i < 2 || i > 3) sendhelp(from, command); if (i == 2) { strcpy(grp, adr); strcpy(adr, from); } cleanup(grp,&i); cleanup(adr,&outsider); if (strcasecmp(grp, adr) == 0) { callmailer("", from, ""); fprintf(mailer,"Subscription address loop: %s\n", adr); fflush(mailer); pclose(mailer); return(-1); } if (outsider) sprintf(tmp,"%s/%s.pub", SERVDIR, grp); else sprintf(tmp,"%s/%s.info", SERVDIR, grp); if (access(tmp,R_OK) != 0) { callmailer("", from, ""); fprintf(mailer,"The mailing list \"%s\" could not be found.\n", grp); fprintf(mailer,"You may use the INDEX command to get a listing\n"); fprintf(mailer,"of available mailing lists.\n"); fflush(mailer); pclose(mailer); return(-1); } strcpy(tmp, SERVDIR); strcat(tmp, "/"); strcat(tmp, grp); if (add) { l = open(tmp, AFLAGS, AMODE); if (l == -1) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[1] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } else { list = fdopen(l, "a"); if (list == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[1] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } } flock(l, LOCK_EX); fprintf(list, "%s\n", adr); fflush(list); flock(l, LOCK_UN); close(l); } else { del = 0; list = fopen(tmp, "r"); if (list == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[2] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } flock(fileno(list), LOCK_EX); strcpy(tmp, SERVDIR); strcat(tmp, "/"); strcat(tmp, grp); strcat(tmp, ".tmp"); listtmp = fopen(tmp, "w"); if (listtmp == NULL) { callmailer(LISTSERVMANAGER, from, ""); fprintf(mailer,"Error[3] processing request. Please try later.\n"); fprintf(mailer,">%s\n", command); fflush(mailer); pclose(mailer); return(-1); } /* copy the list, omitting the one address */ while (fgets(buf, sizeof(buf), list)) { buf[strlen(buf)-1] = '\0'; if (strcasecmp(buf, adr)) { fputs(buf, listtmp); fputs("\n", listtmp); } else del++; } fflush(listtmp); fclose(listtmp); /* replace the old list with the shortened one */ strcpy(buf, SERVDIR); strcat(buf, "/"); strcat(buf, grp); unlink(buf); /* delete original file */ rename(tmp, buf); /* put updated one in place */ flock(fileno(list), LOCK_UN); /* release lock */ } #ifndef DEBUG if (strcmp(from, adr)) { callmailer(adr, from, command); fprintf(mailer,"Per request by %s\n", from); } else { callmailer("", from, command); fprintf(mailer,"Per your request\n"); } #else callmailer("", from, command); fprintf(mailer,"Per your request\n"); #endif fprintf(mailer,"\t\"%s\"\n", command); /* Mail subscription confirmation and info/intro files.*/ if (add) { fprintf(mailer,"'%s' was ADDED to the '%s' mailing list.\n", adr, grp); fflush(mailer); pclose(mailer); sprintf(tmp, "%s/%s.intro", SERVDIR,grp); if (access(tmp,R_OK) == 0) { #ifndef DEBUG callmailer("", adr, command); #else callmailer("", from, command); #endif mailcat(tmp, ""); sprintf(tmp, "%s/%s.faq", SERVDIR, grp); if(access(tmp,R_OK) == 0) mailcat(tmp,"\n\n"); fflush(mailer); pclose(mailer); } } else if (del) { fprintf(mailer, "'%s' was DELETED from the '%s' mailing list.\n", adr, grp); fprintf(mailer, "\nAlthough you have been deleted from the list,"); fprintf(mailer, " some mail sent previous to your deletion may be\n"); fprintf(mailer, "queued in the system. Please don't panic if you"); fprintf(mailer, " receive a few last pieces of mail.\n"); fflush(mailer); pclose(mailer); } else { fprintf(mailer, "'%s' was NOT FOUND on the '%s' mailing list.\n", adr, grp); fflush(mailer); pclose(mailer); } } !Funky!Stuff! .