Thursday, June 26, 2008

pyVerify version 2

Following on from my previous post that shows how to verify cd or dvd integrity, I have this following update:
  1. The volumeid.sh script now reads the whole isoinfo -d -i from the dvd and pipes it through to md5sum to generate a "signature" that identifies the disk. The thinking here is that it would be near impossible to have have two disk headers that match completely. Even those that share volume ids... For example openSUSE 11.0 i386 and openSUSE 11.0 x86_64
  2. The Verify class now includes checking for more than one row in the database that has the same volume label. ( md5sum result from volumeid.sh ) This is to catch anything that matches for some strange reason.
The updated code for all the files is included below.

volumeid.sh

#!/bin/sh
#
# small utility to find the md5sum of the isoinfo header information
#

isoinfo -d -i /dev/cdrom | md5sum | cut -d " " -f 1


verify.sh

#!/bin/sh
#

# Start with verifying CDs
#

device="/dev/cdrom"
checksumtype=$1

#Find details of the device
blocksize=`isoinfo -d -i $device | grep "^Logical block size is:" | cut -d " " -f 5`
if test "$blocksize" = ""; then
echo catdevice FATAL ERROR: Blank blocksize >&2
exit 1
fi

blockcount=`isoinfo -d -i $device | grep "^Volume size is:" | cut -d " " -f 4`
if test "$blockcount" = ""; then
echo catdevice FATAL ERROR: Blank blockcount >&2
exit 1
fi

command="dd if=$device bs=$blocksize count=$blockcount conv=notrunc,noerror status=noxfer"

# execute the command to read the disk and pipe through md5sum or sha1sum
result=`$command | $checksumtype`

#get the checksum
checksumresult=`echo $result | cut -d " " -f1`

echo $checksumresult


Verify.py

#!/usr/bin/env python

#===============================================================================
# Version information
# Version 0.1 Use Volume ID from isoinfo to identify current dvd/cd
# Version 0.2 Use an MD5SUM of the whole header found by isoinfo to
# identify the cd/dvd - This is called the signature in further
# comments
#===============================================================================

import MySQLdb
import popen2
import string

class Verify:

volumeid=''
checksumtype='md5sum'
goodchecksum=''

#===============================================================================
# Use volumeid.sh to find the signature of the disk
# and checks it against the database.
# If no match is found then report and exit. Otherwise
# checksum details are stored in class variables.
#===============================================================================

def __init__(self):

#===============================================================================
# get the signature from the inserted disk.
# Check for errors
# Store the signature in self.volumeid
#===============================================================================

(fout, fin, ferr) = popen2.popen3('./volumeid.sh')

errLineCount = 0
while True:
if ferr.readline():
errLineCount += 1
else:
break

if errLineCount > 0:
print "Errors were found."
exit()

id = ''
while True:
c = fout.read(1)
if c != "\n":
id += c
else:
break
self.volumeid = id

fout.close()
fin.close()

#===============================================================================
# Connect to database
# Find the distro with a distro_label matching the signature
#===============================================================================

conn = MySQLdb.connect(host = 'localhost',
user = 'root',
passwd = 'greycat',
db = 'cdburner' )
cursor = conn.cursor(MySQLdb.cursors.DictCursor)
sql = "SELECT * FROM distro WHERE distro_label = '%s'" % self.volumeid
cursor.execute(sql)

rows = cursor.fetchall()

#===============================================================================
# We still want to make sure there are no duplicates in the database.
# I would expect this to not ever happen as the block count is stored
# in the header and it would be quite unusual for two different CD or DVDs
# to have the same header details and block counts.
#===============================================================================

if( len(rows) > 1 ):
row = self.confirm(rows)
elif ( len(rows) < 1 ):
print "Could not find a matching record in the database."
print rows
print "SQL: %s" % sql
print "Quitting..."
exit()
else:
row = rows[0]

#===============================================================================
# Retrieve details of full checksum and checksum type
# from the database.
# Report findings and inform what's happening next.
#===============================================================================

