#! /bin/sh /usr/share/dpatch/dpatch-run
## 10_spamass-milter-cvs-2006-06-17-rejecttext-and-internal-network2.dpatch by Herbert Straub <herbert@linuxhacker.at>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Customizeable reject text, internal networks and customizeable reject 
## DP: text and score

@DPATCH@

--- spamass-milt/spamass-milter.cpp.ORIG	2006-06-17 21:49:14.000000000 +0200
+++ spamass-milt/spamass-milter.cpp	2006-06-18 23:38:43.000000000 +0200
@@ -154,14 +154,21 @@
 
 int flag_debug = (1<<D_ALWAYS);
 bool flag_reject = false;
+bool flag_reject_intern = false;
 int reject_score = -1;
+int reject_score_intern = -1;
+bool dontmodifyspam_intern = false;    // Don't modify/add body or spam results headers, coming from internal
+bool dontmodify_intern = false;        // Don't add SA headers, ever, coming from internal
 bool dontmodifyspam = false;    // Don't modify/add body or spam results headers
 bool dontmodify = false;        // Don't add SA headers, ever.
 bool flag_sniffuser = false;
 char *defaultuser;				/* Username to send to spamc if there are multiple recipients */
 char *defaultdomain;			/* Domain to append if incoming address has none */
 char *spamdhost;
+char *rejecttext = NULL;				/* If we reject a mail, then use this text */
+char *rejecttext_intern = NULL;		/* If we reject a mail from the internal server, then use this text */
 struct networklist ignorenets;
+struct networklist internalnets;	/* Internal network, see options -s and -S */
 int spamc_argc;
 char **spamc_argv;
 bool flag_bucket = false;
