skrenta@blekko.commodore.com (Rich Skrenta) (04/18/91)
# This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # This archive contains: # mail.c main.c misc.c nntp.h # nntp_open.c prompt.c tass.h time.c # echo x - mail.c cat >mail.c <<'@EOF' #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #define TRUE 1 #define FALSE 0 char *mailbox_name = NULL; off_t mailbox_size; /* * Record size of mailbox so we can detect if new mail has arrived */ mail_setup() { struct stat buf; extern char *getenv(); if (mailbox_name == NULL) mailbox_name = getenv("MAIL"); if (mailbox_name == NULL) mailbox_size = 0; else { if (stat(mailbox_name, &buf) >= 0) mailbox_size = buf.st_size; else mailbox_size = 0; } } /* * Return TRUE if new mail has arrived */ mail_check() { struct stat buf; if (mailbox_name != NULL && stat(mailbox_name, &buf) >= 0 && mailbox_size < buf.st_size) return TRUE; return FALSE; } @EOF chmod 640 mail.c echo x - main.c cat >main.c <<'@EOF' /* * Tass, a visual Usenet news reader * (c) Copyright 1990 by Rich Skrenta * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. */ #include <stdio.h> #include <signal.h> #include <termio.h> /* for struct winsize */ #ifdef SCO_UNIX #include <sys/types.h> #include <sys/stream.h> #include <sys/ptem.h> #endif #include "tass.h" int LINES, COLS; int max_active; struct group_ent *active; /* active file */ int group_hash[TABLE_SIZE]; /* group name --> active[] */ int *my_group; /* .newsrc --> active[] */ int *unread; /* highest art read in group */ int num_active; /* one past top of active */ int local_top; /* one past top of my_group */ int update = FALSE; /* update index files only mode */ struct header *arts; long *base; int max_art; int top = 0; int top_base; int tass_uid; int tass_gid; int real_uid; int real_gid; int local_index; /* do private indexing? */ char *cvers = "Tass 3.2 (c) Copyright 1991 by Rich Skrenta. All rights reserved"; #ifdef SIGTSTP void main_susp(i) int i; { Raw(FALSE); putchar('\n'); signal(SIGTSTP, SIG_DFL); kill(0, SIGTSTP); signal(SIGTSTP, main_susp); mail_setup(); Raw(TRUE); } #endif main(argc, argv) int argc; char **argv; { extern int optind, opterr; extern char *optarg; int errflag = 0; int i; int c; extern char group_search_string[]; extern char author_search_string[]; extern char subject_search_string[]; extern char *is_remote(); group_search_string[0] = '\0'; author_search_string[0] = '\0'; subject_search_string[0] = '\0'; hash_init(); for (i = 0; i < TABLE_SIZE; i++) group_hash[i] = -1; signal(SIGPIPE, SIG_IGN); #ifdef SIGTSTP signal(SIGTSTP, main_susp); #endif tass_uid = geteuid(); tass_gid = getegid(); real_uid = getuid(); real_gid = getgid(); init_selfinfo(); /* set up char *'s: homedir, newsrc, etc. */ init_alloc(); /* allocate initial array sizes */ if (tass_uid == real_uid) { /* run out of someone's account */ local_index = TRUE; /* index in their home directory */ mkdir(indexdir, 0755); } else /* we're setuid, index in /usr/spool/news */ local_index = FALSE; while ((c = getopt(argc, argv, "f:u")) != -1) { switch(c) { case 'f': strcpy(newsrc, optarg); break; case 'u': update = TRUE; break; case '?': default: errflag++; } } if (errflag) { fprintf(stderr, "usage: tass [options] [newsgroups]\n"); fprintf(stderr, " -f file use file instead of $HOME/.newsrc\n"); fprintf(stderr, " -u update index files only\n"); exit(1); } if (!update) printf("Tass 3.2%s\n", is_remote()); nntp_startup(); /* connect to server if we're using nntp */ read_active(); /* load the active file into active[] */ if (optind < argc) { while (optind < argc) { if (add_group(argv[optind], TRUE) < 0) fprintf(stderr, "group %s not found in active file\n", argv[optind]); optind++; } } else read_newsrc(TRUE); if (update) { /* index file updater only */ do_update(); exit(0); } if (InitScreen() == FALSE) { fprintf(stderr,"Screen initialization failed\n"); exit(1); } ScreenSize(&LINES, &COLS); Raw(TRUE); #ifdef TIOCGWINSZ { struct winsize win; if (ioctl(0, TIOCGWINSZ, &win) == 0) { if (win.ws_row != 0) LINES = win.ws_row - 1; if (win.ws_col != 0) COLS = win.ws_col; } } #endif mail_setup(); /* record mailbox size for "you have mail" */ selection_index(); tass_done(0); } tass_done(ret) int ret; { nntp_finish(); /* close connection if we're using nntp */ MoveCursor(LINES, 0); printf("\r\n"); Raw(FALSE); exit(ret); } /* * Dynamic table management * These settings are memory conservative: small initial allocations * and a 50% expansion on table overflow. A fast vm system with * much memory might want to start with higher initial allocations * and a 100% expansion on overflow, especially for the arts[] array. */ init_alloc() { max_active = 100; /* initial alloc */ active = (struct group_ent *) my_malloc(sizeof(*active) * max_active); my_group = (int *) my_malloc(sizeof(int) * max_active); unread = (int *) my_malloc(sizeof(int) * max_active); max_art = 300; /* initial alloc */ arts = (struct header *) my_malloc(sizeof(*arts) * max_art); base = (long *) my_malloc(sizeof(long) * max_art); } expand_art() { max_art += max_art / 2; /* increase by 50% */ arts = (struct header *) my_realloc(arts, sizeof(*arts) * max_art); base = (long *) my_realloc(base, sizeof(long) * max_art); } expand_active() { max_active += max_active / 2; /* increase by 50% */ active = (struct group_ent *) my_realloc(active, sizeof(*active) * max_active); my_group = (int *) my_realloc(my_group, sizeof(int) * max_active); unread = (int *) my_realloc(unread, sizeof(int) * max_active); } /* * Load the active file into active[] */ read_active() { FILE *fp; char *p, *q; char buf[200]; long h; int i; extern long hash_groupname(); FILE *open_active_fp(); num_active = 0; fp = open_active_fp(); if (fp == NULL) { fprintf(stderr, "can't get active file\n"); exit(1); } while (fgets(buf, 200, fp) != NULL) { for (p = buf; *p && *p != ' '; p++) ; if (*p != ' ') { fprintf(stderr, "active file corrupt\n"); continue; } *p++ = '\0'; if (num_active >= max_active) expand_active(); h = hash_groupname(buf); if (group_hash[h] == -1) group_hash[h] = num_active; else { /* hash linked list chaining */ for (i = group_hash[h]; active[i].next >= 0; i = active[i].next) { if (strcmp(active[i].name, buf) == 0) goto read_active_continue; /* kill dups */ } if (strcmp(active[i].name, buf) == 0) goto read_active_continue; active[i].next = num_active; } for (q = p; *q && *q != ' '; q++) ; if (*q != ' ') { fprintf(stderr, "active file corrupt\n"); continue; } active[num_active].name = str_save(buf); active[num_active].max = atol(p); active[num_active].min = atol(q); active[num_active].next = -1; /* hash chaining */ active[num_active].flag = NOTGOT; /* not in my_group[] yet */ num_active++; read_active_continue:; } fclose(fp); } /* * Read $HOME/.newsrc into my_group[]. my_group[] ints point to * active[] entries. Sub_only determines whether we just read * subscribed groups or all of them. */ read_newsrc(sub_only) int sub_only; /* TRUE=subscribed groups only, FALSE=all groups */ { FILE *fp; char *p; char buf[8192]; char c; int i; local_top = 0; fp = fopen(newsrc, "r"); if (fp == NULL) { /* attempt to make a .newsrc */ for (i = 0; i < num_active; i++) { if (local_top >= max_active) expand_active(); my_group[local_top] = i; active[i].flag = 0; unread[local_top] = -1; local_top++; } write_newsrc(); return; } while (fgets(buf, 8192, fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; *p++ = '\0'; if (c == '!' && sub_only) continue; /* unsubscribed */ if ((i = add_group(buf, FALSE)) < 0) { fprintf(stderr, "group %s not found in active file\n", buf); continue; } if (c != '!') /* if we're subscribed to it */ active[my_group[i]].flag |= SUBS; unread[i] = parse_unread(p, my_group[i]); } fclose(fp); } /* * Write a new newsrc from my_group[] and active[] * Used to a create a new .newsrc if there isn't one already, or when * the newsrc is reset. */ write_newsrc() { FILE *fp; int i; setuid(real_uid); /* become the user to write in his */ setgid(real_gid); /* home directory */ fp = fopen(newsrc, "w"); if (fp == NULL) goto write_newsrc_done; for (i = 0; i < num_active; i++) fprintf(fp, "%s: \n", active[i].name); fclose(fp); write_newsrc_done: setuid(tass_uid); setgid(tass_gid); } /* * Load the sequencer rang lists and mark arts[] according to the * .newsrc info for a particular group. i.e. rec.arts.comics: 1-94,97 */ read_newsrc_line(group) char *group; { FILE *fp; char buf[8192]; char *p; fp = fopen(newsrc, "r"); if (fp == NULL) return; while (fgets(buf, 8192, fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') p++; *p++ = '\0'; if (strcmp(buf, group) != 0) continue; parse_seq(p); break; } fclose(fp); } /* * For our current group, update the sequencer information in .newsrc */ update_newsrc(group, groupnum) char *group; int groupnum; /* index into active[] for this group */ { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; setuid(real_uid); setgid(real_gid); fp = fopen(newsrc, "r"); newfp = fopen(newnewsrc, "w"); if (newfp == NULL) goto update_done; if (fp != NULL) { while (fgets(buf, 8192, fp) != NULL) { for (p = buf; *p; p++) if (*p == '\n') { *p = '\0'; break; } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp(buf, group) == 0) { fprintf(newfp, "%s%c ", buf, c); gotit = TRUE; print_seq(newfp, groupnum); fprintf(newfp, "\n"); } else fprintf(newfp, "%s%c%s\n", buf, c, p); } fclose(fp); } fclose(newfp); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); update_done: setuid(tass_uid); setgid(tass_gid); } /* * Subscribe/unsubscribe to a group in .newsrc. ch should either be * '!' to unsubscribe or ':' to subscribe. num is the group's index * in active[]. */ subscribe(group, ch, num, out_seq) char *group; char ch; int num; int out_seq; /* output sequencer info? */ { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; if (ch == '!') active[num].flag &= ~SUBS; else active[num].flag |= SUBS; setuid(real_uid); setgid(real_gid); fp = fopen(newsrc, "r"); newfp = fopen(newnewsrc, "w"); if (newfp == NULL) goto subscribe_done; if (fp != NULL) { while (fgets(buf, 8192, fp) != NULL) { for (p = buf; *p; p++) if (*p == '\n') { *p = '\0'; break; } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp(buf, group) == 0) { fprintf(newfp, "%s%c%s\n", buf, ch, p); gotit = TRUE; } else fprintf(newfp, "%s%c%s\n", buf, c, p); } fclose(fp); } if (!gotit) { if (out_seq) { fprintf(newfp, "%s%c ", group, ch); print_seq(newfp, num); fprintf(newfp, "\n"); } else fprintf(newfp, "%s%c\n", group, ch); } fclose(newfp); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); subscribe_done: setuid(tass_uid); setgid(tass_gid); } print_seq(fp, groupnum) FILE *fp; int groupnum; /* index into active[] for this group */ { int i; int flag = FALSE; if (top <= 0) { if (active[groupnum].min > 1) { fprintf(fp, "1-%ld", active[groupnum].min); fflush(fp); } return; } i = 0; if (arts[0].artnum > 1) { for (; i < top && !arts[i].unread; i++) ; if (i > 0) fprintf(fp, "1-%ld", arts[i-1].artnum); else fprintf(fp, "1-%ld", arts[0].artnum - 1); flag = TRUE; } for (; i < top; i++) { if (!arts[i].unread) { if (flag) fprintf(fp, ","); else flag = TRUE; fprintf(fp, "%ld", arts[i].artnum); if (i+1 < top && !arts[i+1].unread) { while (i+1 < top && !arts[i+1].unread) i++; fprintf(fp, "-%ld", arts[i].artnum); } } } if (!flag && active[groupnum].min > 1) fprintf(fp, "1-%ld", active[groupnum].min); fflush(fp); } parse_seq(s) char *s; { long low, high; int i; while (*s) { while (*s && (*s < '0' || *s > '9')) s++; if (*s && *s >= '0' && *s <= '9') { low = atol(s); while (*s && *s >= '0' && *s <= '9') s++; if (*s == '-') { s++; high = atol(s); while (*s && *s >= '0' && *s <= '9') s++; } else high = low; for (i = 0; i < top; i++) if (arts[i].artnum >= low && arts[i].artnum <= high) arts[i].unread = 0; } } } parse_unread(s, groupnum) char *s; int groupnum; /* index for group in active[] */ { long low, high; long last_high; int i; int sum = 0; int gotone = FALSE; int n; /* * Read the first range from the .newsrc sequencer information. If the * top of the first range is higher than what the active file claims is * the bottom, use it as the new bottom instead */ high = 0; if (*s) { while (*s && (*s < '0' || *s > '9')) s++; if (*s && *s >= '0' && *s <= '9') { low = atol(s); while (*s && *s >= '0' && *s <= '9') s++; if (*s == '-') { s++; high = atol(s); while (*s && *s >= '0' && *s <= '9') s++; } else high = low; gotone = TRUE; } } if (high < active[groupnum].min) high = active[groupnum].min; while (*s) { last_high = high; while (*s && (*s < '0' || *s > '9')) s++; if (*s && *s >= '0' && *s <= '9') { low = atol(s); while (*s && *s >= '0' && *s <= '9') s++; if (*s == '-') { s++; high = atol(s); while (*s && *s >= '0' && *s <= '9') s++; } else high = low; if (low > last_high) /* otherwise seq out of order */ sum += (low - last_high) - 1; } } if (gotone) { if (active[groupnum].max > high) sum += active[groupnum].max - high; return sum; } n = (int) (active[groupnum].max - active[groupnum].min); if (n < 2) return 0; return -1; } get_line_unread(group, groupnum) char *group; int groupnum; /* index for group in active[] */ { FILE *fp; char buf[8192]; char *p; int ret = -1; fp = fopen(newsrc, "r"); if (fp == NULL) return -1; while (fgets(buf, 8192, fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') p++; *p++ = '\0'; if (strcmp(buf, group) != 0) continue; ret = parse_unread(p, groupnum); break; } fclose(fp); return ret; } reset_newsrc() { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; int i; setuid(real_uid); setgid(real_gid); fp = fopen(newsrc, "r"); newfp = fopen(newnewsrc, "w"); if (newfp == NULL) goto update_done; if (fp != NULL) { while (fgets(buf, 8192, fp) != NULL) { for (p = buf; *p && *p != '\n'; p++) ; *p = '\0'; p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; fprintf(newfp, "%s%c\n", buf, c); } fclose(fp); } fclose(newfp); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); update_done: setuid(tass_uid); setgid(tass_gid); for (i = 0; i < local_top; i++) unread[i] = -1; } delete_group(group) char *group; { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; FILE *del; setuid(real_uid); setgid(real_gid); fp = fopen(newsrc, "r"); newfp = fopen(newnewsrc, "w"); if (newfp == NULL) goto del_done; del = fopen(delgroups, "a+"); if (del == NULL) goto del_done; if (fp != NULL) { while (fgets(buf, 8192, fp) != NULL) { for (p = buf; *p && *p != '\n'; p++) ; *p = '\0'; p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp(buf, group) == 0) { fprintf(del, "%s%c%s\n", buf, c, p); gotit = TRUE; } else fprintf(newfp, "%s%c%s\n", buf, c, p); } fclose(fp); } fclose(newfp); if (!gotit) fprintf(del, "%s! \n", group); fclose(del); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); del_done: setuid(tass_uid); setgid(tass_gid); } undel_group() { FILE *del; FILE *newfp; FILE *fp; char buf[2][8192]; char *p; int which = 0; long h; extern int cur_groupnum; int i, j; char c; setuid(real_uid); setgid(real_gid); del = fopen(delgroups, "r"); if (del == NULL) { setuid(tass_uid); setgid(tass_gid); return FALSE; } unlink(delgroups); newfp = fopen(delgroups, "w"); if (newfp == NULL) { setuid(tass_uid); setgid(tass_gid); return FALSE; } buf[0][0] = '\0'; buf[1][0] = '\0'; while (fgets(buf[which], 8192, del) != NULL) { which = !which; if (*buf[which]) fputs(buf[which], newfp); } fclose(del); fclose(newfp); which = !which; if (!*buf[which]) { setuid(tass_uid); setgid(tass_gid); return FALSE; } for (p = buf[which]; *p && *p != '\n'; p++) ; *p = '\0'; p = buf[which]; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; { /* find the hash of the group name */ char *t = buf[which]; h = *t++; while (*t) h = (h * 64 + *t++) % TABLE_SIZE; } for (i = group_hash[h]; i >= 0; i = active[i].next) { if (strcmp(buf[which], active[i].name) == 0) { for (j = 0; j < local_top; j++) if (my_group[j] == i) { setuid(tass_uid); setgid(tass_gid); return j; } active[i].flag &= ~NOTGOT; /* mark that we got it */ if (c != '!') active[i].flag |= SUBS; if (local_top >= max_active) expand_active(); local_top++; for (j = local_top; j > cur_groupnum; j--) { my_group[j] = my_group[j-1]; unread[j] = unread[j-1]; } my_group[cur_groupnum] = i; unread[cur_groupnum] = parse_unread(p, i); fp = fopen(newsrc, "r"); if (fp == NULL) { setuid(tass_uid); setgid(tass_gid); return FALSE; } newfp = fopen(newnewsrc, "w"); if (newfp == NULL) { fclose(fp); setuid(tass_uid); setgid(tass_gid); return FALSE; } i = 0; while (fgets(buf[!which], 8192, fp) != NULL) { for (p = buf[!which]; *p && *p != '\n'; p++) ; *p = '\0'; p = buf[!which]; while (*p && *p!=' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; while (i < cur_groupnum) { if (strcmp(buf[!which], active[my_group[i]].name) == 0) { fprintf(newfp, "%s%c%s\n", buf[!which], c, p); goto foo_cont; } i++; } fprintf(newfp, "%s%c%s\n", buf[which], c, p); fprintf(newfp, "%s%c%s\n", buf[!which], c, p); break; foo_cont:; } while (fgets(buf[!which], 8192, fp) != NULL) fputs(buf[!which], newfp); fclose(newfp); fclose(fp); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); setuid(tass_uid); setgid(tass_gid); return TRUE; } } setuid(tass_uid); setgid(tass_gid); return FALSE; } mark_group_read(group, groupnum) char *group; int groupnum; /* index into active[] for this group */ { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; if (active[groupnum].max < 2) return; setuid(real_uid); setgid(real_gid); fp = fopen(newsrc, "r"); newfp = fopen(newnewsrc, "w"); if (newfp == NULL) goto mark_group_read_done; if (fp != NULL) { while (fgets(buf, 8192, fp) != NULL) { for (p = buf; *p; p++) if (*p == '\n') { *p = '\0'; break; } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp(buf, group) == 0) { fprintf(newfp, "%s%c 1-%ld\n", buf, c, active[groupnum].max); gotit = TRUE; } else fprintf(newfp, "%s%c%s\n", buf, c, p); } fclose(fp); } fclose(newfp); unlink(newsrc); link(newnewsrc, newsrc); unlink(newnewsrc); mark_group_read_done: setuid(tass_uid); setgid(tass_gid); } long hash_groupname(buf) /* hash group name for fast lookup later */ char *buf; { char *t = buf; unsigned long h; h = *t++; while (*t) h = ((h << 1) ^ *t++) % TABLE_SIZE; /* h = (h * 64 + *t++) % TABLE_SIZE; */ return h; } #ifdef M_XENIX mkdir(path, mode) char *path; int mode; { char buf[200]; sprintf(buf, "mkdir %s", path); system(buf); chmod(path, mode); } #endif @EOF chmod 644 main.c echo x - misc.c cat >misc.c <<'@EOF' #include <stdio.h> #include <ctype.h> #include <signal.h> #include <pwd.h> #include <sys/types.h> #include <sys/stat.h> #include "tass.h" char active_file[LEN]; char homedir[LEN]; char userid[LEN]; char delgroups[LEN]; char newsrc[LEN]; char newnewsrc[LEN]; char indexdir[LEN]; char my_org[LEN]; /* organization */ char sig[LEN]; char signature[LEN]; /* * Which base note (an index into base[]) does a respnum * (an index into arts[]) corresponsd to? * * In other words, base[] points to an entry in arts[] which is * the head of a thread, linked with arts[].thread. For any q: arts[q], * find i such that base[i]->arts[n]->arts[o]->...->arts[q] */ which_base(n) int n; { int i, j; for (i = 0; i < top_base; i++) for (j = base[i]; j >= 0; j = arts[j].thread) if (j == n) return i; fprintf(stderr, "can't find base article\n"); return 0; } /* * Find how deep in a thread a response is. Start counting at zero */ which_resp(n) int n; { int i, j; int num = 0; i = which_base(n); for (j = base[i]; j != -1; j = arts[j].thread) if (j == n) break; else num++; return num; } /* * Given an index into base[], find the number of responses for * that basenote */ nresp(n) int n; { int i; int oldi = -3; int sum = 0; assert(n < top_base); for (i = base[n]; i != -1; i = arts[i].thread) { assert(i != -2); assert(i != oldi); oldi = i; sum++; } return sum - 1; } asfail(file, line, cond) char *file; int line; char *cond; { fprintf(stderr, "tass: assertion failure: %s (%d): %s\n", file, line, cond); exit(1); } /* * init_selfinfo * Deterimines users home directory, userid, and a path * for an rc file in the home directory */ init_selfinfo() { struct passwd *myentry; extern struct passwd *getpwuid(); struct stat sb; char nam[LEN]; char *p; extern char *getenv(); FILE *fp; myentry = getpwuid(getuid()); strcpy(userid, myentry->pw_name); strcpy(homedir, myentry->pw_dir); sprintf(signature, "%s/.signature", homedir); sprintf(sig, "%s/.Sig", homedir); sprintf(newsrc, "%s/.newsrc", homedir); sprintf(newnewsrc, "%s/.newnewsrc", homedir); sprintf(delgroups, "%s/.delgroups", homedir); sprintf(indexdir, "%s/.tindx", homedir); sprintf(active_file, "%s/active", LIBDIR); if (stat(active_file, &sb) >= 0) goto got_active; /* * I hate forgetting to define LIBDIR correctly. Guess a * couple of likely places if it's not where LIBDIR says it is. */ strcpy(active_file, "/usr/lib/news/active"); if (stat(active_file, &sb) >= 0) goto got_active; strcpy(active_file, "/usr/local/lib/news/active"); if (stat(active_file, &sb) >= 0) goto got_active; strcpy(active_file, "/usr/public/lib/news/active"); if (stat(active_file, &sb) >= 0) goto got_active; /* * Oh well. Revert to what LIBDIR says it is to produce a * useful error message when read_active() fails later. */ sprintf(active_file, "%s/active", LIBDIR); got_active: *my_org = '\0'; p = getenv("ORGANIZATION"); if (p != NULL) { strcpy(my_org, p); goto got_org; } sprintf(nam, "%s/organization", LIBDIR); fp = fopen(nam, "r"); if (fp == NULL) { sprintf(nam, "/usr/lib/news/organization"); fp = fopen(nam, "r"); } if (fp == NULL) { sprintf(nam, "/usr/local/lib/news/organization"); fp = fopen(nam, "r"); } if (fp == NULL) { sprintf(nam, "/usr/public/lib/news/organization"); fp = fopen(nam, "r"); } if (fp == NULL) { sprintf(nam, "/etc/organization"); fp = fopen(nam, "r"); } if (fp != NULL) { if (fgets(my_org, LEN, fp) != NULL) { for (p = my_org; *p && *p != '\n'; p++) ; *p = '\0'; } fclose(fp); } got_org:; } char * my_malloc(size) unsigned size; { char *p; extern char *malloc(); p = malloc(size); if (p == NULL) { fprintf(stderr, "tass: out of memory\n"); exit(1); } return p; } char * my_realloc(p, size) char *p; unsigned size; { extern char *malloc(); extern char *realloc(); if (p == NULL) p = malloc(size); else p = realloc(p, size); if (p == NULL) { fprintf(stderr, "tass: out of memory\n"); exit(1); } return p; } char * str_save(s) char *s; { char *p; assert(s != NULL); p = my_malloc(strlen(s) + 1); strcpy(p, s); return(p); } copy_fp(a, b, prefix) FILE *a; FILE *b; char *prefix; { char buf[8192]; while (fgets(buf, 8192, a) != NULL) fprintf(b, "%s%s", prefix, buf); } char * get_val(env, def) char *env; /* Environment variable we're looking for */ char *def; /* Default value if no environ value found */ { extern char *getenv(); char *ptr; if ((ptr = getenv(env)) != NULL) return(ptr); else return(def); } invoke_editor(nam) char *nam; { char buf[200]; static int first = TRUE; static char editor[200]; int ret; if (first) { strcpy(editor, get_val("EDITOR", DEF_EDITOR)); first = FALSE; } sprintf(buf, "%s %s", editor, nam); printf("\r%s\n", buf); ret = invoke_cmd(buf); setuid(real_uid); setgid(real_gid); return ret; } invoke_cmd(nam) char *nam; { int ret; #ifdef SIGTSTP void (*susp)(); #endif Raw(FALSE); setuid(real_uid); setgid(real_gid); #ifdef SIGTSTP susp = signal(SIGTSTP, SIG_DFL); #endif ret = system(nam); #ifdef SIGTSTP signal(SIGTSTP, susp); #endif setuid(tass_uid); setgid(tass_gid); Raw(TRUE); return ret == 0; } shell_escape() { char shell[LEN]; char *p; #ifdef SIGTSTP void (*susp)(); #endif if (!parse_string("!", shell)) strcpy(shell, get_val("SHELL", "/bin/sh")); for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) ; if (!*p) strcpy(shell, get_val("SHELL", "/bin/sh")); Raw(FALSE); setuid(real_uid); setgid(real_gid); fputs("\r\n", stdout); #ifdef SIGTSTP susp = signal(SIGTSTP, SIG_DFL); #endif system(p); #ifdef SIGTSTP signal(SIGTSTP, susp); #endif setuid(tass_uid); setgid(tass_gid); Raw(TRUE); continue_prompt(); mail_setup(); } /* * Find the previous response. Go to the last response in the previous * thread if we go past the beginning of this thread. */ prev_response(n) int n; { int resp; int i; resp = which_resp(n); if (resp > 0) return choose_resp( which_base(n), resp-1 ); i = which_base(n) - 1; if (i < 0) return -1; return choose_resp( i, nresp(i) ); } /* * Find the next response. Go to the next basenote if there * are no more responses in this thread */ next_response(n) int n; { int i; if (arts[n].thread >= 0) return arts[n].thread; i = which_base(n) + 1; if (i >= top_base) return -1; return base[i]; } /* * Given a respnum (index into arts[]), find the respnum of the * next basenote */ next_basenote(n) int n; { int i; i = which_base(n) + 1; if (i >= top_base) return -1; return base[i]; } /* * Find the next unread response in this group */ next_unread(n) int n; { while (n >= 0) { if (arts[n].unread == 1) return n; n = next_response(n); } return -1; } /* * Find the previous unread response in this thread */ prev_unread(n) int n; { while (n >= 0) { if (arts[n].unread == 1) return n; n = prev_response(n); } return -1; } add_signature(fp, flag) FILE *fp; int flag; { FILE *sigf; sigf = fopen(signature, "r"); if (sigf != NULL) { if (flag) { fprintf(fp, "\n--\n"); copy_fp(sigf, fp, ""); } fclose(sigf); return; } sigf = fopen(sig, "r"); if (sigf != NULL) { fprintf(fp, "\n--\n"); copy_fp(sigf, fp, ""); fclose(sigf); } } make_lower(s, t) char *s; char *t; { while (*s) { if (isupper(*s)) *t = tolower(*s); else *t = *s; s++; t++; } *t = 0; } match(s, t, n) char *s; char *t; int n; { while (*t) { if (*s == *t && strncmp(s, t, n) == 0) return TRUE; t++; } return FALSE; } @EOF chmod 644 misc.c echo x - nntp.h cat >nntp.h <<'@EOF' /* nntp.h -- nntp support for tass */ /* Changed a bit so nntp knows about Tass */ /* * This file is originally from the nntp 1.5 source, * but modified a bit */ #define NNTP_SERVER "/etc/nntpserver" /* * External routine declarations */ extern char *getserverbyfile(); extern int server_init(); extern int get_tcp_socket(); extern int handle_server_response(); extern void put_server(); extern int get_server(); extern void close_server(); /* * External file descriptors for the server connection */ extern FILE *ser_wr_fp; extern FILE *ser_wr_fp; /* * Response codes for NNTP server * * @(#)nntp.h 1.7 (Berkeley) 1/11/88 * * First digit: * * 1xx Informative message * 2xx Command ok * 3xx Command ok so far, continue * 4xx Command was correct, but couldn't be performed * for some specified reason. * 5xx Command unimplemented, incorrect, or a * program error has occured. * * Second digit: * * x0x Connection, setup, miscellaneous * x1x Newsgroup selection * x2x Article selection * x3x Distribution * x4x Posting */ #define CHAR_INF '1' #define CHAR_OK '2' #define CHAR_CONT '3' #define CHAR_ERR '4' #define CHAR_FATAL '5' #define INF_HELP 100 /* Help text on way */ #define INF_DEBUG 199 /* Debug output */ #define OK_CANPOST 200 /* Hello; you can post */ #define OK_NOPOST 201 /* Hello; you can't post */ #define OK_SLAVE 202 /* Slave status noted */ #define OK_GOODBYE 205 /* Closing connection */ #define OK_GROUP 211 /* Group selected */ #define OK_GROUPS 215 /* Newsgroups follow */ #define OK_TASSINDEX 218 /* Tass index follows */ #define OK_ARTICLE 220 /* Article (head & body) follows */ #define OK_HEAD 221 /* Head follows */ #define OK_BODY 222 /* Body follows */ #define OK_NOTEXT 223 /* No text sent -- stat, next, last */ #define OK_NEWNEWS 230 /* New articles by message-id follow */ #define OK_NEWGROUPS 231 /* New newsgroups follow */ #define OK_XFERED 235 /* Article transferred successfully */ #define OK_POSTED 240 /* Article posted successfully */ #define CONT_XFER 335 /* Continue to send article */ #define CONT_POST 340 /* Continue to post article */ #define ERR_GOODBYE 400 /* Have to hang up for some reason */ #define ERR_NOGROUP 411 /* No such newsgroup */ #define ERR_NCING 412 /* Not currently in newsgroup */ #define ERR_NOTASS 418 /* No tass index for this group */ #define ERR_NOCRNT 420 /* No current article selected */ #define ERR_NONEXT 421 /* No next article in this group */ #define ERR_NOPREV 422 /* No previous article in this group */ #define ERR_NOARTIG 423 /* No such article in this group */ #define ERR_NOART 430 /* No such article at all */ #define ERR_GOTIT 435 /* Already got that article, don't send */ #define ERR_XFERFAIL 436 /* Transfer failed */ #define ERR_XFERRJCT 437 /* Article rejected, don't resend */ #define ERR_NOPOST 440 /* Posting not allowed */ #define ERR_POSTFAIL 441 /* Posting failed */ #define ERR_COMMAND 500 /* Command not recognized */ #define ERR_CMDSYN 501 /* Command syntax error */ #define ERR_ACCESS 502 /* Access to server denied */ #define ERR_FAULT 503 /* Program fault, command not performed */ /* RFC 977 defines this; don't change it. */ #define NNTP_STRLEN 512 @EOF chmod 644 nntp.h echo x - nntp_open.c cat >nntp_open.c <<'@EOF' #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include "nntp.h" #include "tass.h" char * is_remote() { return " (remote)"; } nntp_startup() { char *server_name; int ret; extern char *getenv(); server_name = getserverbyfile(NNTP_SERVER); if (server_name == NULL) { fprintf(stderr, "Can't get nntp server name\n"); fprintf(stderr, "Either put the name in the file %s, or put\n", NNTP_SERVER); fprintf(stderr, "it in the environment variable NNTPSERVER\n"); exit(1); } ret = server_init(server_name); switch (ret) { case OK_CANPOST: case OK_NOPOST: break; case -1: fprintf(stderr, "failed to connect to server\n"); exit(1); default: fprintf(stderr, "rejected by server, nntp error %d\n", ret); exit(1); } } nntp_finish() { close_server(); } /* * get_respcode * get a response code from the server and return it to the caller */ int get_respcode() { char line[NNTP_STRLEN]; if (get_server(line, NNTP_STRLEN) == -1) { fprintf(stderr, "connection to server broken\n"); tass_done(1); } return atoi(line); } stuff_nntp(fnam) char *fnam; { FILE *fp; char line[NNTP_STRLEN]; extern char *mktemp(); struct stat sb; extern long note_size; strcpy(fnam, "/tmp/tass_nntpXXXXXX"); mktemp(fnam); fp = fopen(fnam, "w"); if (fp == NULL) { fprintf(stderr, "stuff_nntp: can't open %s: ", fnam); perror(""); return FALSE; } while (1) { if (get_server(line, NNTP_STRLEN) == -1) { fprintf(stderr, "connection to server broken\n"); tass_done(1); } if (strcmp(line, ".") == 0) break; /* end of text */ strcat(line, "\n"); if (line[0] == '.') /* reduce leading .'s */ fputs(&line[1], fp); else fputs(line, fp); } fclose(fp); if (stat(fnam, &sb) < 0) note_size = 0; else note_size = sb.st_size; return TRUE; } FILE * nntp_to_fp() { char fnam[LEN]; FILE *fp; if (!stuff_nntp(fnam)) return NULL; fp = fopen(fnam, "r"); if (fp == NULL) { fprintf(stderr, "nntp_to_fp: can't reopen %s: ", fnam); perror(""); return NULL; } unlink(fnam); return fp; } nntp_to_fd() { char fnam[LEN]; int fd; if (!stuff_nntp(fnam)) return NULL; fd = open(fnam, 0); if (fd == NULL) { fprintf(stderr, "nntp_to_fd: can't reopen %s: ", fnam); perror(""); return -1; } unlink(fnam); return fd; } FILE * open_active_fp() { put_server("list"); if (get_respcode() != OK_GROUPS) return NULL; return nntp_to_fp(); } FILE * open_art_fp(group_path, art) char *group_path; long art; { char buf[LEN]; sprintf(buf, "article %ld", art); put_server(buf); if (get_respcode() != OK_ARTICLE) return NULL; return nntp_to_fp(); } open_header_fd(group_path, art) char *group_path; long art; { char buf[LEN]; sprintf(buf, "head %ld", art); put_server(buf); if (get_respcode() != OK_HEAD) return -1; return nntp_to_fd(); } setup_base(group, group_path) char *group; char *group_path; { char buf[LEN]; char line[NNTP_STRLEN]; long start, last, dummy, count; top_base = 0; sprintf(buf, "group %s", group); put_server(buf); if (get_server(line, NNTP_STRLEN) == -1) { fprintf(stderr, "connection to server broken\n"); tass_done(1); } if (atoi(line) != OK_GROUP) return; sscanf(line,"%ld %ld %ld %ld", &dummy, &count, &start, &last); if (last - count > start) start = last - count; while (start <= last) { if (top_base >= max_art) expand_art(); base[top_base++] = start++; } } @EOF chmod 644 nntp_open.c echo x - prompt.c cat >prompt.c <<'@EOF' #include <stdio.h> #include "tass.h" /* * parse_num * get a number from the user * Return -1 if missing or bad number typed */ parse_num(ch, prompt) char ch; char *prompt; { char buf[40]; int len; int i; int num; MoveCursor(LINES,0); printf("%s %c",prompt,ch); fflush(stdout); buf[0] = ch; buf[1] = '\0'; len = 1; ch = ReadCh(); while (ch != '\n'&& ch != '\r') { if (ch >= '0' && ch <= '9' && len < 4) { buf[len++] = ch; buf[len] = '\0'; putchar(ch); } else if (ch == 8 || ch == 127) { if (len) { len--; buf[len] = '\0'; putchar('\b'); putchar(' '); putchar('\b'); } else { MoveCursor(LINES, 0); CleartoEOLN(); return(-1); } } else if (ch == 21) { /* control-U */ for (i = len;i>0;i--) { putchar('\b'); putchar(' '); putchar('\b'); } buf[0] = '\0'; len = 0; } else putchar(7); fflush(stdout); ch = ReadCh(); } MoveCursor(LINES, 0); CleartoEOLN(); if (len) { num = atoi(buf); return(num); } else return(-1); } /* * parse_string * get a string from the user * Return TRUE if a valid string was typed, FALSE otherwise */ parse_string(prompt, buf) char *prompt; char *buf; { int len; int i; char ch; clear_message(); MoveCursor(LINES,0); printf("%s", prompt); fflush(stdout); buf[0] = '\0'; len = 0; ch = ReadCh(); while (ch != '\n' && ch != '\r') { if (ch >= ' ' && len < 60) { buf[len++] = ch; buf[len] = '\0'; putchar(ch); } else if (ch == 8 || ch == 127) { if (len) { len--; buf[len] = '\0'; putchar('\b'); putchar(' '); putchar('\b'); } else { MoveCursor(LINES, 0); CleartoEOLN(); return(FALSE); } } else if (ch == 21) { /* control-U */ for (i = len;i>0;i--) { putchar('\b'); putchar(' '); putchar('\b'); } buf[0] = '\0'; len = 0; } else putchar(7); fflush(stdout); ch = ReadCh(); } MoveCursor(LINES,0); CleartoEOLN(); return TRUE; } prompt_yn(prompt) char *prompt; { char ch; clear_message(); MoveCursor(LINES,0); printf("%s", prompt); fflush(stdout); ch = ReadCh(); clear_message(); if (ch == 'y' || ch == 'Y') return TRUE; return FALSE; } continue_prompt() { printf("-Hit return to continue-"); fflush(stdout); ReadCh(); } @EOF chmod 644 prompt.c echo x - tass.h cat >tass.h <<'@EOF' #define LIBDIR "/usr/lib/news" #define SPOOLDIR "/usr/spool/news" #define MAILER "/bin/rmail" #define DEF_EDITOR "/usr/bin/vi" #define TRUE 1 #define FALSE 0 #define LEN 200 #define INDEX_TOP 4 #define NOTESLINES (LINES - INDEX_TOP - 2) #define RIGHT_POS (COLS - 16) #define MORE_POS (COLS - 20) #define MAX_FROM 25 #define MAX_SUBJ 38 #define TABLE_SIZE 1409 /* should be prime */ /* #define MAX_SUBJ (COLS - 42) */ struct header { long artnum; char *subject; char *from; int thread; int inthread; int unread; /* has this article been read? */ /* 0 = read, 1 = unread, 2 = will return */ }; /* * header.artnum: * article number in spool directory for group * * header.thread: * initially -1 * points to another arts[] (struct header): zero and up * -2 means article has expired (wasn't found in file search * of spool directory for the group) * * header.inthread: * FALSE for the first article in a thread, TRUE for all * following articles in thread * * header.read: * boolean, has this article been read or not */ struct group_ent { char *name; long max; long min; int next; /* next active entry in hash chain */ int flag; }; #define NOTGOT 0x01 /* haven't put in my_group yet */ #define SUBS 0x02 /* subscribed to */ extern int top; extern struct header *arts; extern long *base; extern int max_art; extern char sig[LEN]; extern char signature[LEN]; extern char userid[LEN]; extern char homedir[LEN]; extern char indexdir[LEN]; extern char my_org[LEN]; extern char active_file[LEN]; extern char newsrc[LEN]; extern char newnewsrc[LEN]; extern char delgroups[LEN]; extern int top_base; extern int LINES, COLS; extern char *str_save(); extern char *my_malloc(); extern char *my_realloc(); extern int group_hash[TABLE_SIZE]; extern int num_active; extern struct group_ent *active; extern int *my_group; extern int *unread; extern int max_active; extern int local_top; extern char *eat_re(); extern char *nice_time(); extern int update; extern int inverse_okay; extern int tass_uid; extern int tass_gid; extern int real_uid; extern int real_gid; extern int local_index; extern char *strcpy(); extern char *strncat(); extern char *strncpy(); extern long atol(); #define ctrl(c) ((c) & 0x1F) /* * Assertion verifier */ #ifdef __STDC__ #define assert(p) if(! (p)) asfail(__FILE__, __LINE__, #p); else #else #define assert(p) if(! (p)) asfail(__FILE__, __LINE__, "p"); else #endif #define TASS_HEADER "Tass 3.2" @EOF chmod 644 tass.h echo x - time.c cat >time.c <<'@EOF' #include <sys/types.h> #include <time.h> nicedate(timestr, newstr) char *timestr, *newstr; { int i; for (i = 0; i <= 7; i++) *newstr++ = timestr[i]; if (timestr[8] != ' ') *newstr++ = timestr[8]; *newstr++ = timestr[9]; *newstr++ = ','; *newstr++ = ' '; for (i = 20;i <= 23; i++) *newstr++ = timestr[i]; *newstr++ = '\0'; } nicetime(timestr, newstr) char *timestr, *newstr; { int hours; char dayornite[3]; if (timestr[11] == ' ') hours = timestr[12] - '0'; else hours = (timestr[11]-'0')*10 + (timestr[12]-'0'); if (hours < 12) strcpy(dayornite, "am"); else strcpy(dayornite, "pm"); if (hours >= 13) hours -= 12; if (!hours) hours = 12; sprintf(newstr, "%d:%c%c%s", hours, timestr[14], timestr[15], dayornite); } char *nice_time() { char *timestr; char the_date[17]; char the_time[8]; extern char *ctime(); long time_now; static char buf[25]; time(&time_now); timestr = ctime(&time_now); nicedate(timestr, the_date); nicetime(timestr, the_time); sprintf(buf,"%s %s", the_date, the_time); return(buf); } @EOF chmod 644 time.c exit 0