(ASP.NET) ASP.NET (2009 год)

ExchangeLogAnalyzer - OpenSource парсер журнала MS Exchange Server.

Ничто в современном мире не может обходится без рассылки рекламы - как бы мы не плевались от нее. Например, на мои почтовые ящики приходит 99,4% спама - за месяц я получил всего 8216 писем и только 46 осмысленных писем.

Надо понимать, что как бы нам не нравилась рассылка рекламы (ох а как нас загружают рекламой по телевидению!), но для отделения интернет-спама от нормальной интернет-рекламы суды используют два критерия:

И, поскольку эти требования я соблюдаю, то в какой-то момент я тоже добавил на свой портал сервер рассылки рекламы.


Однако почтовый протокол отличается тем, что отдельные письма (по практике) - десятки тысяч раз тыкаются из передающего SMTP-сервера в приемный SMTP-сеервер. У меня на почтовике стоит ограничение - пытаться высылать письмо в течении четырех суток. И иногда некоторые письма (скажем 2-3-4 штуки на тысячу) действительно уходят лишь на четвертые сутки!

Поэтому код возврата, который формирует почтовик, сразу после обращения к нему с сайта (или из клиента типа TheBat или Outlook) - никакого значения не имеет. Нужен итоговый код возврата отправки письма - который в конфигурации моего почтовики становится известным лишь на четвертые сутки, а у кого-то может быть и через месяц.

Именно для этого предназначен анализатор журнала работы MS Exchange Server.


Немного потыкавшись в интернете, я обнаружил, что анализатор журнала работы почтовика - это одна из самых распространенных в интернете программ. В частности, только на одном-единственном сайте sourceforge.net их выложено аж 758 штук! И количество загрузок некоторых из них, например AWStats составляет 2,1 миллиона скачиваний! И все остальные скачивания тоже в миллионах и сотнях тысяч! Например Zenoss Core - Enterprise IT Monitoring тоже более миллиона скачиваний!


Ну нет, скачивать такой простой строчный парсер - я не унижусь, решил я. И за свободный вечер я написал простенькую страничку, которая делает этот анализ результатов рассылки рекламы с помощью Exchange Server. Эту страничку я опишу здесь.


Работа этой странички осуществляется в два этапа - сначала на сайт загружается файл журанала Exchange, потом он подвергается анализу, в ходе которого результат рассылки показывается на сайте и, главное, вписывается в базу:




Структуру базы вы можете видеть на рисунке, первое поле I - Identity(1,1), второе ID - это ГУИД, остальные поля текстовые. Email - это адрес, куда мы высылаем рекламу, SMTP_Replay - это ответ почтовика, который мы записываем в базу в результате работы описываемого парсера. Tag-это тег, на основании которого рассылается реклама. Понятно, что сайтам на ASP.NET можно предлагать только программирование на ASP.NET, но никак не на Битриксе.

Поле IsBlockOnRecipient - это как раз то самое поле отписки от рекламы, если получатель рекламы кликнет на ссылку отписки в тексте рекламы, то в этом поле появляется единичка и реклама болше никогда по этому адресу не рассылается.


Как вы понимаете, основная тема - каким образом сформировать эту базу с адресами для рассылки рекламы. Ее легко формировать для рассылки предложений о порно или недвижимости, но невероятно тяжело сформировать для программирования на ASP.NET. Ведь сайтов на ASP.NET - всего 0,4%. И подавляющее большинство из них - студенческий мусор или некоммерческие проекты, не представляющие интереса для профессиональных программистов. Еще сложнее сформировать базу для предложений о хостинге. Как среди миллионов и миллиардов почтовых адресов найти те несколько человек (а в Москве на хостингах находится всего несколько тысяч серверов) - как найти среди миллиардов почтовых адресов тех, кто готов заплатить 30 тысяч в месяц за размещение выделенного сервера на хостинге? Как найти того человека, кто принимает решение? Как вы понимаете - никакой автоматический механизм не поможет отобрать такие почтовые адреса. Поэтому адреса эти вносятся в базу вручную.


