Example #1
0
        public void PurgeOldIncomingMailsFiles(IList <Guid> contentIds)
        {
            foreach (var contentId in contentIds)
            {
                try { IncomingMailDTO.DeleteContentFile(contentId); } catch { }
            }

            // Drop any missed mail folders
            var zombieThreshold       = DateTime.UtcNow.AddMinutes(-2 * Configuration.Instance.TimeToLiveInMinutes);
            var incomingMailDirectory = new DirectoryInfo(Configuration.Instance.IncomingMailDirectory);

            foreach (var incomingMail in incomingMailDirectory.GetFiles())
            {
                if (incomingMail.LastWriteTimeUtc < zombieThreshold)
                {
                    try { File.Delete(incomingMail.FullName); } catch { }
                }
            }
        }
Example #2
0
 public Extractor(IDbConnection conn, IncomingMailDTO m)
 {
     _conn = conn;
     _m    = m;
 }
Example #3
0
 private void ExtractIncomingMail(IDbConnection c, IncomingMailDTO m)
 {
     new Extractor(c, m).Extract();
 }
Example #4
0
 private void DeleteIncomingMail(IDbConnection c, IncomingMailDTO m)
 {
     c.Execute("delete from IncomingMail where Id=@Id", m);
     m.DeleteContentFile();
 }
Example #5
0
 private void DispatchIncomingMail(IDbConnection conn, IncomingMailDTO m)
 {
     conn.Execute("insert into IncomingMail (ReceivedOn,Recipient,Sender,ContentSize,ContentId) values (@ReceivedOn,@Recipient,@Sender,@ContentSize,@ContentId);", m);
 }
Example #6
0
 public void Enqueue(IncomingMailDTO m)
 {
     _queue.Add(m);
 }
