GithubHelp home page GithubHelp logo

Comments (13)

dandare100 avatar dandare100 commented on June 19, 2024 2

lol, yes you are correct re Mikrotik.

The below is for a Java client.

image

I like your approach, my only question would be why would you add a switch (multipleRcptto )?
Why not just let it function in multiple mode as a normal/standard way ?

The client can send 1 message when the server is in multiple mode : no problem

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024 2

@dandare100 Thanks for java client examples ❤️ I think it can be configured behaviour against hardcoded. It will bring more flexibility and more context even from configuration, for example:

smtpmock.New(smtpmock.ConfigurationAttr{
  MultipleRcptto: true,
  MultipleMessageReceiving: true,
  NotRegisteredEmails: []string{"[email protected]"},
})

Also we should folowing current 'configuring flow' to save our main feature - 'Configurable multithreaded RFC compatible SMTP server'. Agree? 😃

If it okay I'll prepare to 2.x.x migration and will start developing this feature 👨‍💻

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024 1

@dandare100 Hahaha, I knew this question would be asked someday) Agree! Let's back for this point at the beginning of October 🍻

from go-smtp-mock.

benjamin-rood avatar benjamin-rood commented on June 19, 2024 1

👍
This is essential for testing emailer implementations. It should indeed be a string slice in sequential order of RCPT commands.
e.g. if the emailer implementation is sending the email to two recipents "a" and "b" on the "example" domain, then the RcpttoRequest() method on a Message should return:

[]string{"RCPT TO:<[email protected]>", "RCPT TO:<[email protected]>"}

from go-smtp-mock.

benjamin-rood avatar benjamin-rood commented on June 19, 2024 1

@bestwebua I'm happy to give this a go, if you can point me to the bits of the code that would need changing. The current implementation seems to do some "writing" that involves some significant indirection that I would rather not spend the time trying to unravel if you could just explain it to me?

from go-smtp-mock.

dandare100 avatar dandare100 commented on June 19, 2024 1

It seems like the client sends the first one and then each subsequent one after receiving a 250 Received from the server.

image

For my quick use on my project I just did this

// Writes handled RCPTTO result to session, message. Always returns true
func (handler *handlerRcptto) writeResult(isSuccessful bool, request, response string) bool {
	session, message := handler.session, handler.message
	if !isSuccessful {
		session.addError(errors.New(response))
	}
	message.rcpttoRequests = append(message.rcpttoRequests, request)
	message.rcpttoResponse, message.rcptto = response, isSuccessful
	session.writeResponse(response, handler.configuration.responseDelayRcptto)
	return true
}

and

// Structure for storing the result of SMTP client-server interaction. Context-included
// commands should be represented as request/response structure fields
type Message struct {
	heloRequest, heloResponse                         string
	mailfromRequest, mailfromResponse                 string
	rcpttoResponse                                    string
	rcpttoRequests                                    []string
	dataRequest, dataResponse                         string
	msgRequest, msgResponse                           string
	rsetRequest, rsetResponse                         string
	helo, mailfrom, rcptto, data, msg, rset, quitSent bool
}

I had no need for separate messages in my case. I understand your requirement to have multiple messages :-)

When adding the rcpt's, something to be careful of is the clearMessage call below in handler_rcptto.go.

I commented it out because my needs were still met.

I suspect that clear to have something to do with the "-multipleMessageReceiving" option but I didn't look into it further.

// Main RCPTTO handler runner
func (handler *handlerRcptto) run(request string) {
	handler.clearError()
	//I scheme it's because of the mutlimessage disting in the config
	//handler.clearMessage()

	if handler.isInvalidRequest(request) {
		return
	}

	handler.writeResult(true, request, handler.configuration.msgRcpttoReceived)
}

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024 1

@dandare100 Thanks for your knowledge sharing, Mark! I'll play with some stdlib SMTP clients from ruby, python and go to make sure that it is exactly expected multiple rcpt to behaviour. Have you screened logs from mikrotik built-in smtp client, am I right?

multipleMessageReceiving it's about ability to send multiple messages during one session. And entry point for this case is multipleMessageReceiving enabled flag and RSET command, please look on this test example:

go-smtp-mock/server_test.go

Lines 189 to 267 in 03e6fbe

t.Run("when complex successful session, multiple message receiving scenario enabled", func(t *testing.T) {
session, configuration := &sessionMock{}, createConfiguration()
configuration.multipleMessageReceiving = true
server := newServer(configuration)
session.On("writeResponse", configuration.msgGreeting, defaultSessionResponseDelay).Once().Return(nil)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("helo example.com", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgHeloReceived, configuration.responseDelayHelo).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("ehlo example.com", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgHeloReceived, configuration.responseDelayHelo).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("mail from: [email protected]", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgMailfromReceived, configuration.responseDelayMailfrom).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("rcpt to: [email protected]", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgRcpttoReceived, configuration.responseDelayRcptto).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("data", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgDataReceived, configuration.responseDelayData).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("readBytes").Once().Return([]uint8(".some message"), nil)
session.On("readBytes").Once().Return([]uint8(".\r\n"), nil)
session.On("writeResponse", configuration.msgMsgReceived, configuration.responseDelayMessage).Once().Return(nil)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("rset", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgRsetReceived, configuration.responseDelayRset).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("mail from: [email protected]", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgMailfromReceived, configuration.responseDelayMailfrom).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("rcpt to: [email protected]", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgRcpttoReceived, configuration.responseDelayRcptto).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("data", nil)
session.On("clearError").Once().Return(nil)
session.On("writeResponse", configuration.msgDataReceived, configuration.responseDelayData).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("readBytes").Once().Return([]uint8(".some message"), nil)
session.On("readBytes").Once().Return([]uint8(".\r\n"), nil)
session.On("writeResponse", configuration.msgMsgReceived, configuration.responseDelayMessage).Once().Return(nil)
session.On("setTimeout", defaultSessionTimeout).Once().Return(nil)
session.On("readRequest").Once().Return("quit", nil)
session.On("writeResponse", configuration.msgQuitCmd, configuration.responseDelayQuit).Once().Return(nil)
session.On("isErrorFound").Once().Return(false)
session.On("finish").Once().Return(nil)
server.handleSession(session)
assert.Equal(t, 2, len(server.Messages()))
})

session.On("readRequest").Once().Return("rset", nil)

What you think about next steps of implementation?:

  1. Add flag multipleRcptto
  2. Change Message structure to be able to save slice of strings:
type Message struct {
	heloRequest, heloResponse                   			string
	mailfromRequest, mailfromResponse           			string
	rcpttoRequestResponse                       			[][]string // new fileld with [["request", "response"]]
	dataRequest, dataResponse                   			string
	msgRequest, msgResponse                     			string
	rsetRequest, rsetResponse                         		string
	helo, mailfrom, rcptto, data, msg, rset, quitSent		bool
}
  1. Leave current behaviour for case when multipleRcptto is false
  2. When multipleRcptto is true we need to record all RCPT TO request/response pairs until receive succesful response and next smtp command has been passed. Also message clearing flow should be used for case when RCPT TO will passed after DATA and upper or when has been used RSET

P.S.: View for successful send messages we can add later. I believe it should be a slice of "successful mesages" where all multiple messages will be represented as independent messages. For instance, we have 1 single message and 1 multiple message with 2 receivers. Total count of this successful messages should be 3.

from go-smtp-mock.

dandare100 avatar dandare100 commented on June 19, 2024 1

Good points, I agree

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024 1

@dandare100, @benjamin-rood Already in latest (2.0.0) release 🚀

from go-smtp-mock.

dandare100 avatar dandare100 commented on June 19, 2024

@dandare100 Hahaha, I knew this question would be asked someday) Agree! Let's back for this point at the beginning of October 🍻

Cool, cheers

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024

@benjamin-rood Sounds good. Okay, but let's complete race condition fix firsly ;)

from go-smtp-mock.

bestwebua avatar bestwebua commented on June 19, 2024

Mates, @dandare100, @benjamin-rood! Could somebody provide an example of multiple RCPT TO command example? I expect that it should be somesing like:

And it will produce 2 different messages with different RCPT TO contexts.

from go-smtp-mock.

benjamin-rood avatar benjamin-rood commented on June 19, 2024

image

from go-smtp-mock.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.