<?xml version="1.0" encoding="utf-8"?>


<rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">

  <channel>
    
    <title><![CDATA[Jeremy Messenger blog]]></title>
    <link>http://jlmessenger.com/blog</link>
    <description>Jeremy Messenger blog</description>
    <dc:language></dc:language>
    <dc:creator></dc:creator>
    <dc:rights>Copyright 2026</dc:rights>
    <dc:date>2026-04-14T06:54:05+00:00</dc:date>
    <admin:generatorAgent rdf:resource="http://statamic.com/" />
    
        <item>
      <title><![CDATA[Beating Caesar&rsquo;s Cipher with Wheel-of-Fortune]]></title>
      <link>http://jlmessenger.com/blog/caesar-cipher-wheel-of-fortune</link>
      <guid>http://jlmessenger.com/blog/caesar-cipher-wheel-of-fortune</guid>
      <description><![CDATA[<p>While on vacation my wife showed me a children&rsquo;s riddle book. When she was young her family
went crazy for a few days trying to solve the riddle before giving up. The book contains
a cipher at the end which can be used to validate your solution and finish the story. I
knew it would be trivial to brute-force, but I wanted to demonstrate a statistical attack
with nothing more than Wheel-of-Fortune&rsquo;s free letters &ndash; R,S,T,L,N,E.<!--more--></p>

<h2>Approach</h2>

<ul>
<li>Create baseline distribution of english letter usage</li>
<li>Calculate distribution of letters in the secret message</li>
<li>Check the correlation coefficient between the distributions</li>
<li>Shift the distribution and check the correlation</li>
<li>Find shifted distribution with best correlation</li>
<li>Decrypt the message</li>
</ul>

<p>I&rsquo;ve added my toolbox methods at the end.</p>

<h3>English Distributions of Letters</h3>

<p>I&rsquo;m sure google could find a better distribution, but who&rsquo;s got time for that?
My mother&rsquo;s love of Wheel-of-Fortune has taught me the six most common letters.</p>

<pre><code>var wofLetters = 'RSTLNE';

// code of 'A'
var cA = 'A'.charCodeAt(0);

// array of letter codes where 'A' = 0
var wofLettersZero = arrayOffset(stringAsCodes(wofLetters), -cA);

// distribution of letters
var wofHist = Algebra.histPercent(wofLettersZero, 26);

// WHEEL OF FORTUNE DISTRIBUTION
// 0.10          |             |   |       | | |            
// 0.08          |             |   |       | | |            
// 0.06          |             |   |       | | |            
// 0.04          |             |   |       | | |            
// 0.02          |             |   |       | | |            
// 0.00  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
</code></pre>

<h3>Secret Message</h3>

<p>To stay free of any copyright trouble I&rsquo;ve provided my own secret message:</p>

<pre><code>var text = ''+
'OT ZNK HKMOTTOTM MUJ IXKGZKJ ZNK NKGBKTY GTJ ZNK KGXZN.\n'+
'TUC ZNK KGXZN CGY LUXSRKYY GTJ KSVZE, JGXQTKYY CGY UBKX\n'+
'ZNK YAXLGIK UL ZNK JKKV, GTJ ZNK YVOXOZ UL MUJ CGY NUBKXOTM\n'+
'UBKX ZNK CGZKXY. GTJ MUJ YGOJ, "RKZ ZNKXK HK ROMNZ," GTJ\n'+
'ZNKXK CGY ROMNZ. MUJ YGC ZNGZ ZNK ROMNZ CGY MUUJ, GTJ NK\n'+
'YKVGXGZKJ ZNK ROMNZ LXUS ZNK JGXQTKYY. MUJ IGRRKJ ZNK ROMNZ\n'+
'"JGE," GTJ ZNK JGXQTKYY NK IGRRKJ "TOMNZ." GTJ ZNKXK CGY\n'+
'KBKTOTM, GTJ ZNKXK CGY SUXTOTM—ZNK LOXYZ JGE.';

// get rid of all spaces, etc.
var textJustLetters = lettersOnly(text);

// array of letter codes where 'A' = 0
var textLettersZero = arrayOffset(stringAsCodes(textJustLetters), -cA);

// distribution of letters
var textHist = Algebra.histPercent(textLettersZero);

// CIPHER DISTRIBUTION
// 0.10                      |                             |
// 0.08              |       |     |                       |
// 0.06              |     | |     |           |       | | |
// 0.04              |     | |   | | |         | |     | | |
// 0.02      |       |     | |   | | |     |   | |     | | |
// 0.00  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
</code></pre>

<h3>Shift and Find Best Correlation</h3>

<pre><code>var i, oHist, r,
    bestR = 0, bestI = -1;
for (i = 0; i &lt; 26; i++) {
    // shift distribution
    oHist = arrayRotate(textHist, i);

    // check correlation
    r = Algebra.correl(oHist, wofHist);

    // save if better
    if (r &gt; bestR) {
        bestR = r;
        bestI = i;
    }
}
</code></pre>

<h3>Best Distribution</h3>

<p>I won&rsquo;t tell which one it is, but it looks like:</p>

<pre><code>// BEST DISTRIBUTION (r=0.57734)
// 0.10          |                             |            
// 0.08  |       |     |                       |            
// 0.06  |     | |     |           |       | | |            
// 0.04  |     | |   | | |         | |     | | |            
// 0.02  |     | |   | | |     |   | |     | | |     |      
// 0.00  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
                 *             *   *       * * *
*Wheel of Fortune letters
</code></pre>

<h3>Basic Toolbox</h3>

<pre><code>var Algebra = (function () {

    var api = {};

    // sum of an array of numbers
    api.sum = function (a) {
        var i, s = 0;
        for (i = 0; i &lt; a.length; i++) {
            s += a[i];
        }
        return s;
    };

    // average of an array of numbers
    api.mean = function (a) {
        var s = api.sum(a);
        return s / a.length;
    };

    // correlation coefficient between two arrays
    api.correl = function (x, y) {
        if (x.length !== y.length) {
            throw new Error('correl() Must be same length!');
        }
        var i,
            dmx, dmy,
            sdmxy = 0, ssx = 0, ssy = 0,
            mx = api.mean(x),
            my = api.mean(y);

        for (i = 0; i &lt; x.length; i++) {
            dmx = x[i] - mx;
            dmy = y[i] - my;
            sdmxy += dmx * dmy;
            ssx += dmx * dmx;
            ssy += dmy * dmy;
        }
        return sdmxy / Math.sqrt(ssx * ssy);
    };

    // histogram of an array or numbers
    // returns array with accumulation
    api.hist = function (a, bins) {
        var i, x;
        if (!bins) {
            bins = bins || [];
        } else if (!bins.length &amp;&amp; bins &gt; 0) {
            i = bins;
            bins = [];
            while (i &gt; bins.length) {
                bins.push(0);
            }
        }

        for (i = 0; i &lt; a.length; i++) {
            x = a[i];
            while (x &gt; (bins.length - 1)) {
                bins.push(0);
            }
            bins[x]++;
        }
        return bins;
    };

    // histogram percentages of an array of numbers
    // returns array with accumulation percentage
    api.histPercent = function (a, bins) {
        var i,
            binsp = api.hist(a, bins);

        for (i = 0; i &lt; binsp.length; i++) {
            binsp[i] = binsp[i] / a.length;
        }
        return binsp;
    };

    return api;
}());

// add 'offset' to each number in the array of numbers
var arrayOffset = function (a, off) {
    var i;
    for (i = 0; i &lt; a.length; i++) {
        a[i] += off;
    }
    return a;
};

// rotate the last 'n' array entries around to the beginning
var arrayRotate = function (a, n) {
    return a.slice(n).concat(a.slice(0, n));
};

// convert string to array of number codes
var stringAsCodes = function (s) {
    var out = [];
    out.length = s.length;
    for (i = 0; i &lt; s.length; i++) {
        out[i] = s.charCodeAt(i);
    }
    return out;
};

// string with only uppercase letters
var lettersOnly = function (s) {
    return s.replace(/[^A-Z]/g, '');
};

// decrypt caesar cipher by shifting letter to A
var caesarDecrypt = function (text, letter) {
    var i, n, q,
        out = [],
        cA = 'A'.charCodeAt(0),
        cZ = 'Z'.charCodeAt(0),
        k = letter.charCodeAt(0) - cA;

    for (i = 0; i &lt; text.length; i++) {
        n = text.charCodeAt(i);
        if (n &lt; cA || n &gt; cZ) {
            out[i] = n;
            continue;
        }
        q = n - k;
        if (q &lt; cA) {
            q = cZ + (q - cA) + 1;
        } else if (q &gt; cZ) {
            q = cA + (q - cZ) - 1;
        }
        out[i] = q;
    }
    return String.fromCharCode.apply(String, out);
};

var solve = function (text) {
    var i;
    var cA = 'A'.charCodeAt(0);

    var textLettersZero = arrayOffset(stringAsCodes(lettersOnly(text)), -cA);
    var textHist = Algebra.histPercent(textLettersZero);

    var wofLettersZero = arrayOffset(stringAsCodes('RSTLNE'), -cA);
    var wofHist = Algebra.histPercent(wofLettersZero, 26);

    var oHist, r;
    var bestR = 0, bestI = -1;
    for (i = 0; i &lt; 26; i++) {
        oHist = arrayRotate(textHist, i);
        r = Algebra.correl(oHist, wofHist);
        if (r &gt; bestR) {
            bestR = r;
            bestI = i;
        }
    }

    var c;
    if (bestI &gt; -1) {
        return String.fromCharCode(bestI + cA);
    } else {
        throw new Error('No Solution?!');
    }
};

var key = solve(...text...);
console.log('MESSAGE: (shifted ' + key + ' to A)');
console.log(caesarDecrypt(text, key));

// Toolbox unit tests
var assert = require('assert');
assert.equal(Algebra.sum([1, 2, 3]), 6, 'Test Sum');
assert.equal(Algebra.mean([1, 2, 3]), 2.0, 'Test Mean');
assert.equal(Algebra.correl([1, 2, 3], [1, 2, 3]), 1.0, 'Test Correl');
assert.equal(Algebra.correl([3, 2, 1], [1, 2, 3]), -1.0, 'Test -Correl');
assert.equal(arrayOffset([0], 2)[0], 2, 'Test Offset');
assert.equal(Algebra.hist([1, 1, 1])[1], 3, 'Test Hist');
assert.equal(Algebra.hist([1], 3).length, 3, 'Test Hist Num Bins');
assert.equal(Algebra.histPercent([1,1,1,2,2,2])[1], 0.5, 'Test Hist Percent');
assert.equal(caesarDecrypt('M NO!', 'M'), 'A BC!', 'Test Caesar Decrypt');
assert.equal(caesarDecrypt('XYZABC', 'Z'), 'YZABCD', 'Test Caesar Decrypt');
assert.equal(stringAsCodes('ABC')[1], 'B'.charCodeAt(0), 'Test stringAsCodes');
assert.equal(lettersOnly('!-ABC Zaz'), 'ABCZ', 'Test Letters Only');
assert.equal(arrayRotate([1,2,3,4,5], -1)[0], 5, 'Test Rotate Right');
assert.equal(arrayRotate([1,2,3,4,5], 1)[3], 5, 'Test Rotate Left');
</code></pre>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2014-04-15T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Linux RAID with mdadm - Do's and Dont's]]></title>
      <link>http://jlmessenger.com/blog/linux-raid-mdadm-dos-donts</link>
      <guid>http://jlmessenger.com/blog/linux-raid-mdadm-dos-donts</guid>
      <description><![CDATA[<p>I've been keeping my personal data safe with linux software raids for almost a decade. I've even convinced many friends to do the same. Lost data is so frustrating... actually, loosing data was one of the forces that pushed me to abandon M$ windowz and become a daily linux user.</p>

<h2>History</h2>

<p>It was the early 2000&rsquo;s and everyone was sharing multimedia files. The p2p networks had anything you could want and DVD writers + sneakernet allowed even the bandwidth poor to get anything from friends.</p>

<p>I bought a <em>huge</em> 160GB drive to act as the primary dumping ground for my new digital treasure and I filled it to the brim, or so I thought. While editing some videos with a friend I discovered chunks of other video files interspersed with the file I was editing. Turns out windowz was only using 32bit adressing on my disk and after I reached 128GB it started truncating the addresses and writing back over the other data while reporting that everything was OK.</p>

<p>I thought RAID 5 would be cool, until I realized how expensive and loud that would be. Taking a step back I decided that I could afford 2 drives in a mirrored configuration called RAID 1. I originally RAIDed my drives with the so-called raid controller on my motherboard. Bad Idea - it wasn&rsquo;t a <em>real</em> raid controller. It used the CPU to do all the heavy lifting, the admin interface sucked and no other vendor&rsquo;s controller wanted anything to do with those drives. Linux to the rescue!</p>

<h2>Glorious Software RAID</h2>

<p>Creating a software RAID in linux is easy and has allowed me to move the same pair of drives into multiple systems with different hardware and kernels without a problem for nearly a decade. Additionally, in a pinch I can mount each drive independently without any raid software at-all.</p>

<p>You may need to install <code>mdadm</code>. In Ubuntu or Debian use:</p>

<pre><code>sudo apt-get install mdadm
</code></pre>

<h3>Preparing Your Drives</h3>

<p>If your drives are brand new you can skip to step 2. For this example I'll call the drives <code>/dev/sdX</code> and <code>/dev/sdY</code> but replace them with the actual names/letters from your system. These instructions also assume that the entire drive will be used for your RAID.</p>

<p><strong>1) Check for previous raid superblocks</strong></p>

<p>If you get the following response, then you are likely in good shape.</p>

<pre><code>&gt; sudo mdadm --manage --examine /dev/sdX
mdadm: No md superblock detected on /dev/sdX.
</code></pre>

<p>But if you get the following, you'll need to do some cleanup first.</p>

<pre><code>&gt; sudo mdadm --manage --examine /dev/sdY
/dev/sdY:
          Magic : a92b4efc
        Version : 0.90.00
           UUID : 2a321d73:92a29f89:91a9f934:c8ab7b11 (local to host)
  Creation Time : Tue Jan 25 17:02:50 2011
     Raid Level : raid1
  Used Dev Size : 625131776 (596.17 GiB 640.13 GB)
     Array Size : 625131776 (596.17 GiB 640.13 GB)
   Raid Devices : 2
  Total Devices : 2
Preferred Minor : 1

    Update Time : Sun Feb 10 11:39:41 2013
          State : clean
 Active Devices : 2
Working Devices : 1
 Failed Devices : 0
  Spare Devices : 0
       Checksum : 83ef590e - correct
         Events : 122


      Number   Major   Minor   RaidDevice State
this     1       8       48        1      active sync   /dev/sdY
</code></pre>

<p>Remove the old superblock.</p>

<pre><code>&gt; sudo mdadm --misc --zero-superblock /dev/sdY
</code></pre>

<p>Erase all drive meta data.</p>

<pre><code>&gt; sudo dd if=/dev/zero of=/dev/sdY bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 1.86259 s, 56.3 MB/s
</code></pre>

<p><strong>2) Create partitions</strong></p>

