<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
    <title>The League Of Paul</title>
    <link>http://www.theleagueofpaul.com/</link>
    <atom:link href="http://www.theleagueofpaul.com/rss.xml" rel="self" type="application/rss+xml" />
    <description></description>
    <language>en-au</language>
    <pubDate>Thu, 02 Feb 2012 22:49:27 +1100</pubDate>
    <lastBuildDate>Thu, 02 Feb 2012 22:49:27 +1100</lastBuildDate>

    
    <item>
      <title>Code52 - I'm going to be busy this year</title>
      <link>http://www.theleagueofpaul.com/code52.html</link>
      <pubDate>Mon, 16 Jan 2012 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/code52</guid>
      <description>&lt;p&gt;&lt;img src=&quot;http://code52.org/img/logo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yup, 52 open source projects, one a week. It's going to be a hectic year. Join in, if you dare.&lt;/p&gt;

&lt;p&gt;So far we've made &lt;a href=&quot;http://code52.org/DownmarkerWPF&quot;&gt;MarkPad&lt;/a&gt; (week 1), a WPF/Metro MarkDown editor (which I've used to write this post!)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://code52.org/DownmarkerWPF/screenshot.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;and &lt;a href=&quot;http://code52.org/jibbr/&quot;&gt;Jibbr&lt;/a&gt; (week 2), a bot platform for Jabbr. I contributed the IronJS layer (although not 100% complete) which compiles CoffeeScript compiler, then CoffeeScript -&gt; Javascript, and allows Hubot scripts. Unfortunately, I couldn't nut out the 'http' object properly, because of the weird syntax, so not all scripts work.&lt;/p&gt;

&lt;p&gt;Next week could be intense.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MahApps.Metro 0.4 Released!</title>
      <link>http://www.theleagueofpaul.com/mahapps-metro-04-released.html</link>
      <pubDate>Fri, 06 Jan 2012 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/mahapps-metro-04-released</guid>
      <description>&lt;p&gt;39 commits from three authors later, MahApps 0.4 is now out! There is a few breaking changes, but nothing significant has been removed so it shouldn't be &lt;em&gt;too&lt;/em&gt; much to migrate.&lt;/p&gt;

&lt;p&gt;Grab the latest source from &lt;a href=&quot;https://github.com/MahApps/MahApps.Metro&quot;&gt;GitHub&lt;/a&gt;, the latest release from &lt;a href=&quot;http://nuget.org/packages/MahApps.Metro&quot;&gt;NuGet&lt;/a&gt; or the latest demo via &lt;a href=&quot;http://deployment.mahapps.com/metro/setup.exe&quot;&gt;ClickOnce&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;What is fixed or changed?&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Squared corners on &lt;code&gt;ComboBox&lt;/code&gt;/&lt;code&gt;ComboBoxItem&lt;/code&gt; and &lt;code&gt;TextBox&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ComboBoxItems&lt;/code&gt; now adjust width correctly&lt;/li&gt;
&lt;li&gt;Fixed accent colour on &lt;code&gt;ToggleSwitch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Many styles are separated out into their own files. This does mean if you want to include everything, you need to include more files, but it's a cleaner, more discoverable approach.&lt;/li&gt;
&lt;li&gt;Accessor keys now work on &lt;code&gt;TabItems&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TabItems&lt;/code&gt; now work with datatemplates or plain strings (only worked with strings previously)&lt;/li&gt;
&lt;li&gt;Better design time support for several controls&lt;/li&gt;
&lt;li&gt;Fixed a few animations that crashed Visual Studio's designer&lt;/li&gt;
&lt;li&gt;A few extra icons from &lt;a href=&quot;http://templarian.com/project_windows_phone_icons/&quot;&gt;Templarian's Project: Windows Phone Icons&lt;/a&gt; added&lt;/li&gt;
&lt;li&gt;The assemblies are now strong named and signed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ToUpperConverter and ToLowerConverter are now markup extensions rather than converters&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;To use MahApps.Metro, add it via NuGet (&lt;code&gt;Install-Package MahApps.Metro&lt;/code&gt;), then to any of your windows add the following to resources&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   &amp;lt;Window.Resources&amp;gt;
        &amp;lt;ResourceDictionary&amp;gt;
            &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Icons/MergedResources.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml&quot; /&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml&quot; /&amp;gt;
            &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
        &amp;lt;/ResourceDictionary&amp;gt;
      &amp;lt;/Window.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Due to existing WPF bugs, we can't add this to App resources.&lt;/p&gt;

&lt;h2&gt;What is new?&lt;/h2&gt;

&lt;h3&gt;WindowCommands control.&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://images.theleagueofpaul.com/WindowCommands.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It might look pretty boring but the WindowCommands is a combination of the traditional minimise, maximise/restore and close button. You don't &lt;em&gt;have&lt;/em&gt; to use this, but it is there if you want those controls and is part of the new &lt;code&gt;MetroWindow&lt;/code&gt; (more on that later).&lt;/p&gt;

&lt;p&gt;WindowCommands is made up of three text buttons using the Marlett font. I discovered the wonders of &lt;a href=&quot;http://www.nichesoftware.co.nz/blog/2011-10/540/custom-chrome&quot;&gt;Marlett via Bevan's Niche Software blog&lt;/a&gt;. By using text instead of images or instead of XAML, its simplier, smaller in filesize and scales so much better.&lt;/p&gt;

&lt;h3&gt;MetroWindow&lt;/h3&gt;

&lt;p&gt;This comes to MahApps.Metro via &lt;a href=&quot;https://twitter.com/#!/distantcam&quot;&gt;Cameron MacFarland&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While you still need to add the stack of ResourceDictionaries to the Window, MetroWindow simplifies the initial setup of MahApps.Metro.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;Controls:MetroWindow...&lt;/code&gt; instead of &lt;code&gt;&amp;lt;Window...&lt;/code&gt; automatically adds the dropshadow behaviour, sets up Window movement (which is 'lost' when you remove default window chrome), the previously mentioned WindowCommands, and adds in the animated Metro Content Control.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://images.theleagueofpaul.com/metrowindowcontrol.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You don't &lt;em&gt;have&lt;/em&gt; to use this control, but I highly recommend you do just because it saves adding all that stuff by hand.&lt;/p&gt;

&lt;h3&gt;Buttons for everybody!&lt;/h3&gt;

&lt;p&gt;There are so many buttons!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard Button&lt;/strong&gt;&lt;br/&gt;
This just replaces the standard button when you drop in the library, nothing fancy to activate it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MetroCircleButton&lt;/strong&gt;&lt;br/&gt;
&quot;Standard&quot; circle button, designed for icons.&lt;br/&gt;
Add the following to a button to apply this style: &lt;code&gt;Style=&quot;{DynamicResource MetroCircleButtonStyle}&quot;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AppBarButton&lt;/strong&gt;&lt;br/&gt;
Inspired by Windows Phone 7's app bar buttons which are a circle button with text underneath.&lt;br/&gt;
&lt;img src=&quot;http://images.theleagueofpaul.com/appbarbuttoncontrol.png&quot; alt=&quot;&quot; /&gt;&lt;br/&gt;
Use the &lt;code&gt;AppBarButton&lt;/code&gt; control to use this type of button.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  &amp;lt;Controls:AppBarButton
                              VerticalAlignment=&quot;Top&quot;
                              MetroImageSource=&quot;{StaticResource appbar_barcode}&quot;
                              Foreground=&quot;{DynamicResource BlackBrush}&quot;
                              Content=&quot;scan&quot; /&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Square button&lt;/strong&gt;&lt;br/&gt;
Another WP7 styled button, this time just for text. Like all the buttons here, has normal, clicked, and hover states.&lt;br/&gt;
&lt;img src=&quot;http://images.theleagueofpaul.com/squarebutton04.png&quot; alt=&quot;&quot; /&gt;&lt;br/&gt;
Add the following to a button to apply this style: &lt;code&gt;Style=&quot;{DynamicResource SquareButtonStyle}&quot;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FlatButton&lt;/strong&gt;&lt;br/&gt;
Oh look! Another WP7 styled button! This sort of button can be found when you're making a call - all of the controls (hang up, keypad, etc) are 'flat buttons'.&lt;br/&gt;
&lt;img src=&quot;http://images.theleagueofpaul.com/flatbutton04.png&quot; alt=&quot;&quot; /&gt;&lt;br/&gt;
Flat button lives in &lt;code&gt;&amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml&quot; /&amp;gt;&lt;/code&gt;, so you'll need to import that as well to use it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    
    <item>
      <title>WiX Auto-generation without using XLST</title>
      <link>http://www.theleagueofpaul.com/wix-autogen-without-xlst.html</link>
      <pubDate>Sat, 31 Dec 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/wix-autogen-without-xlst</guid>
      <description>&lt;blockquote&gt;&lt;p&gt;A quick recap: &lt;a href=&quot;http://wix.sourceforge.net/&quot;&gt;WiX&lt;/a&gt; (Windows Installer XML) is as the name implies XML based system of building &lt;em&gt;installers&lt;/em&gt;. You gather your bunch of XML files that describe how you want the installer to look, function, etc, and when compiled with WiX, out pops an MSI. WiX is pretty much the defacto installer generation library - ClickOnce is balls, VS Installer is deprecated, and many other options either cost a fortune or simply just aren't as robust as WiX. While WiX is super powerful, it does have a bit of a learning curve which has given it a bit of a stigma.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I'm not too full of myself to say there are certain techs I suck at and don't want to learn. XLS/T is one of them. &lt;a href=&quot;http://wix.sourceforge.net/&quot;&gt;WiX&lt;/a&gt; (Windows Installer XML) is as the name implies XML based, and unfortunately that also means using XLST to transform any of the auto generated content from &lt;a href=&quot;http://wix.sourceforge.net/manual-wix3/heat.htm&quot;&gt;Heat&lt;/a&gt; (the &quot;Harvest&quot; tool which takes a directory and spits out a WiX fragment) into something a bit more usable. That's not to say Heat's output is useless, just for my particular configuration it wasn't &quot;right&quot;.&lt;/p&gt;

