Archive for the ‘Programming’ Category

Enable POP3 in Gmail

Friday, January 6th, 2012

To enable POP3 in Gmail:

  1. Sign in to Gmail.
  2. Click the gear icon in the upper-right and select Mail settings .
  3. Click Forwarding and POP/IMAP.
  4. SSelect Enable POP for all mail or Enable POP for mail that arrives from now on.
  5. Now you are able to connect to your Gmail account with Mail.dll POP3 library.

Remember that Gmail only allows secure SSL connections so we need to use ConnectSSL method.

Enable IMAP in Gmail

Friday, January 6th, 2012

To enable IMAP in Gmail:

  1. Sign in to Gmail.
  2. Click the gear icon in the upper-right and select Mail settings .
  3. Click Forwarding and POP/IMAP.
  4. Select Enable IMAP.
  5. Now you are able to connect to your Gmail account with Mail.dll IMAP library.

Remember that Gmail only allows secure SSL connections so we need to use ConnectSSL method.

INotifyPropertyChanged with custom targets

Tuesday, May 24th, 2011

Wouldn’t it be nice to have a simple attribute instead of backing field madness in Silverlight/WPF? Just like this:

public class MainViewModel : ViewModelBase
{
    [NotifyPropertyChanged]
    public string Title { get; set; }
}

You can use PostSharp for that, you should at least use lambda expressions instead of strings.

Here’s how to do it without 3rd party software:

1.
In your project declare base view model:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged
        = delegate { };

    protected void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged(
            this,
            new PropertyChangedEventArgs(propertyName));
    }
};

2.
Declare the attribute

It will be used to mark properties that should raise the PropertyChanged event.

[AttributeUsage(AttributeTargets.Property)]
public class NotifyPropertyChangedAttribute : Attribute
{
}

3.
Create new MSBuildTasks library project (it can be in different solution)

Add references to:

  • Mono.Cecil.dll
  • Mono.Cecil.Pdb.dll (this needed so Cecil can updated pdb file, which is need for debugging the modified assembly)
  • MSBuild assemblies

4.
Create new MSBuild task

It will inject PropertyChanged event invocation on properties marked with NotifyPropertyChanged attribute.

using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace MSBuildTasks
{
    public class NotifyPropertyChangedTask : Task
    {
        public override bool Execute()
        {
            InjectMsil();
            return true;
        }

        private void InjectMsil()
        {
            var assemblyDefinition = AssemblyDefinition.ReadAssembly(
                AssemblyPath,
                new ReaderParameters { ReadSymbols = true });

            var module = assemblyDefinition.MainModule;  

            foreach (var type in module.Types)
            {
                foreach (var prop in type.Properties)
                {
                    foreach (var attribute in prop.CustomAttributes)
                    {
                        string fullName = attribute.Constructor.DeclaringType.FullName;
                        if (fullName.Contains("NotifyPropertyChanged"))
                        {
                            InjectMsilInner(module, type, prop);
                        }
                    }
                }
            }
            assemblyDefinition.Write(
                this.AssemblyPath,
                new WriterParameters { WriteSymbols = true });
        }

        private static void InjectMsilInner(
            ModuleDefinition module,
            TypeDefinition type,
            PropertyDefinition prop)
        {
            var msilWorker = prop.SetMethod.Body.GetILProcessor();
            var ldarg0 = msilWorker.Create(OpCodes.Ldarg_0);

            MethodDefinition raisePropertyChangedMethod =
                FindRaisePropertyChangedMethod(type);
            if (raisePropertyChangedMethod == null)
                throw new Exception(
                    "RaisePropertyChanged method was not found in type "
                    + type.FullName);

            var raisePropertyChanged = module.Import(
                raisePropertyChangedMethod);
            var propertyName = msilWorker.Create(
                OpCodes.Ldstr,
                prop.Name);
            var callRaisePropertyChanged = msilWorker.Create(
                OpCodes.Callvirt,
                raisePropertyChanged);

            msilWorker.InsertBefore(
                prop.SetMethod.Body.Instructions[
                    prop.SetMethod.Body.Instructions.Count - 1],
                ldarg0);

            msilWorker.InsertAfter(ldarg0, propertyName);
            msilWorker.InsertAfter(
                propertyName,
                callRaisePropertyChanged);
        }

        private static MethodDefinition FindRaisePropertyChangedMethod(
            TypeDefinition type)
        {
            foreach (var method in type.Methods)
            {
                if (method.Name == "RaisePropertyChanged"
                    && method.Parameters.Count == 1
                    && method.Parameters[0].ParameterType.FullName
                        == "System.String")
                {
                    return method;
                }
            }
            if (type.BaseType.FullName == "System.Object")
                return null;
            return FindRaisePropertyChangedMethod(
                type.BaseType.Resolve());
        }

        [Required]
        public string AssemblyPath { get; set; }
    }
}

5.
Compile the task assembly,

and copy it to “$(SolutionDir)..libMSBuildMSBuildTasks.dll” folder along with Mono.Cecil.dll and Mono.Cecil.Pdb.dll assemblies.

4.
Finally modify your Silverlight/WPF project (.csproj file):