<p>You'll want do these steps on both drives.</p>

<pre><code>&gt; sudo fdisk /dev/sdX
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0xe0fd90a7.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

&gt; Command (m for help): o
Building a new DOS disklabel with disk identifier 0xacab143c.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

&gt; Command (m for help): p
Disk /dev/sdc: 640.1 GB, 640133946880 bytes
255 heads, 63 sectors/track, 77825 cylinders, total 1250261615 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xacab143c

   Device Boot      Start         End      Blocks   Id  System

&gt; Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
&gt; Select (default p): p
&gt; Partition number (1-4, default 1): 1
&gt; First sector (2048-1250261614, default 2048): 2048
</code></pre>

<p>Do <strong>not</strong> setup your partitions all the way to the last sector. I left 2 GiB at the end of my partition. I once had a drive run out of spare sectors. Rather than warning me, it truncated the data and resized it and it suddenly became too small to pair with the other drive. Choosing to leave even more freespace will help if you ever need to replace a failed disk with one that is not identical to the other. (see fixing a busted array)</p>

<p>As you can see above my sectors are each 512 bytes and I wanted 2GB free.<br />
1024 * 1024 * 1024 * 2 = 2147483648 bytes to save<br />
2147483648 / 512 = 4194304 sectors to save</p>

<p>last sector - sectors to save = end of the partition<br />
1250261614 - 4194304 = 1246067310 last sector</p>

<pre><code>&gt; Last sector, +sectors or +size{K,M,G} (2048-1250261614, default 1250261614): 1246067310

&gt; Command (m for help): t
Selected partition 1
&gt; Hex code (type L to list codes): fd
Changed system type of partition 1 to fd (Linux raid autodetect)

&gt; Command (m for help): p
Disk /dev/sdc: 640.1 GB, 640133946880 bytes
238 heads, 28 sectors/track, 187614 cylinders, total 1250261615 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xacab143c

   Device Boot      Start         End      Blocks   Id  System
/dev/sdcX            2048  1246067310   623032631+  fd  Linux raid autodetect

&gt; Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
</code></pre>

<p>Repeat this process for the second drive, but use the same last sector value that you used for the first drive so the partitions are identical.</p>

<h3>Create the RAID array</h3>

<p><strong>1) Create the RAID array</strong></p>

<p><em>Note</em>: You want to setup the partitions in your raid, not the raw disk, so use <code>/dev/sdX1</code> <strong>not</strong> <code>/dev/sdX</code>.</p>

<pre><code>&gt; sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdX1 /dev/sdY1
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
mdadm: size set to 622901376K
&gt; Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
</code></pre>

<p><strong>2) Format the partition</strong></p>

<p>I used the default linux filesystem <code>ext4</code>.</p>

<pre><code>&gt; sudo mkfs.ext4 -v /dev/md0
mke2fs 1.42 (29-Nov-2011)
fs_types for mke2fs.conf resolution: 'ext4'
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
38936576 inodes, 155725344 blocks
7786267 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
4753 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
    4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
    102400000

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done     
</code></pre>

<h3>Configure for use</h3>

<p><strong>1) Setup mdadm.conf</strong></p>

<p>Each raid array is given a unique id which can be used by mdadm to setup the raid array during boot. You can use mdadm to get the unique id of your array.</p>

<pre><code>&gt;sudo mdadm --misc --detail /dev/md0
    /dev/md0:
            Version : 1.2
      Creation Time : Mon Feb 18 10:26:45 2013
         Raid Level : raid1
         Array Size : 622901376 (594.05 GiB 637.85 GB)
      Used Dev Size : 622901376 (594.05 GiB 637.85 GB)
       Raid Devices : 2
      Total Devices : 2
        Persistence : Superblock is persistent

        Update Time : Mon Feb 18 15:38:08 2013
              State : clean
     Active Devices : 2
    Working Devices : 2
     Failed Devices : 0
      Spare Devices : 0

               UUID : 449ec249:ca6af101:8ca61121:a4f427b9  &lt;--- unique id
             Events : 4394

        Number   Major   Minor   RaidDevice State
           0       8       33        0      active sync   /dev/sdX1
           2       8       49        1      active sync   /dev/sdY1
</code></pre>

<p>Update <code>mdadm.conf</code></p>

<pre><code>&gt; sudo vim /etc/mdadm/mdadm.conf
</code></pre>

<p>Then in the array section I added the line:</p>

<pre><code>ARRAY /dev/md0 449ec249:ca6af101:8ca61121:a4f427b9
</code></pre>

<p><strong>2) Mount and Use It</strong></p>

<p>I'll let you take it from here. Enjoy!
Don't forget <code>/etc/fstab</code></p>

<h2>Fixing a Busted RAID Array</h2>

<p>As mentioned above, I once made the mistake of using the entire drive for my raid array and then found myself with 2 drives that would not assemble.</p>

<p>Below is how I fixed it.</p>

<p><strong>1) Make a backup</strong></p>

<p>If you can make an additional backup than do it. I didn't have enough space so I backed-up what I cared about most - but the process below should allow you to reconfigure your raid array without losing any data.</p>

<p><strong>2) Free up one drive in the array</strong></p>

<p>If your array is already degraded then you can skip to step 3 using the already rejected drive.</p>

<p>If your array has not yet failed, but you know it is flawed you can manually fail one of the drives. This will leave the array running in a degraded mode with only a single drive.</p>

<pre><code>&gt; sudo mdadm --manage /dev/md0 --fail /dev/sdX
</code></pre>

<p><strong>3) Clean up and configure</strong></p>

<p>Follow steps under <em>Preparing Your Drives</em> to cleanup and configure <code>/dev/sdX</code></p>

<p><strong>4) Create a new incomplete raid array</strong></p>

<p>Using the newly prepped partition you can create a new raid array.</p>

<pre><code>&gt; sudo mdadm --create /dev/md1 -v --level=1 --raid-devices=2 /dev/sdX1 missing
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
mdadm: size set to 622901376K
&gt;Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md1 started.
</code></pre>

<p>Format the new array as-well</p>

<pre><code>&gt; sudo mkfs.ext4 /dev/md1
</code></pre>

<p><strong>5) Mount and copy the data</strong></p>

<p>I&rsquo;d mount the original array <code>/dev/md0</code> readonly and the new array <code>/dev/md1</code> readwrite. Then copy all the data from the old array to the new array.</p>

<p><strong>6) Check everything is copied</strong></p>

<p>I&rsquo;m a little paranoid so I dumped the file paths and sizes of the data on each drive and compared them with <code>diff</code>.</p>

<pre><code>&gt; sudo find mount_point/of_md0 -type f -printf "%P %s\n" | sort &gt; drive_md0.txt
&gt; sudo find mount_point/of_md1 -type f -printf "%P %s\n" | sort &gt; drive_md1.txt
&gt; diff -u drive_md0.txt drive_md1.txt
--- drive_md0.txt   2013-02-18 15:18:55.721848694 -0500
+++ drive_md1.txt   2013-02-18 15:19:30.909173089 -0500
@@ -33439,7 +33439,6 @@
 afolder/file-x.jpg 57344
 afolder/file-y.jpg 430729
 afolder/file-z.jpg 46778
-.DS_Store 6148
 exported/img-040.jpg 1442109
 exported/img-180.jpg 1703897
 exported/img-186.jpg 2062038    
</code></pre>

<p>As you can see I managed to leave behind a hidden file <code>.DS_Store</code> that I don&rsquo;t care about.</p>

<p><strong>7) Kill the original array</strong></p>

<p>We are now going to kill the last disk from the original array. It&rsquo;s ok if you triple check your backups, I did.</p>

<p>Unmount <code>/dev/md0</code> then stop it</p>

<pre><code>&gt; sudo mdadm --manage --stop /dev/md0
mdadm: stopped /dev/md0
</code></pre>

<p>This will remove the last remaining drive <code>/dev/sdY</code> from the original raid array.</p>

<p><strong>8) Clean up and configure</strong></p>

<p>Follow steps under <em>Preparing Your Drives</em> to cleanup and configure <code>/dev/sdY</code></p>

<p><strong>9) Add the other drive to the new array</strong></p>

<p>This will add the drive so that it can begin syncing</p>

<pre><code>&gt; sudo mdadm /dev/md1 --manage --add /dev/sdY1
mdadm: added /dev/sdY1
&gt; cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] 
md1 : active raid1 sdX1[2] sdY1[0]
      622901376 blocks super 1.2 [2/1] [U_]
      [&gt;....................]  recovery =  0.7% (4703744/622901376) finish=93.7min speed=109830K/sec
</code></pre>

<p><strong>10) Follow the steps in <em>Configure for use</em></strong></p>

<p>And update <code>mdadm.conf</code> and <code>/etc/fstab</code> to reference the new array.</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2013-02-19T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Statamic SASS Plugin]]></title>
      <link>http://jlmessenger.com/blog/statamic-sass</link>
      <guid>http://jlmessenger.com/blog/statamic-sass</guid>
      <description><![CDATA[<p><span class="bubble">Statamic</span> Lets you use SASS/SCSS in your templates.<!--more--> Features:</p>

<ul>
<li>Mature <a href="http://sass-lang.com/">SASS</a> parser from <a href="http://www.phpsass.com/">PHPSass</a></li>
<li>Sane production ready defaults</li>
<li>Simple dev mode switch</li>
</ul>

<p>Get the <a href="https://github.com/jlmessenger/Statamic-SASS">Statamic SASS plugin</a> at GitHub.</p>

<h3>Example</h3>

<pre><code>&lt;link rel="stylesheet" href="{{ sass src='scss/myfile.scss' }}"&gt;
</code></pre>

<h3>Parameters</h3>

<ul>
<li><code>src</code> = The path to your SASS/SCSS file in your theme</li>
<li><code>dev</code> = <code>true</code> is equivalent to: <code>style="nested" update="always" error="die"</code></li>
<li><code>style</code> = how the output CSS if formatted

<ul>
<li><strong>compact</strong>: single line per selector <em>(default)</em></li>
<li><strong>compressed</strong>: minimal whitespace</li>
<li><strong>expanded</strong>: human readable multi-line style</li>
<li><strong>nested</strong>: mimics sass indent structure</li>
</ul></li>
<li><code>update</code> = what events expire the cached CSS

<ul>
<li><strong>dir</strong>: update CSS file if any SASS/SCSS file is changed in the same folder <em>(default)</em></li>
<li><strong>file</strong>: update CSS only when the file referenced by <code>src</code> is updated</li>
<li><strong>always</strong>: update CSS every time - for development</li>
</ul></li>
<li><code>error</code> = how to handle errors

<ul>
<li><strong>ignore</strong>: use last generated CSS, if any <em>(default)</em></li>
<li><strong>echo</strong>: inject into output stream with echo</li>
<li><strong>die</strong>: echo error to output and exit</li>
</ul></li>
<li><code>debug_info</code> = <code>true</code> will add debug info to the CSS file for the FireSass Firebug extension</li>
<li><code>line_numbers</code> = <code>true</code> will add SCSS line numbers references to CSS file</li>
</ul>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-07-24T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Switch to Statamic]]></title>
      <link>http://jlmessenger.com/blog/switch-to-statamic</link>
      <guid>http://jlmessenger.com/blog/switch-to-statamic</guid>
      <description><![CDATA[<p>When I first lauched my website over a year ago I wrote a simple PHP
script that converted markdown files into page routes. However, I abandond
it for wordpress because I had no time to add all the features I needed.</p>

<p>Then just a few weeks ago, some friends of mine released a robust
flat-file CMS. It turns out it has all the features of my
custom PHP script, <em>plus</em> all the <a href="http://statamic.com/docs/managing-content">features I needed</a> and <a href="http://statamic.com/docs/control-panel">more</a>.</p>

<p>So yes I&rsquo;m a <a href="http://statamic.com">Statamic</a> fan!</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-07-20T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Simple TCP Messages with NetSocket]]></title>
      <link>http://jlmessenger.com/blog/simple-tcp-messages-with-netsocket</link>
      <guid>http://jlmessenger.com/blog/simple-tcp-messages-with-netsocket</guid>
      <description><![CDATA[<p><strong>Update:</strong> The NetSocket library has been tested in <a href="http://mono-project.com/">mono</a>.
Now you can use it to communicate between Windows, OS X and Linux.</p>

<p>Back in the days before .Net it was easy to add network messaging
with the Winsock Control. Unfortunately .Net does not include an
out-of-the box messaging API. Thankfully it has everything
you need to make one.<!--more--></p>

<p>There are many other projects that re-implement the old Winsock
API, but using a VB4 API just never felt very dot-net. NetSocket
is a simple library for transmitting binary messages between
networked .Net apps. It uses standard dot-net conventions and
has no additional dependancies.</p>

<p>See the <a href="https://github.com/jlmessenger/NetSocket">NetSocket</a> code on GitHub.<br />
Note: NetSocket is not designed to be compatible with
Winsock or and other messaging protocol. The header format is
easy to change so feel free to rewrite it. It could be updated to
support any protocol with the message byte-length in the header.</p>

<p>The project includes a pair of demo applications and code
that show how the library works.</p>

<h3>Test NetSocket Client</h3>

<p><img src="/assets/img/netsocket_testclient.png" alt="NetSocket Demo Client" /></p>

<h3>Test NetSocket Server</h3>

<p><img src="/assets/img/netsocket_testserver.png" alt="NetSocket Demo Server" /></p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-05-21T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Introducing JSON Lite]]></title>
      <link>http://jlmessenger.com/blog/introducing-json-lite</link>
      <guid>http://jlmessenger.com/blog/introducing-json-lite</guid>
      <description><![CDATA[<p>Thankfully, as of PHP 5.2 there are built-in functions for
converting between PHP objects and JSON. But, as we&rsquo;ve all
experienced sometimes you need to support people stuck in
the past. Without further adieu, I introduce JSON Lite.<!--more--></p>

<p>Get <a href="https://github.com/jlmessenger/JSON-Lite">JSON Lite</a> from GitHub.</p>

<h2>Benchmark</h2>

<p>To compare speed with the popular JSON libraries available I setup a
very simple benchmark. The contenders include:</p>

<ul>
<li><a href="http://pecl.php.net/package/json">The lib</a> native PHP methods</li>
<li><a href="http://pear.php.net/pepr/pepr-proposal-show.php?id=198">PEAR JSON Lib</a></li>
<li><a href="http://sourceforge.net/projects/phpxmlrpc/files/extras/">Jsonrpc</a> extension to php-xmlrpc lib</li>
<li><a href="https://github.com/jlmessenger/JSON-Lite">JSON Lite</a></li>
</ul>

<h3>Setup</h3>

<p>Each library was setup to encode a PHP array to JSON 50
times, then convert it back to a PHP array 50 times.</p>

<h3>Results PHP to JSON</h3>

<ul>
<li><strong>Native PHP Methods</strong>: 0.2ms</li>
<li><strong>JSON-Lite</strong>: 14.0ms</li>
<li><strong>PEAR JSON Lib</strong>: 14.0ms</li>
<li><strong>Jsonrpc</strong>: 49.8ms</li>
</ul>

<h3>Results JSON to PHP</h3>

<ul>
<li><strong>Native PHP Methods</strong>: 0.4ms</li>
<li><strong>JSON-Lite</strong>: 14.1ms</li>
<li><strong>Jsonrpc</strong>: 47.9ms</li>
<li><strong>PEAR JSON Lib</strong>: 92.8ms</li>
</ul>

<h3>Benchmark Code Excerpts</h3>

<pre><code>// test array
$php_test = array(
    'numbers' =&gt; array(234234, 345345, 345245, 234234),
    'strings' =&gt; array('!@#$%^&amp;*()_+', '{}|\?&gt;&lt;:', 'asasdd'),
    'special' =&gt; array(true, false, null, 0, '0'),
    'hashes' =&gt; array('key1' =&gt; 'a', 'key2' =&gt; 'b', 'key3' =&gt; 'c'),
    '!@#$%^&amp;*)(_){}[]' =&gt; 'weird key'
);

// Native PHP Methods
$json_str = json_encode($php_test);
$php_arr = json_decode($json_str, true);

// PEAR JSON Lib
$pear_obj = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$json_str = $pear_obj-&gt;encode($php_test);
$php_arr = $pear_obj-&gt;decode($json_str);

// Jsonrpc
$value = php_jsonrpc_encode($php_test);
$json_str = $value-&gt;serialize();
$value = php_jsonrpc_decode_json($json_str);
$php_arr = php_jsonrpc_decode($value);

// JSON Lite
$json_str = json_lite::to_json($php_test);
$php_arr = json_lite::from_json($json_str, FALSE);
</code></pre>

<h3>Final Thoughts</h3>

<p>It should be noted that JSON Lite is the only library in
group that produced a slightly different, although fully
valid, JSON string.</p>

<p>JSON Lite produced:</p>

<pre><code>{"numbers":[234234,345345,345245,234234],
"strings":["!@#$%^&amp;*()_+","\u007b\u007d|\\?&gt;&lt;:","asasdd"],
"special":[true,false,null,0,0],
"hashes":{"key1":"a","key2":"b","key3":"c"},
"!@#$%^&amp;*)(_)\u007b\u007d\u005b\u005d":"weird key"}
</code></pre>

<p>The others librarys produced:</p>

<pre><code>{"numbers":[234234,345345,345245,234234],
"strings":["!@#$%^&amp;*()_+","{}|\\?&gt;&lt;:","asasdd"],
"special":[true,false,null,0,"0"],
"hashes":{"key1":"a","key2":"b","key3":"c"},
"!@#$%^&amp;*)(_){}[]":"weird key"}
</code></pre>

<p>So, JSON Lite is faster than the other libraries because
it makes some tradeoffs in how it encodes JSON strings
and is limited to creating PHP arrays from JSON strings
it created. Hopefully you will find it useful for PHP to PHP
process communication / RPC calls or for exporting from PHP
to other JSON parsers and APIs.</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-04-25T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Command Line EE]]></title>
      <link>http://jlmessenger.com/blog/command-line-ee</link>
      <guid>http://jlmessenger.com/blog/command-line-ee</guid>
      <description><![CDATA[<p><span class="bubble">EE1</span> Ever needed an easy way to schedule an action with a cron or get
your website content from the command line. The code below has everything you need to interact
with your EE website using the PHP CLI.<!--more--></p>

<h2>File cli.php</h2>

<pre><code>&lt;?php
/**
 * Command Line ExpressionEngine
 *
 * Arguments:
 *  server_[name]=[value]
 *  get_[name]=[value]
 *  post_[name]=[value]
 *  cookie_[name]=[value]
 *  env_[name]=[value]
 *  files_[name]=[value]
 *  session_[name]=[value]
 *  [segment_1] [segment_2] ... [segment_n]
 *
 * Examples:
 * Run action id 12:
 * cli.php get_ACT=12
 *
 * Get page at /site/info
 * cli.php site info
 *
 * Post 'email' to action id 12 on page /site/form/contact
 * cli.php "get_ACT=12" post_email=bjones@test.com site form contact
 *
 * Notes:
 * In order to allow more memory usage or a longer execution time
 * execute with php option -d
 * -d max_execution_time=60 memory_limit=134217728
 *
 * @package   EE-CLI
 * @version   1.0
 * @author    Jeremy Messenger &lt;jlmessengertech+github@gmail.com&gt;
 * @copyright 2011 Jeremy Messenger
 * @license   GPL
 * @link      http://jlmessenger.com
 */

// make sure this isn't being called by a web browser
if (isset($_SERVER['REMOTE_ADDR'])) die('Permission denied.');

/**
 * Constant to identify command line execution thru this script
 */
define('CMD', 1);

// _REQUEST is built automatically later
$superglobals = array('SERVER', 'GET', 'POST', 'COOKIE', 'ENV', 'FILES', 'SESSION');

// manually set the URI path based on command line arguments...
unset($argv[0]); // first argument is this file itself

// parse arguments for overridden superglobals
foreach ($argv as $key =&gt; $arg)
{
    $subargs = explode('=', $arg, 2);
    if (count($subargs) == 2)
    {
        $opts = explode('_', $subargs[0], 2);
        if (count($opts) == 2)
        {
            $opts[0] = strtoupper($opts[0]);
            if (in_array($opts[0], $superglobals))
            {
                $gl = '_'.$opts[0];

                if ($opts[0] == 'SERVER')
                {
                    $opts[1] = strtoupper($opts[1]);
                }

                $GLOBALS[$gl][$opts[1]] = $subargs[1];
                unset($argv[$key]);
            }
        }
    }
}

// build _REQUEST from _GET, _POST, &amp; _COOKIE
// build in the same order as defined in php.ini
$requestorder = str_split(ini_get('variables_order'));
foreach($requestorder as $char)
{
    switch($char)
    {
        case 'G':
            $_REQUEST = array_merge($_GET, $_REQUEST);
            break;
        case 'P':
            $_REQUEST = array_merge($_POST, $_REQUEST);
            break;
        case 'C':
            $_REQUEST = array_merge($_COOKIE, $_REQUEST);
            break;
        //case 'E': _ENV
        //case 'S': _SERVER
    }
}

// build required _SERVER vars
// QUERY_STRING from _GET
// PATH_INFO from argv
// REQUEST_URI from the others
$querystring = array();
foreach($_GET as $key =&gt; $val)
{
    $querystring[] = urlencode($key).'='.urlencode($val);
}

$_SERVER['SCRIPT_NAME'] = 'index.php';
$_SERVER['PATH_INFO'] = count($argv) ? '/'.implode('/', $argv).'/' : '';
$_SERVER['QUERY_STRING'] = implode('&amp;', $querystring);
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'].$_SERVER['PATH_INFO'].'?'.$_SERVER['QUERY_STRING'];

// EE Ouput class assumes that this is set
// and will cause a notice unless we set it
if (!isset($_SERVER['SERVER_PROTOCOL']))
{
    $_SERVER['SERVER_PROTOCOL'] = 'CLI';
}

// no longer needed
unset($superglobals);
unset($requestorder);
unset($querystring);

/**
 * Call up the framework
 */
include('index.php');
?&gt;
</code></pre>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-04-09T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[He&rsquo;s Here]]></title>
      <link>http://jlmessenger.com/blog/hes-here</link>
      <guid>http://jlmessenger.com/blog/hes-here</guid>
      <description><![CDATA[<p>Yesterday, on March 24th at around 4:30pm Elijah Len was born.
He and Danielle are doing great! Thanks for the support everyone.</p>

<p><img src="/assets/img/elijah_len.jpg" alt="Elijah Len" /></p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-03-25T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[Backporting EE Views]]></title>
      <link>http://jlmessenger.com/blog/backporting-ee-views</link>
      <guid>http://jlmessenger.com/blog/backporting-ee-views</guid>
      <description><![CDATA[<p><span class="bubble">EE1</span> I prefer to keep software up-to-date, especially when
there are so many security fixes. Sometimes the best options are taken away and you must
do bad things. Well, there is a saying about lemons and pimm&rsquo;s making a great summer drink.<!--more--></p>

<p>Recently I was tasked with updating some custom module code on an older
EE 1.6.x series website. The module has been updated to take advantage of
new EE features, like the addition of CI views. Without views the
whole module produced a blank screen in the CP. The code below provides this
functionality in older EE versions.</p>

<h2>Nuts and Bolts</h2>

<p>Save the file at the bottom of this article in your module.
Then before you call $DSP->view make sure you override it with the following code:</p>

<pre><code>global $DSP;
// this check is only needed if not in the mcp file
if ( ! class_exists('Display') )
{
    require PATH_CP.'cp.display'.EXT;
    $DSP = new Display();
}
if ( ! method_exists($DSP, 'view') )
{
    require PATH_MOD.'yourmod/display_view'.EXT;
    $DSP = new Display_view($DSP, PATH_MOD.'yourmod/views/');
}
</code></pre>

<h2>Display View Class</h2>

<pre><code>/**
 * Display_view class wraps EE Display class and provides
 * DSP-&gt;view method to the control panel for older versions of EE
 *
 * Save as display_view.php in your module
 *
 * @author Jeremy Messenger &lt;jlmessengertech+eeview@gmail.com&gt;
 * @license the view function is based on CodeIgniter and
 *  may fall under it's licensing terms, all other code is
 *  public domain and is 'AS IS' without warranty.
 * @copyright 2011 Jeremy Messenger
 */
class Display_view
{
    // path to view files, set to the current addon's path
    var $view_path      = '';
    // array of cached view variables
    var $cached_vars    = array();

    var $old_display = NULL;

    function Display_view($old_display, $view_path)
    {
        $this-&gt;old_display = $old_display;
        $this-&gt;view_path = $view_path;
    }

    // wrap the old display instance
    function __call($name, $args) {
        return call_user_func_array(array($this-&gt;old_display, $name), $args);}
    function __set($name, $value) {
        $this-&gt;old_display-&gt;$name = $value;}
    function __get($name) {
        return $this-&gt;old_display-&gt;$name;}
    function __isset($name) {
        return isset($this-&gt;old_display-&gt;$name);}
    function __unset($name) {
        unset($this-&gt;old_display-&gt;$name);}

    // add view method in newer version of EE
    function view($view, $vars = array(), $return = FALSE, $path = '')
    {
        global $FNS, $LANG, $LOC, $PREFS, $REGX, $SESS;

        // Set the path to the requested file
        if ($path == '')
        {
            $ext = pathinfo($view, PATHINFO_EXTENSION);
            $file = ($ext == '') ? $view.EXT : $view;
            $path = $this-&gt;view_path.$file;
        }
        else
        {
            $x = explode('/', $path);
            $file = end($x);
        }

        if ( ! file_exists($path))
        {
            trigger_error('Unable to load the requested file: '.$file);
            return FALSE;
        }

        /*
         * Extract and cache variables
         *
         * You can either set variables using the dedicated
         * $this-&gt;load_vars() function or via the second
         * parameter of this function. We'll merge the two types
         * and cache them so that views that are embedded within
         * other views can have access to these variables.
         */ 
        if (is_array($vars))
        {
            $this-&gt;cached_vars = array_merge($this-&gt;cached_vars, $vars);
        }
        extract($this-&gt;cached_vars);

        /*
         * Buffer the output
         *
         * We buffer the output for two reasons:
         * 1. Speed. You get a significant speed boost.
         * 2. So that the final rendered template can be
         * post-processed by the output class.  Why do we
         * need post processing?  For one thing, in order to
         * show the elapsed page load time.  Unless we can
         * intercept the content right before it's sent to the
         * browser and then stop the timer it won't be accurate.
         */
        ob_start();

        // If the PHP installation does not support short tags we'll
        // do a little string replacement, changing the short tags
        // to standard PHP echo statements.
        if ((bool) @ini_get('short_open_tag') === FALSE)
        {
            echo eval('?&gt;'.preg_replace("/;*\s*\?&gt;/", "; ?&gt;",
                str_replace('&lt;?=', '&lt;?php echo ', file_get_contents($path))));
        }
        else
        {
            // include() vs include_once() allows for multiple views with the same name
            include($path);
        }

        // Return the file data if requested
        if ($return === TRUE)
        {       
            $buffer = ob_get_contents();
            ob_end_clean();
            return $buffer;
        }

        /*
         * Flush the buffer... or buff the flusher?
         *
         * In order to permit views to be nested within
         * other views, we need to flush the content back out
         * whenever we are beyond the first level of output
         * buffering so that it can be seen and included properly
         * by the first included template and any subsequent
         * ones. Oy!
         */
        if (ob_get_level() &gt; 1)
        {
            ob_end_flush();
        }
        else
        {
            $buffer = ob_get_contents();
            ob_end_clean();
            return $buffer;
        }
    }
}
</code></pre>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-03-07T00:00:00+00:00</dc:date>
    </item>
        <item>
      <title><![CDATA[EE Passwd Plugin]]></title>
      <link>http://jlmessenger.com/blog/passwd-plugin</link>
      <guid>http://jlmessenger.com/blog/passwd-plugin</guid>
      <description><![CDATA[<p><span class="bubble">EE1</span> Allows users to change their password.<!--more--> Features:</p>

<ul>
<li>Supports a list of disallowed passwords (for forcing users to change their password)</li>
<li>Can require current password (or not)</li>
<li>Users don&rsquo;t need to re-login</li>
</ul>

<p>Get the <a href="https://github.com/jlmessenger/Passwd-Plugin">EE Passwd Plugin</a> from GitHub.</p>

<h2>Documentation</h2>

<h3>Parameters</h3>

<ul>
<li><code>new_password</code> = The name of the form field or field array which supply the new user password</li>
<li><code>expired_passwords</code> = (optional) A pipe "|" separated list of disallowed/expired passwords</li>
<li><code>old_password</code> = (optional) The form field with the current password, only checked if not expired</li>
<li><code>secure</code> = (optional) Set to "y" to post the new password using https</li>
</ul>

<h3>Variables</h3>

<ul>
<li><code>{passwd_form} {/passwd_form}</code> = Wrap your change password form and writes HTML &lt;form&gt; elements</li>
</ul>

<h3>Conditionals</h3>

<ul>
<li><code>{if no_results}</code> = If a required parameter is missing, or the user is not a logged in user</li>
<li><code>{if error}</code> = The submitted password was blank, did not match, or was not secure per EE settings</li>
<li><code>{if changed}</code> = The user's password has been changed</li>
<li><code>{if password_expired}</code> = The user's current password is in the <code>expired_passwords</code> list</li>
</ul>

<h3>Example</h3>

<p>The example below allows a user to change their password and requires their existing password if it's not in the expired list.</p>

<pre><code>{exp:passwd
    expired_passwords="changeme|firstlogin"
    old_password="oldpass"
    new_password="newpass"
    secure="y"
}
{if changed}
    &lt;p&gt;Password Changed!&lt;/p&gt;
{if:elseif error}
    &lt;p&gt;Passwords did not match, were blank, or were not secure.&lt;/p&gt;
{/if}
{if password_expired}
    {passwd_form}
    New Password: &lt;input type="password" name="newpass[1]" /&gt;&lt;br/&gt;
    New Password: &lt;input type="password" name="newpass[2]" /&gt;&lt;br/&gt;
    &lt;input type="submit" value="Change" /&gt;
    {/passwd_form}
{if:else}
    {passwd_form}
    Existing Password: &lt;input type="password" name="oldpass" /&gt;&lt;br/&gt;
    New Password: &lt;input type="password" name="newpass[1]" /&gt;&lt;br/&gt;
    New Password: &lt;input type="password" name="newpass[2]" /&gt;&lt;br/&gt;
    &lt;input type="submit" value="Change" /&gt;
    {/passwd_form}
{/if}
{/exp:passwd}
</code></pre>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2011-02-17T00:00:00+00:00</dc:date>
    </item>
    
    
  </channel>

</rss>