home *** CD-ROM | disk | FTP | other *** search
-
- /* For a long time, certain pieces of potentially dangerous software have
- * been in the possession of only a small number of people. Partially in
- * response to some pieces of such software being made available (either
- * completely publicly, or to a limited but wide audience, or for sale),
- * and mostly in response to what has been the all but destruction of
- * everything the net once was (and my resulting disgustion with it), I
- * have released this. I intend to write and release any dangerous or
- * potentially destructive software that I can conceive ideas for. The name
- * of this program, "purify", reflects my feelings. Usenet has degraded to
- * the point where it would be better off gone.
- */
-
- /*
- to compile:
-
- all reasonable unixes:
- gcc -O2 -o purify purify.c
-
- Solaris:
- gcc -O2 -o purify purify.c -lsocket -lnsl
-
- SunOS:
- gcc -O2 -o purify purify.c -Dsunos
-
- (you can use cc if you don't have gcc, except on SunOS, since their cc sucks)
- */
-
- /*
- purify version 0.1, by Electric Eel. An Armageddon Software product.
- This program is Copyright 1997 Electric Eel and Armageddon Software. It is
- released under the terms of the GPL (http://www.fsf.org/copyleft/gpl.html)
-
- This program will cancel all the articles in specified usenet newsgroups,
- by posting a control article for each article that we cancel. There are a
- great many reasons why you may find it useful to cancel all the articles in
- a newsgroup. For example, if you consider the group or what is posted to it
- offensive, if it is being used to libel or otherwise attack you, or if you
- want to stop what the discussion in the group might cause (for example the
- creation of a new newsgroup that you are opposed to). Even moderated groups
- can be cancelled.
-
- This program is not intended for use. If you do use it, against my explicit
- advice, I can not be held responsible for what you do with it. Use of this
- program could get you hurt, arrested, expelled, or possibly deported.
-
- Usage:
-
- purify [options] newsgroup [newsgroups...]
-
- -n nntp server to use (else we use $NNTPSERVER, /etc/nntpserver, "news")
- -h use header (-h Organization to take from original, -h 'Organization:
- whatever' to specify)
- -i post with ihave (will fail if we do not have permission)
- -v verbose (more v's, more verbose, 1, 2, or 3)
- -o only post cancel to group we saw, not full Newsgroups list
- -m messageid type - 0=none, 1=prepend random, or string to prepend
- ("cancel." default)
- -b string to use for body
- -c continuous mode, loop forever canceling new articles
- -A Approved header to use if a group is moderated (-h overrides. default is
- original Approved line.)
- -N don't add "X-No-Archive: Yes" header
-
- we will take wildcards for newsgroup name, sent to 'list active ...'.
- If an argument (newsgroup name or header) contains spaces or wildcards,
- remember to quote it to protect it from the shell.
-
- Usage examples:
-
- cancel all articles in alt.2600, and then quit
- purify alt.2600
-
- cancel all articles in the news.* hierarchy, and then quit. You may
- consider this to be not only a usage example, but a suggested usage:
- purify "news.*"
-
- cancel all articles in the groups news.admin.net-abuse.*, and continue to
- cancel any new ones that show up. the nohup makes purify keep running
- after you log off, the >&/dev/null causes the output to be suppressed, and
- the & puts the process in the background.
- nohup purify -c "news.admin.net-abuse.*" >&/dev/null &
-
- cancel all articles in groups containing censorship or conspiracy in their
- names, using the nntp server "news.foo.edu", making the message-id of the
- cancel a random number plus the original message-id, give verbose output
- (enough to keep track of the progress), take the Approved line from the
- original article, and add our own header "Organization: Men In Black
- Evidence Removal Service". You may also consider this a suggested usage (I
- think it would be rather funny).
- purify -n news.foo.edu -m1 -v -h Approved -h "Organization: Men In Black \
- Evidence Removal Service" "*censorship* "*conspiracy*"
-
- Notes:
- It may be a good idea to do -h Organization or -h "Organization: <whatever>"
- to prevent your nntp server from adding the default Organization line. You
- may also wish to do -h Date to obfuscate exactly when the articles were
- cancelled.
-
- If an article is posted to a newsgroup that is not moderated, but is
- crossposted to a moderated newsgroup, and we are cancelling in the
- unmoderated group, and the -o or -h Approved options are not given, our
- cancel will either be rejected or forwarded to the moderator. This should
- be a pretty rare case, but it's worth being aware of. We make no provision
- for resubmitting the article with an Approved header. If you're worried
- about this, use -h Approved (which carries a slight penalty in data
- received), or -o (which will cause less to be received, but may cause the
- article to not be cancelled on systems which carry other groups it was
- posted to but not the one we posted the cancel to).
-
- Some newsgroups, notably news.admin.net-abuse.*, have a bot that re-posts
- all cancelled articles. Do not let this discourage you, however. This is
- intended to be a remedy for individual cancellations, and not
- mass-cancellation such as this program is designed for. The bot will cause
- far more problems than it solves. Unless it crashes or is disabled by its
- owner, it will end up posting a lot of articles in a short time - as many
- as you cancelled, which could be a whole week's traffic. Everyone reading
- the group will see each one as a new article. If you are attempting to
- prevent use of the newsgroup, the resulting chaos is a far more desirable
- situation.
-
- If the server does not support "list active <newsgroup>", we will not be
- able to determine whether a group is moderated or deal with wildcards. We
- could do it by downloading the entire list, which could be well over a meg.
- Since it's pointless over a slow connection, and most nntp servers support
- it anyway, I didn't worry about it. In this case, wildcards will cause an
- error, and moderated will be assumed (meaning we will copy any Approved
- header in the article).
-
- The speed of this program is mainly dependent on the speed of your nntp
- server, and to a lesser extent the speed of the connection to the nntp
- server. After the headers are retrieved, you can count on somewhere between
- a few cancels per second to a few seconds per cancel.
-
- A Message-ID over 254 characters will not be accepted by the nntp server.
- By default, "cancel." is prepended to the article's Message-ID to create
- the Message-ID for the cancel. If the cancel's Message-ID would be too
- long, which would take a deliberate act on the part of the original poster,
- it will be trimmed down. (People reading this might realize that if they
- post their articles with Message-IDs greater than 247 characters, any
- cancellation software that hasn't been written with this in mind will not
- work without modification).
-
- Also on the subject of deliberate acts to avoid cancellation, people might
- post a dummy article with a Message-ID intended to collide with the
- cancel's. (perhaps to alt.test, or a cancel of a nonexistant article, or a
- series of posts each with one more "cancel." prepended to the Message-ID).
- The -m options are intended to remedy this, if it becomes a problem. People
- worrying about having their articles cancelled by less versatile software
- should also take note of this.
-
- The -o option will not prevent articles from being cancelled in groups
- other than that the cancel was posted to. It will only cause them to not be
- cancelled on news servers that do not get the group the cancel was posted
- to, but do get other groups the article was posted to.
-
- If you use the -A option, the "X-No-Archive: Yes" header will not be
- included. This will cause your cancels will be archived in dejanews and
- altavista. You probably don't want an easily searchable long-lived record
- of your cancels, so there's really no reason to ever use this option.
-
- TODO:
- The next version will handle a local news spool. This probably isn't useful
- to most people (I wouldn't even have somewhere to test it), but I should
- include it for completeness. Other than that, I can't see that there is
- much else to add in the way of indiscriminantly cancelling a newsgroup.
- Any new features I can think of would be outside the goal of this program.
-
- Handling multiple nntp servers simultaneously, or simultaneous sending and
- receiving to/from the sever (with two connections) might be useful, but I
- don't see that it would at all be worth the trouble.
-
- I'm certain there are bugs, this was quickly written and not extensively
- tested. Some stuff should be cleaned up.
-
- The intent of the verbosity levels is: 0 - only important information is
- displayed, fatal or serious errors are sent to stderr, 1 - enough
- information to keep track of the progress is displayed, 2 - all data sent
- and received is displayed, 3 - internal information such as status of data
- structures is displayed
-
- With -v, '<' is displayed when each post is sent, and '>' is displayed
- when the server returns the result, so each <> is one cancel posted.
- */
- /*e576894661eefab2c5345391e94bf05d*/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdarg.h>
- #include <time.h>
-
- #ifdef sunos /* sucks */
- #define memmove(a,b,c) bcopy(b,a,c)
- #endif /* sunos */
-
- #define VERSION "0.1"
-
- #define Die(x) { fputs(x, stderr); exit(1); }
- #define Fatal(x) { perror(x); exit(1); }
- #define Please2(x, y) if((x)<0) { perror(y); exit(1); }
- #define Return(x) { x; return; }
-
- struct newsgroups {
- char *name; /* newsgroup name */
- char flags; /* y, n, m */
- int low; /* low article number */
- struct newsgroups *next;
- };
-
- struct article {
- int number;
- char *headers;
- struct article *next;
- };
-
- int only_specified_newsgroup=0;
- char *custom_headers[32]={NULL};
- char *body=NULL;
- int verbose=0;
- int use_ihave=0;
- char *message_id="cancel.";
- char *Approved=NULL;
-
- /* appends a null-terminated list of strings to s */
- char *strcats(char *s, ...)
- {
- va_list args;
- char *p;
-
- va_start(args, s);
- while((p=va_arg(args, char *))) strcat(s, p);
- return s;
- }
-
- /* makes an rfc822 compliant date. stolen from mutt. */
- char *make_date(void)
- {
- static char s[80];
- time_t t = time (NULL);
- struct tm *l = gmtime(&t);
- int yday = l->tm_yday;
- int tz = l->tm_hour * 60 + l->tm_min;
- const char *Weekdays[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- const char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec", "ERR" };
-
- l = localtime(&t);
- tz = l->tm_hour * 60 + l->tm_min - tz;
- yday = l->tm_yday - yday;
-
- if (yday != 0) tz += yday * 24 * 60; /* GMT is next or previous day! */
-
- sprintf (s, "%s, %d %s %d %02d:%02d:%02d %+03d%02d",
- Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon], l->tm_year+1900,
- l->tm_hour, l->tm_min, l->tm_sec, tz/60, abs(tz) % 60);
- return (s);
- }
-
-
- /* connects to host:port, returns a socket descriptor */
- int tcp_connect(char *host, unsigned short port)
- {
- int sd;
- struct sockaddr_in sa;
-
- if( (sa.sin_addr.s_addr=inet_addr(host)) == -1)
- {
- struct hostent *he;
-
- he = gethostbyname(host);
- if(he==NULL) Fatal(host);
- bcopy(he->h_addr, &sa.sin_addr, he->h_length);
- }
-
- sd = socket(AF_INET, SOCK_STREAM, 0);
- if(sd<0) Fatal("socket");
-
- sa.sin_family=AF_INET;
- sa.sin_port=htons(port);
-
- if(connect(sd, (struct sockaddr *)&sa, sizeof(sa))<0) Fatal("connect");
- fcntl(sd, F_SETFL, O_NONBLOCK);
-
- return sd;
- }
-
- /* read a newline-terminated line from sd.. have to buffer the data,
- * otherwise we'd have to do a recv() for every byte received.
- * some day i'll clean this mess up, but it works fine for our puposes.
- */
- void readln(int sd, char *line)
- {
- static char buf[16384];
- static char *head=buf, *tail=buf;
- int x=0;
- char *line_start=line;
-
- again:
-
- /* 4k free && <1k left in buffer or got full 4k on last read */
- while(tail-buf<(16384-4096) && (tail-head<1024 || x==4096))
- {
- int x;
- int blocked=0;
-
- if(verbose>=3)
- printf("t:%d h:%d t-h:%d\n", tail-buf, head-buf, tail-head);
- if(tail<head) Die("can't happen, tail<head\n");
- if(tail==head) /* we have no data, so we'll have to block */
- {
- blocked=1;
- fcntl(sd, F_SETFL, 0);
- }
-
- x=recv(sd, tail, 4096, 0);
- if(x==-1)
- {
- if(errno==EWOULDBLOCK) break;
- else Fatal("recv");
- }
- if (blocked) fcntl(sd, F_SETFL, O_NONBLOCK); /* back to non-blocking */
-
- tail+=x;
- if(x<4096) break;
- }
-
- while((*line++ = *head++) != '\n') /* copy it */
- if(head>=tail) goto again; /* more data needed */
-
- *line=0;
-
- if(head==tail) head=tail=buf; /* no data, reset head+tail */
-
- if(head-buf>=4096) /* first 4k free, shift data */
- {
- memmove(buf, head, (tail-head));
- tail=buf+(tail-head);
- head=buf;
- }
-
- if(verbose>=2) fputs(line_start, stdout);
- }
-
- /* send null-terminated string in buf to socket sd */
- void Send(int sd, char *buf)
- {
- if(verbose>=2) printf("Send(): %s\n", buf);
- Please2(send(sd, buf, strlen(buf), 0), "send");
- }
-
- /* send null-terminated string plus \r\n to socket */
- void Sendln(int sd, char *buf)
- {
- int x;
- char *s=malloc(x=(strlen(buf)+3));
-
- if(verbose>=2) printf("Sendln(): %s\n", buf);
- strcpy(s, buf);
- strcat(s, "\r\n");
- Please2(send(sd, s, x-1, 0), "send");
- free(s);
- }
-
- /* like fprintf, to a socket */
- void Sendf(int sd, char *format, ...)
- {
- va_list args;
- char buf[4096];
-
- va_start(args, format);
- vsprintf(buf, format, args);
- if(verbose>=2) printf("Sendln(): %s", buf);
- Please2(send(sd, buf, strlen(buf), 0), "send");
- }
-
- /* reads the first line of a file and returns that */
- char *read_line_from_file(char *filename)
- {
- static char buf[1024];
- FILE *f;
-
- f=fopen(filename, "r");
- if(f==NULL) return NULL;
- fgets(buf, sizeof(buf), f);
- if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;
- fclose(f);
- return buf;
- }
-
- /* add a group to a newsgroups list, buf is list active output */
- void addgroup(struct newsgroups **first, char *buf)
- {
- struct newsgroups *this, *last=NULL;
- char s[4096];
- int low;
- char flags;
-
- sscanf(buf, "%s %*d %d %c", s, &low, &flags);
-
- /* we could post the cancel to another group, or with ihave. but this is a
- * rare case anyway. */
- if(flags=='n')
- Return(printf("Can't post to %s, ignoring\n", s));
-
- /* make sure its not a dupe */
- for(this=*first; this; this=this->next)
- {
- last=this;
- if(!strcmp(this->name, s))
- Return(if(verbose>=3) printf("addgroup(): duplicate: %s\n", s));
- }
-
- /* add it */
- this=malloc(sizeof(struct newsgroups));
- this->next=NULL;
- this->low=low;
- this->name=strdup(s);
- this->flags=flags;
-
- if(verbose>=3)
- printf("added %s %d %c\n", this->name, this->low, this->flags);
-
- /* deal with a new list */
- if(last) last->next=this; /* (else) */
- if(*first==NULL) *first=this;
-
- }
-
- /* does list active groups, feeds the output to addgroup */
- struct newsgroups *get_group_info(int sd, char *groups)
- {
- char buf[1024];
- static int no_list=0;
- static struct newsgroups *firstgroup;
-
- redo:
- if(no_list)
- {
- if(strpbrk(groups, "?*"))
- {
- fprintf(stderr, "error: can't do list, wildcards in %s\n", groups);
- return firstgroup;
- }
-
- sprintf(buf, "%s 0 1 m", groups);
- addgroup(&firstgroup, buf);
- return firstgroup;
- }
-
- Sendf(sd, "LIST ACTIVE %s\r\n", groups);
-
- readln(sd, buf); /* 215 Newsgroups in form "group high low flags". */
- if(strncmp(buf, "215", 3))
- {
- printf("can't do list active <group>, server said %s", buf);
- no_list=1;
- goto redo;
- }
-
- for(;;)
- {
- readln(sd, buf);
- if(!strcmp(buf, ".\r\n")) break;
- addgroup(&firstgroup, buf);
-
- }
- return firstgroup;
- }
-
- /* does xhdr header low-high, stores data in article list. Must be called
- * with Message-ID first (which is a double special-case, does both
- * allocation and Control headers)
- */
- void do_xhdr(int sd, struct article **first, char *header, int low, int high)
- {
- char buf[1024];
- struct article *this, *previous;
- int firstpass;
-
- if(verbose)
- printf("do_xhdr: %d-%d, %s\n", low, high, header);
-
- Sendf(sd, "XHDR %s %d-%d\r\n", header, low, high);
- /* expect: 221 whatever fields follow */
-
- readln(sd, buf);
- if(strncmp(buf, "221", 3))
- {
- fprintf(stderr, "error: Sent XHDR %s, got %s", header, buf);
- exit(1);
- }
-
- firstpass=!strcmp("Message-ID", header); /* Message-ID *must* be first*/
-
- for(previous=this=*first;;)
- {
- char buf2[4096], *p;
-
- readln(sd, buf);
- if(!strcmp(buf, ".\r\n")) break;
-
- if(firstpass) /* allocation crap */
- {
- if(!*first) this=*first=malloc(sizeof(struct article));
- else {
- this->next=malloc(sizeof(struct article));
- this=this->next;
- }
- this->next=NULL;
- }
-
- for(p=buf;*p && *p!=' ';p++);
- *p++=0; /* so it's number\0<header info>\r\n, *p=='<'*/
- if(firstpass) this->number=atoi(buf);
- else if(this->number!=atoi(buf)) /* message numbers dont match */
- {
- printf("Article number mismatch %d, %s\n", this->number, buf);
- while(this && this->number<atoi(buf)) /* saw it b4, rm it now */
- {
- if(this==*first) /* on first one */
- *first=(*first)->next; /* so shift it to the 2nd */
- else
- previous->next=this->next; /* skip it in the list */
-
- free(this->headers);
- free(this);
- this=previous->next; /* new current one */
- }
- if(!this) return;
- if(this->number>atoi(buf)) continue; /* never seen, new now */
- }
-
- if(firstpass) /* special Message-ID stuff */
- {
-
- sprintf(buf2, "Control: cancel %sSubject: cmsg cancel %s", p, p);
- if( !(*message_id=='0' && !message_id[1]) ) /* setting the m-id*/
- {
- char *prepend=message_id;
- if(message_id[0]=='1' && !message_id[1]) /* prepend random */
- {
- static char blah[10];
- sprintf(blah, "%d", rand());
- prepend=blah;
- }
- p++;
- while(strlen(p)+strlen(prepend) > 253) /*254+'<'*/
- if(*p++=='@') *p='@'; /* messageid needs an @ */
- if(*p=='@' && prepend[strlen(prepend)-1]=='.')
- { /* cant have ".@" in message-id */
- *p=p[1];
- p[1]='@';
- }
-
- strcats(buf2, "Message-ID: <", prepend, p, NULL);
- }
-
- this->headers=strdup(buf2);
- } else if(strcmp(p, "(none)\r\n")) { /*(none)==header not there*/
- sprintf(buf2, "%s%s: %s", this->headers, header, p);
- free(this->headers);
- this->headers=strdup(buf2);
- }
- previous=this;
- if(!firstpass) this=this->next;
- }
-
- }
-
- /* extract a header from an article, used by post_article. hrmph. */
- char *extract_header(char *buf, char *header)
- {
- static char *p;
- static char r[1024];
- char *q=malloc(strlen(header)+5); /*\r\n: \0*/
-
- *q=0;
- strcats(q, "\r\n", header, ": ", NULL);
- p=strstr(buf, q)+4+strlen(header);
- free(q);
-
- strncpy(r, p, strchr(p, '\n')-p);
- *strchr(r, '\r')=0;
- return r;
- }
-
- /* post an article to nntp server on sd. a and b are first and second half
- of the article. */
- void post_article(int sd, char *a, char *b)
- {
- char buf[1024];
- static int failed; /* failed postings. 10 consecutive and we give up. */
- #ifndef TEST
- if(use_ihave)
- Sendf(sd, "IHAVE %s\r\n", extract_header(a, "Message-ID")); /*hrm*/
- else
- Sendln(sd, "POST");
- readln(sd, buf); /* expected: 340 Ok */
- if(*buf!='3')
- {
- printf("Can't post! Server said %s", buf);
- exit(1);
- }
-
- Send(sd, a);
- if(b) Send(sd, b);
- Send(sd, "\r\n.\r\n");
-
- if(verbose==1) {fputs("<", stdout); fflush(stdout);}
- readln(sd, buf); /* expected 240 Article posted */
- if(verbose==1) {fputs(">", stdout); fflush(stdout);}
- if(*buf!='2')
- {
- printf("posting failed, #%d, server said %s\n", ++failed, buf);
- if(failed>=10) Die("Too many errors posting\n");
- } else if(failed>0) failed=0;
- #else
- if(use_ihave)
- printf("IHAVE %s\r\n", extract_header(a, "Message-ID")); /*hrm*/
- else
- printf("POST\n");
- fputs(a, stdout);
- fputs(b, stdout);
- fputs("\r\n.\r\n", stdout);
- #endif
- }
-
- /* cancel all the articles in a newsgroup, update the low article count to
- * be the highest article number we cancelled
- * calls do_xhdr and post_article
- */
- void cancel_group(int sd, struct newsgroups *group)
- {
- char buf[1024];
- int count, high;
- char **p;
- char rest[4096]=""; /* headers for all, and body */
- int did_approved=0; /* for mod'd groups, Approved was in -h */
- int x=0;
-
- struct article *articles=NULL, *this;
-
- if(verbose)
- printf("cancel_group: %s\n", group->name);
-
- /* send group <groupname> */
- Sendf(sd, "GROUP %s\r\n", group->name);
-
- /* expected: 211 count low high groupname, or 411 for no such group */
- readln(sd, buf);
- if(!strncmp(buf, "411", 3)) /* cant happen, i suppose */
- Return(printf("No such group %s, server said %s", group->name, buf));
-
- if(strncmp(buf, "211", 3))
- {
- fprintf(stderr, "error: Sent GROUP, got %s", buf);
- exit(1);
- }
- /* get all the header information */
- sscanf(buf, "211 %d %*d %d", &count, &high);
- if(!count || group->low>high)
- Return(if(verbose) printf("%s is empty\n", group->name));
-
- do_xhdr(sd, &articles, "Message-ID", group->low, high);
-
- if(!articles)
- Return(if(verbose) printf("%s was empty\n", group->name));
-
- do_xhdr(sd, &articles, "From", group->low, high);
- if(only_specified_newsgroup)
- strcats(rest, "Newsgroups: ", group->name, "\r\n", NULL);
- else
- do_xhdr(sd, &articles, "Newsgroups", group->low, high);
-
- if(use_ihave) /* then we need the Date and Path headers */
- {
- int path_set=0, date_set=0;
- for(p=custom_headers; *p; p++) /*check if they were user-specified*/
- {
- if(!strncmp(*p, "Path", 4)) path_set=1; else
- if(!strncmp(*p, "Date", 4)) date_set=1;
- if(path_set && date_set) break;
- }
- if(!date_set);
- strcats(rest, "Date: ", make_date(), "\r\n", NULL);
- if(!path_set)
- strcats(rest, "Path: nntp!usenet\r\n", NULL);
- }
-
-
- /* add -h headers */
- for(p=custom_headers; *p; p++)
- {
- if(group->flags=='m' && !strncmp(*p, "Approved", 8)) did_approved=1;
-
- if(!strchr(*p, ':'))
- do_xhdr(sd, &articles, *p, group->low, high);
- else
- strcats(rest, *p, "\r\n", NULL);
- }
-
- if(group->flags=='m' && !did_approved) /* group is moderated */
- {
- if(!Approved) /* user specified Approved header for mod'd groups*/
- do_xhdr(sd, &articles, "Approved", group->low, high);
- else
- strcats(rest, "Approved: ", Approved, "\r\n");
- }
-
- if(body) /* user specified body */
- strcats(rest, "\r\n", body, "\r\n", NULL);
- else
- strcat(rest, "\r\ncancel.\r\n");
-
- /* for the next time around, skip the articles we did */
- group->low=high+1;
-
- for(this=articles; this;x++) /* post articles and free memory */
- {
- struct article *p=this->next;
- post_article(sd, this->headers, rest);
- free(this->headers);
- free(this);
- this=p;
- }
- if(verbose) printf("\n%d cancels sent\n", x);
-
- }
-
- void usage(char *myname)
- {
- printf("purify version "VERSION", by Electric Eel.\nUsage:\n\
- %s [options] newsgroup [newsgroups...]\n\
- \n\
- -n nntp server to use (else we use $NNTPSERVER, /etc/nntpserver, \"news\")\n\
- -h use header (-h Organization to take from original, -h 'Organization:\n\
- whatever' to specify)\n\
- -i post with ihave (will fail if we do not have permission)\n\
- -v verbose (more v's, more verbose, 1, 2, or 3)\n\
- -o only post cancel to group we saw, not full Newsgroups list\n\
- -m messageid type - 0=none, 1=prepend random, or string to prepend \n\
- (\"cancel.\" default)\n\
- -b string to use for body\n\
- -c continuous mode, loop forever canceling new articles\n\
- -A Approved header to use if a group is moderated (-h overrides. default is \n\
- original Approved line.)\n\
- -N don't add \"X-No-Archive: Yes\" header\n\
- \n\
- we will take wildcards for newsgroup name, sent to 'list active ...'.\n\
- Read the source for more documentation.\n", myname);
- exit(1);
- }
-
- int main(int argc, char **argv)
- {
- char s[1024]; /* general buffer */
- int sd; /* socket for nntp server */
- int c; /* for getopt */
- int chc=0; /* custom headers count, temp for arg processing */
- int continuous=0; /* run forever */
- int x_no_archive=1; /* include X-No-Archive: Yes header */
- struct newsgroups *first=NULL, *this;
-
- char *nntp_server=NULL;
-
- if(argc<=1) usage(argv[0]);
-
- while((c=getopt(argc, argv, "cvioNA:n:h:b:m:")) != -1) switch(c)
- {
- case 'c': continuous=1; break;
- case 'v': verbose++; break;
- case 'i': use_ihave=1; break;
- case 'o': only_specified_newsgroup=1; break;
- case 'N': x_no_archive=0; break;
-
- case 'A': Approved=optarg; break;
- case 'n': nntp_server=optarg; break;
- case 'h': custom_headers[chc++]=optarg; break;
- case 'b': body=optarg; break;
- case 'm': if(!strchr(optarg, '@') && *optarg && strlen(optarg)<200)
- message_id=optarg;
- else puts("ignoring invalid messageid prefix");
- break;
- }
- if(x_no_archive) custom_headers[chc++]="X-No-Archive: Yes";
- custom_headers[chc]=NULL;
-
- /* check for bad options */
- if(message_id[0]=='0' && message_id[1]==0 && use_ihave)
- {
- printf("Server will require a Message-ID with ihave, using default\n");
- message_id="cancel.";
- }
-
- srand((unsigned int)time(NULL));
-
- /* find nntp server, connect to it */
- if(!nntp_server) nntp_server=getenv("NNTPSERVER");
- if(!nntp_server) nntp_server=read_line_from_file("/etc/nntpserver");
- if(!nntp_server) nntp_server="news";
- sd=tcp_connect(nntp_server, 119);
-
- readln(sd, s);
- fputs(s, stdout);
- if(*s!='2') Die("No permission to use nntp server\n");
-
- /* so now we're connected to the nntp server, it's waiting for us */
-
- /* send out list active groupnanme for all the args, save the info */
-
- while(optind<argc)
- first=get_group_info(sd, argv[optind++]);
-
- if(!first) Die("No groups to do!\n");
-
- do {
- for(this=first;this;this=this->next)
- cancel_group(sd, this);
- if(continuous) sleep(5);
- } while(continuous);
-
- return 0;
- }
- /************************************************************************/
-
-
-
-