B4J Tutorial Building a mini "Email based server"

Discussion in 'B4J Tutorials' started by Erel, Nov 27, 2013.

  1. Erel

    Erel Administrator Staff Member Licensed User

    This example uses jNet library together with the MailParser module to build a program that responds to emails.

    [​IMG]

    Although it sounds a bit strange, using emails as the communication channel can be very simple and useful.

    You do not need to worry about hosting, firewall or (partially) downtime issues. Let Google, Yahoo, Microsoft or one of the other email providers solve these issues for you.

    This program checks an email account every two minutes. If it finds a message it downloads it, parses it and deletes it.

    In this case the "server" doesn't do anything useful. It checks the subject line. If the value is 13 then it sends a reply with the current time.

    A client (B4A) program can use SMTP from the Net library to send emails to this server.

    You can for example send database updates as an attachment and insert the data to a database. Or any other creative solution you can think of...
     

    Attached Files:

    Beja, aidymp, JakeBullet70 and 9 others like this.
  2. George Popovic

    George Popovic Member Licensed User

    I tried this program with correct Gmail account and password but get this message:

    12/06/2013 10:13:28: Error: (SocketException) java.net.SocketException: Network is unreachable: connect
     
  3. Erel

    Erel Administrator Staff Member Licensed User

    Your firewall / antivirus is probably blocking the outgoing connection.
     
  4. Erel

    Erel Administrator Staff Member Licensed User

  5. pesquera

    pesquera Active Member Licensed User

    Hi Erel, is possible to do an email server from the Android side? like the B4J desktop example, but with B4A
    trying to intercept emails like that example does
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    This is not an implementation of an email server. The server uses POP3 to check for new mails.
     
  7. Theera

    Theera Well-Known Member Licensed User

    Hi all,
    I try to change PopServer="pop.hotmail.com" and StmpServer="stmp.hotmail.com" ,I 've message "01/18/2014 09:22:48: Error: (UnknownHostException) java.net.UnknownHostException: pop.hotmail.com" How do I set?
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    You should find the correct values in Hotmail POP / SMTP instructions.
     
  9. Christos Dorotheou

    Christos Dorotheou Member Licensed User

    Dear All,

    I've wrote an application based on Erel's EmailServer example which receives incoming Emails that have pictures as attachments and stores
    those pictures into a structured folder on a Server and various Email Details into an Access Database.

    Everything works perfectly except in those cases where the Sender of the Email uses Greek language in the Mail's Subject, at which case the
    Subject text in the Header of the Email is encoded as =?iso-8859-7 and becomes unreadable.

    Is there any way to encode the subject text back to Greek?

    Any help it will be most welcome

    Regards Christos
     
  10. Erel

    Erel Administrator Staff Member Licensed User

  11. Christos Dorotheou

    Christos Dorotheou Member Licensed User

    Erel,

    I have read the 'encoding specification' you have recommended and it helped.

    I have come up with a solution for my problem with a subroutine that I wrote,
    which accepts the Raw Subject Data extracted from the Email and returns it back as a readable string.

    Because I am not sure if pasting the code here, so people with similar problem can use it, is allowed,
    I would be glad to post it to anyone on request.


    Thanks for your help.
     
    manios likes this.
  12. Erel

    Erel Administrator Staff Member Licensed User

    Sure, please post it.
     
  13. Christos Dorotheou

    Christos Dorotheou Member Licensed User

    These are the 2 Subroutines I have come up with to handle encoded text contained in a MIME email header

    DecodeMIMEText(strParam) will accept a string which may contain a MIME encoded string and will return a decoded string
    as in the case of the From: field in the Header where the name of the Sender is encoded and address is not.

    From: =?ISO-8859-7?B?wtzz7/Ig0+nv/fb04fI=?= <vasrecoveryservices@gmail.com>

    Example

    Log(DecodeMIMEText("=?ISO-8859-7?B?wtzz7/Ig0+nv/fb04fI=?= <vasrecoveryservices@gmail.com>"))


    Code:
    '(B for Base64, Q for Quoted-Printable)

    Sub DecodeMIMEText(EncodedText As StringAs String
    Dim CharSet As String
    Dim Buffer() As Byte
    Dim SupportedEncodings() As String
    Dim result As String
    Dim Part As String
    Dim cnv As ByteConverter
    Dim sbj_sb As StringBuilder
    Dim s_u As StringUtils
    Dim k, j, x As Int
    Dim booCharSetOK, HasMIMECoding, ValidMIME As Boolean
    Dim MIMEStart, MIMEEnd As Int
    Dim MIMEPart As String
    Dim NONMIMEPart1 As String
    Dim NONMIMEPart2 As String

    sbj_sb.Initialize
    SupportedEncodings = cnv.SupportedEncodings

    MIMEStart = 
    0
    MIMEEnd = 
    0
    HasMIMECoding = 
    False

    MIMEStart = EncodedText.Trim.IndexOf(
    "=?")
    MIMEEnd = EncodedText.Trim.IndexOf(
    "?=")

    If MIMEEnd >=0 Then
      MIMEEnd = MIMEEnd + 
    2 'To account for '?=' when will be be passed later through SubSustring Function
    End If

    '(B for Base64, Q for Quoted-Printable)
    If EncodedText.Trim.IndexOf("?B?") >= 0 OR EncodedText.Trim.IndexOf("?Q?") >= 0 Then
      HasMIMECoding = 
    True
    Else
      HasMIMECoding = 
    False
    End If

    If MIMEStart >= 0 AND MIMEEnd > MIMEStart AND HasMIMECoding = True Then
      ValidMIME = 
    True
     
      
    If MIMEStart > 0 Then
          NONMIMEPart1 = EncodedText.Trim.SubString2(
    0,MIMEStart)
      
    Else
          NONMIMEPart1 = 
    ""
      
    End If
     
      MIMEPart = EncodedText.Trim.SubString2(MIMEStart,MIMEEnd)

      
    If MIMEEnd < EncodedText.Trim.Length Then
          NONMIMEPart2 = EncodedText.Trim.SubString(MIMEEnd)
      
    Else
          NONMIMEPart2 = 
    ""
      
    End If 
    Else
      ValidMIME = 
    False
      NONMIMEPart1 = EncodedText
      MIMEPart = 
    ""
      NONMIMEPart2 = 
    ""
    End If

    If ValidMIME = True Then
        
    'Check if String passed as parameter is MIME encoded
        If MIMEPart.Length > 0 Then
          
    'Check if MIME coding used is "B" (B for Base64, Q for Quoted-Printable)
          If MIMEPart.Trim.Contains("?B?") = True Then
              
    'Retrieve Character Set Used for Coding
              CharSet = MIMEPart.Trim.SubString2(0, MIMEPart.Trim.IndexOf("?B?"))
              
    If CharSet.Trim.StartsWith("=?") = True Then
                CharSet = CharSet.Trim.SubString(
    2)
              
    End If
             
              booCharSetOK = 
    False 'Check if Character Set of Header is supported by the system
              For j = 0 To SupportedEncodings.Length - 1
                  
    If CharSet.ToUpperCase = SupportedEncodings(j).ToUpperCase Then
                    booCharSetOK = 
    True
                  
    End If       
              
    Next
             
              
    If booCharSetOK = True Then
                
    Part = MIMEPart.Trim.SubString2(MIMEPart.Trim.IndexOf("?B?")+3,MIMEPart.Trim.Length-2)
                
    Try
                  Buffer = s_u.DecodeBase64(
    Part)
                  
    'result = BytesToString(Buffer, 0, Buffer.Length, CharSet)
                  result = cnv.StringFromBytes(Buffer, CharSet)
                
    Catch
                  result = MIMEPart
                
    End Try
                sbj_sb.Append(result.Trim)
              
    Else
                sbj_sb.Append(MIMEPart) 
    'Since Header's Character Set is not supported by the system, return it back asis
              End If
             
          
    'Check if MIME coding used is "Q" (B for Base64, Q for Quoted-Printable)
          Else If MIMEPart.Contains("?Q?") = True Then
              
    Dim WordsBuffer() As String
              
    Dim CharsBuffer() As String

              
    'Retrieve Character Set Used for Coding
              CharSet = MIMEPart.Trim.SubString2(0, MIMEPart.Trim.IndexOf("?Q?"))
              
    If CharSet.Trim.StartsWith("=?") = True Then
                CharSet = CharSet.Trim.SubString(
    2)
              
    End If
             
              booCharSetOK = 
    False 'Check if Character Set of Header is supported by the system
              For j = 0 To SupportedEncodings.Length - 1
                  
    If CharSet.ToUpperCase = SupportedEncodings(j).ToUpperCase Then
                    booCharSetOK = 
    True
                  
    End If       
              
    Next
             
              
    If booCharSetOK = True Then
                
    Part = MIMEPart.Trim.SubString2(MIMEPart.Trim.IndexOf("?Q?")+3,MIMEPart.Trim.Length-2).Trim
                
    'Split string to Words
                WordsBuffer=Regex.Split("_"Part)
                
    For k = 0 To WordsBuffer.Length - 1
                    
    If WordsBuffer(k).Contains("=") = True Then
                        
    'Split Word to Characters
                        CharsBuffer=Regex.Split("=", WordsBuffer(k))
                        
    For x = 0 To CharsBuffer.Length - 1
                            
    Try
                              Buffer = cnv.HexToBytes(CharsBuffer(x))
                              
    'sbj_sb.Append(cnv.StringFromBytes(Buffer,"WINDOWS-1253").Trim)
                              sbj_sb.Append(cnv.StringFromBytes(Buffer,CharSet).Trim)
                            
    Catch
                              sbj_sb.Append(CharsBuffer(x))
                            
    End Try
                        
    Next
                    
    Else
                        sbj_sb.Append(WordsBuffer(k))
                    
    End If
                    sbj_sb.Append(
    " ")
                
    Next
              
    Else
                sbj_sb.Append(MIMEPart) 
    'Since Header's Character Set is not supported by the system, return it back asis
              End If
          
    Else
              sbj_sb.Append(MIMEPart) 
    'Header is not Encoded or Wrongly Encoded therefor send it back asis
          End If     
        
    Else
          sbj_sb.Append(MIMEPart) 
    'Header is not Encoded or Wrongly Encoded therefor send it back asis
        End If

        
    Return NONMIMEPart1 & sbj_sb.ToString & NONMIMEPart2
    Else
        
    Return NONMIMEPart1 & MIMEPart & NONMIMEPart2
    End If

    End Sub


    DecodeMIMEArray(arrParam) will accept String Array with MIME encoded string elements and will return a decoded string
    as in the case of the Subject: field in the Header where the subject may be in multiple encoded lines.

    Subject: =?utf-8?B?zpTPhc+Dz4TPjc+HzrfOvM6xIEtCQTEyMyDPg8+EzrfOvSDOn860z4wgzpzOsQ==?=
    =?utf-8?B?zrrOsc+Bzq/Ov8+FIM+Dz4TOuc+CIDMwLzAxLzIwMTQ=?=


    or

    Subject: =?iso-8859-7?Q?=C1=F4=FD=F7=E7=EC=E1_=F3=F4=E9=F2_31/01/2014_=F3=F4=E7=ED?=
    =?iso-8859-7?B?IMvl+fb88e8gwfH34ePj3evv9SDM6ffh3ussIMvl9er589/hLg==?=


    Example

    Dim Test(2) as string

    Test(0) = "?utf-8?B?zpTPhc+Dz4TPjc+HzrfOvM6xIEtCQTEyMyDPg8+EzrfOvSDOn860z4wgzpzOsQ==?="
    Test(1) = "=?utf-8?B?zrrOsc+Bzq/Ov8+FIM+Dz4TOuc+CIDMwLzAxLzIwMTQ=?="

    Log(DecodeMIMEArray(Test))


    Code:
    Sub DecodeMIMEArray(EndcodedStringParts() As StringAs String
    Dim CharSet As String
    Dim Buffer() As Byte
    Dim SupportedEncodings() As String
    Dim result As String
    Dim Part As String
    Dim cnv As ByteConverter
    Dim sbj_sb As StringBuilder
    Dim s_u As StringUtils
    Dim i, k, j, x As Int
    Dim booCharSetOK As Boolean

    sbj_sb.Initialize
    SupportedEncodings = cnv.SupportedEncodings

    For i = 0 To EndcodedStringParts.Length-1
        
    'Check if String passed as parameter is MIME encoded
        If EndcodedStringParts(i).Trim.StartsWith("=?") = True AND EndcodedStringParts(i).Trim.EndsWith("?=") = True Then
          
    'Check if MIME coding used is "B" (B for Base64, Q for Quoted-Printable)
          If EndcodedStringParts(i).Trim.Contains("?B?") = True Then
              
    'Retrieve Character Set Used for Coding
              CharSet = EndcodedStringParts(i).Trim.SubString2(0, EndcodedStringParts(i).Trim.IndexOf("?B?"))
              
    If CharSet.Trim.StartsWith("=?") = True Then
                CharSet = CharSet.Trim.SubString(
    2)
              
    End If
             
              booCharSetOK = 
    False 'Check if Character Set of Header is supported by the system
              For j = 0 To SupportedEncodings.Length - 1
                  
    If CharSet.ToUpperCase = SupportedEncodings(j).ToUpperCase Then
                    booCharSetOK = 
    True
                  
    End If       
              
    Next
             
              
    If booCharSetOK = True Then
                
    Part = EndcodedStringParts(i).Trim.SubString2(EndcodedStringParts(i).Trim.IndexOf("?B?")+3,EndcodedStringParts(i).Trim.Length-2)
                
    Try
                  Buffer = s_u.DecodeBase64(
    Part)
                  
    'result = BytesToString(Buffer, 0, Buffer.Length, CharSet)
                  result = cnv.StringFromBytes(Buffer, CharSet)
                
    Catch
                  result = EndcodedStringParts(i)
                
    End Try
                sbj_sb.Append(result.Trim)
              
    Else
                sbj_sb.Append(EndcodedStringParts(i)) 
    'Since Header's Character Set is not supported by the system, return it back asis
              End If
             
          
    'Check if MIME coding used is "Q" (B for Base64, Q for Quoted-Printable)
          Else If EndcodedStringParts(i).Contains("?Q?") = True Then
              
    Dim WordsBuffer() As String
              
    Dim CharsBuffer() As String

              
    'Retrieve Character Set Used for Coding
              CharSet = EndcodedStringParts(i).Trim.SubString2(0, EndcodedStringParts(i).Trim.IndexOf("?Q?"))
              
    If CharSet.Trim.StartsWith("=?") = True Then
                CharSet = CharSet.Trim.SubString(
    2)
              
    End If
             
              booCharSetOK = 
    False 'Check if Character Set of Header is supported by the system
              For j = 0 To SupportedEncodings.Length - 1
                  
    If CharSet.ToUpperCase = SupportedEncodings(j).ToUpperCase Then
                    booCharSetOK = 
    True
                  
    End If       
              
    Next
             
              
    If booCharSetOK = True Then
                
    Part = EndcodedStringParts(i).Trim.SubString2(EndcodedStringParts(i).Trim.IndexOf("?Q?")+3,EndcodedStringParts(i).Trim.Length-2).Trim
                
    'Split string to Words
                WordsBuffer=Regex.Split("_"Part)
                
    For k = 0 To WordsBuffer.Length - 1
                    
    If WordsBuffer(k).Contains("=") = True Then
                        
    'Split Word to Characters
                        CharsBuffer=Regex.Split("=", WordsBuffer(k))
                        
    For x = 0 To CharsBuffer.Length - 1
                            
    Try
                              Buffer = cnv.HexToBytes(CharsBuffer(x))
                              
    'sbj_sb.Append(cnv.StringFromBytes(Buffer,"WINDOWS-1253").Trim)
                              sbj_sb.Append(cnv.StringFromBytes(Buffer,CharSet).Trim)
                            
    Catch
                              sbj_sb.Append(CharsBuffer(x))
                            
    End Try
                        
    Next
                    
    Else
                        sbj_sb.Append(WordsBuffer(k))
                    
    End If
                    sbj_sb.Append(
    " ")
                
    Next
              
    Else
                sbj_sb.Append(EndcodedStringParts(i)) 
    'Since Header's Character Set is not supported by the system, return it back asis
              End If
          
    Else
              sbj_sb.Append(EndcodedStringParts(i)) 
    'Header is not Encoded or Wrongly Encoded therefor send it back asis
          End If     
        
    Else
          sbj_sb.Append(EndcodedStringParts(i)) 
    'Header is not Encoded or Wrongly Encoded therefor send it back asis
        End If
    Next

    Return sbj_sb.ToString

    End Sub
    Any suggestions or improvements are welcome.

    Chris
     
    JakeBullet70, Erel and rboeck like this.
  14. Christos Dorotheou

    Christos Dorotheou Member Licensed User

    I recommend to disable the timer while checking for new mail and while attachments are still been downloaded and processed, because if there
    is a significant amount of emails with attachments in queue then the timer will fire again before the whole process is over.

    You can disable the timer from within the MailTimer_Tick Subroutine as shown bellow:

    Code:
    Sub MailTimer_Tick
        LogMessage(
    "[SCM] Start Checking Mail on " & DateTime.Date(DateTime.Now))

        mailTimer.Interval = 
    0
        mailTimer.Enabled = 
    False ' Disable timer while checking for new mail
       
        pop.Initialize(popServer, popPort, user, password, 
    "pop")
       
        pop.UseSSL = 
    True
        pop.ListMessages
       
        progress1.Visible = 
    True
    End Sub
    And Re-Enable the timer from within POP_DownloadCompleted subroutine as follows:

    Code:
    Sub POP_DownloadCompleted (Success As Boolean, MessageId As Int, MessageText As String)
        
    If Success = False Then
            LogMessage(
    LastException.Message)
        
    Else
            
    'Parse the mail
            Dim m As Message
            m = MailParser.ParseMail(MessageText, strAttFolder)
            HandleMessage(m)
        
    End If
       
        
    If MessageId = lastMsgId Then
          progress1.Visible = 
    False
          LogMessage(
    "[ECM] End Checking Mail on " & DateTime.Date(DateTime.Now))
         
          mailTimer.Initialize(
    "mailTimer", PolingFreq)
          mailTimer.Enabled = 
    True ' Re-Enable timer after finished checking for new mail
        End If 
       
    End Sub
     
    JakeBullet70 and Erel like this.
  15. manios

    manios Active Member Licensed User

    Hi all,
    I receive mails coded "Content-Type: text/plain; charset=utf-8", however the body I get from the mailparser is not decoded correctly.

    Instead of "Hückelhoven" I get "H=C3=BCckelhoven". Did I overlook something, do I need to run an extra decode?

    Manios
     
  16. Erel

    Erel Administrator Staff Member Licensed User

    MailParser doesn't do anything to the body. It seems like the text was not encoded correctly.
     
  17. manios

    manios Active Member Licensed User

    Thanks, will have to check with the sender!
     
  18. rboeck

    rboeck Well-Known Member Licensed User

    I think there is nothing wrong; its the system, how special characters are coded;
    look at: http://www.utf8-zeichentabelle.de/
    and you will find C3 BC as Code for ü.
     
  19. Christos Dorotheou

    Christos Dorotheou Member Licensed User

    This routine should work:

    Example:

    Log(UTF2UNI("H=C3=BCckelhoven","UTF-8"))

    Returns: Hückelhoven

    Code:
    Sub UTF2UNI(s2match As String, CharSet As StringAs String
    Dim m As Matcher
    Dim Parts As List
    Dim cnv As ByteConverter
    Dim i As Int
    Dim i2s As String

    Parts.Initialize

    m = 
    Regex.Matcher("=[0-9a-fA-F][0-9a-fA-F]=[0-9a-fA-F][0-9a-fA-F]",s2match)

    i = -
    1
    Do While m.Find = True
      i = i + 
    1
      i2s = i
      Parts.Add(m.Group(
    0).Replace("=",""))
      s2match = s2match.Replace(m.Group(
    0),"~" & i2s)
      m = 
    Regex.Matcher("=[0-9a-fA-F][0-9a-fA-F]=[0-9a-fA-F][0-9a-fA-F]",s2match)
    Loop

    If Parts.Size > 0 Then
      
    For i = 0 To Parts.Size - 1
        i2s = i
        s2match = s2match.Replace(
    "~" & i2s, cnv.StringFromBytes(cnv.HexToBytes(Parts.Get(i)), CharSet))
      
    Next
    End If

    Return s2match

    End Sub
     
    Last edited: Feb 14, 2014
    jmon and manios like this.
  20. manios

    manios Active Member Licensed User

    Hi Christos,
    Thanks for the Sub. It helps and works OK.
    However something is still wrong with the coding of the mails or the decoding. I get intermittend some "=" and "=20" characters/strings in the message body from the Mailparser. This finally screws up the conversion and contents.
    I will try to get the raw contents of the mail with an old VB program and see what is wrong
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice