forked from flashmob/go-guerrilla
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for ENHANCEDSTATUSCODES
Support for ENHANCEDSTATUSCODES (rfc3463) - Issue flashmob#34
- Loading branch information
Showing
8 changed files
with
326 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,20 +5,22 @@ import ( | |
"bytes" | ||
"crypto/tls" | ||
"encoding/json" | ||
log "github.com/Sirupsen/logrus" | ||
"github.com/flashmob/go-guerrilla" | ||
"github.com/flashmob/go-guerrilla/backends" | ||
test "github.com/flashmob/go-guerrilla/tests" | ||
"github.com/flashmob/go-guerrilla/tests/testcert" | ||
"github.com/spf13/cobra" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"runtime" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
log "github.com/Sirupsen/logrus" | ||
"github.com/flashmob/go-guerrilla" | ||
"github.com/flashmob/go-guerrilla/backends" | ||
test "github.com/flashmob/go-guerrilla/tests" | ||
"github.com/flashmob/go-guerrilla/tests/testcert" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var configJsonA = ` | ||
|
@@ -343,16 +345,20 @@ func TestServe(t *testing.T) { | |
ioutil.WriteFile("configJsonA.json", []byte(configJsonB), 0644) | ||
|
||
// test SIGHUP via the kill command | ||
ecmd := exec.Command("kill", "-HUP", string(data)) | ||
_, err = ecmd.Output() | ||
if err != nil { | ||
t.Error("could not SIGHUP", err) | ||
t.FailNow() | ||
} | ||
time.Sleep(time.Second) // allow sighup to do its job | ||
// did the pidfile change as expected? | ||
if _, err := os.Stat("./pidfile2.pid"); os.IsNotExist(err) { | ||
t.Error("pidfile not changed after sighup SIGHUP", err) | ||
// Would not work on windows as kill is not available. | ||
// TODO: Implement an alternative test for windows. | ||
if runtime.GOOS != "windows" { | ||
ecmd := exec.Command("kill", "-HUP", string(data)) | ||
_, err = ecmd.Output() | ||
if err != nil { | ||
t.Error("could not SIGHUP", err) | ||
t.FailNow() | ||
} | ||
time.Sleep(time.Second) // allow sighup to do its job | ||
// did the pidfile change as expected? | ||
if _, err := os.Stat("./pidfile2.pid"); os.IsNotExist(err) { | ||
t.Error("pidfile not changed after sighup SIGHUP", err) | ||
} | ||
} | ||
// send kill signal and wait for exit | ||
sigKill() | ||
|
@@ -681,7 +687,7 @@ func TestAllowedHostsEvent(t *testing.T) { | |
t.Error("Expected", expect, "but got", result) | ||
} else { | ||
if result, err = test.Command(conn, buffin, "RCPT TO:[email protected]"); err == nil { | ||
expect := "454 Error: Relay access denied: grr.la" | ||
expect := "454 4.1.1 Error: Relay access denied: grr.la" | ||
if strings.Index(result, expect) != 0 { | ||
t.Error("Expected:", expect, "but got:", result) | ||
} | ||
|
@@ -714,7 +720,7 @@ func TestAllowedHostsEvent(t *testing.T) { | |
t.Error("Expected", expect, "but got", result) | ||
} else { | ||
if result, err = test.Command(conn, buffin, "RCPT TO:[email protected]"); err == nil { | ||
expect := "250 OK" | ||
expect := "250 2.1.5 OK" | ||
if strings.Index(result, expect) != 0 { | ||
t.Error("Expected:", expect, "but got:", result) | ||
} | ||
|
@@ -791,7 +797,7 @@ func TestTLSConfigEvent(t *testing.T) { | |
t.Error("Expected", expect, "but got", result) | ||
} else { | ||
if result, err = test.Command(conn, buffin, "STARTTLS"); err == nil { | ||
expect := "220 Ready to start TLS" | ||
expect := "220 2.0.0 Ready to start TLS" | ||
if strings.Index(result, expect) != 0 { | ||
t.Error("Expected:", expect, "but got:", result) | ||
} else { | ||
|
@@ -894,7 +900,7 @@ func TestBadTLS(t *testing.T) { | |
t.Error("Expected", expect, "but got", result) | ||
} else { | ||
if result, err = test.Command(conn, buffin, "STARTTLS"); err == nil { | ||
expect := "220 Ready to start TLS" | ||
expect := "220 2.0.0 Ready to start TLS" | ||
if strings.Index(result, expect) != 0 { | ||
t.Error("Expected:", expect, "but got:", result) | ||
} else { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package response | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
const ( | ||
// ClassSuccess specifies that the DSN is reporting a positive delivery | ||
// action. Detail sub-codes may provide notification of | ||
// transformations required for delivery. | ||
ClassSuccess = 2 | ||
// ClassTransientFailure - a persistent transient failure is one in which the message as | ||
// sent is valid, but persistence of some temporary condition has | ||
// caused abandonment or delay of attempts to send the message. | ||
// If this code accompanies a delivery failure report, sending in | ||
// the future may be successful. | ||
ClassTransientFailure = 4 | ||
// ClassPermanentFailure - a permanent failure is one which is not likely to be resolved | ||
// by resending the message in the current form. Some change to | ||
// the message or the destination must be made for successful | ||
// delivery. | ||
ClassPermanentFailure = 5 | ||
) | ||
|
||
// codeMap for mapping Enhanced Status Code to Basic Code | ||
// Mapping according to https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml | ||
// This might not be entierly useful | ||
var codeMap = struct { | ||
m map[string]int | ||
}{m: map[string]int{ | ||
"2.1.5": 250, | ||
"2.3.0": 250, | ||
"2.5.0": 250, | ||
"2.6.4": 250, | ||
"2.6.8": 252, | ||
"2.7.0": 220, | ||
"4.1.1": 451, | ||
"4.1.8": 451, | ||
"4.2.4": 450, | ||
"4.3.0": 421, | ||
"4.3.1": 452, | ||
"4.3.2": 453, | ||
"4.4.1": 451, | ||
"4.4.2": 421, | ||
"4.4.3": 451, | ||
"4.4.5": 451, | ||
"4.5.0": 451, | ||
"4.5.1": 430, | ||
"4.5.3": 452, | ||
"4.5.4": 451, | ||
"4.7.0": 450, | ||
"4.7.1": 451, | ||
"4.7.12": 422, | ||
"4.7.15": 450, | ||
"4.7.24": 451, | ||
"5.1.1": 550, | ||
"5.1.3": 501, | ||
"5.1.8": 501, | ||
"5.1.10": 556, | ||
"5.2.2": 552, | ||
"5.2.3": 552, | ||
"5.3.0": 550, | ||
"5.3.4": 552, | ||
"5.4.3": 550, | ||
"5.5.0": 501, | ||
"5.5.1": 500, | ||
"5.5.2": 500, | ||
"5.5.4": 501, | ||
"5.5.6": 500, | ||
"5.6.3": 554, | ||
"5.6.6": 554, | ||
"5.6.7": 553, | ||
"5.6.8": 550, | ||
"5.6.9": 550, | ||
"5.7.0": 550, | ||
"5.7.1": 551, | ||
"5.7.2": 550, | ||
"5.7.4": 504, | ||
"5.7.8": 554, | ||
"5.7.9": 534, | ||
"5.7.10": 523, | ||
"5.7.11": 524, | ||
"5.7.13": 525, | ||
"5.7.14": 535, | ||
"5.7.15": 550, | ||
"5.7.16": 552, | ||
"5.7.17": 500, | ||
"5.7.18": 500, | ||
"5.7.19": 500, | ||
"5.7.20": 550, | ||
"5.7.21": 550, | ||
"5.7.22": 550, | ||
"5.7.23": 550, | ||
"5.7.24": 550, | ||
"5.7.25": 550, | ||
"5.7.26": 550, | ||
"5.7.27": 550, | ||
}} | ||
|
||
// DefaultMap contains defined default codes (RfC 3463) | ||
const ( | ||
OtherStatus = ".0.0" | ||
OtherAddressStatus = ".1.0" | ||
BadDestinationMailboxAddress = ".1.1" | ||
BadDestinationSystemAddress = ".1.2" | ||
BadDestinationMailboxAddressSyntax = ".1.3" | ||
DestinationMailboxAddressAmbiguous = ".1.4" | ||
DestinationMailboxAddressValid = ".1.5" | ||
MailboxHasMoved = ".1.6" | ||
BadSendersMailboxAddressSyntax = ".1.7" | ||
BadSendersSystemAddress = ".1.8" | ||
OtherOrUndefinedMailboxStatus = ".2.0" | ||
MailboxDisabled = ".2.1" | ||
MailboxFull = ".2.2" | ||
MessageLengthExceedsAdministrativeLimit = ".2.3" | ||
MailingListExpansionProblem = ".2.4" | ||
OtherOrUndefinedMailSystemStatus = ".3.0" | ||
MailSystemFull = ".3.1" | ||
SystemNotAcceptingNetworkMessages = ".3.2" | ||
SystemNotCapableOfSelectedFeatures = ".3.3" | ||
MessageTooBigForSystem = ".3.4" | ||
OtherOrUndefinedNetworkOrRoutingStatus = ".4.0" | ||
NoAnswerFromHost = ".4.1" | ||
BadConnection = ".4.2" | ||
RoutingServerFailure = ".4.3" | ||
UnableToRoute = ".4.4" | ||
NetworkCongestion = ".4.5" | ||
RoutingLoopDetected = ".4.6" | ||
DeliveryTimeExpired = ".4.7" | ||
OtherOrUndefinedProtocolStatus = ".5.0" | ||
InvalidCommand = ".5.1" | ||
SyntaxError = ".5.2" | ||
TooManyRecipients = ".5.3" | ||
InvalidCommandArguments = ".5.4" | ||
WrongProtocolVersion = ".5.5" | ||
OtherOrUndefinedMediaError = ".6.0" | ||
MediaNotSupported = ".6.1" | ||
ConversionRequiredAndProhibited = ".6.2" | ||
ConversionRequiredButNotSupported = ".6.3" | ||
ConversionWithLossPerformed = ".6.4" | ||
ConversionFailed = ".6.5" | ||
) | ||
|
||
// TODO: More defaults needed.... | ||
var defaultTexts = struct { | ||
m map[string]string | ||
}{m: map[string]string{ | ||
"2.0.0": "OK", | ||
"2.1.0": "OK", | ||
"2.1.5": "Recipient valid", | ||
"2.5.0": "OK", | ||
"4.5.3": "Too many recipients", | ||
"4.5.4": "Relay access denied", | ||
"5.5.1": "Invalid command", | ||
}} | ||
|
||
// CustomString builds an enhanced status code string using your custom string and basic code | ||
func CustomString(enhancedCode string, basicCode, class int, comment string) string { | ||
e := buildEnhancedResponseFromDefaultStatus(class, enhancedCode) | ||
return fmt.Sprintf("%d %s %s", basicCode, e, comment) | ||
} | ||
|
||
// String builds an enhanced status code string | ||
func String(enhancedCode string, class int) string { | ||
e := buildEnhancedResponseFromDefaultStatus(class, enhancedCode) | ||
basicCode := getBasicStatusCode(e) | ||
comment := defaultTexts.m[enhancedCode] | ||
|
||
if len(comment) == 0 { | ||
switch class { | ||
case 2: | ||
comment = "OK" | ||
case 4: | ||
comment = "Temporary failure." | ||
case 5: | ||
comment = "Permanent failure." | ||
} | ||
} | ||
return CustomString(enhancedCode, basicCode, class, comment) | ||
} | ||
|
||
func getBasicStatusCode(enhancedStatusCode string) int { | ||
if val, ok := codeMap.m[enhancedStatusCode]; ok { | ||
return val | ||
} | ||
// Fallback if code is not defined | ||
fb, _ := strconv.Atoi(fmt.Sprintf("%c00", enhancedStatusCode[0])) | ||
return fb | ||
} | ||
|
||
func buildEnhancedResponseFromDefaultStatus(c int, status string) string { | ||
// Construct code | ||
return fmt.Sprintf("%d%s", c, status) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package response | ||
|
||
import "testing" | ||
|
||
func TestClass(t *testing.T) { | ||
if ClassPermanentFailure != 5 { | ||
t.Error("ClassPermanentFailure is not 5") | ||
} | ||
if ClassTransientFailure != 4 { | ||
t.Error("ClassTransientFailure is not 4") | ||
} | ||
if ClassSuccess != 2 { | ||
t.Error("ClassSuccess is not 2") | ||
} | ||
} | ||
|
||
func TestGetBasicStatusCode(t *testing.T) { | ||
// Known status code | ||
a := getBasicStatusCode("2.5.0") | ||
if a != 250 { | ||
t.Errorf("getBasicStatusCode. Int \"%d\" not expected.", a) | ||
} | ||
|
||
// Unknown status code | ||
b := getBasicStatusCode("2.0.0") | ||
if b != 200 { | ||
t.Errorf("getBasicStatusCode. Int \"%d\" not expected.", b) | ||
} | ||
} | ||
|
||
// TestString for the String function | ||
func TestCustomString(t *testing.T) { | ||
// Basic testing | ||
a := CustomString(OtherStatus, 200, ClassSuccess, "Test") | ||
if a != "200 2.0.0 Test" { | ||
t.Errorf("CustomString failed. String \"%s\" not expected.", a) | ||
} | ||
|
||
// Default String | ||
b := String(OtherStatus, ClassSuccess) | ||
if b != "200 2.0.0 OK" { | ||
t.Errorf("String failed. String \"%s\" not expected.", b) | ||
} | ||
} | ||
|
||
func TestBuildEnhancedResponseFromDefaultStatus(t *testing.T) { | ||
a := buildEnhancedResponseFromDefaultStatus(ClassPermanentFailure, InvalidCommand) | ||
if a != "5.5.1" { | ||
t.Errorf("buildEnhancedResponseFromDefaultStatus failed. String \"%s\" not expected.", a) | ||
} | ||
} |
Oops, something went wrong.