if row["hash_type"] == 1:
self.checksumtype = 'md5sum'
else:
self.checksumtype = 'sha1sum'
self.goodchecksum = row['hash_detail']

print "Found [ %s ] in drive" % row['distro_name']
print "..."
print "performing %s check on disk now..." % self.checksumtype

cursor.close()
conn.close()

#===============================================================================
# Find the checksum of the inserted disk.
# using the external command verify.sh
#===============================================================================

cmd = "./verify.sh %s" % self.checksumtype
print "Executing ... %s " % cmd

(fout, fin) = popen2.popen2(cmd)
checksum = ''
while True:
c = fout.read(1)
if c != "\n":
checksum += c
else:
break
fout.close()
fin.close()

if checksum == self.goodchecksum:
print "Good Checksum : %s" % self.goodchecksum
print "Checksum found: %s" % checksum
print "DISK SUCCESSFULLY VERIFIED"
else:
print "Good Checksum : %s" % self.goodchecksum
print "Checksum found: %s" % checksum
print "*** ERROR *** DISK COULD NOT BE SUCCESSFULLY VERIFIED *** ERROR ***"

#===============================================================================
# We have more than one option. Ask for clarification
# Example. openSUSE 11 has the same volume id for both
# i386 and x86_64
# Keep asking until y or q entered.
#===============================================================================
def confirm(self,rows):
if len(rows) > 1:
while True:
print "Please select the ID that matches the media you wish to verify"
print "ID - Name"
count = 1
for row in rows:
print "%2d - %s" % (count, row["distro_name"])
count += 1
selected = raw_input(">")
print "You selected %s" % rows[int(selected)-1]["distro_name"]
confirmed = raw_input( "Is this ok? (y=yes, n=no, q=quit)" )
if ( string.lower(confirmed) == 'y'):
break
elif ( string.lower(confirmed) == 'q'):
print "QUIT APP"
exit()
return rows[int(selected)-1]


if __name__ == "__main__":

print "DISK Verifier -- Console Application."
print "by David Latham ( The Linux CD Store ) 2008"
print " "
v = Verify()
exit()

Tuesday, June 24, 2008

Python + Bash + isoinfo + mysql = Python CD Integrity Verifier

I have a requirement to verify by md5sum or sha1sum, CDs or DVDs that I burn - so I wrote a bunch of scripts. I am not saying that this is the best way to skin this particular cat, but it is working.

First of all a bit of background info.

This stuff only works on Linux because the commands make use of Linux tools such as isoinfo and dd. I am sure Windows command line equivalents exist...

I have a mysql database with one table in it that has the following fields:
distro_label --- Volume ID of CD or DVD
distro_name ---- Name of the CD or DVD
hash_type ------ 1 = md5sum, 2 = sha1sum
hash_detail ---- Known good md5sum or sha1sum of the particular CD or DVD

Here is an example record:
distro_label --- Slack11d1
distro_name ---- Slackware 11 Disk 1
hash_type ------ 1
hash_detail ---- a7cfcb4be158beca63af21b3b4dbc69c

In case you are wondering how I know the volume id - try this while you have a CD or DVD in your cd / dvd drive:
[~]$  isoinfo -d -i /dev/cdrom

Requirements
This python script has one dependency. Basically I need my python script to be able to query the database.

  • MySQL-python - Download and install from here or if you are in Fedora try: yum install MySQL-python

Retrieving the volume id from the disk.
I have a small utility bash script that does this.
#!/bin/sh
#
# small utility to find the volume id of a cd / dvd
#

isoinfo -d -i /dev/cdrom \
| grep "Volume id:" \
| cut -d ":" -f 2 \
| sed "s/ //g" 

It does the following:

  1. read the iso info from the disk

  2. Find the line with the Volume id

  3. Cut out the second field delimited by ":"

  4. remove all spaces

Reading the disk
In order to accurately read the CD we need to know some details about the cd first. There is a very useful script ( from which I borrowed all the technical stuff about finding the blocksizes and blockcounts required by dd ) I found the rawread.sh script here.
Here it is for your convenience:
#!/bin/sh
device=$1

blocksize=`isoinfo -d -i $device | grep "^Logical block size is:" | cut -d " " -f 5`
if test "$blocksize" = ""; then
  echo catdevice FATAL ERROR: Blank blocksize >&2
  exit
fi

blockcount=`isoinfo -d -i $device | grep "^Volume size is:" | cut -d " " -f 4`
if test "$blockcount" = ""; then
  echo catdevice FATAL ERROR: Blank blockcount >&2
  exit
fi

command="dd if=$device bs=$blocksize count=$blockcount conv=notrunc,noerror"
echo "$command" >&2

$command


Here is my modified version to suit a call from my python script.
#!/bin/sh
#

device="/dev/cdrom"
checksumtype=$1

#Find details of the device
blocksize=`isoinfo -d -i $device | grep "^Logical block size is:" | cut -d " " -f 5`
if test "$blocksize" = ""; then
 echo catdevice FATAL ERROR: Blank blocksize >&2
 exit 1
fi

blockcount=`isoinfo -d -i $device | grep "^Volume size is:" | cut -d " " -f 4`
if test "$blockcount" = ""; then
 echo catdevice FATAL ERROR: Blank blockcount >&2
 exit 1
fi

command="dd if=$device bs=$blocksize count=$blockcount conv=notrunc,noerror"

# execute the command to read the disk and pipe through md5sum or sha1sum
result=`$command | $checksumtype`

#get the checksum
checksumresult=`echo $result | cut -d " " -f1`

echo $checksumresult

This script does the same things as rawread.sh but lets the user specify a checksum type as a command line argument. When called from within our python script this bash script will simply return the real checksum of the disk in the cd / dvd device.

  1. Store the checksum type in a variable.

  2. Find the block size and block count values for the disk.

  3. Format the dd command

  4. Execute the dd command and pipe into checksum type. eg: dd .... | md5sum

  5. cut the resulting checksum from the output of the above.

  6. echo just the checksum


The python script.
I have tried to comment it so it all makes sense. I look forward to a lively discussion in the comments. I will soon know how many of my readers actaually care about python or, more to the point, how many readers I have...
#!/usr/bin/env python

#================================================================================
# MySQLdb is the only dependency required for this script.
# popen2 comes with standards python 2.5
#================================================================================

import MySQLdb
import popen2

class Verify:
    
    volumeid=''
    checksumtype='md5sum'
    goodchecksum=''
    
    #================================================================================
    # Constructor uses volumeid.sh to find the volumeid if the cdrom
    # and checks it against the database.
    # If no match is found then an error is generated otherwise
    # checksum details are stored in class variables.
    #================================================================================
    def __init__(self):
        
        ## Get the volumeid
 # fout = stdout
 # fin = stdin
 # ferr = stderr
        (fout, fin, ferr) = popen2.popen3('./volumeid.sh')
        id = ''
        
 ## Check for errors
        errLineCount = 0
        while True:
            if ferr.readline():
                errLineCount += 1
            else:
                break
        
        if errLineCount > 0:
            print "Errors were found."
            exit()
        
 ## We are reading each character of the standard out because
 ## we do not wish to capture the newline at the end.
        while True:
            c = fout.read(1)
            if c != "\n":
                id += c
            else:
                break
 ## Store the volumeid in the class variable.
        self.volumeid = id
 
 ## Clean up.
        fout.close()
        fin.close()
        
 ## Establish mysql connection and query database.
        conn = MySQLdb.connect(host = 'localhost',
                               user = 'resu',
                               passwd = 'drowsapp',
                               db = 'cdburner' )
        cursor = conn.cursor(MySQLdb.cursors.DictCursor)
        sql = "SELECT * FROM distro WHERE distro_label = '%s'" % self.volumeid
        cursor.execute(sql)

        row = cursor.fetchone()

        ## TO DO: Check for non existent entry in database and throw error.

 ## Find the required checksum type from the database.
        if row["hash_type"] == 1:
            self.checksumtype = 'md5sum'
        else:
            self.checksumtype = 'sha1sum'
 
 ## Find the known checksum from the database
        self.goodchecksum = row['hash_detail']
        
 ## Print some information to the user.
        print "Found [ %s ] in cd drive" % row['distro_name']
        print "Good Checksum = %s" % self.goodchecksum
        print "..."
        print "performing %s check on disk now..." % self.checksumtype
        
 ## Clean up.
        cursor.close()
        conn.close()
        
        ##  Read checksum from disk
 
 ## TO DO: change this to check for errors like the popen3 command above.
 
        cmd = "./verify.sh %s" % self.checksumtype
        (fout, fin) = popen2.popen2(cmd)
        checksum = ''
 ## Same as above in terms of not wanting the newline at the end of stdout.
        while True:
            c = fout.read(1)
            if c != "\n":
                checksum += c
            else:
                break
        
 ## Clean up
 fout.close()
        fin.close()

        ## Compare the checksums and report!
 if checksum == self.goodchecksum:
            print "Checksum found: %s" % checksum
            print "DISK SUCCESSFULLY VERIFIED"
        else:
            print "*** ERROR *** DISK COULD NOT BE SUCCESSFULLY VERIFIED *** ERROR ***"

## If this script is being executed then do this stuff.
## This block allows us to use the above as a class or library or as a simple script.
if __name__ == "__main__":
    
    print "DISK Verifier -- Console Application."
    print "by David Latham ( The Linux CD Store ) 2008"
    
    v = Verify()
    exit()


All in all these scripts join together to provide a non technical user ( ie: My lovely wife ) the ability to verify Linux distros before she ships them out. Want to know more? Check out: http://www.thelinuxcdstore.com.

Saturday, June 21, 2008

Python + inotify = Pyinotify [ how to watch folders for file activity ]

Sometimes it just might be handy to be able to watch a folder on a hard disk for changes. For example: A client app might drop small files on a shared folder. A server app might be watching the folder for just such an event. Once the file is created, the server will kick into action and perform whatever tasks are required.

This all comes from my CD burning application. I am currently thinking that the client apps will drop small xml files containing information about what to burn onto a folder the webserver has access to and the cdburner service will be watching...

The linux kernel provides inotify. This from wikipedia:
notify is a Linux kernel subsystem that provides file system event notification. It was written by John McCutchan with help from Robert Love and later Amy Griffis to replace dnotify. It was included in the mainline kernel from release 2.6.13 (2005-06-18), and could be compiled into 2.6.12 and possibly earlier releases by use of a patch. Its function is essentially an extension to filesystems to notice changes to the filesystem, and report those changes to applications.
Pyinotify is a python module that exposes the inotify api in python. This from http://pyinotify.sourceforge.net/:
pyinotify is a Python module for watching filesystems changes. pyinotify can be used for various kind of fs monitoring. pyinotify relies on a recent Linux Kernel feature (merged in kernel 2.6.13) called inotify. inotify is an event-driven notifier, its notifications are exported from kernel space to user space through three system calls. pyinotify binds these system calls and provides an implementation on top of them offering a generic and abstract way to use inotify from Python. Pyinotify doesn't requires much detailed knowledge of inotify. Moreover, it only needs few statements for initializing, watching, handling (optionnaly trough a new separate thread), and processing events notifications through subclassing. The only things to know is the path of items to watch, the kind of events to monitor and the actions to execute on these notifications. Note: pyinotify requires Python 2.3 and above, and Linux 2.6.13 at least.
I went ahead and gave it a go. I did find though, that on my fedora system ( fedora 9 x86_64 ) that the tutorial on the above wiki didn't quite work. Here is the tutorial code for the non threaded example that does work on my system.


#!/usr/bin/python
import os
import pyinotify

wm = pyinotify.WatchManager()
mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE

class PTmp(pyinotify.ProcessEvent):
def process_IN_CREATE(self, event):
print "Create: %s " % os.path.join(event.path, event.name)
def process_IN_DELETE(self, event):
print "Delete: %s " % os.path.join(event.path, event.name)


notifier = pyinotify.Notifier(wm, PTmp())

wdd = wm.add_watch('/home/dave/projects', mask, rec=True)

while True:
try:
notifier.process_events()
if notifier.check_events():
notifier.read_events()
except KeyboardInterrupt:
notifier.stop()
break

Unfortunately inotify is a Linux Kernel technology that is not currently available on windows. I guess Windows has some other kind of API for filesystem event monitoring but if you are like me and want to keep things simple with python then I am afraid inotify on windows is not possible. If you feel that you would like to give this stuff a go then I suggest getting yourself a LiveCD of one of the Linux Distros. They can be bought here.

Sunday, June 15, 2008

Python + YAMI = 3 Tier

Background

I have spent most of last night and this afternoon working out how to implement a website for my local LAN that would enable use of my DVD writer from a remote host over a web interface. I need to provide a small web application that can be used to burn ISO images onto CDs or DVDs. The application should also verify the CD or DVD once it has been burnt.

Security

To start with I needed to find out how to control the CD or DVD burner from the website. There is the small problem of security here. I could not simply add the Apache user access to /dev/sr0 ( the cd device ) because then it is conceivable that anyone or any rouge application might be able to use the Apache service to monkey with my device. I had to provide some kind of abstraction which could authenticate / authorise the request prior to performing it.

Python

Python is fast becoming my favourite scripting language for working in Linux. It has some very nice libraries that makes things like network programming very easy. It also has great SYS and OS libraries that are useful for working with the native operating system and environments.

YAMI ( Yet Another Messaging Infrastructure )

YAMI makes the nuts and bolts of client server communication very easy. Read up on it here. It can be compiled with support for c/c++, java, tcl and python. I only bothered with support for python. I had to ensure that the yamipyc.so module was located in the default python search path for my machine so mod_python could find it.


Apache

It is a reactively simple procedure to add a python handler to a website. Lookup mod_python. I will just say that you can configure mod_python in the Apache config to use a specific python file to handle all python requests. In my case I used the mod_python.publisher handler which is a built-in handler that is geared for reading post and get vars as well as publishing responses. I could have done all this in PHP, but seeing as though my plan was to use python for the application layer, I thought a connector to python was the easiest.

In the background the plan is to have a python server listening for connections on a specific port. The client will send it commands and it will respond appropriately. AS the service is executed under a user with permissions to the CD device and there is authentication and sanitisation going on in front of the device, we have extra security. I also plan to implement controls on the firewall to allow only one specific machine on my LAN to connect to it.

Flow

SO here is how it should all work:

  1. Website posts form to python handler ( handler.py )
  2. Apache mod_python knows how to manage this.
  3. handler.py Authenticates the request
  4. handler.py establishes a client connection to server.py
  5. handler.py sends commands based on the post it has received to server.py
  6. server.py sanitises the commands and executes an os.system call to the device. OR it rejects the commands.
  7. server.py responds with status messages and results.
  8. handler.py receives the results or status messages and reports back to the website.
To do: Think about asynchronous calls from the website so that the status of a burn can be presented incrementally.

Here are the scripts: ( source code highlighting found here.)

handler.py

#!/usr/bin/env python
from mod_python import apache
from YAMI import *
import os

def eject(req):
agent = Agent()
agent.domainRegister('cdburner', '127.0.0.1', 12340, 2)
agent.sendOneWay('cdburner', 'cd', 'eject', [''])
del agent

def shutdown(req):
agent = Agent()
agent.domainRegister('cdburner', '127.0.0.1', 12340, 2)
agent.sendOneWay('cdburner', 'cd', 'shutdown', [''])
del agent


Server.py

#!/usr/bin/env python
from YAMI import *
import os

agent = Agent(12340)
agent.objectRegister('cd')

print 'server started'

while 1:
im = agent.getIncoming('cd', 1)
src = im.getSourceAddr()
msgname = im.getMsgName()
if msgname == 'eject':
print 'Ejecting'
os.system("eject")
elif msgname == 'shutdown':
print 'Shutting down'
del im
break

del im

del agent


So, a request to http://localhost/cdburner/cdburner.py/eject will call the eject function ( this functionality is provided by mod_python.publisher ) and the cd tray is ejected. ( so long as the server.py script is running. )
A request to http://localhost/cdburner/cdburner.py/shutdown will stop the server.py service altogether.

I will also be looking at logging and all sorts of other things.

Conclusion

I have looked at a very simple web layer to application layer messaging system provided by mod_python and the mod_python.publisher handler, and YAMI compiled with support for python. The thing to note here is that the web server can make calls to the application server ( which, incidentally can be on a different physical server ) and the application server responds to the client which then reports back to the website. All this without changing any security permissions of the underlying operating system.

Where's the third tier?

Well that's the database. Python has excellent support for databases. This application will be no different. I intend to use the python connector so that access to the database is managed by the application and not the web server. Unfortunately I only have one machine so all three tiers will be on the same physical hardware. I accept this blatant security risk because a) I am cheap and b) this is a LAN application only. It will have no access from the world wide web. I control that little nugget with a real firewall in front of my LAN.

Wednesday, June 4, 2008

So Whatddya Know?

Social networking sites seem to tap in to the idea that we should all be connected together and on-line. We should be able to do something, upload a photo, write a comment, update a status or simply forward something along to all of our friends for them to see.

Don't you mean, "So what do your mates know?"

It's not what we know that is important any more. What is important is what we share. The information we give out to people about our feelings and experiences has suddenly - through the exponential growth [ reference ] of social networking sites - become valuable. Valuable to our friends who are now taking notice of us... and valuable to big business who piggy-back their relevant and targeted marketing. Every post you write will be read by at least half a dozen of your friends. If you have a popular profile / blog it will be read by half a dozen thousand people. That's a lot of people that know stuff about you.

So what - I am more popular now than ever before!

Now that you are writing and sharing and people are reading your words and watching your forwarded funwall posts you are feeling like you are connected to people. I wonder though, just how connected one really is in a virtual, on-line environment. Where is the balance with real life. If you are not doing so well socially in real life but are managing "just fine, thank you!" on your social network then I suggest that real life holds the potential for much greater reward and should be pursued with even more zeal than the virtual world. If we would spend the same energy with actual people - you know - face to face: then we might find our lives even further enriched than is possible on a virtual social network.

An experiment

Here is the point - sort of. I am new to blog writing so making my point is a little difficult. I have to practice.

Consider what you could do with your time if you DIDN'T have a virtual social network and instead worked on the real social network.

In the following - half aside half to the point snippet I use Facebook as an example to describe my own experience with social networking.

I have not picked Facebook over another virtual social network for any reason other than that it was the one I belonged to before I decided to give it up.

Remember - Blogs are a way for us to hold public opinion without fear of recourse or legal action... I hope.

After about a year of holding an FB ( Face Book ) profile and managing - sort of - to keep it up to date, I decided to erase my profile. The reasons are:

  1. I was becoming increasingly dissalusioned with the product itself. The site seems to be slow at times, and lacks anything other than the fun wall to interact with. Vampire bites and the Booze Wall are a little too juvinile for my tastes.
  2. I was being spammed by facebook. It was a requirement for me to actively turn off email notifications. I prefer it when I am made to activly turn them on when I need something.
  3. I was becomming increasingly concerned about the notion that my profile might be linked to another ( through an intemediary or through some third party application like Fun Wall ) dodgey profile. My good name and reputation might be jeapordised in a way that I would have no real control over.
  4. Facebook own all my data on my profile in perputuity to the extent that they can sell it to another organization without my consent.
  5. PARANOID REASON - I am certain that governement organisations can easily obtain warrants to access the data.