&lt;p&gt;There were a few approaches I could have gone for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn XLST - that sounded painful&lt;/li&gt;
&lt;li&gt;Use C# to transform the XML generated by Heat.exe - more reasonable but still seemed pointless&lt;/li&gt;
&lt;li&gt;Use C# to just generate the XML from a directory, skipping Heat.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;While I'm my approach would incur the wrath of &lt;a href=&quot;http://twitter.com/robmen&quot;&gt;Rob Mensching&lt;/a&gt;, by using C# - a language I'm familiar with, a language I can &lt;em&gt;maintain&lt;/em&gt;, I get a more robust install process.&lt;/p&gt;

&lt;p&gt;As mentioned, WiX is all XML. .NET has a few ways to work with XML, but the absolute best? Linq-to-XML (XLinq).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static void Main(string[] args)
{
    var input = args[0];
    var output = args[1];

    XNamespace ns = &quot;http://schemas.microsoft.com/wix/2006/wi&quot;;
    var document = new XDocument();
    var files = Directory.GetFiles(input).Select(x =&amp;gt; new FileInfo(x));
    var diretories = Directory.GetDirectories(input).Select(x =&amp;gt; new DirectoryInfo(x));
    var i = 1;
    document.Add(
        new XElement(ns + &quot;Wix&quot;,
            new XElement(ns + &quot;Fragment&quot;,
                new XElement(ns + &quot;DirectoryRef&quot;,
                new XAttribute(&quot;Id&quot;, &quot;INSTALLLOCATION&quot;),
                    new XElement(ns + &quot;Component&quot;,
                    new XAttribute(&quot;Id&quot;, &quot;MahChatsFiles&quot;),
                    new XAttribute(&quot;Guid&quot;, &quot;4ac9e15b-89c1-4fde-84c9-286ef9d24af8&quot;),
                    new XElement(ns + &quot;RegistryKey&quot;,
                        new XAttribute(&quot;Root&quot;, &quot;HKCU&quot;),
                        new XAttribute(&quot;Key&quot;, @&quot;Software\MahApps\MahChats&quot;),
                        new XAttribute(&quot;Action&quot;, &quot;createAndRemoveOnUninstall&quot;),
                            new XElement(ns + &quot;RegistryValue&quot;,
                                new XAttribute(&quot;Name&quot;, &quot;Version&quot;),
                                new XAttribute(&quot;Value&quot;, &quot;[ProductVersion]&quot;),
                                new XAttribute(&quot;Type&quot;, &quot;string&quot;),
                                new XAttribute(&quot;KeyPath&quot;, &quot;yes&quot;))),
                    new XElement(ns + &quot;CreateFolder&quot;),
                        new XElement(ns + &quot;RemoveFolder&quot;,
                            new XAttribute(&quot;Id&quot;, &quot;RemoveAppRootDirectory&quot;),
                            new XAttribute(&quot;On&quot;, &quot;uninstall&quot;)),
                            files.Select(x =&amp;gt; new XElement(ns + &quot;File&quot;,
                                new XAttribute(&quot;Id&quot;, x.Name.Replace('-', '_')),
                                new XAttribute(&quot;Source&quot;, x.FullName)))
                            ),
                            diretories.Select(x =&amp;gt; new XElement(ns + &quot;Directory&quot;,
                                new XAttribute(&quot;Id&quot;, x.Name.ToUpper()),
                                new XAttribute(&quot;Name&quot;, x.Name),
                                new XElement(ns + &quot;Component&quot;,
                                    new XAttribute(&quot;Id&quot;, &quot;ProductComponent&quot; + i),
                                    new XAttribute(&quot;Guid&quot;, Guid.NewGuid()),
                                    new XElement(ns + &quot;RegistryKey&quot;,
                                    new XAttribute(&quot;Root&quot;, &quot;HKCU&quot;),
                                    new XAttribute(&quot;Key&quot;, @&quot;Software\MahApps\MahChats&quot;),
                                    new XAttribute(&quot;Action&quot;, &quot;createAndRemoveOnUninstall&quot;),
                                        new XElement(ns + &quot;RegistryValue&quot;,
                                            new XAttribute(&quot;Name&quot;, &quot;Version&quot;),
                                            new XAttribute(&quot;Value&quot;, &quot;[ProductVersion]&quot;),
                                            new XAttribute(&quot;Type&quot;, &quot;string&quot;),
                                            new XAttribute(&quot;KeyPath&quot;, &quot;yes&quot;))),
                                     new XElement(ns + &quot;RemoveFolder&quot;,
                                        new XAttribute(&quot;Id&quot;, &quot;ProductComponent&quot; + i++),
                                        new XAttribute(&quot;On&quot;, &quot;uninstall&quot;)),
                                    x.GetFiles().Select(y =&amp;gt; new XElement(ns + &quot;File&quot;,
                                        new XAttribute(&quot;Id&quot;, y.Name),
                                        new XAttribute(&quot;Source&quot;, y.FullName)))))
                                    )))));

    document.Save(output);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm sure many of you might balk at this gigantic statement, but (to me) it's more readable than XLST. It is a very &lt;em&gt;specific&lt;/em&gt; query - for MahChats I'm toying with installing in a per-user, non-UAC required environment, like ClickOnce. That means it gets installed into &lt;code&gt;C:\Users\User\AppData\Local\MahChats&lt;/code&gt;, and because it doesn't require elevation/is installed to that specific directory, I need to add in some registry keys and extra information.&lt;/p&gt;

&lt;p&gt;The ultimate goal of this was to have the setup being generated by TeamCity as part of the CI build process. Now my build process looks like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build project using Visual Studio Solution file&lt;/li&gt;
&lt;li&gt;Use my &quot;BurnTransformer&quot; (specifically, &lt;code&gt;BurnTransformer.exe %teamcity.build.checkoutDir%/src/MahChats/Bin/Release/ %teamcity.build.checkoutDir%\src\MahChats.Setup\fragment.wxs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Build WiX project using a different Visual Studio Solution file&lt;/li&gt;
&lt;li&gt;copy artifacts from &lt;code&gt;src\MahChats.Setup\bin\**\*.msi&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Why not ClickOnce which is &quot;easier&quot; and I've covered before? It's evil. Next month I hope to put out a &quot;short horror story&quot; on how ClickOnce is evil, and how to get similar installer experience (the good bits, that is) with WiX and other alternatives.&lt;/p&gt;

&lt;p&gt;This means that yes, MahChats Alpha &lt;em&gt;is&lt;/em&gt; getting closer. I still need a good/easy way to do updates, and I'm still in the process of setting up a custom NuGet server for (optional) plugins. On the installer front, I &lt;em&gt;really&lt;/em&gt; want to take a look at WiX 3.6 which is currently in beta, as it has a way to do custom bootstrapper GUI's in WPF!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Shotty Rackspace upload plugin</title>
      <link>http://www.theleagueofpaul.com/shotty-rackspace-plugin.html</link>
      <pubDate>Tue, 20 Dec 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/shotty-rackspace-plugin</guid>
      <description>&lt;p&gt;&lt;a href=&quot;http://www.hanselman.com/blog/ScottHanselmans2011UltimateDeveloperAndPowerUsersToolListForWindows.aspx&quot;&gt;Scott Hanselman's 2011 Dev Tools&lt;/a&gt; list included a new (to me) screenshot utility, &lt;a href=&quot;http://shotty.devs-on.net/en/Overview.aspx&quot;&gt;Shotty&lt;/a&gt;. What's nice about these newer screenshot utilities is the ability to respect alpha transparency and drop shadows created by Aero - it's much nicer than having to line various windows up with a white background before taking a screenshot.&lt;/p&gt;

&lt;p&gt;For all my blog posts I use Rackspace to store images, which unfortunately Shotty doesn't handle natively. Shotty is however a .NET 2.0 app, with a &lt;a href=&quot;http://shotty.devs-on.net/en/Support.aspx&quot;&gt;plugin API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/MahApps/Shotty.Rackspace&quot;&gt;code is up on GitHub&lt;/a&gt;, or you can &lt;a href=&quot;http://deployment.mahapps.com/Shotty.Rackspace.0.1.zip&quot;&gt;download the compiled plugin&lt;/a&gt; - just unzip it into your Shotty directory.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://images.theleagueofpaul.com/shottysettings.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At this stage, there is only root container level upload access. Later on, I'll add the ability to put it into subcontainers.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using the new NugetGallery for your own purposes</title>
      <link>http://www.theleagueofpaul.com/nugetgallery.html</link>
      <pubDate>Thu, 08 Dec 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/nugetgallery</guid>
      <description>&lt;blockquote&gt;&lt;p&gt;Disclaimer: I only had 3-4 hours sleep last night, so I'm &lt;em&gt;zonked&lt;/em&gt; but still coding. Please forgive me for the code sins I've probably commited and haven't realised.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;For &lt;a href=&quot;http://www.mahchats.com&quot;&gt;MahChats&lt;/a&gt; I'm wanting to seperate out the individual plugins from the main distribution - not everybody uses Skype or Messenger, so why have &quot;bloat&quot;? &lt;a href=&quot;http://twitter.com/mabster&quot;&gt;Matt Hamiliton&lt;/a&gt; demonstrated how easy it can be to have &lt;a href=&quot;http://matthamilton.net/nuget-for-plug-ins&quot;&gt;Nuget powered extensions in your app&lt;/a&gt;, taking care of browsing, distribution and updates.&lt;/p&gt;

&lt;p&gt;I'm not one to &lt;em&gt;underengineer&lt;/em&gt; things like Matt so a simple semi-static list that required manually uploading the files via FTP just wasn't going to cut it for me. However, &lt;a href=&quot;/going-saas&quot;&gt;I don't like paying too much for my hosting&lt;/a&gt; particularly for FOSS projects, so a combination of Rackspace and AppHarbor is &lt;em&gt;perfect&lt;/em&gt;. After all, the new Nuget server is hosted on Azure, and AppHarbor is &lt;em&gt;&quot;Azure done right&quot;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After checking out the &lt;a href=&quot;https://github.com/NuGet/NuGetGallery&quot;&gt;Nuget Gallery source&lt;/a&gt;, it was pleasantly clear and easy to see what I'd have to replace if I wanted to change &quot;cloud provider&quot; for package storage.&lt;/p&gt;

&lt;p&gt;Note: To compile the Nuget Gallery, you'll need to install the Azure SDK from Web Platform Installer &lt;em&gt;or&lt;/em&gt; just delete all references to Azure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Modify &lt;strong&gt;App_Start\PackageStoreType.cs&lt;/strong&gt;. I just added in a fourth value, Rackspace.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; public enum PackageStoreType
 {
     NotSpecified = 0,
     FileSystem,
     AzureStorageBlob,
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt; became&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; public enum PackageStoreType
 {
     NotSpecified = 0,
     FileSystem,
     AzureStorageBlob,
     Rackspace
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You'll then need to change your configure change config file to use new value in the enum. While we're at it, you may as well store the values of your container name, Rackspace username, and Rackspace token in the config. You can find your API token at in the &lt;a href=&quot;https://manage.rackspacecloud.com/APIAccess.do&quot;&gt;Rackspace Management Console&lt;/a&gt;, once you've logged in.  Open up web.config, look for the AppSettings section and add&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; &amp;lt;add key=&quot;PackageStoreType&quot; value=&quot;3&quot; /&amp;gt;
 &amp;lt;add key=&quot;ContainerName&quot; value=&quot;&amp;lt;NameOfTheContainerToUse&amp;gt;&quot;/&amp;gt;
 &amp;lt;add key=&quot;RackspaceUser&quot; value=&quot;&amp;lt;RackspaceName&amp;gt;&quot;/&amp;gt;
 &amp;lt;add key=&quot;RackspaceToken&quot; value=&quot;&amp;lt;RackspaceToken&amp;gt;&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement just one interface - &lt;code&gt;IFileStorageService&lt;/code&gt;. &lt;code&gt;IFileStorageService&lt;/code&gt; is what ultimately tells Nuget where to put files and where to retrieve them from. This class makes use of the &lt;a href=&quot;http://nuget.org/packages/csharp-cloudfiles&quot;&gt;CloudFiles API Wrapper nuget package&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; public class RackspaceStorage : IFileStorageService
 {
     private readonly UserCredentials _userCredentials = new UserCredentials(GetValue(&quot;RackspaceUser&quot;), GetValue(&quot;RackspaceToken&quot;));
     private Connection _connection;

     public Connection Connection
     {
         get { return _connection ?? (_connection = new Connection(_userCredentials)); }
     }

     public ActionResult CreateDownloadFileActionResult(string folderName, string fileName)
     {
         var container = Connection.GetContainerInformation(GetValue(&quot;ContainerName&quot;));
         return new RedirectResult(Path.Combine(container.CdnUri, fileName), false);
     }

     public void DeleteFile(string folderName, string fileName)
     {
         Connection.DeleteStorageItem(folderName, fileName);
     }

     public Stream GetFile(string folderName, string fileName)
     {
         try
         {
             var file = Connection.GetStorageItem(folderName, fileName);
             file.ObjectStream.Position = 0;
             return file.ObjectStream;
         }
         catch (StorageItemNotFoundException)
         {
             return null;
         }
     }

     public void SaveFile(string folderName, string fileName, Stream packageFile)
     {
         Connection.PutStorageItem(GetValue(&quot;ContainerName&quot;), packageFile, fileName);
     }

     private static string GetValue(string key)
     {
         return ConfigurationManager.AppSettings[key];
     }
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a switch statement (starts with &lt;code&gt;switch (configuration.PackageStoreType)&lt;/code&gt;) in ContainerBindings.cs. which determines which storage mechanism is used based on the config. We just need to add a switch for our new package store.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; case PackageStoreType.Rackspace:
     Bind&amp;lt;IFileStorageService&amp;gt;()
         .To&amp;lt;RackspaceStorage&amp;gt;()
         .InSingletonScope();
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unless you've got a SSL cert/HTTPS server setup for your NuGet install, you'll need to &lt;em&gt;disable&lt;/em&gt; HTTPS by going into &lt;code&gt;AuthenticationController&lt;/code&gt; and removing &lt;code&gt;[RequireRemoteHttps]&lt;/code&gt; from the &lt;code&gt;LogOn()&lt;/code&gt; and &lt;code&gt;public virtual ActionResult LogOn(SignInRequest request, string returnUrl)&lt;/code&gt; methods, otherwise you'll be caught in a redirect loop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, some of the settings cannot be set from the config file but must be entered in the database. You can do this directly using something like SQL Management Studio, or if you're debugging locally you can access &lt;code&gt;http://localhost:55880/dbadmin/GallerySettings/List&lt;/code&gt; and add the values there.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;After that, it's realy up to you to customise the gallery for how &lt;em&gt;you&lt;/em&gt; want it to look.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://images.theleagueofpaul.com/postimages/mahchats_nuget.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I still have a fair bit of work cut out for me in restyling the NuGet gallery, but I'm happy with the results of the Rackspace provider so far - I can upload/download via the NuGet tools just fine by just pointing it to &lt;myNugetInstall&gt;/v1.&lt;/p&gt;

&lt;p&gt;Once I've made a few extra customisations, I'll put a fork up on GitHub (and a follow up post) of the &quot;complete&quot; customisation - new theme, new storage provider, etc.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Pretzels</title>
      <link>http://www.theleagueofpaul.com/pretzels.html</link>
      <pubDate>Sat, 03 Dec 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/pretzels</guid>
      <description>&lt;div class=&quot;fullimagewrapper&quot;&gt;
    &lt;img src=&quot;http://images.theleagueofpaul.com/recipes/pretzels/header.jpg&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;This recipe is originally from &lt;a href=&quot;http://www.kingarthurflour.com/recipes/hot-buttered-pretzels-recipe&quot;&gt;King Arthur Flour&lt;/a&gt; with some tweaks for Australian (instead of US) terminology and measurements. It's a good recipe, but not without a few confusing bits.&lt;/p&gt;

&lt;p&gt;This recipe makes &lt;strong&gt;8 pretzels&lt;/strong&gt;, and takes &lt;strong&gt;just over an hour&lt;/strong&gt; from start to finish (10 minutes work, 45 minutes resting/rising, ~10 mins baking)&lt;/p&gt;

&lt;h2&gt;Ingredients&lt;/h2&gt;

&lt;h3&gt;Dough&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2 &amp;frac12; cups Plain Flour&lt;/li&gt;
&lt;li&gt;&amp;frac12; teaspoon salt&lt;/li&gt;
&lt;li&gt;1 teaspoon sugar&lt;/li&gt;
&lt;li&gt;2 &amp;frac14; teaspoons dried yeast&lt;/li&gt;
&lt;li&gt;1 cup warm water&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Topping&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&amp;frac12; cup warm water&lt;/li&gt;
&lt;li&gt;2 tablespoons baking soda&lt;/li&gt;
&lt;li&gt;coarse salt (sea salt, for example)&lt;/li&gt;
&lt;li&gt;2 tablespoons unsalted butter, melted OR about a tablespoon of olive oil&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If you want to make sweet pretzels rather than savoury/traditional pretzels, replace the coarse salt topping with a mixture of cocoa, cinnamon and a little icing sugar.&lt;/p&gt;

&lt;div class=&quot;fullimagewrapper&quot;&gt;
    &lt;img src=&quot;http://images.theleagueofpaul.com/recipes/pretzels/IMG_7971.jpg&quot; /&gt;
&lt;/div&gt;


&lt;h2&gt;Process&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Place all of the dough ingredients into a bowl, and beat till well-combined. Knead the dough, for about 5 minutes, till it's soft, smooth, and quite slack.&lt;/li&gt;
&lt;li&gt;Flour the dough in a bowl, cover and allow it to rest for 30 minutes.&lt;/li&gt;
&lt;li&gt;Preheat your oven to 240°C (fan forced, 260c fanless).&lt;/li&gt;
&lt;li&gt;Prepare two baking sheets/trays by spraying them with oil spray, or lining them with baking paper.&lt;/li&gt;
&lt;li&gt;Transfer the dough to a lightly greased work surface, and divide it into eight equal pieces (about 70g each). Allow the pieces to rest, uncovered, for 5 minutes.&lt;/li&gt;
&lt;li&gt;While the dough is resting, combine the 1/2 cup warm water and the baking soda, and place it in a shallow bowl. Make sure the baking soda is thoroughly dissolved; if it isn't, it'll make your pretzels splotchy.&lt;/li&gt;
&lt;li&gt;Roll each piece of dough into a long, thin rope (about 70cm long), and twist each rope into a pretzel. Dip each pretzel in the baking soda wash (this will give the pretzels a nice, golden-brown color), and place them on the baking sheets. Sprinkle lightly with salt (or sweet pretzel mix outlined above). Allow them to rest, uncovered, for 10 minutes.&lt;/li&gt;
&lt;li&gt;Bake the pretzels for 7 to 8 minutes, or until they're golden brown, reversing the baking sheets halfway through.&lt;/li&gt;
&lt;li&gt;Remove the pretzels from the oven, and brush them thoroughly with the melted butter (or oil).&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt; Eat the pretzels warm, or reheat them in an oven or microwave.&lt;/p&gt;

&lt;p&gt; &lt;a href=&quot;/recipes/pretzels.yumml&quot;&gt;YumML for this recipe&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;fullimagewrapper&quot;&gt;
    &lt;img src=&quot;http://images.theleagueofpaul.com/recipes/pretzels/IMG_7990.jpg&quot; /&gt;
&lt;/div&gt;



</description>
    </item>
    
    <item>
      <title>Facebook Chat over XMPP using OAuth2</title>
      <link>http://www.theleagueofpaul.com/facebook-over-xmpp-with-oauth2.html</link>
      <pubDate>Sat, 26 Nov 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/facebook-over-xmpp-with-oauth2</guid>
      <description>&lt;blockquote&gt;&lt;p&gt;Unlike the MSNXMPP posts, there isn't a huge code dump in this post. Working code &lt;em&gt;is&lt;/em&gt; up on GitHub for the &lt;a href=&quot;https://github.com/MahApps/Hanoi/blob/brendan-testability/source/Hanoi.Authentication.Facebook/FacebookPlatformAuthenticator.cs&quot;&gt;XMPP Authenticator&lt;/a&gt; and &lt;a href=&quot;https://github.com/MahApps/Hanoi/blob/brendan-testability/source/Hanoi.Authentication.Facebook/OAuth2Helper.cs&quot;&gt;OAuth2Helper&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;/messenger-over-xmpp&quot;&gt;Like Messenger&lt;/a&gt;, Facebook has an XMPP gateway into their Chat system. Unlike Messenger though, Facebook XMPP is fairly mature and has multiple forms of authentication available - namely &lt;code&gt;DIGEST-MD5&lt;/code&gt; and &lt;code&gt;X-FACEBOOK-PLATFORM&lt;/code&gt;. &lt;code&gt;DIGEST-MD5&lt;/code&gt; is fairly straightforward and isn't the focus of this post but it should be pointed out that &lt;em&gt;most&lt;/em&gt; XMPP clients are capable of &lt;code&gt;DIGEST-MD5&lt;/code&gt; as it is a standard auth mechanism that relies on the username and password. This is &lt;em&gt;good&lt;/em&gt; for Facebook support - &lt;a href=&quot;http://developers.facebook.com/docs/chat/#jabber&quot;&gt;as their docs point out&lt;/a&gt;, you can use &lt;code&gt;DIGEST-MD5&lt;/code&gt; when the client isn't even aware of Facebook.&lt;/p&gt;

&lt;h2&gt;OAuth2&lt;/h2&gt;

&lt;p&gt;The main reasons I see for using &lt;code&gt;X-FACEBOOK-PLATFORM&lt;/code&gt; are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building a Facebook chat client that also uses other Facebook data, so you want access with a single login and/or&lt;/li&gt;
&lt;li&gt;you don't want to store username/passwords.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Facebook were one of the first (if not the first?) major sites to adopt OAuth2, moving away from their custom token based API. The url for beginning OAuth2 requests is&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://www.facebook.com/dialog/oauth?client_id=&amp;lt;CLIENTID&amp;gt;&amp;amp;redirect_uri=&amp;lt;REDIRECT&amp;gt;&amp;amp;scope=&amp;lt;SCOPE&amp;gt;&amp;amp;display=touch&amp;amp;response_type=token&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you're developing a desktop or mobile app that can control the browser/have an embedded browser instance, you're encouraged to use &lt;code&gt;https://www.facebook.com/connect/login_success.html&lt;/code&gt; as the redirect uri.&lt;/p&gt;

&lt;p&gt;For scope, you use &lt;em&gt;comma&lt;/em&gt; separated values rather than &lt;em&gt;space&lt;/em&gt; separated values that Messenger uses. At a minimum, you want access to &lt;code&gt;xmpp_login&lt;/code&gt; and unless you want to refresh it every hour, &lt;code&gt;offline_acccess&lt;/code&gt; too. If you're building a Facebook app, this is a good time to &lt;a href=&quot;http://developers.facebook.com/docs/reference/api/permissions/&quot;&gt;add those additional permissions&lt;/a&gt;. Your scope string should look something like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;offline_access,xmpp_login
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On success, you'll be redirected to that redirect uri with a &lt;code&gt;#access_token=..&lt;/code&gt; appended. Handling it is exactly the same as handling Messengers &lt;em&gt;implicit&lt;/em&gt; flow.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webBrowser.Navigate(&amp;lt;FACEBOOK URL&amp;gt;);
webBrowser.LoadCompleted += (s, e) =&amp;gt; 
{
    if (!e.Uri.Fragment.Contains(&quot;access_token&quot;))
        return;

    var responseAll = Regex.Split(e.Uri.Fragment.Remove(0, 1), &quot;&amp;amp;&quot;);
    for (var i = 0; i &amp;lt; responseAll.Count(); i++)
    {
        var nvPair = Regex.Split(responseAll[i], &quot;=&quot;);

        if (nvPair[0] != &quot;access_token&quot;) 
            continue;

        var accessToken =  nvPair[1];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I think once I've nutted out Google's OAuth2 XMPP flow, I'll create a library of helpers for all three - there are enough differences that they all need customisation, but enough similarities that based helpers would be... helpful.&lt;/p&gt;

&lt;h2&gt;X-FACEBOOK-PLATFORM&lt;/h2&gt;

&lt;p&gt;Like &lt;code&gt;X-MESSENGER-OAUTH2&lt;/code&gt;, &lt;code&gt;X-FACEBOOK-PLATFORM&lt;/code&gt; uses OAuth2 tokens and requires an embedded webbrowser client side to process. Unlike &lt;code&gt;X-MESSENGER-OAUTH2&lt;/code&gt;, it requires a bit of extra processing - the client must issue a request, server issues a challenge, then finally the response.&lt;/p&gt;

&lt;p&gt;Just like any other XMPP implementation, the auth mechanisms will appear in the 'stream features' when initially connecting to &lt;code&gt;chat.facebook.com&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;stream:features&amp;gt;
    &amp;lt;mechanisms xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;&amp;gt;
        &amp;lt;mechanism&amp;gt;X-FACEBOOK-PLATFORM&amp;lt;/mechanism&amp;gt;
        &amp;lt;mechanism&amp;gt;DIGEST-MD5&amp;lt;/mechanism&amp;gt;
    &amp;lt;/mechanisms&amp;gt;
&amp;lt;/stream:features&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The Challenge&lt;/h3&gt;

&lt;p&gt;To begin the challenge process, you need to send what type of auth (&lt;code&gt;X-FACEBOOK-PLATFORM&lt;/code&gt;) like this&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;﻿&amp;lt;auth mechanism='X-FACEBOOK-PLATFORM' xmlns='urn:ietf:params:xml:ns:xmpp-sasl' /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's the same mechanism we sent with Messenger, except without contents, and it just starts the process. Next up the server will send a challenge. Challenges are a standard part of XMPP, so this isn't anything unusual. The only thing unusual is that it isn't a JSON or XML packet, but instead an &quot;http-url-like&quot; string thats URL and Base64 encoded.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;challenge xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;&amp;gt;dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9OEQwRkJGRDdBQTY5RkI0REJDRjc2MzEzNkY2NjFDRUM=&amp;lt;/challenge&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Decoded (Base64 the Url decode), that challenge looks something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;version=1&amp;amp;method=auth.xmpp_login&amp;amp;nonce=8D0FBFD7AA69FB4DBCF763136F661CEC
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can discard the version (it'll always be 1), but the method and nonce are important. I'd say that the method will always be &lt;code&gt;auth.xmpp_login&lt;/code&gt;, but I can't guarantee it and the documentation says to extract it.&lt;/p&gt;

&lt;h3&gt;The Response&lt;/h3&gt;

&lt;p&gt;In response, you must build up a similar string, then Base64 encode it. For what it's worth, Base64 is something XMPP dictates, not Facebook.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;method=auth.xmpp_login&amp;amp;api_key=&amp;lt;APIKEY&amp;gt;&amp;amp;access_token=&amp;lt;ACCESSTOKEN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To do that, I used the following code&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private string BuildResponse(string method, string nonce)
{
    var requestString = new StringBuilder();
    requestString.AppendFormat(&quot;method={0}&quot;, method);
    requestString.AppendFormat(&quot;&amp;amp;api_key={0}&quot;, _apiKey);
    requestString.AppendFormat(&quot;&amp;amp;access_token={0}&quot;, Connection.UserPassword);
    requestString.AppendFormat(&quot;&amp;amp;call_id={0}&quot;, &quot;0&quot;);
    requestString.AppendFormat(&quot;&amp;amp;v={0}&quot;, &quot;1.0&quot;);
    requestString.AppendFormat(&quot;&amp;amp;nonce={0}&quot;, nonce);

    var response = EncodeTo64(requestString.ToString());
    return response;
}

static public string EncodeTo64(string toEncode)
{
    byte[] toEncodeAsBytes = Encoding.ASCII.GetBytes(toEncode);
    string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
    return returnValue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once encoded, send the response like so&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;﻿&amp;lt;response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'&amp;gt;bWV0aG9kPWF1dGgueG1wcF9sb2dpbiZhcGlfa2V5PTxBUElLRVk+JmFjY2Vzc190b2tlbj08QUNDRVNTVE9LRU4+&amp;lt;/response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And (hopefully!) you should get back a success followed by regular XMPP roster/etc.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;success xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Caveats&lt;/h2&gt;

&lt;p&gt;Facebook Chat is not actually an XMPP server - or something along those lines. There is the gateway/translation process, but like Messenger it doesn't operate directly on XMPP so there are some &quot;lost in translation&quot; issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It handles &lt;em&gt;plaintext&lt;/em&gt; messages only (not HTML)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://xmpp.org/extensions/xep-0085.html&quot;&gt;XEP-0085&lt;/a&gt; not XEP-0022 for typing notifications&lt;/li&gt;
&lt;li&gt;vCards via &lt;a href=&quot;http://xmpp.org/extensions/xep-0054.html&quot;&gt;XEP-0054&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Just like Messenger you can't add/remove contacts via Roster management - you'd need to use the Facebook Graph API&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt; There are a few other things explicitly not supported, but basically it can be summed up as: if it's not on this list, it probably won't work. XMPP Core is supported, but not too much else.&lt;/p&gt;

&lt;h2&gt;Is this a good thing?&lt;/h2&gt;

&lt;p&gt;At the end of my first Messenger/Xmpp post, I pondered was that implementation a good thing, concluding that 'yes but mostly no'. Facebook doesn't suffer the same response&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook XMPP documentation was actually really useful&lt;/li&gt;
&lt;li&gt;Addition of &lt;code&gt;offline_access&lt;/code&gt; scope means no renewing of tokens&lt;/li&gt;
&lt;li&gt;Fallback to &lt;code&gt;DIGEST-MD5&lt;/code&gt; allows non-Facebook aware apps to connect&lt;/li&gt;
&lt;li&gt;There is a &lt;a href=&quot;http://developers.facebook.com/roadmap/&quot;&gt;roadmap&lt;/a&gt; (for Facebook APIs in general)&lt;/li&gt;
&lt;li&gt;We know there won't ever be VoIP/file transfer (a 'no' is better than not knowing) as Skype handles VoIP/Video.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Knowing what limitations are in place allows a more consistent app. Knowing that some of the data can be replaced with other API calls is also helpful. Messenger is a chat client/network trying to be a social network - the &quot;homepage&quot; of live.com is useless relying on data aggregation and GUID's for profile urls. Facebook is a social network trying to be a chat network and succeeding relatively well at it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Live Messenger over XMPP Refresh Tokens</title>
      <link>http://www.theleagueofpaul.com/messenger-over-xmpp-refresh-tokens.html</link>
      <pubDate>Thu, 24 Nov 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/messenger-over-xmpp-refresh-tokens</guid>
      <description>&lt;p&gt;&lt;a href=&quot;http://www.theleagueofpaul.com/messenger-over-xmpp&quot;&gt;While I was previously unable to get refresh tokens working in Live Messenger over XMPP&lt;/a&gt;, I've finally figured it out. To be fair, the documentation did have it tucked away under the &lt;code&gt;Reference/OAuth 2&lt;/code&gt; section, not under XMPP or Messenger sections but really, I should have just RTFM a little bit better.&lt;/p&gt;

&lt;p&gt;There are two types of &quot;authorisation flow&quot; - &lt;em&gt;implicit&lt;/em&gt; and &lt;em&gt;code&lt;/em&gt; and the difference starts with the initial request. Only &lt;em&gt;code&lt;/em&gt; flow can get refresh tokens, &lt;em&gt;implicit&lt;/em&gt; flow is designed as short lived (one hour) access. Having &lt;code&gt;response_type=token&lt;/code&gt; in the url starts &lt;em&gt;implicit&lt;/em&gt; flow, but having &lt;code&gt;response_type=code&lt;/code&gt; starts &lt;em&gt;code&lt;/em&gt; flow.&lt;/p&gt;

&lt;h2&gt;Using Code Flow&lt;/h2&gt;

&lt;p&gt;Where we previously had&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/authorize?client_id=CLIENTID&amp;amp;redirect_uri=https://oauth.live.com/desktop&amp;amp;response_type=token&amp;amp;scope=SCOPES
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It should now be&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/authorize?client_id=CLIENTID&amp;amp;redirect_uri=https://oauth.live.com/desktop&amp;amp;response_type=code&amp;amp;scope=SCOPES
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you want a refresh token, you &lt;em&gt;must&lt;/em&gt; have &lt;code&gt;wl.offline_access&lt;/code&gt; in your scopes. Ideally, scopes should look like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;scope=wl.basic%20wl.offline_access%20wl.messenger
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Implicit&lt;/em&gt; flow (as the name implies) restricts access and thus doesn't need to do as thorough checking of who you say you are. You still can't get in without user authorisation and the proper ClientID, but a great deal less processing is required. By contrast &lt;em&gt;code flow&lt;/em&gt; has two steps of confirmation and require your client secret code as well.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;implicit&lt;/em&gt; flow, once you have directed the user to the login url and they've granted access, you're redirected instantly to your &lt;code&gt;redirect_uri&lt;/code&gt; with the auth token attached in the uri fragment. With &lt;em&gt;code&lt;/em&gt; flow, you're redirected to your &lt;code&gt;redirect_uri&lt;/code&gt; but get back a code used to then confirm you are who you say you are - not the actual auth token. If you're using the default &lt;code&gt;redirect_uri&lt;/code&gt;, that url will look something like &lt;code&gt;https://oauth.live.com/desktop?code=&amp;lt;code&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point you can close the browser view from the user, as they don't need to interact with it anymore. Your code, however, still needs to make one final web call to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/token?client_id=CLIENT_ID&amp;amp;redirect_uri=REDIRECT_URL&amp;amp;client_secret=CLIENT_SECRET&amp;amp;code=AUTHORIZATION_CODE&amp;amp;grant_type=authorization_code
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You'll then get back a JSON payload that looks like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    &quot;access_token&quot; : &quot;a_really_long_string&quot;,
    &quot;authentication_token&quot;  :&quot;another_really_long_string&quot;,
    &quot;expires_in&quot; :3600,
    &quot;refresh_token&quot; : &quot;final_long_string&quot;,
    &quot;scope&quot; : &quot;wl.basic wl.messenger wl.offline_access&quot;,
    &quot;token_type&quot; : &quot;bearer&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally! We have our refresh token! Remember, you don't have to reconnect every hour, it's just that new connections after the token expires (&lt;code&gt;DateTime.Now.Add(new TimeSpan(1, 0, 0))&lt;/code&gt;) will need the access token 'refreshed' first, and to do that, you have to send another web request to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/token?client_id=[YOUR_CLIENT_ID]&amp;amp;client_secret=[YOUR_CLIENT_SECRET]&amp;amp;redirect_uri=REDIRECT_URL&amp;amp;grant_type=refresh_token&amp;amp;refresh_token=[REFRESH_TOKEN]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again you'll get back a JSON payload that looks almost identical to the one above, just missing the &lt;code&gt;authentication_token&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Coding it all&lt;/h2&gt;

&lt;p&gt;These changes make my previous OAuth2Helper somewhat less useful. It'll still work, just in less useful situations. Using &lt;a href=&quot;https://github.com/liveservices/LiveSDK/blob/master/Samples/Asp.net/OAuthSample/Callback.aspx.cs&quot;&gt;parts of the code&lt;/a&gt; from Microsoft's &lt;a href=&quot;https://github.com/liveservices/LiveSDK/&quot;&gt;LiveSDK on GitHub&lt;/a&gt; I cobbled together a new helper library that should handle it from start-to-finish. This should take on no external dependencies, but if you were to start using the rest of the LiveSDK API, I'd recommend switching to something like &lt;a href=&quot;https://github.com/restsharp/RestSharp&quot;&gt;RestSharp&lt;/a&gt; and &lt;a href=&quot;http://json.codeplex.com/&quot;&gt;JSON.NET&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've taken the effort to make sure all scopes are available in the enum, most of them don't need to be used unless you're using the REST API as well. Many of the scope options are supersets of previous scopes, for example &lt;code&gt;ContactsBirthday&lt;/code&gt; gives access to your contacts birthday &lt;em&gt;and&lt;/em&gt; the logged in users birthday, whereas &lt;code&gt;Birthday&lt;/code&gt; just gives the latter. &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/hh243646.aspx&quot;&gt;Information is up on MSDN about all the permissions each scope gives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also realise this isn't ideal - no error checking nor is it asynchronous - it is meant as demo code to prove a point, if somebody is actually interested in this, I'm happy to lend a hand to make it more usable.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[DataContract]
public class OAuthToken
{
    [DataMember(Name = OAuthConstants.AccessToken)]
    public string AccessToken { get; set; }

    [DataMember(Name = OAuthConstants.RefreshToken)]
    public string RefreshToken { get; set; }

    [DataMember(Name = OAuthConstants.ExpiresIn)]
    public string ExpiresIn{get; set;}

    [DataMember(Name = OAuthConstants.Scope)]
    public string Scope { get; set; }
}

public static class OAuthConstants
{
    public const string ClientID = &quot;client_id&quot;;
    public const string ClientSecret = &quot;client_secret&quot;;
    public const string Callback = &quot;redirect_uri&quot;;
    public const string ClientState = &quot;state&quot;;
    public const string Scope = &quot;scope&quot;;
    public const string Code = &quot;code&quot;;
    public const string AccessToken = &quot;access_token&quot;;
    public const string ExpiresIn = &quot;expires_in&quot;;
    public const string RefreshToken = &quot;refresh_token&quot;;
    public const string ResponseType = &quot;response_type&quot;;
    public const string GrantType = &quot;grant_type&quot;;
    public const string Error = &quot;error&quot;;
    public const string ErrorDescription = &quot;error_description&quot;;
    public const string Display = &quot;display&quot;;
}   

[Flags]
public enum Scope
{
    [Description(&quot;wl.basic&quot;)]
    Basic = 1,

    [Description(&quot;wl.messenger&quot;)]
    Messenger = 2,

    [Description(&quot;wl.offline_access&quot;)]
    Offline = 4,

    [Description(&quot;wl.signin&quot;)]
    Signin = 8,

    [Description(&quot;wl.birthday&quot;)]
    Birthday = 16,

    [Description(&quot;wl.calendars&quot;)]
    Calendars = 32,

    [Description(&quot;wl.calendars_update&quot;)]
    CalendarsUpdate = 64,

    [Description(&quot;wl.contacts_birthday&quot;)]
    ContactsBirthday = 128,

    [Description(&quot;wl.contacts_create&quot;)]
    ContactsCreate = 256,

    [Description(&quot;wl.contacts_calendars&quot;)]
    ContactsCalendars = 512,

    [Description(&quot;wl.contacs_photos&quot;)]
    ContactsPhotos = 1024,

    [Description(&quot;wl.contacts_skydrive&quot;)]
    ContactsSkydrive = 2048,

    [Description(&quot;wl.emails&quot;)]
    Emails = 4096,

    [Description(&quot;wl.events_create&quot;)]
    EventsCreate = 8192,

    [Description(&quot;wl.phone_numbers&quot;)]
    PhoneNumbers = 16384,

    [Description(&quot;wl.photos&quot;)]
    Photos = 32768,

    [Description(&quot;wl.postal_addresses&quot;)]
    PostalAddresses = 65536,

    [Description(&quot;wl.share&quot;)]
    Share = 131072,

    [Description(&quot;wl.skydrive&quot;)]
    Skydrive = 262144,

    [Description(&quot;wl.skydrive_update&quot;)]
    SkydriveUpdate = 524288,

    [Description(&quot;wl.work_profile&quot;)]
    WorkProfile = 1048576,

    [Description(&quot;wl.applications&quot;)]
    Applications = 2097152,

    [Description(&quot;wl.applications_create&quot;)]
    ApplicationsCreate = 4194304
}

public static class OAuth2Helper
{
    public static Uri GenerateRequestUrl(string clientId, Scope scopes, string type=&quot;token&quot;, string callback=&quot;https://oauth.live.com/desktop&quot;, string display = &quot;touch&quot;)
    {
        string scopeString = &quot;&quot;;
        var y = Enum.GetValues(typeof(Scope)).Cast&amp;lt;Scope&amp;gt;();
        foreach (var s in y)
        {
            if ((scopes &amp;amp; s) == s)
                scopeString += GetDescription(s) + &quot;%20&quot;;
        }

        return new Uri(String.Format(@&quot;https://oauth.live.com/authorize?client_id={0}&amp;amp;display={2}&amp;amp;redirect_uri={4}&amp;amp;response_type={3}&amp;amp;scope={1}&quot;, clientId, scopeString, display, type, callback));
    }

    public static OAuthToken RefreshToken(string clientId, string clientSecret, string refreshToken, string callback = &quot;https://oauth.live.com/desktop&quot;)
    {
        var wc = new WebClient();
        var url = string.Format(&quot;https://oauth.live.com/token?client_id={0}&amp;amp;redirect_uri={1}&amp;amp;client_secret={2}&amp;amp;refresh_token={3}&amp;amp;grant_type=refresh_token&quot;, clientId, callback, clientSecret, refreshToken);
        var response = wc.DownloadString(url);
        var ms = new MemoryStream(Encoding.Unicode.GetBytes(response));
        var serializer = new DataContractJsonSerializer(typeof(OAuthToken));
        return serializer.ReadObject(ms) as OAuthToken;
    }

    public static string CodeFromUriResponse(Uri response)
    {
         if (!response.Query.Contains(&quot;code&quot;))
            return null;

        var nvPair = Regex.Split(response.Query.Substring(1), &quot;=&quot;);
        return nvPair[0] == &quot;code&quot; ? nvPair[1] : null;
    }

    public static string AccessTokenFromUriResponse(Uri response)
    {
        if (!response.Fragment.Contains(&quot;access_token&quot;))
            return string.Empty;

        var responseAll = Regex.Split(response.Fragment.Remove(0, 1), &quot;&amp;amp;&quot;);
        return (from t in responseAll
                select Regex.Split(t, &quot;=&quot;) into nvPair
                where nvPair[0] == &quot;access_token&quot;
                select nvPair[1]).FirstOrDefault();
    }

    public static OAuthToken AuthCodeToToken(string clientId, string clientSecret, string auth, string callback = &quot;https://oauth.live.com/desktop&quot;)
    {
        var wc = new WebClient();
        var url = string.Format(&quot;https://oauth.live.com/token?client_id={0}&amp;amp;redirect_uri={1}&amp;amp;client_secret={2}&amp;amp;code={3}&amp;amp;grant_type=authorization_code&quot;, clientId, callback, clientSecret, auth);
        var response = wc.DownloadString(url);
        var ms = new MemoryStream(Encoding.Unicode.GetBytes(response));
        var serializer = new DataContractJsonSerializer(typeof(OAuthToken));
        return serializer.ReadObject(ms) as OAuthToken;
    }

    public static string GetDescription&amp;lt;T&amp;gt;(T value) where T : struct
    {
        string name = value.ToString();

        object[] attrs =
            value.GetType().GetField(name).GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);

        return (attrs.Length &amp;gt; 0) ? ((System.ComponentModel.DescriptionAttribute)attrs[0]).Description : name;
    }

    public static T GetEnumFromDescription&amp;lt;T&amp;gt;(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            throw new ArgumentException(&quot;value must be non-empty&quot;);
        }

        var field = typeof(T).GetFields().FirstOrDefault(f =&amp;gt;
        {
            var attrs = f.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);

            if (attrs.Length &amp;gt; 0)
            {
                if (((System.ComponentModel.DescriptionAttribute)attrs[0]).Description == value)
                {
                    return true;
                }
            }

            return false;
        });

        if (field != null)
        {
            return (T)field.GetValue(null);
        }
        else
        {
            return (T)Enum.Parse(typeof(T), value);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;To start the OAuth2 process&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webBrowser.Navigate(OAuth2Helper.GenerateRequestUrl(&quot;clientid&quot;, Scope.Messenger | Scope.Offline, &quot;code&quot;));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To get the token back&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webBrowser.LoadCompleted += (s, e) =&amp;gt; 
{
    var code = OAuth2Helper.CodeFromUriResponse(e.Uri);
        if (!string.IsNullOrEmpty(code))
        {
            var token = OAuth2Helper.AuthCodeToToken(&quot;clientid&quot;, &quot;secret&quot;, code);
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Where does that leave us?&lt;/h2&gt;

&lt;p&gt;While refresh tokens are annoying, using it correctly does solve one of the major issues I had with MSNXMPP. However, this doesn't solve the &quot;idle&quot; (&lt;a href=&quot;http://xmpp.org/extensions/xep-0199.html&quot;&gt;XEP-0199&lt;/a&gt;) issue that caused the connection to be dropped, nor non-standard Roster management.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Live Messenger over XMPP</title>
      <link>http://www.theleagueofpaul.com/messenger-over-xmpp.html</link>
      <pubDate>Mon, 21 Nov 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/messenger-over-xmpp</guid>
      <description>&lt;p&gt;&lt;img src=&quot;http://images.theleagueofpaul.com/postimages/msnxmpp.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Microsoft's Windows Live Messenger uses a custom protocol known as &quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Microsoft_Notification_Protocol&quot;&gt;MSNP&lt;/a&gt;&quot;, now up to at least version 21 of the protocol. Every client that supports Messenger - up until recently - has used a version of this protocol. The only problem with MSNP is that to use it, you need to reverse engineer Microsoft's work and given significant parts of it are encrypted, it results in a cat-and-mouse game of trying to decrypt, understand, and then support the new features, often resulting in bungled implementations.&lt;/p&gt;

&lt;p&gt;Recently, however, Microsoft announced that they'd have XMPP access to Messenger contacts/IM. Does this mean you can plug Messenger server details into any XMPP client? Not exactly. It does mean with a little bit of modification, you could support multiple IM networks via a single client a lot easier - working on one protocol/plugin (even with a few variants) is easier than maintaining netcode for two distinct protocols.&lt;/p&gt;

&lt;h2&gt;Authorization&lt;/h2&gt;

&lt;p&gt;Instead of plugging in your username and password (SASL-PLAIN or SASL-DIGEST-MD5), Messenger over XMPP uses OAuth2 and a custom SASL method - &lt;code&gt;X-MESSENGER-OAUTH2&lt;/code&gt;. For reference, Google have &lt;code&gt;X-GOOGLE-TOKEN&lt;/code&gt; and Facebook &lt;code&gt;X-FACEBOOK-PLATFORM&lt;/code&gt; for their XMPP implementations, but they both have fallbacks to allow you to use your username/password. OAuth2 is significantly easier than OAuth1 to consume - you won't need any encryption or OAuth helper libraries to handle it as there isn't any secret/token generation required to consume.&lt;/p&gt;

&lt;p&gt;First you need to sign up and &quot;create&quot; an application through the &lt;a href=&quot;http://manage.dev.live.com/&quot;&gt;Live Connect control panel&lt;/a&gt; - there isn't any cost, its just a simple registration for a &lt;code&gt;Client ID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then you need to use a browser control (or browser window with redirect back to a webapp) to display&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/authorize?client_id=CLIENTID&amp;amp;redirect_uri=https://oauth.live.com/desktop&amp;amp;response_type=token&amp;amp;scope=SCOPES
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At a minimum, you need the scope to be set to &lt;code&gt;wl.messenger&lt;/code&gt;, but &lt;code&gt;wl.offline_access&lt;/code&gt; will work too (either by itself, or combine the scopes separating them with a space). CLIENTID in this case is the Client ID generated when you signed up and created your Live Connect app. Once the user logs in and authorises, you'll be redirected to a page with the query &lt;em&gt;fragment&lt;/em&gt; (that is, stuff after a #) containing &lt;code&gt;access_token&lt;/code&gt;. You need to store that token.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hint: if you want a finger-mash friendly page, make sure you include &lt;code&gt;display=touch&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Show me the (C#) code!&lt;/h2&gt;

&lt;p&gt;Whether you use WinForms, WPF, WP7 or something else, you need to generate a request url to pass to a browser, and peek at the response Uri.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public static class OAuth2Helper
{
    public static Uri GenerateRequestUrl(string clientId, string scope, string display = &quot;touch&quot;)
    {
        return new Uri(String.Format(@&quot;https://oauth.live.com/authorize?client_id={0}&amp;amp;display={2}&amp;amp;redirect_uri=https://oauth.live.com/desktop&amp;amp;response_type=token&amp;amp;scope={1}&quot;, clientId, scope, display));
    }

    public static string AccessTokenFromUriResponse(Uri response)
    {
        if (!response.Fragment.Contains(&quot;access_token&quot;))
            return string.Empty;

        var responseAll = Regex.Split(response.Fragment.Remove(0, 1), &quot;&amp;amp;&quot;);
        return (from t in responseAll 
                select Regex.Split(t, &quot;=&quot;) into nvPair 
                where nvPair[0] == &quot;access_token&quot; 
                select nvPair[1]).FirstOrDefault();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To start the OAuth2 process:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webBrowser.Navigate(OAuth2Helper.GenerateRequestUrl(&quot;12354&quot;, &quot;wl.messenger&quot;));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To get the token back:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webBrowser.LoadCompleted += (s, e) =&amp;gt; 
{
     if (!e.Uri.Fragment.Contains(&quot;access_token&quot;)) 
     {
        var authToken = OAuth2Helper.AccessTokenFromUriResponse(e.Uri);
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;SASL&lt;/h2&gt;

&lt;p&gt;It took a little while for me to get the hang of it as I was implementing it from scratch, but custom SASL implementations &lt;em&gt;can&lt;/em&gt; be very straight forward. After the initial XMPP connection is established, you'll get back a &quot;features&quot; list from the server that will look similar to this (although without the linebreaks and indentation)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;stream:features xmlns:stream=&quot;http://etherx.jabber.org/streams&quot;&amp;gt;
    &amp;lt;starttls xmlns=&quot;urn:ietf:params:xml:ns:xmpp-tls&quot;&amp;gt;
        &amp;lt;required /&amp;gt;
    &amp;lt;/starttls&amp;gt;
    &amp;lt;mechanisms xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;&amp;gt;
        &amp;lt;mechanism&amp;gt;X-MESSENGER-OAUTH2&amp;lt;/mechanism&amp;gt;
    &amp;lt;/mechanisms&amp;gt;
&amp;lt;/stream:features&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If that doesn't make sense to you, TLS/SSL is required, and X-MESSENGER-OAUTH2 is the &lt;em&gt;only&lt;/em&gt; SASL mechanism that is supported - you cannot use MD5-DIGEST or PLAIN auth. Your response should be&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;auth mechanism='X-MESSENGER-OAUTH2' xmlns='urn:ietf:params:xml:ns:xmpp-sasl'&amp;gt;OAUTH_TOKEN_GOES_HERE&amp;lt;/auth&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And if that was a valid token, you'll receive back &lt;code&gt;&amp;lt;success xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot; /&amp;gt;&lt;/code&gt; which (should) be followed by roster and presence information. &lt;strong&gt;Make sure that you Uri &lt;em&gt;decode&lt;/em&gt; the token!&lt;/strong&gt;. Other reasons why it might reject your token include an expired token (more on that below), insufficient privledges (ie, you set the scope to &lt;code&gt;wl.basic&lt;/code&gt;) or a revoked token.&lt;/p&gt;

&lt;p&gt;Most XMPP/SASL connect libraries will have a way to pass in a name (&lt;code&gt;X-MESSENGER-OAUTH2&lt;/code&gt;) and string/token.&lt;/p&gt;

&lt;h2&gt;Limitations&lt;/h2&gt;

&lt;p&gt;According to the documentation&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The Windows Live Messenger Extensible Messaging and Presence Protocol (XMPP) service supports &lt;strong&gt;only&lt;/strong&gt; these extensions as defined by the XMPP Standards Foundation, with partial support for these extensions as noted.&lt;/p&gt;

&lt;p&gt;RFC6120: XMPP: Core &lt;br/&gt;
RFC6121: XMPP: Instant Messaging and Presence. &lt;strong&gt;Roster management is not supported. &lt;/strong&gt;&lt;br/&gt;
XEP-0054: vcard-temp. The Messenger XMPP service supports fetching vCards but &lt;strong&gt;doesn't support updating vCards. &lt;/strong&gt; &lt;br/&gt;
XEP-0085: Chat State Notifications. &lt;br/&gt;
XEP-0203: Delayed Delivery&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;The emphasis is mine, but it highlights how this is a partial solution to using XMPP to connect to Messenger - there is no VoIP, there is no video, there is no file transfer. Even forgetting those parts (most third party implementations of MSNP lack those three features anyway), lack of roster management and only partial vCard support is problematic - it isn't as simple as an authentication layer being put in front to get it to work, XMPP roster management (that is, adding/removing contacts) would have to be done via the REST API (if those exist - I haven't looked).&lt;/p&gt;

&lt;p&gt;Currently I'm experiencing &quot;idle&quot; issues - after 1-2 minutes of no traffic, the connection is closed by the other end. Is this an issue with &lt;em&gt;their&lt;/em&gt; XMPP server? XEP-0199 isn't specifically mentioned as supported (thats &quot;ping/pong&quot; in XMPP standards) although it does have references throughout RFC4120, but it's only the Messenger XMPP server that drops the connection (against my code that is).&lt;/p&gt;

&lt;h3&gt;Token Expiry&lt;/h3&gt;

&lt;p&gt;This is a big one, and fundamentally breaks the way any existing XMPP client works. Unlike OAuth1, OAuth2 tokens are designed to be short lived and in this case, they expire after an hour. This doesn't mean the connection is dropped after an hour, it just means next time you go to log in, if it's been greater than an hour, you'll need to get a new token.&lt;/p&gt;

&lt;p&gt;Facebook uses OAuth2 for it's &quot;Graph Protocol&quot;, so how does that get around mobile/desktop apps that don't renew? By requesting &lt;code&gt;offline_access&lt;/code&gt; during the initial OAuth2 stages, that sets the token to &lt;em&gt;never&lt;/em&gt; expire. Access can still be revoked, but the token won't timeout.&lt;/p&gt;

&lt;p&gt;Tokens can be renewed, the SDK documents tell me, but I haven't figured out how. You're suppose to send a request to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://oauth.live.com/token?client_id=[CLIENTID]&amp;amp;client_secret=[CLIENTSECRET]&amp;amp;redirect_uri=https://oauth.live.com/desktop&amp;amp;grant_type=refresh_token&amp;amp;refresh_token=[REFRESHTOKEN]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The client ID and secret are found on the Live Connect control panel, but I'm not sure what the refresh token is - neither (&lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;authentication_token&lt;/code&gt;) tokens returned by the initial authorisation process seem to work.&lt;/p&gt;

&lt;p&gt;Ultimately, I think the Facebook approach of having long-lasting tokens when required would have been better in this situation.&lt;/p&gt;

&lt;h2&gt;Is this a good thing?&lt;/h2&gt;

&lt;p&gt;While I'm happy to see a more standard IM protocl AND a standard authentication protocol be adopted/permitted, I'm not entirely sold on this particular implementation of XMPP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There isn't a (public) roadmap for future features (if there will be any),&lt;/li&gt;
&lt;li&gt;the changes required aren't as simple as the initial authentication,&lt;/li&gt;
&lt;li&gt;the documentation and samples don't actually cover .NET (and I don't really feel like setting up a Java dev environment just to see if their samples are &quot;stable&quot; on Java),&lt;/li&gt;
&lt;li&gt;Token expiry reaaaallly sucks for any compatibility with existing XMPP implementations&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This experiment wasn't a complete loss. I'll still be using &lt;a href=&quot;http://code.google.com/p/msnp-sharp/&quot;&gt;MSNPSharp&lt;/a&gt;, but the changes made to &lt;a href=&quot;https://github.com/MahApps/Hanoi&quot;&gt;Hanoi&lt;/a&gt; (our XMPP library) and MahChats were needed anyway - that is, inheriting from a base Xmpp plugin to create preconfigured &quot;profiles&quot; (ie, Facebook, GTalk then generic XMPP/Jabber) and the code Hanoi was based on (BabelIM) didn't allow custom SASL mechanisms to be easily plugged in - they had to be hard coded in.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MahApps.Metro 0.3 Release</title>
      <link>http://www.theleagueofpaul.com/metro-03-released.html</link>
      <pubDate>Mon, 07 Nov 2011 00:00:00 +1100</pubDate>
      <author>paul@theleagueofpaul.com (Paul Jenkins)</author>
      <guid>http://www.theleagueofpaul.com/metro-03-released</guid>
      <description>&lt;p&gt;&lt;img src=&quot;http://c713056.r56.cf2.rackcdn.com/mahapps.metro.logo2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Exciting times for MahApps.Metro - &lt;a href=&quot;http://nuget.org/List/Packages/MahApps.Metro&quot;&gt;over 100 downloads via Nuget&lt;/a&gt;, more sightings in the wild, &lt;a href=&quot;http://jake.ginnivan.net/&quot;&gt;a new contributer&lt;/a&gt; and &lt;a href=&quot;https://github.com/MahApps/MahApps.Metro&quot;&gt;a move to GitHub&lt;/a&gt;. And finally, version 0.3 has now been released and pushed to NuGet.&lt;/p&gt;

&lt;h2&gt;What's new in 0.3?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Combined MahApps.Metro.Controls into MahApps.Metro for a single library&lt;/strong&gt;&lt;br/&gt;
This is considered a breaking change but thankfully it isn't horrible to fix and it's the only breaking change. Previously MahApps.Metro.Controls were in a separate assembly, but they've been combined into a single assembly. The namespaces are the same, so it will just be project references that need to change.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xmlns:Controls=&quot;clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro.Controls&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will become&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xmlns:Controls=&quot;clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;New controls&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Panorama&lt;/li&gt;
&lt;li&gt;MetroImage&lt;/li&gt;
&lt;li&gt;Tile&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;Added Theme Manager&lt;/strong&gt;&lt;br/&gt;
Previously you had to roll your own theme/accent switching code, but we've now included in a static &lt;code&gt;ThemeManager&lt;/code&gt; class to handle that.&lt;/p&gt;

&lt;p&gt;Using one of the built-in accents&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ThemeManager.ChangeTheme(this, ThemeManager.DefaultAccents.First(a =&amp;gt; a.Name == &quot;Red&quot;), Theme.Light);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The built in accents are &lt;code&gt;Red&lt;/code&gt;, &lt;code&gt;Blue&lt;/code&gt;, &lt;code&gt;Green&lt;/code&gt; and &lt;code&gt;Purple&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using your own accent&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var myAccent = new Accent(&quot;MyAccent&quot;, new Uri(&quot;pack://application:,,,/My.App;component/MyAccent.xaml&quot;));
ThemeManager.ChangeTheme(this, myAccent, Theme.Dark);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This does &lt;em&gt;not&lt;/em&gt; persist theme changes - you'll need to handle that yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And various bug fixes and missing styles&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional controls styled:

&lt;ul&gt;
&lt;li&gt;Slider&lt;/li&gt;
&lt;li&gt;GridView&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fixed button hover and pressed state&lt;/li&gt;
&lt;li&gt;Fixed TabItem focus issues&lt;/li&gt;
&lt;li&gt;Fixed Dark -&amp;gt; Light theme transition issues&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Show Me The Demo!&lt;/h2&gt;

&lt;p&gt;You can grab the demo app from source or now you can &lt;a href=&quot;http://deployment.mahapps.com/metro/setup.exe&quot;&gt;install it via ClickOnce&lt;/a&gt;, or watch the short video below demoing the new controls&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;&lt;iframe class=&quot;vimeo&quot; src=&quot;http://player.vimeo.com/video/31759569?title=0&amp;amp;byline=0&amp;amp;portrait=0&quot; width=&quot;640&quot; height=&quot;368&quot; frameborder=&quot;0&quot; webkitAllowFullScreen allowFullScreen&gt;&lt;/iframe&gt;&lt;/p&gt;

</description>
    </item>
    

  </channel> 
</rss>
