NetClients.ESMTP package

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

NetClients.ESMTP package

Joachim Jaeckel
Hello,

I have slightly modified the SMTPClient and SMTPProtocolInterpreter
class and packaged an ESMTP.st (as NetClients.ESMTP) (with the Base64
code as NetClients.MIME.Base64).

The ESMTP Client is ready for future enhancements, but does his job for
my purpose (mostly... Currently, I couldn't mail to GMX.net, but to my
company, which is also very restritive with SPAMs... have to investiage
it further.)

It is currently only capable of using the AUTH LOGIN method for the
esmtp protocol, but others could be implemented (with a bit more work,
for deciding which authentication method should be used in sending mails...)

With the ESMTPClient, mail sending was like the way, you could use the
SMTPClient, with a little addition. You should provide the username and
password to the client before sending the mail.

(The following example is from the "Mail scripting" example from Stephen
Woolerton, enhanced by the line for username/password).

[client := NetClients.ESMTP.ESMTPClient connectToHost: 'mx.example.com'.
      client username: 'myUserName' password: 'myPassword'.
        [client sendMessage: message.

If you are interested, drop me a line, would be glad, if I could
contribute it to gnu-smalltalk.

(it was not such a big modification, after having the base64 code.)

Best regards,
Joachim.


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Chun, Sungjin
This is COOL!!!, I like it.


On Jun 27, 2009, at 10:33 AM, Joachim Jaeckel wrote:

> Hello,
>
> I have slightly modified the SMTPClient and SMTPProtocolInterpreter  
> class and packaged an ESMTP.st (as NetClients.ESMTP) (with the  
> Base64 code as NetClients.MIME.Base64).
>
> The ESMTP Client is ready for future enhancements, but does his job  
> for my purpose (mostly... Currently, I couldn't mail to GMX.net, but  
> to my company, which is also very restritive with SPAMs... have to  
> investiage it further.)
>
> It is currently only capable of using the AUTH LOGIN method for the  
> esmtp protocol, but others could be implemented (with a bit more  
> work, for deciding which authentication method should be used in  
> sending mails...)
>
> With the ESMTPClient, mail sending was like the way, you could use  
> the SMTPClient, with a little addition. You should provide the  
> username and password to the client before sending the mail.
>
> (The following example is from the "Mail scripting" example from  
> Stephen Woolerton, enhanced by the line for username/password).
>
> [client := NetClients.ESMTP.ESMTPClient connectToHost:  
> 'mx.example.com'.
>     client username: 'myUserName' password: 'myPassword'.
> [client sendMessage: message.
>
> If you are interested, drop me a line, would be glad, if I could  
> contribute it to gnu-smalltalk.
>
> (it was not such a big modification, after having the base64 code.)
>
> Best regards,
> Joachim.
>
>
> _______________________________________________
> help-smalltalk mailing list
> [hidden email]
> http://lists.gnu.org/mailman/listinfo/help-smalltalk
>



_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Paolo Bonzini-3
In reply to this post by Joachim Jaeckel
> If you are interested, drop me a line, would be glad, if I could
> contribute it to gnu-smalltalk.

Yeah, I was going to ask, where is the code? ;-)

It would be great to just include it in SMTP, trying to send EHLO first
and HELO if it fails.  You can add an exception for no ESMTP and raise
it in the login methods.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Joachim Jaeckel
> Yeah, I was going to ask, where is the code? ;-)

Here it is.

> It would be great to just include it in SMTP, trying to send EHLO first
> and HELO if it fails.  You can add an exception for no ESMTP and raise
> it in the login methods.

That is a good idea. Today, there's only little time for me (and I have
to look into the exception thing in smalltalk a little bit deeper...)

And the notes from Stefan haven't it made 'till now into the Base64 code...

But to have something to present, here's the early form of code.

Paolo, I have pasted some of the Copyrights which I found in the
gst-sources to the beginning of the files, hope it's ok.

(The package.xml is only for having it as a package in my current
project. If it goes into gst, than it would be a good idea to have it in
the NetClients.star package, as the others - I think...)


Regards,
Joachim.

"======================================================================
|
|   Base64 class declarations
|
|
 ======================================================================"

"======================================================================
|
| Copyright 2009
| Written by Joachim Jaeckel
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
|
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LIB.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02110-1301, USA.
|
 ======================================================================"



Namespace current: NetClients.MIME [

Object subclass: Base64 [

    Base64 class >> encode: aString [
        | chars i j tripple pads c1 c2 c3 c4 b64string |
        chars := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.
        b64string := String new.

        pads := 0.
        i := 1.

        [i <= aString size] whileTrue: [
            j := i +2.
            (j > aString size) ifTrue: [j := aString size].
            tripple := aString copyFrom: i to: j.

            (tripple size < 3) ifTrue: [
                pads := 3 - tripple size.
                1 to: pads do: [ :n |
                    tripple growBy: 1.
                    tripple at: (tripple size) put: Character nul.
                ]
            ].

            b64string growBy: 4.
            c1 := (tripple at: 1) asInteger bitShift: -2.
            b64string at: (b64string size -3) put: (chars at: c1 +1).

            c2 := (((tripple at: 1) asInteger bitAnd: 3) bitShift: 4) bitOr: ((tripple at: 2) asInteger bitShift: -4).
            b64string at: (b64string size -2) put: (chars at: c2 +1).

            c3 := (((tripple at: 2) asInteger bitAnd: 15) bitShift: 2) bitOr: ((tripple at: 3) asInteger bitShift: -6).
            b64string at: (b64string size -1) put: (chars at: c3 +1).

            c4 := (tripple at: 3) asInteger bitAnd: 63.
            b64string at: (b64string size) put: (chars at: c4 +1).

            i := i +3.
        ].

        (pads > 0) ifTrue: [
            1 to: pads do: [ :n |
                b64string at: (b64string size -n +1) put: $=.
            ]
        ].

        ^b64string
    ]
]

]

"======================================================================
|
|   ESMTP protocol support
|
|
 ======================================================================"

"======================================================================
|
| Based on code copyright (c) Kazuki Yasumatsu, and in the public domain
| Copyright (c) 2002 Free Software Foundation, Inc.
| Adapted by Paolo Bonzini.
| Modified for ESMTP by Joachim Jaeckel.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
|
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LIB.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02110-1301, USA.
|
 ======================================================================"


Namespace current: NetClients.ESMTP [

NetClients.SMTP.SMTPClient subclass: ESMTPClient [

    protocolInterpreter [
        <category: 'private'>
        ^ESMTPProtocolInterpreter
    ]

    sendMailStream: aStream sender: sender recipients: recipients [
        <category: 'accessing'>
        self connectIfClosed.
        self clientPI esmtpHello: self getHostname.
        self clientPI esmtpAuthLogin: self username.
        self clientPI esmtpPassword: self password.
        self clientPI smtpMail: sender.
        recipients do: [:addr | self clientPI smtpRecipient: addr].
        self clientPI smtpData: [self clientPI sendMessageWithPeriod: aStream]
    ]

    sendMessage: aMessage sender: sender recipients: recipients [
        <category: 'accessing'>
        self connectIfClosed.
        self clientPI esmtpHello: self getHostname.
        self clientPI esmtpAuthLogin: self username.
        self clientPI esmtpPassword: self password.
        self clientPI smtpMail: sender.
        recipients do: [:addr | self clientPI smtpRecipient: addr].
        self clientPI smtpData: [aMessage printMessageOnClient: self clientPI]
    ]
]

NetClients.SMTP.SMTPProtocolInterpreter subclass: ESMTPProtocolInterpreter [

    esmtpHello: domain [
        <category: 'esmtp protocol'>
        self
            nextPutAll: 'EHLO ',domain;
            nl.
        self checkResponse
    ]

    esmtpAuthLogin: user [
        <category: 'esmtp protocol'>
        self
            nextPutAll: 'AUTH LOGIN ', (NetClients.MIME.Base64 encode: user);
            nl.
        self checkResponse.
        "Here comes hopefully the response 334"
    ]

    esmtpPassword: password [
        <category: 'esmtp protocol'>
        self
            nextPutAll: (NetClients.MIME.Base64 encode: password);
            nl.
        self checkResponse
        "Here comes hopefully the response 235"
    ]

    checkResponse: response ifError: errorBlock [
        <category: 'private'>
        | status |
        status := response status.

        "Positive Completion reply"
        status = 211
            ifTrue:
            ["System status, or system help reply"

            ^self].
        status = 214
            ifTrue:
            ["Help message"

            ^self].
        status = 220
            ifTrue:
            ["Service ready"

            ^self].
        status = 221
            ifTrue:
            ["Service closing channel"

            ^self].
        status = 235
            ifTrue:
            ["Authentication successful"

            ^self].
        status = 250
            ifTrue:
            ["Requested mail action okay"

            ^self].
        status = 251
            ifTrue:
            ["User not local; will forward"

            ^self].
        status = 334
            ifTrue:
            ["Authetication password"

            ^self].

        "Positive Intermediate reply"
        status = 354
            ifTrue:
            ["Start mail input"

            ^self].

        "Transient Negative Completion reply"
        status = 421
            ifTrue:
            ["Service not available"

            ^errorBlock value].
        status = 450
            ifTrue:
            ["Requested mail action not taken"

            ^errorBlock value].
        status = 451
            ifTrue:
            ["Requested action aborted"

            ^errorBlock value].
        status = 452
            ifTrue:
            ["Requested action not taken"

            ^errorBlock value].

        "Permanent Negative Completion reply"
        status = 500
            ifTrue:
            ["Syntax error"

            ^errorBlock value].
        status = 501
            ifTrue:
            ["Syntax error in parameters"

            ^errorBlock value].
        status = 502
            ifTrue:
            ["Command not implemented"

            ^errorBlock value].
        status = 503
            ifTrue:
            ["Bad sequence of commands"

            ^errorBlock value].
        status = 504
            ifTrue:
            ["Command parameter not implemented"

            ^errorBlock value].
        status = 550
            ifTrue:
            ["Requested action not taken"

            ^errorBlock value].
        status = 551
            ifTrue:
            ["User not local; please try"

            ^errorBlock value].
        status = 552
            ifTrue:
            ["Requested mail action aborted"

            ^errorBlock value].
        status = 553
            ifTrue:
            ["Requested action not taken"

            ^errorBlock value].
        status = 554
            ifTrue:
            ["Transaction failed"

            ^errorBlock value].
   
        "Unknown status"
        ^errorBlock value

        "334 password z.B."
        "235 Authentication successfull"
    ]
]

]

<package>
  <name>NetClientsExtensions</name>
  <prereq>NetClients</prereq>

  <filein>Base64.st</filein>
  <filein>ESMTP.st</filein>

  <file>Base64.st</file>
  <file>ESMTP.st</file>
</package>

PackageLoader fileInPackage: 'NetClientsExtensions'.

Object subclass: EsmtpTest [

    sendEmailMsgTo: recipient from: sender through: host as: username with: password [
        | mailmsg message client |

        mailmsg :=
'From: %1
To: %2
Subject: %3

%4
' bindWith: sender
  with: recipient
  with: 'Testmail from Smalltalk'
  with: 'This is a genereated testmail with the ESMTP package!'.

    message := NetClients.MIME.MimeEntity readFrom: mailmsg readStream.
    [client := NetClients.ESMTP.ESMTPClient connectToHost: host.
     client username: username password: password.
        [client sendMessage: message.
         Transcript showCr: 'send mail']
        ensure: [client close] ]
    on: Error
    do: [:sig | Transcript showCr: 'Error: Couldnt connect to SMTP server.'].
    ]

]

EsmtpTest new
    sendEmailMsgTo: '[hidden email]'
    from: '[hidden email]'
    through: 'mail.example.com'
    as: '[hidden email]'
    with: 'topSecret'.

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Stefan Schmiedl
On Sat, 27 Jun 2009 14:30:03 +0200
Joachim Jaeckel <[hidden email]> wrote:

> Paolo, I have pasted some of the Copyrights which I found in the
> gst-sources to the beginning of the files, hope it's ok.

Interesting point ... if you copy a copyright notice, aren't
you already in violation of copyright, when you claim that the
file contents are copyrighted by yourself?

waaaaa

s.


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Joachim Jaeckel
In reply to this post by Joachim Jaeckel
Hello again,

I currently "finished" the work on the Base64 class and here it is.

If I have time or you recommend it as an important part, I'll enhance it
with base64 after MIME, base 64 after PEM and base64 with the URL-save
alphabet.
(The changes are only CRLF after 76 or 64 Bytes of data and for URLs,
using another alphabet which differs in using the character '-' and '_'
instead of '+' and '/'.)

But the current implementation would be enough for my current needs.

Best regards,
Joachim.

"======================================================================
|
|   Base64 class declarations
|
|
 ======================================================================"

"======================================================================
|
| Copyright 2009
| Written by Joachim Jaeckel
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
|
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LIB.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02110-1301, USA.
|
 ======================================================================"



Namespace current: NetClients.MIME [

Object subclass: Base64 [

    Base64 class >> encode: aString [
        | chars i j copyTo triple c1 c2 c3 c4 aStringSize b64SizeMultiplier b64String |
        chars := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.

        aStringSize := aString size.

        b64SizeMultiplier := aStringSize // 3.
        (aStringSize \\ 3 ~= 0) ifTrue: [b64SizeMultiplier := b64SizeMultiplier +1].

        b64String := String new: (b64SizeMultiplier * 4) withAll: $=.

        i := j := 1.

        [i <= aStringSize] whileTrue: [
            copyTo := i +2.
            (copyTo > aStringSize) ifTrue: [copyTo := aStringSize].
            triple := aString copyFrom: i to: copyTo.

            triple growBy: 1.
            triple at: (triple size) put: Character nul.

            c1 := (triple at: 1) asInteger bitShift: -2.
            b64String at: j put: (chars at: c1 +1).
            j := j +1.

            c2 := (((triple at: 1) asInteger bitAnd: 3) bitShift: 4) bitOr: ((triple at: 2) asInteger bitShift: -4).
            b64String at: j put: (chars at: c2 +1).
            j := j +1.

            (triple size > 2) ifTrue: [
                c3 := (((triple at: 2) asInteger bitAnd: 15) bitShift: 2) bitOr: ((triple at: 3) asInteger bitShift: -6).
                b64String at: j put: (chars at: c3 +1).
                j := j +1.
            ].

            (triple size > 3) ifTrue: [
                c4 := (triple at: 3) asInteger bitAnd: 63.
                b64String at: j put: (chars at: c4 +1).
                j := j +1.
            ].

            i := i +3.
        ].

        ^b64String
    ]

    Base64 class >> decode: aString [
        | chars b64StringSize i j quad padding string c1 c2 c3 |
        chars := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.

        b64StringSize := aString size.

        "Size should be a multiple of 4"
        (b64StringSize \\ 4 ~= 0)
            ifTrue: [
                ^NotBase64Encoded new signal: ('String: ''%1'' is not base64 encoded' bindWith: aString)
            ].
        "and should contain only defined character"
        aString do: [:char |
            (chars indexOf: char) = 0 & (char ~= $=) ifTrue: [
                ^NotBase64Encoded new signal: ('String: ''%1'' is not base64 encoded' bindWith: aString)
            ]
        ].

        padding := 0.
        i := b64StringSize.
        [(aString at: i) = $=] whileTrue: [
            padding := padding +1.
            i := i -1.
        ].

        string := String new: (b64StringSize // 4 * 3 - padding) withAll: Character nul.

        i := j := 1.
        [i <= b64StringSize] whileTrue: [
            quad := aString copyFrom: i to: i +3.

            c1 := ((chars indexOf: (quad at: 1)) asInteger -1 bitShift: 2) bitOr: ((chars indexOf: (quad at: 2)) asInteger -1 bitShift: -4).
            string at: j put: c1 asCharacter.
            j := j +1.

            (quad at: 3) ~= $= ifTrue: [
                c2 := (((chars indexOf: (quad at: 2)) asInteger -1 bitAnd: 15) bitShift: 4) bitOr: ((chars indexOf: (quad at: 3)) asInteger -1 bitShift: -2).
                string at: j put: c2 asCharacter.
                j := j +1.
            ].

            (quad at: 4) ~= $= ifTrue: [
                c3 := (((chars indexOf: (quad at: 3)) asInteger -1 bitAnd: 3) bitShift: 6) bitOr: ((chars indexOf: (quad at: 4)) asInteger -1).
                string at: j put: c3 asCharacter.
                j := j +1.
            ].

            i := i +4.
        ].

        ^string
    ]
]

Notification subclass: NotBase64Encoded [

    description [
        ^'String is not Base64 encoded'
    ]

]

]

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Paolo Bonzini-3

> I currently "finished" the work on the Base64 class and here it is.
>
> If I have time or you recommend it as an important part, I'll enhance it
> with base64 after MIME, base 64 after PEM and base64 with the URL-save
> alphabet.

I'll be committing it tonight as NetProtocolInterpreter class >>
#encodeBase64:.  I plan to do some changes.  You can look at them for
your own education and benchmark the difference (I didn't).

For decoding, I'm adding Integer >> #digitAt: and using the existing
code instead.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Joachim Jaeckel
 > I plan to do some changes.  You can look at them for
> your own education and benchmark the difference (I didn't).

No problem! Looking forward to see your "tune up".

> For decoding, I'm adding Integer >> #digitAt: and using the existing
> code instead.

My first try was a modification to the existing code, that it accepts a
string as the only parameter, but then I had problems with some encoded
strings. That was the time where I decided to have a look into the rfc
and do an implementation by my own.

(The problematic text-string in my case was 'ABCdef'. First do an encode
  to: QUJDZGVm and than the decode with the existing method with my
modification fails. Other strings were not a problem.)

Best regards,
Joachim.


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Paolo Bonzini-3
Joachim Jaeckel wrote:

>  > I plan to do some changes.  You can look at them for
>> your own education and benchmark the difference (I didn't).
>
> No problem! Looking forward to see your "tune up".
>
>> For decoding, I'm adding Integer >> #digitAt: and using the existing
>> code instead.
>
> My first try was a modification to the existing code, that it accepts a
> string as the only parameter, but then I had problems with some encoded
> strings. That was the time where I decided to have a look into the rfc
> and do an implementation by my own.

Yeah, I fixed that now.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Paolo Bonzini-3
In reply to this post by Joachim Jaeckel
I finally integrated this, thanks! (commit fd7929d).

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: NetClients.ESMTP package

Joachim Jaeckel
> I finally integrated this, thanks! (commit fd7929d).

You'r welcome! (If I could say that for such a tiny "contribution"... or
the idea).

And to answer your other question:
Everything is a learning expirience!

Even here with:

('%<EHLO|HELO>1 %2' % {esmtp. domain})

to found it defined in the CharacterArray class!

:-)

Cool! Thanks!
Joachim.


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk