<?xml version="1.0"?>
<rss version="2.0">
 <channel>
  <title>Home.Dataforce.org.uk</title>
  <link>http://home.dataforce.org.uk/</link>
  <description>Latest 10 News Articles</description>
  <language>en-us</language>
  <copyright>Copyright 2005 Shane "Dataforce" Mc Cormack</copyright>
 
  <docs>http://backend.userland.com/rss</docs>
  <managingEditor>rss_home@dataforce.org.uk</managingEditor>
  <webMaster>rss_home@dataforce.org.uk</webMaster>
  <generator>php</generator>
  <item>
    <title>Ident Server</title>
    <description><![CDATA[I recently encountered a problem on a server that I manage where by the oidentd server didn't seem to be working.<br>
<br>
Manual tests worked, but connecting to IRC Servers didn't.<br>
<br>
I tried switching oidentd with ident2 and the same problem.<br>
<br>
After switching back, and a bit of debugging later it appeared that the problem was that the IRC Servers were expecting spaces in the ident reply, whereas oidentd wasn't giving them.<br>
<br>
I then quickly threw together an xinet.d-powered ident server with support for spoofing.<br>
<br>
First the xinet.d config:<br>
<pre class="prettyprint">
service ident
{
	disable = no
	socket_type = stream
	protocol = tcp
	wait = no
	user = root
	server = /root/identServer.php
	nice = 10
}
</pre>Unfortunately yes, this does need to run as root otherwise it is unable to see what process is listening on a socket. In future I plan to change it to allow it to run without needing to be root (by using sudo for the netstat part)<br>
<br>
Now for the code itself:<br>
<pre class="prettyprint">
#!/usr/bin/php
&lt;?php
	/**
	 * Simple PHP-Based inetd ident server, version 0.1.
	 * Copyright (c) 2010 - Shane &quot;Dataforce&quot; Mc Cormack
	 * This code is licensed under the MIT License, of which a copy can be found
	 * at http://www.opensource.org/licenses/mit-license.php
	 *
	 * The latest version of the code can be found at
	 * http://home.dataforce.org.uk/index.php?p=news&amp;id=135
	 *
	 * This should be run from inetd, it will take input on stdin and write to stdout.
	 *
	 * By default users can spoof ident by having a .ident file in /home/&lt;username&gt;/.ident
	 * If this is present, it will be read.
	 * It should be a file with a format like so:
	 *
	 * &lt;pid&gt; &lt;ident&gt;
	 * &lt;local host&gt;:&lt;local port&gt;:&lt;target host&gt;:&lt;target port&gt; &lt;ident&gt;
	 *
	 * The first line that matches is used, any bit can be a * and it will always match,
	 * so &quot;* user&quot; is valid. In future more sophisticated matches will be permitted
	 * (eg 127.*) but for now its either all or nothing.
	 *
	 * Its worth noting that &lt;target host&gt; is the host that requests the ident, so if this
	 * is likely to be different than the host that was connected to, then &quot;STRICT_HOST&quot; will
	 * need to be set to false.
	 *
	 * At the moment &lt;local host&gt; is ignored, in future versions this might be changed, so
	 * it is still required.
	 *
	 * Lines with a ':' in them are assumed to be of the second format, and must contain
	 * all 4 sections or they will be ignored.
	 *
	 * Lines starting with a # are ignored.
	 *
	 * There are some special values that can be used as idents:
	 *    ! = Send an error instead.
	 *    * = Send the default ident.
	 *    ? = Send a random ident (In future a 3rd parameter will specify the format,
	 *        # for a number, @ for a letter, ? for either, but this is not implemented yet)
	 *
	 * In future there will also be support for /home/user/.ident.d/ directories, where
	 * every file will be read for the ident response untill one matches.
	 * This will allow multiple processes to create files rather than needing to
	 * lock and edit .ident
	 */

	// Allow spoofing idents.
	define('ALLOW_SPOOF', true);

	// Requesting host must be the same as the host that was connected to.
	define('STRICT_HOST', true);

	// Error to send when '!' is used as an ident.
	define('HIDE_ERROR', 'UNKNOWN-ERROR');

	openlog('simpleIdent', LOG_PID | LOG_ODELAY, LOG_DAEMON);

	$result = 'ERROR : UNKNOWN-ERROR' . &quot;\n&quot;;

	$host = $_SERVER['REMOTE_HOST'];

	syslog(LOG_INFO, 'Connection from: '.$host);

	// Red in the line from the socket.
	$fh = @fopen('php://stdin', 'r');
	if ($fh) {
		$input = @fgets($fh);
		$line = trim($input);
		if ($input !== FALSE &amp;&amp; !empty($line)) {
			$result = trim($input) . ' : ' . $result;
			// Get the data from it.
			$bits = explode(',', $line);
			$source = trim($bits[0]);
			$dest = isset($bits[1]) ? trim($bits[1]) : '';

			// Check if it is valid
			if (preg_match('/^[0-9]+$/', $source) &amp;&amp; preg_match('/^[0-9]+$/', $dest)) {
				// Now actually look for this!
				$match = STRICT_HOST ? &quot;:$source .*$host:$dest &quot; : &quot;:$source.*:$dest&quot;;

				$output = `netstat -napW 2&gt;&amp;1 | grep '$match' | awk '{print \$7}'`;

				$bits = explode('/', $output);
				$pid = $bits[0];

				if (preg_match('/^[0-9]+$/', $pid)) {
					$user = `ps -o ruser=SOME-REALLY-WIDE-USERNAMES-ARE-PERMITTED-HERE $pid | tail -n 1`;

					$senduser = trim($user);

					// Look for special ident file: /home/user/.ident this is an ini-format file.
					$file = '/home/'.trim($user).'/.ident';

					if (file_exists($file)) {
						$config = file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES | FILE_TEXT);
						foreach ($config as $line) {
							// Ignore comments.
							$line = trim($line);
							if (substr($line, 1) == '#') { continue; }

							// Make sure line is valid.
							$bits = explode(' ', $line);
							if (count($bits) == 1) { continue; }

							// Check type of line
							if (strpos($bits[0], ':') !== FALSE) {
								// LocalHost:LocalPort:RemoteHost:RemotePort
								$match = explode(':', $bits[0]);
								if (count($match) != 4) { continue; }

								if (($match[1] == '*' || $match[1] == $source) &amp;&amp;
								    ($match[2] == '*' || $match[2] == $host) &amp;&amp;
								    ($match[3] == '*' || $match[3] == $dest)) {
									syslog(LOG_INFO, 'Spoof for '.$senduser.': '.$line);
									$senduser = $bits[1];
									break;
								}
							} else if ($bits[0] == '*' || $bits[0] == $pid) {
								syslog(LOG_INFO, 'Spoof for '.$senduser.': '.$line);
								$senduser = $bits[1];
							}
						}

						if ($senduser == &quot;*&quot;) {
							$senduser = trim(user);
						} else if ($senduser == &quot;?&quot;) {
							$senduser = 'user'.rand(1000,9999);
						}
					}

					if ($senduser != &quot;!&quot;) {
						$result = $source . ', ' . $dest . ' : USERID : UNIX : ' . trim($senduser);
					} else {
						$result = $source . ', ' . $dest . ' : ERROR : ' . HIDE_ERROR;
					}
				}
			}
		}
	}

	echo $result;
	syslog(LOG_INFO, 'Result: '.$result);
	closelog();
	exit(0);
?&gt;
</pre><br>
I welcome any comments about this, or any improvements and hope that it will be useful for someone else.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Wed, 03 Mar 2010 22:53:00 +0000</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=135</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=135</link>
  </item>
  <item>
    <title>Images Restored</title>
    <description><![CDATA[With help from the &quot;Wayback Machine&quot; I've restored the majority of the images that were used publically on the site so things should mostly work now.<br>
<br>
To clarify on the previous post, I had setup a chroot environment for testing stuff on the server, and had mounted /home inside the chroot aswell as at /home, when I deleted the chroot, it took /home with it.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Tue, 18 Aug 2009 12:31:43 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=134</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=134</link>
  </item>
  <item>
    <title>I suck...</title>
    <description><![CDATA[Due to an incredibly stupid mistake on my part, I accidentally managed to `rm -Rf` the /home directory on the server this site is hosted on (and lost about 200gb of data, including everything that was hosted under the home.dataforce.org.uk domain.)<br>
<br>
As such this site will probably be horrible broken for some time (logins won't work, changing page style, any images or downloads) until I recover the up to date version of the scripts and as many of the files as possible. (Most of the actual content for the site is stored in mysql so none of that was lost, just the php scripts that pull it all together and anything non-page like).<br>
<br>
If you came here looking for something that now gives you a 404, feel free to leave a comment here and I'll see if I can find what you were looking for.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Mon, 06 Apr 2009 09:41:34 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=133</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=133</link>
  </item>
  <item>
    <title>GitWeb Hacking.</title>
    <description><![CDATA[Recently I setup gitweb on one of my servers to allow a web-based frontend to any git projects which the users of the server place in their ~/git/ directory.<br>
<br>
After playing about with it, I noticed that it allowed for placing a README.html file in the git config directory to allow extra info to be shown on the summary view, managed to get it to pull the README.html file from the actual repository itself, and not the config directory, thus allowing the README.html to be versioned along with everything else, and not require the user to edit it on the server, but rather just edit it locally and push it.<br>
<br>
This is a simple change in /usr/lib/cgi-bin/gitweb.cgi:<br>
<br>
From (line 3916 or so):<br>
<pre class="prettyprint">
	if (-s &quot;$projectroot/$project/README.html&quot;) {
		if (open my $fd, &quot;$projectroot/$project/README.html&quot;) {
			print &quot;&lt;div class=\&quot;title\&quot;&gt;readme&lt;/div&gt;\n&quot; .
			      &quot;&lt;div class=\&quot;readme\&quot;&gt;\n&quot;;
			print $_ while (&lt;$fd&gt;);
			print &quot;\n&lt;/div&gt;\n&quot;; # class=&quot;readme&quot;
			close $fd;
		}
	}
</pre><br>
To:<br>
<pre class="prettyprint">
	if (my $readme_base = $hash_base || git_get_head_hash($project)) {
		if (my $readme_hash = git_get_hash_by_path($readme_base, &quot;README.html&quot;, &quot;blob&quot;)) {
			if (open my $fd, &quot;-|&quot;, git_cmd(), &quot;cat-file&quot;, &quot;blob&quot;, $readme_hash) {
				print &quot;&lt;div class=\&quot;title\&quot;&gt;readme&lt;/div&gt;\n&quot;;
				print &quot;&lt;div class=\&quot;readme\&quot;&gt;\n&quot;;
				
				print &lt;$fd&gt;;
				close $fd;
				print &quot;\n&lt;/div&gt;\n&quot;;
			}
		}
	}
</pre><br>
I also added a second slightly hack that uses google's code prettyfier when displaying a file, and makes the line numbers separate from the code so they don't copy also when you copy the code,<br>
<br>
From (line 2476 or so):<br>
<pre class="prettyprint">
        print &quot;&lt;/head&gt;\n&quot; .
              &quot;&lt;body&gt;\n&quot;;

</pre><br>
To:<br>
<pre class="prettyprint">
        print qq(&lt;link href=&quot;http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css&quot; type=&quot;text/css&quot; rel=&quot;stylesheet&quot; /&gt;\n);
        print qq(&lt;script src=&quot;http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;\n);

        print &quot;&lt;/head&gt;\n&quot; .
              &quot;&lt;body onload=\&quot;prettyPrint()\&quot;&gt;\n&quot;;
</pre><br>
and<br>
<br>
From (line 4351 or so):<br>
<pre class="prettyprint">
	while (my $line = &lt;$fd&gt;) {
		chomp $line;
		$nr++;
		$line = untabify($line);
		printf &quot;&lt;div class=\&quot;pre\&quot;&gt;&lt;a id=\&quot;l%i\&quot; href=\&quot;#l%i\&quot; class=\&quot;linenr\&quot;&gt;%4i&lt;/a&gt; %s&lt;/div&gt;\n&quot;,
		       $nr, $nr, $nr, esc_html($line, -nbsp=&gt;1);
	}
</pre><br>
To:<br>
<pre class="prettyprint">
	print &quot;&lt;table&gt;&lt;tr&gt;&lt;td class=\&quot;numbers\&quot;&gt;&lt;pre&gt;&quot;;
	while (my $line = &lt;$fd&gt;) {
		chomp $line;
		$nr++;
		printf &quot;&lt;a id=\&quot;l%i\&quot; href=\&quot;#l%i\&quot; class=\&quot;linenr\&quot;&gt;%4i&lt;/a&gt;\n&quot;, $nr, $nr, $nr;
	}
	print &quot;&lt;/pre&gt;&lt;/td&gt;&quot;;
	open my $fd2, &quot;-|&quot;, git_cmd(), &quot;cat-file&quot;, &quot;blob&quot;, $hash;
	print &quot;&lt;td class=\&quot;lines\&quot;&gt;&lt;pre class=\&quot;prettyprints\&quot;&gt;&quot;;
	while (my $line = &lt;$fd2&gt;) {
		chomp $line;
		$line = untabify($line);
		printf &quot;%s\n&quot;, esc_html($line, -nbsp=&gt;1)
	}
	print &quot;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;;
	close $fd2;
</pre><br>
This could do with a quick clean up (reuse $fd rather than opening $fd2) but it works.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Fri, 27 Mar 2009 02:52:27 +0000</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=132</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=132</link>
  </item>
  <item>
    <title>New Phone - T-Mobile G1.</title>
    <description><![CDATA[Recently I acquired a T-Mobile G1 to replace my old T-Mobile MDA Vario 2 (HTC Hermes).<br>
<br>
All I can say about this phone is that it is quite awesome. I no longer need to run an exchange server to keep my contacts/calendar synced somewhere as the G1 syncs everything to Google Mail/Calendar.<br>
<br>
Its a really good phone and I recommend it to anyone who is thinking of getting a new phone, the integration with google is especially useful, and the full-html (including CSS and javascript) is very nice.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Thu, 11 Dec 2008 01:56:12 +0000</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=131</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=131</link>
  </item>
  <item>
    <title>JDesktopPane Replacement</title>
    <description><![CDATA[As as I <a href="http://home.dataforce.org.uk/index.php?p=news&amp;id=128">mentioned before</a> I've been recently converting an old project to Java.<br>
<br>
This old project was an MDI application, and when creating the UI for the conversion, I found the default JDesktopPane to be rather crappy. Google revealed others thought the same, one of the results that turned up was: <a href="http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-mdi.html">http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-mdi.html</a><br>
<br>
So, I created DFDesktopPane based on this code, with some extra changes:<br>
<ul><li> Frames can't end up with a negative x/y<br>
<li> Respond to resize events of the JViewport parent<br>
<li> Iconified icons move themselves to remain inside the desktop at all times.<br>
<li> Handles maximised frames correctly (desktop doesn't scroll, option to hide/remove titlebar)<br>
</ul><br>
My modified JDesktopPane can be found as <a href="http://code.google.com/p/dflibs/source/browse/trunk/java/uk/org/dataforce/swing/DFDesktopPane.java">here</a> part of my <a href="http://code.google.com/p/dflibs/">dflibs</a> google code project. <br>
<br>
Other useful things can be found <a href="http://code.google.com/p/dflibs/source/browse/trunk/java/uk/org/dataforce/">here</a>, take a look and leave any feedback either here or on the project issue tracker<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Sat, 09 Aug 2008 03:46:49 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=130</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=130</link>
  </item>
  <item>
    <title>GMail - apply labels to email from group members</title>
    <description><![CDATA[As Noted by <a href="http://chris.smith.name">Chris</a> recently on IRC, <a href="http://gmail.com">Google Mail</a> lacks a feature in its ability to automatically label/filter messages - you can't do it based on emails from people in a contact group, short of adding a filter with all their email address on it.<br>
<br>
At the time it was mentioned this didn't affect me, however later when I got round to adding loads of labels/filters in gmail (yay for, nicely coloured inbox!) to nicely separate things for me I also ran into this problem, so came up with the following python script that does it for me.<br>
<br>
It checks messages, sees if the sender is in the contacts, then checks each group to see if there is a label with that group name that is not already set, then checks to see if the contact is in the group, and finally sets the label if everything matches up.<br>
<br>
I ran it initially to tag my entire inbox (set &quot;checkAllIndex&quot; to &quot;True&quot; change &quot;ga.getMessagesByFolder(folderName)&quot; to &quot;ga.getMessagesByFolder(folderName, True)&quot;) and now have it running on a 15 minute cron (not using loopMode) to tag new messages for me.<br>
<br>
Hopefully this will be useful to someone else, I'm not sure how well it works in general, it worked fine for me with ~700 messages at first, however after a few runs (due to regrouping some contacts) I was greeted by an &quot;Account Lockdown: Unusual Activity Detected&quot; message when trying to do anything - This went away after about 20 minutes, but don't say you wern't warned if it happens to you.<br>
<pre class="prettyprint">
#!/usr/bin/env python
&quot;&quot;&quot;
 This script will login to gmail, and add labels to messages for contact groups.

 By default the script will only check items from the past 2 days where email
 was recieved.

 Loop mode can be enabled to save logging in repeatedly from cron.
 Loop mode may fail after some time if google kills the session, or gmail
 becomes unavailable or so. (Untested in these situations). On the other hand
 it may also just keep running indefinetly as if no problem occured, loop mode
 is relatively untested and was added as an after thought.

 When running in loop mode, it is best to have a crontab entry also that checks
 and restarts the script if it dies.

 Copyright 2008 Shane 'Dataforce' Mc Cormack

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the &quot;Software&quot;), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
&quot;&quot;&quot;

# Uncomment the lines below if python can't find libgmail on its own, and edit
# the sys,path.insert to point to where libgmail.py is.

# import sys 
# sys.path.insert(0, 'libgmail')
import libgmail
import time

###############################################################################
# Configuration
###############################################################################

# Email Address
email = &quot;YOUR EMAIL HERE&quot;
# Password
password = &quot;YOUR PASS HERE&quot;
# Check all on index, rather than just the first 2 dates found
checkAllIndex = False
# Use Loop (if true the script will keep looping, and sleep between checking
# for new mail to modify)
useLoop = False
# Time in seconds to sleep when looping (300 = 5 mins)
loopTime = 300
# Label Prefix - if group-based labeles are prefixed, set the prefix here.
# (eg &quot;Groups/&quot;)
labelPrefix = &quot;&quot;
# What folder to check? ('inbox' or 'all' are probbaly the most common settings)
folderName = 'inbox'

###############################################################################
# Helper classes/methods
###############################################################################

class ContactGroup:
	def __init__(self, id, name, contacts):
		self.id = id
		self.name = name
		self.contacts = contacts
	
	def containsContact(self, contact):
		for knownContact in self.contacts:
			if knownContact[0] == contact.id:
				return True
		return False
	
		def __str__(self):
			return self.name

# Get Contacts and Groups
# Modified from libgmail 0.1.10 to include groups aswell
def getContacts(account):
	&quot;&quot;&quot;
	Returns a GmailContactList object
	that has all the contacts in it as
	GmailContacts
	&quot;&quot;&quot;
	contactList = []
	groupList = []
	# pnl = a is necessary to get *all* contacts
	myUrl = libgmail._buildURL(view='cl',search='contacts', pnl='a')
	myData = account._parsePage(myUrl)
	# This comes back with a dictionary
	# with entry 'cl'
	addresses = myData['cl']
	
	# Now loop through the addresses and get the contacts
	for entry in addresses:
		if len(entry) &gt;= 6 and entry[0]=='ce':
			newGmailContact = libgmail.GmailContact(entry[1], entry[2], entry[4], entry[5])
			contactList.append(newGmailContact)
	
	contacts = libgmail.GmailContactList(contactList)
	
	# And now, the groups
	for entry in addresses:
		if entry[0]=='cle':
			newGroup = ContactGroup(entry[1], entry[2], entry[5])
			groupList.append(newGroup)
	
	return contacts, groupList

###############################################################################
# Setup
###############################################################################

print &quot;Running..&quot;
print &quot;Use Loop:&quot;, useLoop
if useLoop:
	print &quot;  Loop Time:&quot;, loopTime
print &quot;Check all on index:&quot;, checkAllIndex
print &quot;Label Prefix:&quot;, labelPrefix
print &quot;Checking Folder:&quot;, folderName
print &quot;libgmail Version:&quot;, libgmail.Version
print &quot;&quot;

# Login to gmail
print &quot;Logging in as&quot;, email
ga = libgmail.GmailAccount(email, password)
ga.login()

# Loop at least once.
loop = True;

while loop:
	loop = useLoop
	
	print &quot;Getting label names..&quot;
	# Get Labels
	labels = ga.getLabelNames(refresh=True)
	# Get Messages
	print &quot;Getting messages..&quot;
	inbox = ga.getMessagesByFolder(folderName)
	# Get Contacts
	print &quot;Getting contacts and groups&quot;
	contacts, groups = getContacts(ga)
	
	# Check each thread in the inbox
	lastDate = '';
	secondDate = False;
	for thread in inbox:
		# Only check dates we are supposed to.
		if not checkAllIndex:
			# Get the date
			threadDate = thread.__getattr__('date');
			# Make sure a date is set
			if lastDate == '':
				lastDate = threadDate
				
			# If this date is different to the last one do something.
			if lastDate != threadDate:
				# If we are already on the second date, then we stop now
				if secondDate:
					break;
				# Otherwise, if the new data is a non-time date, we can change to the
				# second date.
				elif &quot;am&quot; not in threadDate and &quot;pm&quot; not in threadDate:
					lastDate = threadDate
					secondDate = True
		
		print &quot;Thread:&quot;, thread.id, len(thread), thread.subject, thread.getLabels(), thread.__getattr__('date'), thread._authors, thread.__getattr__('unread')
		try: 
			# Current Labels
			threadCurrentLabels = thread.getLabels();	
			# We will add labels here first to prevent dupes
			threadLabels = set([])
			# Check each message in the thread.
			for msg in thread:
				print &quot;  Message:&quot;, msg.id, msg.sender
				# Check if sender is a known  contact
				contact = contacts.getContactByEmail(msg.sender)
				if contact != False:
					# Check each group for this contact
					for group in groups:
						# If we have a label with this group name
						labelName = labelPrefix+group.name
						if (labelName in labels) and (labelName not in threadCurrentLabels):
							# And the group contains the contact we want
							if group.containsContact(contact):
								# Add it to the list
								print &quot;    Sender Label:&quot;, labelName
								threadLabels.add(labelName)
		except Exception, detail:
			print &quot;  Error parsing messages:&quot;, type(detail), detail
			
		# Now add the labels
		for label in threadLabels:
			print &quot;  Adding Label:&quot;, label
			thread.addLabel(label)
		# If thread was unread, make it unread again.
		if thread.__getattr__('unread'):
			print &quot;  Remarking as unread&quot;
			ga._doThreadAction(&quot;ur&quot;, thread)
	
	if loop:
		print &quot;&quot;
		print &quot;Sleeping&quot;
		time.sleep(loopTime)
	else:
		print &quot;Done&quot;
</pre><br>
On a related note, I've also recently started to use the &quot;<a href="https://addons.mozilla.org/en-US/firefox/addon/6076">Better Gmail 2</a>&quot; addon for firefox (Official page seems down atm, but more info <a href="http://lifehacker.com/software/exclusive-lifehacker-download/better-gmail-2-firefox-extension-for-new-gmail-320618.php">here</a>) mostly for the grouping of labels feature.<br>
<br>
<b>Edit:</b> Script will now preserve unread status of threads.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Tue, 05 Aug 2008 06:03:27 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=129</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=129</link>
  </item>
  <item>
    <title>MD5</title>
    <description><![CDATA[I was recently looking at converting an old application from VB6 to Java that used MD5 in its output files as hashes for validation.<br>
<br>
The first thing I did was to make a java class that read in the file and checked the hashes, I tried it on a few files and it worked fine, then I found a file that it failed on.<br>
<br>
Now, this app wrote all the files using the exact same function, so it seemed odd that 1 of them wouldn't parse and the rest would.<br>
When I looked at the file closer, I found that this one contained some symbols in the output that the others didn't - I eventually figured out that the symbol that was causing the problem was the pound sign (&pound;).<br>
<br>
Without going into too much detail, this presented a major problem, the string in question was used as part of the password validation for the app (the output files are encrypted using the password as a key), and the java code was getting different results than the old VB6 code, and was unable to decode the file as a result.<br>
<br>
So, this sparked my curiosity a bit, the VB6 code I was using wasn't a built in, it was code I'd gotten elsewhere and used, so I assumed it was faulty code (not that this helped me much, as I needed to get the exact same output, but ignoring that).<br>
<br>
I edited the initial form of my application to return the MD5 String for &quot;&pound;&quot; on its own, and got: &quot;d527ca074d412d9d0ffc844872c4603c&quot;<br>
I did the same for my java code and got: &quot;6465dad1d31752be3f3283e8f70feef7&quot;<br>
<br>
So now all I needed to do was to see which was right, so I made a quick PHP script, and did the same and got: &quot;d99731d14c7750048538404febb0e357&quot; ... Yet another different hash!?<br>
<br>
Ok, I thought, md5sum will help me figure out which one is right. one `echo '&pound;' | md5sum -` and I had &quot;67160ce935d7cb5339047b12ad4611cb&quot;. Yes, that is correct, a 4th different hash.<br>
<br>
So here I was with 4 different hashes and no idea which one was correct.<br>
<br>
So after a bit of googling, I discovered that the MD5 RFC (1321) had the source code for a test application in it.<br>
So I extracted the code from the Appendix of http://www.ietf.org/rfc/rfc1321.txt and tried to compile it with `gcc md5.c mddriver.c -o mddriver` only to discover that it failed to compile with lots of errors, fortunately this was an easy fix, near the top of mddriver.c, change &quot;#define MD MD5&quot; to &quot;#define MD 5&quot; and then it compiles without problem.<br>
<br>
So, I ran &quot;./mddriver -s&pound;&quot; and got the output &quot;MD5 (&quot;&pound;&quot;) = d99731d14c7750048538404febb0e357&quot; which agreed with what the PHP md5() function gave.<br>
(Its worth noting that `echo '&pound;' | ./mddriver` agreed with md5sum, which made me remember that `echo` appends a &quot;\n&quot;, which was why I got a different output, running `echo -n '&pound;' | md5sum` gives the correct result, and would have saved me googling and finding the test suite!)<br>
<br>
I tested a few other things and got the following results:<br>
<pre>
        mddriver: d99731d14c7750048538404febb0e357
             PHP: d99731d14c7750048538404febb0e357
           mySQL: d99731d14c7750048538404febb0e357
          python: d99731d14c7750048538404febb0e357
      postgreSQL: d99731d14c7750048538404febb0e357
          md5sum: d99731d14c7750048538404febb0e357

      JavaScript: d527ca074d412d9d0ffc844872c4603c
     VisualBasic: d527ca074d412d9d0ffc844872c4603c
         Eggdrop: d527ca074d412d9d0ffc844872c4603c
   Java (custom): d527ca074d412d9d0ffc844872c4603c

 Java (built in): 6465dad1d31752be3f3283e8f70feef7
</pre><ul><li>JavaScript implementation from <a href="http://pajhome.org.uk/crypt/md5/">http://pajhome.org.uk/crypt/md5/</a><br>
<li>VisualBasic implementation from <a href="http://www.frez.co.uk/freecode.htm#md5;">http://www.frez.co.uk/freecode.htm#md5;</a><br>
<li>Custom Java implementation from <a href="http://www.freevbcode.com/ShowCode.Asp?ID=741">http://www.freevbcode.com/ShowCode.Asp?ID=741</a><br>
</ul><br>
There is also a list of MD5 implementations at <a href="http://userpages.umbc.edu/~mabzug1/cs/md5/md5.html">http://userpages.umbc.edu/~mabzug1/cs/md5/md5.html</a><br>
<br>
----<br>
The differences are primarily due to character encoding in the different languages. (In the case of my app, there was also a flaw in the implementation for strings where (length % 64) is &gt; than 55 as well)<br>
<br>
Example:<br>
<pre>
[07:14:55] [shane@Xion:~]$ php -r 'echo md5(utf8_encode("£"))."\n";'
2ccf59396b3c0958eec4ba721e2d083f
[07:15:01] [shane@Xion:~]$ php -r 'echo md5("£")."\n";'
d99731d14c7750048538404febb0e357
</pre><br>
<pre>
Java: System.out.println((int)'£'); => "163"
PHP: echo ord('£'); => "194"
PHP: echo ord(utf8_encode('£')); => "195"
</pre><br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Mon, 28 Jul 2008 02:55:09 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=128</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=128</link>
  </item>
  <item>
    <title>RSS Feed</title>
    <description><![CDATA[I fixed the rss feed for the site today, its been broken for a while now (it was using a different user name to connect to the SQL DB than the rest of the site, and I changed access permissions on a load of things a while ago). <br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Sun, 20 Jul 2008 23:40:50 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=127</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=127</link>
  </item>
  <item>
    <title>"Dr. Horrible's Sing-Along Blog - Act 3" a Dissapointment</title>
    <description><![CDATA[Despite all the &quot;This was amazing&quot; &quot;fantastic&quot; reviews I seem to find for this everywhere, I found myself dissapointed after watching it.<br>
<br>
The first 2 acts were funny and very rewatchable, there was gold bars that became gold liquid, the &quot;Bad Horse&quot; letters and phone calls, Captain Hammer and his &quot;Hammer&quot;, they made me laugh and alongside the humor was a kick ass sound track (my personal favourite being &quot;Its a brand new day&quot;).<br>
<br>
Act 3 on the other hand was a complete change in direction, the songs weren't as good, it wasn't really all that funny (Infact I think the onyl bit I laughed at was him stopping his song to correct the spelling of his name), it suddently became all serious. All in all I found it a rather dissapointing, and somewhat obvious, end and a let down to an otherwise awesome show.<br>
<br>
Despite this I'm still going to buy the DVD (and hopefully the OST if one comes out), as I approve of the idea of a web-streamed show (I think there was a push to get a firefly season 2 done in this way at one point) and would like to see more of them.<br><br><a href="http://home.dataforce.org.uk/index.php?p=news&id=#Comments">Comments</a>]]></description>
    <pubDate>Sun, 20 Jul 2008 14:26:23 +0100</pubDate>
    <guid isPermaLink="true">http://home.dataforce.org.uk/?page=viewarticle&amp;id=126</guid>
    <link>http://home.dataforce.org.uk/?page=viewarticle&amp;id=126</link>
  </item>
  </channel>
</rss>