@@ -181,7 +188,7 @@
 main(int argc, char* argv[])
 {
    int c, err = 0;
-   const char *args = "fd:mMp:P:r:u:D:i:b:B:e:x";
+   const char *args = "fd:mMp:P:r:R:s:S:u:D:i:n:oOp:b:B:e:x";
    char *sock = NULL;
    bool dofork = false;
    char *pidfilename = NULL;
@@ -193,6 +200,11 @@
 
    openlog("spamass-milter", LOG_PID, LOG_MAIL);
 
+
+    syslog(LOG_ERR, "argc: %d", argc);
+	for (int xy=0; xy<argc; xy++) {
+			syslog(LOG_ERR, "argv[%d]: %s", xy, argv[xy]);
+	}
 	/* Process command line options */
 	while ((c = getopt(argc, argv, args)) != -1) {
 		switch (c) {
@@ -222,6 +234,17 @@
 				dontmodifyspam = true;
 				smfilter.xxfi_flags &= ~(SMFIF_CHGBODY|SMFIF_CHGHDRS);
 				break;
+			case 'n':
+				debug(D_MISC, "Parsing internal network list");
+				parse_networklist(optarg, &internalnets);
+				break;
+			case 'o':
+				dontmodifyspam_intern = true;
+				break;
+			case 'O':
+				dontmodify_intern = true;
+				dontmodifyspam_intern = true;
+				break;
 			case 'p':
 				sock = strdup(optarg);
 				break;
@@ -232,6 +255,16 @@
 				flag_reject = true;
 				reject_score = atoi(optarg);
 				break;
+			case 'R':
+				rejecttext = strdup (optarg);
+				break;
+			case 's':
+				flag_reject_intern = true;
+				reject_score_intern = atoi(optarg);
+				break;
+			case 'S':
+				rejecttext_intern = strdup(optarg);
+				break;
 			case 'u':
 				flag_sniffuser = true;
 				defaultuser = strdup(optarg);
@@ -294,11 +327,22 @@
       cout << "   -f: fork into background" << endl;
       cout << "   -i: skip (ignore) checks from these IPs or netblocks" << endl;
       cout << "          example: -i 192.168.12.5,10.0.0.0/8,172.16.0.0/255.255.0.0" << endl;
+      cout << "   -I network: define the internal network. Works in combination with\n"
+	      "          the options -s and -S\n"
+	      "          Example: -I 192.168.12.5,10.0.0.0/8,172.16.0.0/255.255.0.0" << endl;
       cout << "   -m: don't modify body, Content-type: or Subject:" << endl;
       cout << "   -M: don't modify the message at all" << endl;
       cout << "   -P pidfile: Put processid in pidfile" << endl;
       cout << "   -r nn: reject messages with a score >= nn with an SMTP error.\n"
               "          use -1 to reject any messages tagged by SA." << endl;
+      cout << "   -R RejectText: using this Reject Text." << endl;
+      cout << "   -s nn: reject messages from the internal network\n"
+	      "          with a score >= nn with an SMTP error.\n"
+	      "          use -1 to reject any messages tagged by SA.\n"
+	      "          This require the specification of the internal network\n"
+	      "          with the -I option." << endl;
+      cout << "   -S RejectText: specifiy a alternate reject text for rejects\n"
+	      "          coming from the internal network" << endl;
       cout << "   -u defaultuser: pass the recipient's username to spamc.\n"
               "          Uses 'defaultuser' if there are multiple recipients." << endl;
       cout << "   -x: pass email address through alias and virtusertable expansion." << endl;
@@ -307,6 +351,14 @@
       exit(EX_USAGE);
    }
 
+	/* Set standard reject text */
+	if (rejecttext == NULL) {
+		rejecttext = strdup ("Blocked by SpamAssassin");
+	}
+	if (rejecttext_intern == NULL) {
+		rejecttext_intern = strdup ("Blocked by Spamassassin");
+	}
+
 	if (pidfilename)
 	{
 		unlink(pidfilename);
@@ -377,8 +429,20 @@
 	debug(D_UORI, "u_or_i: newstring: <%s>", newstring.c_str());
 
 	oldsize = callsetter(*assassin,setter)(newstring);
-      
-	if (!dontmodify)
+
+	struct context *sctx = (struct context*)smfi_getpriv(ctx);
+	bool internal_network=false;
+	if (ip_in_networklist(sctx->connect_ip, &internalnets)) {
+		internal_network=true;
+		debug(D_NET, "%s is in our internal list(update_or_insert)",
+	    		inet_ntoa(sctx->connect_ip));
+	}
+
+	/*
+	 * if -M then dontmodify headers
+	 * if -O and -n, then dont modify headers (mail from internal network)
+	 */
+	if (!(dontmodify || (dontmodify_intern && internal_network)))
 	{
 		if (newstring != oldstring)
 		{
@@ -422,14 +486,26 @@
   update_or_insert(assassin, ctx, assassin->spam_flag(), &SpamAssassin::set_spam_flag, "X-Spam-Flag");
   update_or_insert(assassin, ctx, assassin->spam_status(), &SpamAssassin::set_spam_status, "X-Spam-Status");
 
+  bool internal_network = false;
+  struct context *sctx = (struct context*)smfi_getpriv(ctx);
+  
+  if (ip_in_networklist(sctx->connect_ip, &internalnets))
+  {
+	internal_network = true;
+	debug(D_NET, "%s is in our internal list - accepting message",
+	    inet_ntoa(sctx->connect_ip));
+  }
+
   /* Summarily reject the message if SA tagged it, or if we have a minimum
      score, reject if it exceeds that score. */
-  if (flag_reject)
+  if (flag_reject || flag_reject_intern)
   {
 	bool do_reject = false;
-	if (reject_score == -1 && !assassin->spam_flag().empty())
+
+	if ((flag_reject && reject_score == -1 && !assassin->spam_flag().empty()) ||
+		(flag_reject_intern && reject_score_intern == -1 && !assassin->spam_flag().empty()))
 		do_reject = true;
-	if (reject_score != -1)
+	if (reject_score != -1 || reject_score_intern != -1)
 	{
 		int score, rv;
 		const char *spam_status = assassin->spam_status().c_str();
@@ -445,14 +521,15 @@
 		else 
 		{
 			debug(D_MISC, "SA score: %d", score);
-			if (score >= reject_score)
+			if ((flag_reject && score >= reject_score) ||
+				(flag_reject_intern && score >= reject_score_intern))
 				do_reject = true;
 		}
 	}
 	if (do_reject)
 	{
 		debug(D_MISC, "Rejecting");
-		smfi_setreply(ctx, "550", "5.7.1", "Blocked by SpamAssassin");
+		smfi_setreply(ctx, "550", "5.7.1", (internal_network) ?(rejecttext_intern) : (rejecttext));
 
 
 		if (flag_bucket)
@@ -560,7 +637,9 @@
   //  However, only issue the header replacement calls if the content has
   //  actually changed. If SA didn't change subject or content-type, don't
   //  replace here unnecessarily.
-  if (!dontmodifyspam && assassin->spam_flag().size()>0)
+  //
+  //  if -O and -n, then don't modiy Mail (mail from internal network)
+  if (!(dontmodifyspam || (dontmodifyspam_intern && internal_network)) && assassin->spam_flag().size()>0)
     {
 	  update_or_insert(assassin, ctx, assassin->subject(), &SpamAssassin::set_subject, "Subject");
 	  update_or_insert(assassin, ctx, assassin->content_type(), &SpamAssassin::set_content_type, "Content-Type");
