/* * * reg.c - Lysators medlemsregister. A few things to remember are 1. when * answering a question be sure to answer with the first letter * typed, 2. all reports are local to connected directory and are * always appended to whatever was in them before. * * * usage: reg [file] * * * jp 891218 - the original * jp 901112 - added sending mail (using program "mail" within system()) * to members being late. * jp 901116 - minor changes: LINELEN;Member# in decimal also. * jp 901127 - made it unix-compatible using fork(),exec(),time(),etc. also * a check on 'operator' is made (sending mail + updating the * register on disk), consisting of check of access(read+write) on * the datafile. * jp 910411 - A few minor changes concerning payment and editing. * ceder 911128 - Talk to sendmail instead of "mail" within system(). * */ #include #include #include #include #include #include #include "sendmail-if.h" /* ***** declarations ***** */ #define VERSION "$Id: reg-sendmail.c,v 1.8 1992/08/05 21:48:33 ceder Exp $" /* guess what! */ #ifndef NULL #define NULL 0 #endif /* the maximum no of members in the struct[] */ #ifndef MEMBERS #define MEMBERS 1024 #endif /* LINELEN = how many chars on a line (in datatype 'line'), has it's dependencies during printout also! (%65s %-65s ... ) */ #ifndef LINELEN #define LINELEN 65 #endif /* MEMBERFEE is the default memberfee, remenber that the fee is NOT saved! */ #ifndef MEMBERFEE #define MEMBERFEE 128 #endif /* just in case */ #ifndef __STDC__ #ifndef void #define void int #endif #endif /* our datafile on disk */ /* @elinor: PS:MEMBERS.LYS @lage: members.lys @nanny: /usr/adm/reg/members.lys @lysator:/users/wing/reg/members.lys */ #ifndef DATAFILE #define DATAFILE "/users/wing/reg/members.lys" #endif /* our reportfile, here goes all printouts to reports */ #ifndef REPORTFILE #define REPORTFILE "lys.rpt" #endif /* our POSTGIRO-file, is used to generate pay-bills (with or without name) */ #ifndef POSTGIROFILE #define POSTGIROFILE "lys.pg" #endif /* our mailfile, this is the letter that will be sent to all who are late paying their memberfee */ #ifndef MAILFILE #define MAILFILE "lys.pay" #endif /* the number of fields in struct {} reg[]; */ #define NFIELDS 11 /* guess what! */ #define UPC(x) (((x >= 'a') && (x <= '}'))?(x & ~0x20):(x)) #define EQUAL(a,b) (UPC(a) == UPC(b)) /* our variables etc. */ typedef char line[LINELEN]; /* Zero terminated string. */ /* The register is kept in-core in an array. reg[0] is used as a scratch area. reg[1] is our inspector, TJH. */ struct { line name; /* Surename, First name. */ line adr[2]; /* Two lines. */ line tel; /* One line. */ line email; /* Comma-separated. */ line remark; /* Free format. */ int active; /* 1 (active) or 0 (not active). */ int expdate; /* YYMMDD, as an integer. */ int want_garb; /* Send GARB via snail-mail? */ int want_call; /* Call-for-participation to member confs. */ line education; /* Such as 'D88'. */ int index; /* Used when sorting the register. */ } reg[MEMBERS+1]; /* remember: reg[0] == dummy record! */ struct { int no_of_members; /* Really: number of allocated member-nos. */ int date; /* Current date in JP-format. */ int memberfee; /* See MEMBERFEE above! */ int sorted; int operator; line df_filename; FILE *df; /* datafile */ FILE *rf; /* reportfile */ FILE *pg; /* postgiro-file */ } regstatus; void newreg(); /* Are we connected to the SMTP daemon yet? */ static int connected = 0; /* ***** help routines ***** */ /* get line from file 'f' and put into 's', do some checking also */ int get_line(FILE *f, char *s) { int c; int n=0; while (1) { if ( (c=getc(f)) == EOF) return 1; n++; if ((c=='\n')||(c=='\r')) { *s='\0'; return 0; } if (n==LINELEN-1) { *s='\0'; while ( (c=getc(f)) != EOF) if (c=='\n' || c=='\r') break; printf("\n***ERROR: too long input - cut off!\n\n"); return 0; } *s++ = (char) c; } } /* copy string 't' into 's' */ void str_copy(char *s, char *t) { while ( (*s++ = *t++) != '\0') ; } /* return (s=1) { printf("."); fflush(stdout); for(i=1;i<=n-d;i++) { j=i; while (j>=1) { if (!str_less(j+d, j)) break; reg[0].index = reg[j+d].index; reg[j+d].index = reg[j].index; reg[j].index = reg[0].index; j-=d; } } d/=2; } regstatus.sorted=1; /* indicate that it is sorted */ } /* clear record# 'i' */ void clear_record(int i) { str_copy(reg[i].name, ""); str_copy(reg[i].adr[0], ""); str_copy(reg[i].adr[1], ""); str_copy(reg[i].tel, ""); str_copy(reg[i].email, ""); str_copy(reg[i].remark, ""); str_copy(reg[i].education, ""); reg[i].active=0; reg[i].expdate=0; reg[i].want_garb=0; reg[i].want_call=0; if (i) regstatus.sorted=0; /* indicate register not sorted */ } /* convert string in 's' into an integer and put in '*n'. return 0=ok, 1=error */ int ascii_to_int(char *s, int *n) { int res=0; while (*s) { if ((*s<'0')||(*s>'9')) return 1; res=res*10+((int)*s++ - '0'); if (res<0) return 1; /* overflow! */ } *n = res; return 0; } /* convert integer 'n' into string 's' of binary numbers */ void int_to_bin(char *s, int n) { line tmp; char *t=tmp; char *p=s; int i=n; str_copy(s, "0"); if (!n) return; while (i) { *t++ = (char)((i&1)|'0'); i>>=1; } while (t!=tmp) *p++ = *--t; *p='\0'; } /* convert binary string in 's' into integer in '*n', return 0=ok, 1=error */ int bin_to_int(char *s, int *n) { *n=0; while (*s) { if ((*s<'0')||(*s>'1')) return 1; *n<<=1; *n|=(*s++ & ~'0'); } return 0; } /* get record from regstatus.df_filename. Save in temporary buffer (reg[0]). return 0=ok, 1=error */ int get_record(int expected_member) { int i; line tmp[NFIELDS]; if (get_line(regstatus.df, tmp[0])) return 1; if (atoi(tmp[0]) != expected_member) { printf("Datafile error: Expected member number %d, but got the " "line\n'%s'\n", i, tmp[0]); exit(1); } clear_record(0); for(i=0;iregstatus.no_of_members) regstatus.no_of_members=i; if (i > MEMBERS) { printf("***ERROR: register has grown too large, please "); printf("recompile with larger value\n on MEMBERS "); printf("(now: %d)\n", MEMBERS); regstatus.no_of_members=MEMBERS; newreg(); /* Save the register, then exit. */ exit(1); } str_copy(reg[i].name, reg[0].name); str_copy(reg[i].adr[0], reg[0].adr[0]); str_copy(reg[i].adr[1], reg[0].adr[1]); str_copy(reg[i].tel, reg[0].tel); str_copy(reg[i].email, reg[0].email); str_copy(reg[i].education, reg[0].education); reg[i].active=reg[0].active; reg[i].expdate=reg[0].expdate; reg[i].want_garb=reg[0].want_garb; reg[i].want_call=reg[0].want_call; str_copy(reg[i].remark, reg[0].remark); regstatus.sorted=0; /* indicate register not sorted */ } /* show some status on this register */ void status(void) { int i; int m=0; for(i=1;i<=regstatus.no_of_members;i++) if (reg[i].active) m++; printf("\nRegister status: Date=%d Members=%d (%d active) Fee=%d\n\n", regstatus.date, regstatus.no_of_members, m, regstatus.memberfee); printf("Files: Data='%s', Report='%s'\n", regstatus.df_filename, REPORTFILE); printf(" Postgiro='%s', Mail='%s'\n", POSTGIROFILE, MAILFILE); printf("Status: Register is %ssorted in name-order\n", regstatus.sorted ? "" : "NOT "); printf(" User is %san operator (mail + update register)\n", regstatus.operator ? "" : "NOT "); printf(" Maximum linelength=%d\n", LINELEN); } /* show verbose info in report */ void show_verbose(FILE *f, int i, char *tmp) { fprintf(f, "%15s (#%5d) %s\n", tmp, i, reg[i].name); fprintf(f, "%24s %s\n", "", reg[i].adr[0]); fprintf(f, "%24s %s\n", "", reg[i].adr[1]); fprintf(f, "%24s %s\n", "", reg[i].tel); fprintf(f, "%24s %s\n", "", reg[i].email); fprintf(f, "%24s %s\n", "", reg[i].remark); fprintf(f, "%24s %s\n", "", reg[i].education); fprintf(f, "%24s Garb: %3s Kallelse: %3s\n", "", reg[i].want_garb ? "Yes" : "-", reg[i].want_call ? "Yes" : "-"); fprintf(f, "%24s %sctive member\tExpdate: %d\n\n", "", (reg[i].active ? "A" : "Ina"), reg[i].expdate); } /* show brief info in report */ void show_brief(FILE *f, int i, char *tmp) { line z; str_copy(z, reg[i].name); z[40]='\0'; if (f==stdout) fprintf(f, "%15s (#%5d) %s\n", tmp, i, z); else fprintf(f, "%15s (#%5d) %-40s %s\n", tmp, i, z, reg[i].email); } /* show brief info on payments */ void pay_brief(FILE *f, int i, char *tmp, int email) { line z; str_copy(z, reg[i].name); z[40]='\0'; if (f==stdout || !email) fprintf(f, "%15s (#%5d) %-40s %6d\n", tmp, i, z, reg[i].expdate); else fprintf(f, "%15s (#%5d) %-40s %6d %s\n", tmp, i, z, reg[i].expdate, reg[i].email); } /* output a headerline, m=1 if in payments */ void hdr_line(FILE *f, int m) { fprintf(f, "------------------------ "); fprintf(f, "----------------------------------------"); fprintf(f, "%s", m ? " ---------" : ""); fprintf(f, "%s\n", (f!=stdout) ? " ---------------" : ""); } /* give verbose report header */ void hdr_verbose(FILE *f, char *s) { fprintf(f, "\n\n%s, date=%d, %d members\n\n", s, regstatus.date, regstatus.no_of_members); fprintf(f, "\n%-24s Name\n", "Member#"); fprintf(f, "%24s Address\n", ""); fprintf(f, "%24s Tel\n", ""); fprintf(f, "%24s Email\n", ""); fprintf(f, "%24s Remark\n", ""); fprintf(f, "%24s Education\n", ""); fprintf(f, "%24s Garb via mail Kallelse till medlemsm|te\n", ""); fprintf(f, "%24s Activity \tExpdate\n", ""); hdr_line(f, 0); } /* give brief header */ void hdr_brief(FILE *f, char *s, int l, /* List expdate? */ int m) /* ? */ { fprintf(f, "\n\n%s, date=%d, %d members\n\n", s, regstatus.date, regstatus.no_of_members); fprintf(f, "\n%-24s %-40s%s", "Member#", "Name", l ? " Expdate" : ""); fprintf(f, "%s\n", (f!=stdout) ? " Email" : ""); hdr_line(f, m); } /* try to find a match of the string 's' in 't' */ int match(char *t, char *s) { char *a=s; char *b; while (*t) { while (*t) /* Find first equal char. */ { if (EQUAL(*t, *s)) break; t++; } if (!*t) break; b=t; /* Fist char is equal. Full match? */ while (*s) { s++; t++; if (!EQUAL(*s, *t)) break; } if (!*s) /* Match found. */ return(1); if (!*t) break; s=a; t= ++b; } return(0); /* No match found. */ } /* show all who match string 's' */ void match_name(char *s) { line tmp; int cnt=0; int i; sprintf(tmp, "Report on members matching '%s'", s); hdr_brief(stdout, tmp, 0, 0); for(i=1; i<=regstatus.no_of_members; i++) if (match(reg[i].name, s)) { int_to_bin(tmp, i); cnt++; show_brief(stdout, i, tmp); } hdr_line(stdout, 0); printf("\nNumber of matching names: %d\n", cnt); return; } /* names in POSTGIRO can't be more than 29 chars! */ void adjust(char *s) { int n=0; while (*s) { n++; s++; if (n==29) { *s='\0'; return; } } } /* write POSTGIRO */ void postgiro(char *s, int n) { line tmp; fprintf(regstatus.pg, " 88 56 69 - 2 88 56 69 - 2\n"); fprintf(regstatus.pg, "\n Medlemsavgift f|r tolv m}nader\n"); fprintf(regstatus.pg, " ( vid felaktiga uppgifter, Datorf|reningen\n"); fprintf(regstatus.pg, " tag ny blankett! ) Lysator vid LiTH\n"); fprintf(regstatus.pg, "\n\n%s%s\n", *s ? " Medlemsnr: " : "", s); str_copy(tmp, reg[n].name); adjust(tmp); fprintf(regstatus.pg, " Namn: %-30s %s\n", tmp, *s?tmp:"Namn:"); str_copy(tmp, reg[n].adr[0]); adjust(tmp); fprintf(regstatus.pg, " Adr: %-30s %s\n", tmp, *s?tmp:"Adr:"); str_copy(tmp, reg[n].adr[1]); adjust(tmp); fprintf(regstatus.pg, " %-30s %s\n", tmp, tmp); str_copy(tmp, reg[n].tel); adjust(tmp); fprintf(regstatus.pg, " Tel: %s\n", tmp); str_copy(tmp, reg[n].email); adjust(tmp); fprintf(regstatus.pg, " Email: %s\n", tmp); fprintf(regstatus.pg, "\n"); fprintf(regstatus.pg, "%30s%3d -- %11s%3d --\n", "", regstatus.memberfee, "", regstatus.memberfee); fprintf(regstatus.pg, "\n\n\n\n\n\n\n\n\n"); } /* ***** main routines ***** */ /* initiate the register from regstatus.df_filename */ void init_reg(void) { int i; long int c; struct tm *tp, *localtime(); regstatus.no_of_members=0; regstatus.date=0; regstatus.memberfee=MEMBERFEE; regstatus.sorted=0; regstatus.operator=0; if (access(regstatus.df_filename, R_OK)==0 && access(regstatus.df_filename, W_OK)==0) { regstatus.operator=1; } time(&c); tp=localtime(&c); tp->tm_mon++; regstatus.date= 10000*(tp->tm_year) + 100*(tp->tm_mon) + (tp->tm_mday); for(i=0;i<=MEMBERS;i++) clear_record(i); regstatus.df=fopen(regstatus.df_filename, "r"); if (regstatus.df==(FILE *)NULL) { printf("***ERROR: cannot open the datafile '%s'!\n", regstatus.df_filename); return; } while (!get_record(regstatus.no_of_members + 1)) newrecord(++regstatus.no_of_members); fclose(regstatus.df); status(); } /* save this register in regstatus.df_filename */ void newreg(void) { int i; regstatus.df=fopen(regstatus.df_filename, "w"); if (regstatus.df==(FILE *)NULL) { printf("***ERROR: cannot update the datafile '%s'!\n", regstatus.df_filename); return; } for(i=1;i<=regstatus.no_of_members;i++) { fprintf(regstatus.df, "%d -- ** --\n", i); fprintf(regstatus.df, "%s\n", reg[i].name); fprintf(regstatus.df, "%s\n", reg[i].adr[0]); fprintf(regstatus.df, "%s\n", reg[i].adr[1]); fprintf(regstatus.df, "%s\n", reg[i].tel); fprintf(regstatus.df, "%s\n", reg[i].email); fprintf(regstatus.df, "%d\n", reg[i].active); fprintf(regstatus.df, "%d\n", reg[i].expdate); fprintf(regstatus.df, "%s\n", reg[i].remark); fprintf(regstatus.df, "%s\n", reg[i].education); fprintf(regstatus.df, "%d\n", reg[i].want_garb); fprintf(regstatus.df, "%d\n", reg[i].want_call); } fclose(regstatus.df); printf("\nDatafile has been updated.\n"); } /* menu alternative 1 */ void payment(void) { int i; int rfile; int brief; int pgiro; int future; int indirect; int i_save; int mail; line tmp; char x_mailbuf[2*BUFSIZ]; int firstday; int lastday; int exptime; future=regstatus.date+150; /* 1.5 month ahead! */ i=(future%10000)/100; /* cryptic? simply including next month in expdate! */ if (i==13) future+=8800; /* date == YYMMDD == YY*10000 + MM*100 + DD */ printf("\n\nReport on file '%s'? (y/n) (n) ", REPORTFILE); get_line(stdin, tmp); rfile=(*tmp=='y' || *tmp=='Y'); if (rfile) printf("Print report on file '%s'\n", REPORTFILE); printf("\nShow only a brief listing? (y/n) (y) "); get_line(stdin, tmp); brief=(*tmp!='n' && *tmp!='N'); if (!brief) printf("Show verbose listing\n"); printf("\nReport on POSTGIRO-file '%s'? (y/n) (n) ", POSTGIROFILE); get_line(stdin, tmp); pgiro=(*tmp=='y' || *tmp=='Y'); if (pgiro) printf("Print report to POSTGIRO-file '%s'\n", POSTGIROFILE); mail=0; if (regstatus.operator) { /* not available to everyone... */ printf("\nSend mail to remind? (using file '%s')? (y/n) (n) ", MAILFILE); get_line(stdin, tmp); mail=(*tmp=='y' || *tmp=='Y'); if (mail) printf("Send mail to remind using file '%s'\n", MAILFILE); } printf("\nSort output in name-order? (y/n) (n) "); get_line(stdin, tmp); indirect=(*tmp=='Y' || *tmp=='y'); if (indirect) { printf("Sort in name-order:"); fflush(stdout); /* This line added by ceder 1991-11-04. */ if (!regstatus.sorted) do_sort(); printf(" ok.\n"); } printf("\nMake selections on expdate? (y/n) (n) "); get_line(stdin, tmp); exptime=(*tmp=='y' || *tmp=='Y'); if (exptime) { firstday = lastday = (-1); /* no restriction */ printf("First day (yymmdd) (RETURN for no selection) : "); get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(&tmp[0], &firstday)) { firstday = (-1); printf("***ERROR: illegal date entered: %s\n", tmp); } } printf("Last day (yymmdd) (RETURN for no selection) : "); get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(&tmp[0], &lastday)) { lastday = (-1); printf("***ERROR: illegal date entered: %s\n", tmp); } } } if (pgiro) { regstatus.pg=fopen(POSTGIROFILE, "a"); if (regstatus.pg==(FILE *)NULL) { printf("***ERROR: cannot append to postgirofile "); printf("'%s'!\n", POSTGIROFILE); return; } } if (rfile) { regstatus.rf=fopen(REPORTFILE, "a"); if (regstatus.rf==(FILE *)NULL) { printf("***ERROR: cannot append to reportfile "); printf("'%s'!\n", REPORTFILE); return; } if (brief) hdr_brief(regstatus.rf, "Check on payments", 1, 1); else hdr_verbose(regstatus.rf, "Check on payments"); } if (brief) hdr_brief(stdout, "Check on payments", 1, 1); else hdr_verbose(stdout, "Check on payments"); for(i=2;i<=regstatus.no_of_members;i++) { /* never charge #1 == TJH */ i_save=i; if (indirect) i=reg[i].index; if (exptime) { if (firstday >= 0) { if (reg[i].expdate < firstday) { i = i_save; continue; } } if (lastday >= 0) { if (reg[i].expdate > lastday) { i = i_save; continue; } } } if (!reg[i].active) { i=i_save; continue; } if (!exptime && !(reg[i].expdate <= future)) { i=i_save; continue; } int_to_bin(tmp, i); if (pgiro) postgiro(tmp, i); if (brief) { pay_brief(stdout, i, tmp, 1); if (rfile) pay_brief(regstatus.rf, i, tmp, 1); } else { show_verbose(stdout, i, tmp); if (rfile) show_verbose(regstatus.rf, i, tmp); } if (mail) { if (reg[i].email[0]=='\0') { printf("***ERROR: Member without email: '%s'\n", reg[i].name); } else { sprintf(x_mailbuf, "mail %s < %s", reg[i].email, MAILFILE); if (system(x_mailbuf)!=0) { printf("***ERROR: error executing mail!\n"); } sleep(2); /* Don't raise the load too much... */ } } i=i_save; } if (pgiro) fclose(regstatus.pg); if (rfile) { hdr_line(regstatus.rf, brief); fprintf(regstatus.rf, "\f\n"); fclose(regstatus.rf); } hdr_line(stdout, brief); printf("\n\nPress RETURN to continue "); get_line(stdin, tmp); } /* menu alternative 2 */ void edit_members(void) { line tmp; int no; int i; printf("\nInstructions to edit members\n\n"); printf("- Always press RETURN to accept default values\n"); printf("- In 'Member#'-field, press RETURN to register new member, \n"); printf(" 'bbb' or '#ddd' to enter other member#, '?' to do a name-\n"); printf(" match on the members in the register, 'E' or 'Q' to exit."); while (1) { printf("\n\nEdit members, date=%d\n\n", regstatus.date); no=regstatus.no_of_members+1; int_to_bin(tmp, no); printf("Member# : %s (#%d)\n", tmp, no); printf("Member# : "); get_line(stdin, tmp); if ((*tmp=='E'||*tmp=='e'||*tmp=='Q'||*tmp=='q')&&!tmp[1]) return; if (*tmp=='?') { printf("\nName-matching: "); get_line(stdin, tmp); if (*tmp) match_name(tmp); continue; } if (*tmp == '#') { /* decimal no */ if (ascii_to_int(&tmp[1], &no)) { printf("***ERROR: illegal number entered: %s\n", tmp); return; } } else if (*tmp) { /* binary no */ if (bin_to_int(tmp, &no)) { printf("***ERROR: illegal number entered: %s\n", tmp); return; } } if (no>MEMBERS) { printf("***ERROR: member# too large!\n"); return; } if (no<1) { printf("***ERROR: member# too small!\n"); return; } if (reg[no].name[0] != '\0') { printf("\nThis is an existing member\n\n"); int_to_bin(tmp, no); printf("Member# : %s (#%d)\n", tmp, no); printf("Name : %s\n", reg[no].name); printf("Address : %s\n", reg[no].adr[0]); printf("Address : %s\n", reg[no].adr[1]); printf("Tel : %s\n", reg[no].tel); printf("Email : %s\n", reg[no].email); printf("Remark : %s\n", reg[no].remark); printf("Educat. : %s\n", reg[no].education); printf("Garb : %s\n", reg[no].want_garb ? "Yes" : "-"); printf("Kallelse: %s\n", reg[no].want_call ? "Yes" : "-"); printf("Active : %d (0=inactive, 1=active)\n", reg[no].active); printf("Expdate : %d\n\n", reg[no].expdate); printf("\nEdit this member? (y=yes) "); get_line(stdin, tmp); if ((*tmp!='y') && (*tmp!='Y')) continue; } else printf("\nThis is a new member.\n\n"); printf("Name : "); str_copy(reg[0].name, reg[no].name); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].name, tmp); printf("Address : "); str_copy(reg[0].adr[0], reg[no].adr[0]); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].adr[0], tmp); printf("Address : "); str_copy(reg[0].adr[1], reg[no].adr[1]); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].adr[1], tmp); printf("Tel : "); str_copy(reg[0].tel, reg[no].tel); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].tel, tmp); printf("Email : "); str_copy(reg[0].email, reg[no].email); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].email, tmp); printf("Remark : "); str_copy(reg[0].remark, reg[no].remark); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].remark, tmp); printf("Educat. : "); str_copy(reg[0].education, reg[no].education); get_line(stdin, tmp); if (*tmp) str_copy(reg[0].education, tmp); printf("Garb : "); reg[0].want_garb=reg[no].want_garb; get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(tmp, &i)) printf("***ERROR: wrong input!\n"); else if ((i<0)||(i>1)) printf("***ERROR: wrong input!\n"); else reg[0].want_garb=i; } printf("Kallelse: "); reg[0].want_call=reg[no].want_call; get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(tmp, &i)) printf("***ERROR: wrong input!\n"); else if ((i<0)||(i>1)) printf("***ERROR: wrong input!\n"); else reg[0].want_call=i; } printf("Active : "); reg[0].active=reg[no].active; get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(tmp, &i)) printf("***ERROR: wrong input!\n"); else if (i<0) printf("***ERROR: wrong input!\n"); else reg[0].active=!!i; } printf("Expdate : "); reg[0].expdate=reg[no].expdate; get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(tmp, &i)) printf("***ERROR: wrong input!\n"); else if (i<0) printf("***ERROR: wrong input!\n"); else reg[0].expdate=i; } printf("\nVerification of input\n\n"); int_to_bin(tmp, no); printf("Member# : %s (#%d)\n", tmp, no); printf("Name : %s\n", reg[0].name); printf("Adr : %s\n", reg[0].adr[0]); printf("Adr : %s\n", reg[0].adr[1]); printf("Tel : %s\n", reg[0].tel); printf("Email : %s\n", reg[0].email); printf("Remark : %s\n", reg[0].remark); printf("Educat. : %s\n", reg[no].education); printf("Garb : %s\n", reg[no].want_garb ? "Yes" : "-"); printf("Kallelse: %s\n", reg[no].want_call ? "Yes" : "-"); printf("Active : %d (0=inactive, 1=active)\n", reg[0].active); printf("Expdate : %d\n", reg[0].expdate); printf("\nSave in register? (y/n) (y) "); get_line(stdin, tmp); if (*tmp!='n' && *tmp!='N') newrecord(no); } } /* menu alternative 3 */ void report(void) { int i; int brief; int rfile; int inactive; int j; int indirect; int i_save; line tmp; line name_match; printf("\n\nReport on file '%s'? (y/n) (n) ", REPORTFILE); get_line(stdin, tmp); rfile=(*tmp=='y' || *tmp=='Y'); if (rfile) printf("Print report on file '%s'\n", REPORTFILE); printf("\nShow only a brief listing? (y/n) (y) "); get_line(stdin, tmp); brief=(*tmp!='n' && *tmp!='N'); if (!brief) printf("Show verbose listing\n"); printf("\nShow inactive members also? (y/n) (n) "); get_line(stdin, tmp); inactive=(*tmp=='y' || *tmp=='Y'); if (inactive) printf("Show inactive members\n"); printf("\nName-matching (press RETURN for no matching) : "); get_line(stdin, name_match); if (*name_match) printf("Show only members whose names match '%s'\n", name_match); printf("\nSort output in name-order? (y/n) (n) "); get_line(stdin, tmp); indirect=(*tmp=='Y' || *tmp=='y'); if (indirect) { printf("Sort in name-order:"); if (!regstatus.sorted) do_sort(); printf(" ok.\n"); } if (rfile) { regstatus.rf=fopen(REPORTFILE, "a"); if (regstatus.rf==(FILE *)NULL) { printf("***ERROR: cannot append to reportfile "); printf("'%s'!\n", REPORTFILE); return; } if (brief) hdr_brief(regstatus.rf, "Report on members in Lysator", 0, 0); else hdr_verbose(regstatus.rf, "Report on members in Lysator"); } if (brief) hdr_brief(stdout, "Report on members in Lysator", 0, 0); else hdr_verbose(stdout, "Report on members in Lysator"); for(i=1;i<=regstatus.no_of_members;i++) { i_save=i; if (indirect) i=reg[i].index; j=1; if (!inactive && !reg[i].active) j--; if (j && *name_match && !match(reg[i].name, name_match)) j--; if (j) { int_to_bin(tmp, i); if (brief) { show_brief(stdout, i, tmp); if (rfile) show_brief(regstatus.rf, i, tmp); } else { show_verbose(stdout, i, tmp); if (rfile) show_verbose(regstatus.rf, i, tmp); } } i=i_save; } hdr_line(stdout, 0); if (rfile) { hdr_line(regstatus.rf, 0); fprintf(regstatus.rf, "\f\n"); fclose(regstatus.rf); } printf("\n\nPress RETURN to continue "); get_line(stdin, tmp); } /* menu alternative 4 */ void pg_blanks(void) { int i; regstatus.pg=fopen(POSTGIROFILE, "a"); if (regstatus.pg==(FILE *)NULL) { printf("***ERROR: cannot create postgirofile "); printf("'%s'!\n", POSTGIROFILE); return; } clear_record(0); for(i=0;i<12;i++) postgiro("", 0); fclose(regstatus.pg); } /* menu alternative 5 */ void change_fee(void) { line tmp; int i; printf("\n\nOld memberfee: %d, please enter new (RETURN to keep old) ", regstatus.memberfee); get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(tmp, &i)) printf("***ERROR: wrong input!\n"); else if (i<0) printf("***ERROR: wrong input!\n"); else regstatus.memberfee=i; } printf("\nNew memberfee: %d.\n" "Remember to recompile with the new memberfee.\n", regstatus.memberfee); } /* Read from fp until eof. Feed the characters to sm_putc. Interpret som escape sequences: %e expdate, printed as "1992-01-19". %n name, printed as "Cederqvist Per". %b member-no, printed as "110101000". %d member-no, printed as "#424". %f Full-dump of register contents. Leading and trailing newlines (one of each) are inserted. */ void convert_mail(int i, FILE *fp) { int c; line tmp; while((c = getc(fp)) != EOF) { if (c == '%') { c = getc(fp); switch(c) { case 'e': sprintf(tmp, "%d", reg[i].expdate); sm_putc('1'); sm_putc('9'); sm_putc(tmp[0]); sm_putc(tmp[1]); sm_putc('-'); sm_putc(tmp[2]); sm_putc(tmp[3]); sm_putc('-'); sm_putc(tmp[4]); sm_putc(tmp[5]); break; case 'n': sm_puts(reg[i].name); break; case 'b': int_to_bin(tmp, i); sm_puts(tmp); break; case 'd': sprintf(tmp, "#%d", i); sm_puts(tmp); break; case 'f': int_to_bin(tmp, i); sm_puts("\nMedlemsnr: "); sm_puts(tmp); sprintf(tmp, " (#%d)", i); sm_puts(tmp); sm_puts("\nNamn: "); sm_puts(reg[i].name); sm_puts("\nAddress: "); sm_puts(reg[i].adr[0]); sm_puts("\n "); sm_puts(reg[i].adr[1]); sm_puts("\nTel: "); sm_puts(reg[i].tel); sm_puts("\nEmail: "); sm_puts(reg[i].email); sm_puts("\nNot: "); sm_puts(reg[i].remark); sm_puts("\nLinje: "); sm_puts(reg[i].education); sm_puts("\nGarb via postverket: "); sm_puts(reg[i].want_garb ? "Ja" : "Nej"); sm_puts("\nKallelse till medlemsm|ten via postverket: "); sm_puts(reg[i].want_garb ? "Ja" : "Nej"); sm_puts("\nMedlemsavgift betald till och med: "); sprintf(tmp, "%d\n", reg[i].expdate); sm_puts(tmp); break; case EOF: printf("*** Unexpected EOF in escape sequence. " "Escape sequence ignored.\n"); break; default: printf("** Unknown escape sequence %%%c -- ignored.\n", c); break; } if (c == EOF) break; } else { sm_putc(c); } } } void do_send_mail(int i, line mailfilename) { FILE *fp; line email_copy; char *recipient; strcpy(email_copy, reg[i].email); recipient = strtok(email_copy, " ,"); if (recipient == NULL) { printf("** Recipient with no email -- %d %s", i, reg[i].name); return; } fp = fopen(mailfilename, "r"); if (fp == NULL) { printf("Can't open file "); fflush(stdout); perror(mailfilename); return; } if (sm_mail_from("kassor@lysator.liu.se (Mattias Wingstedt)")) { printf("Mail error 1.\n"); fclose(fp); return; } while (recipient != NULL) { if (sm_rcpt_to(recipient)) { printf("** Illegal email recipient -- %d <%s>", i, recipient); } recipient = strtok(NULL, " ,"); } sm_rcpt_to("kassor@lysator.liu.se (Mattias Wingstedt)"); if (sm_data()) { printf("Mail error 2.\n"); fclose(fp); return; } strcpy(email_copy, reg[i].email); recipient = strtok(email_copy, " ,"); if (recipient == NULL) { printf("Internal error -- no recipient. %d %s.\n", i, reg[i].name); fclose(fp); return; } while (recipient != NULL) { sm_puts("To: "); sm_puts(recipient); sm_putc('\n'); recipient = strtok(NULL, " ,"); } convert_mail(i, fp); if (sm_send()) { printf("Mail error 4.\n"); fclose(fp); return; } fclose(fp); } /* menu alternative 8 */ void new_payment(void) { int i; int rfile; int brief; int mail; line tmp; line mailfilename; int firstday; int lastday; mail=0; if (regstatus.operator) { /* not available to everyone... */ printf("\nName of file containing message to send via mail? (just " "RETURN for no mail)\n"); get_line(stdin, mailfilename); mail = (*mailfilename!='\0'); if (mail) printf("Send mail to remind using file '%s'\n", mailfilename); } if (mail) rfile = 1; else { printf("\n\nReport on file '%s'? (y/n) (n) ", REPORTFILE); get_line(stdin, tmp); rfile=(*tmp=='y' || *tmp=='Y'); } if (rfile) printf("Print report on file '%s'\n", REPORTFILE); printf("\nShow only a brief listing? (y/n) (y) "); get_line(stdin, tmp); brief = *tmp!='n' && *tmp!='N'; if (!brief) printf("Show verbose listing\n"); firstday = lastday = (-1); /* no restriction */ printf("First day (yymmdd) (RETURN for no selection) : "); get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(&tmp[0], &firstday)) { firstday = (-1); printf("***ERROR: illegal date entered: %s\n", tmp); } } printf("Last day (yymmdd) (RETURN for no selection) : "); get_line(stdin, tmp); if (*tmp) { if (ascii_to_int(&tmp[0], &lastday)) { lastday = (-1); printf("***ERROR: illegal date entered: %s\n", tmp); } } if (rfile) { regstatus.rf=fopen(REPORTFILE, "a"); if (regstatus.rf==(FILE *)NULL) { printf("***ERROR: cannot append to reportfile "); printf("'%s'!\n", REPORTFILE); return; } if (brief) hdr_brief(regstatus.rf, "Check on payments", 1, 1); else hdr_verbose(regstatus.rf, "Check on payments"); fprintf(regstatus.rf, "List expdates in range %d-%d.\n\n", firstday, lastday); if (mail) fprintf(regstatus.rf, "Sending mail from %s\n\n", mailfilename); } if (brief) hdr_brief(stdout, "Check on payments", 1, 1); else hdr_verbose(stdout, "Check on payments"); if (mail && !connected) { if (sm_conn()) { printf("*** Can't connect to SMTP port. No mail sent.\n"); mail = 0; } else connected = 1; } for(i=2;i<=regstatus.no_of_members;i++) { /* never charge #1 == TJH */ if (firstday >= 0 && reg[i].expdate < firstday) continue; if (lastday >= 0 && reg[i].expdate > lastday) continue; if (!reg[i].active) continue; int_to_bin(tmp, i); if (brief) { pay_brief(stdout, i, tmp, 0); if (rfile) pay_brief(regstatus.rf, i, tmp, 0); } else { show_verbose(stdout, i, tmp); if (rfile) show_verbose(regstatus.rf, i, tmp); } if (mail) { if (reg[i].email[0]=='\0') { printf("***ERROR: Member without email: '%s'\n", reg[i].name); } else { do_send_mail(i, mailfilename); } } } if (rfile) { hdr_line(regstatus.rf, brief); fprintf(regstatus.rf, "\f\n"); fclose(regstatus.rf); } hdr_line(stdout, brief); printf("\n\nPress RETURN to continue "); get_line(stdin, tmp); } /* menu alternative 9 */ void send_mail_to_user(void) { int rfile; int brief; int mail; line tmp; line mailfilename; int firstday; int lastday; int no; mail=0; if (regstatus.operator) { /* not available to everyone... */ printf("\nName of file containing message to send via mail? (just " "RETURN for no mail)\n"); get_line(stdin, mailfilename); mail = (*mailfilename!='\0'); if (mail) printf("Send mail to remind using file '%s'\n", mailfilename); } if (mail) rfile = 1; else { printf("\n\nReport on file '%s'? (y/n) (n) ", REPORTFILE); get_line(stdin, tmp); rfile=(*tmp=='y' || *tmp=='Y'); } if (rfile) printf("Print report on file '%s'\n", REPORTFILE); printf("\nShow only a brief listing? (y/n) (y) "); get_line(stdin, tmp); brief = *tmp!='n' && *tmp!='N'; if (!brief) printf("Show verbose listing\n"); printf("User number (blank for none) : "); get_line(stdin, tmp); if (*tmp == '#') { /* decimal no */ if (ascii_to_int(&tmp[1], &no)) { printf("***ERROR: illegal number entered: %s\n", tmp); return; } } else if (*tmp) { /* binary no */ if (bin_to_int(tmp, &no)) { printf("***ERROR: illegal number entered: %s\n", tmp); return; } } else return; if (rfile) { regstatus.rf=fopen(REPORTFILE, "a"); if (regstatus.rf==(FILE *)NULL) { printf("***ERROR: cannot append to reportfile "); printf("'%s'!\n", REPORTFILE); return; } if (brief) hdr_brief(regstatus.rf, "Check on payments", 1, 1); else hdr_verbose(regstatus.rf, "Check on payments"); fprintf(regstatus.rf, "List expdates in range %d-%d.\n\n", firstday, lastday); if (mail) fprintf(regstatus.rf, "Sending mail from %s\n\n", mailfilename); } if (brief) hdr_brief(stdout, "Check on payments", 1, 1); else hdr_verbose(stdout, "Check on payments"); if (mail && !connected) { if (sm_conn()) { printf("*** Can't connect to SMTP port. No mail sent.\n"); mail = 0; } else connected = 1; } if (!reg[no].active) { printf("*** Inactive user. No mail sent.\n"); return; } int_to_bin(tmp, no); if (brief) { pay_brief(stdout, no, tmp, 0); if (rfile) pay_brief(regstatus.rf, no, tmp, 0); } else { show_verbose(stdout, no, tmp); if (rfile) show_verbose(regstatus.rf, no, tmp); } if (mail) { if (reg[no].email[0]=='\0') { printf("***ERROR: Member without email: '%s'\n", reg[no].name); } else { do_send_mail(no, mailfilename); } } if (rfile) { hdr_line(regstatus.rf, brief); fprintf(regstatus.rf, "\f\n"); fclose(regstatus.rf); } hdr_line(stdout, brief); printf("\n\nPress RETURN to continue "); get_line(stdin, tmp); } /* guess what? */ void menu(void) { line tmp; while (1) { printf("\n\nMembers in Lysator (%s), date=%d, ", VERSION, regstatus.date); printf(" %d members\n\n", regstatus.no_of_members); printf("0. Register status\n"); printf("1. Check payment of members\n"); printf("2. Edit Members\n"); printf("3. Report on members\n"); printf("4. Postgiro - print a dozen blanks\n"); printf("5. Change memberfee\n"); if (regstatus.operator) printf("6. Exit and update datafile\n"); printf("7. Exit immediately\n"); printf("8. Send payment reminders.\n"); printf("9. Send mail to one user.\n"); printf("\n\nChoice (0-9) "); get_line(stdin, tmp); if (*tmp=='0') status(); if (*tmp=='1') payment(); if (*tmp=='2') edit_members(); if (*tmp=='3') report(); if (*tmp=='4') pg_blanks(); if (*tmp=='5') change_fee(); if (regstatus.operator) { if (*tmp=='6') break; } if (*tmp=='7') exit(0); if (*tmp=='8') new_payment(); if (*tmp=='9') send_mail_to_user(); } status(); } void main(int argc, char **argv) { sprintf(regstatus.df_filename, DATAFILE); if (argc==2) sprintf(regstatus.df_filename, argv[1]); if (argc>2) { fprintf(stderr, "usage: %s [file]\n", argv[0]); exit(1); } init_reg(); menu(); if (regstatus.operator) newreg(); }