Imports System.ComponentModel
Imports System.Text
Imports ComponentPro
Imports ComponentPro.Net
Imports ComponentPro.Net.Mail

Namespace Pop3Downloader
	Partial Public Class Pop3Form
		Inherits Form
		Private ReadOnly _exception As Boolean
		Private _mailsDir As String
		Private _workingDir As String
		Private _disconnect As Boolean
		Private _disconnected As Boolean

		''' <summary>
		''' Gets the working directory.
		''' </summary>
		Public ReadOnly Property WorkingDir() As String
			Get
				If _workingDir Is Nothing Then
					If AppDomain.CurrentDomain.BaseDirectory.EndsWith("\") Then
						_workingDir = AppDomain.CurrentDomain.BaseDirectory
					Else
						_workingDir = (AppDomain.CurrentDomain.BaseDirectory & AscW("\"c))
					End If
				End If

				Return _workingDir
			End Get
		End Property

		''' <summary>
		''' Gets the directory which is used to store mails.
		''' </summary>
		Public ReadOnly Property MailsDir() As String
			Get
				If _mailsDir Is Nothing Then
					_mailsDir = WorkingDir

					If Not System.IO.Directory.Exists(_mailsDir & "..\..\Mails") Then
						_mailsDir &= "Mails"
					Else
						_mailsDir &= "..\..\Mails"
					End If
				End If

				Return _mailsDir
			End Get
		End Property

		''' <summary>
		''' Constructor.
		''' </summary>
		Public Sub New()
			Try
				InitializeComponent()
			Catch exc As ComponentPro.Licensing.Mail.UltimateLicenseException
				MessageBox.Show(exc.Message, "Error")
				_exception = True
				Return
			End Try

			cbxSecurity.SelectedIndex = 0
			' The following settings are for a gmail Mailbox:
			'cbxSecurity.SelectedIndex = 2;
			'txtServer.Text = "myserver";
			'txtPort.Text = "995";
			'txtUserName.Text = "someone@gmail.com";
			'txtPassword.Text = "password";

			pop3.Timeout = 20000
			pop3.Proxy.ProxyType = ProxyType.None

			If Not System.IO.Directory.Exists(MailsDir) Then
				System.IO.Directory.CreateDirectory(MailsDir)
			End If

			AddHandler Me.pop3.DisconnectCompleted, AddressOf pop3Client_DisconnectCompleted
			AddHandler Me.pop3.ListMessagesCompleted, AddressOf pop3Client_ListMessagesCompleted
			AddHandler Me.pop3.AuthenticateCompleted, AddressOf pop3Client_LoginCompleted
			AddHandler Me.pop3.ConnectCompleted, AddressOf pop3Client_ConnectCompleted
		End Sub

		''' <summary>
		''' Handles the form's Load event.
		''' </summary>
		''' <param name="e">The event arguments.</param>
		Protected Overrides Sub OnLoad(ByVal e As EventArgs)
			MyBase.OnLoad(e)
			If _exception Then
				Me.Close()
			End If
		End Sub

		''' <summary>
		''' Adds a line to the result listbox.
		''' </summary>
		''' <param name="str">Text to write.</param>
		Private Sub WriteLine(ByVal str As String)
			lsbResult.Items.Add(str)
		End Sub

		''' <summary>
		''' Adds a formatted line to the result listbox.
		''' </summary>
		''' <param name="str">Formatted text to write.</param>
		''' <param name="parameters">The parameters for the formatted text.</param>
		Private Sub WriteLine(ByVal str As String, ParamArray ByVal parameters() As Object)
			lsbResult.Items.Add(String.Format(str, parameters))
		End Sub

		''' <summary>
		''' Handles the Download button's Click event.
		''' </summary>
		''' <param name="sender">The button object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub btnDownload_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnDownload.Click
			' Downloading?
			If btnDownload.Text = "Download" Then
				Dim port As Integer

				' Validate server name, port, user name and password.
				If String.IsNullOrEmpty(txtServer.Text) Then
					MessageBox.Show("Please enter server name", "Error")
					Return
				End If

				Try
					port = Integer.Parse(txtPort.Text)
					If port < 1 OrElse port > 65535 Then
						MessageBox.Show("Invalid port, port must be from 1->65535", "Error")
						Return
					End If
				Catch e1 As FormatException
					MessageBox.Show("Invalid port, port must be from 1->65535", "Error")
					Return
				End Try

				If String.IsNullOrEmpty(txtUserName.Text) Then
					MessageBox.Show("Please enter user name")
					Return
				End If

				If String.IsNullOrEmpty(txtPassword.Text) Then
					MessageBox.Show("Please enter password")
					Return
				End If

				_disconnect = False
				_disconnected = False

				grbAuth.Enabled = False
				grbServer.Enabled = False
				'grbStatus.Enabled = false;

				btnDownload.Text = "Cancel"
				Me.btnDownload.Image = My.Resources.Stop
				btnProxy.Enabled = False

				WriteLine("Connecting to the POP3 server {0}:{1}, security: {2}...", txtServer.Text, port, cbxSecurity.Text)

				Dim sec As SecurityMode

				Select Case cbxSecurity.SelectedIndex
					Case 0
						sec = SecurityMode.None

					Case 1
						sec = SecurityMode.Explicit

					Case Else
						sec = SecurityMode.Implicit
				End Select

				' Asynchronously connect to the POP server.
				pop3.ConnectAsync(txtServer.Text, port, sec)
			Else
				pop3.Cancel()
				' Wait for the last pending operation.
				_disconnect = True
				btnDownload.Enabled = False
			End If
		End Sub


		''' <summary>
		''' Handles the pop3 client's ConnectCompleted event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub pop3Client_ConnectCompleted(ByVal sender As Object, ByVal e As ExtendedAsyncCompletedEventArgs(Of String))
			If e.Error IsNot Nothing Then
				ShowError(e.Error)
				Disconnect()
				Return
			End If

			WriteLine("Connected.")

			Login()
		End Sub

		Private Sub Login()
			' Disconnects if user clicks on the Close button or the Cancel button.
			If _disconnect Then
				Disconnect()
				Return
			End If
			WriteLine("Authorizing as {0}...", txtUserName.Text)

			' Asynchronously login to the server.
			pop3.AuthenticateAsync(txtUserName.Text, txtPassword.Text)
		End Sub


		''' <summary>
		''' Handles the pop3 client's AuthenticateCompleted event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub pop3Client_LoginCompleted(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
			If e.Error IsNot Nothing Then
				ShowError(e.Error)
				Disconnect()
				Return
			End If

			ListMessages()
		End Sub

		Private Sub ListMessages()
			If _disconnect Then
				' Disconnect.
				Disconnect()
				Return
			End If

			WriteLine("Logged In.")

			Try
				Dim info As Pop3MailboxStat = pop3.GetMailboxStat()
				' Print out total messages.
				WriteLine("{0} messages found.", info.MessageCount)
			Catch ex As Exception
				ShowError(ex)
				Disconnect()
				Return
			End Try

			WriteLine("Retrieving the list of messages...")

			' Asynchronously get message list.
			pop3.ListMessagesAsync(Pop3EnvelopeParts.UniqueId Or Pop3EnvelopeParts.MessageInboxIndex Or Pop3EnvelopeParts.Size)
		End Sub

		''' <summary>
		''' Handles the pop3 client's ListMessagesCompleted event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub pop3Client_ListMessagesCompleted(ByVal sender As Object, ByVal e As ExtendedAsyncCompletedEventArgs(Of Pop3MessageCollection))
			If e.Error IsNot Nothing Then
				ShowError(e.Error)
				Disconnect()
				Return
			End If

			_list = e.Result

			DownloadEmls()
		End Sub

		Private _list As Pop3MessageCollection
		Private _index As Integer
		Private _messageSize As Long

		Private Sub DownloadEmls()
			If _disconnect Then
				Disconnect()
				Return
			End If

			WriteLine("List of messages retrieved.")
			_index = 0

			' Get EML messages.
			DownloadEml()
		End Sub

		''' <summary>
		''' Downloads EML files.
		''' </summary>
		Private Sub DownloadEml()
			Retry:
			If _disconnect Then
				' Close the connection.
				Disconnect()
				Return
			End If

			' Not finished?
			If _index < _list.Count Then
				Dim message As Pop3Message = _list(_index)
				_index += 1
				Dim fileName As String = MailsDir & "\" & GetFilename(message.UniqueId) & ".eml"

				If System.IO.File.Exists(fileName) Then
					WriteLine("Skipping message '{0}'...", message.UniqueId)
					GoTo Retry
				End If

				_messageSize = message.Size
				progressBar.Value = 0

				WriteLine("Retrieving message '{0}'...", message.UniqueId)

				' Continue downloading messages.
				pop3.DownloadMessageAsync(message.MessageInboxIndex, fileName, AddressOf pop3Client_DownloadMessageCompleted, Nothing)
			Else
				' Close the connection when finish.
				Disconnect()
			End If
		End Sub

		''' <summary>
		''' Handles the pop3 client's DownloadMessageCompleted event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub pop3Client_DownloadMessageCompleted(ByVal sender As Object, ByVal e As ExtendedAsyncCompletedEventArgs(Of Long))
			If e.Error IsNot Nothing Then
				ShowError(e.Error)
				Disconnect()
				Return
			End If

			progressBarTotal.Value = _index * 100 \ _list.Count

			' Download EML files.
			DownloadEml()
		End Sub

		''' <summary>
		''' Disconnects to the POP server.
		''' </summary>
		Private Sub Disconnect()
			WriteLine("Disconnecting...")
			pop3.DisconnectAsync(False)
		End Sub

		Private Sub SetDisconnectedStatus()
			WriteLine("Disconnected.")

			grbAuth.Enabled = True
			grbServer.Enabled = True
			'grbStatus.Enabled = true;

			btnDownload.Text = "Download"
			Dim resources As New System.ComponentModel.ComponentResourceManager(GetType(Pop3Downloader.Pop3Form))
			Me.btnDownload.Image = CType(resources.GetObject("btnDownload.Image"), System.Drawing.Image)
			btnProxy.Enabled = True
			btnDownload.Enabled = True

			progressBarTotal.Value = 0
			progressBar.Value = 0
		End Sub

		''' <summary>
		''' Handles the pop3 client's DisconnectCompleted event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub pop3Client_DisconnectCompleted(ByVal sender As Object, ByVal e As ExtendedAsyncCompletedEventArgs(Of String))
			If e.Error IsNot Nothing Then
				ShowError(e.Error)
			End If

			SetDisconnectedStatus()
		End Sub

		''' <summary>
		''' Shows an error.
		''' </summary>
		''' <param name="exc">Exception object.</param>
		Private Shared Sub ShowError(ByVal exc As Exception)
			Dim str As String

			If exc.InnerException IsNot Nothing Then
				str = String.Format(Nothing, "An error occurred: {0}" & vbCrLf & "{1}", exc.Message, exc.InnerException.Message)
			Else
				str = String.Format(Nothing, "An error occurred: {0}", exc.Message)
			End If

			MessageBox.Show(str, "Error")
		End Sub

		''' <summary>
		''' Returns a uniquely correct file name from the specified unique message ID.
		''' </summary>
		''' <param name="uniqueId">The unique id.</param>
		''' <returns>The corrected file name.</returns>
		Private Shared Function GetFilename(ByVal uniqueId As String) As String
			' Characters allowed in the filename
			'string allowed = " .-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";            
			Const allowed As String = " .-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

			' Replace invalid charactes with its hex representation
			Dim sb As New StringBuilder()
			For i As Integer = 0 To uniqueId.Length - 1
				If allowed.IndexOf(uniqueId.Chars(i)) < 0 Then
					sb.AppendFormat("_{0:X2}", System.Convert.ToInt32(uniqueId.Chars(i)))
				Else
					sb.Append(uniqueId.Chars(i))
				End If
			Next i
			Return sb.ToString()
		End Function

		''' <summary>
		''' Handles the proxy settings button's Click event.
		''' </summary>
		''' <param name="sender">The pop3 client object.</param>
		''' <param name="e">The event arguments.</param>
		Private Sub btnProxy_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnProxy.Click
			' Creates new ProxySettings dialog and initialize all parameters.
			Dim proxy As New ProxySettings()
			proxy.ProxyServer = pop3.Proxy.Server
			proxy.Port = pop3.Proxy.Port
			proxy.UserName = pop3.Proxy.UserName
			proxy.Password = pop3.Proxy.Password
			proxy.AuthenticationMethod = pop3.Proxy.AuthenticationMethod
			proxy.Type = pop3.Proxy.ProxyType
			' Popups the ProxySetting dialog.
			If proxy.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
				' Updates settings.
				pop3.Proxy.Server = proxy.ProxyServer
				If proxy.Port > 0 Then
					pop3.Proxy.Port = proxy.Port
				End If
				pop3.Proxy.UserName = proxy.UserName
				pop3.Proxy.Password = proxy.Password
				pop3.Proxy.AuthenticationMethod = proxy.AuthenticationMethod
				pop3.Proxy.ProxyType = proxy.Type
			End If
		End Sub

		''' <summary>
		''' Handles the form's Closing event.
		''' </summary>
		''' <param name="e">The event arguments.</param>
		Protected Overrides Sub OnClosing(ByVal e As System.ComponentModel.CancelEventArgs)
			' Have a connection?
			If btnDownload.Text = "Cancel" Then
				' We want to disconnect.
				_disconnect = True

				' While the last operation is not completed.
				Do While Not _disconnected
					System.Threading.Thread.Sleep(50)
					System.Windows.Forms.Application.DoEvents()
				Loop
			End If

			MyBase.OnClosing(e)
		End Sub

		Private Sub pop3Client_Progress(ByVal sender As Object, ByVal e As Pop3ProgressEventArgs) Handles pop3.Progress
			If e.State = MailClientTransferState.Downloading Then
				progressBar.Value = CInt(Fix(e.CalculatePercentage(_messageSize)))
			End If
		End Sub
	End Class
End Namespace
