home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.bin / SourceCode / MiscKit1.2.6 / Examples / receiptfilter / receiptfilter.m < prev   
Encoding:
Text File  |  1994-03-03  |  12.6 KB  |  482 lines

  1. #import <stdio.h>
  2. #import <string.h>
  3. #import <sys/file.h>
  4. #import <misckit/MiscString.h>
  5. #import "PanelView.h"
  6.  
  7. /*
  8.  * $Version$
  9.  * $Log:    receiptfilter.m,v $
  10.  * Revision 1.1  94/01/06  13:02:54  shayman
  11.  * Initial revision
  12.  * 
  13.  * Revision 1.2  94/03/01  13:01:34  pmarc
  14.  * Second revision
  15.  * 
  16.  */
  17.  
  18.  
  19. void main(int argc, char *argv[])
  20. {
  21.  
  22.     int scratch;
  23.     FILE *rcfile;
  24.     char tempfile[] = "/tmp/receiptfilter.XXXXXX";
  25.     char linebuf[1024];        // Hope for no e-mail lines longer than this
  26.     BOOL isReceipt = NO;
  27.     BOOL pass = NO;
  28.     BOOL filter = NO;
  29.     BOOL autoAdd = NO;
  30.     BOOL verboseLog = NO;
  31.     BOOL logging = NO;
  32.     BOOL fileOpen = YES;     // NO only if there is an error.
  33.     id destString, mailerString, rcPathString, logFileString, messageString;
  34.     
  35.     /*
  36.      * We need an application object to be able to use NXRunAlertPanel()
  37.      * and to run the modal loop for the custom alert panel I create.
  38.      */ 
  39.     
  40.     NXApp = [Application new];
  41.     
  42.     /*
  43.      * Copy stdin to a temp file (and make sure it goes away
  44.      * when we're through with it). 
  45.      */
  46.      
  47.     scratch = mkstemp(tempfile);
  48.     unlink(tempfile);    
  49.  
  50.     
  51.     /*
  52.      * Read header lines from stdin and write them to temp file.
  53.      * Search for telltale "Subject: Read Receipt".
  54.      * Stop search when first blank line is reached.
  55.      */
  56.      
  57.     while ( fgets(linebuf, sizeof(linebuf)-1, stdin) ) {
  58.         write(scratch, linebuf, strlen(linebuf));
  59.     
  60.         if ( strcmp(linebuf, "\n") == 0 ) 
  61.             break;
  62.  
  63.         if ( strcmp(linebuf, "Subject: Read Receipt\n") == 0 ) {
  64.             isReceipt = YES;
  65.             continue;
  66.         }
  67.  
  68.     }
  69.  
  70.     /*
  71.      * Read message lines from stdin and write them to temp file.
  72.      * If the message is a read receipt, stick it in a MiscString 
  73.      * for logging use.
  74.      */
  75.     
  76.     if (isReceipt) {
  77.         messageString = [MiscString newWithString:""];
  78.     
  79.         while ( fgets(linebuf, sizeof(linebuf)-1, stdin) ) {
  80.             write(scratch, linebuf, strlen(linebuf));
  81.             [messageString cat:linebuf];
  82.         }
  83.     }
  84.     else {
  85.         while ( fgets(linebuf, sizeof(linebuf)-1, stdin) ) 
  86.             write(scratch, linebuf, strlen(linebuf));
  87.     }
  88.  
  89.     /*
  90.      * Rewind scratch file. Close stdin. Dup scratch file.
  91.      * Effect is that the scratch file is our process's new stdin.
  92.      */
  93.      
  94.     lseek( scratch, 0, L_SET );
  95.     close(0);
  96.     dup(scratch);
  97.     
  98.     /*
  99.      * Initialize the various MiscStrings used throughout the program.
  100.      */
  101.      
  102.     destString = [MiscString newWithString: argv[1]];
  103.     
  104.     mailerString = [MiscString newWithString: "/usr/lib/sendmail"];
  105.     
  106.     rcPathString = [MiscString newWithString: "~/.rfrc"];
  107.     [rcPathString replaceTildeWithHome];
  108.     
  109.     /*
  110.      * Open ~/.rfrc. If it doesn't appear to exist, create it.
  111.      * If there are any errors, display panels to warn the user,
  112.      * and set flags so that it will use the default behavior
  113.      * associated with an empty configuration file.
  114.      */
  115.     
  116.     rcfile = fopen([rcPathString stringValue], "r");
  117.     if (rcfile == NULL) {
  118.         NXRunAlertPanel("receiptfilter error", 
  119.             "%s doesn't seem to exist.\nAttempting to create it...\n"
  120.             "Mailing with %s.",
  121.             "OK", NULL, NULL, [rcPathString stringValue], 
  122.             [mailerString stringValue]);
  123.         
  124.         rcfile = fopen([rcPathString stringValue], "w");
  125.         
  126.         if(rcfile == NULL) {
  127.             NXRunAlertPanel("receiptfilter error", 
  128.                 "Can't create %s.\nUsing default behavior.",
  129.                 "OK", NULL, NULL, [rcPathString stringValue]);
  130.             fileOpen = NO;
  131.         }
  132.     }
  133.     
  134.     /*
  135.      * Use MiscStrings to parse ~/.rfrc. Check for a mailer other than
  136.      * /usr/lib/sendmail. 
  137.      * Check for logging and autoadd features.
  138.      * If the message is a receipt, try to match the recipient's 
  139.      * address to one of the lines in ~/.rfrc.
  140.      */
  141.     
  142.     if (fileOpen) {
  143.  
  144.         id recordString, firstField, lastField;
  145.      
  146.         recordString = [[MiscString alloc] init];
  147.         firstField = [[MiscString alloc] init];
  148.         lastField = [[MiscString alloc] init];
  149.             
  150.         while( fgets(linebuf, sizeof(linebuf) - 1, rcfile) ) {
  151.             [recordString setStringValue:linebuf];
  152.             [recordString trimWhiteSpaces];
  153.             if ([recordString length] < 1) 
  154.                 continue;        // don't mess with blank lines
  155.             [firstField setStringValue:[[recordString 
  156.                 left:1 ] 
  157.                 stringValueAndFree]];
  158.             if ([firstField cmp:"M"] == 0) {
  159.                 [mailerString setStringValue:[[recordString 
  160.                     removeFrom:0 length:1]
  161.                     stringValue]];
  162.                 [mailerString trimWhiteSpaces];
  163.                 continue;
  164.             }
  165.             if ([firstField cmp:"A"] == 0) {
  166.                 autoAdd = YES;
  167.                 continue;
  168.             }
  169.             if (([firstField cmp:"L"] == 0) || ([firstField cmp:"l"] == 0)) {
  170.                 logFileString = [[MiscString alloc] init];
  171.                 [logFileString setStringValue:[[recordString 
  172.                     removeFrom:0 length:1]
  173.                     stringValue]];
  174.                 [logFileString trimWhiteSpaces];
  175.                 if ([logFileString length] < 1) continue;
  176.                 logging = YES;
  177.                 if ([firstField cmp:"L"] == 0) verboseLog = YES;
  178.                 [logFileString replaceTildeWithHome];
  179.                 continue;
  180.             }
  181.             if ( isReceipt ) {
  182.                 [lastField setStringValue:[[recordString 
  183.                     removeFrom:0 length:1]
  184.                     stringValue]];
  185.                 [lastField trimWhiteSpaces];
  186.                 if ([lastField length] < 1)
  187.                     continue;
  188.                 if ([destString grep:[lastField stringValue] caseSensitive:YES 
  189.                     before:nil middle:nil after:nil]) {
  190.                     if ([firstField cmp:"P"] == 0) {
  191.                         filter = NO;
  192.                         pass = YES;
  193.                     }
  194.                     if ([firstField cmp:"F"] == 0) {
  195.                         pass = NO;
  196.                         filter = YES;
  197.                     }
  198.                 }
  199.             }
  200.         }
  201.  
  202.         [recordString free];
  203.         [firstField free];
  204.         [lastField free];
  205.         fclose(rcfile);
  206.     }
  207.     
  208.     /*
  209.      * If this message was a return receipt, filter or pass it as specified
  210.      * in ~/.rfrc.  If the recipient wasn't matched, allow the user to make
  211.      * the choice.
  212.      */
  213.      
  214.     if ( isReceipt ) {
  215.         if (!pass && !filter) {
  216.  
  217.     /*
  218.      * This part is kind of gross. I had two choices the first being
  219.      %    [ed. note:  not "kind of".  "is". :-)  -don]
  220.      %      [ed. note:  I concur!  ;-)  -paul]
  221.      * to create a custom alert panel programatically, the second
  222.      * being to use interface builder and figure out how to incorporate
  223.      * the NIB into a command line executable. Each way is cleaner for
  224.      * different reasons. I believe that the following way is just a 
  225.      * little better. Besides it's a good example for those who may
  226.      * find themselves in the same situation even in a regular app.
  227.      */
  228.  
  229.             id myPanel, panelView;
  230.  
  231.             NXRect panelRect, 
  232.                    buttonRect1, 
  233.                    buttonRect2, 
  234.                    boxRect, 
  235.                    textRect1, 
  236.                    textRect2,
  237.                    checkRect;
  238.         
  239.             NXSetRect(&panelRect, 370.0, 360.0, 350.0, 175.0);
  240.             myPanel = [[Panel alloc]
  241.                         initContent:     &panelRect
  242.                         style:            NX_TITLEDSTYLE
  243.                         backing:        NX_BUFFERED
  244.                         buttonMask:        0
  245.                         defer:            NO];
  246.             [myPanel setTitle:"Read Receipt"];
  247.     
  248.     /*
  249.      * Initialize the custom view and add the subviews (buttons, 
  250.      * text, etc.) to it.
  251.      */
  252.     
  253.             panelView = [[PanelView alloc] 
  254.                             initFrame: &panelRect
  255.                             memory: autoAdd];
  256.             
  257.     /*
  258.      * Create a button which has an icon and key equivalent assigned
  259.      * to it in addition to the normal text label.
  260.      */        
  261.                     
  262.             NXSetRect(&buttonRect1, 274.0, 13.0, 55.0, 30.0);
  263.             [[panelView superview] convertRectToSuperview: &buttonRect1];
  264.             [panelView addSubview:[[[[Button alloc]
  265.                                     initFrame: &buttonRect1
  266.                                     title: "Yes"
  267.                                     tag: 2
  268.                                     target: panelView
  269.                                     action: @selector(buttonPress:)
  270.                                     key: 13
  271.                                     enabled: YES]
  272.                                     setImage: 
  273.                                       [NXImage findImageNamed:"NXreturnSign"]]
  274.                                     setIconPosition: NX_ICONRIGHT]];
  275.  
  276.     /*
  277.      * Typical button with text label.
  278.      */
  279.                                     
  280.             NXSetRect(&buttonRect2, 194.0, 13.0, 55.0, 30.0);
  281.             [[panelView superview] convertRectToSuperview: &buttonRect2];
  282.             [panelView addSubview:[[Button alloc]
  283.                                     initFrame: &buttonRect2
  284.                                     title: "No"
  285.                                     tag: 1
  286.                                     target: panelView
  287.                                     action: @selector(buttonPress:)
  288.                                     key: 0
  289.                                     enabled: YES]];
  290.  
  291.     /*
  292.      * This is one of those horizontal lines you fake in IB with a box.
  293.      * Guess what, this was the easiest one to do.
  294.      */
  295.  
  296.             NXSetRect(&boxRect, 0.0, 130.0, 350.0, 2.0);
  297.             [[panelView superview] convertRectToSuperview: &boxRect];
  298.             [panelView addSubview:[[[Box alloc] initFrame: &boxRect]
  299.                                      setTitlePosition: NX_NOTITLE]];
  300.  
  301.     /*
  302.      * Now for a check box. It's just a standard button with the type set to
  303.      * a particular value. Still IB presents it as a separate object.
  304.      */
  305.  
  306.             NXSetRect(&checkRect, 35.0, 20.0, 100.0, 15.0);
  307.             [[panelView superview] convertRectToSuperview: &checkRect];
  308.             [panelView addSubview:[[[[[Button alloc]
  309.                                     initFrame: &checkRect
  310.                                     title: "Remember"
  311.                                     tag: 0
  312.                                     target: panelView
  313.                                     action: @selector(rememberToggle)
  314.                                     key: 0
  315.                                     enabled: YES]
  316.                                     setType: NX_SWITCH]
  317.                                     setIconPosition: NX_ICONRIGHT]
  318.                                     setAlignment: NX_CENTERED]];
  319.  
  320.     /* 
  321.      * We use the view to return the id for the check box since the View
  322.      * class contains a List instance containing all of the subviews
  323.      * alleviating the need for a new instance variable for each subview.
  324.      * Adding or removing subviews requires less change to the code this
  325.      * way too.
  326.      */
  327.      
  328.      /*
  329.       * Set the default appearance of the check box:
  330.       * Selected, Unselected, or Disabled.
  331.       */  
  332.  
  333.             if (autoAdd)
  334.                 [[[panelView subviews] objectAt:3] 
  335.                 performClick:[[panelView subviews] objectAt:3]];
  336.             if (!fileOpen)
  337.                 [[[panelView subviews] objectAt:3]
  338.                 setEnabled: NO];    
  339.             
  340.     /*
  341.      * Set up the text fields that appear in the panel.
  342.      */
  343.  
  344.             NXSetRect(&textRect1, 0.0, 142.0, 350.0, 22.0);
  345.             [[panelView superview] convertRectToSuperview: &textRect1];
  346.             [panelView addSubview:[[[[[[[TextField alloc]
  347.                                     initFrame: &textRect1]
  348.                                     setSelectable: NO]
  349.                                     setBordered: NO]
  350.                                     setBackgroundGray: NX_LTGRAY]
  351.                                     setFont:[Font newFont:"Helvetica"
  352.                                                   size: 18.0]]
  353.                                     setStringValue:
  354.                                         "   Send read receipt to:"]];
  355.  
  356.             NXSetRect(&textRect2, 0.0, 60.0, 350.0, 44.0);
  357.             [[panelView superview] convertRectToSuperview: &textRect2];
  358.             [panelView addSubview:[[[[[[[[TextField alloc]
  359.                                     initFrame: &textRect2]
  360.                                     setSelectable: NO]
  361.                                     setBordered: NO]
  362.                                     setBackgroundGray: NX_LTGRAY]
  363.                                     setFont:[Font newFont:"Helvetica"
  364.                                                   size: 16.0]]
  365.                                     setAlignment: NX_CENTERED]
  366.                                     setStringValue:
  367.                                         [destString stringValue]]];
  368.  
  369.     /*
  370.      * Here is the important part:
  371.      * First I set up the panel and bring it to the front of the 
  372.      * screen list.
  373.      * For this to do anything in a command line program, it is then
  374.      * necessary to activate the app.
  375.      * Finally, the app is told to run a modal loop for the custom
  376.      * alert panel which was implemented to stop the loop when the user
  377.      * makes a choice.
  378.      */
  379.  
  380.  
  381.             [myPanel setContentView:panelView];
  382.             [myPanel display];
  383.             [myPanel makeKeyAndOrderFront:nil];
  384.  
  385.             [NXApp activateSelf:YES];
  386.             [NXApp runModalFor:myPanel];
  387.     
  388.     /*
  389.      * Query the view for the user's selections and get rid of the panel.
  390.      */
  391.     
  392.             autoAdd = [panelView remember];
  393.             pass = [panelView choice];
  394.             filter = !pass;
  395.             [myPanel free];
  396.     
  397.     /*
  398.      * Now that you've seen the above mess, aren't you glad IB solves most
  399.      * such problems! :-)
  400.      %    [ed. note:  But it is good to bang your head on the wall once in
  401.      %             a while because it feels good when you stop.  :-)  -don]
  402.      */
  403.     
  404.                                 
  405.     /*
  406.      * If desired, add the destination address to the configuration file.
  407.      */
  408.  
  409.             if (autoAdd) {
  410.                 rcfile = fopen([rcPathString stringValue], "a");
  411.                 
  412.                 if (rcfile != NULL) {
  413.                     if (pass) 
  414.                         fprintf(rcfile,"P %s\n",[destString stringValue]);
  415.                     if (filter)
  416.                         fprintf(rcfile,"F %s\n",[destString stringValue]);
  417.                     fclose(rcfile);
  418.                 }
  419.             }
  420.             
  421.         }
  422.         
  423.     /*
  424.      * Perform logging.
  425.      * Display a panel if there is an error opening the logfile, but
  426.      * don't let it prevent the message from being sent.
  427.      */    
  428.     
  429.         if (logging) {
  430.             FILE *logFile;
  431.                 
  432.             logFile = fopen([logFileString stringValue], "a");
  433.             if (logFile != NULL) {
  434.                 fprintf(logFile,"*************************\n\n");
  435.                 fprintf(logFile,"Read receipt to: ");
  436.                 fprintf(logFile,"%s",[destString stringValue]);
  437.                 if (pass)
  438.                     fprintf(logFile," was passed.\n\n");
  439.                 else
  440.                     fprintf(logFile," was filtered.\n\n");
  441.                 if (verboseLog) {
  442.                     fprintf(logFile,"The message body follows:\n\n");    
  443.                     fprintf(logFile,"%s",[messageString stringValue]);
  444.                 }
  445.                 fclose(logFile);
  446.             }
  447.             else 
  448.                 NXRunAlertPanel("receiptfilter error", 
  449.                     "Can't open logfile:\n%s.",
  450.                     "OK", NULL, NULL, [logFileString stringValue]);
  451.             [logFileString free];
  452.         }        
  453.         [messageString free];
  454.     }
  455.  
  456.     [rcPathString free];
  457.     [destString free];
  458.     
  459.     /*
  460.      * Run mailer, let it pick up our arguments and standard input.
  461.      * If the message is to be filtered, just exit the program.
  462.      */
  463.  
  464.     if (!filter) { 
  465.         execv( [mailerString stringValue], argv );
  466.  
  467.         /*
  468.          * shouldn't get here.
  469.          */
  470.     
  471.         NXRunAlertPanel("receiptfilter error", 
  472.             "Couldn't execute mailer:\n%s.",
  473.             "OK", NULL, NULL, [mailerString stringValue]);
  474.     }
  475.     
  476.     [mailerString free];
  477.     
  478.     exit(0);
  479.     
  480. }
  481.  
  482.