Example #7
0
        private void HandleCompleteLine(ArraySegment <byte> line)
        {
            var isHandled = false;
            var nextState = _state;

            byte[] response = null;

            ArraySegment <byte> cmd = _emptyBuffer;
            var tokens = new ArraySegment <byte>[] { };

            if (_state != SmtpState.WaitingForEndOfData)
            {
                tokens = Bytes.Split(line, Whitespace);
                if (tokens.Length > 0)
                {
                    Bytes.ToUpper(tokens[0]);
                    cmd = tokens[0];
                }
            }

            if (Bytes.IsSame(RSET, cmd))
            {
                isHandled = true;
                if (tokens.Length == 1)
                {
                    ResetMailInfo();
                    response  = ReplyOk;
                    nextState = SmtpState.WaitingForHelo;
                }
                else
                {
                    response = ReplySyntaxErrorRset;
                }
            }
            else if (_state == SmtpState.WaitingForHelo && Bytes.IsSame(EHLO, cmd))
            {
                isHandled = true;
                if (tokens.Length == 2)
                {
                    response  = ReplyEhlo;
                    nextState = SmtpState.WaitingForMailFrom;
                }
                else
                {
                    response = ReplySyntaxErrorEhlo;
                }
            }
            else if (_state == SmtpState.WaitingForHelo && Bytes.IsSame(HELO, cmd))
            {
                isHandled = true;
                if (tokens.Length == 2)
                {
                    response  = ReplyHelo;
                    nextState = SmtpState.WaitingForMailFrom;
                }
                else
                {
                    response = ReplySyntaxErrorHelo;
                }
            }
            else if (_state == SmtpState.WaitingForMailFrom && Bytes.IsSame(MAIL, cmd))
            {
                isHandled = true;

                // extract mail address between < > after :
                bool go = false;
                int  lt = -1, gt = -1;
                for (var i = line.Offset; i < line.Offset + line.Count; i++)
                {
                    byte ch = line.Array[i];
                    if (go)
                    {
                        if (lt == -1 && ch == LessThan)
                        {
                            lt = i;
                        }
                        if (lt != -1 && ch == GreaterThan)
                        {
                            gt = i;
                            break;
                        }
                    }
                    else if (ch == SemiColon)
                    {
                        go = true;
                    }
                }

                if (lt != -1 && gt != -1)
                {
                    // sender found,
                    _sender = Encoding.ASCII.GetString(line.Array, lt + 1, gt - lt - 1).Trim();

                    // check for optional SIZE=nnnnnnn extension after the sender
                    int sizeOffset = Bytes.OffsetOf(MessageSizeExtension, line.Array, gt + 1, line.Count - gt - 1);
                    if (sizeOffset != -1)
                    {
                        int valueBeginOffset = sizeOffset + MessageSizeExtension.Length;
                        int valueEndOffset   = -1;
                        for (var i = valueBeginOffset; i < line.Offset + line.Count; i++)
                        {
                            byte ch    = line.Array[i];
                            byte digit = Bytes.Digits[ch];
                            if (Whitespace.Contains(ch))
                            {
                                break;
                            }
                            else if (digit > 9 || digit < 0)
                            {
                                valueEndOffset = -1;
                                break;
                            }
                            valueEndOffset = i;
                        }

                        if (valueEndOffset >= valueBeginOffset)
                        {
                            int size = 0, scale = 1;
                            for (var i = valueEndOffset; i >= valueBeginOffset && size < MaximumMessageSize; i--, scale *= 10)
                            {
                                size += Bytes.Digits[line.Array[i]] * scale;
                            }

                            if (size > MaximumMessageSize)
                            {
                                response  = ReplyMessageSizeTooBig;
                                nextState = SmtpState.WaitingForRset;
                            }
                            else
                            {
                                _mailContentExpectedSize = size;
                                response  = ReplyOk;
                                nextState = SmtpState.WaitingForRcptTo;
                            }
                        }
                        else
                        {
                            response = ReplySyntaxErrorMail;
                        }
                    }
                    else
                    {
                        // no size extension
                        response  = ReplyOk;
                        nextState = SmtpState.WaitingForRcptTo;
                    }
                }
                else
                {
                    response = ReplySyntaxErrorMail;
                }
            }
            else if ((_state == SmtpState.WaitingForRcptTo || _state == SmtpState.WaitingForAdditionalRcptTo) && Bytes.IsSame(RCPT, cmd))
            {
                // standard deviation: send only to the last given recipient even if not pretending so.
                isHandled = true;

                // extract mail address between < > after :
                bool go = false;
                int  lt = -1, gt = -1;
                for (var i = line.Offset; i < line.Offset + line.Count; i++)
                {
                    byte ch = line.Array[i];
                    if (go)
                    {
                        if (lt == -1 && ch == LessThan)
                        {
                            lt = i;
                        }
                        if (lt != -1 && ch == GreaterThan)
                        {
                            gt = i;
                            break;
                        }
                    }
                    else if (ch == SemiColon)
                    {
                        go = true;
                    }
                }

                if (lt != -1 && gt != -1)
                {
                    var recipient = Encoding.ASCII.GetString(line.Array, lt + 1, gt - lt - 1).Trim().ToLowerInvariant();
                    if (_server.MailDispatcher.IsMailboxActive(recipient))
                    {
                        _lastRecipient = recipient;
                        response       = ReplyOk;
                        nextState      = SmtpState.WaitingForAdditionalRcptTo;
                    }
                    else
                    {
                        response = ReplyNoSuchUserHere;
                    }
                }
                else
                {
                    response = ReplySyntaxErrorRcpt;
                }
            }
            else if (_state == SmtpState.WaitingForAdditionalRcptTo && Bytes.IsSame(DATA, cmd))
            {
                isHandled = true;
                if (tokens.Length == 1)
                {
                    response = ReplyStartMail;
                    if (_mailContent == null)
                    {
                        // presize stream to avoid garbage & extra reallocation if expected size makes sense.
                        // add a bit of slack for size errors
                        var size = (_mailContentExpectedSize > 0 && _mailContentExpectedSize <= MaximumMessageSize) ? _mailContentExpectedSize + 1024 : 4096;
                        _mailContentId = Guid.NewGuid();
                        _mailContent   = new FileStream(IncomingMailDTO.GetContentFileName(_mailContentId), FileMode.CreateNew, FileAccess.Write, FileShare.None, 16384);
                    }
                    nextState = SmtpState.WaitingForEndOfData;
                }
                else
                {
                    response = ReplySyntaxErrorData;
                }
            }
            else if (_state == SmtpState.WaitingForEndOfData)
            {
                isHandled = true;
                if (line.Count == EndOfMail.Length && Bytes.StartsWith(EndOfMail, line))
                {
                    if (_mailContent.Length <= MaximumMessageSize)
                    {
                        response  = ReplyOk;
                        nextState = SmtpState.WaitingForMailFrom;
                        _mailContent.Flush();
                        _server.MailDispatcher.Enqueue(new IncomingMailDTO(DateTime.UtcNow, _lastRecipient, _sender, (int)_mailContent.Length, _mailContentId));
                        _mailContent.Dispose();
                        _mailContent = null;
                    }
                    else
                    {
                        response  = ReplyMessageSizeTooBig;
                        nextState = SmtpState.WaitingForRset;
                    }
                    ResetMailInfo();
                }
                else
                {
                    // accumulate mail body & handle dot unstuffing (while msg size doesn't exceed maximum size, since it will be discarded later anyway).
                    int offset = line.Offset;
                    int count  = line.Count;
                    if (line.Array[offset] == Dot)
                    {
                        offset++;
                        count--;
                    }

                    if (_mailContent != null && _mailContent.Length + count <= MaximumMessageSize)
                    {
                        _mailContent.Write(line.Array, offset, count);
                    }
                }
            }
            else if (Bytes.IsSame(QUIT, cmd))
            {
                isHandled = true;
                if (tokens.Length == 1)
                {
                    ResetMailInfo();
                    response  = ReplyBye;
                    nextState = SmtpState.Disconnect;
                }
                else
                {
                    response = ReplySyntaxErrorQuit;
                }
            }
            else if (Bytes.IsSame(VRFY, cmd) || Bytes.IsSame(EXPN, cmd))
            {
                isHandled = true;
                response  = ReplyCommandNotImplemented;
            }
            else if (Bytes.IsSame(HELP, cmd))
            {
                isHandled = true;
                response  = ReplyHelp;
            }
            else if (Bytes.IsSame(NOOP, cmd))
            {
                isHandled = true;
                response  = ReplyOk;
            }

            if (!isHandled)
            {
                var isKnownCommand = KnownCommands.Any((knownCmd) => Bytes.IsSame(knownCmd, cmd));
                response = isKnownCommand ? ReplyBadSequence : ReplyUnknownCommand;
            }

            _state = nextState;
            if (response != null && response.Length > 0)
            {
                var e = CreateArgs(SendCompleted, response);
                if (!_socket.SendAsync(e))
                {
                    SendCompleted(_socket, e);
                }
            }
        }