Но вернемся собственно к самой популярной (!) интернет-программе - анализатору журнала. Cтраничка анализа журнала выглядит беспредельно просто:


   1:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
   2:  <div style="text-align:left" >
   3:   
   4:      <br /><center><h4>Анализ журнала рассылки</h4></center><br />
   5:   
   6:      <asp:Label ID="Label1" runat="server" Text="Журнал Exchange"></asp:Label><br />
   7:      <asp:TextBox ID="TextBox1" runat="server" Width="400px"></asp:TextBox>
   8:      <br />
   9:      <asp:Button ID="Button1" runat="server" Text="Анализ" />
  10:      <br />
  11:      <asp:Label ID="lErr1" runat="server" ForeColor="Red"></asp:Label>
  12:      <br />
  13:      <asp:Label ID="lReplay" runat="server"></asp:Label>
  14:      <br /><br />
  15:      <asp:Button ID="Button2" runat="server" Text="Новый журнал" />
  16:      
  17:  </div>
  18:   
  19:      <asp:SqlDataSource ID="SetExchangeReplay" runat="server" 
  20:          ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>" 
  21:          SelectCommand="Update Reklama Set SMTP_Replay=@SMTP_Replay Where Email=@Email">
  22:          <SelectParameters>
  23:          <asp:Parameter Name="SMTP_Replay" Type="String" />
  24:          <asp:Parameter Name="Email" Type="String" />
  25:          </SelectParameters>
  26:      </asp:SqlDataSource>
  27:      
  28:  </asp:Content>

Записи от разных приемных серверов перемешаны, повторяются иногда тысячи раз, но в базу пишется последний ответ от приемного SMTP.


   1:  Partial Class ExchangeAnalize
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   5:          If Not Me.Page.User.Identity.IsAuthenticated Then
   6:              Response.Redirect("Login.aspx")
   7:              Exit Sub
   8:          End If
   9:          '
  10:          If not Roles.IsUserInRole(ConfigurationManager.AppSettings("UserAdminGroup")) Then
  11:              Response.Redirect("//www.vb-net.com/Default.aspx")
  12:          End If
  13:          '
  14:          If Not IsPostBack Then
  15:              TextBox1.Text = Request.QueryString("Log")
  16:          End If
  17:      End Sub
  18:   
  19:      'Анализ журнала вида:
  20:      '2009-12-03 23:16:48 213.85.31.238 OutboundConnectionCommand SMTPSVC2 MAIL - 25 RCPT - TO:<gh0st@mail333.com>
  21:      '2009-12-03 23:16:48 213.85.31.238 OutboundConnectionResponse SMTPSVC2 MAIL - 25 - - 451+4.7.1+Greylisting+in+action,+please+come+back+in+00:01:24 0 0 61 0 22329 SMTP - - - -
  22:      'Записи от разных серверов перемешаны
  23:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  24:          If TextBox1.Text <> "" Then
  25:              Dim MailIP As New System.Collections.ArrayList
  26:              Dim MailTo As New System.Collections.ArrayList
  27:              Dim RCPT_Replay As New System.Collections.ArrayList
  28:              '
  29:              Try
  30:                  Dim LogPath As String = Server.MapPath("Log")
  31:                  Dim FullPath As String = System.IO.Path.Combine(LogPath, TextBox1.Text)
  32:                  Dim RDR1 As New System.IO.StreamReader(FullPath)
  33:                  Dim Str1 As String
  34:                  Dim Log() As String
  35:                  While True
  36:                      Str1 = RDR1.ReadLine
  37:                      If Str1 Is Nothing Then Exit While
  38:                      Log = Str1.Split(" ")
  39:                      If Log.Length > 10 Then
  40:                          If Log(3) = "OutboundConnectionCommand" Then
  41:                              If Log(8) = "RCPT" Then 'попалась команда RCPT
  42:                                  MailIP.Add(Log(2))
  43:                                  MailTo.Add(Log(10).Replace("TO:<", "").Replace(">", ""))
  44:                                  RCPT_Replay.Add("") 'Признак ожидания ответа от этого сервера
  45:                              End If
  46:                          ElseIf Log(3) = "OutboundConnectionResponse" Then
  47:                              'запись ответа
  48:                              Dim ServerNumber As Integer = MailIP.LastIndexOf(Log(2))
  49:                              If ServerNumber >= 0 Then
  50:                                  'Есть ответ именно от этого сервера
  51:                                  If RCPT_Replay(ServerNumber) = "" Then
  52:                                      'Запоминаем только первый ответ от этого сервера, поступивший после команды RCPT
  53:                                      RCPT_Replay(ServerNumber) = Log(10)
  54:                                  End If
  55:                              End If
  56:                          End If
  57:                      End If
  58:                  End While
  59:                  'Все, табла с ответами серверов, ответивших Exchange - готова. Уплотняем ее
  60:                  Dim MailIP_ As New System.Collections.ArrayList
  61:                  Dim MailTo_ As New System.Collections.ArrayList
  62:                  Dim RCPT_Replay_ As New System.Collections.ArrayList
  63:                  For i As Integer = 0 To MailIP.Count - 1
  64:                      Dim NewNumber As Integer = MailTo_.LastIndexOf(MailTo(i))
  65:                      If NewNumber = -1 Then
  66:                          MailIP_.Add(MailIP(i))
  67:                          MailTo_.Add(MailTo(i))
  68:                          RCPT_Replay_.Add(RCPT_Replay(i))
  69:                      End If
  70:                  Next
  71:                  'вывод на форму
  72:                  For i As Integer = 0 To MailIP_.Count - 1
  73:                      lReplay.Text &= MailTo_(i) & " : " & RCPT_Replay_(i) & "<br>"
  74:                  Next
  75:                  'и запись в базу
  76:                  For i As Integer = 0 To MailIP_.Count - 1
  77:                      SetExchangeReplay.SelectParameters("SMTP_Replay").DefaultValue = MailIP_(i) & " : " & RCPT_Replay_(i).ToString.Replace("+", " ")
  78:                      SetExchangeReplay.SelectParameters("Email").DefaultValue = MailTo_(i)
  79:                      SetExchangeReplay.Select(New DataSourceSelectArguments)
  80:                  Next
  81:                  '
  82:              Catch ex As Exception
  83:                  lErr1.Text = ex.Message
  84:              End Try
  85:          End If
  86:      End Sub
  87:   
  88:      Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
  89:          Response.Redirect("ExchangeLoad.aspx")
  90:      End Sub
  91:  End Class

