home *** CD-ROM | disk | FTP | other *** search
- #import <stdio.h>
- #import <string.h>
- #import <sys/file.h>
- #import <misckit/MiscString.h>
- #import "PanelView.h"
-
- /*
- * $Version$
- * $Log: receiptfilter.m,v $
- * Revision 1.1 94/01/06 13:02:54 shayman
- * Initial revision
- *
- * Revision 1.2 94/03/01 13:01:34 pmarc
- * Second revision
- *
- */
-
-
- void main(int argc, char *argv[])
- {
-
- int scratch;
- FILE *rcfile;
- char tempfile[] = "/tmp/receiptfilter.XXXXXX";
- char linebuf[1024]; // Hope for no e-mail lines longer than this
- BOOL isReceipt = NO;
- BOOL pass = NO;
- BOOL filter = NO;
- BOOL autoAdd = NO;
- BOOL verboseLog = NO;
- BOOL logging = NO;
- BOOL fileOpen = YES; // NO only if there is an error.
- id destString, mailerString, rcPathString, logFileString, messageString;
-
- /*
- * We need an application object to be able to use NXRunAlertPanel()
- * and to run the modal loop for the custom alert panel I create.
- */
-
- NXApp = [Application new];
-
- /*
- * Copy stdin to a temp file (and make sure it goes away
- * when we're through with it).
- */
-
- scratch = mkstemp(tempfile);
- unlink(tempfile);
-
-
- /*
- * Read header lines from stdin and write them to temp file.
- * Search for telltale "Subject: Read Receipt".
- * Stop search when first blank line is reached.
- */
-
- while ( fgets(linebuf, sizeof(linebuf)-1, stdin) ) {
- write(scratch, linebuf, strlen(linebuf));
-
- if ( strcmp(linebuf, "\n") == 0 )
- break;
-
- if ( strcmp(linebuf, "Subject: Read Receipt\n") == 0 ) {
- isReceipt = YES;
- continue;
- }
-
- }
-
- /*
- * Read message lines from stdin and write them to temp file.
- * If the message is a read receipt, stick it in a MiscString
- * for logging use.
- */
-
- if (isReceipt) {
- messageString = [MiscString newWithString:""];
-
- while ( fgets(linebuf, sizeof(linebuf)-1, stdin) ) {
- write(scratch, linebuf, strlen(linebuf));
- [messageString cat:linebuf];
- }
- }
- else {
- while ( fgets(linebuf, sizeof(linebuf)-1, stdin) )
- write(scratch, linebuf, strlen(linebuf));
- }
-
- /*
- * Rewind scratch file. Close stdin. Dup scratch file.
- * Effect is that the scratch file is our process's new stdin.
- */
-
- lseek( scratch, 0, L_SET );
- close(0);
- dup(scratch);
-
- /*
- * Initialize the various MiscStrings used throughout the program.
- */
-
- destString = [MiscString newWithString: argv[1]];
-
- mailerString = [MiscString newWithString: "/usr/lib/sendmail"];
-
- rcPathString = [MiscString newWithString: "~/.rfrc"];
- [rcPathString replaceTildeWithHome];
-
- /*
- * Open ~/.rfrc. If it doesn't appear to exist, create it.
- * If there are any errors, display panels to warn the user,
- * and set flags so that it will use the default behavior
- * associated with an empty configuration file.
- */
-
- rcfile = fopen([rcPathString stringValue], "r");
- if (rcfile == NULL) {
- NXRunAlertPanel("receiptfilter error",
- "%s doesn't seem to exist.\nAttempting to create it...\n"
- "Mailing with %s.",
- "OK", NULL, NULL, [rcPathString stringValue],
- [mailerString stringValue]);
-
- rcfile = fopen([rcPathString stringValue], "w");
-
- if(rcfile == NULL) {
- NXRunAlertPanel("receiptfilter error",
- "Can't create %s.\nUsing default behavior.",
- "OK", NULL, NULL, [rcPathString stringValue]);
- fileOpen = NO;
- }
- }
-
- /*
- * Use MiscStrings to parse ~/.rfrc. Check for a mailer other than
- * /usr/lib/sendmail.
- * Check for logging and autoadd features.
- * If the message is a receipt, try to match the recipient's
- * address to one of the lines in ~/.rfrc.
- */
-
- if (fileOpen) {
-
- id recordString, firstField, lastField;
-
- recordString = [[MiscString alloc] init];
- firstField = [[MiscString alloc] init];
- lastField = [[MiscString alloc] init];
-
- while( fgets(linebuf, sizeof(linebuf) - 1, rcfile) ) {
- [recordString setStringValue:linebuf];
- [recordString trimWhiteSpaces];
- if ([recordString length] < 1)
- continue; // don't mess with blank lines
- [firstField setStringValue:[[recordString
- left:1 ]
- stringValueAndFree]];
- if ([firstField cmp:"M"] == 0) {
- [mailerString setStringValue:[[recordString
- removeFrom:0 length:1]
- stringValue]];
- [mailerString trimWhiteSpaces];
- continue;
- }
- if ([firstField cmp:"A"] == 0) {
- autoAdd = YES;
- continue;
- }
- if (([firstField cmp:"L"] == 0) || ([firstField cmp:"l"] == 0)) {
- logFileString = [[MiscString alloc] init];
- [logFileString setStringValue:[[recordString
- removeFrom:0 length:1]
- stringValue]];
- [logFileString trimWhiteSpaces];
- if ([logFileString length] < 1) continue;
- logging = YES;
- if ([firstField cmp:"L"] == 0) verboseLog = YES;
- [logFileString replaceTildeWithHome];
- continue;
- }
- if ( isReceipt ) {
- [lastField setStringValue:[[recordString
- removeFrom:0 length:1]
- stringValue]];
- [lastField trimWhiteSpaces];
- if ([lastField length] < 1)
- continue;
- if ([destString grep:[lastField stringValue] caseSensitive:YES
- before:nil middle:nil after:nil]) {
- if ([firstField cmp:"P"] == 0) {
- filter = NO;
- pass = YES;
- }
- if ([firstField cmp:"F"] == 0) {
- pass = NO;
- filter = YES;
- }
- }
- }
- }
-
- [recordString free];
- [firstField free];
- [lastField free];
- fclose(rcfile);
- }
-
- /*
- * If this message was a return receipt, filter or pass it as specified
- * in ~/.rfrc. If the recipient wasn't matched, allow the user to make
- * the choice.
- */
-
- if ( isReceipt ) {
- if (!pass && !filter) {
-
- /*
- * This part is kind of gross. I had two choices the first being
- % [ed. note: not "kind of". "is". :-) -don]
- % [ed. note: I concur! ;-) -paul]
- * to create a custom alert panel programatically, the second
- * being to use interface builder and figure out how to incorporate
- * the NIB into a command line executable. Each way is cleaner for
- * different reasons. I believe that the following way is just a
- * little better. Besides it's a good example for those who may
- * find themselves in the same situation even in a regular app.
- */
-
- id myPanel, panelView;
-
- NXRect panelRect,
- buttonRect1,
- buttonRect2,
- boxRect,
- textRect1,
- textRect2,
- checkRect;
-
- NXSetRect(&panelRect, 370.0, 360.0, 350.0, 175.0);
- myPanel = [[Panel alloc]
- initContent: &panelRect
- style: NX_TITLEDSTYLE
- backing: NX_BUFFERED
- buttonMask: 0
- defer: NO];
- [myPanel setTitle:"Read Receipt"];
-
- /*
- * Initialize the custom view and add the subviews (buttons,
- * text, etc.) to it.
- */
-
- panelView = [[PanelView alloc]
- initFrame: &panelRect
- memory: autoAdd];
-
- /*
- * Create a button which has an icon and key equivalent assigned
- * to it in addition to the normal text label.
- */
-
- NXSetRect(&buttonRect1, 274.0, 13.0, 55.0, 30.0);
- [[panelView superview] convertRectToSuperview: &buttonRect1];
- [panelView addSubview:[[[[Button alloc]
- initFrame: &buttonRect1
- title: "Yes"
- tag: 2
- target: panelView
- action: @selector(buttonPress:)
- key: 13
- enabled: YES]
- setImage:
- [NXImage findImageNamed:"NXreturnSign"]]
- setIconPosition: NX_ICONRIGHT]];
-
- /*
- * Typical button with text label.
- */
-
- NXSetRect(&buttonRect2, 194.0, 13.0, 55.0, 30.0);
- [[panelView superview] convertRectToSuperview: &buttonRect2];
- [panelView addSubview:[[Button alloc]
- initFrame: &buttonRect2
- title: "No"
- tag: 1
- target: panelView
- action: @selector(buttonPress:)
- key: 0
- enabled: YES]];
-
- /*
- * This is one of those horizontal lines you fake in IB with a box.
- * Guess what, this was the easiest one to do.
- */
-
- NXSetRect(&boxRect, 0.0, 130.0, 350.0, 2.0);
- [[panelView superview] convertRectToSuperview: &boxRect];
- [panelView addSubview:[[[Box alloc] initFrame: &boxRect]
- setTitlePosition: NX_NOTITLE]];
-
- /*
- * Now for a check box. It's just a standard button with the type set to
- * a particular value. Still IB presents it as a separate object.
- */
-
- NXSetRect(&checkRect, 35.0, 20.0, 100.0, 15.0);
- [[panelView superview] convertRectToSuperview: &checkRect];
- [panelView addSubview:[[[[[Button alloc]
- initFrame: &checkRect
- title: "Remember"
- tag: 0
- target: panelView
- action: @selector(rememberToggle)
- key: 0
- enabled: YES]
- setType: NX_SWITCH]
- setIconPosition: NX_ICONRIGHT]
- setAlignment: NX_CENTERED]];
-
- /*
- * We use the view to return the id for the check box since the View
- * class contains a List instance containing all of the subviews
- * alleviating the need for a new instance variable for each subview.
- * Adding or removing subviews requires less change to the code this
- * way too.
- */
-
- /*
- * Set the default appearance of the check box:
- * Selected, Unselected, or Disabled.
- */
-
- if (autoAdd)
- [[[panelView subviews] objectAt:3]
- performClick:[[panelView subviews] objectAt:3]];
- if (!fileOpen)
- [[[panelView subviews] objectAt:3]
- setEnabled: NO];
-
- /*
- * Set up the text fields that appear in the panel.
- */
-
- NXSetRect(&textRect1, 0.0, 142.0, 350.0, 22.0);
- [[panelView superview] convertRectToSuperview: &textRect1];
- [panelView addSubview:[[[[[[[TextField alloc]
- initFrame: &textRect1]
- setSelectable: NO]
- setBordered: NO]
- setBackgroundGray: NX_LTGRAY]
- setFont:[Font newFont:"Helvetica"
- size: 18.0]]
- setStringValue:
- " Send read receipt to:"]];
-
- NXSetRect(&textRect2, 0.0, 60.0, 350.0, 44.0);
- [[panelView superview] convertRectToSuperview: &textRect2];
- [panelView addSubview:[[[[[[[[TextField alloc]
- initFrame: &textRect2]
- setSelectable: NO]
- setBordered: NO]
- setBackgroundGray: NX_LTGRAY]
- setFont:[Font newFont:"Helvetica"
- size: 16.0]]
- setAlignment: NX_CENTERED]
- setStringValue:
- [destString stringValue]]];
-
- /*
- * Here is the important part:
- * First I set up the panel and bring it to the front of the
- * screen list.
- * For this to do anything in a command line program, it is then
- * necessary to activate the app.
- * Finally, the app is told to run a modal loop for the custom
- * alert panel which was implemented to stop the loop when the user
- * makes a choice.
- */
-
-
- [myPanel setContentView:panelView];
- [myPanel display];
- [myPanel makeKeyAndOrderFront:nil];
-
- [NXApp activateSelf:YES];
- [NXApp runModalFor:myPanel];
-
- /*
- * Query the view for the user's selections and get rid of the panel.
- */
-
- autoAdd = [panelView remember];
- pass = [panelView choice];
- filter = !pass;
- [myPanel free];
-
- /*
- * Now that you've seen the above mess, aren't you glad IB solves most
- * such problems! :-)
- % [ed. note: But it is good to bang your head on the wall once in
- % a while because it feels good when you stop. :-) -don]
- */
-
-
- /*
- * If desired, add the destination address to the configuration file.
- */
-
- if (autoAdd) {
- rcfile = fopen([rcPathString stringValue], "a");
-
- if (rcfile != NULL) {
- if (pass)
- fprintf(rcfile,"P %s\n",[destString stringValue]);
- if (filter)
- fprintf(rcfile,"F %s\n",[destString stringValue]);
- fclose(rcfile);
- }
- }
-
- }
-
- /*
- * Perform logging.
- * Display a panel if there is an error opening the logfile, but
- * don't let it prevent the message from being sent.
- */
-
- if (logging) {
- FILE *logFile;
-
- logFile = fopen([logFileString stringValue], "a");
- if (logFile != NULL) {
- fprintf(logFile,"*************************\n\n");
- fprintf(logFile,"Read receipt to: ");
- fprintf(logFile,"%s",[destString stringValue]);
- if (pass)
- fprintf(logFile," was passed.\n\n");
- else
- fprintf(logFile," was filtered.\n\n");
- if (verboseLog) {
- fprintf(logFile,"The message body follows:\n\n");
- fprintf(logFile,"%s",[messageString stringValue]);
- }
- fclose(logFile);
- }
- else
- NXRunAlertPanel("receiptfilter error",
- "Can't open logfile:\n%s.",
- "OK", NULL, NULL, [logFileString stringValue]);
- [logFileString free];
- }
- [messageString free];
- }
-
- [rcPathString free];
- [destString free];
-
- /*
- * Run mailer, let it pick up our arguments and standard input.
- * If the message is to be filtered, just exit the program.
- */
-
- if (!filter) {
- execv( [mailerString stringValue], argv );
-
- /*
- * shouldn't get here.
- */
-
- NXRunAlertPanel("receiptfilter error",
- "Couldn't execute mailer:\n%s.",
- "OK", NULL, NULL, [mailerString stringValue]);
- }
-
- [mailerString free];
-
- exit(0);
-
- }
-
-