This is receiptfilter.m in view mode; [Download] [Up]
// Copyright 1995 Paul Cardon. // Use is governed by the MiscKit license #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. */ /* * The recipient is the last commandline argument. * (Previously we looked at argv[1] but it could be that * sendmail is using a "-oi" option or something - which it * does when you use 3.3 Mail.app) */ destString = [MiscString newWithString: argv[argc-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); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.