Ну а вспомогательная страничка загрузки журнала работы почтовика на сайт выглядит еще проще:


   1:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
   2:   
   3:      <div style="text-align:left" >
   4:   
   5:      <br /><center><h4>Загрузка журнала рассылки</h4></center><br />
   6:   
   7:      <asp:Label ID="Label1" runat="server" Text="Журнал Exchange"></asp:Label><br />
   8:      <asp:FileUpload ID="FileUpload1" runat="server" Width="400px" />  
   9:      <br />
  10:      <asp:Button ID="Button1" runat="server" Text="Загрузить" />
  11:      <br />
  12:      <asp:Label ID="lErr1" runat="server" ForeColor="Red"></asp:Label>
  13:      </div>
  14:   
  15:  </asp:Content>

   1:  Partial Class ExchangeLoad
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   5:          If Not Me.Page.User.Identity.IsAuthenticated Then
   6:              Response.Redirect("Login.aspx")
   7:              Exit Sub
   8:          End If
   9:          '
  10:          If not Roles.IsUserInRole(ConfigurationManager.AppSettings("UserAdminGroup")) Then
  11:              Response.Redirect("//www.vb-net.com/Default.aspx")
  12:          End If
  13:      End Sub
  14:   
  15:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  16:          If FileUpload1.FileName <> "" Then
  17:              Try
  18:                  Dim LogPath As String = Server.MapPath("Log")
  19:                  Dim LogName As String = System.IO.Path.GetFileName(FileUpload1.FileName)
  20:                  Dim FullPath As String = System.IO.Path.Combine(LogPath, LogName)
  21:                  If My.Computer.FileSystem.FileExists(FullPath) Then
  22:                      My.Computer.FileSystem.DeleteFile(FullPath)
  23:                  End If
  24:                  FileUpload1.SaveAs(FullPath)
  25:                  Response.Redirect("ExchangeAnalize.aspx?Log=" & LogName)
  26:                  Exit Sub
  27:              Catch ex As Exception
  28:                  lErr1.Text = ex.Message
  29:              End Try
  30:          End If
  31:      End Sub
  32:  End Class

Как видите, этот код действительно проще было написать, чем эспериментировать с 758 программами примерно аналогичного назначения, опубликованными на SourceForge.NET.

Удачи!



Comments ( )
Link to this page: //www.vb-net.com/ExchangeLogAnalyzer/index.htm
< THANKS ME>