Removing the profile was not a simple matter.

I had to first de-activate it. While it was de-activated I continued to receive emails about a class reunion I had no intention of attending.

Next I had to send an email to privacy@facebook.com requesting that my profile be permanently erased from the servers. The reply I received pointed out to me that I could adjust my privacy settings and ensure that my data and reputation remain safe. It just didnt ring true to me. Here is the email:

Date: Sat, 24 May 2008 19:37:07 -0700
To: david@XXXXXXX
From: Facebook Support
Content-Transfer-Encoding: 8bit

Hi David,

We appreciate your concern. We use user content in connection with various features and services on the site (for example, displaying it in profile pages, photo pages, news feeds and other messages to users' friends, etc.). Additionally, though it may not happen often, there are cases when law officers and judges issue subpoenas or other legal orders requiring us to provide certain information about specific users for use in court. Aside from such scenarios, however, we do not currently share any information with third parties except with the user's consent (for example, if a user elects to add a third party application), and we never sell your information to anyone. In addition, we may use aggregate data from the user base, but this data is disassociated from specific user information.

Except as described above, it is highly unlikely that Facebook will ever use any material that you have uploaded to the site. However, for legal reasons, we must keep the following clause in our Terms of Use to protect ourselves from possible litigation:

"By posting User Content to any part of the Site, you automatically grant, and you represent and warrant that you have the right to grant, to the Company an irrevocable, perpetual, non-exclusive, transferable, fully paid, worldwide license (with the right to sublicense) to use, copy, publicly perform, publicly display, reformat, translate, excerpt (in whole or in part) and distribute such User Content for any purpose, commercial, advertising, or otherwise, on or in connection with the Site or the promotion thereof, to prepare derivative works of, or incorporate into other works, such User Content, and to grant and authorize sublicenses of the foregoing. You may remove your User Content from the Site at any time. If you choose to remove your User Content, the license granted above will automatically expire, however you acknowledge that the Company may retain archived copies of your User Content. Facebook does not assert any ownership over your User Content; rather, as between!
us and
you, subject to the rights granted to us in these Terms, you retain full ownership of all of your User Content and any intellectual property rights or other proprietary rights associated with your User Content."

Additionally, this license or sublicense does not affect ownership or copyright privileges for material on the site.

Also, although Facebook was never intended as a tool to monitor people's actions, we cannot always prevent the site from being used in this way. By default, only your confirmed friends and people on your networks can view your profile. That said, we cannot prevent people with valid network affiliated email addresses from joining one of your networks. For instance, if a campus police department gives its employees a college email address, these people will be able to affiliate with that network on Facebook.

Aside from such situations, however, we do not grant anyone, including employers, law officers, school administrators, and even parents, any kind of special access to information on Facebook that they would not normally be able to view.

In order to avoid any issues involving your Facebook information, we encourage you to make use of your options on the Privacy page. These options allow you to restrict certain people from viewing your profile or finding you in searches.

I hope this helps. Let me know if you have any further questions or if you still would like to permanently delete your account.

Thanks for contacting Facebook,

Theodore
User Operations
Facebook
Well thanks Theo, but it's not really good enough. I wrote back and once again asked them to remove my profile from the servers. They accommodated my request the second time round. Doesn't the above scare you? I guess I am still not all that comfortable having my personal details and expression open for public sale in this way.

I do understand that the Facebook team are a business and that they need to make money too. I just don't wich to contribute to an organisation who make so much money anyway and without regard for the rights of the indivuduals who make it so successful.

I was a photographer, I would not attempt to showcase my stuff on facebook. If I did I would watermark it really well.

Anyway - I know its a long post and I appreciate that you read through it all. You will find me expressing myself here and not on social networking sites anymore.

I understand that sigining up with Blogger has it's own risks but I accept those risks and state categorically that all data on this blog is open to the public domain for whatever they wish to do with it. Friends or no friends.

IN the mean time I have to go and work on building real relationships with real people.