<Project>
  ...
  <UsingTask TaskName="MSBuildTasks.NotifyPropertyChangedTask" AssemblyFile="$(SolutionDir)..libMSBuildMSBuildTasks.dll" />
  <Target Name="AfterCompile">
    <MSBuildTasks.NotifyPropertyChangedTask AssemblyPath="$(ProjectDir)obj$(Configuration)$(TargetFileName)" />
  </Target>
</Project>

Voila! Enjoy!

Here’s the source code of the MSBuildTasks project:
MSBuildTasks

Most ideas in this blog post are those of Marcin Obel but he’s to lazy to write a blog post.

Update the bound property as the user types

Friday, April 15th, 2011

In Silverlight there is no way for 2-way-bound TextBox to update it’s bound property every time user hits a key on the keyboard.

In WPF you could use UpdateSourceTrigger=PropertyChanged, which is not available in Silverlight.

Anyway here’s the nice Silverlight behavior to add this feature to Silverlight:

// Please note that Behavior class is from
// System.Windows.Interactivity assembly that is available
// after you have installed Expression Blend.
// (C:Program FilesMicrosoft SDKsExpressionBlendSilverlightv4.0
//  LibrariesSystem.Windows.Interactivity.dll)

public class UpdateOnTextChangedBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.TextChanged +=
                AssociatedObject_TextChanged;
    }

    void AssociatedObject_TextChanged(
        object sender,
        TextChangedEventArgs e)
    {
        BindingExpression binding =
            this.AssociatedObject.GetBindingExpression(
                TextBox.TextProperty);

        if (binding != null)
        {
            binding.UpdateSource();
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        this.AssociatedObject.TextChanged -=
                AssociatedObject_TextChanged;
    }
}

And usage:

<TextBox Text="{Binding Title, Mode=TwoWay}">
   <i:Interaction.Behaviors>
      <local:UpdateOnTextChangedBehavior/>
   </i:Interaction.Behaviors>
</TextBox>

Received an unexpected EOF or 0 bytes from the transport stream

Monday, January 17th, 2011

When you are getting the following error:
Received an unexpected EOF or 0 bytes from the transport stream.
while using ConnectSSL() most likely your server incorrectly advertises TLS support. You might be required to use SSL2 or SSL3.

This article describes how to force SSL3 or SSL2 in Mail.dll or in regular .NET SslStream class.

Here’s the code for Mail.dll

// C# version
using (Imap client = new Imap())
{
   // Force to use SSL3
   client.SSLConfiguration.EnabledSslProtocols = SslProtocols.Ssl3;
   // Ignore certificate errors
   client.ServerCertificateValidate += (sender, e) => { e.IsValid = true; };
   client.ConnectSSL("imap.gmail.com");
   client.Login("user@gmail.com", "password");

   client.SelectInbox();
   foreach(long uid in client.SearchFlag(Flag.Unseen))
   {
       string eml = client.GetMessageByUID(uid);
       IMail email = new MailBuilder().CreateFromEml(eml);
       Console.WriteLine(email.Subject);
   }
   client.Close();
}
' VB.NET version

Using client As New Imap()
	' Force to use SSL3
	client.SSLConfiguration.EnabledSslProtocols = SslProtocols.Ssl3
	' Ignore certificate errors
	AddHandler client.ServerCertificateValidate, AddressOf Validate
	client.ConnectSSL("imap.gmail.com")
	client.Login("user@gmail.com", "password")

	client.SelectInbox()
	For Each uid As Long In client.SearchFlag(Flag.Unseen)
		Dim eml As String = client.GetMessageByUID(uid)
		Dim email As IMail = New MailBuilder().CreateFromEml(eml)
		Console.WriteLine(email.Subject)
	Next
	client.Close()
End Using

Private Sub ValidateCerificate( _
    ByVal sender As Object, _
    ByVal e As ServerCertificateValidateEventArgs)

    Const ignoredErrors As SslPolicyErrors = _
        SslPolicyErrors.RemoteCertificateChainErrors Or _
        SslPolicyErrors.RemoteCertificateNameMismatch

    If (e.SslPolicyErrors And Not ignoredErrors) = SslPolicyErrors.None Then
        e.IsValid = True
        Return
    End If
    e.IsValid = False
End Sub

Here’s the code for SslStream:

// C# version
const string host = "imap.gmail.com";

Socket socket = new Socket(
   AddressFamily.InterNetwork,
   SocketType.Stream,
   ProtocolType.Tcp);
socket.Connect(host, 993);
using(SslStream ssl = new SslStream(new NetworkStream(socket), true))
{
   ssl.AuthenticateAsClient(
      host,
      new X509CertificateCollection(),
      SslProtocols.Ssl3,
      true);
   using(StreamReader reader = new StreamReader(ssl))
   {
       Console.WriteLine(reader.ReadLine());
   }
}
socket.Close();
' VB.NET version

Const  host As String = "imap.gmail.com"

Dim socket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
socket.Connect(host, 993)
Using ssl As New SslStream(New NetworkStream(socket), True)
	ssl.AuthenticateAsClient(host, New X509CertificateCollection(), SslProtocols.Ssl3, True)
	Using reader As New StreamReader(ssl)
		Console.WriteLine(reader.ReadLine())
	End Using
End Using
socket.Close()